diff --git a/implementations/cpp/Makefile b/implementations/cpp/Makefile index 173c8a8..651ec38 100644 --- a/implementations/cpp/Makefile +++ b/implementations/cpp/Makefile @@ -1,9 +1,11 @@ -CXX=g++ -std=c++14 -Wall -Wextra -Werror -O0 -I googletest +CXX=g++ -std=c++14 -Wall -Wextra -Werror -g -O0 -I googletest -test: m +test: all ./m -m: main.cpp preserves.hpp preserves_text.hpp googletest.a +all: m + +m: main.cpp preserves.hpp preserves_text.hpp preserves_binary.hpp googletest.a $(CXX) -o $@ main.cpp googletest.a googletest.a: googletest/src/gtest-all.o googletest/src/gtest_main.o diff --git a/implementations/cpp/preserves.hpp b/implementations/cpp/preserves.hpp index 84d8e08..aa019c8 100644 --- a/implementations/cpp/preserves.hpp +++ b/implementations/cpp/preserves.hpp @@ -63,6 +63,7 @@ namespace Preserves { static Value from_unsigned(uint64_t i) { return from_int(i); } static Value from_signed(int64_t i) { return from_int(i); } + static Value from_bignum(std::vector const& v); static Value from_number(uint64_t i) { return from_int(i); } static Value from_number(int64_t i) { return from_int(i); } @@ -120,6 +121,9 @@ namespace Preserves { boost::optional as_signed() const; int64_t to_signed() const { return as_signed().value(); } + boost::optional const&> as_bignum() const; + std::vector const& to_bignum() const { return as_bignum().value(); } + boost::optional as_string() const; std::string const& to_string() const { return as_string().value(); } @@ -166,6 +170,7 @@ namespace Preserves { virtual boost::optional as_double() const { return boost::none; } virtual boost::optional as_unsigned() const { return boost::none; } virtual boost::optional as_signed() const { return boost::none; } + virtual boost::optional const&> as_bignum() const { return boost::none; } virtual boost::optional as_string() const { return boost::none; } virtual boost::optional const&> as_bytes() const { return boost::none; } virtual boost::optional as_symbol() const { return boost::none; } @@ -267,6 +272,7 @@ namespace Preserves { return boost::none; } }); + PRESERVES_ATOMIC_VALUE_CLASS(BigNum, std::vector, std::vector const&, ValueKind::SignedInteger, as_bignum,); PRESERVES_ATOMIC_VALUE_CLASS(String, std::string, std::string const&, ValueKind::String, as_string,); PRESERVES_ATOMIC_VALUE_CLASS(ByteString, std::vector, std::vector const&, ValueKind::ByteString, as_bytes,); PRESERVES_ATOMIC_VALUE_CLASS(Symbol, std::string, std::string const&, ValueKind::Symbol, as_symbol,); @@ -458,6 +464,7 @@ namespace Preserves { PRESERVES_DELEGATE_CAST(double, as_double); PRESERVES_DELEGATE_CAST(uint64_t, as_unsigned); PRESERVES_DELEGATE_CAST(int64_t, as_signed); + PRESERVES_DELEGATE_CAST(std::vector const&, as_bignum); PRESERVES_DELEGATE_CAST(std::string const&, as_string); PRESERVES_DELEGATE_CAST(std::vector const&, as_bytes); PRESERVES_DELEGATE_CAST(std::string const&, as_symbol); @@ -473,6 +480,21 @@ namespace Preserves { template boost::optional> Value::get(size_t index) const { return p->get(index); } template size_t Value::size() const { return p->size(); } + bool bignum_lt(std::vector const& a, std::vector const& b) { + bool aNegative = (a.size() > 0) && (a[0] & 0x80); + bool bNegative = (b.size() > 0) && (b[0] & 0x80); + if (aNegative != bNegative) return aNegative; + if (aNegative) { + if (a.size() > b.size()) return true; + if (a.size() < b.size()) return false; + return a < b; + } else { + if (a.size() > b.size()) return false; + if (a.size() < b.size()) return true; + return a < b; + } + } + template bool operator<(Value const& a, Value const &b) { auto aKind = a.value_kind(); @@ -494,7 +516,19 @@ namespace Preserves { if (auto bv = b.as_signed()) { return false; } else { - return a.to_unsigned() < b.to_unsigned(); + if (auto av = a.as_unsigned()) { + if (auto bv = b.as_unsigned()) { + return *av < *bv; + } else { + return true; + } + } else { + if (auto bv = b.as_unsigned()) { + return false; + } else { + return bignum_lt(a.to_bignum(), b.to_bignum()); + } + } } } } diff --git a/implementations/cpp/preserves_binary.hpp b/implementations/cpp/preserves_binary.hpp index 50cbbc4..154dffe 100644 --- a/implementations/cpp/preserves_binary.hpp +++ b/implementations/cpp/preserves_binary.hpp @@ -45,9 +45,11 @@ namespace Preserves { // Can read max 9 bytes, each with 7 bits of payload, for 9*7 = 63 bits. for (size_t count = 0; count < 9; count++) { int b = i.get(); - if (b == i.eof()) return boost::none; + if (i.eof()) return boost::none; n |= (b & 0x7f) << (count * 7); - if ((b & 0x80) == 0) return n; + if ((b & 0x80) == 0) { + return n; + } } return boost::none; } @@ -87,6 +89,13 @@ namespace Preserves { return v; } + boost::optional> next_bignum(size_t n) { + auto b = std::make_shared>(std::vector()); + b->_value().resize(n); + if (!next_chunk(&b->_value()[0], n)) return boost::none; + return Value(b); + } + boost::optional> next() { bool end_sentinel; return _next(end_sentinel); @@ -95,7 +104,10 @@ namespace Preserves { boost::optional> _next(bool& end_sentinel) { more: end_sentinel = false; - switch (auto tag = BinaryTag(i.get())) { + auto tag = BinaryTag(i.get()); + // std::cout << "tag " << std::hex << int(tag) << " pos " << i.tellg() - 1 << std::endl; + if (i.eof()) return boost::none; + switch (tag) { case BinaryTag::False: return Value::from_bool(false); case BinaryTag::True: return Value::from_bool(true); case BinaryTag::Float: return next_float().map(Value::from_float); @@ -117,13 +129,14 @@ namespace Preserves { return next_unsigned(n).map([](uint64_t v) { return Value::from_int(int64_t(v)); }); } if (n == 9) { - // We can only handle this if it's unsigned and the first byte is 0. - if (i.get() != 0) return boost::none; - return next_unsigned(8).map(Value::from_unsigned); + // We can handle this with uint64_t if it's unsigned and the first byte is 0. + if (i.get() == 0) return next_unsigned(8).map(Value::from_unsigned); } - return boost::none; + return next_bignum(n); } - case BinaryTag::SignedInteger: return boost::none; + case BinaryTag::SignedInteger: return varint(i).flat_map([&](size_t len) { + return next_bignum(len); + }); case BinaryTag::String: return varint(i).flat_map([&](size_t len)-> boost::optional> { auto s = std::make_shared>(std::string()); s->_value().resize(len); @@ -161,7 +174,7 @@ namespace Preserves { if (!v) return boost::none; s->values.push_back(*v); } - } + } case BinaryTag::Set: { auto s = std::make_shared>(); while (true) { @@ -189,4 +202,4 @@ namespace Preserves { } } }; -} \ No newline at end of file +}