blob: 3dab9c94f4875a9a8ef4323f30ee999fd01f8427 [file] [log] [blame]
#ifndef DECODER_H
#define DECODER_H
/*
* 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.
*/
/// @cond INTERNAL
/// XXX change namespace, review better
#include "proton/error.hpp"
#include "proton/type_traits.hpp"
#include "proton/types.hpp"
#include "proton/object.hpp"
#include <iosfwd>
#ifndef PN_NO_CONTAINER_CONVERT
#include <vector>
#include <deque>
#include <list>
#include <map>
#if PN_HAS_CPP11
#include <array>
#include <forward_list>
#include <unordered_map>
#endif // PN_HAS_CPP11
#endif // PN_NO_CONTAINER_CONVERT
struct pn_data_t;
namespace proton {
class scalar;
class data;
class message_id;
class annotation_key;
class value;
/** Skips a value with `dec >> skip()`. */
struct skip{};
/** Assert the next type of value in the decoder: `dec >> assert_type(t)`
* throws if decoder.type() != t
*/
struct assert_type {
type_id type;
assert_type(type_id t) : type(t) {}
};
/** Rewind the decoder with `dec >> rewind()`. */
struct rewind{};
/**
Stream-like decoder from AMQP bytes to C++ values.
@see types.hpp defines C++ types corresponding to AMQP types.
The decoder operator>> will extract AMQP types into any compatible C++
type or throw an exception if the types are not compatible.
+-------------------------+-------------------------------+
|AMQP type |Compatible C++ types |
+=========================+===============================+
|BOOLEAN |amqp_boolean, bool |
+-------------------------+-------------------------------+
|signed integer type I |C++ signed integer type T where|
| |sizeof(T) >= sizeof(I) |
+-------------------------+-------------------------------+
|unsigned integer type U |C++ unsigned integer type T |
| |where sizeof(T) >= sizeof(U) |
+-------------------------+-------------------------------+
|CHAR |amqp_char, wchar_t |
+-------------------------+-------------------------------+
|FLOAT |amqp_float, float |
+-------------------------+-------------------------------+
|DOUBLE |amqp_double, double |
+-------------------------+-------------------------------+
|STRING |amqp_string, std::string |
+-------------------------+-------------------------------+
|SYMBOL |amqp_symbol, std::string |
+-------------------------+-------------------------------+
|BINARY |amqp_binary, std::string |
+-------------------------+-------------------------------+
|DECIMAL32/64/128 |amqp_decimal32/64/128 |
+-------------------------+-------------------------------+
|TIMESTAMP |amqp_timestamp |
+-------------------------+-------------------------------+
|UUID |amqp_uuid |
+-------------------------+-------------------------------+
The special proton::value type can hold any AMQP type, simple or compound.
By default operator >> will do any conversion that does not lose data. For example
any AMQP signed integer type can be extracted as follows:
int64_t i;
dec >> i;
You can assert the exact AMQP type with proton::assert_type, for example
the following will throw if the AMQP type is not an AMQP INT (32 bits)
amqp_int i;
dec >> assert_type(INT) >> i; // Will throw if decoder does not contain an INT
You can extract AMQP ARRAY, LIST or MAP into standard C++ containers of compatible types, for example:
std::vector<int32_t> v;
dec >> v;
This will work if the decoder contains an AMQP ARRAY or LIST of SHORT or INT values. It won't work
for LONG or other types. It will also work for a MAP with keys and values that are SHORT OR INT,
the map will be "flattened" into a sequence [ key1, value1, key2, value2 ] This will work with
std::dequeue, std::array, std::list or std::forward_list.
You can extract a MAP into a std::map or std::unordered_map
std::map<std::string, std::string> v;
dec >> v;
This will work for any AMQP map with keys and values that are STRING, SYMBOL or BINARY.
If you have non-standard container types that meet the most basic requirements for
the container or associative-container concepts, you can use them via helper functions:
my_sequence_type<int64_t> s;
dec >> proton::to_sequence(s); // Decode sequence of integers
my_map_type<amqp_string, bool> s;
dec >> proton::to_map(s); // Decode map of string: bool.
Finally you can extract an AMQP LIST with mixed type elements into a container of proton::value, e.g.
std::vector<proton::value> v;
dec >> v;
You can also extract container values element-by-element, see decoder::operator>>(decoder&, start&)
*/
class decoder : public object<pn_data_t> {
public:
decoder(pn_data_t* d) : object<pn_data_t>(d) {}
/** Copy AMQP data from a byte buffer into the decoder. */
PN_CPP_EXTERN decoder(const char* buffer, size_t size);
/** Copy AMQP data from a std::string into the decoder. */
PN_CPP_EXTERN decoder(const std::string&);
/** Decode AMQP data from a byte buffer onto the end of the value stream. */
PN_CPP_EXTERN void decode(const char* buffer, size_t size);
/** Decode AMQP data from bytes in std::string onto the end of the value stream. */
PN_CPP_EXTERN void decode(const std::string&);
/** Return true if there are more values to read at the current level. */
PN_CPP_EXTERN bool more() const;
/** Type of the next value that will be read by operator>>
*@throw error if empty().
*/
PN_CPP_EXTERN type_id type() const;
/** Rewind to the start of the data. */
PN_CPP_EXTERN void rewind();
/** Skip one value */
PN_CPP_EXTERN void skip();
/** Back up by one value */
PN_CPP_EXTERN void backup();
PN_CPP_EXTERN class data data();
/** @name Extract simple types
* Overloads to extract simple types.
* @throw error if the decoder is empty or the current value has an incompatible type.
* @{
*/
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_null);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_boolean&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_ubyte&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_byte&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_ushort&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_short&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_uint&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_int&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_char&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_ulong&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_long&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_timestamp&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_float&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_double&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_decimal32&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_decimal64&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_decimal128&);
PN_CPP_EXTERN friend decoder operator>>(decoder, amqp_uuid&);
PN_CPP_EXTERN friend decoder operator>>(decoder, std::string&);
PN_CPP_EXTERN friend decoder operator>>(decoder, message_id&);
PN_CPP_EXTERN friend decoder operator>>(decoder, annotation_key&);
PN_CPP_EXTERN friend decoder operator>>(decoder, value&);
PN_CPP_EXTERN friend decoder operator>>(decoder, scalar&);
///@}
/** Extract and return a value of type T. */
template <class T> T extract() { T value; *this >> value; return value; }
/** start extracting a container value, one of array, list, map, described.
* The basic pattern is:
*
* start s;
* dec >> s;
* // check s.type() to see if this is an ARRAY, LIST, MAP or DESCRIBED type.
* if (s.described) extract the descriptor...
* for (size_t i = 0; i < s.size(); ++i) Extract each element...
* dec >> finish();
*
* The first value of an ARRAY is a descriptor if start::descriptor is true,
* followed by start.size elements of type start::element.
*
* A LIST has start.size elements which may be of mixed type.
*
* A MAP has start.size elements which alternate key, value, key, value...
* and may be of mixed type.
*
* A DESCRIBED contains a descriptor and a single element, so it always has
* start.described=true and start.size=1.
*
* You must always end a complex type by extracting to an instance of `finish`,
* the decoder::scope automates this.
*
*@throw decoder::error if the current value is not a container type.
*/
PN_CPP_EXTERN friend decoder operator>>(decoder, start&);
/** Finish extracting a container value. */
PN_CPP_EXTERN friend decoder operator>>(decoder, finish);
/** Skip a value */
PN_CPP_EXTERN friend decoder operator>>(decoder, struct skip);
/** Throw an exception if decoder.type() != assert_type.type */
PN_CPP_EXTERN friend decoder operator>>(decoder, assert_type);
/** Rewind to the beginning */
PN_CPP_EXTERN friend decoder operator>>(decoder, struct rewind);
private:
PN_CPP_EXTERN void check_type(type_id);
};
/** Call decoder::start() in constructor, decoder::finish in destructor().
*
*/
struct scope : public start {
decoder decoder_;
scope(decoder d) : decoder_(d) { decoder_ >> *this; }
~scope() { decoder_ >> finish(); }
};
// operator >> for integer types that are not covered by the standard overrides.
template <class T>
typename enable_if<is_unknown_integer<T>::value, decoder>::type
operator>>(decoder d, T& i) {
typename integer_type<sizeof(T), is_signed<T>::value>::type v;
d >> v; // Extract as a known integer type
i = v;
return d;
}
///@cond INTERNAL
template <class T> struct ref {
ref(T& v) : value(v) {}
T& value;
};
template <class T> struct sequence_ref : public ref<T> { sequence_ref(T& v) : ref<T>(v) {} };
template <class T> struct map_ref : public ref<T> { map_ref(T& v) : ref<T>(v) {} };
template <class T> struct pairs_ref : public ref<T> { pairs_ref(T& v) : ref<T>(v) {} };
///@endcond
/**
* Return a wrapper for a C++ container to be decoded as a sequence. The AMQP
* ARRAY, LIST, and MAP types can all be decoded as a sequence, a map will be
* decoded as alternating key and value (provided they can both be converted to
* the container's value_type)
*
* The following expressions must be valid for T t;
* T::iterator
* t.clear()
* t.resize()
* t.begin()
* t.end()
*/
template <class T> sequence_ref<T> to_sequence(T& v) { return sequence_ref<T>(v); }
/** Return a wrapper for a C++ map container to be decoded from an AMQP MAP.
* The following expressions must be valid for T t;
* T::key_type
* T::mapped_type
* t.clear()
* T::key_type k; T::mapped_type v; t[k] = v;
*/
template <class T> map_ref<T> to_map(T& v) { return map_ref<T>(v); }
/** Return a wrapper for a C++ container of std::pair that can be decoded from AMQP maps,
* preserving the encoded map order.
*
* The following expressions must be valid for T t;
* T::iterator
* t.clear()
* t.resize()
* t.begin()
* t.end()
* T::iterator i; i->first; i->second
*/
template <class T> pairs_ref<T> to_pairs(T& v) { return pairs_ref<T>(v); }
/** Extract any AMQP sequence (ARRAY, LIST or MAP) to a C++ container of T if
* the elements types are convertible to T. A MAP is extracted as [key1, value1,
* key2, value2...]
*/
template <class T> decoder operator>>(decoder d, sequence_ref<T> ref) {
scope s(d);
if (s.is_described) d >> skip();
T& v = ref.value;
v.clear();
v.resize(s.size);
for (typename T::iterator i = v.begin(); i != v.end(); ++i)
d >> *i;
return d;
}
PN_CPP_EXTERN void assert_map_scope(const scope& s);
/** Extract an AMQP MAP to a C++ map */
template <class T> decoder operator>>(decoder d, map_ref<T> ref) {
scope s(d);
assert_map_scope(s);
T& m = ref.value;
m.clear();
for (size_t i = 0; i < s.size/2; ++i) {
typename remove_const<typename T::key_type>::type k;
typename remove_const<typename T::mapped_type>::type v;
d >> k >> v;
m[k] = v;
}
return d;
}
/** Extract an AMQP MAP to a C++ container of std::pair, preserving order. */
template <class T> decoder operator>>(decoder d, pairs_ref<T> ref) {
scope s(d);
assert_map_scope(s);
T& m = ref.value;
m.clear();
m.resize(s.size/2);
for (typename T::iterator i = m.begin(); i != m.end(); ++i) {
d >> i->first >> i->second;
}
return d;
}
#ifndef PN_NO_CONTAINER_CONVERT
// Decode to sequence.
template <class T, class A> decoder operator>>(decoder d, std::vector<T, A>& v) { return d >> to_sequence(v); }
template <class T, class A> decoder operator>>(decoder d, std::deque<T, A>& v) { return d >> to_sequence(v); }
template <class T, class A> decoder operator>>(decoder d, std::list<T, A>& v) { return d >> to_sequence(v); }
// Decode to map.
template <class K, class T, class C, class A> decoder operator>>(decoder d, std::map<K, T, C, A>& v) { return d >> to_map(v); }
#if PN_HAS_CPP11
// Decode to sequence.
template <class T, class A> decoder operator>>(decoder d, std::forward_list<T, A>& v) { return d >> to_sequence(v); }
template <class T, std::size_t N> decoder operator>>(decoder d, std::array<T, N>& v) { return d >> to_sequence(v); }
// Decode to map.
template <class K, class T, class C, class A> decoder operator>>(decoder d, std::unordered_map<K, T, C, A>& v) { return d >> to_map(v); }
#endif // PN_HAS_CPP11
#endif // PN_NO_CONTAINER_CONVERT
}
/// @endcond
#endif // DECODER_H