Fix annotations; progress on integers

This commit is contained in:
Tony Garnock-Jones 2023-06-23 22:28:31 +02:00
parent 3c76819687
commit ae02cff81d
4 changed files with 163 additions and 105 deletions

View File

@ -1,6 +1,7 @@
#include "preserves.hpp"
#include <fstream>
#include <sstream>
#include "googletest/gtest/gtest.h"
@ -22,10 +23,43 @@ TEST(Value, Basics) {
ASSERT_EQ(ValueKind::String, vs[2].value_kind());
}
TEST(BinaryReader, Negative257) {
istringstream input("\xA1\xFE\xFF");
auto v = BinaryReader<>(input).next();
ASSERT_TRUE(v);
ASSERT_EQ(v->to_signed(), -257);
}
TEST(BinaryReader, Negative127) {
istringstream input("\xA0\x81");
auto v = BinaryReader<>(input).next();
ASSERT_TRUE(v);
ASSERT_EQ(v->to_signed(), -127);
}
TEST(BinaryWriter, Negative257) {
ostringstream s;
BinaryWriter w(s);
w << -257;
std::string output(s.str());
ASSERT_EQ(output[0], char(BinaryTag::MediumInteger_lo) + 1);
ASSERT_EQ(output[1], char(0xFE));
ASSERT_EQ(output[2], char(0xFF));
}
TEST(BinaryWriter, Negative127) {
ostringstream s;
BinaryWriter w(s);
w << -127;
std::string output(s.str());
ASSERT_EQ(output[0], char(BinaryTag::MediumInteger_lo));
ASSERT_EQ(output[1], char(0x81));
}
TEST(BinaryReader, ReadSamples) {
ifstream f("../../tests/samples.bin", ios::binary);
BinaryReader<> r(f, &GenericEmbedded::wrap);
auto v = r.next();
ASSERT_TRUE(v);
BinaryWriter(cerr) << *v;
// BinaryWriter(cerr) << *v;
}

View File

@ -4,6 +4,7 @@
#include <cstring>
#include <iostream>
#include <functional>
#include <type_traits>
namespace Preserves {
template <typename T = class GenericEmbedded>
@ -31,6 +32,10 @@ namespace Preserves {
return boost::none;
}
BinaryReader(typename std::enable_if<std::is_same<T, GenericEmbedded>::value, std::istream&>::type i) :
BinaryReader(i, &GenericEmbedded::wrap)
{}
BinaryReader(std::istream& i, std::function<std::shared_ptr<T>(Value<>)> decodeEmbedded) :
i(i),
decodeEmbedded(decodeEmbedded)
@ -89,14 +94,21 @@ namespace Preserves {
case BinaryTag::Float: return next_float().map(Value<T>::from_float);
case BinaryTag::Double: return next_double().map(Value<T>::from_double);
case BinaryTag::End: end_sentinel = true; return boost::none;
case BinaryTag::Annotation: return next().flat_map([&](Value<T> const& ann) -> boost::optional<Value<T>> {
return next().flat_map([&](Value<T> const& v) -> boost::optional<Value<T>> {
return v.annotate(ann);
});
});
case BinaryTag::Annotation: {
std::vector<Value<T>> annotations;
while (true) {
auto ann = next();
if (!ann) return boost::none;
annotations.push_back(*ann);
if (BinaryTag(i.peek()) != BinaryTag::Annotation) break;
i.get();
}
auto underlying = next();
if (!underlying) return boost::none;
return Value<T>(new AnnotatedValue<T>(std::move(annotations), *underlying));
}
case BinaryTag::Embedded:
return BinaryReader<GenericEmbedded>(i, &GenericEmbedded::wrap)
.next().map(decodeEmbedded).map(Value<T>::from_embedded);
return BinaryReader<>(i).next().map(decodeEmbedded).map(Value<T>::from_embedded);
case BinaryTag::SmallInteger_lo ... BinaryTag::SmallInteger_hi: {
int64_t n = int64_t(tag) - int64_t(BinaryTag::SmallInteger_lo);
return Value<T>::from_int(n <= 12 ? n : n - 16);

View File

@ -4,6 +4,8 @@
#include <cstring>
#include <iostream>
#include <functional>
#include <type_traits>
#include <limits>
namespace Preserves {
enum class BinaryTag {
@ -28,9 +30,32 @@ namespace Preserves {
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) {}
@ -134,23 +159,98 @@ namespace Preserves {
}
template <typename Collection>
BinaryWriter& operator<<(Collection const& c) {
return writeseq(c.begin(), c.end());
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 << fields << BinaryTag::End;
return (*this) << BinaryTag::Record << label << iterable(fields) << BinaryTag::End;
}
template <typename T>
BinaryWriter& sequence(std::vector<Value<T>> const& vs) {
return (*this) << BinaryTag::Sequence << vs << BinaryTag::End;
return (*this) << BinaryTag::Sequence << iterable(vs) << BinaryTag::End;
}
template <typename T>
BinaryWriter& set(std::set<Value<T>> const& vs) {
return (*this) << BinaryTag::Set << vs << BinaryTag::End;
return (*this) << BinaryTag::Set << iterable(vs) << BinaryTag::End;
}
template <typename T>
@ -160,91 +260,4 @@ namespace Preserves {
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);
}
}
}

View File

@ -271,8 +271,8 @@ namespace Preserves {
template <typename T>
class AnnotatedValue: public ValueImpl<T> {
Value<T> underlying;
std::vector<Value<T>> anns;
Value<T> underlying;
friend class ValueImpl<T>;
@ -282,15 +282,14 @@ namespace Preserves {
anns.push_back(ann);
return self;
} else {
auto a = std::make_shared<AnnotatedValue<T>>(underlying);
a->anns.insert(a->anns.end(), anns.begin(), anns.end());
auto a = std::make_shared<AnnotatedValue<T>>(std::vector<Value<T>>(anns), underlying);
a->anns.push_back(ann);
return Value<T>(a);
}
}
public:
AnnotatedValue(Value<T> const& v) : underlying(v) {}
AnnotatedValue(std::vector<Value<T>> &&anns, Value<T> const& v) : anns(anns), underlying(v) {}
ValueKind value_kind() const override { return underlying.value_kind(); }
bool is_mutable() const override { return underlying.is_mutable(); }
@ -346,7 +345,7 @@ namespace Preserves {
template <typename T>
Value<T> ValueImpl<T>::internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const &ann) {
auto a = std::make_shared<AnnotatedValue<T>>(Value<T>(self));
auto a = std::make_shared<AnnotatedValue<T>>(std::vector<Value<T>>(), Value<T>(self));
a->anns.push_back(ann);
return Value<T>(a);
}