diff --git a/.bazelrc b/.bazelrc index dc6e513..7b4736c 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,6 +1,7 @@ common \ --color=yes \ --experimental_enable_bzlmod \ + --registry=http://localhost:8000 \ --registry=https://github.com/raw/asoffer/bazel-registry/main \ --registry=https://bcr.bazel.build build \ diff --git a/MODULE.bazel b/MODULE.bazel index ea067e1..1353d75 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,3 +1,3 @@ bazel_dep(name = "abseil-cpp", repo_name = "com_google_absl", version = "20230125.1") bazel_dep(name = "bazel_skylib", repo_name = "bazel_skylib", version = "1.4.2") -bazel_dep(name = "nth-cc", repo_name = "nth_cc", version = "20240130.00") +bazel_dep(name = "nth-cc", repo_name = "nth_cc", version = "20240205.00") diff --git a/examples/BUILD b/examples/BUILD index 9e8b6b5..4547a3c 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -52,15 +52,18 @@ cc_binary( name = "serialization", srcs = ["serialization.cc"], deps = [ + "//jasmin/core:instruction", + "//jasmin/core:program", + "//jasmin/core:serialization", "//jasmin/instructions:arithmetic", "//jasmin/instructions:compare", "//jasmin/instructions:common", - "//jasmin/serialize:deserialize", - "//jasmin/serialize:serialize", - "//jasmin/serialize:string_reader", - "//jasmin/serialize:string_writer", "@nth_cc//nth/debug", "@nth_cc//nth/debug/log:stderr_log_sink", + "@nth_cc//nth/io/serialize", + "@nth_cc//nth/io/serialize:deserialize", + "@nth_cc//nth/io/serialize:string_reader", + "@nth_cc//nth/io/serialize:string_writer", "@com_google_absl//absl/container:flat_hash_set", ], ) diff --git a/examples/serialization.cc b/examples/serialization.cc index c7d61ea..4a61b71 100644 --- a/examples/serialization.cc +++ b/examples/serialization.cc @@ -2,16 +2,18 @@ #include #include "jasmin/core/instruction.h" +#include "jasmin/core/program.h" +#include "jasmin/core/serialization.h" #include "jasmin/instructions/arithmetic.h" #include "jasmin/instructions/common.h" #include "jasmin/instructions/compare.h" -#include "jasmin/serialize/deserialize.h" -#include "jasmin/serialize/serialize.h" -#include "jasmin/serialize/string_reader.h" -#include "jasmin/serialize/string_writer.h" #include "nth/container/interval.h" #include "nth/debug/debug.h" #include "nth/debug/log/stderr_log_sink.h" +#include "nth/io/serialize/deserialize.h" +#include "nth/io/serialize/serialize.h" +#include "nth/io/serialize/string_reader.h" +#include "nth/io/serialize/string_writer.h" using Instructions = jasmin::MakeInstructionSet< jasmin::Duplicate, jasmin::Swap, jasmin::Push, @@ -46,29 +48,30 @@ jasmin::Program MakeProgram() { int main() { // The string which is going to hold the serialized program. - std::string s; + std::string content; { - // Construct a program and serialize it into `s`. + // Construct a program and serialize it into `content`. jasmin::Program p = MakeProgram(); - jasmin::StringWriter w(p.functions(), s); - jasmin::Serialize(p, w); + jasmin::Serializer serializer(content); + if (not nth::io::serialize(serializer, p)) { return 1; } } // At this point the constructed program has been destroyed. The only remnants // of it are this serialization. std::puts("Serialized program:"); - for (size_t i = 0; i < s.size(); ++i) { - std::printf(" %0.2x", s[i]); + for (size_t i = 0; i < content.size(); ++i) { + std::printf(" %0.2x", content[i]); if (i % 8 == 7) { std::putchar('\n'); } } std::putchar('\n'); { - // Reconstitute the program previously serialized into `s` back into `p`. + // Reconstitute the program previously serialized into `content` back into + // the program `p`. jasmin::Program p; - jasmin::StringReader r(s); - if (not jasmin::Deserialize(r, p)) { return 1; } + jasmin::Deserializer deserializer(content); + if (not nth::io::deserialize(deserializer, p)) { return 1; } uint64_t n = 15; nth::stack stack = {n}; diff --git a/jasmin/core/BUILD b/jasmin/core/BUILD index b55edb7..c0630af 100644 --- a/jasmin/core/BUILD +++ b/jasmin/core/BUILD @@ -22,10 +22,13 @@ cc_library( deps = [ ":instruction", ":instruction_index", + ":metadata", ":value", "//jasmin/core/internal:function_base", "//jasmin/core/internal:instruction_traits", "@nth_cc//nth/container:interval", + "@nth_cc//nth/io/serialize", + "@nth_cc//nth/io/serialize:deserialize", ], ) @@ -148,10 +151,39 @@ cc_library( ":instruction", ":value", "@nth_cc//nth/debug", + "@nth_cc//nth/io/serialize", + "@nth_cc//nth/io/serialize:deserialize", "@nth_cc//nth/utility:iterator_range", ], ) +cc_library( + name = "serialization", + hdrs = ["serialization.h"], + visibility = ["//visibility:public"], + deps = [ + ":function", + ":instruction", + ":metadata", + ":program", + ":value", + "@nth_cc//nth/io/serialize", + "@nth_cc//nth/io/serialize:deserialize", + "@nth_cc//nth/io/serialize:string_reader", + "@nth_cc//nth/io/serialize:string_writer", + ], +) + +cc_test( + name = "serialization_test", + srcs = ["serialization_test.cc"], + deps = [ + ":serialization", + "//jasmin/instructions:common", + "@nth_cc//nth/test:main", + ], +) + cc_library( name = "value", hdrs = ["value.h"], diff --git a/jasmin/core/function.h b/jasmin/core/function.h index 9f70b9e..b1d741d 100644 --- a/jasmin/core/function.h +++ b/jasmin/core/function.h @@ -6,6 +6,7 @@ #include "jasmin/core/internal/frame.h" #include "jasmin/core/internal/function_base.h" #include "jasmin/core/internal/instruction_traits.h" +#include "jasmin/core/metadata.h" #include "nth/container/interval.h" #include "nth/meta/type.h" @@ -33,6 +34,9 @@ struct Function : Function<> { static_assert(InstructionSetType); + // Constructs an empty function to be used only for overwriting. + explicit Function(); + // Constructs an empty `Function` given a `parameter_count` representing // the number of parameters to the function, and a `return_count` // representing the number of return values for the function. @@ -93,6 +97,10 @@ Function::Function(uint32_t parameter_count, uint32_t return_count) : Function<>(parameter_count, return_count, internal::Invoke) {} +template +Function::Function() + : Function<>(0, 0, internal::Invoke) {} + template template requires(Set::instructions.template contains>()) // @@ -105,7 +113,8 @@ requires(Set::instructions.template contains>()) // return internal::FunctionBase::append({&I::template ExecuteImpl}); } else if constexpr (nth::type == nth::type) { static_assert(sizeof...(vs) == 1); - return internal::FunctionBase::append({&I::template ExecuteImpl, static_cast(vs)...}); + return internal::FunctionBase::append( + {&I::template ExecuteImpl, static_cast(vs)...}); } else { constexpr size_t DropCount = internal::HasFunctionState ? 3 : 2; return internal::InstructionFunctionType() diff --git a/jasmin/core/internal/BUILD b/jasmin/core/internal/BUILD index 0600054..8cd7983 100644 --- a/jasmin/core/internal/BUILD +++ b/jasmin/core/internal/BUILD @@ -47,6 +47,8 @@ cc_library( "//jasmin/core:value", "@nth_cc//nth/meta:concepts", "@nth_cc//nth/meta:type", + "@nth_cc//nth/io/serialize", + "@nth_cc//nth/io/serialize:deserialize", ], ) diff --git a/jasmin/core/internal/instruction_traits.h b/jasmin/core/internal/instruction_traits.h index bdbfd8a..e841057 100644 --- a/jasmin/core/internal/instruction_traits.h +++ b/jasmin/core/internal/instruction_traits.h @@ -5,6 +5,8 @@ #include "jasmin/core/internal/function_state.h" #include "jasmin/core/output.h" #include "jasmin/core/value.h" +#include "nth/io/serialize/deserialize.h" +#include "nth/io/serialize/serialize.h" #include "nth/meta/concepts.h" #include "nth/meta/type.h" @@ -21,6 +23,13 @@ struct Return; struct InstructionSpecification { uint32_t parameters; uint32_t returns; + + friend bool NthSerialize(auto& s, InstructionSpecification spec) { + return nth::io::serialize(s, spec.parameters, spec.returns); + } + friend bool NthDeserialize(auto& d, InstructionSpecification& spec) { + return nth::io::deserialize(d, spec.parameters, spec.returns); + } }; namespace internal { diff --git a/jasmin/core/program.cc b/jasmin/core/program.cc deleted file mode 100644 index 8f9dac0..0000000 --- a/jasmin/core/program.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include "jasmin/core/program.h" - -#include - -#include "jasmin/core/function.h" -#include "nth/debug/debug.h" - -namespace jasmin { - -Function<>& Program::function(std::string_view name) { - auto iter = functions_.find(name); - NTH_REQUIRE((v.harden), iter != functions_.end()); - return *iter->second; -} - -Function<> const& Program::function(std::string_view name) const { - auto iter = functions_.find(name); - NTH_REQUIRE((v.harden), iter != functions_.end()); - return *iter->second; -} - -} // namespace jasmin diff --git a/jasmin/core/program.h b/jasmin/core/program.h index b2c5a81..3413933 100644 --- a/jasmin/core/program.h +++ b/jasmin/core/program.h @@ -9,6 +9,8 @@ #include "jasmin/core/instruction.h" #include "nth/container/flyweight_map.h" #include "nth/debug/debug.h" +#include "nth/io/serialize/deserialize.h" +#include "nth/io/serialize/serialize.h" #include "nth/utility/iterator_range.h" namespace jasmin { @@ -58,6 +60,51 @@ struct Program { [[nodiscard]] Function const& function(function_identifier id) const; [[nodiscard]] Function& function(function_identifier id); + friend bool NthSerialize(auto& s, Program const& p) { + if (not nth::io::serialize_integer(s, p.functions_.size())) { + return false; + } + for (auto const& [name, fn] : p.functions_) { + s.register_function(fn); + if (not nth::io::serialize_integer(s, name.size())) { return false; } + if (not s.write(std::span( + reinterpret_cast(name.data()), name.size()))) { + return false; + } + } + + for (auto const& [name, fn] : p.functions_) { + if (not nth::io::serialize(s, fn)) { return false; } + } + return true; + } + + friend bool NthDeserialize(auto& d, Program& p) { + size_t size; + if (not nth::io::deserialize_integer(d, size)) { return false; } + + for (uint32_t i = 0; i < size; ++i) { + size_t name_size; + if (not nth::io::deserialize_integer(d, name_size)) { return false; } + std::string name(name_size, '\0'); + if (not d.read(std::span( + reinterpret_cast(name.data()), name.size()))) { + return false; + } + + auto [iter, inserted] = p.functions_.try_emplace(name); + if (not inserted) { return false; } + d.register_function(iter->second); + } + + for (Function<>*& fn : d.registered_functions()) { + if (not nth::io::deserialize(d, static_cast&>(*fn))) { + return false; + } + } + return true; + } + // Returns the number of functions managed by this `Program`. size_t function_count() const { return functions_.size(); } @@ -65,6 +112,7 @@ struct Program { return nth::iterator_range(functions_.begin(), functions_.end()); } + private: nth::flyweight_map> functions_; }; diff --git a/jasmin/core/serialization.h b/jasmin/core/serialization.h new file mode 100644 index 0000000..ba32aa6 --- /dev/null +++ b/jasmin/core/serialization.h @@ -0,0 +1,203 @@ +#ifndef JASMIN_CORE_SERIALIZATION_H +#define JASMIN_CORE_SERIALIZATION_H + +#include +#include + +#include "jasmin/core/function.h" +#include "jasmin/core/instruction.h" +#include "jasmin/core/program.h" +#include "nth/io/serialize/deserialize.h" +#include "nth/io/serialize/reader.h" +#include "nth/io/serialize/serialize.h" +#include "nth/io/serialize/writer.h" + +namespace jasmin { +namespace internal { + +template +bool InstructionSerializer(S &, std::span); +template +bool InstructionDeserializer(D &, Function<> &); + +} // namespace internal + +template +struct Serializer : W { + explicit Serializer(std::string& content) : W(content) {} + + friend bool NthSerialize(Serializer &s, std::integral auto x) { + return nth::io::serialize_integer(s, x); + } + + friend bool NthSerialize(Serializer &s, std::floating_point auto x) { + return nth::io::serialize_fixed(s, x); + } + + friend bool NthSerialize(Serializer &s, Function<> const *f) { + auto iter = s.registered_functions_.find(f); + if (iter == s.registered_functions_.end()) { return false; } + return nth::io::serialize_fixed(s, static_cast(iter->second)); + } + + template + friend bool NthSerialize(Serializer &s, Function const &fn) { + if (not nth::io::serialize_integer(s, fn.parameter_count())) { return false; } + if (not nth::io::serialize_integer(s, fn.return_count())) { return false; } + auto const &set_metadata = Metadata(); + std::optional c = s.allocate(sizeof(uint32_t)); + if (not c) { return false; } + std::span insts = fn.raw_instructions(); + + static constexpr auto Serializers = + Set::instructions.reduce([](auto... ts) { + return std::array), + sizeof...(ts)>{ + internal::InstructionSerializer>...}; + }); + + while (not insts.empty()) { + uint16_t index = set_metadata.opcode(insts[0]); + if (not nth::io::serialize_fixed(s, index)) { return false; } + auto immediate_value_count = + set_metadata.metadata(index).immediate_value_count; + Serializers[index](s, insts.subspan(1, immediate_value_count)); + insts = insts.subspan(immediate_value_count + 1); + } + uint16_t distance = static_cast(s.cursor() - *c); + return s.write_at(*c, nth::bytes(distance)); + } + + void register_function(Function<> const & f) { + registered_functions_.emplace(&f, registered_functions_.size()); + } + + private: + absl::flat_hash_map const *, size_t> registered_functions_; +}; + +template +struct Deserializer : R { + explicit Deserializer(std::string_view content) : R(content) {} + + friend bool NthDeserialize(Deserializer &d, std::integral auto &x) { + return nth::io::deserialize_integer(d, x); + } + + friend bool NthDeserialize(Deserializer &d, std::floating_point auto &x) { + return nth::io::deserialize_fixed(d, x); + } + + friend bool NthDeserialize(Deserializer &d, Function<> *&fn) { + uint32_t index; + if (not nth::io::deserialize_fixed(d, index)) { return false; } + if (index >= d.registered_functions_.size()) { return false; } + fn = d.registered_functions_[index]; + return true; + } + + template + friend bool NthDeserialize(Deserializer &d, Function &fn) { + uint32_t parameter_count, return_count; + if (not nth::io::deserialize_integer(d, parameter_count)) { return false; } + if (not nth::io::deserialize_integer(d, return_count)) { return false; } + fn = Function(parameter_count, return_count); + + uint32_t expected_length; + auto c = d.cursor(); + if (not nth::io::deserialize_fixed(d, expected_length)) { return false; } + + auto const &set_metadata = Metadata(); + static constexpr auto Deserializers = + Set::instructions.reduce([](auto... ts) { + return std::array &), + sizeof...(ts)>{ + internal::InstructionDeserializer>...}; + }); + + while (d.cursor() - c < expected_length) { + uint16_t opcode; + if (not nth::io::deserialize_fixed(d, opcode)) { return false; } + + fn.raw_append(set_metadata.function(opcode)); + if (not Deserializers[opcode](d, fn)) { return false; } + } + + return d.cursor() - c == expected_length; + } + + std::span *> registered_functions() { + return registered_functions_; + } + void register_function(Function<> &f) { registered_functions_.push_back(&f); } + + private: + std::vector *> registered_functions_; +}; + +namespace internal { + +template +bool InstructionSerializer(S &s, std::span v) { + if constexpr (nth::type == nth::type) { + return true; + } else if constexpr (nth::type == nth::type) { + return nth::io::serialize(s, v[0].as()); + } else if constexpr (nth::any_of) { + return nth::io::serialize_integer(s, v[0].as()); + } else { + constexpr auto params = [] { + if constexpr (requires { I::execute; }) { + return nth::type.parameters(); + } else { + return nth::type.parameters(); + } + }(); + return params + .template drop<2 + (::jasmin::FunctionState() != nth::type)>() + .reduce([&](auto... ts) { + size_t i = 0; + return ( + nth::io::serialize(s, v[i++].template as>()) and + ...); + }); + } +} + +template +bool InstructionDeserializer(D &d, Function<> &fn) { + if constexpr (nth::type == nth::type) { + return true; + } else if constexpr (nth::type == nth::type) { + InstructionSpecification spec; + if (not nth::io::deserialize(d, spec)) { return false; } + fn.raw_append(spec); + return true; + } else if constexpr (nth::type == nth::type or + nth::type == nth::type) { + ptrdiff_t amount; + if (not nth::io::deserialize(d, amount)) { return false; } + fn.raw_append(amount); + return true; + } else { + constexpr bool HasFunctionState = internal::HasFunctionState; + constexpr auto parameters = + internal::InstructionFunctionType().parameters(); + return parameters.template drop().reduce( + [&](auto... ts) { + return ([&](auto t) { + nth::type_t value; + if (not nth::io::deserialize(d, value)) { return false; } + fn.raw_append(value); + return true; + }(ts) and + ...); + }); + } +} + +} // namespace internal +} // namespace jasmin + +#endif // JASMIN_CORE_SERIALIZATION_H diff --git a/jasmin/serialize/round_trip_test.cc b/jasmin/core/serialization_test.cc similarity index 64% rename from jasmin/serialize/round_trip_test.cc rename to jasmin/core/serialization_test.cc index c467634..141cb21 100644 --- a/jasmin/serialize/round_trip_test.cc +++ b/jasmin/core/serialization_test.cc @@ -1,27 +1,23 @@ +#include "jasmin/core/serialization.h" + #include "jasmin/core/program.h" #include "jasmin/instructions/common.h" -#include "jasmin/serialize/deserialize.h" -#include "jasmin/serialize/reader.h" -#include "jasmin/serialize/serialize.h" -#include "jasmin/serialize/string_reader.h" -#include "jasmin/serialize/string_writer.h" -#include "jasmin/serialize/writer.h" +#include "nth/io/serialize/string_reader.h" +#include "nth/io/serialize/string_writer.h" #include "nth/test/test.h" namespace jasmin { namespace { -std::vector&>> FunctionData() { return {}; } - NTH_TEST("round-trip/integer", auto n) { - std::string s; - StringWriter w(FunctionData(), s); - WriteInteger(w, n); + std::string content; + Serializer serializer(content); + NTH_ASSERT(nth::io::serialize_integer(serializer, n)); decltype(n) m; - StringReader r(s); - ReadInteger(r, m); - NTH_ASSERT(r.size() == 0); + Deserializer deserializer(content); + NTH_ASSERT(nth::io::deserialize_integer(deserializer, m)); + NTH_ASSERT(deserializer.size() == 0); NTH_EXPECT(m == n); } @@ -43,13 +39,13 @@ using Set = MakeInstructionSet*>>; NTH_TEST("round-trip/program/empty") { Program p; - std::string s; - StringWriter w(p.functions(), s); - Serialize(p, w); + std::string content; + Serializer serializer(content); + NTH_ASSERT(nth::io::serialize(serializer, p)); Program q; - StringReader r(s); - NTH_ASSERT(Deserialize(r, q)); + Deserializer deserializer(content); + NTH_ASSERT(nth::io::deserialize(deserializer, q)); NTH_EXPECT(q.function_count() == p.function_count()); } @@ -58,20 +54,13 @@ NTH_TEST("round-trip/program/functions") { auto& f = p.declare("f", 0, 0).function; f.append(); - std::string s; - StringWriter w(p.functions(), s); - Serialize(p, w); - - std::puts("Serialized program:"); - for (size_t i = 0; i < s.size(); ++i) { - std::printf(" %0.2x", s[i]); - if (i % 8 == 7) { std::putchar('\n'); } - } - std::putchar('\n'); + std::string content; + Serializer serializer(content); + NTH_ASSERT(nth::io::serialize(serializer, p)); Program q; - StringReader r(s); - NTH_ASSERT(Deserialize(r, q)); + Deserializer deserializer(content); + NTH_ASSERT(nth::io::deserialize(deserializer, q)); NTH_EXPECT(q.function_count() == p.function_count()); NTH_ASSERT(p.function("f").raw_instructions().size() == q.function("f").raw_instructions().size()); @@ -88,20 +77,13 @@ NTH_TEST("round-trip/program/recursion") { f.append({0, 0}); f.append(); - std::string s; - StringWriter w(p.functions(), s); - Serialize(p, w); - - std::puts("Serialized program:"); - for (size_t i = 0; i < s.size(); ++i) { - std::printf(" %0.2x", s[i]); - if (i % 8 == 7) { std::putchar('\n'); } - } - std::putchar('\n'); + std::string content; + Serializer serializer(content); + NTH_ASSERT(nth::io::serialize(serializer, p)); Program q; - StringReader r(s); - NTH_ASSERT(Deserialize(r, q)); + Deserializer deserializer(content); + NTH_ASSERT(nth::io::deserialize(deserializer, q)); NTH_EXPECT(q.function_count() == p.function_count()); NTH_ASSERT(p.function("f").raw_instructions().size() == q.function("f").raw_instructions().size()); diff --git a/jasmin/serialize/BUILD b/jasmin/serialize/BUILD deleted file mode 100644 index 5b97f30..0000000 --- a/jasmin/serialize/BUILD +++ /dev/null @@ -1,111 +0,0 @@ -package(default_visibility = ["//visibility:private"]) - -cc_library( - name = "deserialize", - hdrs = ["deserialize.h"], - visibility = ["//visibility:public"], - deps = [ - ":reader", - "//jasmin/core:instruction", - "//jasmin/core:metadata", - "@nth_cc//nth/meta:type", - "@nth_cc//nth/meta:sequence", - ], -) - -cc_library( - name = "reader", - hdrs = ["reader.h"], - visibility = ["//visibility:public"], - deps = [ - "//jasmin/core:function", - ], -) - -cc_test( - name = "round_trip_test", - srcs = ["round_trip_test.cc"], - deps = [ - ":deserialize", - ":serialize", - ":string_reader", - ":string_writer", - ":reader", - ":writer", - "//jasmin/instructions:common", - "@nth_cc//nth/test:main", - ], -) - -cc_library( - name = "serialize", - hdrs = ["serialize.h"], - visibility = ["//visibility:public"], - deps = [ - ":writer", - "//jasmin/core:function", - "//jasmin/core:instruction", - "//jasmin/core:metadata", - "//jasmin/core:program", - "@nth_cc//nth/meta:type", - "@nth_cc//nth/meta:sequence", - ], -) - -cc_library( - name = "string_reader", - hdrs = ["string_reader.h"], - srcs = ["string_reader.cc"], - visibility = ["//visibility:public"], - deps = [ - ":reader", - "//jasmin/core:function", - "@com_google_absl//absl/container:flat_hash_map", - ], -) - -cc_test( - name = "string_reader_test", - srcs = ["string_reader_test.cc"], - deps = [ - ":string_reader", - "@nth_cc//nth/test:main", - ], -) - -cc_library( - name = "string_writer", - hdrs = ["string_writer.h"], - srcs = ["string_writer.cc"], - visibility = ["//visibility:public"], - deps = [ - ":writer", - "//jasmin/core:function", - "@com_google_absl//absl/container:flat_hash_map", - ], -) - -cc_test( - name = "string_writer_test", - srcs = ["string_writer_test.cc"], - deps = [ - ":string_writer", - "@nth_cc//nth/test:main", - ], -) - -cc_library( - name = "writer", - hdrs = ["writer.h"], - visibility = ["//visibility:public"], -) - -cc_test( - name = "writer_test", - srcs = ["writer_test.cc"], - deps = [ - ":string_writer", - ":writer", - "@nth_cc//nth/test:main", - ], -) diff --git a/jasmin/serialize/deserialize.h b/jasmin/serialize/deserialize.h deleted file mode 100644 index 72c56e4..0000000 --- a/jasmin/serialize/deserialize.h +++ /dev/null @@ -1,150 +0,0 @@ -#ifndef JASMIN_SERIALIZE_DESERIALIZE_H -#define JASMIN_SERIALIZE_DESERIALIZE_H - -#include -#include - -#include "jasmin/core/function.h" -#include "jasmin/core/instruction.h" -#include "jasmin/core/metadata.h" -#include "jasmin/core/program.h" -#include "jasmin/core/value.h" -#include "jasmin/serialize/reader.h" -#include "nth/meta/sequence.h" -#include "nth/meta/type.h" - -namespace jasmin { - -// Deserializes a `Program` from the reader `r` into `p`. Returns a bool -// indicating whether or not deserialization succeeded. On failure, `p` will be -// in a valid, but unspecified state. -template -bool Deserialize(R& r, Program& p); - -// Implementation - -namespace internal { - -template -bool ReadImpl(Reader auto& r, Function<>& f) { - T value; - if (not JasminDeserialize(r, value)) { return false; } - f.raw_append(value); - return true; -} - -template -bool InstructionDeserializer(R& r, Function<>& f) { - if constexpr (nth::type == nth::type) { - return true; - } else if constexpr (nth::type == nth::type) { - return ReadImpl(r, f); - } else if constexpr (nth::type == nth::type or - nth::type == nth::type) { - return ReadImpl(r, f); - } else { - constexpr bool HasFunctionState = internal::HasFunctionState; - constexpr auto parameters = - internal::InstructionFunctionType().parameters(); - return parameters.template drop().reduce( - [&](auto... ts) { - return (jasmin::internal::ReadImpl>(r, f) and ...); - }); - } -} - -template -inline constexpr std::array InstructionDeserializers = - Set::instructions.reduce([](auto... ts) { - return std::array{InstructionDeserializer, R>...}; - }); - -template -bool Deserialize(R& r, Function& f) { - uint16_t length; - auto c = jasmin::ReadLengthPrefix(r, length); - if (not c) { return false; } - while (r.size() != 0) { - uint16_t op_code; - if (not jasmin::ReadFixed(r, op_code)) { return false; } - f.raw_append(Metadata().function(op_code)); - if (not InstructionDeserializers[op_code](r, f)) { return false; } - } - return r.cursor() - *c == length; -} - -} // namespace internal - -template -bool Deserialize(R& r, Program& p) { - auto start = r.cursor(); - std::vector> cursors; - uint32_t function_count; - if (not jasmin::ReadFixed(r, function_count)) { return false; } - cursors.reserve(function_count); - - for (size_t i = 0; i < function_count; ++i) { - uint32_t offset; - auto c = r.cursor(); - if (not jasmin::ReadFixed(r, offset)) { return false; } - - uint32_t char_count; - if (not jasmin::ReadFixed(r, char_count)) { return false; } - std::string name(char_count, '\0'); - if (not r.read(std::span( - reinterpret_cast(name.data()), char_count))) { - return false; - } - - uint32_t parameter_count, return_count; - if (not jasmin::ReadInteger(r, parameter_count)) { return false; } - if (not jasmin::ReadInteger(r, return_count)) { return false; } - p.declare(name, parameter_count, return_count); - cursors.emplace_back(std::move(name), c); - } - - for (auto& [name, f] : p.functions()) { - r.register_function( - name, &const_cast&>(static_cast const&>(f))); - } - - for (size_t i = 0; i < function_count; ++i) { - uint32_t offset; - r.read(cursors[i].second, - std::span(reinterpret_cast(&offset), sizeof(uint32_t))); - if (r.cursor() - start != offset) { return false; } - - auto& fn = p.function(cursors[i].first); - if (not internal::Deserialize(r, fn)) { return false; } - } - return r.size() == 0; -} - -bool JasminDeserialize(Reader auto& r, std::byte& b) { - return r.read(std::span(&b, 1)); -} - -bool JasminDeserialize(Reader auto& r, std::integral auto& n) { - if constexpr (nth::type == nth::type) { - return jasmin::ReadFixed(r, n); - } else { - return jasmin::ReadInteger(r, n); - } -} - -bool JasminDeserialize(Reader auto& r, std::floating_point auto& f) { - return jasmin::ReadFixed(r, f); -} - -bool JasminDeserialize(Reader auto&, Function<> const*&) { - NTH_UNIMPLEMENTED(); -} - -bool JasminDeserialize(Reader auto& r, InstructionSpecification& spec) { - return jasmin::ReadInteger(r, spec.parameters) and - jasmin::ReadInteger(r, spec.returns); -} - -} // namespace jasmin - -#endif // JASMIN_SERIALIZE_DESERIALIZE_H diff --git a/jasmin/serialize/reader.h b/jasmin/serialize/reader.h deleted file mode 100644 index b8c3fec..0000000 --- a/jasmin/serialize/reader.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef JASMIN_SERIALIZE_READER_H -#define JASMIN_SERIALIZE_READER_H - -#include -#include -#include -#include -#include -#include - -#include "jasmin/core/function.h" - -namespace jasmin { - -// Concept defining a `Reader`, representing an object from which one can -// extract data to deserialize a `Program`. -template -concept Reader = requires(R r, R const& c) { - typename R::cursor_type; - { - c.read(std::declval(), - std::declval>()) - } -> std::same_as; - { r.read(std::declval>()) } -> std::same_as; - { r.skip(size_t{}) } -> std::same_as; - { c.size() } -> std::same_as; - { - r.register_function(std::declval(), - std::declval*>()) - } -> std::same_as; - { c.cursor() } -> std::same_as; -}; - -// Reads `sizeof(n)` bytes from `r` interpreting them as a `T`. On success, -// returns `true` and populates `x` with the read data. On failure, returns -// `false`, and the value of `x` is unspecified. -template -requires std::integral or std::floating_point -bool ReadFixed(Reader auto& r, T& x); - -template -std::optional ReadLengthPrefix(R& r, LengthType& l); - -// Reads a length-prefixed integer from `r` into `n`. The length prefix must fit -// in a single byte, meaning that integers represented must be representable in -// `256` or fewer bytes. -template -bool ReadInteger(Reader auto& r, Num& n); -template -bool ReadInteger(Reader auto& r, Num& n); - -// Implementation - -template -requires std::integral or std::floating_point -bool ReadFixed(Reader auto& r, T& x) { - return r.read( - std::span(reinterpret_cast(&x), sizeof(T))); -} - -template -std::optional ReadLengthPrefix(R& r, LengthType& l) { - if (not jasmin::ReadFixed(r, l)) { return std::nullopt; } - auto c = r.cursor(); - if (l > r.size()) { return std::nullopt; } - return c; -} - -template -bool ReadInteger(Reader auto& r, Num& n) { - uint8_t length; - auto c = jasmin::ReadLengthPrefix(r, length); - if (not c) { return false; } - if (sizeof(n) < length) { return false; } - n = 0; - - for (size_t i = 0; i < length; ++i) { - std::byte b; - r.read(b); - n |= static_cast(static_cast(b)) << (CHAR_BIT * i); - } - - return r.cursor() - *c == length; -} - -template -bool ReadInteger(Reader auto& r, Num& n) { - uint8_t length; - auto c = jasmin::ReadLengthPrefix(r, length); - if (not c) { return false; } - if (sizeof(n) + 1 < length) { return false; } - - std::make_unsigned_t m = 0; - std::byte sign; - r.read(sign); - - for (size_t i = 0; i < length - 1; ++i) { - std::byte b; - r.read(b); - m |= static_cast(static_cast(b)) << (CHAR_BIT * i); - } - if (sign == std::byte{1}) { m = ~m + 1; } - std::memcpy(&n, &m, sizeof(n)); - - return r.cursor() - *c == length; -} - -} // namespace jasmin - -#endif // JASMIN_SERIALIZE_READER_H diff --git a/jasmin/serialize/serialize.h b/jasmin/serialize/serialize.h deleted file mode 100644 index 20d3571..0000000 --- a/jasmin/serialize/serialize.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef JASMIN_SERIALIZE_SERIALIZE_H -#define JASMIN_SERIALIZE_SERIALIZE_H - -#include -#include -#include - -#include "jasmin/core/function.h" -#include "jasmin/core/instruction.h" -#include "jasmin/core/metadata.h" -#include "jasmin/core/program.h" -#include "jasmin/core/value.h" -#include "jasmin/serialize/writer.h" -#include "nth/meta/sequence.h" -#include "nth/meta/type.h" - -namespace jasmin { - -// Serializes a `Program p` via the `Writer w`. -template -void Serialize(Program const& p, W& w); - -// Implementation - -void JasminSerialize(Writer auto& w, std::integral auto n) { - if constexpr (nth::type == nth::type) { - jasmin::WriteFixed(w, n); - } else { - jasmin::WriteInteger(w, n); - } -} -void JasminSerialize(Writer auto& w, std::floating_point auto f) { - jasmin::WriteFixed(w, f); -} - -void JasminSerialize(Writer auto& w, std::byte b) { w.write(b); } - -void JasminSerialize(Writer auto&, Function<> const*) { NTH_UNIMPLEMENTED(); } - -void JasminSerialize(Writer auto& w, InstructionSpecification spec) { - jasmin::WriteInteger(w, spec.parameters); - jasmin::WriteInteger(w, spec.returns); -} - -namespace internal { - -template -void InstructionSerializer(std::span v, W& w) { - if constexpr (nth::type == nth::type) { - } else if constexpr (nth::type == nth::type) { - JasminSerialize(w, v[0].as()); - } else if constexpr (nth::any_of) { - JasminSerialize(w, v[0].as()); - } else { - constexpr auto params = [] { - if constexpr (requires { I::execute; }) { - return nth::type.parameters(); - } else { - return nth::type.parameters(); - } - }(); - params - .template drop<2 + (::jasmin::FunctionState() != nth::type)>() - .reduce([&](auto... ts) { - size_t i = 0; - (JasminSerialize(w, v[i++].template as>()), ...); - }); - } -} - -template -inline constexpr std::array InstructionSerializers = - Set::instructions.reduce([](auto... ts) { - return std::array{InstructionSerializer>...}; - }); - -template -std::span View(T const& value) { - return std::span( - reinterpret_cast(std::addressof(value))); -} - -} // namespace internal - -template -void Serialize(Program const& p, W& w) { - auto start = w.cursor(); - absl::flat_hash_map locations; - jasmin::WriteFixed(w, static_cast(p.function_count())); - for (auto const& [name, fn] : p.functions()) { - locations.emplace(name, w.allocate(sizeof(uint32_t))); - jasmin::WriteFixed(w, static_cast(name.size())); - w.write(std::span( - reinterpret_cast(name.data()), name.size())); - jasmin::WriteInteger(w, fn.parameter_count()); - jasmin::WriteInteger(w, fn.return_count()); - } - for (auto const& [name, fn] : p.functions()) { - auto iter = locations.find(name); - NTH_REQUIRE(iter != locations.end()); - uint32_t distance = w.cursor() - start; - w.write(iter->second, - std::span(reinterpret_cast(&distance), - sizeof(distance))); - auto const& set_metadata = Metadata(); - typename W::cursor_type c = w.allocate(sizeof(uint16_t)); - std::span insts = fn.raw_instructions(); - while (not insts.empty()) { - uint16_t index = set_metadata.opcode(insts[0]); - jasmin::WriteFixed(w, index); - auto immediate_value_count = - set_metadata.metadata(index).immediate_value_count; - internal::InstructionSerializers[index]( - insts.subspan(1, immediate_value_count), w); - insts = insts.subspan(immediate_value_count + 1); - } - WritePrefixedLength(w, c); - } -} - -} // namespace jasmin - -#endif // JASMIN_SERIALIZE_SERIALIZE_H diff --git a/jasmin/serialize/string_reader.cc b/jasmin/serialize/string_reader.cc deleted file mode 100644 index 7000b02..0000000 --- a/jasmin/serialize/string_reader.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include "jasmin/serialize/string_reader.h" - -#include "jasmin/serialize/reader.h" - -namespace jasmin { - -bool StringReader::read(cursor_type c, std::span buffer) const { - if (c.dist_ < buffer.size()) { return false; } - std::memcpy(buffer.data(), s_.end() - c.dist_, buffer.size()); - return true; -} - -bool StringReader::read(std::byte& b) { - if (s_.empty()) { return false; } - b = static_cast(s_[0]); - s_.remove_prefix(1); - return true; -} - -bool StringReader::read(std::span buffer) { - if (s_.size() < buffer.size()) { return false; } - std::memcpy(buffer.data(), s_.data(), buffer.size()); - s_.remove_prefix(buffer.size()); - return true; -} - -bool StringReader::skip(size_t n) { - if (s_.size() < n) { return false; } - s_.remove_prefix(n); - return true; -} - -size_t StringReader::size() const { return s_.size(); } - -typename StringReader::cursor_type StringReader::cursor() const { - return cursor_type(s_.size()); -} - -bool JasminDeserialize(StringReader& r, Function<>*& f) { - uint32_t length; - if (not jasmin::ReadFixed(r, length)) { return false; } - - std::string name(length, '\0'); - if (not r.read(std::span(reinterpret_cast(name.data()), - length))) { - return false; - } - auto iter = r.by_name_.find(name); - if (iter == r.by_name_.end()) { return false; } - f = iter->second; - return true; -} - -void StringReader::register_function(std::string name, Function<>* f) { - by_name_.emplace(std::move(name), f); -} - -} // namespace jasmin diff --git a/jasmin/serialize/string_reader.h b/jasmin/serialize/string_reader.h deleted file mode 100644 index 82c8a2f..0000000 --- a/jasmin/serialize/string_reader.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef JASMIN_SERIALIZE_STRING_READER_H -#define JASMIN_SERIALIZE_STRING_READER_H - -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "jasmin/core/function.h" - -namespace jasmin { - -// Writes data to a string referenced by the writer. -struct StringReader { - explicit StringReader(std::string_view s) : s_(s) {} - - struct cursor_type { - friend constexpr bool operator==(cursor_type, cursor_type) = default; - friend constexpr ptrdiff_t operator-(cursor_type lhs, cursor_type rhs) { - return rhs.dist_ - lhs.dist_; - } - - private: - friend StringReader; - constexpr cursor_type(ptrdiff_t n) : dist_(n) {} - ptrdiff_t dist_; - }; - - bool read(cursor_type c, std::span) const; - bool read(std::span buffer); - bool read(std::byte& b); - - // If `size() >= n`, returns `true` and skips forward `n` bytes, reducing the - // size by `n`. Otherwise, returns `false` without modifying the reader. - bool skip(size_t n); - - // Returns the number of bytes left to be read. - size_t size() const; - - cursor_type cursor() const; - - void register_function(std::string name, Function<>* f); - - friend bool JasminDeserialize(StringReader& r, Function<>*& f); - - private: - std::string_view s_; - absl::flat_hash_map*> by_name_; -}; - -} // namespace jasmin - -#endif // JASMIN_SERIALIZE_STRING_READER_H diff --git a/jasmin/serialize/string_reader_test.cc b/jasmin/serialize/string_reader_test.cc deleted file mode 100644 index 5aa7f33..0000000 --- a/jasmin/serialize/string_reader_test.cc +++ /dev/null @@ -1,89 +0,0 @@ -#include "jasmin/serialize/string_reader.h" - -#include - -#include "nth/test/test.h" - -namespace jasmin { -namespace { - -NTH_TEST("StringReader/basic") { - std::string_view input = "abcdefghijklmnopqrstuvwxyz"; - StringReader r(input); - NTH_ASSERT(r.size() == 26); - input = ""; - NTH_ASSERT(r.size() == 26); - r = StringReader(input); - NTH_ASSERT(r.size() == 0); -} - -NTH_TEST("StringReader/skip") { - std::string_view input = "abcdefghijklmnopqrstuvwxyz"; - StringReader r(input); - NTH_ASSERT(r.skip(2)); - NTH_ASSERT(r.size() == 24); - NTH_ASSERT(r.skip(2)); - NTH_ASSERT(r.size() == 22); - NTH_ASSERT(not r.skip(200)); - NTH_ASSERT(r.size() == 22); - NTH_ASSERT(r.skip(r.size())); - NTH_ASSERT(r.size() == 0); -} - -NTH_TEST("StringReader/cursor-skip") { - std::string_view input = "abcdefghijklmnopqrstuvwxyz"; - StringReader r(input); - auto c = r.cursor(); - NTH_ASSERT(r.skip(2)); - NTH_ASSERT(r.cursor() - c == 2); -} - -NTH_TEST("StringReader/read-byte") { - std::string_view input = "abcdefghijklmnopqrstuvwxyz"; - StringReader r(input); - std::byte b; - auto c = r.cursor(); - NTH_ASSERT(r.read(b)); - NTH_EXPECT(b == std::byte{'a'}); - NTH_ASSERT(r.cursor() - c == 1); -} - -NTH_TEST("StringReader/read-bytes") { - std::string_view input = "abcdefghijklmnopqrstuvwxyz"; - StringReader r(input); - std::array bs; - auto c = r.cursor(); - NTH_ASSERT(r.read(bs)); - NTH_EXPECT(bs == std::array{ - std::byte{'a'}, - std::byte{'b'}, - std::byte{'c'}, - std::byte{'d'}, - }); - NTH_ASSERT(r.cursor() - c == 4); - std::array too_many_bytes; - NTH_ASSERT(not r.read(too_many_bytes)); - NTH_ASSERT(r.cursor() - c == 4); -} - -NTH_TEST("StringReader/read-bytes-at") { - std::string_view input = "abcdefghijklmnopqrstuvwxyz"; - StringReader r(input); - std::array bs; - auto c = r.cursor(); - r.skip(4); - NTH_ASSERT(r.read(c, bs)); - NTH_EXPECT(bs == std::array{ - std::byte{'a'}, - std::byte{'b'}, - std::byte{'c'}, - std::byte{'d'}, - }); - NTH_ASSERT(r.cursor() - c == 4); - std::array too_many_bytes; - NTH_EXPECT(not r.read(c, too_many_bytes)); - NTH_ASSERT(r.cursor() - c == 4); -} - -} // namespace -} // namespace jasmin diff --git a/jasmin/serialize/string_writer.cc b/jasmin/serialize/string_writer.cc deleted file mode 100644 index 4e8298f..0000000 --- a/jasmin/serialize/string_writer.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "jasmin/serialize/string_writer.h" - -#include - -#include "jasmin/serialize/writer.h" - -namespace jasmin { - -void StringWriter::write(std::span data) { - auto const* p = reinterpret_cast(data.data()); - s_.insert(s_.end(), p, p + data.size()); -} - -void StringWriter::write(std::byte data) { - s_.push_back(static_cast(data)); -} - -StringWriter::cursor_type StringWriter::allocate(size_t n) { - auto c = cursor(); - s_.resize(s_.size() + n); - return c; -} - -StringWriter::cursor_type StringWriter::cursor() const { return s_.size(); } - -void StringWriter::write(cursor_type c, std::span data) { - std::memcpy(s_.data() + c, data.data(), data.size()); -} - -void JasminSerialize(StringWriter& w, Function<> const* f) { - auto iter = w.functions_.find(f); - NTH_REQUIRE(iter != w.functions_.end()); - std::string_view name = iter->second; - jasmin::WriteFixed(w, static_cast(name.size())); - w.write(std::span( - reinterpret_cast(name.data()), name.size())); -} - -} // namespace jasmin diff --git a/jasmin/serialize/string_writer.h b/jasmin/serialize/string_writer.h deleted file mode 100644 index b9ec179..0000000 --- a/jasmin/serialize/string_writer.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef JASMIN_SERIALIZE_STRING_WRITER_H -#define JASMIN_SERIALIZE_STRING_WRITER_H - -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "jasmin/core/function.h" - -namespace jasmin { - -// Writes data to a string referenced by the writer. -struct StringWriter { - using cursor_type = size_t; - - // Contsructs a string writer which writes all data to the string referenced - // by `s`. - explicit StringWriter(auto const& function_range, std::string& s) : s_(s) { - for (auto const& [name, fn] : function_range) { - functions_.emplace(&fn, name); - } - } - - cursor_type allocate(size_t n); - cursor_type cursor() const; - - void write(std::span data); - void write(std::byte data); - - void write(cursor_type c, std::span data); - - friend void JasminSerialize(StringWriter& w, Function<> const* f); - - private: - std::string& s_; - absl::flat_hash_map const*, std::string_view> functions_; -}; - -} // namespace jasmin - -#endif // JASMIN_SERIALIZE_STRING_WRITER_H diff --git a/jasmin/serialize/string_writer_test.cc b/jasmin/serialize/string_writer_test.cc deleted file mode 100644 index 49d1102..0000000 --- a/jasmin/serialize/string_writer_test.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "jasmin/serialize/string_writer.h" - -#include -#include - -#include "nth/test/test.h" - -namespace jasmin { -namespace { - -std::vector &>> FunctionData() { return {}; } - -NTH_TEST("StringWriter/basic") { - std::string s; - StringWriter w(FunctionData(), s); - NTH_EXPECT(s.empty()); -} - -NTH_TEST("StringWriter/allocate") { - std::string s; - StringWriter w(FunctionData(), s); - auto c = w.allocate(4); - NTH_EXPECT(w.cursor() - c == 4); - NTH_EXPECT(s.size() == 4); - w.write(c, std::span(reinterpret_cast("abcd"), 4)); - NTH_EXPECT(s.size() == 4); - NTH_EXPECT(w.cursor() - c == 4); - NTH_EXPECT(s == "abcd"); -} - -NTH_TEST("StringWriter/write") { - std::string s; - StringWriter w(FunctionData(), s); - w.write(std::span(reinterpret_cast("abcd"), 4)); - NTH_EXPECT(s.size() == 4); - NTH_EXPECT(s == "abcd"); - w.write(std::span(reinterpret_cast("efgh"), 4)); - NTH_EXPECT(s == "abcdefgh"); - w.write(std::byte{'i'}); - NTH_EXPECT(s == "abcdefghi"); -} - -} // namespace -} // namespace jasmin diff --git a/jasmin/serialize/writer.h b/jasmin/serialize/writer.h deleted file mode 100644 index 4963234..0000000 --- a/jasmin/serialize/writer.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef JASMIN_SERIALIZE_WRITER_H -#define JASMIN_SERIALIZE_WRITER_H - -#include -#include -#include -#include - -namespace jasmin { - -// Concept defining a `Writer`, representing a mechanism by which one can -// serialize a `Program`. -template -concept Writer = requires(W w) { - typename W::cursor_type; - { - w.allocate(std::declval()) - } -> std::same_as; - { w.cursor() } -> std::same_as; - { - w.write(std::declval(), - std::declval>()) - } -> std::same_as; - { w.write(std::byte{}) } -> std::same_as; - { w.write(std::declval>()) } -> std::same_as; -}; - -// Writes `n` to `w` with the same bit-representation, taking exactly -// `sizeof(n)` bytes. -void WriteFixed(Writer auto& w, std::integral auto n); -void WriteFixed(Writer auto& w, std::floating_point auto f); - -// Writes a number into the `sizeof(LengthType)` bytes starting at `c` which is -// equal to the number of bytes between `w.cursor()` and then end of the -// location being written to. That is, `w.cursor() - c - sizeof(LengthType)`. -// Behavior is undefined if `w.cursor() - c - sizeof(LengthType)` is not -// expressible as an unsigned number in `sizeof(LengthType)` or fewer bytes. -template -void WritePrefixedLength(W& w, typename W::cursor_type c); - -// Writes a length-prefixed integer to `w` with the value `n`. The length prefix -// must fit in a single byte, meaning that integers represented must be -// representable in `256` or fewer bytes. -void WriteInteger(Writer auto& w, std::integral auto n); - -// Implementation - -void WriteFixed(Writer auto& w, std::integral auto n) { - w.write(std::span(reinterpret_cast(std::addressof(n)), - sizeof(n))); -} -void WriteFixed(Writer auto& w, std::floating_point auto f) { - w.write(std::span(reinterpret_cast(std::addressof(f)), - sizeof(f))); -} - -template -void WritePrefixedLength(W& w, typename W::cursor_type c) { - LengthType length = w.cursor() - c - sizeof(LengthType); - w.write(c, std::span(reinterpret_cast(&length), - sizeof(length))); -} - -void WriteInteger(Writer auto& w, std::integral auto n) { - auto c = w.allocate(1); - - std::make_unsigned_t m; - if constexpr (std::is_signed_v) { - std::memcpy(&m, &n, sizeof(m)); - w.write(std::byte{n < 0}); - if (n < 0) { m = ~m + 1; } - } else { - m = n; - } - - if constexpr (sizeof(n) == 1) { - w.write(static_cast(m)); - } else { - while (m) { - w.write(static_cast(m & uint8_t{0xff})); - m >>= 8; - } - } - jasmin::WritePrefixedLength(w, c); -} - -} // namespace jasmin - -#endif // JASMIN_SERIALIZE_WRITER_H diff --git a/jasmin/serialize/writer_test.cc b/jasmin/serialize/writer_test.cc deleted file mode 100644 index 01b1a4e..0000000 --- a/jasmin/serialize/writer_test.cc +++ /dev/null @@ -1,102 +0,0 @@ -#include "jasmin/serialize/writer.h" - -#include -#include - -#include "jasmin/serialize/string_writer.h" -#include "nth/test/test.h" - -namespace jasmin { -namespace { - -std::vector &>> FunctionData() { return {}; } - -NTH_TEST("writer/fixed/char") { - std::string s; - StringWriter w(FunctionData(), s); - WriteFixed(w, 'x'); - NTH_EXPECT(s == "x"); -} - -NTH_TEST("writer/fixed/uint64_t") { - std::string s; - StringWriter w(FunctionData(), s); - WriteFixed(w, uint64_t{1}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00", 8)); - - WriteFixed(w, uint64_t{255}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00" - "\xff\x00\x00\x00\x00\x00\x00\x00", - 16)); - - WriteFixed(w, uint64_t{257}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00" - "\xff\x00\x00\x00\x00\x00\x00\x00" - "\x01\x01\x00\x00\x00\x00\x00\x00", - 24)); - WriteFixed(w, uint32_t{257}); - WriteFixed(w, uint32_t{257}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00" - "\xff\x00\x00\x00\x00\x00\x00\x00" - "\x01\x01\x00\x00\x00\x00\x00\x00" - "\x01\x01\x00\x00\x01\x01\x00\x00", - 32)); -} - -NTH_TEST("writer/fixed/int64_t") { - std::string s; - StringWriter w(FunctionData(), s); - WriteFixed(w, int64_t{1}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00", 8)); - - WriteFixed(w, int64_t{255}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00" - "\xff\x00\x00\x00\x00\x00\x00\x00", - 16)); - - WriteFixed(w, int64_t{257}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00" - "\xff\x00\x00\x00\x00\x00\x00\x00" - "\x01\x01\x00\x00\x00\x00\x00\x00", - 24)); - WriteFixed(w, int32_t{-257}); - WriteFixed(w, int32_t{-257}); - NTH_ASSERT(s == std::string_view("\x01\x00\x00\x00\x00\x00\x00\x00" - "\xff\x00\x00\x00\x00\x00\x00\x00" - "\x01\x01\x00\x00\x00\x00\x00\x00" - "\xff\xfe\xff\xff\xff\xfe\xff\xff", - 32)); -} - -NTH_TEST("writer/integer") { - std::string s; - StringWriter w(FunctionData(), s); - WriteInteger(w, 2u); - NTH_ASSERT(s == std::string_view("\x01\x02", 2)); - - s.clear(); - WriteInteger(w, 0u); - NTH_ASSERT(s == std::string_view("\x00", 1)); - - s.clear(); - WriteInteger(w, 257u); - NTH_ASSERT(s == std::string_view("\x02\x01\x01", 3)); - - s.clear(); - WriteInteger(w, -1); - NTH_EXPECT(s.size() == 3); - NTH_ASSERT(s == std::string_view("\x02\x01\x01", 3)); - - s.clear(); - WriteInteger(w, -257); - NTH_EXPECT(s.size() == 4); - NTH_ASSERT(s == std::string_view("\x03\x01\x01\x01", 4)); - - s.clear(); - WriteInteger(w, 257); - NTH_EXPECT(s.size() == 4); - NTH_ASSERT(s == std::string_view("\x03\x00\x01\x01", 4)); -} - -} // namespace -} // namespace jasmin