/* * * 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/client/amqp0_10/Codecs.h" #include "qpid/messaging/Variant.h" #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/List.h" #include #include using namespace qpid::framing; using namespace qpid::messaging; namespace qpid { namespace client { namespace amqp0_10 { namespace { const std::string iso885915("iso-8859-15"); const std::string utf8("utf8"); const std::string utf16("utf16"); const std::string amqp0_10_binary("amqp0-10:binary"); const std::string amqp0_10_bit("amqp0-10:bit"); const std::string amqp0_10_datetime("amqp0-10:datetime"); const std::string amqp0_10_struct("amqp0-10:struct"); } template void convert(const T& from, U& to, F f) { std::transform(from.begin(), from.end(), std::inserter(to, to.begin()), f); } Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in); FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in); Variant toVariant(boost::shared_ptr in); boost::shared_ptr toFieldValue(const Variant& in); template void translate(boost::shared_ptr in, U& u, F f) { T t; getEncodedValue(in, t); convert(t, u, f); } template T* toFieldValueCollection(const U& u, F f) { typename T::ValueType t; convert(u, t, f); return new T(t); } FieldTableValue* toFieldTableValue(const Variant::Map& map) { FieldTable ft; convert(map, ft, &toFieldTableEntry); return new FieldTableValue(ft); } ListValue* toListValue(const Variant::List& list) { List l; convert(list, l, &toFieldValue); return new ListValue(l); } void setEncodingFor(Variant& out, uint8_t code) { switch(code){ case 0x80: case 0x90: case 0xa0: out.setEncoding(amqp0_10_binary); break; case 0x84: case 0x94: out.setEncoding(iso885915); break; case 0x85: case 0x95: out.setEncoding(utf8); break; case 0x86: case 0x96: out.setEncoding(utf16); break; case 0xab: out.setEncoding(amqp0_10_struct); break; default: //do nothing break; } } qpid::messaging::Uuid getUuid(FieldValue& value) { unsigned char data[16]; value.getFixedWidthValue<16>(data); return qpid::messaging::Uuid(data); } Variant toVariant(boost::shared_ptr in) { Variant out; //based on AMQP 0-10 typecode, pick most appropriate variant type switch (in->getType()) { //Fixed Width types: case 0x01: out.setEncoding(amqp0_10_binary); case 0x02: out = in->getIntegerValue(); break; case 0x03: out = in->getIntegerValue(); break; case 0x04: break; //TODO: iso-8859-15 char case 0x08: out = static_cast(in->getIntegerValue()); break; case 0x10: out.setEncoding(amqp0_10_binary); case 0x11: out = in->getIntegerValue(); break; case 0x12: out = in->getIntegerValue(); break; case 0x20: out.setEncoding(amqp0_10_binary); case 0x21: out = in->getIntegerValue(); break; case 0x22: out = in->getIntegerValue(); break; case 0x23: out = in->get(); break; case 0x27: break; //TODO: utf-32 char case 0x30: out.setEncoding(amqp0_10_binary); case 0x31: out = in->getIntegerValue(); break; case 0x38: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding case 0x32: out = in->getIntegerValue(); break; case 0x33: out = in->get(); break; case 0x48: out = getUuid(*in); break; //TODO: figure out whether and how to map values with codes 0x40-0xd8 case 0xf0: break;//void, which is the default value for Variant case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant //Variable Width types: //strings: case 0x80: case 0x84: case 0x85: case 0x86: case 0x90: case 0x94: case 0x95: case 0x96: case 0xa0: case 0xab: setEncodingFor(out, in->getType()); out = in->get(); break; case 0xa8: out = Variant::Map(); translate(in, out.asMap(), &toVariantMapEntry); break; case 0xa9: out = Variant::List(); translate(in, out.asList(), &toVariant); break; case 0xaa: //convert amqp0-10 array into variant list out = Variant::List(); translate(in, out.asList(), &toVariant); break; default: //error? break; } return out; } boost::shared_ptr toFieldValue(const Variant& in) { boost::shared_ptr out; switch (in.getType()) { case VAR_VOID: out = boost::shared_ptr(new VoidValue()); break; case VAR_BOOL: out = boost::shared_ptr(new BoolValue(in.asBool())); break; case VAR_UINT8: out = boost::shared_ptr(new Unsigned8Value(in.asUint8())); break; case VAR_UINT16: out = boost::shared_ptr(new Unsigned16Value(in.asUint16())); break; case VAR_UINT32: out = boost::shared_ptr(new Unsigned32Value(in.asUint32())); break; case VAR_UINT64: out = boost::shared_ptr(new Unsigned64Value(in.asUint64())); break; case VAR_INT8: out = boost::shared_ptr(new Integer8Value(in.asInt8())); break; case VAR_INT16: out = boost::shared_ptr(new Integer16Value(in.asInt16())); break; case VAR_INT32: out = boost::shared_ptr(new Integer32Value(in.asInt32())); break; case VAR_INT64: out = boost::shared_ptr(new Integer64Value(in.asInt64())); break; case VAR_FLOAT: out = boost::shared_ptr(new FloatValue(in.asFloat())); break; case VAR_DOUBLE: out = boost::shared_ptr(new DoubleValue(in.asDouble())); break; //TODO: check encoding (and length?) when deciding what AMQP type to treat string as case VAR_STRING: out = boost::shared_ptr(new Str16Value(in.asString())); break; case VAR_UUID: out = boost::shared_ptr(new UuidValue(in.asUuid().data())); break; case VAR_MAP: out = boost::shared_ptr(toFieldTableValue(in.asMap())); break; case VAR_LIST: out = boost::shared_ptr(toListValue(in.asList())); break; } return out; } Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in) { return Variant::Map::value_type(in.first, toVariant(in.second)); } FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in) { return FieldTable::value_type(in.first, toFieldValue(in.second)); } struct EncodeBuffer { char* data; Buffer buffer; EncodeBuffer(size_t size) : data(new char[size]), buffer(data, size) {} ~EncodeBuffer() { delete[] data; } template void encode(T& t) { t.encode(buffer); } void getData(std::string& s) { s.assign(data, buffer.getSize()); } }; struct DecodeBuffer { Buffer buffer; DecodeBuffer(const std::string& s) : buffer(const_cast(s.data()), s.size()) {} template void decode(T& t) { t.decode(buffer); } }; template void _encode(const U& value, std::string& data, F f) { T t; convert(value, t, f); EncodeBuffer buffer(t.encodedSize()); buffer.encode(t); buffer.getData(data); } template void _decode(const std::string& data, U& value, F f) { T t; DecodeBuffer buffer(data); buffer.decode(t); convert(t, value, f); } void MapCodec::encode(const Variant& value, std::string& data) { _encode(value.asMap(), data, &toFieldTableEntry); } void MapCodec::decode(const std::string& data, Variant& value) { value = Variant::Map(); _decode(data, value.asMap(), &toVariantMapEntry); } void ListCodec::encode(const Variant& value, std::string& data) { _encode(value.asList(), data, &toFieldValue); } void ListCodec::decode(const std::string& data, Variant& value) { value = Variant::List(); _decode(data, value.asList(), &toVariant); } void translate(const Variant::Map& from, FieldTable& to) { convert(from, to, &toFieldTableEntry); } void translate(const FieldTable& from, Variant::Map& to) { convert(from, to, &toVariantMapEntry); } const std::string ListCodec::contentType("amqp/list"); const std::string MapCodec::contentType("amqp/map"); }}} // namespace qpid::client::amqp0_10