blob: bdc22086e1cd057894db5dcc7964033a9508959f [file] [log] [blame]
#ifndef PROTON_CODEC_DECODER_HPP
#define PROTON_CODEC_DECODER_HPP
/*
*
* 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 "./data.hpp"
#include "../internal/type_traits.hpp"
#include "../types_fwd.hpp"
#include <utility>
namespace proton {
class annotation_key;
class message_id;
class scalar;
class value;
namespace internal {
class value_base;
}
namespace codec {
/// **Experimental** - Stream-like decoder from AMQP bytes to C++
/// values.
///
/// For internal use only.
///
/// @see @ref types_page for the recommended ways to manage AMQP data
class decoder : public data {
public:
/// Wrap a Proton C data object. The exact flag if set means
/// decode only when there is an exact match between the AMQP and
/// C++ type. If not set then perform automatic conversions.
explicit decoder(const data& d, bool exact=false) : data(d), exact_(exact) {}
/// Attach decoder to a proton::value. The decoder is rewound to
/// the start of the data.
PN_CPP_EXTERN explicit decoder(const internal::value_base&, bool exact=false);
/// Decode AMQP data from a buffer and add it to the end of the
/// decoders stream.
PN_CPP_EXTERN void decode(const char* buffer, size_t size);
/// Decode AMQP data from a std::string and add it to the end of
/// the decoders stream.
PN_CPP_EXTERN void decode(const std::string&);
/// Return true if there are more value to extract at the current level.
PN_CPP_EXTERN bool more();
/// Get the type of the next value that will be read by
/// operator>>.
///
/// @throw conversion_error if no more values. @see
/// decoder::more().
PN_CPP_EXTERN type_id next_type();
/// @name Extract built-in types
///
/// @throw conversion_error if the decoder is empty or has an
/// incompatible type.
///
/// @{
PN_CPP_EXTERN decoder& operator>>(bool&);
PN_CPP_EXTERN decoder& operator>>(uint8_t&);
PN_CPP_EXTERN decoder& operator>>(int8_t&);
PN_CPP_EXTERN decoder& operator>>(uint16_t&);
PN_CPP_EXTERN decoder& operator>>(int16_t&);
PN_CPP_EXTERN decoder& operator>>(uint32_t&);
PN_CPP_EXTERN decoder& operator>>(int32_t&);
PN_CPP_EXTERN decoder& operator>>(wchar_t&);
PN_CPP_EXTERN decoder& operator>>(uint64_t&);
PN_CPP_EXTERN decoder& operator>>(int64_t&);
PN_CPP_EXTERN decoder& operator>>(timestamp&);
PN_CPP_EXTERN decoder& operator>>(float&);
PN_CPP_EXTERN decoder& operator>>(double&);
PN_CPP_EXTERN decoder& operator>>(decimal32&);
PN_CPP_EXTERN decoder& operator>>(decimal64&);
PN_CPP_EXTERN decoder& operator>>(decimal128&);
PN_CPP_EXTERN decoder& operator>>(uuid&);
PN_CPP_EXTERN decoder& operator>>(std::string&);
PN_CPP_EXTERN decoder& operator>>(symbol&);
PN_CPP_EXTERN decoder& operator>>(binary&);
PN_CPP_EXTERN decoder& operator>>(message_id&);
PN_CPP_EXTERN decoder& operator>>(annotation_key&);
PN_CPP_EXTERN decoder& operator>>(scalar&);
PN_CPP_EXTERN decoder& operator>>(internal::value_base&);
PN_CPP_EXTERN decoder& operator>>(null&);
///@}
/// Start decoding a container type, such as an ARRAY, LIST or
/// MAP. This "enters" the container, more() will return false at
/// the end of the container. Call finish() to "exit" the
/// container and move on to the next value.
PN_CPP_EXTERN decoder& operator>>(start&);
/// Finish decoding a container type, and move on to the next
/// value in the stream.
PN_CPP_EXTERN decoder& operator>>(const finish&);
/// @cond INTERNAL
template <class T> struct sequence_ref { T& ref; sequence_ref(T& r) : ref(r) {} };
template <class T> struct associative_ref { T& ref; associative_ref(T& r) : ref(r) {} };
template <class T> struct pair_sequence_ref { T& ref; pair_sequence_ref(T& r) : ref(r) {} };
template <class T> static sequence_ref<T> sequence(T& x) { return sequence_ref<T>(x); }
template <class T> static associative_ref<T> associative(T& x) { return associative_ref<T>(x); }
template <class T> static pair_sequence_ref<T> pair_sequence(T& x) { return pair_sequence_ref<T>(x); }
/// @endcond
/// Extract any AMQP sequence (ARRAY, LIST or MAP) to a C++
/// sequence 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>>(sequence_ref<T> r) {
start s;
*this >> s;
if (s.is_described) next();
r.ref.resize(s.size);
for (typename T::iterator i = r.ref.begin(); i != r.ref.end(); ++i)
*this >> *i;
return *this;
}
/// Extract an AMQP MAP to a C++ associative container
template <class T> decoder& operator>>(associative_ref<T> r) {
using namespace internal;
start s;
*this >> s;
assert_type_equal(MAP, s.type);
r.ref.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;
*this >> k >> v;
r.ref[k] = v;
}
return *this;
}
/// Extract an AMQP MAP to a C++ push_back sequence of pairs
/// preserving encoded order.
template <class T> decoder& operator>>(pair_sequence_ref<T> r) {
using namespace internal;
start s;
*this >> s;
assert_type_equal(MAP, s.type);
r.ref.clear();
for (size_t i = 0; i < s.size/2; ++i) {
typedef typename T::value_type value_type;
typename remove_const<typename value_type::first_type>::type k;
typename remove_const<typename value_type::second_type>::type v;
*this >> k >> v;
r.ref.push_back(value_type(k, v));
}
return *this;
}
private:
type_id pre_get();
template <class T, class U> decoder& extract(T& x, U (*get)(pn_data_t*));
bool exact_;
friend class message;
};
/// @cond INTERNAL
/// XXX Document this
template<class T> T get(decoder& d) {
assert_type_equal(internal::type_id_of<T>::value, d.next_type());
T x;
d >> x;
return x;
}
/// @endcond
/// operator>> for integer types that are not covered by the standard
/// overrides.
template <class T> typename internal::enable_if<internal::is_unknown_integer<T>::value, decoder&>::type
operator>>(decoder& d, T& i) {
using namespace internal;
typename integer_type<sizeof(T), is_signed<T>::value>::type v;
d >> v; // Extract as a known integer type
i = v; // C++ conversion to the target type.
return d;
}
} // codec
} // proton
#endif // PROTON_CODEC_DECODER_HPP