264 lines
8.5 KiB
C++
264 lines
8.5 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <functional>
|
|
#include <type_traits>
|
|
#include <limits>
|
|
|
|
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 <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;
|
|
}
|
|
|
|
class BinaryWriter {
|
|
std::ostream& o;
|
|
|
|
template <typename Collection>
|
|
struct _iterable {
|
|
Collection const& c;
|
|
};
|
|
|
|
template <typename Collection>
|
|
_iterable<Collection> iterable(Collection const& c) { return _iterable<Collection>{c}; }
|
|
|
|
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<<(_iterable<Collection> const& c) {
|
|
return writeseq(c.c.begin(), c.c.end());
|
|
}
|
|
|
|
BinaryWriter& operator<<(bool b) {
|
|
return (*this) << (b ? BinaryTag::True : BinaryTag::False);
|
|
}
|
|
|
|
BinaryWriter& operator<<(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;
|
|
(*this) << BinaryTag::Float;
|
|
return write(buf, sizeof(buf));
|
|
}
|
|
|
|
BinaryWriter& operator<<(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;
|
|
(*this) << BinaryTag::Double;
|
|
return write(buf, sizeof(buf));
|
|
}
|
|
|
|
BinaryWriter& _put_medium_int(uint64_t u, int shift, int extra) {
|
|
put(uint8_t(int(BinaryTag::MediumInteger_lo) + (shift >> 3) + extra));
|
|
if (extra) put(0);
|
|
while (shift >= 0) {
|
|
put(uint8_t((u >> shift) & 0xff));
|
|
shift -= 8;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<std::is_integral<T>::value, BinaryWriter&>::type operator<<(T t) {
|
|
if (std::numeric_limits<T>::is_signed) {
|
|
auto i = static_cast<int64_t>(t);
|
|
if (i < 0) {
|
|
if (i >= -3) {
|
|
return put(uint8_t(int(BinaryTag::SmallInteger_lo) + 16 + i));
|
|
} else {
|
|
uint64_t u;
|
|
memcpy(&u, &i, sizeof(i));
|
|
return _put_medium_int(u, _shift_for<0xff, 0x80>(u), 0);
|
|
}
|
|
} else {
|
|
if (i <= 12) {
|
|
return put(uint8_t(int(BinaryTag::SmallInteger_lo) + i));
|
|
} else {
|
|
uint64_t u;
|
|
memcpy(&u, &i, sizeof(i));
|
|
return _put_medium_int(u, _shift_for<0x00, 0x00>(u), 0);
|
|
}
|
|
}
|
|
} else {
|
|
auto u = static_cast<uint64_t>(t);
|
|
if (u <= 12) {
|
|
return put(uint8_t(int(BinaryTag::SmallInteger_lo) + u));
|
|
} else if ((u & 0x8000000000000000) != 0) {
|
|
return _put_medium_int(u, 56, 1);
|
|
} else {
|
|
return _put_medium_int(u, _shift_for<0x00, 0x00>(u), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
BinaryWriter& record(Value<T> const& label, std::vector<Value<T>> const& fields) {
|
|
return (*this) << BinaryTag::Record << label << iterable(fields) << BinaryTag::End;
|
|
}
|
|
|
|
template <typename T>
|
|
BinaryWriter& sequence(std::vector<Value<T>> const& vs) {
|
|
return (*this) << BinaryTag::Sequence << iterable(vs) << BinaryTag::End;
|
|
}
|
|
|
|
template <typename T>
|
|
BinaryWriter& set(std::set<Value<T>> const& vs) {
|
|
return (*this) << BinaryTag::Set << iterable(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;
|
|
}
|
|
};
|
|
}
|