Add support for float and double values to libcppbor.
Taken from: http://cl/649433692
Change-Id: Ieeb4da07bb051a756729f44c948d63e61118893f
diff --git a/README.md b/README.md
index 6463766..4558779 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
* Parsing Indefinite length values for major types 2 (byte string) and 3 (text string)
* Writing Indefinite length values
* Semantic tagging
-* Floating point
+* Half floating point
LibCppBor requires C++-17.
@@ -38,7 +38,10 @@
variable-length array of pairs of `Item`s.
* `Simple` corresponds to major type 7. It's an abstract class since
items require more specific type.
-* `Bool` is the only currently-implemented subclass of `Simple`.
+* `Bool` is implemented as a subclass of `Simple`.
+* `Null` is implemented as a subclass of `Simple`.
+* `Float` is implemented as a subclass of `Simple`.
+* `Double` is implemented as a subclass of `Simple`.
Note that major type 6, semantic tag, is not yet implemented.
diff --git a/include/cppbor/cppbor.h b/include/cppbor/cppbor.h
index 043e833..6ea1296 100644
--- a/include/cppbor/cppbor.h
+++ b/include/cppbor/cppbor.h
@@ -18,7 +18,9 @@
#include <algorithm>
#include <cassert>
+#include <cstddef>
#include <cstdint>
+#include <cstring>
#include <functional>
#include <iterator>
#include <memory>
@@ -56,7 +58,9 @@
enum SimpleType {
BOOLEAN,
- NULL_T, // Only two supported, as yet.
+ NULL_T,
+ FLOAT,
+ DOUBLE, // Only four supported, as yet.
};
enum SpecialAddlInfoValues : uint8_t {
@@ -66,7 +70,9 @@
ONE_BYTE_LENGTH = 24,
TWO_BYTE_LENGTH = 25,
FOUR_BYTE_LENGTH = 26,
+ FLOAT_V = 26,
EIGHT_BYTE_LENGTH = 27,
+ DOUBLE_V = 27,
INDEFINITE_LENGTH = 31,
};
@@ -85,6 +91,8 @@
class EncodedItem;
class ViewTstr;
class ViewBstr;
+class Float;
+class Double;
/**
* Returns the size of a CBOR header that contains the additional info value addlInfo.
@@ -148,6 +156,10 @@
const Bool* asBool() const { return const_cast<Item*>(this)->asBool(); }
virtual Null* asNull() { return nullptr; }
const Null* asNull() const { return const_cast<Item*>(this)->asNull(); }
+ virtual Float* asFloat() { return nullptr; }
+ const Float* asFloat() const { return const_cast<Item*>(this)->asFloat(); }
+ virtual Double* asDouble() { return nullptr; }
+ const Double* asDouble() const { return const_cast<Item*>(this)->asDouble(); }
virtual Map* asMap() { return nullptr; }
const Map* asMap() const { return const_cast<Item*>(this)->asMap(); }
@@ -926,6 +938,80 @@
std::unique_ptr<Item> clone() const override { return std::make_unique<Null>(); }
};
+#ifdef __STDC_IEC_559__
+/**
+ * Float is a concrete type that implements CBOR major type 7, with additional item value for
+ * FLOAT.
+ */
+class Float : public Simple {
+ public:
+ static constexpr SimpleType kSimpleType = FLOAT;
+
+ explicit Float(float v) : mValue(v) {}
+
+ SimpleType simpleType() const override { return kSimpleType; }
+ Float* asFloat() override { return this; }
+
+ float value() const { return mValue; }
+ size_t encodedSize() const override { return 5; }
+
+ using Item::encode;
+ uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
+ uint32_t bits;
+ std::memcpy(&bits, &mValue, sizeof(float));
+ return encodeHeader(bits, pos, end);
+ }
+ void encode(EncodeCallback encodeCallback) const override {
+ uint32_t bits;
+ std::memcpy(&bits, &mValue, sizeof(float));
+ encodeHeader(bits, encodeCallback);
+ }
+
+ std::unique_ptr<Item> clone() const override {
+ return std::make_unique<Float>(mValue);
+ }
+
+ private:
+ float mValue;
+};
+
+/**
+ * Double is a concrete type that implements CBOR major type 7, with additional item value for
+ * DOUBLE.
+ */
+class Double : public Simple {
+ public:
+ static constexpr SimpleType kSimpleType = DOUBLE;
+
+ explicit Double(double v) : mValue(v) {}
+
+ SimpleType simpleType() const override { return kSimpleType; }
+ Double* asDouble() override { return this; }
+
+ double value() const { return mValue; }
+ size_t encodedSize() const override { return 9; }
+
+ using Item::encode;
+ uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
+ uint64_t bits;
+ std::memcpy(&bits, &mValue, sizeof(double));
+ return encodeHeader(bits, pos, end);
+ }
+ void encode(EncodeCallback encodeCallback) const override {
+ uint64_t bits;
+ std::memcpy(&bits, &mValue, sizeof(double));
+ encodeHeader(bits, encodeCallback);
+ }
+
+ std::unique_ptr<Item> clone() const override {
+ return std::make_unique<Double>(mValue);
+ }
+
+ private:
+ double mValue;
+};
+#endif // __STDC_IEC_559__
+
/**
* Returns pretty-printed CBOR for |item|
*
diff --git a/src/cppbor.cpp b/src/cppbor.cpp
index d916ce4..14fcdde 100644
--- a/src/cppbor.cpp
+++ b/src/cppbor.cpp
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <openssl/sha.h>
#include <cstdint>
+#include <cstdio>
#include "cppbor_parse.h"
@@ -237,17 +238,35 @@
} break;
case SIMPLE:
- const Bool* asBool = item->asSimple()->asBool();
- const Null* asNull = item->asSimple()->asNull();
- if (asBool != nullptr) {
- out.append(asBool->value() ? "true" : "false");
- } else if (asNull != nullptr) {
- out.append("null");
- } else {
+ switch (item->asSimple()->simpleType()) {
+ case BOOLEAN:
+ out.append(item->asSimple()->asBool()->value() ? "true" : "false");
+ break;
+ case NULL_T:
+ out.append("null");
+ break;
+#ifdef __STDC_IEC_559__
+ case FLOAT:
+ snprintf(buf, sizeof(buf), "%f", item->asSimple()->asFloat()->value());
+ out.append(buf);
+ break;
+ case DOUBLE:
+ snprintf(buf, sizeof(buf), "%f", item->asSimple()->asDouble()->value());
+ out.append(buf);
+ break;
+#else
+ case FLOAT:
+ case DOUBLE:
#ifndef __TRUSTY__
- LOG(ERROR) << "Only boolean/null is implemented for SIMPLE";
+ LOG(ERROR) << "float/double not supported for this platform.";
#endif // __TRUSTY__
- return false;
+ return false;
+ #endif // __STDC_IEC_559__
+ default:
+#ifndef __TRUSTY__
+ LOG(ERROR) << "Only boolean/null/float/double is implemented for SIMPLE";
+#endif // __TRUSTY__
+ return false;
}
break;
}
@@ -372,6 +391,12 @@
return *asBool() == *(other.asBool());
case NULL_T:
return true;
+#ifdef __STDC_IEC_559__
+ case FLOAT:
+ return *asFloat() == *(other.asFloat());
+ case DOUBLE:
+ return *asDouble() == *(other.asDouble());
+#endif // __STDC_IEC_559__
default:
CHECK(false); // Impossible to get here.
return false;
diff --git a/src/cppbor_parse.cpp b/src/cppbor_parse.cpp
index 6b01df3..6c8a3cd 100644
--- a/src/cppbor_parse.cpp
+++ b/src/cppbor_parse.cpp
@@ -17,6 +17,8 @@
#include "cppbor_parse.h"
#include <algorithm>
+#include <cstdint>
+#include <cstring>
#include <memory>
#include <optional>
#include <sstream>
@@ -102,6 +104,28 @@
parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
}
+#ifdef __STDC_IEC_559__
+std::tuple<const uint8_t*, ParseClient*> handleFloat(uint32_t value, const uint8_t* hdrBegin,
+ const uint8_t* hdrEnd,
+ ParseClient* parseClient) {
+ float f;
+ std::memcpy(&f, &value, sizeof(float));
+ std::unique_ptr<Item> item = std::make_unique<Float>(f);
+ return {hdrEnd,
+ parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
+}
+
+std::tuple<const uint8_t*, ParseClient*> handleDouble(uint64_t value, const uint8_t* hdrBegin,
+ const uint8_t* hdrEnd,
+ ParseClient* parseClient) {
+ double d;
+ std::memcpy(&d, &value, sizeof(double));
+ std::unique_ptr<Item> item = std::make_unique<Double>(d);
+ return {hdrEnd,
+ parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
+}
+#endif // __STDC_IEC_559__
+
template <typename T>
std::tuple<const uint8_t*, ParseClient*> handleString(uint64_t length, const uint8_t* hdrBegin,
const uint8_t* valueBegin, const uint8_t* end,
@@ -354,14 +378,25 @@
end, "semantic", emitViews, parseClient, depth);
case SIMPLE:
- switch (*addlData) {
+ switch (tagInt) {
case TRUE:
case FALSE:
return handleBool(*addlData, begin, pos, parseClient);
+#ifdef __STDC_IEC_559__
+ case FLOAT_V:
+ return handleFloat(*addlData, begin, pos, parseClient);
+ case DOUBLE_V:
+ return handleDouble(*addlData, begin, pos, parseClient);
+#else
+ case FLOAT_V:
+ case DOUBLE_V:
+ parseClient->error(begin, "Unsupported floating-point value for platform.");
+ return {begin, nullptr};
+#endif // __STDC_IEC_559__
case NULL_V:
return handleNull(begin, pos, parseClient);
default:
- parseClient->error(begin, "Unsupported floating-point or simple value.");
+ parseClient->error(begin, "Unsupported half-floating-point or simple value.");
return {begin, nullptr};
}
}
diff --git a/tests/cppbor_test.cpp b/tests/cppbor_test.cpp
index b977581..b6f7b05 100644
--- a/tests/cppbor_test.cpp
+++ b/tests/cppbor_test.cpp
@@ -1946,18 +1946,161 @@
auto [item, pos, message] = parse(unassignedSimpleValue);
EXPECT_THAT(item, IsNull());
EXPECT_EQ(pos, unassignedSimpleValue.data());
- EXPECT_EQ("Unsupported floating-point or simple value.", message);
+ EXPECT_EQ("Unsupported half-floating-point or simple value.", message);
}
+#ifdef __STDC_IEC_559__
TEST(FullParserTest, FloatingPointValue) {
vector<uint8_t> floatingPointValue = {0xFA, 0x12, 0x75, 0x34, 0x37};
+ float f_val = 7.737272847557572e-28;
auto [item, pos, message] = parse(floatingPointValue);
- EXPECT_THAT(item, IsNull());
- EXPECT_EQ(pos, floatingPointValue.data());
- EXPECT_EQ("Unsupported floating-point or simple value.", message);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asFloat()->value(), f_val);
+
+ Float f(f_val);
+ EXPECT_EQ(f.encode(), floatingPointValue);
}
+TEST(FullParserTest, PositiveInfinityFloatingPointValue) {
+ vector<uint8_t> floatingPointValue = {0xFA, 0x7F, 0x80, 0x00, 0x00};
+ float f_val = std::numeric_limits<float>::infinity();
+
+ auto [item, pos, message] = parse(floatingPointValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asFloat()->value(), f_val);
+
+ Float f(f_val);
+ EXPECT_EQ(f.encode(), floatingPointValue);
+}
+
+TEST(FullParserTest, NegativeInfinityFloatingPointValue) {
+ vector<uint8_t> floatingPointValue = {0xFA, 0xFF, 0x80, 0x00, 0x00};
+ float f_val = -std::numeric_limits<float>::infinity();
+
+ auto [item, pos, message] = parse(floatingPointValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asFloat()->value(), f_val);
+
+ Float f(f_val);
+ EXPECT_EQ(f.encode(), floatingPointValue);
+}
+
+TEST(FullParserTest, QuietNaNFloatingPointValue) {
+ vector<uint8_t> floatingPointValue = {0xFA, 0x7F, 0xC0, 0x00, 0x00};
+
+ auto [item, pos, message] = parse(floatingPointValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_TRUE(std::isnan(item->asSimple()->asFloat()->value()));
+
+ float f_val = std::numeric_limits<float>::quiet_NaN();
+ Float f(f_val);
+ EXPECT_EQ(f.encode(), floatingPointValue);
+}
+
+TEST(FullParserTest, MaxFloatingPointValue) {
+ vector<uint8_t> floatingPointValue = {0xFA, 0x7F, 0x7F, 0xFF, 0xFF};
+ float f_val = std::numeric_limits<float>::max();
+
+ auto [item, pos, message] = parse(floatingPointValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asFloat()->value(), f_val);
+
+ Float f(f_val);
+ EXPECT_EQ(f.encode(), floatingPointValue);
+}
+
+TEST(FullParserTest, MinFloatingPointValue) {
+ vector<uint8_t> floatingPointValue = {0xFA, 0x00, 0x80, 0x00, 0x00};
+ float f_val = std::numeric_limits<float>::min();
+
+ auto [item, pos, message] = parse(floatingPointValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asFloat()->value(), f_val);
+
+ Float f(f_val);
+ EXPECT_EQ(f.encode(), floatingPointValue);
+}
+
+TEST(FullParserTest, DoubleValue) {
+ vector<uint8_t> doubleValue =
+ {0xFB, 0x40, 0x09, 0x21, 0xFB, 0x4D, 0x12, 0xD8, 0x4A};
+ double d_val = 3.1415926000000001;
+
+ auto [item, pos, message] = parse(doubleValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asDouble()->value(), d_val);
+
+ Double d(d_val);
+ EXPECT_EQ(d.encode(), doubleValue);
+}
+
+TEST(FullParserTest, PositiveInfinityDoubleValue) {
+ vector<uint8_t> doubleValue =
+ {0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ double d_val = std::numeric_limits<double>::infinity();
+
+ auto [item, pos, message] = parse(doubleValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asDouble()->value(), d_val);
+
+ Double d(d_val);
+ EXPECT_EQ(d.encode(), doubleValue);
+}
+
+TEST(FullParserTest, NegativeInfinityDoubleValue) {
+ vector<uint8_t> doubleValue =
+ {0xFB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ double d_val = -std::numeric_limits<double>::infinity();
+
+ auto [item, pos, message] = parse(doubleValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asDouble()->value(), d_val);
+
+ Double d(d_val);
+ EXPECT_EQ(d.encode(), doubleValue);
+}
+
+TEST(FullParserTest, QuietNaNDoubleValue) {
+ vector<uint8_t> doubleValue =
+ {0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ auto [item, pos, message] = parse(doubleValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_TRUE(std::isnan(item->asSimple()->asDouble()->value()));
+
+ double d_val = std::numeric_limits<double>::quiet_NaN();
+ Double d(d_val);
+ EXPECT_EQ(d.encode(), doubleValue);
+}
+
+TEST(FullParserTest, MaxDoubleValue) {
+ vector<uint8_t> doubleValue =
+ {0xFB, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ double d_val = std::numeric_limits<double>::max();
+
+ auto [item, pos, message] = parse(doubleValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asDouble()->value(), d_val);
+
+ Double d(d_val);
+ EXPECT_EQ(d.encode(), doubleValue);
+}
+
+TEST(FullParserTest, MinDoubleValue) {
+ vector<uint8_t> doubleValue =
+ {0xFB, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ double d_val = std::numeric_limits<double>::min();
+
+ auto [item, pos, message] = parse(doubleValue);
+ EXPECT_THAT(item, NotNull());
+ EXPECT_EQ(item->asSimple()->asDouble()->value(), d_val);
+
+ Double d(d_val);
+ EXPECT_EQ(d.encode(), doubleValue);
+}
+#endif // __STDC_IEC_559__
+
TEST(MapGetValueByKeyTest, Map) {
Array compoundItem(1, 2, 3, 4, 5, Map(4, 5, "a", "b"));
auto clone = compoundItem.clone();