blob: 30ec5a81e807313df9c473188e168f6f97acf0f9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "proton/codec/encoder.hpp"
#include "proton_bits.hpp"
#include "types_internal.hpp"
#include "msg.hpp"
#include "proton/annotation_key.hpp"
#include "proton/binary.hpp"
#include "proton/decimal.hpp"
#include "proton/message_id.hpp"
#include "proton/null.hpp"
#include "proton/scalar_base.hpp"
#include "proton/symbol.hpp"
#include "proton/timestamp.hpp"
#include "proton/value.hpp"
#include <proton/codec.h>
#include <algorithm>
#include <assert.h>
namespace proton {
namespace codec {
void encoder::check(long result) {
if (result < 0)
throw conversion_error(error_str(pn_data_error(pn_object()), result));
}
encoder::encoder(internal::value_base& v) : data(v.data()) {
clear();
}
bool encoder::encode(char* buffer, size_t& size) {
internal::state_guard sg(*this); // In case of error
ssize_t result = pn_data_encode(pn_object(), buffer, size);
if (result == PN_OVERFLOW) {
result = pn_data_encoded_size(pn_object());
if (result >= 0) {
size = size_t(result);
return false;
}
}
check(result);
size = size_t(result);
sg.cancel(); // Don't restore state, all is well.
pn_data_clear(pn_object());
return true;
}
void encoder::encode(std::string& s) {
s.resize(std::max(s.capacity(), size_t(1))); // Use full capacity, ensure not empty
size_t size = s.size();
assert(!s.empty());
if (!encode(&s[0], size)) {
s.resize(size);
assert(!s.empty());
encode(&s[0], size);
}
}
std::string encoder::encode() {
std::string s;
encode(s);
return s;
}
encoder& encoder::operator<<(const start& s) {
switch (s.type) {
case ARRAY: pn_data_put_array(pn_object(), s.is_described, pn_type_t(s.element)); break;
case MAP: pn_data_put_map(pn_object()); break;
case LIST: pn_data_put_list(pn_object()); break;
case DESCRIBED: pn_data_put_described(pn_object()); break;
default:
throw conversion_error(MSG("" << s.type << " is not a container type"));
}
pn_data_enter(pn_object());
return *this;
}
encoder& encoder::operator<<(const finish&) {
pn_data_exit(pn_object());
return *this;
}
namespace {
template <class T, class U> T coerce(const U &x) { return x; }
template <> pn_uuid_t coerce(const uuid& x) { pn_uuid_t y; byte_copy(y, x); return y; }
template <> pn_decimal32_t coerce(const decimal32 &x) { pn_decimal32_t y; byte_copy(y, x); return y; }
template <> pn_decimal64_t coerce(const decimal64 &x) { pn_decimal64_t y; byte_copy(y, x); return y; }
template <> pn_decimal128_t coerce(const decimal128 &x) { pn_decimal128_t y; byte_copy(y, x); return y; }
int pn_data_put_amqp_string(pn_data_t *d, const std::string& x) { return pn_data_put_string(d, pn_bytes(x)); }
int pn_data_put_amqp_binary(pn_data_t *d, const binary& x) { return pn_data_put_binary(d, pn_bytes(x)); }
int pn_data_put_amqp_symbol(pn_data_t *d, const symbol& x) { return pn_data_put_symbol(d, pn_bytes(x)); }
} // namespace
template <class T, class U>
encoder& encoder::insert(const T& x, int (*put)(pn_data_t*, U)) {
internal::state_guard sg(*this); // Save state in case of error.
check(put(pn_object(), coerce<U>(x)));
sg.cancel(); // Don't restore state, all is good.
return *this;
}
encoder& encoder::operator<<(bool x) { return insert(x, pn_data_put_bool); }
encoder& encoder::operator<<(uint8_t x) { return insert(x, pn_data_put_ubyte); }
encoder& encoder::operator<<(int8_t x) { return insert(x, pn_data_put_byte); }
encoder& encoder::operator<<(uint16_t x) { return insert(x, pn_data_put_ushort); }
encoder& encoder::operator<<(int16_t x) { return insert(x, pn_data_put_short); }
encoder& encoder::operator<<(uint32_t x) { return insert(x, pn_data_put_uint); }
encoder& encoder::operator<<(int32_t x) { return insert(x, pn_data_put_int); }
encoder& encoder::operator<<(wchar_t x) { return insert(x, pn_data_put_char); }
encoder& encoder::operator<<(uint64_t x) { return insert(x, pn_data_put_ulong); }
encoder& encoder::operator<<(int64_t x) { return insert(x, pn_data_put_long); }
encoder& encoder::operator<<(timestamp x) { return insert(x.milliseconds(), pn_data_put_timestamp); }
encoder& encoder::operator<<(float x) { return insert(x, pn_data_put_float); }
encoder& encoder::operator<<(double x) { return insert(x, pn_data_put_double); }
encoder& encoder::operator<<(decimal32 x) { return insert(x, pn_data_put_decimal32); }
encoder& encoder::operator<<(decimal64 x) { return insert(x, pn_data_put_decimal64); }
encoder& encoder::operator<<(decimal128 x) { return insert(x, pn_data_put_decimal128); }
encoder& encoder::operator<<(const uuid& x) { return insert(x, pn_data_put_uuid); }
encoder& encoder::operator<<(const std::string& x) { return insert(x, pn_data_put_amqp_string); }
encoder& encoder::operator<<(const symbol& x) { return insert(x, pn_data_put_amqp_symbol); }
encoder& encoder::operator<<(const binary& x) { return insert(x, pn_data_put_amqp_binary); }
encoder& encoder::operator<<(const null&) { pn_data_put_null(pn_object()); return *this; }
encoder& encoder::operator<<(decltype(nullptr)) { pn_data_put_null(pn_object()); return *this; }
encoder& encoder::operator<<(const scalar_base& x) { return insert(x.atom_, pn_data_put_atom); }
encoder& encoder::operator<<(const internal::value_base& x) {
data d = x.data_;
if (*this == d)
throw conversion_error("cannot insert into self");
if (!d || d.empty())
return *this << null();
d.rewind();
check(append(d));
return *this;
}
} // codec
} // proton