#ifndef SPACETIMEDB_MODULE_IMPL_H #define SPACETIMEDB_MODULE_IMPL_H /** * SpacetimeDB C++ bindings - Module Implementation * * Core module system implementation with optimized type handling, * table registration, and reducer management. * * Architecture: * - Type traits for compile-time type detection * - BSATN serialization utilities * - Unified table and reducer registration * - V9 builder integration for constraints */ #include "Module.h" #include "field_registration.h" #include "../table_with_constraints.h" #include "../outcome.h" #include #include "bsatn_adapters.h" #include #include #include #include "autogen/Lifecycle.g.h" #include "v10_builder.h" #include #include #include #include #include #include #include namespace SpacetimeDB { namespace Internal { // ============================================================================= // TYPE TRAITS // ============================================================================= // Detect unit structs template struct is_unit_struct_type { private: template static auto test(int) -> decltype(U::__is_unit_type__, std::bool_constant{}); template static std::false_type test(...); public: static constexpr bool value = decltype(test(0))::value; }; template inline constexpr bool is_unit_struct_v = is_unit_struct_type::value; // Detect vector types template struct is_vector_type : std::false_type {}; template struct is_vector_type> : std::true_type {}; // Detect optional types template struct is_optional : std::false_type {}; template struct is_optional> : std::true_type {}; // Check for big integer types template constexpr bool is_big_integer_v = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; // Check if type needs registry registration template constexpr bool needs_type_registration_v = (!std::is_arithmetic_v && !std::is_same_v && !is_big_integer_v) || requires { bsatn::bsatn_traits::algebraic_type(); }; // Get BSATN tag for primitive types template struct type_id { static constexpr uint8_t value = []() { if constexpr (std::is_same_v) return 4; else if constexpr (std::is_same_v) return 5; else if constexpr (std::is_same_v) return 6; else if constexpr (std::is_same_v) return 7; else if constexpr (std::is_same_v) return 8; else if constexpr (std::is_same_v) return 9; else if constexpr (std::is_same_v) return 10; else if constexpr (std::is_same_v) return 11; else if constexpr (std::is_same_v) return 12; else if constexpr (std::is_same_v) return 17; else if constexpr (std::is_same_v) return 18; else if constexpr (std::is_same_v) return 19; else return 0; // Complex type }(); }; // Check for BSATN traits template struct has_bsatn_traits : std::false_type {}; template struct has_bsatn_traits::deserialize(std::declval()))>> : std::true_type {}; template constexpr bool has_bsatn_traits_v = has_bsatn_traits::value; // Check if type can be written inline template constexpr bool is_basic_inlineable_v = std::is_arithmetic_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; // ============================================================================= // BINARY I/O UTILITIES // ============================================================================= inline void write_u32(std::vector& buf, uint32_t val) { bsatn::Writer writer(buf); writer.write_u32_le(val); } inline void write_string(std::vector& buf, const std::string& str) { bsatn::Writer writer(buf); writer.write_string(str); } inline uint8_t read_u8(uint32_t source) { BytesSource src{source}; BytesSourceReader reader(src); return reader.read_u8(); } inline uint32_t read_u32(uint32_t source) { BytesSource src{source}; BytesSourceReader reader(src); return reader.read_u32_le(); } // ============================================================================= // TABLE REGISTRATION // ============================================================================= // Unified table registration - single implementation template void Module::RegisterTableInternalImpl(const char* name, bool is_public, bool is_event) { // V10 registration entrypoint. SetTableIsEventFlag(name, is_event); getV10Builder().RegisterTable(name, is_public, is_event); } // ============================================================================= // ARGUMENT DESERIALIZATION // ============================================================================= template T read_arg(uint32_t source) { BytesSource src{source}; if constexpr (is_unit_struct_v) { return T{}; } else if constexpr (!needs_type_registration_v) { BytesSourceReader reader(src); if constexpr (std::is_same_v) { return reader.read_bool(); } else if constexpr (std::is_same_v) { return reader.read_u8(); } else if constexpr (std::is_same_v) { return reader.read_u16_le(); } else if constexpr (std::is_same_v) { return reader.read_u32_le(); } else if constexpr (std::is_same_v) { return reader.read_u64_le(); } else if constexpr (std::is_same_v) { return reader.read_i8(); } else if constexpr (std::is_same_v) { return reader.read_i16_le(); } else if constexpr (std::is_same_v) { return reader.read_i32_le(); } else if constexpr (std::is_same_v) { return reader.read_i64_le(); } else if constexpr (std::is_same_v) { return reader.read_f32_le(); } else if constexpr (std::is_same_v) { return reader.read_f64_le(); } else if constexpr (std::is_same_v) { return reader.read_string(); } else { static_assert(!sizeof(T), "Unsupported primitive type"); } } else if constexpr (has_bsatn_traits_v) { std::vector buffer; constexpr size_t CHUNK_SIZE = 256; uint8_t chunk[CHUNK_SIZE]; while (true) { size_t requested = CHUNK_SIZE; size_t actual = requested; FFI::bytes_source_read(src, chunk, &actual); if (actual == 0) break; buffer.insert(buffer.end(), chunk, chunk + actual); if (actual < requested) break; } bsatn::Reader reader(buffer); return bsatn::bsatn_traits::deserialize(reader); } else { static_assert(std::is_default_constructible_v, "Type must be default constructible or have BSATN traits"); return T{}; } } // Read multiple arguments as tuple template auto read_args_tuple(uint32_t args_source) { BytesSource src{args_source}; // Handle single unit struct specially if constexpr (sizeof...(Args) == 1) { using FirstArg = std::tuple_element_t<0, std::tuple>; if constexpr (is_unit_struct_v) { return std::make_tuple(FirstArg{}); } } if constexpr ((has_bsatn_traits_v || ...)) { std::vector buffer; constexpr size_t CHUNK_SIZE = 256; uint8_t chunk[CHUNK_SIZE]; while (true) { size_t requested = CHUNK_SIZE; size_t actual = requested; FFI::bytes_source_read(src, chunk, &actual); if (actual == 0) break; buffer.insert(buffer.end(), chunk, chunk + actual); if (actual < requested) break; } bsatn::Reader reader(buffer); return std::make_tuple(bsatn::bsatn_traits::deserialize(reader)...); } else { BytesSourceReader reader(src); return std::make_tuple([&reader]() -> Args { if constexpr (std::is_same_v) { return reader.read_u32_le(); } else if constexpr (std::is_same_v) { return reader.read_i32_le(); } else if constexpr (std::is_same_v) { return reader.read_u64_le(); } else if constexpr (std::is_same_v) { return reader.read_i64_le(); } else if constexpr (std::is_same_v) { return reader.read_bool(); } else if constexpr (std::is_same_v) { return reader.read_string(); } else { return bsatn::bsatn_traits::deserialize(reader); } }()...); } } // ============================================================================= // REDUCER WRAPPERS // ============================================================================= // Lifecycle reducer wrapper template void builtin_reducer_wrapper(Func func, ReducerContext& ctx, uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3) { std::array senderBytes{}; memcpy(senderBytes.data(), &sender_0, sizeof(uint64_t)); memcpy(senderBytes.data() + 8, &sender_1, sizeof(uint64_t)); memcpy(senderBytes.data() + 16, &sender_2, sizeof(uint64_t)); memcpy(senderBytes.data() + 24, &sender_3, sizeof(uint64_t)); Identity sender(senderBytes); if constexpr (std::is_invocable_v) { func(ctx); } else if constexpr (std::is_invocable_v) { func(ctx, sender); } } // Generic reducer wrapper template void spacetimedb_reducer_wrapper(void (*func)(ReducerContext, Args...), ReducerContext& ctx, uint32_t args_source) { if constexpr (sizeof...(Args) == 0) { func(ctx); } else { auto args_tuple = read_args_tuple(args_source); std::apply([&](auto&&... args) { func(ctx, std::forward(args)...); }, args_tuple); } } // ============================================================================= // TYPE SERIALIZATION // ============================================================================= // Write algebraic type inline (for special types and primitives) template void write_algebraic_type_inline(std::vector& buf) { if constexpr (is_optional::value) { using inner_type = typename T::value_type; buf.push_back(1); // AlgebraicType::Sum write_u32(buf, 2); // 2 variants buf.push_back(0); // Has name write_string(buf, "some"); write_algebraic_type_inline(buf); buf.push_back(0); // Has name write_string(buf, "none"); buf.push_back(2); // AlgebraicType::Product (empty) write_u32(buf, 0); // 0 elements } else if constexpr (is_vector_type::value) { using element_type = typename T::value_type; buf.push_back(3); // AlgebraicType::Array write_algebraic_type_inline(buf); } else if constexpr (std::is_same_v) { buf.push_back(10); } else if constexpr (std::is_same_v) { buf.push_back(11); } else if constexpr (std::is_same_v) { buf.push_back(4); } else if constexpr (std::is_same_v) { buf.push_back(5); } else if constexpr (std::is_same_v) { buf.push_back(6); } else if constexpr (std::is_same_v) { buf.push_back(7); } else if constexpr (std::is_same_v) { buf.push_back(8); } else if constexpr (std::is_same_v) { buf.push_back(9); } else if constexpr (std::is_same_v) { buf.push_back(12); } else if constexpr (std::is_same_v) { buf.push_back(13); } else if constexpr (std::is_same_v) { buf.push_back(18); } else if constexpr (std::is_same_v) { buf.push_back(19); } else if constexpr (std::is_same_v) { buf.push_back(15); } else if constexpr (std::is_same_v) { buf.push_back(17); } else if constexpr (std::is_same_v) { buf.push_back(14); } else if constexpr (std::is_same_v) { buf.push_back(16); } else if constexpr (std::is_same_v) { buf.push_back(2); // AlgebraicType::Product write_u32(buf, 1); // 1 field buf.push_back(0); // Some (has name) write_string(buf, bsatn::IDENTITY_TAG); buf.push_back(17); // AlgebraicType::U256 } else if constexpr (std::is_same_v) { buf.push_back(2); // AlgebraicType::Product write_u32(buf, 1); // 1 field buf.push_back(0); // Some (has name) write_string(buf, bsatn::CONNECTION_ID_TAG); buf.push_back(15); // AlgebraicType::U128 } else if constexpr (std::is_same_v) { buf.push_back(2); // AlgebraicType::Product write_u32(buf, 1); // 1 field buf.push_back(0); // Some (has name) write_string(buf, bsatn::TIMESTAMP_TAG); buf.push_back(12); // AlgebraicType::I64 } else if constexpr (std::is_same_v) { buf.push_back(2); // AlgebraicType::Product write_u32(buf, 1); // 1 field buf.push_back(0); // Some (has name) write_string(buf, bsatn::TIME_DURATION_TAG); buf.push_back(12); // AlgebraicType::I64 } else if constexpr (std::is_enum_v) { buf.push_back(4); // String fallback for complex enums } else { buf.push_back(4); // AlgebraicType::String fallback } } // ============================================================================= // REDUCER REGISTRATION // ============================================================================= inline std::optional get_lifecycle_for_name(const std::string& name) { if (name == "init") return Lifecycle::Init; if (name == "client_connected") return Lifecycle::OnConnect; if (name == "client_disconnected") return Lifecycle::OnDisconnect; return std::nullopt; } template void Module::RegisterReducerInternalImpl(const std::string& name, Func func) { auto lifecycle = get_lifecycle_for_name(name); if (lifecycle.has_value()) { getV10Builder().RegisterLifecycleReducer(name, func, lifecycle.value()); } else { getV10Builder().RegisterReducer(name, func, std::vector{}); } } template void Module::RegisterReducerInternalWithNames(const std::string& name, Func func, const std::vector& param_names) { auto lifecycle = get_lifecycle_for_name(name); if (lifecycle.has_value()) { getV10Builder().RegisterLifecycleReducer(name, func, lifecycle.value()); } else { getV10Builder().RegisterReducer(name, func, param_names); } } // ============================================================================= // HELPER FUNCTIONS // ============================================================================= template void register_table_type(const char* name, bool is_public) { Module::RegisterTableInternalImpl(name, is_public); } // Optimized parameter name parsing inline std::vector parse_parameter_names(const std::string& params_str) { std::vector names; size_t pos = 0; bool first_param = true; while (pos < params_str.length()) { size_t comma_pos = params_str.find(',', pos); if (comma_pos == std::string::npos) comma_pos = params_str.length(); std::string param = params_str.substr(pos, comma_pos - pos); // Skip ReducerContext if (first_param) { first_param = false; pos = comma_pos + 1; continue; } // Trim whitespace size_t start = param.find_first_not_of(" \t"); if (start != std::string::npos) { param = param.substr(start); size_t end = param.find_last_not_of(" \t"); if (end != std::string::npos) { param = param.substr(0, end + 1); } } // Extract parameter name size_t last_space = param.find_last_of(" \t"); if (last_space != std::string::npos) { std::string param_name = param.substr(last_space + 1); size_t name_end = param_name.find_first_of("&*[]"); if (name_end != std::string::npos) { param_name = param_name.substr(0, name_end); } names.push_back(std::move(param_name)); } pos = comma_pos + 1; } return names; } } // namespace Internal } // namespace SpacetimeDB #endif // SPACETIMEDB_MODULE_IMPL_H