| /* |
| * |
| * 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/amqp_0_10/Codecs.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 "qpid/log/Statement.h" |
| #include <algorithm> |
| #include <functional> |
| #include <limits> |
| |
| using namespace qpid::framing; |
| using namespace qpid::types; |
| |
| namespace qpid { |
| namespace amqp_0_10 { |
| |
| namespace { |
| const std::string iso885915("iso-8859-15"); |
| const std::string utf8("utf8"); |
| const std::string utf16("utf16"); |
| const std::string binary("binary"); |
| 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 <class T, class U, class F> 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<FieldValue> in); |
| boost::shared_ptr<FieldValue> toFieldValue(const Variant& in); |
| |
| template <class T, class U, class F> void translate(boost::shared_ptr<FieldValue> in, U& u, F f) |
| { |
| T t; |
| getEncodedValue<T>(in, t); |
| convert(t, u, f); |
| } |
| |
| template <class T, class U, class F> 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::types::Uuid getUuid(FieldValue& value) |
| { |
| unsigned char data[16]; |
| value.getFixedWidthValue<16>(data); |
| return qpid::types::Uuid(data); |
| } |
| |
| Variant toVariant(boost::shared_ptr<FieldValue> 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<int8_t>(); break; |
| case 0x03: out = in->getIntegerValue<uint8_t>(); break; |
| case 0x04: break; //TODO: iso-8859-15 char |
| case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t>()); break; |
| case 0x10: out.setEncoding(amqp0_10_binary); |
| case 0x11: out = in->getIntegerValue<int16_t, 2>(); break; |
| case 0x12: out = in->getIntegerValue<uint16_t, 2>(); break; |
| case 0x20: out.setEncoding(amqp0_10_binary); |
| case 0x21: out = in->getIntegerValue<int32_t, 4>(); break; |
| case 0x22: out = in->getIntegerValue<uint32_t, 4>(); break; |
| case 0x23: out = in->get<float>(); break; |
| case 0x27: break; //TODO: utf-32 char |
| case 0x30: out.setEncoding(amqp0_10_binary); |
| case 0x31: out = in->getIntegerValue<int64_t, 8>(); break; |
| case 0x38: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding |
| case 0x32: out = in->getIntegerValue<uint64_t, 8>(); break; |
| case 0x33: out = in->get<double>(); 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: |
| out = in->get<std::string>(); |
| setEncodingFor(out, in->getType()); |
| break; |
| |
| case 0xa8: |
| out = Variant::Map(); |
| translate<FieldTable>(in, out.asMap(), &toVariantMapEntry); |
| break; |
| |
| case 0xa9: |
| out = Variant::List(); |
| translate<List>(in, out.asList(), &toVariant); |
| break; |
| case 0xaa: //convert amqp0-10 array into variant list |
| out = Variant::List(); |
| translate<Array>(in, out.asList(), &toVariant); |
| break; |
| |
| default: |
| //error? |
| break; |
| } |
| return out; |
| } |
| |
| boost::shared_ptr<FieldValue> convertString(const std::string& value, const std::string& encoding) |
| { |
| bool large = value.size() > std::numeric_limits<uint16_t>::max(); |
| if (encoding.empty() || encoding == amqp0_10_binary || encoding == binary) { |
| if (large) { |
| return boost::shared_ptr<FieldValue>(new Var32Value(value, 0xa0)); |
| } else { |
| return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x90)); |
| } |
| } else if (encoding == utf8) { |
| if (!large) |
| return boost::shared_ptr<FieldValue>(new Str16Value(value)); |
| throw Exception(QPID_MSG("Could not encode utf8 character string - too long (" << value.size() << " bytes)")); |
| } else if (encoding == utf16) { |
| if (!large) |
| return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x96)); |
| throw Exception(QPID_MSG("Could not encode utf16 character string - too long (" << value.size() << " bytes)")); |
| } else if (encoding == iso885915) { |
| if (!large) |
| return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x94)); |
| throw Exception(QPID_MSG("Could not encode iso-8859-15 character string - too long (" << value.size() << " bytes)")); |
| } else { |
| // the encoding was not recognised |
| QPID_LOG(warning, "Unknown byte encoding: [" << encoding << "], encoding as vbin32."); |
| return boost::shared_ptr<FieldValue>(new Var32Value(value, 0xa0)); |
| } |
| } |
| |
| boost::shared_ptr<FieldValue> toFieldValue(const Variant& in) |
| { |
| boost::shared_ptr<FieldValue> out; |
| switch (in.getType()) { |
| case VAR_VOID: out = boost::shared_ptr<FieldValue>(new VoidValue()); break; |
| case VAR_BOOL: out = boost::shared_ptr<FieldValue>(new BoolValue(in.asBool())); break; |
| case VAR_UINT8: out = boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8())); break; |
| case VAR_UINT16: out = boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16())); break; |
| case VAR_UINT32: out = boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32())); break; |
| case VAR_UINT64: out = boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64())); break; |
| case VAR_INT8: out = boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8())); break; |
| case VAR_INT16: out = boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16())); break; |
| case VAR_INT32: out = boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32())); break; |
| case VAR_INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break; |
| case VAR_FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break; |
| case VAR_DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break; |
| case VAR_STRING: out = convertString(in.asString(), in.getEncoding()); break; |
| case VAR_UUID: out = boost::shared_ptr<FieldValue>(new UuidValue(in.asUuid().data())); break; |
| case VAR_MAP: |
| out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap())); |
| break; |
| case VAR_LIST: |
| out = boost::shared_ptr<FieldValue>(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 <class T> 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<char*>(s.data()), s.size()) {} |
| |
| template <class T> void decode(T& t) { t.decode(buffer); } |
| |
| }; |
| |
| template <class T, class U, class F> 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 <class T, class U, class F> 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::Map& value, std::string& data) |
| { |
| _encode<FieldTable>(value, data, &toFieldTableEntry); |
| } |
| |
| void MapCodec::decode(const std::string& data, Variant::Map& value) |
| { |
| _decode<FieldTable>(data, value, &toVariantMapEntry); |
| } |
| |
| size_t MapCodec::encodedSize(const Variant::Map& value) |
| { |
| std::string encoded; |
| encode(value, encoded); |
| return encoded.size(); |
| } |
| |
| void ListCodec::encode(const Variant::List& value, std::string& data) |
| { |
| _encode<List>(value, data, &toFieldValue); |
| } |
| |
| void ListCodec::decode(const std::string& data, Variant::List& value) |
| { |
| _decode<List>(data, value, &toVariant); |
| } |
| |
| size_t ListCodec::encodedSize(const Variant::List& value) |
| { |
| std::string encoded; |
| encode(value, encoded); |
| return encoded.size(); |
| } |
| |
| 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::amqp_0_10 |