Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encode function #23

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
BasedOnStyle: Google
BreakStringLiterals: false
NamespaceIndentation: All
BreakBeforeBinaryOperators: NonAssignment
AlignOperands: true
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ endif()
include(${CMAKE_CURRENT_LIST_DIR}/cmake/HunterGate.cmake)

HunterGate(
URL https:/qdrvm/hunter/archive/refs/tags/v0.23.257-qdrvm10.tar.gz
SHA1 72b446a4424ba28ea90f9a68a9134b0f8e44b5b2
URL https:/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm2.zip
SHA1 8599cf0bcf24db83ab0d14e4cf5c3dcc744bd20c
)

project(Scale LANGUAGES CXX VERSION 1.1.0)
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,31 @@ try {
You may need to encode or decode custom data types, you have to define custom << and >> operators.
Please note, that your custom data types must be default-constructible.
```c++
// Implicit encoding/decoding as tuple deduced for `MyTuple1`.
struct MyTuple1 {
int a = 0;
std::string b;
};

struct MyTuple2 {
int a = 0;
std::string b;
str::string debug_message;

// Select fields for encoding/decoding as tuple.
auto tie() { return std::tie(a, b); }
};

struct MyType {
int a = 0;
std::string b;

// Disable encoding/decoding as tuple.
void tie();
};

ScaleEncoderStream &operator<<(ScaleEncoderStream &s, const MyType &v) {
return s << v.a << v.b;
void encodeTo(SCALE_FN, const MyType &v) {
encodeTo(tag, fn, v.a, v.b);
}

ScaleDecoderStream &operator>>(ScaleDecoderStream &s, MyType &v) {
Expand Down
4 changes: 1 addition & 3 deletions include/scale/bitvec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ namespace scale {
struct BitVec {
std::vector<bool> bits;

bool operator==(const BitVec &other) const {
return bits == other.bits;
}
bool operator==(const BitVec &) const = default;
};
} // namespace scale
40 changes: 40 additions & 0 deletions include/scale/encode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <scale/fn.hpp>
#include <scale/outcome/catch.hpp>
#include <vector>

namespace scale {
using Bytes = std::vector<uint8_t>;

outcome::result<void> encodeTo(Bytes &out, const auto &...a) {
return encodeFn(
[&](BytesIn raw) {
// TODO(turuslan): qtils::append(out, raw);
out.insert(out.end(), raw.begin(), raw.end());
},
a...);
}

outcome::result<Bytes> encode(const auto &...a) {
Bytes out;
OUTCOME_TRY(encodeTo(out, a...));
return out;
}

outcome::result<size_t> encodeSizeOutcome(const auto &...a) {
size_t n = 0;
OUTCOME_TRY(encodeFn([&](BytesIn raw) { n += raw.size(); }, a...));
return n;
}

size_t encodeSize(const auto &...a) {
return encodeSizeOutcome(a...).value();
}
} // namespace scale
27 changes: 8 additions & 19 deletions include/scale/encode_append.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,14 @@

#pragma once

#include <cstdint>
#include <scale/outcome/outcome.hpp>
#include <scale/types.hpp>
#include <span>
#include <vector>

namespace scale {

/**
* Vector wrapper, that is scale encoded without prepended CompactInteger
*/
struct EncodeOpaqueValue {
ConstSpanOfBytes v;
};

template <class Stream,
typename = std::enable_if_t<Stream::is_encoder_stream>>
Stream &operator<<(Stream &s, const EncodeOpaqueValue &value) {
for (auto &&it = value.v.begin(), end = value.v.end(); it != end; ++it) {
s << *it;
}
return s;
}
using BytesIn = std::span<const uint8_t>;
using Bytes = std::vector<uint8_t>;

/**
* Adds an EncodeOpaqueValue to a scale encoded vector of EncodeOpaqueValue's.
Expand All @@ -43,6 +31,7 @@ namespace scale {
* \param self_encoded
* @return success if input was appended to self_encoded, failure otherwise
*/
outcome::result<void> append_or_new_vec(std::vector<uint8_t> &self_encoded,
ConstSpanOfBytes input);
outcome::result<void> append_or_new_vec(Bytes &encoded,
BytesIn items_raw,
size_t items_count);
} // namespace scale
34 changes: 34 additions & 0 deletions include/scale/fn.fwd.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <cstdint>
#include <span>

#define _SCALE_FN const ::scale::Fn auto &fn
#define SCALE_FN const ::scale::FnTag &tag, _SCALE_FN

namespace scale {
using BytesIn = std::span<const uint8_t>;

struct FnTag {};

template <typename F>
concept Fn = requires(F f) { f(BytesIn{}); };

struct EncodeCompact {
uint64_t v;

void tie();
};

struct EncodeRaw {
BytesIn v;

void tie();
};
} // namespace scale
213 changes: 213 additions & 0 deletions include/scale/fn.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <boost/variant.hpp>
#include <memory>
#include <optional>
#include <scale/bitvec.hpp>
#include <scale/fn.fwd.hpp>
#include <scale/outcome/catch.hpp>
#include <scale/outcome/outcome_throw.hpp>
#include <scale/scale_error.hpp>
#include <scale/tie.hpp>
#include <scale/to_le.hpp>
#include <scale/types.hpp>
#include <variant>

namespace scale {
outcome::result<void> encodeFn(_SCALE_FN, const auto &...v) {
return outcomeCatch([&] { encodeTo(FnTag{}, fn, v...); });
}

void encodeTo(SCALE_FN, const auto &v0, const auto &v1, const auto &...v) {
encodeTo(tag, fn, v0);
encodeTo(tag, fn, v1, v...);
}

template <std::integral T>
void encodeTo(SCALE_FN, const T &v) {
if constexpr (std::is_same_v<T, bool>) {
encodeTo(tag, fn, static_cast<uint8_t>(v ? 1 : 0));
} else {
auto le = toLE(v);
fn({reinterpret_cast<const uint8_t *>(&le), sizeof(T)});
}
}

template <typename T>
void encodeTo(SCALE_FN, const T &v)
requires(std::is_enum_v<T>)
{
encodeTo(tag, fn, static_cast<std::underlying_type_t<T>>(v));
}

template <typename T>
size_t bitCount(const T &v) {
if constexpr (std::is_same_v<T, CompactInteger>) {
return v.is_zero() ? 0 : msb(v) + 1;
} else {
return 64 - __builtin_clzll(uint64_t{v});
}
}

void encodeCompact(SCALE_FN, uint64_t v, size_t bits) {
auto mask = [&]<typename T>(T v, auto mask) {
encodeTo(tag, fn, static_cast<T>((v << 2) | mask));
};
if (bits <= 8 - 2) {
return mask(static_cast<uint8_t>(v), 0);
}
if (bits <= 16 - 2) {
return mask(static_cast<uint16_t>(v), 1);
}
if (bits <= 32 - 2) {
return mask(static_cast<uint32_t>(v), 2);
}
auto bytes = (bits + 7) / 8;
uint8_t raw[1 + sizeof(uint64_t)];
raw[0] = ((bytes - 4) << 2) | 3;
*reinterpret_cast<uint64_t *>(raw + 1) = toLE(v);
fn({raw, 1 + bytes});
}

void encodeCompact(SCALE_FN, uint64_t v) {
encodeCompact(tag, fn, v, bitCount(v));
}

void encodeTo(SCALE_FN, const std::same_as<CompactInteger> auto &v) {
if (v.sign() < 0) {
raise(EncodeError::NEGATIVE_COMPACT_INTEGER);
}
auto bits = bitCount(v);
if (bits <= 64) {
return encodeCompact(tag, fn, uint64_t{v}, bits);
}
if (bits > 8 * (4 + 63)) {
raise(EncodeError::COMPACT_INTEGER_TOO_BIG);
}
auto bytes = (bits + 7) / 8;
uint8_t raw[1 + 4 + 63];
raw[0] = ((bytes - 4) << 2) | 3;
export_bits(v, raw + 1, 8, false);
fn({raw, 1 + bytes});
}

void encodeTo(SCALE_FN, const EncodeCompact &v) {
encodeCompact(tag, fn, v.v);
}

void encodeTo(SCALE_FN, const EncodeRaw &v) {
fn(v.v);
}

template <typename T>
void encodeTo(SCALE_FN, const std::optional<T> &v) {
if constexpr (std::is_same_v<T, bool>) {
encodeTo(tag, fn, static_cast<uint8_t>(v ? *v ? 1 : 2 : 0));
} else {
encodeTo(tag, fn, static_cast<uint8_t>(v ? 1 : 0));
if (v) {
encodeTo(tag, fn, *v);
}
}
}

template <typename... T>
void encodeTo(SCALE_FN, const std::variant<T...> &v) {
static_assert(sizeof...(T) <= 0xff);
encodeTo(tag, fn, static_cast<uint8_t>(v.index()));
visit([&](const auto &v) { encodeTo(tag, fn, v); }, v);
}

template <typename... T>
void encodeTo(SCALE_FN, const boost::variant<T...> &v) {
static_assert(sizeof...(T) <= 0xff);
encodeTo(tag, fn, static_cast<uint8_t>(v.which()));
apply_visitor([&](const auto &v) { encodeTo(tag, fn, v); }, v);
}

template <typename... T>
void encodeTo(SCALE_FN, const std::tuple<T...> &v) {
if constexpr (sizeof...(T) != 0) {
std::apply([&](const auto &...v) { encodeTo(tag, fn, v...); }, v);
}
}

void encodeTo(SCALE_FN, const std::vector<bool> &v) {
encodeCompact(tag, fn, v.size());
for (bool x : v) {
encodeTo(tag, fn, x);
}
}

void encodeTo(SCALE_FN, const BitVec &v) {
encodeCompact(tag, fn, v.bits.size());
uint64_t mask = 1;
uint64_t value = 0;
for (auto bit : v.bits) {
if (bit) {
value |= mask;
}
if ((mask & 0x8000000000000000) == 0) {
mask <<= 1;
} else {
mask = 1;
encodeTo(tag, fn, value);
value = 0;
}
}
if ((mask & 1) == 0) {
auto bits = bitCount(mask) - 1;
auto bytes = (bits + 7) / 8;
auto le = toLE(value);
fn({reinterpret_cast<const uint8_t *>(&le), bytes});
}
}

template <IsTie T>
requires(not std::ranges::sized_range<T>)
void encodeTo(SCALE_FN, const T &v) {
encodeTo(tag, fn, ::scale::tie(v));
}

void encodeTo(SCALE_FN, const std::ranges::sized_range auto &v) {
if constexpr (requires { std::span{v}; }) {
std::span s{v};
using S = decltype(s);
if constexpr (S::extent == std::dynamic_extent) {
encodeCompact(tag, fn, s.size());
}
using X = std::remove_const_t<typename S::element_type>;
if constexpr (std::is_same_v<X, uint8_t>) {
return fn(s);
}
if constexpr (std::is_same_v<X, char>) {
// TODO: return fn(qtils::str2byte(s));
return fn(
BytesIn{reinterpret_cast<const uint8_t *>(s.data()), s.size()});
}
} else {
encodeCompact(tag, fn, v.size());
}
for (auto &x : v) {
encodeTo(tag, fn, x);
}
}

void encodeTo(SCALE_FN, const std::nullptr_t &) = delete;

void encodeTo(SCALE_FN, const auto *) = delete;

template <typename T>
void encodeTo(SCALE_FN, const std::shared_ptr<T> &v) {
if (v == nullptr) {
raise(EncodeError::DEREF_NULLPOINTER);
}
encodeTo(tag, fn, *v);
}
} // namespace scale
Loading
Loading