blob: 284d53124fed3765f25fe74c738b983a526d984e [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/decoder.hpp"
#include "proton/annotation_key.hpp"
#include "proton/binary.hpp"
#include "proton/decimal.hpp"
#include "proton/message_id.hpp"
#include "proton/scalar.hpp"
#include "proton/symbol.hpp"
#include "proton/timestamp.hpp"
#include "proton/value.hpp"
#include "proton_bits.hpp"
#include "types_internal.hpp"
#include "msg.hpp"
#include <proton/codec.h>
namespace proton {
namespace codec {
/**@file
*
* Note the pn_data_t "current" node is always pointing *before* the next value
* to be returned by the decoder.
*/
decoder::decoder(const internal::value_base& v, bool exact)
: data(const_cast<internal::value_base&>(v).data()), exact_(exact)
{
rewind();
}
namespace {
template <class T> T check(T result) {
if (result < 0)
throw conversion_error(error_str(result));
return result;
}
}
void decoder::decode(const char* i, size_t size) {
internal::state_guard sg(*this);
const char* end = i + size;
while (i < end)
i += check(pn_data_decode(pn_object(), i, size_t(end - i)));
}
void decoder::decode(const std::string& s) { decode(s.data(), s.size()); }
bool decoder::more() {
internal::state_guard sg(*this);
return next();
}
type_id decoder::pre_get() {
if (!next()) throw conversion_error("no more data");
type_id t = type_id(pn_data_type(pn_object()));
if (t < 0) throw conversion_error("invalid data");
return t;
}
namespace {
template <class T, class U> void assign(T& x, const U& y) { x = y; }
void assign(uuid& x, const pn_uuid_t y) { byte_copy(x, y); }
void assign(decimal32& x, const pn_decimal32_t y) { byte_copy(x, y); }
void assign(decimal64& x, const pn_decimal64_t y) { byte_copy(x, y); }
void assign(decimal128& x, const pn_decimal128_t y) { byte_copy(x, y); }
void assign(symbol& x, const pn_bytes_t y) { x = str(y); }
void assign(binary& x, const pn_bytes_t y) { x = bin(y); }
} // namespace
// Simple extract with no type conversion.
template <class T, class U> decoder& decoder::extract(T& x, U (*get)(pn_data_t*)) {
internal::state_guard sg(*this);
assert_type_equal(internal::type_id_of<T>::value, pre_get());
assign(x, get(pn_object()));
sg.cancel(); // No error, cancel the reset.
return *this;
}
type_id decoder::next_type() {
internal::state_guard sg(*this);
return pre_get();
}
decoder& decoder::operator>>(start& s) {
internal::state_guard sg(*this);
s.type = pre_get();
switch (s.type) {
case ARRAY:
s.size = pn_data_get_array(pn_object());
s.element = type_id(pn_data_get_array_type(pn_object())); s.is_described = pn_data_is_array_described(pn_object());
break;
case LIST:
s.size = pn_data_get_list(pn_object());
break;
case MAP:
s.size = pn_data_get_map(pn_object());
break;
case DESCRIBED:
s.is_described = true;
s.size = 1;
break;
default:
throw conversion_error(MSG("" << s.type << " is not a container type"));
}
pn_data_enter(pn_object());
sg.cancel();
return *this;
}
decoder& decoder::operator>>(const finish&) {
pn_data_exit(pn_object());
return *this;
}
decoder& decoder::operator>>(null&) {
internal::state_guard sg(*this);
assert_type_equal(NULL_TYPE, pre_get());
return *this;
}
decoder& decoder::operator>>(decltype(nullptr)&) {
internal::state_guard sg(*this);
assert_type_equal(NULL_TYPE, pre_get());
return *this;
}
decoder& decoder::operator>>(internal::value_base& x) {
if (*this == x.data_)
throw conversion_error("extract into self");
data d = x.data();
d.clear();
narrow();
try {
check(d.appendn(*this, 1));
widen();
} catch(...) {
widen();
throw;
}
next();
return *this;
}
decoder& decoder::operator>>(message_id& x) {
internal::state_guard sg(*this);
type_id got = pre_get();
if (got != ULONG && got != UUID && got != BINARY && got != STRING)
throw conversion_error(
msg() << "expected one of ulong, uuid, binary or string but found " << got);
x.set(pn_data_get_atom(pn_object()));
sg.cancel();
return *this;
}
decoder& decoder::operator>>(annotation_key& x) {
internal::state_guard sg(*this);
type_id got = pre_get();
if (got != ULONG && got != SYMBOL)
throw conversion_error(msg() << "expected one of ulong or symbol but found " << got);
x.set(pn_data_get_atom(pn_object()));
sg.cancel();
return *this;
}
decoder& decoder::operator>>(scalar& x) {
internal::state_guard sg(*this);
type_id got = pre_get();
if (!type_id_is_scalar(got))
throw conversion_error("expected scalar, found "+type_name(got));
x.set(pn_data_get_atom(pn_object()));
sg.cancel(); // No error, no rewind
return *this;
}
decoder& decoder::operator>>(bool &x) { return extract(x, pn_data_get_bool); }
decoder& decoder::operator>>(uint8_t &x) { return extract(x, pn_data_get_ubyte); }
decoder& decoder::operator>>(int8_t &x) { return extract(x, pn_data_get_byte); }
decoder& decoder::operator>>(uint16_t &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(USHORT, tid);
switch (tid) {
case UBYTE: x = pn_data_get_ubyte(pn_object()); break;
case USHORT: x = pn_data_get_ushort(pn_object()); break;
default: assert_type_equal(USHORT, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(int16_t &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(SHORT, tid);
switch (tid) {
case BYTE: x = pn_data_get_byte(pn_object()); break;
case SHORT: x = pn_data_get_short(pn_object()); break;
default: assert_type_equal(SHORT, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(uint32_t &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(UINT, tid);
switch (tid) {
case UBYTE: x = pn_data_get_ubyte(pn_object()); break;
case USHORT: x = pn_data_get_ushort(pn_object()); break;
case UINT: x = pn_data_get_uint(pn_object()); break;
default: assert_type_equal(UINT, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(int32_t &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(INT, tid);
switch (tid) {
case BYTE: x = pn_data_get_byte(pn_object()); break;
case SHORT: x = pn_data_get_short(pn_object()); break;
case INT: x = pn_data_get_int(pn_object()); break;
default: assert_type_equal(INT, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(uint64_t &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(ULONG, tid);
switch (tid) {
case UBYTE: x = pn_data_get_ubyte(pn_object()); break;
case USHORT: x = pn_data_get_ushort(pn_object()); break;
case UINT: x = pn_data_get_uint(pn_object()); break;
case ULONG: x = pn_data_get_ulong(pn_object()); break;
default: assert_type_equal(ULONG, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(int64_t &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(LONG, tid);
switch (tid) {
case BYTE: x = pn_data_get_byte(pn_object()); break;
case SHORT: x = pn_data_get_short(pn_object()); break;
case INT: x = pn_data_get_int(pn_object()); break;
case LONG: x = pn_data_get_long(pn_object()); break;
default: assert_type_equal(LONG, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(wchar_t &x) { return extract(x, pn_data_get_char); }
decoder& decoder::operator>>(timestamp &x) { return extract(x, pn_data_get_timestamp); }
decoder& decoder::operator>>(float &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(FLOAT, tid);
switch (tid) {
case FLOAT: x = pn_data_get_float(pn_object()); break;
case DOUBLE: x = float(pn_data_get_double(pn_object())); break;
default: assert_type_equal(FLOAT, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(double &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(DOUBLE, tid);
switch (tid) {
case FLOAT: x = static_cast<double>(pn_data_get_float(pn_object())); break;
case DOUBLE: x = pn_data_get_double(pn_object()); break;
default: assert_type_equal(DOUBLE, tid);
}
sg.cancel();
return *this;
}
decoder& decoder::operator>>(decimal32 &x) { return extract(x, pn_data_get_decimal32); }
decoder& decoder::operator>>(decimal64 &x) { return extract(x, pn_data_get_decimal64); }
decoder& decoder::operator>>(decimal128 &x) { return extract(x, pn_data_get_decimal128); }
decoder& decoder::operator>>(uuid &x) { return extract(x, pn_data_get_uuid); }
decoder& decoder::operator>>(binary &x) { return extract(x, pn_data_get_binary); }
decoder& decoder::operator>>(symbol &x) { return extract(x, pn_data_get_symbol); }
decoder& decoder::operator>>(std::string &x) {
internal::state_guard sg(*this);
type_id tid = pre_get();
if (exact_) assert_type_equal(STRING, tid);
switch (tid) {
case STRING: x = str(pn_data_get_string(pn_object())); break;
case SYMBOL: x = str(pn_data_get_symbol(pn_object())); break;
default: assert_type_equal(STRING, tid);
}
sg.cancel();
return *this;
}
} // codec
} // proton