From c6bd8565ecc054fa3a393f7df04b1d44836c3a79 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 2 Apr 2008 17:56:14 +0000 Subject: Encoding/decoding for new types: amqp_0_10::Map, amqp_0_10:UnknownType git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@643995 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/rubygen/0-10/specification.rb | 1 + qpid/cpp/rubygen/0-10/typecode.rb | 85 +++++++++++ qpid/cpp/rubygen/cppgen.rb | 11 +- qpid/cpp/src/Makefile.am | 5 + qpid/cpp/src/qpid/Serializer.h | 10 ++ qpid/cpp/src/qpid/amqp_0_10/Codec.h | 12 +- qpid/cpp/src/qpid/amqp_0_10/Map.cpp | 68 +++++++++ qpid/cpp/src/qpid/amqp_0_10/Map.h | 179 +++++++++++++++++++++++ qpid/cpp/src/qpid/amqp_0_10/SerializableString.h | 62 ++++++++ qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp | 56 +++++++ qpid/cpp/src/qpid/amqp_0_10/UnknownType.h | 86 +++++++++++ qpid/cpp/src/qpid/amqp_0_10/built_in_types.h | 80 +++++----- qpid/cpp/src/tests/Makefile.am | 3 +- qpid/cpp/src/tests/amqp_0_10/Map.cpp | 99 +++++++++++++ qpid/cpp/src/tests/serialize.cpp | 15 +- 15 files changed, 716 insertions(+), 56 deletions(-) create mode 100755 qpid/cpp/rubygen/0-10/typecode.rb create mode 100644 qpid/cpp/src/qpid/amqp_0_10/Map.cpp create mode 100644 qpid/cpp/src/qpid/amqp_0_10/Map.h create mode 100644 qpid/cpp/src/qpid/amqp_0_10/SerializableString.h create mode 100644 qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp create mode 100644 qpid/cpp/src/qpid/amqp_0_10/UnknownType.h create mode 100644 qpid/cpp/src/tests/amqp_0_10/Map.cpp (limited to 'qpid/cpp') diff --git a/qpid/cpp/rubygen/0-10/specification.rb b/qpid/cpp/rubygen/0-10/specification.rb index a11abdb877..dbb05fb752 100755 --- a/qpid/cpp/rubygen/0-10/specification.rb +++ b/qpid/cpp/rubygen/0-10/specification.rb @@ -117,6 +117,7 @@ class Specification < CppGen h_file("#{@dir}/specification") { include "#{@dir}/built_in_types" include "#{@dir}/complex_types" + include "#{@dir}/Map.h" include "" include "" genl "using boost::call_traits;" diff --git a/qpid/cpp/rubygen/0-10/typecode.rb b/qpid/cpp/rubygen/0-10/typecode.rb new file mode 100755 index 0000000000..459516e51c --- /dev/null +++ b/qpid/cpp/rubygen/0-10/typecode.rb @@ -0,0 +1,85 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class TypeCode < CppGen + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_#{@amqp.version.bars}" + @dir="qpid/amqp_#{@amqp.version.bars}" + @types = @amqp.collect_all(AmqpType).select { |t| t.code } + + end + + def type_for_code_h() + h_file("#{@dir}/TypeForCode") { + include "#{@dir}/UnknownType.h" + namespace(@ns) { + genl + genl "template struct TypeForCode;" + genl + @types.each { |t| + genl "template <> struct TypeForCode<#{t.code}> { typedef #{t.typename} type; };" + } + genl + genl "template typename V::result_type" + scope("apply_visitor(V& visitor, uint8_t code) {") { + scope("switch (code) {", "}") { + @types.each { |t| + genl "case #{t.code}: return visitor((#{t.typename}*)0);" + } + genl "default: return visitor((UnknownType*)0);" + } + } + genl + genl "std::string typeName(uint8_t code);" + } + } + end + + def type_for_code_cpp() + cpp_file("#{@dir}/TypeForCode") { + include "" + include "" + namespace(@ns) { + namespace("") { + struct("Names") { + scope("Names() {") { + scope("for (int i =0; i < 256; ++i) {") { + genl "std::ostringstream os;" + genl "os << \"UnknownType<\" << i << \">\";" + genl "names[i] = os.str();" + } + @types.each { |t| genl "names[#{t.code}] = \"#{t.name}\";" } + } + genl "std::string names[256];" + } + genl "Names names;" + } + genl "std::string typeName(uint8_t code) { return names.names[code]; }" + }} + end + + def code_for_type_h() + h_file("#{@dir}/CodeForType") { + namespace(@ns) { + genl + genl "template struct CodeForType;" + genl + @types.each { |t| + genl "template <> struct CodeForType<#{t.typename}> { static const uint8_t value=#{t.code}; };" + } + genl + genl "template uint8_t codeFor(const T&) { return CodeForType::value; }" + }} + end + + def generate + type_for_code_h + type_for_code_cpp + code_for_type_h + end +end + +TypeCode.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/cppgen.rb b/qpid/cpp/rubygen/cppgen.rb index 63a2e6857e..d0f31a8ba8 100755 --- a/qpid/cpp/rubygen/cppgen.rb +++ b/qpid/cpp/rubygen/cppgen.rb @@ -57,7 +57,6 @@ class String def cppsafe() CppMangle.include?(self) ? self+"_" : self; end def amqp2cpp() - throw 'Invalid "array".amqp2cpp' if self=="array" path=split(".") name=path.pop return name.typename if path.empty? @@ -116,7 +115,7 @@ end class AmqpElement # convert my amqp type_ attribute to a C++ type. def amqp2cpp() - return "Array<#{ArrayTypes[name].amqp2cpp}> " if type_=="array" + return "ArrayDomain<#{ArrayTypes[name].amqp2cpp}> " if type_=="array" return type_.amqp2cpp end end @@ -166,6 +165,11 @@ class AmqpAction include AmqpHasFields end +class AmqpType + def typename() name.typename; end + def fixed?() fixed_width; end +end + class AmqpCommand < AmqpAction def base() "Command"; end end @@ -281,10 +285,11 @@ class CppGen < Generator genl names = name.split("::") names.each { |n| genl "namespace #{n} {" } + genl "namespace {" if (names.empty?) genl yield genl - genl('}'*names.size+" // namespace "+name) + genl('}'*([names.size, 1].max)+" // namespace "+name) genl end diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 72f63ae48e..05efcb2a71 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -113,8 +113,13 @@ libqpidcommon_la_SOURCES = \ qpid/amqp_0_10/Frame.h \ qpid/amqp_0_10/Segment.h \ qpid/amqp_0_10/Segment.cpp \ + qpid/amqp_0_10/SerializableString.h \ qpid/amqp_0_10/Assembly.h \ qpid/amqp_0_10/Assembly.cpp \ + qpid/amqp_0_10/Map.h \ + qpid/amqp_0_10/Map.cpp \ + qpid/amqp_0_10/UnknownType.h \ + qpid/amqp_0_10/UnknownType.cpp \ qpid/Serializer.h \ qpid/framing/AccumulatedAck.cpp \ qpid/framing/AMQBody.cpp \ diff --git a/qpid/cpp/src/qpid/Serializer.h b/qpid/cpp/src/qpid/Serializer.h index e0e29e24fd..12bcded0f7 100644 --- a/qpid/cpp/src/qpid/Serializer.h +++ b/qpid/cpp/src/qpid/Serializer.h @@ -28,6 +28,16 @@ namespace qpid { namespace serialize { +/** Wrapper to pass serializer functors by reference. */ +template struct SRef { + S& s; + SRef(S& ss) : s(ss) {} + template typename S::result_type operator()(T& x) { return s(x); } + template typename S::result_type operator()(const T& x) { return s(x); } +}; + +template SRef ref(S& s) { return SRef(s); } + // FIXME aconway 2008-03-03: Document. // Encoder/Decoder concept: add op() for primitive types, raw(), // op()(Iter, Iter). Note split, encode, decode. diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codec.h b/qpid/cpp/src/qpid/amqp_0_10/Codec.h index 04112cc4d1..138de8c96e 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/Codec.h +++ b/qpid/cpp/src/qpid/amqp_0_10/Codec.h @@ -46,7 +46,6 @@ template void endianize(T&) {} * AMQP 0-10 encoding and decoding. */ struct Codec { - // FIXME aconway 2008-02-29: drop this wrapper, rename to // IteratorEncoder, IteratorDecoder? @@ -79,7 +78,7 @@ struct Codec { template Encode& operator()(Iter begin, Iter end) { - std::for_each(begin, end, *this); + std::for_each(begin, end, serialize::ref(*this)); return *this; } @@ -125,7 +124,7 @@ struct Codec { Decode& operator()(double& x) { return endian(x); } template Decode& operator()(Iter begin, Iter end) { - std::for_each(begin, end, *this); + std::for_each(begin, end, serialize::ref(*this)); return *this; } @@ -172,9 +171,10 @@ struct Codec { Size& operator()(float x) { size += sizeof(x); return *this; } Size& operator()(double x) { size += sizeof(x); return *this; } - template - Size& operator()(const Iter& a, const Iter& b) { - size += (b-a)*sizeof(*a); + // FIXME aconway 2008-04-02: enable-if optimized (iter,iter) for + // iter on fixed-size type. + template Size& operator()(Iter begin, Iter end) { + std::for_each(begin, end, serialize::ref(*this)); return *this; } diff --git a/qpid/cpp/src/qpid/amqp_0_10/Map.cpp b/qpid/cpp/src/qpid/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..1bfc2f6c85 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Map.cpp @@ -0,0 +1,68 @@ +/* + * + * 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 "Map.h" +#include + +namespace qpid { +namespace amqp_0_10 { + +MapValue::MapValue() : code(codeFor(uint8_t(0))), blob(in_place(0)) {} + +MapValue::MapValue(const MapValue& x) : code(x.code), blob(x.blob) {} + +bool MapValue::operator==(const MapValue& x) const { + return code == x.code; // FIXME aconway 2008-04-01: incomplete +} + +struct OstreamVisitor : public MapValue::Visitor { + std::ostream& out; + OstreamVisitor(std::ostream& o) : out(o) {} + template std::ostream& operator()(const T& t) { + return out << t; + } +}; + +std::ostream& operator<<(std::ostream& o, const MapValue& m) { + o << typeName(m.getCode()) << ":"; + const_cast(m).apply_visitor(OstreamVisitor(o)); + return o; +} + +std::ostream& operator<<(std::ostream& o, const Map::value_type& v) { + return o << v.first << "=" << v.second; +} +std::ostream& operator<<(std::ostream& o, const Map& map) { + o << "map["; + std::ostream_iterator i(o, " "); + std::copy(map.begin(), map.end(), i); + return o << "]"; +} + +uint32_t Map::contentSize() const { + uint32_t result=4; // uint32_t count + for (const_iterator i=begin(); i != end(); ++i) { + result += Codec::size(i->first); + result += Codec::size(i->second); + } + return result; +} + +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/Map.h b/qpid/cpp/src/qpid/amqp_0_10/Map.h new file mode 100644 index 0000000000..ce8cd4bd08 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Map.h @@ -0,0 +1,179 @@ +#ifndef QPID_AMQP_0_10_MAP_H +#define QPID_AMQP_0_10_MAP_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 "qpid/Exception.h" +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/CodeForType.h" +#include "qpid/amqp_0_10/TypeForCode.h" +#include "qpid/amqp_0_10/Codec.h" +#include "qpid/framing/Blob.h" +#include +#include +#include + +namespace qpid { +namespace amqp_0_10 { + +class Map; + +class MapValue { + public: + struct BadTypeException : public Exception {}; + + template struct Visitor { typedef R result_type; }; + + MapValue(); + MapValue(const MapValue& x); + template explicit MapValue(const T& t); + template MapValue& operator=(const T& t); + + template T* get(); + template const T* get() const; + + template typename V::result_type apply_visitor(V&); + template typename V::result_type apply_visitor(const V&); + + uint8_t getCode() const { return code; } + + bool operator==(const MapValue&) const; + + template void serialize(S& s) { s(code); s.split(*this); } + template void encode(S& s) const { + const_cast(this)->apply_visitor(s); + } + template void decode(S& s) { + DecodeVisitor dv(blob, s); + qpid::amqp_0_10::apply_visitor(dv, code); + } + + + private: + static const size_t SIZE=128 < sizeof(Vbin32) ? sizeof(Vbin32) : 128; + typedef framing::Blob Blob; + + template struct VisitVisitor; + template struct GetVisitor; + template struct DecodeVisitor; + + uint8_t code; + Blob blob; +}; + +class Map : public std::map { + public: + template void serialize(S& s) { s.split(*this); } + template void encode(S& s) const; + + // FIXME aconway 2008-04-02: better separation for size calcultion + // support for static size, optimized iterator size calc. + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template void decode(S& s); + + private: + uint32_t contentSize() const; +}; + +std::ostream& operator<<(std::ostream&, const MapValue&); +std::ostream& operator<<(std::ostream&, const Map::value_type&); +std::ostream& operator<<(std::ostream&, const Map&); + +using framing::in_place; + +template MapValue::MapValue(const T& t) : code(codeFor(t)), blob(in_place()) {} + +template MapValue& MapValue::operator=(const T& t) { + code=codeFor(t); + blob=t; + return *this; +} + +template struct MapValue::VisitVisitor { + typedef typename V::result_type result_type; + V& visitor; + Blob& blob; + VisitVisitor(V& v, Blob& b) : visitor(v), blob(b) {} + + template result_type operator()(T*) { + return visitor(*reinterpret_cast(blob.get())); + } +}; + +template typename V::result_type MapValue::apply_visitor(V& v) { + VisitVisitor visitor(v, blob); + return qpid::amqp_0_10::apply_visitor(visitor, code); +} + +template struct MapValue::GetVisitor { + typedef R* result_type; + const MapValue::Blob& blob; + + GetVisitor(const MapValue::Blob& b) : blob(b) {} + + R* operator()(R& r) { return &r; } + template R* operator()(T&) { return 0; } +}; + +template struct MapValue::DecodeVisitor { + typedef void result_type; + MapValue::Blob& blob; + D& decoder; + DecodeVisitor(Blob& b, D& d) : blob(b), decoder(d) {} + + template void operator()(T*) { + T t; + decoder(t); + blob = t; + } +}; + +template T* MapValue::get() { return apply_visitor(GetVisitor(blob)); } +template const T* MapValue::get() const { return apply_visitor(GetVisitor()); } + +template typename V::result_type MapValue::apply_visitor(const V& v) { + return apply_visitor(const_cast(v)); +} + +template void Map::encode(S& s) const { + s(contentSize())(size()); // size, count + for (const_iterator i = begin(); i != end(); ++i) + s(i->first)(i->second); // key (type value) +} + +template void Map::decode(S& s) { + uint32_t cSize, count; + // FIXME aconway 2008-04-02: runtime check that we consume exactly cSize. + s(cSize)(count); + for ( ; count > 0; --count) { + key_type k; MapValue v; + s(k)(v); + insert(value_type(k,v)); + } +} + + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_MAP_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h b/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h new file mode 100644 index 0000000000..485b7ca6a8 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h @@ -0,0 +1,62 @@ +#ifndef QPID_AMQP_0_10_SERIALIZABLESTRING_H +#define QPID_AMQP_0_10_SERIALIZABLESTRING_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. + * + */ +namespace qpid { +namespace amqp_0_10 { + +/** Template for length-prefixed strings/arrays. + * Unique parameter allows creation of distinct SerializableString + * types with the smae T/SizeType + */ +template +struct SerializableString : public std::basic_string { + SerializableString() {} + template SerializableString(const U& u) : std::basic_string(u) {} + template SerializableString(const I& i, const I& j) : std::basic_string(i,j) {} + + using std::basic_string::operator=; + + template void serialize(S& s) { s.split(*this); } + + template void encode(S& s) const { + s(SizeType(this->size()))(this->begin(), this->end()); + } + + template void decode(S& s) { + SizeType newSize; + s(newSize); + this->resize(newSize); + s(this->begin(), this->end()); + } +}; + +// TODO aconway 2008-02-29: separate ostream ops +template +std::ostream& operator<<(std::ostream& o, const SerializableString& s) { + const std::basic_string str(s); + return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o< +#include + +namespace qpid { +namespace amqp_0_10 { + +UnknownType::Width UnknownType::WidthTable[16] = { + { 1, 0 }, + { 2, 0 }, + { 4, 0 }, + { 8, 0 }, + { 16, 0 }, + { 32, 0 }, + { 64, 0 }, + { 128, 0 }, + { 0, 1 }, + { 0, 2 }, + { 0, 4 }, + { -1, -1 }, // Invalid + { 5, 0 }, + { 9, 0 }, + { -1, -1 }, // Invalid + { 0, 0 } +}; + +int UnknownType::fixed() const { return WidthTable[code>>4].fixed; } +int UnknownType::variable() const { return WidthTable[code>>4].variable; } +UnknownType::UnknownType(uint8_t c) : code(c) { data.resize(fixed()); } + +std::ostream& operator<<(std::ostream& o, const UnknownType& u) { + return o << boost::make_iterator_range(u.begin(), u.end()) << std::endl; +} + +}} // namespace qpid::amqp_0_10 + diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h new file mode 100644 index 0000000000..0b4ec550d1 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h @@ -0,0 +1,86 @@ +#ifndef QPID_AMQP_0_10_UNKNOWNTYPE_H +#define QPID_AMQP_0_10_UNKNOWNTYPE_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 +#include +#include + +namespace qpid { +namespace amqp_0_10 { + +/** Encode/decode an unknown type based on typecode. */ +class UnknownType { + public: + UnknownType(uint8_t code=0); + uint8_t getCode() const { return code; } + /** Size of fixed type or 0 if not fixed/0-length. -1 invalid */ + int fixed() const; + /** Bytes in size tyep for variable width. -1 invalid */ + int variable() const; + + typedef std::vector::const_iterator const_iterator; + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + + template void serialize(S& s) { s.split(*this); } + template void encode(S& s) const; + template void decode(S& s); + + private: + uint8_t code; + struct Width { int fixed; int variable; }; + static Width WidthTable[16]; + + std::vector data; +}; + +template void UnknownType::encode(S& s) const { + switch (variable()) { + case 0: break; + case 1: s(uint8_t(data.size())); break; + case 2: s(uint16_t(data.size())); break; + case 4: s(uint32_t(data.size())); break; + } + s(data.begin(), data.end()); +} + +template void UnknownType::decode(S& s) { + uint32_t s8; + uint32_t s16; + uint32_t s32; + switch (variable()) { + case 0: break; + case 1: s(s8); data.resize(s8); break; + case 2: s(s16); data.resize(s16); break; + case 4: s(s32); data.resize(s32); break; + } + s(data.begin(), data.end()); +} + +inline uint8_t codeFor(const UnknownType& u) { return u.getCode(); } + +std::ostream& operator<<(std::ostream&, const UnknownType&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNKNOWNTYPE_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h b/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h index 5b00e933a9..b55f9baf42 100644 --- a/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h +++ b/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h @@ -22,14 +22,16 @@ */ #include "Decimal.h" +#include "SerializableString.h" #include "qpid/framing/SequenceNumber.h" #include "qpid/framing/Uuid.h" #include "qpid/sys/Time.h" #include -#include +#include #include #include #include +#include /**@file Mapping from built-in AMQP types to C++ types */ @@ -37,26 +39,47 @@ namespace qpid { namespace amqp_0_10 { // Fixed size types -typedef void Void; +struct EmptyType { template void serialize(S&) {} }; +inline std::ostream& operator<<(std::ostream& o, const EmptyType&) { return o; } + +struct Void : public EmptyType {}; +struct Bit : public EmptyType {}; -typedef bool Bit; typedef bool Boolean; typedef char Char; typedef int8_t Int8; typedef int16_t Int16; typedef int32_t Int32; typedef int64_t Int64; -typedef uint8_t Bin8; typedef uint8_t Uint8; typedef uint16_t Uint16; -typedef uint32_t CharUtf32 ; typedef uint32_t Uint32; typedef uint64_t Uint64; +// A struct to be distinct from the other 32 bit integrals. +struct CharUtf32 { + uint32_t value; + CharUtf32(uint32_t n=0) : value(n) {} + operator uint32_t&() { return value; } + operator const uint32_t&() const { return value; } + template void serialize(S& s) { s(value); } +}; + template struct Bin : public boost::array { template void serialize(S& s) { s.raw(this->begin(), this->size()); } }; - + +template std::ostream& operator<<(std::ostream& o, const Bin& b) { + return o << boost::make_iterator_range(b.begin(), b.end()); +} + +template <> struct Bin<1> : public boost::array { + Bin(char c=0) { this->front() = c; } + operator char() { return this->front(); } + template void serialize(S& s) { s.raw(data(), size()); } +}; + +typedef Bin<1> Bin8; typedef Bin<128> Bin1024; typedef Bin<16> Bin128; typedef Bin<2> Bin16; @@ -76,58 +99,37 @@ typedef sys::AbsTime Datetime; typedef Decimal Dec32; typedef Decimal Dec64; - -/** Template for length-prefixed strings/arrays. */ -template -struct SerializableString : public std::basic_string { - using std::basic_string::operator=; - - template void serialize(S& s) { s.split(*this); } - - template void encode(S& s) const { - s(SizeType(this->size()))(this->begin(), this->end()); - } - - template void decode(S& s) { - SizeType newSize; - s(newSize); - this->resize(newSize); - s(this->begin(), this->end()); - } -}; - -// TODO aconway 2008-02-29: separate ostream ops -template -std::ostream& operator<<(std::ostream& o, const SerializableString& s) { - const std::basic_string str(s); - return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o< Vbin8; -typedef SerializableString Str8Latin; +typedef SerializableString Str8Latin; typedef SerializableString Str8; typedef SerializableString Str8Utf16; typedef SerializableString Vbin16; -typedef SerializableString Str16Latin; +typedef SerializableString Str16Latin; typedef SerializableString Str16; typedef SerializableString Str16Utf16; typedef SerializableString Vbin32; +// Forward declare class types. +class Map; + // FIXME aconway 2008-02-26: Unimplemented types: -template struct Array : public std::vector { template void serialize(S&) {} }; +template struct ArrayDomain : public std::vector { + template void serialize(S&) {} +}; +struct Array { template void serialize(S&) {} }; struct ByteRanges { template void serialize(S&) {} }; struct SequenceSet { template void serialize(S&) {} }; -struct Map { template void serialize(S&) {} }; struct List { template void serialize(S&) {} }; struct Struct32 { template void serialize(S&) {} }; // FIXME aconway 2008-03-10: dummy ostream operators -template std::ostream& operator<<(std::ostream& o, const Array&) { return o; } +template std::ostream& operator<<(std::ostream& o, const ArrayDomain&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const Array&) { return o; } inline std::ostream& operator<<(std::ostream& o, const ByteRanges&) { return o; } -inline std::ostream& operator<<(std::ostream& o, const Map&) { return o; } inline std::ostream& operator<<(std::ostream& o, const SequenceSet&) { return o; } inline std::ostream& operator<<(std::ostream& o, const List&) { return o; } inline std::ostream& operator<<(std::ostream& o, const Struct32&) { return o; } diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index ca25ced5e0..ae47241e67 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -41,7 +41,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ SequenceSet.cpp \ serialize.cpp \ ProxyTemplate.cpp apply.cpp BoundedIterator.cpp \ - IncompleteMessageList.cpp + IncompleteMessageList.cpp \ + amqp_0_10/Map.cpp check_LTLIBRARIES += libshlibtest.la libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir) diff --git a/qpid/cpp/src/tests/amqp_0_10/Map.cpp b/qpid/cpp/src/tests/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..958470f6d7 --- /dev/null +++ b/qpid/cpp/src/tests/amqp_0_10/Map.cpp @@ -0,0 +1,99 @@ +/* + * + * 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 "unit_test.h" +#include "qpid/amqp_0_10/Map.h" +#include "qpid/amqp_0_10/Codec.h" +#include + +using namespace qpid::amqp_0_10; +using namespace std; + +namespace std { +// Dummy += for back inserters so we can use them with the decoder. +template back_insert_iterator& operator+=(back_insert_iterator& bi, size_t) { return bi; } +} + +QPID_AUTO_TEST_SUITE(MapTestSuite) + + BOOST_AUTO_TEST_CASE(testGetSet) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK(v.get()); + BOOST_CHECK(!v.get()); + BOOST_CHECK_EQUAL(*v.get(), "foo"); + + v = uint8_t(42); + BOOST_CHECK(!v.get()); + BOOST_CHECK(v.get()); + BOOST_CHECK_EQUAL(*v.get(), 42); + + v = uint16_t(12); + BOOST_CHECK(v.get()); + BOOST_CHECK_EQUAL(*v.get(), 12); +} + +template struct TestVisitor : public MapValue::Visitor { + template R operator()(const T&) const { throw MapValue::BadTypeException(); } + R operator()(const R& r) const { return r; } +}; + +BOOST_AUTO_TEST_CASE(testVisit) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor()), "foo"); + v = Uint16(42); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor()), 42); + try { + v.apply_visitor(TestVisitor()); + BOOST_FAIL("Expecting exception"); + } + catch(const MapValue::BadTypeException&) {} +} + + +BOOST_AUTO_TEST_CASE(testEncodeMapValue) { + MapValue mv; + std::string data; + mv = Str8("hello"); + Codec::encode(back_inserter(data))(mv); + BOOST_CHECK_EQUAL(data.size(), Codec::size(mv)); + MapValue mv2; + Codec::decode(data.begin())(mv2); + BOOST_CHECK_EQUAL(mv2.getCode(), 0x85); + BOOST_REQUIRE(mv2.get()); + BOOST_CHECK_EQUAL(*mv2.get(), "hello"); +} + +BOOST_AUTO_TEST_CASE(testEncode) { + Map map; + std::string data; + map["A"] = true; + map["b"] = Str8("hello"); + Codec::encode(back_inserter(data))(map); + Map map2; + Codec::decode(data.begin())(map2); + BOOST_CHECK_EQUAL(map.size(), 2u); + BOOST_CHECK(map["A"].get()); + BOOST_CHECK_EQUAL(*map["b"].get(), "hello"); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/serialize.cpp b/qpid/cpp/src/tests/serialize.cpp index 228a4b6e3a..da71917cbd 100644 --- a/qpid/cpp/src/tests/serialize.cpp +++ b/qpid/cpp/src/tests/serialize.cpp @@ -25,6 +25,7 @@ #include "qpid/amqp_0_10/specification.h" #include "qpid/amqp_0_10/ControlHolder.h" #include "qpid/amqp_0_10/Frame.h" +#include "qpid/amqp_0_10/Map.h" #include #include @@ -76,16 +77,13 @@ template struct concat2 { typedef typename mpl::copy struct concat3 { typedef typename concat2::type>::type type; }; template struct concat4 { typedef typename concat2::type>::type type; }; -typedef mpl::vector::type IntegralTypes; +typedef mpl::vector::type IntegralTypes; typedef mpl::vector::type BinTypes; -// FIXME aconway 2008-03-07: float encoding typedef mpl::vector::type FloatTypes; typedef mpl::vector FixedSizeClassTypes; -typedef mpl::vector VariableSizeTypes; +typedef mpl::vector VariableSizeTypes; - -// FIXME aconway 2008-03-07: float encoding -typedef concat3::type FixedSizeTypes; +typedef concat4::type FixedSizeTypes; typedef concat2::type AllTypes; // TODO aconway 2008-02-20: should test 64 bit integrals for order also. @@ -107,19 +105,22 @@ BOOST_AUTO_TEST_CASE(testNetworkByteOrder) { // Assign test values to the various types. void testValue(bool& b) { b = true; } +void testValue(Bit&) { } template typename boost::enable_if >::type testValue(T& n) { n=42; } +void testValue(CharUtf32& c) { c = 43; } void testValue(long long& l) { l = 0x012345; } void testValue(Datetime& dt) { dt = qpid::sys::now(); } void testValue(Uuid& uuid) { uuid=Uuid(true); } template void testValue(Decimal& d) { d.exponent=2; d.mantissa=0x1122; } void testValue(SequenceNo& s) { s = 42; } template void testValue(Bin& a) { a.assign(42); } -template void testValue(SerializableString& s) { +template void testValue(SerializableString& s) { char msg[]="foobar"; s.assign(msg, msg+sizeof(msg)); } void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; } void testValue(Str8& s) { s = "foobar"; } +void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); } //typedef mpl::vector::type TestTypes; BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) -- cgit v1.2.1