mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-07-25 03:22:43 -04:00
52b6c66fa1
# Description of Changes This adds C++ server bindings (/crate/bindings-cpp) to allow writing C++ 20 modules. - Emscripten WASM build system integration with CMake - Macro-based code generation (SPACETIMEDB_TABLE, SPACETIMEDB_REDUCER, etc) - All SpacetimeDB types supported (primitives, Timestamp, Identity, Uuid, etc) - Product types via SPACETIMEDB_STRUCT - Sum types via SPACETIMEDB_ENUM - Constraints marked with FIELD* macros # API and ABI breaking changes None # Expected complexity level and risk 2 - Doesn't heavily impact any other areas but is complex macro C++ structure to support a similar developer experience, did have a small impact on init command # Testing - [x] modules/module-test-cpp - heavily tested every reducer - [x] modules/benchmarks-cpp - tested through the standalone (~6x faster than C#, ~6x slower than Rust) - [x] modules/sdk-test-cpp - [x] modules/sdk-test-procedure-cpp - [x] modules/sdk-test-view-cpp - [x] Wrote several test modules myself - [x] Quickstart smoketest [Currently in progress] - [ ] Write Blackholio C++ server module --------- Signed-off-by: Jason Larabie <jason@clockworklabs.io> Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: Ryan <r.ekhoff@clockworklabs.io> Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
268 lines
9.5 KiB
C++
268 lines
9.5 KiB
C++
#ifndef SPACETIMEDB_BSATN_READER_H
|
|
#define SPACETIMEDB_BSATN_READER_H
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
#include <cstdint>
|
|
#include <stdexcept>
|
|
#include <optional>
|
|
#include <functional>
|
|
#include <span>
|
|
#include <type_traits>
|
|
#include <cstring>
|
|
#include <variant>
|
|
// uint128_placeholder.h removed - types are in spacetimedb/types.h
|
|
#include "types.h"
|
|
|
|
namespace SpacetimeDB::bsatn {
|
|
|
|
class Reader;
|
|
template<typename T> T deserialize(Reader& r);
|
|
template<typename T> struct bsatn_traits;
|
|
|
|
// Helper traits
|
|
template<typename> struct is_std_optional : std::false_type {};
|
|
template<typename T> struct is_std_optional<std::optional<T>> : std::true_type {};
|
|
template<typename T> constexpr bool is_std_optional_v = is_std_optional<T>::value;
|
|
|
|
template<typename> struct is_std_vector : std::false_type {};
|
|
template<typename T> struct is_std_vector<std::vector<T>> : std::true_type {};
|
|
template<typename T> constexpr bool is_std_vector_v = is_std_vector<T>::value;
|
|
|
|
class Reader {
|
|
public:
|
|
// Constructors - using uint8_t consistently
|
|
Reader(const uint8_t* data, size_t size) : current_ptr(data), end_ptr(data + size) {}
|
|
Reader(std::span<const uint8_t> data) : current_ptr(data.data()), end_ptr(data.data() + data.size()) {}
|
|
Reader(const std::vector<uint8_t>& data) : current_ptr(data.data()), end_ptr(data.data() + data.size()) {}
|
|
|
|
|
|
// Template method for reading primitive types (reduces implementation duplication)
|
|
template<typename T>
|
|
T read_primitive_le() {
|
|
static_assert(std::is_arithmetic_v<T>, "read_primitive_le only works with arithmetic types");
|
|
check_available(sizeof(T));
|
|
T val;
|
|
std::memcpy(&val, current_ptr, sizeof(T));
|
|
advance(sizeof(T));
|
|
return val;
|
|
}
|
|
|
|
// Public API methods (delegates to template where possible)
|
|
inline bool read_bool() {
|
|
check_available(1);
|
|
uint8_t val = *current_ptr;
|
|
advance(1);
|
|
if (val > 1) {
|
|
std::abort(); // Invalid bool value in BSATN deserialization
|
|
}
|
|
return val != 0;
|
|
}
|
|
inline uint8_t read_u8() {
|
|
check_available(1);
|
|
uint8_t val = *current_ptr;
|
|
advance(1);
|
|
return val;
|
|
}
|
|
uint16_t read_u16_le() { return read_primitive_le<uint16_t>(); }
|
|
uint32_t read_u32_le() { return read_primitive_le<uint32_t>(); }
|
|
uint64_t read_u64_le() { return read_primitive_le<uint64_t>(); }
|
|
inline SpacetimeDB::u128 read_u128_le() {
|
|
uint64_t low = read_u64_le();
|
|
uint64_t high = read_u64_le();
|
|
return SpacetimeDB::u128(high, low);
|
|
}
|
|
inline SpacetimeDB::u256_placeholder read_u256_le() {
|
|
check_available(32);
|
|
SpacetimeDB::u256_placeholder val;
|
|
std::memcpy(val.data.data(), current_ptr, 32);
|
|
advance(32);
|
|
return val;
|
|
}
|
|
|
|
int8_t read_i8() { return static_cast<int8_t>(read_u8()); }
|
|
int16_t read_i16_le() { return static_cast<int16_t>(read_u16_le()); }
|
|
int32_t read_i32_le() { return static_cast<int32_t>(read_u32_le()); }
|
|
int64_t read_i64_le() { return static_cast<int64_t>(read_u64_le()); }
|
|
inline SpacetimeDB::i128 read_i128_le() {
|
|
uint64_t low = read_u64_le();
|
|
int64_t high = static_cast<int64_t>(read_u64_le());
|
|
return SpacetimeDB::i128(high, low);
|
|
}
|
|
inline SpacetimeDB::i256_placeholder read_i256_le() {
|
|
check_available(32);
|
|
SpacetimeDB::i256_placeholder val;
|
|
std::memcpy(val.data.data(), current_ptr, 32);
|
|
advance(32);
|
|
return val;
|
|
}
|
|
|
|
float read_f32_le() { return read_primitive_le<float>(); }
|
|
double read_f64_le() { return read_primitive_le<double>(); }
|
|
|
|
inline std::string read_string() {
|
|
uint32_t len = read_u32_le();
|
|
check_available(len);
|
|
std::string result(reinterpret_cast<const char*>(current_ptr), len);
|
|
advance(len);
|
|
return result;
|
|
}
|
|
inline std::vector<uint8_t> read_bytes() {
|
|
uint32_t len = read_u32_le();
|
|
check_available(len);
|
|
std::vector<uint8_t> result(current_ptr, current_ptr + len);
|
|
advance(len);
|
|
return result;
|
|
}
|
|
inline std::vector<uint8_t> read_fixed_bytes(size_t count) {
|
|
check_available(count);
|
|
std::vector<uint8_t> result(current_ptr, current_ptr + count);
|
|
advance(count);
|
|
return result;
|
|
}
|
|
|
|
template<typename T>
|
|
std::optional<T> read_optional() {
|
|
uint8_t tag = read_u8();
|
|
if (tag == 0) {
|
|
return std::nullopt;
|
|
} else if (tag == 1) {
|
|
return SpacetimeDB::bsatn::deserialize<T>(*this);
|
|
} else {
|
|
std::abort(); // Invalid optional tag in BSATN deserialization
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
std::vector<T> read_vector() {
|
|
uint32_t size = read_u32_le();
|
|
std::vector<T> result;
|
|
result.reserve(size);
|
|
for (uint32_t i = 0; i < size; ++i) {
|
|
result.push_back(SpacetimeDB::bsatn::deserialize<T>(*this));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
inline std::vector<uint8_t> read_vector_byte() {
|
|
return read_bytes();
|
|
}
|
|
|
|
// Deserialize a type using C++20 concepts for better error messages
|
|
template<typename T>
|
|
requires requires(Reader& r) { SpacetimeDB::bsatn::deserialize<T>(r); }
|
|
T deserialize_type() {
|
|
return SpacetimeDB::bsatn::deserialize<T>(*this);
|
|
}
|
|
|
|
inline bool is_eos() const {
|
|
return current_ptr >= end_ptr;
|
|
}
|
|
inline size_t remaining_bytes() const {
|
|
return (current_ptr <= end_ptr) ? (end_ptr - current_ptr) : 0;
|
|
}
|
|
|
|
private:
|
|
inline void check_available(size_t num_bytes) const {
|
|
if (current_ptr + num_bytes > end_ptr) {
|
|
std::abort(); // BSATN Reader: Not enough bytes remaining
|
|
}
|
|
}
|
|
inline void advance(size_t num_bytes) {
|
|
current_ptr += num_bytes;
|
|
}
|
|
|
|
const uint8_t* current_ptr;
|
|
const uint8_t* end_ptr;
|
|
};
|
|
|
|
// Type trait for deserializing types - primary template
|
|
template<typename T, typename = void>
|
|
struct deserializer {
|
|
static T deserialize(Reader& r) {
|
|
// Default: try bsatn_traits
|
|
return bsatn_traits<T>::deserialize(r);
|
|
}
|
|
};
|
|
|
|
// Specializations for primitive types
|
|
template<> struct deserializer<bool> {
|
|
static bool deserialize(Reader& r) { return r.read_bool(); }
|
|
};
|
|
template<> struct deserializer<uint8_t> {
|
|
static uint8_t deserialize(Reader& r) { return r.read_u8(); }
|
|
};
|
|
template<> struct deserializer<uint16_t> {
|
|
static uint16_t deserialize(Reader& r) { return r.read_u16_le(); }
|
|
};
|
|
template<> struct deserializer<uint32_t> {
|
|
static uint32_t deserialize(Reader& r) { return r.read_u32_le(); }
|
|
};
|
|
template<> struct deserializer<uint64_t> {
|
|
static uint64_t deserialize(Reader& r) { return r.read_u64_le(); }
|
|
};
|
|
template<> struct deserializer<int8_t> {
|
|
static int8_t deserialize(Reader& r) { return r.read_i8(); }
|
|
};
|
|
template<> struct deserializer<int16_t> {
|
|
static int16_t deserialize(Reader& r) { return r.read_i16_le(); }
|
|
};
|
|
template<> struct deserializer<int32_t> {
|
|
static int32_t deserialize(Reader& r) { return r.read_i32_le(); }
|
|
};
|
|
template<> struct deserializer<int64_t> {
|
|
static int64_t deserialize(Reader& r) { return r.read_i64_le(); }
|
|
};
|
|
template<> struct deserializer<float> {
|
|
static float deserialize(Reader& r) { return r.read_f32_le(); }
|
|
};
|
|
template<> struct deserializer<double> {
|
|
static double deserialize(Reader& r) { return r.read_f64_le(); }
|
|
};
|
|
template<> struct deserializer<std::string> {
|
|
static std::string deserialize(Reader& r) { return r.read_string(); }
|
|
};
|
|
template<> struct deserializer<std::vector<uint8_t>> {
|
|
static std::vector<uint8_t> deserialize(Reader& r) { return r.read_bytes(); }
|
|
};
|
|
|
|
// Specializations for container types
|
|
template<typename T>
|
|
struct deserializer<std::optional<T>> {
|
|
static std::optional<T> deserialize(Reader& r) {
|
|
return r.read_optional<T>();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct deserializer<std::vector<T>, std::enable_if_t<!std::is_same_v<T, uint8_t>>> {
|
|
static std::vector<T> deserialize(Reader& r) {
|
|
return r.read_vector<T>();
|
|
}
|
|
};
|
|
|
|
// Specializations for SpacetimeDB types
|
|
template<> struct deserializer<SpacetimeDB::Identity> {
|
|
static SpacetimeDB::Identity deserialize(Reader& r) {
|
|
SpacetimeDB::Identity id;
|
|
id.bsatn_deserialize(r);
|
|
return id;
|
|
}
|
|
};
|
|
template<> struct deserializer<SpacetimeDB::ConnectionId> {
|
|
static SpacetimeDB::ConnectionId deserialize(Reader& r) {
|
|
SpacetimeDB::ConnectionId conn;
|
|
conn.bsatn_deserialize(r);
|
|
return conn;
|
|
}
|
|
};
|
|
|
|
// Generic deserialize function - now much cleaner!
|
|
template<typename T>
|
|
inline T deserialize(Reader& r) {
|
|
return deserializer<T>::deserialize(r);
|
|
}
|
|
|
|
} // namespace SpacetimeDB::bsatn
|
|
|
|
#endif // SPACETIMEDB_BSATN_READER_H
|