diff options
-rw-r--r-- | qpid/cpp/src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/framing/Endian.cpp | 52 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/framing/Endian.h | 62 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/framing/FieldTable.cpp | 1 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/framing/FieldValue.cpp | 71 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/framing/FieldValue.h | 100 | ||||
-rw-r--r-- | qpid/cpp/src/tests/FieldTable.cpp | 8 | ||||
-rw-r--r-- | qpid/cpp/src/tests/FieldValue.cpp | 35 |
8 files changed, 167 insertions, 163 deletions
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index 86606107cc..8e2b5b73e8 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -777,7 +777,6 @@ set (qpidcommon_SOURCES qpid/framing/AMQHeartbeatBody.cpp qpid/framing/Array.cpp qpid/framing/Buffer.cpp - qpid/framing/Endian.cpp qpid/framing/FieldTable.cpp qpid/framing/FieldValue.cpp qpid/framing/FrameSet.cpp diff --git a/qpid/cpp/src/qpid/framing/Endian.cpp b/qpid/cpp/src/qpid/framing/Endian.cpp deleted file mode 100644 index decfe4b2d9..0000000000 --- a/qpid/cpp/src/qpid/framing/Endian.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * 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/framing/Endian.h" - -namespace qpid { -namespace framing { - -Endian::Endian() : littleEndian(!testBigEndian()) {} - -bool Endian::testBigEndian() -{ - uint16_t a = 1; - uint16_t b; - uint8_t* p = (uint8_t*) &b; - p[0] = 0xFF & (a >> 8); - p[1] = 0xFF & (a); - return a == b; -} - -uint8_t* Endian::convertIfRequired(uint8_t* octets, int width) -{ - if (instance.littleEndian) { - for (int i = 0; i < (width/2); i++) { - uint8_t temp = octets[i]; - octets[i] = octets[width - (1 + i)]; - octets[width - (1 + i)] = temp; - } - } - return octets; -} - -const Endian Endian::instance; - -}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/Endian.h b/qpid/cpp/src/qpid/framing/Endian.h index dea8eaa3b7..faf553fed5 100644 --- a/qpid/cpp/src/qpid/framing/Endian.h +++ b/qpid/cpp/src/qpid/framing/Endian.h @@ -26,21 +26,53 @@ namespace qpid { namespace framing { +namespace endian { -/** - * Conversion utility for little-endian platforms that need to convert - * to and from network ordered octet sequences - */ -class Endian -{ - public: - static uint8_t* convertIfRequired(uint8_t* octets, int width); - private: - const bool littleEndian; - Endian(); - static const Endian instance; - static bool testBigEndian(); -}; -}} // namespace qpid::framing +/** Decode integer from network byte order buffer to type T, buffer must be at least sizeof(T). */ +template <class T> T decodeInt(const uint8_t* buffer) { + T v = buffer[0]; + for (size_t i = 1; i < sizeof(T); ++i) { + v <<= 8; + v |= buffer[i]; + } + return v; +} + +/** Encode integer value to network byte order in buffer, buffer must be at least sizeof(T). */ +template <class T> void encodeInt(uint8_t* buffer, T value) { + for (size_t i = sizeof(T); i > 0; --i) { + buffer[i-1] = value & 0XFF; + value >>= 8; + } +} + +// Compute the int type that can hold a float type. +template <class T> struct IntBox { typedef T Type; }; +template <> struct IntBox<float> { typedef uint32_t Type; }; +template <> struct IntBox<double> { typedef uint64_t Type; }; + +/** Decode floating from network byte order buffer to type T, buffer must be at least sizeof(T). */ +template <class T> T decodeFloat(const uint8_t* buffer) { + typedef typename IntBox<T>::Type Box; + union { T f; Box i; } u; + u.i = decodeInt<Box>(buffer); + return u.f; +} + +/** Encode floating value to network byte order in buffer, buffer must be at least sizeof(T). */ +template <class T> void encodeFloat(uint8_t* buffer, T value) { + typedef typename IntBox<T>::Type Box; + union { T f; Box i; } u; + u.f = value; + encodeInt(buffer, u.i); +} + +}}} // namespace qpid::framing::endian #endif /*!QPID_FRAMING_ENDIAN_H*/ + + + + + + diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp index cd38db1ee8..0b677a6ccb 100644 --- a/qpid/cpp/src/qpid/framing/FieldTable.cpp +++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp @@ -21,7 +21,6 @@ #include "qpid/framing/FieldTable.h" #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" -#include "qpid/framing/Endian.h" #include "qpid/framing/FieldValue.h" #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" diff --git a/qpid/cpp/src/qpid/framing/FieldValue.cpp b/qpid/cpp/src/qpid/framing/FieldValue.cpp index 1d39c57edf..227c12e44e 100644 --- a/qpid/cpp/src/qpid/framing/FieldValue.cpp +++ b/qpid/cpp/src/qpid/framing/FieldValue.cpp @@ -21,15 +21,45 @@ #include "qpid/framing/FieldValue.h" #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" -#include "qpid/framing/Endian.h" #include "qpid/framing/List.h" #include "qpid/framing/Uuid.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/Endian.h" #include "qpid/Msg.h" namespace qpid { namespace framing { +// Some template magic for computing types from sizes. +template<int W> struct IntType{}; +template<> struct IntType<1> { typedef int8_t Type; }; +template<> struct IntType<2> { typedef int16_t Type; }; +template<> struct IntType<4> { typedef int32_t Type; }; +template<> struct IntType<8> { typedef int64_t Type; }; + +template<int W> struct UintType{}; +template<> struct UintType<1> { typedef uint8_t Type; }; +template<> struct UintType<2> { typedef uint16_t Type; }; +template<> struct UintType<4> { typedef uint32_t Type; }; +template<> struct UintType<8> { typedef uint64_t Type; }; + +template<int W> struct FloatType{}; +template<> struct FloatType<1> { typedef int8_t Type; }; // Dummy, never used. +template<> struct FloatType<2> { typedef int16_t Type; }; // Dummy, never used. +template<> struct FloatType<4> { typedef float Type; }; +template<> struct FloatType<8> { typedef double Type; }; + +// Construct the right subclass of FixedWidthValue for numeric types using width and kind. +// Kind 1=int, 2=unsigned int, 3=float +template<int W> FixedWidthValue<W>* numericFixedWidthValue(uint8_t kind) { + switch (kind) { + case 1: return new FixedWidthIntValue<typename IntType<W>::Type>(); + case 2: return new FixedWidthIntValue<typename UintType<W>::Type>(); + case 3: return new FixedWidthFloatValue<typename FloatType<W>::Type>(); + default: return new FixedWidthValue<W>(); + } +} + uint8_t FieldValue::getType() const { return typeOctet; @@ -47,20 +77,22 @@ void FieldValue::setType(uint8_t type) } else if (typeOctet == 0x48) { data.reset(new UuidData()); } else { + uint8_t kind = typeOctet & 0xF; uint8_t lenType = typeOctet >> 4; switch(lenType){ case 0: - data.reset(new FixedWidthValue<1>()); + data.reset(numericFixedWidthValue<1>(kind)); break; case 1: - data.reset(new FixedWidthValue<2>()); + data.reset(numericFixedWidthValue<2>(kind)); break; case 2: - data.reset(new FixedWidthValue<4>()); + data.reset(numericFixedWidthValue<4>(kind)); break; case 3: - data.reset(new FixedWidthValue<8>()); + data.reset(numericFixedWidthValue<8>(kind)); break; + // None of the remaining widths can be numeric types so just use new FixedWidthValue case 4: data.reset(new FixedWidthValue<16>()); break; @@ -157,28 +189,28 @@ Struct32Value::Struct32Value(const std::string& v) : {} IntegerValue::IntegerValue(int v) : - FieldValue(0x21, new FixedWidthValue<4>(v)) + FieldValue(0x21, new FixedWidthIntValue<int32_t>(v)) {} FloatValue::FloatValue(float v) : - FieldValue(0x23, new FixedWidthValue<4>(Endian::convertIfRequired(reinterpret_cast<uint8_t*>(&v), 4))) + FieldValue(0x23, new FixedWidthFloatValue<float>(v)) {} DoubleValue::DoubleValue(double v) : - FieldValue(0x33, new FixedWidthValue<8>(Endian::convertIfRequired(reinterpret_cast<uint8_t*>(&v), 8))) + FieldValue(0x33, new FixedWidthFloatValue<double>(v)) {} Integer64Value::Integer64Value(int64_t v) : - FieldValue(0x31, new FixedWidthValue<8>(v)) + FieldValue(0x31, new FixedWidthIntValue<int64_t>(v)) {} Unsigned64Value::Unsigned64Value(uint64_t v) : - FieldValue(0x32, new FixedWidthValue<8>(v)) + FieldValue(0x32, new FixedWidthIntValue<uint64_t>(v)) {} TimeValue::TimeValue(uint64_t v) : - FieldValue(0x38, new FixedWidthValue<8>(v)) + FieldValue(0x38, new FixedWidthIntValue<uint64_t>(v)) { } @@ -197,24 +229,24 @@ ArrayValue::ArrayValue(const Array& a) : FieldValue(0xaa, new EncodedValue<Array VoidValue::VoidValue() : FieldValue(0xf0, new FixedWidthValue<0>()) {} BoolValue::BoolValue(bool b) : - FieldValue(0x08, new FixedWidthValue<1>(b)) + FieldValue(0x08, new FixedWidthIntValue<bool>(b)) {} Unsigned8Value::Unsigned8Value(uint8_t v) : - FieldValue(0x02, new FixedWidthValue<1>(v)) + FieldValue(0x02, new FixedWidthIntValue<uint8_t>(v)) {} Unsigned16Value::Unsigned16Value(uint16_t v) : - FieldValue(0x12, new FixedWidthValue<2>(v)) + FieldValue(0x12, new FixedWidthIntValue<uint16_t>(v)) {} Unsigned32Value::Unsigned32Value(uint32_t v) : - FieldValue(0x22, new FixedWidthValue<4>(v)) + FieldValue(0x22, new FixedWidthIntValue<uint32_t>(v)) {} Integer8Value::Integer8Value(int8_t v) : - FieldValue(0x01, new FixedWidthValue<1>(v)) + FieldValue(0x01, new FixedWidthIntValue<int8_t>(v)) {} Integer16Value::Integer16Value(int16_t v) : - FieldValue(0x11, new FixedWidthValue<2>(v)) + FieldValue(0x11, new FixedWidthIntValue<int16_t>(v)) {} UuidData::UuidData() {} @@ -232,9 +264,4 @@ void FieldValue::print(std::ostream& out) const { out << ')'; } -uint8_t* FieldValue::convertIfRequired(uint8_t* octets, int width) -{ - return Endian::convertIfRequired(octets, width); -} - }} diff --git a/qpid/cpp/src/qpid/framing/FieldValue.h b/qpid/cpp/src/qpid/framing/FieldValue.h index 94126df053..e20244e7c9 100644 --- a/qpid/cpp/src/qpid/framing/FieldValue.h +++ b/qpid/cpp/src/qpid/framing/FieldValue.h @@ -25,6 +25,7 @@ #include "qpid/framing/amqp_types.h" #include "qpid/framing/Buffer.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/Endian.h" #include "qpid/CommonImportExport.h" #include <iostream> @@ -66,15 +67,17 @@ class QPID_COMMON_CLASS_EXTERN FieldValue { */ class Data { public: - virtual ~Data() {}; + virtual ~Data() {} virtual uint32_t encodedSize() const = 0; virtual void encode(Buffer& buffer) = 0; virtual void decode(Buffer& buffer) = 0; virtual bool operator==(const Data&) const = 0; virtual bool convertsToInt() const { return false; } + virtual bool convertsToFloat() const { return false; } virtual bool convertsToString() const { return false; } virtual int64_t getInt() const { throw InvalidConversionException();} + virtual double getFloat() const { throw InvalidConversionException();} virtual std::string getString() const { throw InvalidConversionException(); } virtual void print(std::ostream& out) const = 0; @@ -106,8 +109,6 @@ class QPID_COMMON_CLASS_EXTERN FieldValue { protected: FieldValue(uint8_t t, Data* d): typeOctet(t), data(d) {} - QPID_COMMON_EXTERN static uint8_t* convertIfRequired(uint8_t* octets, int width); - private: uint8_t typeOctet; std::auto_ptr<Data> data; @@ -123,12 +124,24 @@ template <> inline bool FieldValue::convertsTo<std::string>() const { return data->convertsToString(); } template <> +inline bool FieldValue::convertsTo<float>() const { return data->convertsToFloat(); } + +template <> +inline bool FieldValue::convertsTo<double>() const { return data->convertsToFloat(); } + +template <> inline int FieldValue::get<int>() const { return static_cast<int>(data->getInt()); } template <> inline int64_t FieldValue::get<int64_t>() const { return data->getInt(); } template <> +inline float FieldValue::get<float>() const { return data->getFloat(); } + +template <> +inline double FieldValue::get<double>() const { return data->getFloat(); } + +template <> inline std::string FieldValue::get<std::string>() const { return data->getString(); } inline std::ostream& operator<<(std::ostream& out, const FieldValue& v) { @@ -138,22 +151,14 @@ inline std::ostream& operator<<(std::ostream& out, const FieldValue& v) { template <int width> class FixedWidthValue : public FieldValue::Data { + protected: uint8_t octets[width]; public: FixedWidthValue() {} FixedWidthValue(const uint8_t (&data)[width]) : octets(data) {} - FixedWidthValue(const uint8_t* const data) - { - for (int i = 0; i < width; i++) octets[i] = data[i]; - } - FixedWidthValue(uint64_t v) - { - for (int i = width; i > 1; --i) { - octets[i-1] = (uint8_t) (0xFF & v); v >>= 8; - } - octets[0] = (uint8_t) (0xFF & v); - } + FixedWidthValue(const uint8_t* const data) { std::copy(data, data + width, octets); } + uint32_t encodedSize() const { return width; } void encode(Buffer& buffer) { buffer.putRawData(octets, width); } void decode(Buffer& buffer) { buffer.getRawData(octets, width); } @@ -162,23 +167,37 @@ class FixedWidthValue : public FieldValue::Data { if (rhs == 0) return false; else return std::equal(&octets[0], &octets[width], &rhs->octets[0]); } - - bool convertsToInt() const { return true; } - int64_t getInt() const - { - int64_t v = 0; - for (int i = 0; i < width-1; ++i) { - v |= octets[i]; v <<= 8; - } - v |= octets[width-1]; - return v; - } uint8_t* rawOctets() { return octets; } const uint8_t* rawOctets() const { return octets; } void print(std::ostream& o) const { o << "F" << width << ":"; }; }; +template <class T> class FixedWidthIntValue : public FixedWidthValue<sizeof(T)> { + public: + FixedWidthIntValue(T v = 0) { endian::encodeInt(this->octets, v); } + bool convertsToInt() const { return true; } + int64_t getInt() const { return endian::decodeInt<T>(this->octets); } + bool convertsToFloat() const { return true; } + double getFloat() const { return getInt(); } +}; + +template <class T> class FixedWidthFloatValue : public FixedWidthValue<sizeof(T)> { + public: + FixedWidthFloatValue(T v = 0) { endian::encodeFloat(this->octets, v); } + bool convertsToFloat() const { return true; } + double getFloat() const { return endian::decodeFloat<T>(this->octets); } +}; + +// Dummy implementations that are never used but needed to avoid compile errors. +template <> class FixedWidthFloatValue<uint8_t> : public FixedWidthValue<1> { + FixedWidthFloatValue() { assert(0); } +}; +template <> class FixedWidthFloatValue<uint16_t> : public FixedWidthValue<2> { + FixedWidthFloatValue() { assert(0); } +}; + + class UuidData : public FixedWidthValue<16> { public: UuidData(); @@ -192,13 +211,7 @@ inline T FieldValue::getIntegerValue() const { FixedWidthValue<W>* const fwv = dynamic_cast< FixedWidthValue<W>* const>(data.get()); if (fwv) { - uint8_t* octets = fwv->rawOctets(); - T v = 0; - for (int i = 0; i < W-1; ++i) { - v |= octets[i]; v <<= 8; - } - v |= octets[W-1]; - return v; + return endian::decodeInt<T>(fwv->rawOctets()); } else { throw InvalidConversionException(); } @@ -218,14 +231,9 @@ inline T FieldValue::getIntegerValue() const template <class T, int W> inline T FieldValue::getFloatingPointValue() const { - const FixedWidthValue<W>* fwv = dynamic_cast<FixedWidthValue<W>*>(data.get()); - if (fwv) { - T value; - uint8_t* target = reinterpret_cast<uint8_t*>(&value); - const uint8_t* octets = fwv->rawOctets(); - std::copy(octets, octets + W, target); - convertIfRequired(target, W); - return value; + const FixedWidthFloatValue<T>* fv = dynamic_cast<FixedWidthFloatValue<T>*>(data.get()); + if (fv) { + return endian::decodeFloat<T>(fv->rawOctets()); } else { throw InvalidConversionException(); } @@ -235,23 +243,13 @@ template <int W> void FieldValue::getFixedWidthValue(unsigned char* value) const { FixedWidthValue<W>* const fwv = dynamic_cast< FixedWidthValue<W>* const>(data.get()); if (fwv) { - for (size_t i = 0; i < W; ++i) value[i] = fwv->rawOctets()[i]; + std::copy(fwv->rawOctets(), fwv->rawOctets() + W, value); } else { throw InvalidConversionException(); } } template <> -inline float FieldValue::get<float>() const { - return getFloatingPointValue<float, 4>(); -} - -template <> -inline double FieldValue::get<double>() const { - return getFloatingPointValue<double, 8>(); -} - -template <> class FixedWidthValue<0> : public FieldValue::Data { public: // Implicit default constructor is fine diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp index 81372d87c5..c040f1d433 100644 --- a/qpid/cpp/src/tests/FieldTable.cpp +++ b/qpid/cpp/src/tests/FieldTable.cpp @@ -176,19 +176,19 @@ QPID_AUTO_TEST_CASE(testFloatAndDouble) Buffer rbuffer(buff, 100); FieldTable a; rbuffer.get(a); - BOOST_CHECK(string("abc") == a.getAsString("string")); - BOOST_CHECK(5672 == a.getAsInt("int")); + BOOST_CHECK_EQUAL(string("abc"), a.getAsString("string")); + BOOST_CHECK_EQUAL(5672, a.getAsInt("int")); float f2; BOOST_CHECK(!a.getFloat("string", f2)); BOOST_CHECK(!a.getFloat("int", f2)); BOOST_CHECK(a.getFloat("float", f2)); - BOOST_CHECK(f2 == f); + BOOST_CHECK_EQUAL(f2, f); double d2; BOOST_CHECK(!a.getDouble("string", d2)); BOOST_CHECK(!a.getDouble("int", d2)); BOOST_CHECK(a.getDouble("double", d2)); - BOOST_CHECK(d2 == d); + BOOST_CHECK_EQUAL(d2, d); } } diff --git a/qpid/cpp/src/tests/FieldValue.cpp b/qpid/cpp/src/tests/FieldValue.cpp index 3e2a761c38..2182a43e35 100644 --- a/qpid/cpp/src/tests/FieldValue.cpp +++ b/qpid/cpp/src/tests/FieldValue.cpp @@ -53,22 +53,15 @@ QPID_AUTO_TEST_CASE(testIntegerValueEquals) BOOST_CHECK(IntegerValue(5) != i); BOOST_CHECK(i != s); BOOST_CHECK(i.convertsTo<std::string>() == false); - BOOST_CHECK(i.convertsTo<float>() == false); + BOOST_CHECK(i.convertsTo<float>() == true); BOOST_CHECK(i.convertsTo<int>() == true); BOOST_CHECK_THROW(i.get<std::string>(), InvalidConversionException); - - //FIXME aconway 2015-04-03: fails - //BOOST_CHECK_THROW(i.get<float>(), InvalidConversionException); + BOOST_CHECK_EQUAL(i.get<float>(), 42.0); } QPID_AUTO_TEST_CASE(testFloatValueEquals) { - // FIXME aconway 2015-04-03: The commented out tests are bug QPID-6470. - // The basic problems are: - // - allows meaningles conversion between int and float types. - // - does not allow expected conversion between float and double types. - - // BOOST_CHECK(f.convertsTo<float>() == true); + BOOST_CHECK(f.convertsTo<float>() == true); BOOST_CHECK_EQUAL(FloatValue(42.42), f); BOOST_CHECK_CLOSE(f.get<float>(), 42.42, 0.001); // Check twice, regression test for QPID-6470 where the value was corrupted during get. @@ -76,20 +69,28 @@ QPID_AUTO_TEST_CASE(testFloatValueEquals) BOOST_CHECK_CLOSE(f.get<float>(), 42.42, 0.001); // Float to double conversion - // BOOST_CHECK(f.convertsTo<double>() == true); - // BOOST_CHECK_CLOSE(f.get<double>(), 42.42, 0.001); + BOOST_CHECK(f.convertsTo<double>() == true); + BOOST_CHECK_CLOSE(f.get<double>(), 42.42, 0.001); // Double value + BOOST_CHECK(f.convertsTo<float>() == true); + BOOST_CHECK(f.convertsTo<double>() == true); + BOOST_CHECK_CLOSE(df.get<float>(), 123.123, 0.001); BOOST_CHECK_CLOSE(df.get<double>(), 123.123, 0.001); - // BOOST_CHECK(f.convertsTo<float>() == true); - // BOOST_CHECK(f.convertsTo<double>() == true); - // BOOST_CHECK_CLOSE(df.get<float>(), 123.123, 0.001); // Invalid conversions should fail. BOOST_CHECK(!f.convertsTo<std::string>()); - // BOOST_CHECK(!f.convertsTo<int>()); + BOOST_CHECK(!f.convertsTo<int>()); BOOST_CHECK_THROW(f.get<std::string>(), InvalidConversionException); - // BOOST_CHECK_THROW(f.get<int>(), InvalidConversionException); + BOOST_CHECK_THROW(f.get<int>(), InvalidConversionException); + + // getFloatingPointValue: check twice, regression test for QPID-6470 + BOOST_CHECK_CLOSE((f.getFloatingPointValue<float,sizeof(float)>()), 42.42, 0.001); + BOOST_CHECK_CLOSE((f.getFloatingPointValue<float,sizeof(float)>()), 42.42, 0.001); + BOOST_CHECK_CLOSE((df.getFloatingPointValue<double,sizeof(double)>()), 123.123, 0.001); + // getFloatingPointValue should *not* convert float/double, require exact type. + BOOST_CHECK_THROW((f.getFloatingPointValue<double,sizeof(double)>()), InvalidConversionException); + BOOST_CHECK_THROW((df.getFloatingPointValue<float,sizeof(float)>()), InvalidConversionException); } QPID_AUTO_TEST_SUITE_END() |