diff options
-rw-r--r-- | qpid/cpp/src/qpid/Serializer.h | 1 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/Codec.h | 9 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/PackedCodec.h | 79 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/Packer.h | 148 | ||||
-rw-r--r-- | qpid/cpp/src/tests/amqp_0_10/serialize.cpp | 62 |
5 files changed, 204 insertions, 95 deletions
diff --git a/qpid/cpp/src/qpid/Serializer.h b/qpid/cpp/src/qpid/Serializer.h index 6b67a4ccf8..2660fb2b3e 100644 --- a/qpid/cpp/src/qpid/Serializer.h +++ b/qpid/cpp/src/qpid/Serializer.h @@ -167,6 +167,7 @@ template <class Derived> class DecoderBase : public Serializer<Derived> { /** Default op() for non-primitive types. */ template <class T> Derived& operator()(T& t) { + serializable(t).serialize(self()); return self(); } diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codec.h b/qpid/cpp/src/qpid/amqp_0_10/Codec.h index 622032fddc..c4909e7cbd 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/Codec.h +++ b/qpid/cpp/src/qpid/amqp_0_10/Codec.h @@ -54,16 +54,14 @@ struct Codec { public: typedef EncoderBase<Encoder<OutIter> > Base; typedef OutIter Iterator; - + Encoder(OutIter o, size_t limit=Base::maxLimit()) : out(o) { this->setLimit(limit); } using EncoderBase<Encoder<OutIter> >::operator(); - // FIXME aconway 2008-03-10: wrong encoding, need packing support Encoder& operator()(bool x) { raw(x); return *this;} - Encoder& operator()(char x) { raw(x); return *this; } Encoder& operator()(int8_t x) { raw(x); return *this; } Encoder& operator()(uint8_t x) { raw(x); return *this; } @@ -79,7 +77,6 @@ struct Codec { Encoder& operator()(float x) { return endian(x); } Encoder& operator()(double x) { return endian(x); } - void raw(const void* p, size_t n) { this->addBytes(n); out = std::copy((const char*)p, (const char*)p+n, out); @@ -103,7 +100,7 @@ struct Codec { public: typedef DecoderBase<Decoder<InIter> > Base; typedef InIter Iterator; - + Decoder(InIter i, size_t limit=Base::maxLimit()) : in(i) { this->setLimit(limit); } @@ -135,7 +132,7 @@ struct Codec { } void raw(char &b) { this->addBytes(1); b=*in++; } - + InIter pos() const { return in; } private: diff --git a/qpid/cpp/src/qpid/amqp_0_10/PackedCodec.h b/qpid/cpp/src/qpid/amqp_0_10/PackedCodec.h deleted file mode 100644 index 408c20cb0b..0000000000 --- a/qpid/cpp/src/qpid/amqp_0_10/PackedCodec.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef QPID_AMQP_0_10_PACKEDCODEC_H -#define QPID_AMQP_0_10_PACKEDCODEC_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. - * - */ - -#include "Codec.h" - -namespace qpid { -namespace amqp_0_10 { - -/** - * Packed encoding/decoding. Process or ignore fields based on - * packing bits. - */ -struct PackedCodec { - template <class Bits, class InnerDecoder> - class Decoder : - public DecoderBase<PackedCodec::Decoder<Bits, InnerDecoder> > - { - public: - Decoder(Bits b, const InnerDecoder& d) - : bits(b), decode(const_cast<InnerDecoder&>(d)) {} - - // Decode if pack bit is set. - template<class T> Decoder& operator()(T& t) { - if (bits & 1) - decode(t); - else - t = T(); // FIXME aconway 2008-04-02: see below - bits >>= 1; - return *this; - } - Bits bits; - InnerDecoder& decode; - }; - - template <class Bits, class InnerDecoder> - static Decoder<Bits, InnerDecoder> decode(Bits b, const InnerDecoder& d) { - return Decoder<Bits, InnerDecoder>(b,d); - } - - // FIXME aconway 2008-04-02: Incorrect packed semantics. - // Current implementation is: - // - decode value if packed bit set, else default initialize value. - // - encode always encode values - // - size count all values. - // Correct implementation: - // - optional value of type T is mapped to boost::optional<T> - // - decode value if packed bit set, else value=boost::none - // - PackedCodec::Encoder collect packing bits (1 unless == boost::none) - // Codec::Encoder optional skip if none - // - size: count only non-none values. - // Note we don't encode/decodde the pack bits themselves here, that - // happens in the Holder. Holders handle size, count & pack attributes. -}; - - -}} // namespace qpid::amqp_0_10 - -#endif /*!QPID_AMQP_0_10_PACKEDCODEC_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Packer.h b/qpid/cpp/src/qpid/amqp_0_10/Packer.h new file mode 100644 index 0000000000..1005ef34aa --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Packer.h @@ -0,0 +1,148 @@ +#ifndef QPID_PACKER_H +#define QPID_PACKER_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. + * + */ + +#include <boost/optional.hpp> + +namespace qpid { +namespace amqp_0_10 { + +/** Serialization for optional values */ +template <class T> struct SerializableOptional { + boost::optional<T>& optional; + SerializableOptional(boost::optional<T>& x) : optional(x) {} + template <class S> void serialize(S& s) { + if (optional) + s(*optional); + } +}; + +}} + + +namespace boost { // For argument dependent lookup. + +template <class T> +qpid::amqp_0_10::SerializableOptional<T> serializable(boost::optional<T>& x) { + return qpid::amqp_0_10::SerializableOptional<T>(x); +} + +} // namespace boost + +namespace qpid { +namespace amqp_0_10 { + +/** "Encoder" that encodes a struct as a set of bit flags + * for all non-empty members. + */ +class PackBits { + public: + PackBits() : bit(1), bits(0) {} + + void setBit() { bits |= bit; bit <<= 1; } + void skipBit() { bit <<= 1; } + + uint32_t getBits() { return bits; } + + template <class T> PackBits& operator()(const T&) { setBit(); return *this; } + + template <class T> PackBits& operator()(const boost::optional<T>& opt) { + opt ? setBit() : skipBit(); return *this; + } + + private: + uint32_t bit; + uint32_t bits; +}; + +/** Bit mask to encode a packable struct */ +template<class T> uint32_t packBits(const T& t) { + PackBits pack; + const_cast<T&>(t).serialize(pack); + return pack.getBits(); +} + +template <class Decoder, class Bits> +class PackedDecoder { + public: + PackedDecoder(Decoder& d, Bits b) : decode(d), bits(b) {} + + template <class T> PackedDecoder& operator()(T& t) { decode(t); return *this; } + + template <class T> PackedDecoder& operator()(boost::optional<T>& opt) { + if (bits & 1) { + opt = T(); + decode(*opt); + } + else + opt = boost::none; + bits >>= 1; + return *this; + } + + private: + Decoder& decode; + Bits bits; +}; + +/** Metafunction to compute type to contain pack bits. */ +template <int PackBytes> struct PackBitsType; +template <> struct PackBitsType<1> { typedef uint8_t type; }; +template <> struct PackBitsType<2> { typedef uint16_t type; }; +template <> struct PackBitsType<4> { typedef uint32_t type; }; + +/** + * Helper to serialize packed structs. + */ +template <class T> class Packer +{ + public: + typedef typename PackBitsType<T::PACK>::type Bits; + + Packer(T& t) : data(t) {} + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + Bits bits = packBits(data); + s(bits); + data.serialize(s); + } + + template <class S> void decode(S& s) { + Bits bits; + s(bits); + PackedDecoder<S, Bits> decode(s, bits); + data.serialize(decode); + } + + + private: + T& data; +}; + +}} // namespace qpid::amqp_0_10 + + + +#endif /*!QPID_PACKER_H*/ diff --git a/qpid/cpp/src/tests/amqp_0_10/serialize.cpp b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp index a6cb28caee..dcf27e457a 100644 --- a/qpid/cpp/src/tests/amqp_0_10/serialize.cpp +++ b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp @@ -20,9 +20,9 @@ */ #include "unit_test.h" +#include "qpid/amqp_0_10/Packer.h" #include "qpid/amqp_0_10/built_in_types.h" #include "qpid/amqp_0_10/Codec.h" -#include "qpid/amqp_0_10/PackedCodec.h" #include "qpid/amqp_0_10/specification.h" #include "qpid/amqp_0_10/ControlHolder.h" #include "qpid/amqp_0_10/Frame.h" @@ -31,12 +31,14 @@ #include <boost/test/test_case_template.hpp> #include <boost/type_traits/is_arithmetic.hpp> #include <boost/utility/enable_if.hpp> +#include <boost/optional.hpp> #include <boost/mpl/vector.hpp> #include <boost/mpl/back_inserter.hpp> #include <boost/mpl/copy.hpp> #include <boost/mpl/empty_sequence.hpp> #include <iterator> #include <string> +#include <sstream> #include <iostream> #include <netinet/in.h> @@ -254,15 +256,55 @@ BOOST_AUTO_TEST_CASE(testFrameEncodeDecode) { } -BOOST_AUTO_TEST_CASE(testPackedCodec) { - int i=-1, j=-1, k=-1, l=-1; - std::string data; - Codec::encode(std::back_inserter(data))(1)(3); - PackedCodec::decode(0x5, Codec::decode(data.begin()))(i)(j)(k)(l); - BOOST_CHECK_EQUAL(i, 1); - BOOST_CHECK_EQUAL(j, 0); - BOOST_CHECK_EQUAL(k, 3); - BOOST_CHECK_EQUAL(l, 0); +struct DummyPacked { + static const uint8_t PACK=1; + boost::optional<char> i, j; + char k; + DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c) {} + template <class S> void serialize(S& s) { s(i)(j)(k); } + + string str() const { + ostringstream os; + os << i << j << k; + return os.str(); + } +}; + +Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); } + +BOOST_AUTO_TEST_CASE(testPackBits) { + DummyPacked d('a','b','c'); + BOOST_CHECK_EQUAL(packBits(d), 7u); + d.j = boost::none; + BOOST_CHECK_EQUAL(packBits(d), 5u); +} + + +BOOST_AUTO_TEST_CASE(testPacked) { + string data; + + Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c'); + BOOST_CHECK_EQUAL(data, "abc"); + data.clear(); + + DummyPacked dummy('a','b','c'); + + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data.size(), 4u); + BOOST_CHECK_EQUAL(data, string("\007abc")); + data.clear(); + + dummy.i = boost::none; + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data, string("\6bc")); + data.clear(); + + const char* missing = "\5xy"; + Codec::decode(missing)(dummy); + BOOST_CHECK(dummy.i); + BOOST_CHECK_EQUAL(dummy.i, 'x'); + BOOST_CHECK(!dummy.j); + BOOST_CHECK_EQUAL(dummy.k, 'y'); } QPID_AUTO_TEST_SUITE_END() |