blob: f775001c40f6da158ad2ef11022a4e24038d13ca [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/data.hpp"
#include "proton/decoder.hpp"
#include "proton/value.hpp"
#include "proton/message_id.hpp"
#include "proton/annotation_key.hpp"
#include "proton_bits.hpp"
#include "msg.hpp"
#include <proton/codec.h>
namespace proton {
/**@file
*
* Note the pn_data_t "current" node is always pointing *before* the next value
* to be returned by the decoder.
*
*/
namespace {
struct save_state {
pn_data_t* data;
pn_handle_t handle;
save_state(pn_data_t* d) : data(d), handle(pn_data_point(d)) {}
~save_state() { if (data) pn_data_restore(data, handle); }
void cancel() { data = 0; }
};
struct narrow {
data data_;
narrow(data d) : data_(d) { data_.narrow(); }
~narrow() { data_.widen(); }
};
template <class T> T check(T result) {
if (result < 0)
throw decode_error("" + error_str(result));
return result;
}
}
void decoder::decode(const char* i, size_t size) {
save_state ss(pn_object());
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& buffer) {
decode(buffer.data(), buffer.size());
}
bool decoder::more() const {
save_state ss(pn_object());
return pn_data_next(pn_object());
}
void decoder::rewind() { ::pn_data_rewind(pn_object()); }
void decoder::backup() { ::pn_data_prev(pn_object()); }
void decoder::skip() { ::pn_data_next(pn_object()); }
data decoder::data() { return proton::data(pn_object()); }
namespace {
void bad_type(type_id want, type_id got) {
if (want != got) throw type_error(want, got);
}
type_id pre_get(pn_data_t* data) {
if (!pn_data_next(data)) throw decode_error("no more data");
type_id t = type_id(pn_data_type(data));
if (t < 0) throw decode_error("invalid data");
return t;
}
// Simple extract with no type conversion.
template <class T, class U> void extract(pn_data_t* data, T& x, U (*get)(pn_data_t*)) {
save_state ss(data);
bad_type(type_id_of<T>::value, pre_get(data));
x = T(get(data));
ss.cancel(); // No error, no rewind
}
}
void decoder::check_type(type_id want) {
type_id got = type();
if (want != got) bad_type(want, got);
}
type_id decoder::type() const {
save_state ss(pn_object());
return pre_get(pn_object());
}
decoder operator>>(decoder d0, start& s) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
s.type = pre_get(d);
switch (s.type) {
case ARRAY:
s.size = pn_data_get_array(d);
s.element = type_id(pn_data_get_array_type(d)); s.is_described = pn_data_is_array_described(d);
break;
case LIST:
s.size = pn_data_get_list(d);
break;
case MAP:
s.size = pn_data_get_map(d);
break;
case DESCRIBED:
s.is_described = true;
s.size = 1;
break;
default:
throw decode_error(MSG("" << s.type << " is not a container type"));
}
pn_data_enter(d);
ss.cancel();
return d0;
}
decoder operator>>(decoder d, finish) { pn_data_exit(d.pn_object()); return d; }
decoder operator>>(decoder d, skip) { pn_data_next(d.pn_object()); return d; }
decoder operator>>(decoder d, assert_type a) { bad_type(a.type, d.type()); return d; }
decoder operator>>(decoder d, rewind) { d.rewind(); return d; }
decoder operator>>(decoder d, value& v) {
data ddata = d.data();
data vdata = v.encode().data();
if (d.data() == v.data_) throw decode_error("extract into self");
{
narrow n(ddata);
check(vdata.appendn(ddata, 1));
}
if (!ddata.next()) throw decode_error("no more data");
return d;
}
decoder operator>>(decoder d, message_id& x) {
switch (d.type()) {
case ULONG:
case UUID:
case BINARY:
case STRING:
return d >> x.scalar_;
default:
throw decode_error("expected one of ulong, uuid, binary or string but found " +
type_name(d.type()));
};
}
decoder operator>>(decoder d, annotation_key& x) {
switch (d.type()) {
case ULONG:
case SYMBOL:
return d >> x.scalar_;
default:
throw decode_error("expected one of ulong or symbol but found " + type_name(d.type()));
};
}
decoder operator>>(decoder d, amqp_null) {
save_state ss(d.pn_object());
bad_type(NULL_TYPE, pre_get(d.pn_object()));
return d;
}
decoder operator>>(decoder d, scalar& x) {
save_state ss(d.pn_object());
type_id got = pre_get(d.pn_object());
if (!type_id_is_scalar(got))
throw decode_error("expected scalar, found "+type_name(got));
x.set(pn_data_get_atom(d.pn_object()));
ss.cancel(); // No error, no rewind
return d;
}
decoder operator>>(decoder d, amqp_boolean &x) {
extract(d.pn_object(), x, pn_data_get_bool);
return d;
}
decoder operator>>(decoder d0, amqp_ubyte &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case UBYTE: x = pn_data_get_ubyte(d); break;
default: bad_type(UBYTE, type_id(type_id(pn_data_type(d))));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_byte &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case BYTE: x = pn_data_get_byte(d); break;
default: bad_type(BYTE, type_id(type_id(pn_data_type(d))));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_ushort &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case UBYTE: x = pn_data_get_ubyte(d); break;
case USHORT: x = pn_data_get_ushort(d); break;
default: bad_type(USHORT, type_id(type_id(pn_data_type(d))));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_short &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case BYTE: x = pn_data_get_byte(d); break;
case SHORT: x = pn_data_get_short(d); break;
default: bad_type(SHORT, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_uint &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case UBYTE: x = pn_data_get_ubyte(d); break;
case USHORT: x = pn_data_get_ushort(d); break;
case UINT: x = pn_data_get_uint(d); break;
default: bad_type(UINT, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_int &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case BYTE: x = pn_data_get_byte(d); break;
case SHORT: x = pn_data_get_short(d); break;
case INT: x = pn_data_get_int(d); break;
default: bad_type(INT, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_ulong &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case UBYTE: x = pn_data_get_ubyte(d); break;
case USHORT: x = pn_data_get_ushort(d); break;
case UINT: x = pn_data_get_uint(d); break;
case ULONG: x = pn_data_get_ulong(d); break;
default: bad_type(ULONG, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_long &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case BYTE: x = pn_data_get_byte(d); break;
case SHORT: x = pn_data_get_short(d); break;
case INT: x = pn_data_get_int(d); break;
case LONG: x = pn_data_get_long(d); break;
default: bad_type(LONG, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d, amqp_char &x) {
extract(d.pn_object(), x, pn_data_get_char);
return d;
}
decoder operator>>(decoder d, amqp_timestamp &x) {
extract(d.pn_object(), x, pn_data_get_timestamp);
return d;
}
decoder operator>>(decoder d0, amqp_float &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case FLOAT: x = pn_data_get_float(d); break;
case DOUBLE: x = float(pn_data_get_double(d)); break;
default: bad_type(FLOAT, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d0, amqp_double &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case FLOAT: x = pn_data_get_float(d); break;
case DOUBLE: x = pn_data_get_double(d); break;
default: bad_type(DOUBLE, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
decoder operator>>(decoder d, amqp_decimal32 &x) {
extract(d.pn_object(), x, pn_data_get_decimal32);
return d;
}
decoder operator>>(decoder d, amqp_decimal64 &x) {
extract(d.pn_object(), x, pn_data_get_decimal64);
return d;
}
decoder operator>>(decoder d, amqp_decimal128 &x) {
extract(d.pn_object(), x, pn_data_get_decimal128);
return d;
}
decoder operator>>(decoder d, amqp_uuid &x) {
extract(d.pn_object(), x, pn_data_get_uuid);
return d;
}
decoder operator>>(decoder d0, std::string &x) {
pn_data_t* d = d0.pn_object();
save_state ss(d);
switch (pre_get(d)) {
case STRING: x = str(pn_data_get_string(d)); break;
case BINARY: x = str(pn_data_get_binary(d)); break;
case SYMBOL: x = str(pn_data_get_symbol(d)); break;
default: bad_type(STRING, type_id(pn_data_type(d)));
}
ss.cancel();
return d0;
}
void assert_map_scope(const scope& s) {
if (s.type != MAP)
throw decode_error("cannot decode "+type_name(s.type)+" as map");
if (s.size % 2 != 0)
throw decode_error("odd number of elements in map");
}
}