diff options
author | Alan Conway <aconway@apache.org> | 2008-04-02 17:56:14 +0000 |
---|---|---|
committer | Alan Conway <aconway@apache.org> | 2008-04-02 17:56:14 +0000 |
commit | c6bd8565ecc054fa3a393f7df04b1d44836c3a79 (patch) | |
tree | a6e237fab1bba4ec6ea868ff719d5068fdc6f5c5 | |
parent | 735fbf00f9ddae479c6a4e682fd3fa4224b6131f (diff) | |
download | qpid-python-c6bd8565ecc054fa3a393f7df04b1d44836c3a79.tar.gz |
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
-rwxr-xr-x | qpid/cpp/rubygen/0-10/specification.rb | 1 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/0-10/typecode.rb | 85 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/cppgen.rb | 11 | ||||
-rw-r--r-- | qpid/cpp/src/Makefile.am | 5 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/Serializer.h | 10 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/Codec.h | 12 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/Map.cpp | 68 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/Map.h | 179 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/SerializableString.h | 62 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp | 56 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/UnknownType.h | 86 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/amqp_0_10/built_in_types.h | 80 | ||||
-rw-r--r-- | qpid/cpp/src/tests/Makefile.am | 3 | ||||
-rw-r--r-- | qpid/cpp/src/tests/amqp_0_10/Map.cpp | 99 | ||||
-rw-r--r-- | qpid/cpp/src/tests/serialize.cpp | 15 |
15 files changed, 716 insertions, 56 deletions
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 "<boost/call_traits.hpp>" include "<iosfwd>" 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 <uint8_t Code> struct TypeForCode;" + genl + @types.each { |t| + genl "template <> struct TypeForCode<#{t.code}> { typedef #{t.typename} type; };" + } + genl + genl "template <class V> 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 "<string>" + include "<sstream>" + 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 <class T> struct CodeForType;" + genl + @types.each { |t| + genl "template <> struct CodeForType<#{t.typename}> { static const uint8_t value=#{t.code}; };" + } + genl + genl "template <class T> uint8_t codeFor(const T&) { return CodeForType<T>::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 <class S> struct SRef { + S& s; + SRef(S& ss) : s(ss) {} + template <class T> typename S::result_type operator()(T& x) { return s(x); } + template <class T> typename S::result_type operator()(const T& x) { return s(x); } +}; + +template <class S> SRef<S> ref(S& s) { return SRef<S>(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 <class T> 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 <class Iter> 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 <class Iter> 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 <class Iter> - 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 <class Iter> 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 <ostream> + +namespace qpid { +namespace amqp_0_10 { + +MapValue::MapValue() : code(codeFor(uint8_t(0))), blob(in_place<uint8_t>(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&> { + std::ostream& out; + OstreamVisitor(std::ostream& o) : out(o) {} + template <class T> std::ostream& operator()(const T& t) { + return out << t; + } +}; + +std::ostream& operator<<(std::ostream& o, const MapValue& m) { + o << typeName(m.getCode()) << ":"; + const_cast<MapValue&>(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<Map::value_type> 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 <map> +#include <string> +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +class Map; + +class MapValue { + public: + struct BadTypeException : public Exception {}; + + template <class R> struct Visitor { typedef R result_type; }; + + MapValue(); + MapValue(const MapValue& x); + template <class T> explicit MapValue(const T& t); + template <class T> MapValue& operator=(const T& t); + + template <class T> T* get(); + template <class T> const T* get() const; + + template <class V> typename V::result_type apply_visitor(V&); + template <class V> typename V::result_type apply_visitor(const V&); + + uint8_t getCode() const { return code; } + + bool operator==(const MapValue&) const; + + template <class S> void serialize(S& s) { s(code); s.split(*this); } + template <class S> void encode(S& s) const { + const_cast<MapValue*>(this)->apply_visitor(s); + } + template <class S> void decode(S& s) { + DecodeVisitor<S> 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<SIZE> Blob; + + template <class V> struct VisitVisitor; + template <class T> struct GetVisitor; + template <class D> struct DecodeVisitor; + + uint8_t code; + Blob blob; +}; + +class Map : public std::map<Str8, MapValue> { + public: + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> 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 <class S> 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 <class T> MapValue::MapValue(const T& t) : code(codeFor(t)), blob(in_place<t>()) {} + +template <class T> MapValue& MapValue::operator=(const T& t) { + code=codeFor(t); + blob=t; + return *this; +} + +template <class V> struct MapValue::VisitVisitor { + typedef typename V::result_type result_type; + V& visitor; + Blob& blob; + VisitVisitor(V& v, Blob& b) : visitor(v), blob(b) {} + + template <class T> result_type operator()(T*) { + return visitor(*reinterpret_cast<T*>(blob.get())); + } +}; + +template <class V> typename V::result_type MapValue::apply_visitor(V& v) { + VisitVisitor<V> visitor(v, blob); + return qpid::amqp_0_10::apply_visitor(visitor, code); +} + +template <class R> 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 <class T> R* operator()(T&) { return 0; } +}; + +template <class D> struct MapValue::DecodeVisitor { + typedef void result_type; + MapValue::Blob& blob; + D& decoder; + DecodeVisitor(Blob& b, D& d) : blob(b), decoder(d) {} + + template <class T> void operator()(T*) { + T t; + decoder(t); + blob = t; + } +}; + +template <class T> T* MapValue::get() { return apply_visitor(GetVisitor<T>(blob)); } +template <class T> const T* MapValue::get() const { return apply_visitor(GetVisitor<const T>()); } + +template <class V> typename V::result_type MapValue::apply_visitor(const V& v) { + return apply_visitor(const_cast<V&>(v)); +} + +template <class S> 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 <class S> 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 <class T, class SizeType, int Unique=0> +struct SerializableString : public std::basic_string<T> { + SerializableString() {} + template <class U> SerializableString(const U& u) : std::basic_string<T>(u) {} + template <class I> SerializableString(const I& i, const I& j) : std::basic_string<T>(i,j) {} + + using std::basic_string<T>::operator=; + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + s(SizeType(this->size()))(this->begin(), this->end()); + } + + template <class S> 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 <class T, class SizeType> +std::ostream& operator<<(std::ostream& o, const SerializableString<T,SizeType>& s) { + const std::basic_string<T> str(s); + return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o<<str work? +} + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_SERIALIZABLESTRING_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp new file mode 100644 index 0000000000..844891d732 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp @@ -0,0 +1,56 @@ +/* + * + * 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 "UnknownType.h" +#include <boost/range/iterator_range.hpp> +#include <ostream> + +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 <vector> +#include <iosfwd> +#include <stdint.h> + +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<char>::const_iterator const_iterator; + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const; + template <class S> void decode(S& s); + + private: + uint8_t code; + struct Width { int fixed; int variable; }; + static Width WidthTable[16]; + + std::vector<char> data; +}; + +template <class S> 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 <class S> 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 <boost/array.hpp> -#include <stdint.h> +#include <boost/range/iterator_range.hpp> #include <string> #include <ostream> #include <vector> +#include <stdint.h> /**@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 <class S> 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 <class S> void serialize(S& s) { s(value); } +}; + template <size_t N> struct Bin : public boost::array<char, N> { template <class S> void serialize(S& s) { s.raw(this->begin(), this->size()); } }; - + +template <size_t N> std::ostream& operator<<(std::ostream& o, const Bin<N>& b) { + return o << boost::make_iterator_range(b.begin(), b.end()); +} + +template <> struct Bin<1> : public boost::array<char, 1> { + Bin(char c=0) { this->front() = c; } + operator char() { return this->front(); } + template <class S> 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<Uint8, Int32> Dec32; typedef Decimal<Uint8, Int64> Dec64; - -/** Template for length-prefixed strings/arrays. */ -template <class T, class SizeType> -struct SerializableString : public std::basic_string<T> { - using std::basic_string<T>::operator=; - - template <class S> void serialize(S& s) { s.split(*this); } - - template <class S> void encode(S& s) const { - s(SizeType(this->size()))(this->begin(), this->end()); - } - - template <class S> 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 <class T, class SizeType> -std::ostream& operator<<(std::ostream& o, const SerializableString<T,SizeType>& s) { - const std::basic_string<T> str(s); - return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o<<str work? -} - // Variable width types + typedef SerializableString<Uint8, Uint8> Vbin8; -typedef SerializableString<char, Uint8> Str8Latin; +typedef SerializableString<char, Uint8, 1> Str8Latin; typedef SerializableString<char, Uint8> Str8; typedef SerializableString<Uint16, Uint8> Str8Utf16; typedef SerializableString<Uint8, Uint16> Vbin16; -typedef SerializableString<char, Uint16> Str16Latin; +typedef SerializableString<char, Uint16, 1> Str16Latin; typedef SerializableString<char, Uint16> Str16; typedef SerializableString<Uint16, Uint16> Str16Utf16; typedef SerializableString<Uint8, Uint32> Vbin32; +// Forward declare class types. +class Map; + // FIXME aconway 2008-02-26: Unimplemented types: -template <class T> struct Array : public std::vector<T> { template <class S> void serialize(S&) {} }; +template <class T> struct ArrayDomain : public std::vector<T> { + template <class S> void serialize(S&) {} +}; +struct Array { template <class S> void serialize(S&) {} }; struct ByteRanges { template <class S> void serialize(S&) {} }; struct SequenceSet { template <class S> void serialize(S&) {} }; -struct Map { template <class S> void serialize(S&) {} }; struct List { template <class S> void serialize(S&) {} }; struct Struct32 { template <class S> void serialize(S&) {} }; // FIXME aconway 2008-03-10: dummy ostream operators -template <class T> std::ostream& operator<<(std::ostream& o, const Array<T>&) { return o; } +template <class T> std::ostream& operator<<(std::ostream& o, const ArrayDomain<T>&) { 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 <iostream> + +using namespace qpid::amqp_0_10; +using namespace std; + +namespace std { +// Dummy += for back inserters so we can use them with the decoder. +template <class C> back_insert_iterator<C>& operator+=(back_insert_iterator<C>& bi, size_t) { return bi; } +} + +QPID_AUTO_TEST_SUITE(MapTestSuite) + + BOOST_AUTO_TEST_CASE(testGetSet) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK(v.get<Str8>()); + BOOST_CHECK(!v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo"); + + v = uint8_t(42); + BOOST_CHECK(!v.get<Str8>()); + BOOST_CHECK(v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42); + + v = uint16_t(12); + BOOST_CHECK(v.get<uint16_t>()); + BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12); +} + +template <class R> struct TestVisitor : public MapValue::Visitor<R> { + template <class T> 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<Str8>()), "foo"); + v = Uint16(42); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42); + try { + v.apply_visitor(TestVisitor<bool>()); + 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<Str8>()); + BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "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<bool>()); + BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "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 <boost/test/test_case_template.hpp> #include <boost/type_traits/is_arithmetic.hpp> @@ -76,16 +77,13 @@ template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typen template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; }; template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; }; -typedef mpl::vector<Bit, Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes; +typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes; typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes; -// FIXME aconway 2008-03-07: float encoding typedef mpl::vector<Double, Float>::type FloatTypes; typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes; -typedef mpl::vector<Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes; +typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes; - -// FIXME aconway 2008-03-07: float encoding -typedef concat3<IntegralTypes, BinTypes, /*FloatTypes, */ FixedSizeClassTypes>::type FixedSizeTypes; +typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes; typedef concat2<FixedSizeTypes, VariableSizeTypes>::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 <class T> typename boost::enable_if<boost::is_arithmetic<T> >::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 <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; } void testValue(SequenceNo& s) { s = 42; } template <size_t N> void testValue(Bin<N>& a) { a.assign(42); } -template <class T, class S> void testValue(SerializableString<T, S>& s) { +template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& 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<Str8, Str16>::type TestTypes; BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) |