Binary writer draft
This commit is contained in:
parent
60f23286e2
commit
9c5368b69b
|
@ -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
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "preserves.hpp"
|
||||
#include "preserves_binary.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
|
|
@ -1,546 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace Preserves {
|
||||
|
||||
enum class ValueKind {
|
||||
Boolean,
|
||||
Float,
|
||||
Double,
|
||||
SignedInteger,
|
||||
String,
|
||||
ByteString,
|
||||
Symbol,
|
||||
|
||||
Record,
|
||||
Sequence,
|
||||
Set,
|
||||
Dictionary,
|
||||
|
||||
Embedded,
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded> class Record;
|
||||
template <typename T = class GenericEmbedded> class ValueImpl;
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Value {
|
||||
std::shared_ptr<ValueImpl<T>> p;
|
||||
|
||||
public:
|
||||
Value(std::shared_ptr<ValueImpl<T>> const& p) : p(p) {}
|
||||
Value(ValueImpl<T> *p) : p(p) {}
|
||||
|
||||
std::shared_ptr<ValueImpl<T>> _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<uint8_t> const& v);
|
||||
static Value from_bytes(std::vector<char> const& v);
|
||||
static Value from_bytes(void *p, size_t len);
|
||||
static Value from_symbol(std::string const& s);
|
||||
|
||||
static Value record(Record<T> const& r);
|
||||
static Value record(Value const& label, std::vector<Value> const& fields);
|
||||
static Value sequence(std::vector<Value> const& items);
|
||||
static Value set(std::set<Value> const& items);
|
||||
static Value dictionary(std::map<Value, Value> const& entries);
|
||||
|
||||
static Value from_embedded(std::shared_ptr<T> 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<uint8_t> 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<uint8_t> const& v) { return from_bytes(v); }
|
||||
static Value from(std::vector<char> const& v) { return from_bytes(v); }
|
||||
static Value from(void *p, size_t len) { return from_bytes(p, len); }
|
||||
static Value from(Record<T> const& r) { return record(r); }
|
||||
static Value from(Value const& label, std::vector<Value> const& fields) { return record(label, fields); }
|
||||
static Value from(std::vector<Value> const& items) { return sequence(items); }
|
||||
static Value from(std::set<Value> const& items) { return set(items); }
|
||||
static Value from(std::map<Value, Value> const& entries) { return dictionary(entries); }
|
||||
|
||||
ValueImpl<T>& operator*() const { return *p; }
|
||||
ValueImpl<T>* 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<bool> as_bool() const;
|
||||
bool to_bool() const { return as_bool().value(); }
|
||||
|
||||
boost::optional<float> as_float() const;
|
||||
float to_float() const { return as_float().value(); }
|
||||
|
||||
boost::optional<double> as_double() const;
|
||||
double to_double() const { return as_double().value(); }
|
||||
|
||||
boost::optional<uint64_t> as_unsigned() const;
|
||||
uint64_t to_unsigned() const { return as_unsigned().value(); }
|
||||
|
||||
boost::optional<int64_t> as_signed() const;
|
||||
int64_t to_signed() const { return as_signed().value(); }
|
||||
|
||||
boost::optional<std::vector<uint8_t> const&> as_bignum() const;
|
||||
std::vector<uint8_t> const& to_bignum() const { return as_bignum().value(); }
|
||||
|
||||
boost::optional<std::string const&> as_string() const;
|
||||
std::string const& to_string() const { return as_string().value(); }
|
||||
|
||||
boost::optional<std::vector<uint8_t> const&> as_bytes() const;
|
||||
std::vector<uint8_t> const& to_bytes() const { return as_bytes().value(); }
|
||||
|
||||
boost::optional<std::string const&> as_symbol() const;
|
||||
std::string const& to_symbol() const { return as_symbol().value(); }
|
||||
|
||||
boost::optional<Record<T> const&> as_record() const;
|
||||
Record<T> const& to_record() const { return as_record().value(); };
|
||||
|
||||
boost::optional<std::vector<Value> const&> as_sequence() const;
|
||||
std::vector<Value> const& to_sequence() const { return as_sequence().value(); }
|
||||
|
||||
boost::optional<std::set<Value> const&> as_set() const;
|
||||
std::set<Value> const& to_set() const { return as_set().value(); }
|
||||
|
||||
boost::optional<std::map<Value,Value> const&> as_dictionary() const;
|
||||
std::map<Value,Value> const& to_dictionary() const { return as_dictionary().value(); }
|
||||
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const;
|
||||
std::shared_ptr<T> to_embedded() const { return as_embedded().value(); }
|
||||
|
||||
boost::optional<Value> label() const;
|
||||
size_t size() const;
|
||||
bool contains(Value const& key) const;
|
||||
boost::optional<Value> get(Value const& key) const;
|
||||
Value operator[](Value const& key) const { return get(key).value(); }
|
||||
boost::optional<Value> get(size_t index) const;
|
||||
Value operator[](size_t index) const { return get(index).value(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ValueImpl {
|
||||
public:
|
||||
virtual ~ValueImpl() {}
|
||||
|
||||
virtual ValueKind value_kind() const = 0;
|
||||
virtual bool is_mutable() const { return false; }
|
||||
|
||||
virtual boost::optional<bool> as_bool() const { return boost::none; }
|
||||
virtual boost::optional<float> as_float() const { return boost::none; }
|
||||
virtual boost::optional<double> as_double() const { return boost::none; }
|
||||
virtual boost::optional<uint64_t> as_unsigned() const { return boost::none; }
|
||||
virtual boost::optional<int64_t> as_signed() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<uint8_t> const&> as_bignum() const { return boost::none; }
|
||||
virtual boost::optional<std::string const&> as_string() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<uint8_t> const&> as_bytes() const { return boost::none; }
|
||||
virtual boost::optional<std::string const&> as_symbol() const { return boost::none; }
|
||||
virtual boost::optional<Record<T> const&> as_record() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<Value<T>> const&> as_sequence() const { return boost::none; }
|
||||
virtual boost::optional<std::set<Value<T>> const&> as_set() const { return boost::none; }
|
||||
virtual boost::optional<std::map<Value<T>,Value<T>> const&> as_dictionary() const { return boost::none; }
|
||||
virtual boost::optional<std::shared_ptr<T>> as_embedded() const { return boost::none; }
|
||||
|
||||
virtual boost::optional<Value<T>> label() const { return boost::none; }
|
||||
virtual size_t size() const { return 0; }
|
||||
virtual bool contains(Value<T> const& /* key */) const { return false; }
|
||||
virtual boost::optional<Value<T>> get(Value<T> const& /* key */) const { return boost::none; }
|
||||
virtual boost::optional<Value<T>> get(size_t /* index */) const { return boost::none; }
|
||||
|
||||
virtual bool add(Value<T> const& /* item */) {
|
||||
throw std::runtime_error("Cannot add item to Preserves value");
|
||||
}
|
||||
virtual bool set(Value<T> const& /* key */, Value<T> const& /* value */) {
|
||||
throw std::runtime_error("Cannot set item by key in Preserves value");
|
||||
}
|
||||
virtual bool set(size_t /* index */, Value<T> const& /* value */) {
|
||||
throw std::runtime_error("Cannot set item by index in Preserves value");
|
||||
}
|
||||
virtual bool erase(Value<T> const& /* key */) {
|
||||
throw std::runtime_error("Cannot erase item in Preserves value");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> inline ValueKind Value<T>::value_kind() const { return p->value_kind(); }
|
||||
template <typename T> inline bool Value<T>::is_mutable() const { return p->is_mutable(); }
|
||||
|
||||
template <typename T, typename Atom, ValueKind kind>
|
||||
class Atomic: public ValueImpl<T> {
|
||||
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 <typename T = class GenericEmbedded> \
|
||||
class Name: public Atomic<T, a_t, VK> { \
|
||||
public: \
|
||||
Name(a_t const& value) : Atomic<T, a_t, VK>(value) {} \
|
||||
boost::optional<r_t> 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<double> 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<int64_t> as_signed() const override {
|
||||
if (this->value <= uint64_t(std::numeric_limits<int64_t>::max())) {
|
||||
return this->value;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<float> as_float() const override {
|
||||
if (uint64_t(float(this->value)) == this->value) {
|
||||
return float(this->value);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<double> 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<uint64_t> as_unsigned() const override {
|
||||
if (this->value >= 0) {
|
||||
return this->value;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<float> as_float() const override {
|
||||
if (int64_t(float(this->value)) == this->value) {
|
||||
return float(this->value);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<double> 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<uint8_t>, std::vector<uint8_t> 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<uint8_t>, std::vector<uint8_t> const&, ValueKind::ByteString, as_bytes,);
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(Symbol, std::string, std::string const&, ValueKind::Symbol, as_symbol,);
|
||||
|
||||
template <typename T>
|
||||
class Record: public ValueImpl<T> {
|
||||
public:
|
||||
Value<T> labelValue;
|
||||
std::vector<Value<T>> fields;
|
||||
|
||||
Record(Value<T> const& label) : labelValue(label), fields() {}
|
||||
Record(Value<T> const& label, std::vector<Value<T>> const& fields) : labelValue(label), fields(fields) {}
|
||||
ValueKind value_kind() const { return ValueKind::Record; }
|
||||
boost::optional<Value<T>> label() const override { return labelValue; }
|
||||
size_t size() const { return fields.size(); }
|
||||
boost::optional<Value<T>> get(size_t index) const {
|
||||
if (index < size()) {
|
||||
return fields[index];
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
bool add(Value<T> const& value) override {
|
||||
fields.push_back(value);
|
||||
return true;
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
if (index < size()) {
|
||||
fields[index] = value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator<(Record<T> const& other) const {
|
||||
if (labelValue < other.labelValue) return true;
|
||||
if (other.labelValue < labelValue) return false;
|
||||
return fields < other.fields;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Sequence: public ValueImpl<T> {
|
||||
public:
|
||||
std::vector<Value<T>> values;
|
||||
|
||||
Sequence() : values() {}
|
||||
Sequence(std::vector<Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Sequence; }
|
||||
boost::optional<std::vector<Value<T>> const&> as_sequence() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
boost::optional<Value<T>> get(size_t index) const override {
|
||||
if (index < size()) {
|
||||
return values[index];
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
bool add(Value<T> const& value) override {
|
||||
values.push_back(value);
|
||||
return true;
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
if (index < size()) {
|
||||
values[index] = value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Set: public ValueImpl<T> {
|
||||
public:
|
||||
std::set<Value<T>> values;
|
||||
|
||||
Set() : values() {}
|
||||
Set(std::set<Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Set; }
|
||||
boost::optional<std::set<Value<T>> const&> as_set() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
|
||||
bool add(Value<T> const& value) override {
|
||||
return values.insert(value).second;
|
||||
}
|
||||
bool erase(Value<T> const& value) override {
|
||||
return values.erase(value) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Dictionary: public ValueImpl<T> {
|
||||
public:
|
||||
std::map<Value<T>, Value<T>> values;
|
||||
|
||||
Dictionary() : values() {}
|
||||
Dictionary(std::map<Value<T>, Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Dictionary; }
|
||||
boost::optional<std::map<Value<T>, Value<T>> const&> as_dictionary() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
|
||||
boost::optional<Value<T>> get(Value<T> const& key) const override {
|
||||
auto i = values.find(key);
|
||||
if (i == values.end()) return boost::none;
|
||||
return i->second;
|
||||
}
|
||||
bool set(Value<T> const& key, Value<T> const& value) override {
|
||||
return values.emplace(key, value).second;
|
||||
}
|
||||
bool erase(Value<T> const& key) override {
|
||||
return values.erase(key) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Embedded: public ValueImpl<T> {
|
||||
public:
|
||||
std::shared_ptr<T> value;
|
||||
|
||||
Embedded(std::shared_ptr<T> const& value) : value(value) {}
|
||||
ValueKind value_kind() const { return ValueKind::Embedded; }
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const override {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
class GenericEmbedded: public Value<GenericEmbedded> {
|
||||
public:
|
||||
GenericEmbedded(std::shared_ptr<ValueImpl<GenericEmbedded>> p) :
|
||||
Value(p)
|
||||
{}
|
||||
|
||||
static std::shared_ptr<GenericEmbedded> wrap(Value<> v) {
|
||||
return std::make_shared<GenericEmbedded>(v._impl());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_bool(bool b)
|
||||
{
|
||||
return Value<T>(new Boolean<T>(b));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_float(float f)
|
||||
{
|
||||
return Value<T>(new Float<T>(f));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_double(double d)
|
||||
{
|
||||
return Value<T>(new Double<T>(d));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_int(uint64_t i)
|
||||
{
|
||||
return Value<T>(new Uint64<T>(i));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_int(int64_t i) {
|
||||
return Value<T>(new Int64<T>(i));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_string(std::string const& s) {
|
||||
return Value<T>(new String<T>(s));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::sequence(std::vector<Value<T>> const& values) {
|
||||
return Value<T>(new Sequence<T>(values));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_embedded(std::shared_ptr<T> const& v) {
|
||||
return Value<T>(new Embedded<T>(v));
|
||||
}
|
||||
|
||||
#define PRESERVES_DELEGATE_CAST(t, name) \
|
||||
template <typename T> boost::optional<t> Value<T>::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<uint8_t> const&, as_bignum);
|
||||
PRESERVES_DELEGATE_CAST(std::string const&, as_string);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<uint8_t> const&, as_bytes);
|
||||
PRESERVES_DELEGATE_CAST(std::string const&, as_symbol);
|
||||
PRESERVES_DELEGATE_CAST(Record<T> const&, as_record);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<Value<T>> const&, as_sequence);
|
||||
PRESERVES_DELEGATE_CAST(std::set<Value<T>> const&, as_set);
|
||||
#define COMMA ,
|
||||
PRESERVES_DELEGATE_CAST(std::map<Value<T> COMMA Value<T>> const&, as_dictionary);
|
||||
#undef COMMA
|
||||
PRESERVES_DELEGATE_CAST(std::shared_ptr<T>, as_embedded);
|
||||
#undef PRESERVES_DELEGATE_CAST
|
||||
|
||||
template <typename T> boost::optional<Value<T>> Value<T>::get(size_t index) const { return p->get(index); }
|
||||
template <typename T> size_t Value<T>::size() const { return p->size(); }
|
||||
|
||||
bool bignum_lt(std::vector<uint8_t> const& a, std::vector<uint8_t> 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 <typename T>
|
||||
bool operator<(Value<T> const& a, Value<T> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "preserves_value.hpp"
|
||||
#include "preserves_text.hpp"
|
||||
#include "preserves_binary_writer.hpp"
|
||||
#include "preserves_impl.hpp"
|
||||
#include "preserves_binary_reader.hpp"
|
||||
|
|
|
@ -1,35 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
#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 <typename T = GenericEmbedded>
|
||||
template <typename T = class GenericEmbedded>
|
||||
class BinaryReader {
|
||||
std::istream& i;
|
||||
std::function<std::shared_ptr<T>(Value<>)> decodeEmbedded;
|
|
@ -0,0 +1,250 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
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<char const*>(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<uint8_t> 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<uint8_t> const& v) {
|
||||
return bytes(v.data(), v.size());
|
||||
}
|
||||
|
||||
BinaryWriter& bytes(void const* buf, size_t size) {
|
||||
return write(BinaryTag::ByteString, buf, size);
|
||||
}
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
BinaryWriter& operator<<(Value<T> const& v) {
|
||||
return v->write(*this);
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
BinaryWriter& writeseq(Iter const& begin, Iter const& end) {
|
||||
for (Iter i = begin; i != end; ++i) (*this) << (*i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Collection>
|
||||
BinaryWriter& operator<<(Collection const& c) {
|
||||
return writeseq(c.begin(), c.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& record(Value<T> const& label, std::vector<Value<T>> const& fields) {
|
||||
return (*this) << BinaryTag::Record << label << fields << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& sequence(std::vector<Value<T>> const& vs) {
|
||||
return (*this) << BinaryTag::Sequence << vs << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& set(std::set<Value<T>> const& vs) {
|
||||
return (*this) << BinaryTag::Set << vs << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& dictionary(std::map<Value<T>, Value<T>> 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 <uint64_t wholeTest, uint64_t bitTest>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace Preserves {
|
||||
template <typename T, typename Atom, ValueKind kind>
|
||||
class Atomic: public ValueImpl<T> {
|
||||
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 <typename T = class GenericEmbedded> \
|
||||
class Name: public Atomic<T, a_t, VK> { \
|
||||
public: \
|
||||
Name(a_t const& value) : Atomic<T, a_t, VK>(value) {} \
|
||||
boost::optional<r_t> 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<double> 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<int64_t> as_signed() const override {
|
||||
if (this->value <= uint64_t(std::numeric_limits<int64_t>::max())) {
|
||||
return this->value;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<float> as_float() const override {
|
||||
if (uint64_t(float(this->value)) == this->value) {
|
||||
return float(this->value);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<double> 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<uint64_t> as_unsigned() const override {
|
||||
if (this->value >= 0) {
|
||||
return this->value;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<float> as_float() const override {
|
||||
if (int64_t(float(this->value)) == this->value) {
|
||||
return float(this->value);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<double> 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<uint8_t>, std::vector<uint8_t> 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<uint8_t>, std::vector<uint8_t> 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 <typename T>
|
||||
class Record: public ValueImpl<T> {
|
||||
public:
|
||||
Value<T> labelValue;
|
||||
std::vector<Value<T>> fields;
|
||||
|
||||
Record(Value<T> const& label) : labelValue(label), fields() {}
|
||||
Record(Value<T> const& label, std::vector<Value<T>> const& fields) : labelValue(label), fields(fields) {}
|
||||
ValueKind value_kind() const { return ValueKind::Record; }
|
||||
boost::optional<Value<T>> label() const override { return labelValue; }
|
||||
size_t size() const { return fields.size(); }
|
||||
boost::optional<Value<T>> get(size_t index) const {
|
||||
if (index < size()) {
|
||||
return fields[index];
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
bool add(Value<T> const& value) override {
|
||||
fields.push_back(value);
|
||||
return true;
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
if (index < size()) {
|
||||
fields[index] = value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator<(Record<T> 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 <typename T = class GenericEmbedded>
|
||||
class Sequence: public ValueImpl<T> {
|
||||
public:
|
||||
std::vector<Value<T>> values;
|
||||
|
||||
Sequence() : values() {}
|
||||
Sequence(std::vector<Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Sequence; }
|
||||
boost::optional<std::vector<Value<T>> const&> as_sequence() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
boost::optional<Value<T>> get(size_t index) const override {
|
||||
if (index < size()) {
|
||||
return values[index];
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
bool add(Value<T> const& value) override {
|
||||
values.push_back(value);
|
||||
return true;
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
if (index < size()) {
|
||||
values[index] = value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.sequence(values);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Set: public ValueImpl<T> {
|
||||
public:
|
||||
std::set<Value<T>> values;
|
||||
|
||||
Set() : values() {}
|
||||
Set(std::set<Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Set; }
|
||||
boost::optional<std::set<Value<T>> const&> as_set() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
|
||||
bool add(Value<T> const& value) override {
|
||||
return values.insert(value).second;
|
||||
}
|
||||
bool erase(Value<T> const& value) override {
|
||||
return values.erase(value) > 0;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.set(values);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Dictionary: public ValueImpl<T> {
|
||||
public:
|
||||
std::map<Value<T>, Value<T>> values;
|
||||
|
||||
Dictionary() : values() {}
|
||||
Dictionary(std::map<Value<T>, Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Dictionary; }
|
||||
boost::optional<std::map<Value<T>, Value<T>> const&> as_dictionary() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
|
||||
boost::optional<Value<T>> get(Value<T> const& key) const override {
|
||||
auto i = values.find(key);
|
||||
if (i == values.end()) return boost::none;
|
||||
return i->second;
|
||||
}
|
||||
bool set(Value<T> const& key, Value<T> const& value) override {
|
||||
return values.emplace(key, value).second;
|
||||
}
|
||||
bool erase(Value<T> const& key) override {
|
||||
return values.erase(key) > 0;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.dictionary(values);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Embedded: public ValueImpl<T> {
|
||||
public:
|
||||
std::shared_ptr<T> value;
|
||||
|
||||
Embedded(std::shared_ptr<T> const& value) : value(value) {}
|
||||
ValueKind value_kind() const { return ValueKind::Embedded; }
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const override {
|
||||
return value;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
w << BinaryTag::Embedded;
|
||||
return value->write(w);
|
||||
}
|
||||
};
|
||||
|
||||
class GenericEmbedded: public Value<GenericEmbedded> {
|
||||
public:
|
||||
GenericEmbedded(std::shared_ptr<ValueImpl<GenericEmbedded>> p) :
|
||||
Value(p)
|
||||
{}
|
||||
|
||||
static std::shared_ptr<GenericEmbedded> wrap(Value<> v) {
|
||||
return std::make_shared<GenericEmbedded>(v._impl());
|
||||
}
|
||||
|
||||
BinaryWriter& write(BinaryWriter& w) const {
|
||||
return (*this)->write(w);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_bool(bool b)
|
||||
{
|
||||
return Value<T>(new Boolean<T>(b));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_float(float f)
|
||||
{
|
||||
return Value<T>(new Float<T>(f));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_double(double d)
|
||||
{
|
||||
return Value<T>(new Double<T>(d));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_int(uint64_t i)
|
||||
{
|
||||
return Value<T>(new Uint64<T>(i));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_int(int64_t i) {
|
||||
return Value<T>(new Int64<T>(i));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_string(std::string const& s) {
|
||||
return Value<T>(new String<T>(s));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::sequence(std::vector<Value<T>> const& values) {
|
||||
return Value<T>(new Sequence<T>(values));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_embedded(std::shared_ptr<T> const& v) {
|
||||
return Value<T>(new Embedded<T>(v));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace Preserves {
|
||||
|
||||
enum class ValueKind {
|
||||
Boolean,
|
||||
Float,
|
||||
Double,
|
||||
SignedInteger,
|
||||
String,
|
||||
ByteString,
|
||||
Symbol,
|
||||
|
||||
Record,
|
||||
Sequence,
|
||||
Set,
|
||||
Dictionary,
|
||||
|
||||
Embedded,
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded> class Record;
|
||||
template <typename T = class GenericEmbedded> class ValueImpl;
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Value {
|
||||
std::shared_ptr<ValueImpl<T>> p;
|
||||
|
||||
public:
|
||||
Value(std::shared_ptr<ValueImpl<T>> const& p) : p(p) {}
|
||||
Value(ValueImpl<T> *p) : p(p) {}
|
||||
|
||||
std::shared_ptr<ValueImpl<T>> _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<uint8_t> const& v);
|
||||
static Value from_bytes(std::vector<char> const& v);
|
||||
static Value from_bytes(void *p, size_t len);
|
||||
static Value from_symbol(std::string const& s);
|
||||
|
||||
static Value record(Record<T> const& r);
|
||||
static Value record(Value const& label, std::vector<Value> const& fields);
|
||||
static Value sequence(std::vector<Value> const& items);
|
||||
static Value set(std::set<Value> const& items);
|
||||
static Value dictionary(std::map<Value, Value> const& entries);
|
||||
|
||||
static Value from_embedded(std::shared_ptr<T> 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<uint8_t> 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<uint8_t> const& v) { return from_bytes(v); }
|
||||
static Value from(std::vector<char> const& v) { return from_bytes(v); }
|
||||
static Value from(void *p, size_t len) { return from_bytes(p, len); }
|
||||
static Value from(Record<T> const& r) { return record(r); }
|
||||
static Value from(Value const& label, std::vector<Value> const& fields) { return record(label, fields); }
|
||||
static Value from(std::vector<Value> const& items) { return sequence(items); }
|
||||
static Value from(std::set<Value> const& items) { return set(items); }
|
||||
static Value from(std::map<Value, Value> const& entries) { return dictionary(entries); }
|
||||
|
||||
ValueImpl<T>& operator*() const { return *p; }
|
||||
ValueImpl<T>* 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<bool> as_bool() const;
|
||||
bool to_bool() const { return as_bool().value(); }
|
||||
|
||||
boost::optional<float> as_float() const;
|
||||
float to_float() const { return as_float().value(); }
|
||||
|
||||
boost::optional<double> as_double() const;
|
||||
double to_double() const { return as_double().value(); }
|
||||
|
||||
boost::optional<uint64_t> as_unsigned() const;
|
||||
uint64_t to_unsigned() const { return as_unsigned().value(); }
|
||||
|
||||
boost::optional<int64_t> as_signed() const;
|
||||
int64_t to_signed() const { return as_signed().value(); }
|
||||
|
||||
boost::optional<std::vector<uint8_t> const&> as_bignum() const;
|
||||
std::vector<uint8_t> const& to_bignum() const { return as_bignum().value(); }
|
||||
|
||||
boost::optional<std::string const&> as_string() const;
|
||||
std::string const& to_string() const { return as_string().value(); }
|
||||
|
||||
boost::optional<std::vector<uint8_t> const&> as_bytes() const;
|
||||
std::vector<uint8_t> const& to_bytes() const { return as_bytes().value(); }
|
||||
|
||||
boost::optional<std::string const&> as_symbol() const;
|
||||
std::string const& to_symbol() const { return as_symbol().value(); }
|
||||
|
||||
boost::optional<Record<T> const&> as_record() const;
|
||||
Record<T> const& to_record() const { return as_record().value(); };
|
||||
|
||||
boost::optional<std::vector<Value> const&> as_sequence() const;
|
||||
std::vector<Value> const& to_sequence() const { return as_sequence().value(); }
|
||||
|
||||
boost::optional<std::set<Value> const&> as_set() const;
|
||||
std::set<Value> const& to_set() const { return as_set().value(); }
|
||||
|
||||
boost::optional<std::map<Value,Value> const&> as_dictionary() const;
|
||||
std::map<Value,Value> const& to_dictionary() const { return as_dictionary().value(); }
|
||||
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const;
|
||||
std::shared_ptr<T> to_embedded() const { return as_embedded().value(); }
|
||||
|
||||
boost::optional<Value> label() const;
|
||||
size_t size() const;
|
||||
bool contains(Value const& key) const;
|
||||
boost::optional<Value> get(Value const& key) const;
|
||||
Value operator[](Value const& key) const { return get(key).value(); }
|
||||
boost::optional<Value> 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 <typename T>
|
||||
class ValueImpl {
|
||||
public:
|
||||
virtual ~ValueImpl() {}
|
||||
|
||||
virtual ValueKind value_kind() const = 0;
|
||||
virtual bool is_mutable() const { return false; }
|
||||
|
||||
virtual boost::optional<bool> as_bool() const { return boost::none; }
|
||||
virtual boost::optional<float> as_float() const { return boost::none; }
|
||||
virtual boost::optional<double> as_double() const { return boost::none; }
|
||||
virtual boost::optional<uint64_t> as_unsigned() const { return boost::none; }
|
||||
virtual boost::optional<int64_t> as_signed() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<uint8_t> const&> as_bignum() const { return boost::none; }
|
||||
virtual boost::optional<std::string const&> as_string() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<uint8_t> const&> as_bytes() const { return boost::none; }
|
||||
virtual boost::optional<std::string const&> as_symbol() const { return boost::none; }
|
||||
virtual boost::optional<Record<T> const&> as_record() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<Value<T>> const&> as_sequence() const { return boost::none; }
|
||||
virtual boost::optional<std::set<Value<T>> const&> as_set() const { return boost::none; }
|
||||
virtual boost::optional<std::map<Value<T>,Value<T>> const&> as_dictionary() const { return boost::none; }
|
||||
virtual boost::optional<std::shared_ptr<T>> as_embedded() const { return boost::none; }
|
||||
|
||||
virtual boost::optional<Value<T>> label() const { return boost::none; }
|
||||
virtual size_t size() const { return 0; }
|
||||
virtual bool contains(Value<T> const& /* key */) const { return false; }
|
||||
virtual boost::optional<Value<T>> get(Value<T> const& /* key */) const { return boost::none; }
|
||||
virtual boost::optional<Value<T>> get(size_t /* index */) const { return boost::none; }
|
||||
|
||||
virtual bool add(Value<T> const& /* item */) {
|
||||
throw std::runtime_error("Cannot add item to Preserves value");
|
||||
}
|
||||
virtual bool set(Value<T> const& /* key */, Value<T> const& /* value */) {
|
||||
throw std::runtime_error("Cannot set item by key in Preserves value");
|
||||
}
|
||||
virtual bool set(size_t /* index */, Value<T> const& /* value */) {
|
||||
throw std::runtime_error("Cannot set item by index in Preserves value");
|
||||
}
|
||||
virtual bool erase(Value<T> const& /* key */) {
|
||||
throw std::runtime_error("Cannot erase item in Preserves value");
|
||||
}
|
||||
|
||||
virtual BinaryWriter& write(BinaryWriter& w) const = 0;
|
||||
};
|
||||
|
||||
template <typename T> ValueKind Value<T>::value_kind() const { return p->value_kind(); }
|
||||
template <typename T> bool Value<T>::is_mutable() const { return p->is_mutable(); }
|
||||
|
||||
#define PRESERVES_DELEGATE_CAST(t, name) \
|
||||
template <typename T> boost::optional<t> Value<T>::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<uint8_t> const&, as_bignum);
|
||||
PRESERVES_DELEGATE_CAST(std::string const&, as_string);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<uint8_t> const&, as_bytes);
|
||||
PRESERVES_DELEGATE_CAST(std::string const&, as_symbol);
|
||||
PRESERVES_DELEGATE_CAST(Record<T> const&, as_record);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<Value<T>> const&, as_sequence);
|
||||
PRESERVES_DELEGATE_CAST(std::set<Value<T>> const&, as_set);
|
||||
#define COMMA ,
|
||||
PRESERVES_DELEGATE_CAST(std::map<Value<T> COMMA Value<T>> const&, as_dictionary);
|
||||
#undef COMMA
|
||||
PRESERVES_DELEGATE_CAST(std::shared_ptr<T>, as_embedded);
|
||||
#undef PRESERVES_DELEGATE_CAST
|
||||
|
||||
template <typename T> boost::optional<Value<T>> Value<T>::get(size_t index) const { return p->get(index); }
|
||||
template <typename T> size_t Value<T>::size() const { return p->size(); }
|
||||
|
||||
inline bool bignum_lt(std::vector<uint8_t> const& a, std::vector<uint8_t> 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 <typename T>
|
||||
bool operator<(Value<T> const& a, Value<T> 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");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue