From 9c5368b69bd8867efe0299d3aaabcd6536c8447c Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Fri, 23 Jun 2023 15:40:05 +0200 Subject: [PATCH] Binary writer draft --- implementations/cpp/Makefile | 4 +- implementations/cpp/main.cpp | 1 - implementations/cpp/preserves.hpp | 549 +----------------- ...binary.hpp => preserves_binary_reader.hpp} | 27 +- .../cpp/preserves_binary_writer.hpp | 250 ++++++++ implementations/cpp/preserves_impl.hpp | 315 ++++++++++ implementations/cpp/preserves_value.hpp | 296 ++++++++++ 7 files changed, 871 insertions(+), 571 deletions(-) rename implementations/cpp/{preserves_binary.hpp => preserves_binary_reader.hpp} (92%) create mode 100644 implementations/cpp/preserves_binary_writer.hpp create mode 100644 implementations/cpp/preserves_impl.hpp create mode 100644 implementations/cpp/preserves_value.hpp diff --git a/implementations/cpp/Makefile b/implementations/cpp/Makefile index 651ec38..13d7080 100644 --- a/implementations/cpp/Makefile +++ b/implementations/cpp/Makefile @@ -1,11 +1,13 @@ CXX=g++ -std=c++14 -Wall -Wextra -Werror -g -O0 -I googletest +HEADERS=$(wildcard preserves*.hpp) + test: all ./m all: m -m: main.cpp preserves.hpp preserves_text.hpp preserves_binary.hpp googletest.a +m: main.cpp $(HEADERS) 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/main.cpp b/implementations/cpp/main.cpp index c6a5e3e..38be6cb 100644 --- a/implementations/cpp/main.cpp +++ b/implementations/cpp/main.cpp @@ -1,5 +1,4 @@ #include "preserves.hpp" -#include "preserves_binary.hpp" #include diff --git a/implementations/cpp/preserves.hpp b/implementations/cpp/preserves.hpp index aa019c8..e16c3f5 100644 --- a/implementations/cpp/preserves.hpp +++ b/implementations/cpp/preserves.hpp @@ -1,546 +1,7 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace Preserves { - - enum class ValueKind { - Boolean, - Float, - Double, - SignedInteger, - String, - ByteString, - Symbol, - - Record, - Sequence, - Set, - Dictionary, - - Embedded, - }; - - template class Record; - template class ValueImpl; - - template - class Value { - std::shared_ptr> p; - - public: - Value(std::shared_ptr> const& p) : p(p) {} - Value(ValueImpl *p) : p(p) {} - - std::shared_ptr> _impl() const { return p; } - - static Value from_bool(bool b); - static Value from_float(float f); - static Value from_double(double d); - static Value from_int(uint64_t i); - static Value from_int(int64_t i); - static Value from_string(std::string const& s); - static Value from_bytes(std::vector const& v); - static Value from_bytes(std::vector const& v); - static Value from_bytes(void *p, size_t len); - static Value from_symbol(std::string const& s); - - static Value record(Record const& r); - static Value record(Value const& label, std::vector const& fields); - static Value sequence(std::vector const& items); - static Value set(std::set const& items); - static Value dictionary(std::map const& entries); - - static Value from_embedded(std::shared_ptr const& p); - - 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); } - static Value from_number(float f) { return from_float(f); } - static Value from_number(double d) { return from_double(d); } - - static Value from(bool b) { return from_bool(b); } - static Value from(float f) { return from_float(f); } - static Value from(double d) { return from_double(d); } - static Value from(uint64_t i) { return from_int(i); } - static Value from(unsigned i) { return from_int(uint64_t(i)); } - static Value from(int64_t i) { return from_int(i); } - static Value from(signed i) { return from_int(int64_t(i)); } - static Value from(std::string const& s) { return from_string(s); } - static Value from(char const* s) { return from_string(s); } - static Value from(std::vector const& v) { return from_bytes(v); } - static Value from(std::vector const& v) { return from_bytes(v); } - static Value from(void *p, size_t len) { return from_bytes(p, len); } - static Value from(Record const& r) { return record(r); } - static Value from(Value const& label, std::vector const& fields) { return record(label, fields); } - static Value from(std::vector const& items) { return sequence(items); } - static Value from(std::set const& items) { return set(items); } - static Value from(std::map const& entries) { return dictionary(entries); } - - ValueImpl& operator*() const { return *p; } - ValueImpl* operator->() const { return p.get(); } - - ValueKind value_kind() const; - bool is_mutable() const; - - bool is_bool() const { return value_kind() == ValueKind::Boolean; } - bool is_float() const { return value_kind() == ValueKind::Float; } - bool is_double() const { return value_kind() == ValueKind::Double; } - bool is_int() const { return value_kind() == ValueKind::SignedInteger; } - bool is_string() const { return value_kind() == ValueKind::String; } - bool is_bytes() const { return value_kind() == ValueKind::ByteString; } - bool is_symbol() const { return value_kind() == ValueKind::Symbol; } - bool is_record() const { return value_kind() == ValueKind::Record; } - bool is_sequence() const { return value_kind() == ValueKind::Sequence; } - bool is_set() const { return value_kind() == ValueKind::Set; } - bool is_dictionary() const { return value_kind() == ValueKind::Dictionary; } - - boost::optional as_bool() const; - bool to_bool() const { return as_bool().value(); } - - boost::optional as_float() const; - float to_float() const { return as_float().value(); } - - boost::optional as_double() const; - double to_double() const { return as_double().value(); } - - boost::optional as_unsigned() const; - uint64_t to_unsigned() const { return as_unsigned().value(); } - - 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(); } - - boost::optional const&> as_bytes() const; - std::vector const& to_bytes() const { return as_bytes().value(); } - - boost::optional as_symbol() const; - std::string const& to_symbol() const { return as_symbol().value(); } - - boost::optional const&> as_record() const; - Record const& to_record() const { return as_record().value(); }; - - boost::optional const&> as_sequence() const; - std::vector const& to_sequence() const { return as_sequence().value(); } - - boost::optional const&> as_set() const; - std::set const& to_set() const { return as_set().value(); } - - boost::optional const&> as_dictionary() const; - std::map const& to_dictionary() const { return as_dictionary().value(); } - - boost::optional> as_embedded() const; - std::shared_ptr to_embedded() const { return as_embedded().value(); } - - boost::optional label() const; - size_t size() const; - bool contains(Value const& key) const; - boost::optional get(Value const& key) const; - Value operator[](Value const& key) const { return get(key).value(); } - boost::optional get(size_t index) const; - Value operator[](size_t index) const { return get(index).value(); } - }; - - template - class ValueImpl { - public: - virtual ~ValueImpl() {} - - virtual ValueKind value_kind() const = 0; - virtual bool is_mutable() const { return false; } - - virtual boost::optional as_bool() const { return boost::none; } - virtual boost::optional as_float() const { return boost::none; } - 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; } - virtual boost::optional const&> as_record() const { return boost::none; } - virtual boost::optional> const&> as_sequence() const { return boost::none; } - virtual boost::optional> const&> as_set() const { return boost::none; } - virtual boost::optional,Value> const&> as_dictionary() const { return boost::none; } - virtual boost::optional> as_embedded() const { return boost::none; } - - virtual boost::optional> label() const { return boost::none; } - virtual size_t size() const { return 0; } - virtual bool contains(Value const& /* key */) const { return false; } - virtual boost::optional> get(Value const& /* key */) const { return boost::none; } - virtual boost::optional> get(size_t /* index */) const { return boost::none; } - - virtual bool add(Value const& /* item */) { - throw std::runtime_error("Cannot add item to Preserves value"); - } - virtual bool set(Value const& /* key */, Value const& /* value */) { - throw std::runtime_error("Cannot set item by key in Preserves value"); - } - virtual bool set(size_t /* index */, Value const& /* value */) { - throw std::runtime_error("Cannot set item by index in Preserves value"); - } - virtual bool erase(Value const& /* key */) { - throw std::runtime_error("Cannot erase item in Preserves value"); - } - }; - - template inline ValueKind Value::value_kind() const { return p->value_kind(); } - template inline bool Value::is_mutable() const { return p->is_mutable(); } - - template - class Atomic: public ValueImpl { - protected: - Atom value; - public: - Atomic(Atom const& value) : value(value) {} - Atom& _value() { return value; } - ValueKind value_kind() const { return kind; } - }; - -#define PRESERVES_ATOMIC_VALUE_CLASS(Name, a_t, r_t, VK, getter, extra) \ - template \ - class Name: public Atomic { \ - public: \ - Name(a_t const& value) : Atomic(value) {} \ - boost::optional getter() const override { return this->value; } \ - extra \ - } - - PRESERVES_ATOMIC_VALUE_CLASS(Boolean, bool, bool, ValueKind::Boolean, as_bool,); - PRESERVES_ATOMIC_VALUE_CLASS(Float, float, float, ValueKind::Float, as_float, - boost::optional as_double() const override { - return this->value; - }); - PRESERVES_ATOMIC_VALUE_CLASS(Double, double, double, ValueKind::Double, as_double,); - PRESERVES_ATOMIC_VALUE_CLASS(Uint64, uint64_t, uint64_t, ValueKind::SignedInteger, as_unsigned, - boost::optional as_signed() const override { - if (this->value <= uint64_t(std::numeric_limits::max())) { - return this->value; - } else { - return boost::none; - } - } - boost::optional as_float() const override { - if (uint64_t(float(this->value)) == this->value) { - return float(this->value); - } else { - return boost::none; - } - } - boost::optional as_double() const override { - if (uint64_t(double(this->value)) == this->value) { - return double(this->value); - } else { - return boost::none; - } - }); - PRESERVES_ATOMIC_VALUE_CLASS(Int64, int64_t, int64_t, ValueKind::SignedInteger, as_signed, - boost::optional as_unsigned() const override { - if (this->value >= 0) { - return this->value; - } else { - return boost::none; - } - } - boost::optional as_float() const override { - if (int64_t(float(this->value)) == this->value) { - return float(this->value); - } else { - return boost::none; - } - } - boost::optional as_double() const override { - if (int64_t(double(this->value)) == this->value) { - return double(this->value); - } else { - 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,); - - template - class Record: public ValueImpl { - public: - Value labelValue; - std::vector> fields; - - Record(Value const& label) : labelValue(label), fields() {} - Record(Value const& label, std::vector> const& fields) : labelValue(label), fields(fields) {} - ValueKind value_kind() const { return ValueKind::Record; } - boost::optional> label() const override { return labelValue; } - size_t size() const { return fields.size(); } - boost::optional> get(size_t index) const { - if (index < size()) { - return fields[index]; - } else { - return boost::none; - } - } - bool add(Value const& value) override { - fields.push_back(value); - return true; - } - bool set(size_t index, Value const& value) override { - if (index < size()) { - fields[index] = value; - } - return false; - } - bool operator<(Record const& other) const { - if (labelValue < other.labelValue) return true; - if (other.labelValue < labelValue) return false; - return fields < other.fields; - } - }; - - template - class Sequence: public ValueImpl { - public: - std::vector> values; - - Sequence() : values() {} - Sequence(std::vector> const& values) : values(values) {} - ValueKind value_kind() const { return ValueKind::Sequence; } - boost::optional> const&> as_sequence() const override { - return values; - } - size_t size() const override { return values.size(); } - boost::optional> get(size_t index) const override { - if (index < size()) { - return values[index]; - } else { - return boost::none; - } - } - bool add(Value const& value) override { - values.push_back(value); - return true; - } - bool set(size_t index, Value const& value) override { - if (index < size()) { - values[index] = value; - } - return false; - } - }; - - template - class Set: public ValueImpl { - public: - std::set> values; - - Set() : values() {} - Set(std::set> const& values) : values(values) {} - ValueKind value_kind() const { return ValueKind::Set; } - boost::optional> const&> as_set() const override { - return values; - } - size_t size() const override { return values.size(); } - bool contains(Value const& key) const override { return values.count(key) > 0; } - bool add(Value const& value) override { - return values.insert(value).second; - } - bool erase(Value const& value) override { - return values.erase(value) > 0; - } - }; - - template - class Dictionary: public ValueImpl { - public: - std::map, Value> values; - - Dictionary() : values() {} - Dictionary(std::map, Value> const& values) : values(values) {} - ValueKind value_kind() const { return ValueKind::Dictionary; } - boost::optional, Value> const&> as_dictionary() const override { - return values; - } - size_t size() const override { return values.size(); } - bool contains(Value const& key) const override { return values.count(key) > 0; } - boost::optional> get(Value const& key) const override { - auto i = values.find(key); - if (i == values.end()) return boost::none; - return i->second; - } - bool set(Value const& key, Value const& value) override { - return values.emplace(key, value).second; - } - bool erase(Value const& key) override { - return values.erase(key) > 0; - } - }; - - template - class Embedded: public ValueImpl { - public: - std::shared_ptr value; - - Embedded(std::shared_ptr const& value) : value(value) {} - ValueKind value_kind() const { return ValueKind::Embedded; } - boost::optional> as_embedded() const override { - return value; - } - }; - - class GenericEmbedded: public Value { - public: - GenericEmbedded(std::shared_ptr> p) : - Value(p) - {} - - static std::shared_ptr wrap(Value<> v) { - return std::make_shared(v._impl()); - } - }; - - template - Value Value::from_bool(bool b) - { - return Value(new Boolean(b)); - } - - template - Value Value::from_float(float f) - { - return Value(new Float(f)); - } - - template - Value Value::from_double(double d) - { - return Value(new Double(d)); - } - - template - Value Value::from_int(uint64_t i) - { - return Value(new Uint64(i)); - } - - template - Value Value::from_int(int64_t i) { - return Value(new Int64(i)); - } - - template - Value Value::from_string(std::string const& s) { - return Value(new String(s)); - } - - template - Value Value::sequence(std::vector> const& values) { - return Value(new Sequence(values)); - } - - template - Value Value::from_embedded(std::shared_ptr const& v) { - return Value(new Embedded(v)); - } - -#define PRESERVES_DELEGATE_CAST(t, name) \ - template boost::optional Value::name() const { return p->name(); } - PRESERVES_DELEGATE_CAST(bool, as_bool); - PRESERVES_DELEGATE_CAST(float, as_float); - 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); - PRESERVES_DELEGATE_CAST(Record const&, as_record); - PRESERVES_DELEGATE_CAST(std::vector> const&, as_sequence); - PRESERVES_DELEGATE_CAST(std::set> const&, as_set); - #define COMMA , - PRESERVES_DELEGATE_CAST(std::map COMMA Value> const&, as_dictionary); - #undef COMMA - PRESERVES_DELEGATE_CAST(std::shared_ptr, as_embedded); -#undef PRESERVES_DELEGATE_CAST - - 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(); - auto bKind = b.value_kind(); - if (aKind < bKind) return true; - if (bKind < aKind) return false; - switch (aKind) { - case ValueKind::Boolean: return a.to_bool() < b.to_bool(); - case ValueKind::Float: return a.to_float() < b.to_float(); - case ValueKind::Double: return a.to_double() < b.to_double(); - case ValueKind::SignedInteger: { - if (auto av = a.as_signed()) { - if (auto bv = b.as_signed()) { - return *av < *bv; - } else { - return true; - } - } else { - if (auto bv = b.as_signed()) { - return false; - } else { - 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()); - } - } - } - } - } - case ValueKind::String: return a.to_string() < b.to_string(); - case ValueKind::ByteString: return a.to_bytes() < b.to_bytes(); - case ValueKind::Symbol: return a.to_symbol() < b.to_symbol(); - case ValueKind::Record: return a.to_record() < b.to_record(); - case ValueKind::Sequence: return a.to_sequence() < b.to_sequence(); - case ValueKind::Set: return a.to_set() < b.to_set(); - case ValueKind::Dictionary: return a.to_dictionary() < b.to_dictionary(); - case ValueKind::Embedded: return *a.to_embedded() < *b.to_embedded(); - default: throw std::runtime_error("Invalid ValueKind"); - } - } -} \ No newline at end of file +#include "preserves_value.hpp" +#include "preserves_text.hpp" +#include "preserves_binary_writer.hpp" +#include "preserves_impl.hpp" +#include "preserves_binary_reader.hpp" diff --git a/implementations/cpp/preserves_binary.hpp b/implementations/cpp/preserves_binary_reader.hpp similarity index 92% rename from implementations/cpp/preserves_binary.hpp rename to implementations/cpp/preserves_binary_reader.hpp index 154dffe..bdae954 100644 --- a/implementations/cpp/preserves_binary.hpp +++ b/implementations/cpp/preserves_binary_reader.hpp @@ -1,35 +1,12 @@ #pragma once +#include #include #include #include -#include "preserves.hpp" - namespace Preserves { - enum class BinaryTag { - False = 0x80, - True = 0x81, - Float = 0x82, - Double = 0x83, - End = 0x84, - Annotation = 0x85, - Embedded = 0x86, - SmallInteger_lo = 0x90, - SmallInteger_hi = 0x9f, - MediumInteger_lo = 0xa0, - MediumInteger_hi = 0xaf, - SignedInteger = 0xb0, - String = 0xb1, - ByteString = 0xb2, - Symbol = 0xb3, - Record = 0xb4, - Sequence = 0xb5, - Set = 0xb6, - Dictionary = 0xb7, - }; - - template + template class BinaryReader { std::istream& i; std::function(Value<>)> decodeEmbedded; diff --git a/implementations/cpp/preserves_binary_writer.hpp b/implementations/cpp/preserves_binary_writer.hpp new file mode 100644 index 0000000..f8a399e --- /dev/null +++ b/implementations/cpp/preserves_binary_writer.hpp @@ -0,0 +1,250 @@ +#pragma once + +#include +#include +#include +#include + +namespace Preserves { + enum class BinaryTag { + False = 0x80, + True = 0x81, + Float = 0x82, + Double = 0x83, + End = 0x84, + Annotation = 0x85, + Embedded = 0x86, + SmallInteger_lo = 0x90, + SmallInteger_hi = 0x9f, + MediumInteger_lo = 0xa0, + MediumInteger_hi = 0xaf, + SignedInteger = 0xb0, + String = 0xb1, + ByteString = 0xb2, + Symbol = 0xb3, + Record = 0xb4, + Sequence = 0xb5, + Set = 0xb6, + Dictionary = 0xb7, + }; + + class BinaryWriter { + std::ostream& o; + + public: + BinaryWriter(std::ostream& o) : o(o) {} + + std::ostream& stream() const { return o; } + + BinaryWriter& put(uint8_t b) { + o.put(b); + return *this; + } + + BinaryWriter& operator<<(BinaryTag const& t) { + return put(uint8_t(t)); + } + + BinaryWriter& write(void const* buf, size_t size) { + o.write(reinterpret_cast(buf), size); + return *this; + } + + BinaryWriter& varint(size_t n) { + while (n >= 0x80) { + put(uint8_t(0x80 | (n & 0x7f))); + n >>= 7; + } + return put(uint8_t(n & 0x7f)); + } + + BinaryWriter& write(BinaryTag const& tag, void const* buf, size_t size) { + (*this) << tag; + varint(size); + return write(buf, size); + } + + BinaryWriter& bignum(std::vector const& n) { + if (n.size() == 0) return (*this) << BinaryTag::SmallInteger_lo; + size_t startOffset = 0; + if (n[0] & 0x80) { + if (n.size() == 1 && int8_t(n[0]) >= -3) { + return put(uint8_t(int(BinaryTag::SmallInteger_lo) + 16 + int8_t(n[0]))); + } else { + while (true) { + if (startOffset == n.size() - 1) break; + if (n[startOffset] != 0xff) break; + startOffset++; + if ((n[startOffset] & 0x80) != 0x80) { + startOffset--; + break; + } + } + } + } else { + if (n.size() == 1 && n[0] <= 12) { + return put(uint8_t(int(BinaryTag::SmallInteger_lo) + n[0])); + } else { + while (true) { + if (startOffset == n.size() - 1) break; + if (n[startOffset] != 0x00) break; + startOffset++; + if ((n[startOffset] & 0x80) != 0x00) { + startOffset--; + break; + } + } + } + } + size_t count = n.size() - startOffset; + if (count > 16) { + (*this) << BinaryTag::SignedInteger; + varint(count); + } else { + put(uint8_t(int(BinaryTag::MediumInteger_lo) + count - 1)); + } + return write(&n[startOffset], count); + } + + BinaryWriter& string(std::string const& s) { + return write(BinaryTag::String, &s[0], s.size()); + } + + BinaryWriter& symbol(std::string const& s) { + return write(BinaryTag::Symbol, &s[0], s.size()); + } + + BinaryWriter& bytes(std::vector const& v) { + return bytes(v.data(), v.size()); + } + + BinaryWriter& bytes(void const* buf, size_t size) { + return write(BinaryTag::ByteString, buf, size); + } + + template + BinaryWriter& operator<<(Value const& v) { + return v->write(*this); + } + + template + BinaryWriter& writeseq(Iter const& begin, Iter const& end) { + for (Iter i = begin; i != end; ++i) (*this) << (*i); + return *this; + } + + template + BinaryWriter& operator<<(Collection const& c) { + return writeseq(c.begin(), c.end()); + } + + template + BinaryWriter& record(Value const& label, std::vector> const& fields) { + return (*this) << BinaryTag::Record << label << fields << BinaryTag::End; + } + + template + BinaryWriter& sequence(std::vector> const& vs) { + return (*this) << BinaryTag::Sequence << vs << BinaryTag::End; + } + + template + BinaryWriter& set(std::set> const& vs) { + return (*this) << BinaryTag::Set << vs << BinaryTag::End; + } + + template + BinaryWriter& dictionary(std::map, Value> const& vs) { + (*this) << BinaryTag::Dictionary; + for (auto& i : vs) { (*this) << i.first << i.second; } + return (*this) << BinaryTag::End; + } + }; + + inline BinaryWriter& operator<<(BinaryWriter& w, bool b) { + return w << (b ? BinaryTag::True : BinaryTag::False); + } + + inline BinaryWriter& operator<<(BinaryWriter& w, float f) { + uint32_t n; + memcpy(&n, &f, sizeof(f)); + uint8_t buf[4]; + buf[0] = (n >> 24) & 0xff; + buf[1] = (n >> 16) & 0xff; + buf[2] = (n >> 8) & 0xff; + buf[3] = (n) & 0xff; + w << BinaryTag::Float; + return w.write(buf, sizeof(buf)); + } + + inline BinaryWriter& operator<<(BinaryWriter& w, double d) { + uint64_t n; + memcpy(&n, &d, sizeof(d)); + uint8_t buf[8]; + buf[0] = (n >> 56) & 0xff; + buf[1] = (n >> 48) & 0xff; + buf[2] = (n >> 40) & 0xff; + buf[3] = (n >> 32) & 0xff; + buf[4] = (n >> 24) & 0xff; + buf[5] = (n >> 16) & 0xff; + buf[6] = (n >> 8) & 0xff; + buf[7] = (n) & 0xff; + w << BinaryTag::Double; + return w.write(buf, sizeof(buf)); + } + + template + int _shift_for(uint64_t u) { + int shift = 56; + while (true) { + if (shift == 0) break; + if (((u >> shift) & 0xff) != wholeTest) break; + shift -= 8; + if (((u >> shift) & 0x80) != bitTest) { + shift += 8; + break; + } + } + return shift; + } + + inline BinaryWriter& _put_medium_int(BinaryWriter& w, uint64_t u, int shift, int extra) { + w.put(uint8_t(int(BinaryTag::MediumInteger_lo) + (shift >> 3) + extra)); + if (extra) w.put(0); + while (shift >= 0) { + w.put(uint8_t((u >> shift) & 0xff)); + shift -= 8; + } + return w; + } + + inline BinaryWriter& operator<<(BinaryWriter& w, int64_t i) { + if (i < 0) { + if (i >= -3) { + return w.put(uint8_t(int(BinaryTag::SmallInteger_lo) + 16 + i)); + } else { + uint64_t u; + memcpy(&u, &i, sizeof(i)); + return _put_medium_int(w, u, _shift_for<0xff, 0x80>(u), 0); + } + } else { + if (i <= 12) { + return w.put(uint8_t(int(BinaryTag::SmallInteger_lo) + i)); + } else { + uint64_t u; + memcpy(&u, &i, sizeof(i)); + return _put_medium_int(w, u, _shift_for<0x00, 0x00>(u), 0); + } + } + } + + inline BinaryWriter& operator<<(BinaryWriter& w, uint64_t u) { + if (u <= 12) { + return w.put(uint8_t(int(BinaryTag::SmallInteger_lo) + u)); + } else if ((u & 0x8000000000000000) != 0) { + return _put_medium_int(w, u, 56, 1); + } else { + return _put_medium_int(w, u, _shift_for<0x00, 0x00>(u), 0); + } + } +} diff --git a/implementations/cpp/preserves_impl.hpp b/implementations/cpp/preserves_impl.hpp new file mode 100644 index 0000000..75ea7b9 --- /dev/null +++ b/implementations/cpp/preserves_impl.hpp @@ -0,0 +1,315 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Preserves { + template + class Atomic: public ValueImpl { + protected: + Atom value; + public: + Atomic(Atom const& value) : value(value) {} + Atom& _value() { return value; } + Atom const& _value() const { return value; } + ValueKind value_kind() const { return kind; } + }; + +#define PRESERVES_ATOMIC_VALUE_CLASS(Name, a_t, r_t, VK, getter, extra) \ + template \ + class Name: public Atomic { \ + public: \ + Name(a_t const& value) : Atomic(value) {} \ + boost::optional getter() const override { return this->value; } \ + extra \ + } + + PRESERVES_ATOMIC_VALUE_CLASS(Boolean, bool, bool, ValueKind::Boolean, as_bool, + BinaryWriter& write(BinaryWriter& w) const override { + return w << this->_value(); + }); + PRESERVES_ATOMIC_VALUE_CLASS(Float, float, float, ValueKind::Float, as_float, + BinaryWriter& write(BinaryWriter& w) const override { + return w << this->_value(); + } + boost::optional as_double() const override { + return this->value; + }); + PRESERVES_ATOMIC_VALUE_CLASS(Double, double, double, ValueKind::Double, as_double, + BinaryWriter& write(BinaryWriter& w) const override { + return w << this->_value(); + }); + PRESERVES_ATOMIC_VALUE_CLASS(Uint64, uint64_t, uint64_t, ValueKind::SignedInteger, as_unsigned, + BinaryWriter& write(BinaryWriter& w) const override { + return w << this->_value(); + } + boost::optional as_signed() const override { + if (this->value <= uint64_t(std::numeric_limits::max())) { + return this->value; + } else { + return boost::none; + } + } + boost::optional as_float() const override { + if (uint64_t(float(this->value)) == this->value) { + return float(this->value); + } else { + return boost::none; + } + } + boost::optional as_double() const override { + if (uint64_t(double(this->value)) == this->value) { + return double(this->value); + } else { + return boost::none; + } + }); + PRESERVES_ATOMIC_VALUE_CLASS(Int64, int64_t, int64_t, ValueKind::SignedInteger, as_signed, + BinaryWriter& write(BinaryWriter& w) const override { + return w << this->_value(); + } + boost::optional as_unsigned() const override { + if (this->value >= 0) { + return this->value; + } else { + return boost::none; + } + } + boost::optional as_float() const override { + if (int64_t(float(this->value)) == this->value) { + return float(this->value); + } else { + return boost::none; + } + } + boost::optional as_double() const override { + if (int64_t(double(this->value)) == this->value) { + return double(this->value); + } else { + return boost::none; + } + }); + PRESERVES_ATOMIC_VALUE_CLASS(BigNum, std::vector, std::vector const&, ValueKind::SignedInteger, as_bignum, + BinaryWriter& write(BinaryWriter& w) const override { + return w.bignum(this->_value()); + }); + PRESERVES_ATOMIC_VALUE_CLASS(String, std::string, std::string const&, ValueKind::String, as_string, + BinaryWriter& write(BinaryWriter& w) const override { + return w.string(this->_value()); + }); + PRESERVES_ATOMIC_VALUE_CLASS(ByteString, std::vector, std::vector const&, ValueKind::ByteString, as_bytes, + BinaryWriter& write(BinaryWriter& w) const override { + return w.bytes(this->_value()); + }); + PRESERVES_ATOMIC_VALUE_CLASS(Symbol, std::string, std::string const&, ValueKind::Symbol, as_symbol, + BinaryWriter& write(BinaryWriter& w) const override { + return w.symbol(this->_value()); + }); + + template + class Record: public ValueImpl { + public: + Value labelValue; + std::vector> fields; + + Record(Value const& label) : labelValue(label), fields() {} + Record(Value const& label, std::vector> const& fields) : labelValue(label), fields(fields) {} + ValueKind value_kind() const { return ValueKind::Record; } + boost::optional> label() const override { return labelValue; } + size_t size() const { return fields.size(); } + boost::optional> get(size_t index) const { + if (index < size()) { + return fields[index]; + } else { + return boost::none; + } + } + bool add(Value const& value) override { + fields.push_back(value); + return true; + } + bool set(size_t index, Value const& value) override { + if (index < size()) { + fields[index] = value; + } + return false; + } + bool operator<(Record const& other) const { + if (labelValue < other.labelValue) return true; + if (other.labelValue < labelValue) return false; + return fields < other.fields; + } + BinaryWriter& write(BinaryWriter& w) const override { + return w.record(labelValue, fields); + } + }; + + template + class Sequence: public ValueImpl { + public: + std::vector> values; + + Sequence() : values() {} + Sequence(std::vector> const& values) : values(values) {} + ValueKind value_kind() const { return ValueKind::Sequence; } + boost::optional> const&> as_sequence() const override { + return values; + } + size_t size() const override { return values.size(); } + boost::optional> get(size_t index) const override { + if (index < size()) { + return values[index]; + } else { + return boost::none; + } + } + bool add(Value const& value) override { + values.push_back(value); + return true; + } + bool set(size_t index, Value const& value) override { + if (index < size()) { + values[index] = value; + } + return false; + } + BinaryWriter& write(BinaryWriter& w) const override { + return w.sequence(values); + } + }; + + template + class Set: public ValueImpl { + public: + std::set> values; + + Set() : values() {} + Set(std::set> const& values) : values(values) {} + ValueKind value_kind() const { return ValueKind::Set; } + boost::optional> const&> as_set() const override { + return values; + } + size_t size() const override { return values.size(); } + bool contains(Value const& key) const override { return values.count(key) > 0; } + bool add(Value const& value) override { + return values.insert(value).second; + } + bool erase(Value const& value) override { + return values.erase(value) > 0; + } + BinaryWriter& write(BinaryWriter& w) const override { + return w.set(values); + } + }; + + template + class Dictionary: public ValueImpl { + public: + std::map, Value> values; + + Dictionary() : values() {} + Dictionary(std::map, Value> const& values) : values(values) {} + ValueKind value_kind() const { return ValueKind::Dictionary; } + boost::optional, Value> const&> as_dictionary() const override { + return values; + } + size_t size() const override { return values.size(); } + bool contains(Value const& key) const override { return values.count(key) > 0; } + boost::optional> get(Value const& key) const override { + auto i = values.find(key); + if (i == values.end()) return boost::none; + return i->second; + } + bool set(Value const& key, Value const& value) override { + return values.emplace(key, value).second; + } + bool erase(Value const& key) override { + return values.erase(key) > 0; + } + BinaryWriter& write(BinaryWriter& w) const override { + return w.dictionary(values); + } + }; + + template + class Embedded: public ValueImpl { + public: + std::shared_ptr value; + + Embedded(std::shared_ptr const& value) : value(value) {} + ValueKind value_kind() const { return ValueKind::Embedded; } + boost::optional> as_embedded() const override { + return value; + } + BinaryWriter& write(BinaryWriter& w) const override { + w << BinaryTag::Embedded; + return value->write(w); + } + }; + + class GenericEmbedded: public Value { + public: + GenericEmbedded(std::shared_ptr> p) : + Value(p) + {} + + static std::shared_ptr wrap(Value<> v) { + return std::make_shared(v._impl()); + } + + BinaryWriter& write(BinaryWriter& w) const { + return (*this)->write(w); + } + }; + + template + Value Value::from_bool(bool b) + { + return Value(new Boolean(b)); + } + + template + Value Value::from_float(float f) + { + return Value(new Float(f)); + } + + template + Value Value::from_double(double d) + { + return Value(new Double(d)); + } + + template + Value Value::from_int(uint64_t i) + { + return Value(new Uint64(i)); + } + + template + Value Value::from_int(int64_t i) { + return Value(new Int64(i)); + } + + template + Value Value::from_string(std::string const& s) { + return Value(new String(s)); + } + + template + Value Value::sequence(std::vector> const& values) { + return Value(new Sequence(values)); + } + + template + Value Value::from_embedded(std::shared_ptr const& v) { + return Value(new Embedded(v)); + } +} \ No newline at end of file diff --git a/implementations/cpp/preserves_value.hpp b/implementations/cpp/preserves_value.hpp new file mode 100644 index 0000000..70a46e6 --- /dev/null +++ b/implementations/cpp/preserves_value.hpp @@ -0,0 +1,296 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Preserves { + + enum class ValueKind { + Boolean, + Float, + Double, + SignedInteger, + String, + ByteString, + Symbol, + + Record, + Sequence, + Set, + Dictionary, + + Embedded, + }; + + template class Record; + template class ValueImpl; + + template + class Value { + std::shared_ptr> p; + + public: + Value(std::shared_ptr> const& p) : p(p) {} + Value(ValueImpl *p) : p(p) {} + + std::shared_ptr> _impl() const { return p; } + + static Value from_bool(bool b); + static Value from_float(float f); + static Value from_double(double d); + static Value from_int(uint64_t i); + static Value from_int(int64_t i); + static Value from_string(std::string const& s); + static Value from_bytes(std::vector const& v); + static Value from_bytes(std::vector const& v); + static Value from_bytes(void *p, size_t len); + static Value from_symbol(std::string const& s); + + static Value record(Record const& r); + static Value record(Value const& label, std::vector const& fields); + static Value sequence(std::vector const& items); + static Value set(std::set const& items); + static Value dictionary(std::map const& entries); + + static Value from_embedded(std::shared_ptr const& p); + + 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); } + static Value from_number(float f) { return from_float(f); } + static Value from_number(double d) { return from_double(d); } + + static Value from(bool b) { return from_bool(b); } + static Value from(float f) { return from_float(f); } + static Value from(double d) { return from_double(d); } + static Value from(uint64_t i) { return from_int(i); } + static Value from(unsigned i) { return from_int(uint64_t(i)); } + static Value from(int64_t i) { return from_int(i); } + static Value from(signed i) { return from_int(int64_t(i)); } + static Value from(std::string const& s) { return from_string(s); } + static Value from(char const* s) { return from_string(s); } + static Value from(std::vector const& v) { return from_bytes(v); } + static Value from(std::vector const& v) { return from_bytes(v); } + static Value from(void *p, size_t len) { return from_bytes(p, len); } + static Value from(Record const& r) { return record(r); } + static Value from(Value const& label, std::vector const& fields) { return record(label, fields); } + static Value from(std::vector const& items) { return sequence(items); } + static Value from(std::set const& items) { return set(items); } + static Value from(std::map const& entries) { return dictionary(entries); } + + ValueImpl& operator*() const { return *p; } + ValueImpl* operator->() const { return p.get(); } + + ValueKind value_kind() const; + bool is_mutable() const; + + bool is_bool() const { return value_kind() == ValueKind::Boolean; } + bool is_float() const { return value_kind() == ValueKind::Float; } + bool is_double() const { return value_kind() == ValueKind::Double; } + bool is_int() const { return value_kind() == ValueKind::SignedInteger; } + bool is_string() const { return value_kind() == ValueKind::String; } + bool is_bytes() const { return value_kind() == ValueKind::ByteString; } + bool is_symbol() const { return value_kind() == ValueKind::Symbol; } + bool is_record() const { return value_kind() == ValueKind::Record; } + bool is_sequence() const { return value_kind() == ValueKind::Sequence; } + bool is_set() const { return value_kind() == ValueKind::Set; } + bool is_dictionary() const { return value_kind() == ValueKind::Dictionary; } + + boost::optional as_bool() const; + bool to_bool() const { return as_bool().value(); } + + boost::optional as_float() const; + float to_float() const { return as_float().value(); } + + boost::optional as_double() const; + double to_double() const { return as_double().value(); } + + boost::optional as_unsigned() const; + uint64_t to_unsigned() const { return as_unsigned().value(); } + + 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(); } + + boost::optional const&> as_bytes() const; + std::vector const& to_bytes() const { return as_bytes().value(); } + + boost::optional as_symbol() const; + std::string const& to_symbol() const { return as_symbol().value(); } + + boost::optional const&> as_record() const; + Record const& to_record() const { return as_record().value(); }; + + boost::optional const&> as_sequence() const; + std::vector const& to_sequence() const { return as_sequence().value(); } + + boost::optional const&> as_set() const; + std::set const& to_set() const { return as_set().value(); } + + boost::optional const&> as_dictionary() const; + std::map const& to_dictionary() const { return as_dictionary().value(); } + + boost::optional> as_embedded() const; + std::shared_ptr to_embedded() const { return as_embedded().value(); } + + boost::optional label() const; + size_t size() const; + bool contains(Value const& key) const; + boost::optional get(Value const& key) const; + Value operator[](Value const& key) const { return get(key).value(); } + boost::optional get(size_t index) const; + Value operator[](size_t index) const { return get(index).value(); } + }; + + class BinaryWriter; // forward declaration; see preserves_binary_writer.hpp + + template + class ValueImpl { + public: + virtual ~ValueImpl() {} + + virtual ValueKind value_kind() const = 0; + virtual bool is_mutable() const { return false; } + + virtual boost::optional as_bool() const { return boost::none; } + virtual boost::optional as_float() const { return boost::none; } + 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; } + virtual boost::optional const&> as_record() const { return boost::none; } + virtual boost::optional> const&> as_sequence() const { return boost::none; } + virtual boost::optional> const&> as_set() const { return boost::none; } + virtual boost::optional,Value> const&> as_dictionary() const { return boost::none; } + virtual boost::optional> as_embedded() const { return boost::none; } + + virtual boost::optional> label() const { return boost::none; } + virtual size_t size() const { return 0; } + virtual bool contains(Value const& /* key */) const { return false; } + virtual boost::optional> get(Value const& /* key */) const { return boost::none; } + virtual boost::optional> get(size_t /* index */) const { return boost::none; } + + virtual bool add(Value const& /* item */) { + throw std::runtime_error("Cannot add item to Preserves value"); + } + virtual bool set(Value const& /* key */, Value const& /* value */) { + throw std::runtime_error("Cannot set item by key in Preserves value"); + } + virtual bool set(size_t /* index */, Value const& /* value */) { + throw std::runtime_error("Cannot set item by index in Preserves value"); + } + virtual bool erase(Value const& /* key */) { + throw std::runtime_error("Cannot erase item in Preserves value"); + } + + virtual BinaryWriter& write(BinaryWriter& w) const = 0; + }; + + template ValueKind Value::value_kind() const { return p->value_kind(); } + template bool Value::is_mutable() const { return p->is_mutable(); } + +#define PRESERVES_DELEGATE_CAST(t, name) \ + template boost::optional Value::name() const { return p->name(); } + PRESERVES_DELEGATE_CAST(bool, as_bool); + PRESERVES_DELEGATE_CAST(float, as_float); + 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); + PRESERVES_DELEGATE_CAST(Record const&, as_record); + PRESERVES_DELEGATE_CAST(std::vector> const&, as_sequence); + PRESERVES_DELEGATE_CAST(std::set> const&, as_set); + #define COMMA , + PRESERVES_DELEGATE_CAST(std::map COMMA Value> const&, as_dictionary); + #undef COMMA + PRESERVES_DELEGATE_CAST(std::shared_ptr, as_embedded); +#undef PRESERVES_DELEGATE_CAST + + template boost::optional> Value::get(size_t index) const { return p->get(index); } + template size_t Value::size() const { return p->size(); } + + inline 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(); + auto bKind = b.value_kind(); + if (aKind < bKind) return true; + if (bKind < aKind) return false; + switch (aKind) { + case ValueKind::Boolean: return a.to_bool() < b.to_bool(); + case ValueKind::Float: return a.to_float() < b.to_float(); + case ValueKind::Double: return a.to_double() < b.to_double(); + case ValueKind::SignedInteger: { + if (auto av = a.as_signed()) { + if (auto bv = b.as_signed()) { + return *av < *bv; + } else { + return true; + } + } else { + if (auto bv = b.as_signed()) { + return false; + } else { + 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()); + } + } + } + } + } + case ValueKind::String: return a.to_string() < b.to_string(); + case ValueKind::ByteString: return a.to_bytes() < b.to_bytes(); + case ValueKind::Symbol: return a.to_symbol() < b.to_symbol(); + case ValueKind::Record: return a.to_record() < b.to_record(); + case ValueKind::Sequence: return a.to_sequence() < b.to_sequence(); + case ValueKind::Set: return a.to_set() < b.to_set(); + case ValueKind::Dictionary: return a.to_dictionary() < b.to_dictionary(); + case ValueKind::Embedded: return *a.to_embedded() < *b.to_embedded(); + default: throw std::runtime_error("Invalid ValueKind"); + } + } +} \ No newline at end of file