| cmake_minimum_required(VERSION 3.10) | ||
| project(obj2buf_cpp_example) | ||
| # Set C++ standard | ||
| set(CMAKE_CXX_STANDARD 11) | ||
| set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
| # Find nlohmann/json | ||
| find_package(nlohmann_json QUIET) | ||
| if(nlohmann_json_FOUND) | ||
| message(STATUS "Found nlohmann/json via find_package") | ||
| else() | ||
| # Try to find it manually or download it | ||
| find_path(NLOHMANN_JSON_INCLUDE_DIR | ||
| NAMES nlohmann/json.hpp | ||
| PATHS | ||
| /usr/include | ||
| /usr/local/include | ||
| /opt/homebrew/include | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/third_party | ||
| ) | ||
| if(NLOHMANN_JSON_INCLUDE_DIR) | ||
| message(STATUS "Found nlohmann/json at ${NLOHMANN_JSON_INCLUDE_DIR}") | ||
| add_library(nlohmann_json INTERFACE) | ||
| target_include_directories(nlohmann_json INTERFACE ${NLOHMANN_JSON_INCLUDE_DIR}) | ||
| else() | ||
| message(STATUS "nlohmann/json not found locally, downloading...") | ||
| include(FetchContent) | ||
| FetchContent_Declare( | ||
| nlohmann_json | ||
| URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz | ||
| URL_HASH SHA256=d69f9deb6a75e2580465c6c4c5111b89c4dc2fa94e3a85fcd2ffcd9a143d9273 | ||
| ) | ||
| FetchContent_MakeAvailable(nlohmann_json) | ||
| endif() | ||
| endif() | ||
| # Create example executable | ||
| add_executable(obj2buf_example example.cpp) | ||
| target_link_libraries(obj2buf_example nlohmann_json::nlohmann_json) | ||
| # Include current directory for obj2buf.hpp | ||
| target_include_directories(obj2buf_example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
| # Compiler-specific options | ||
| if(MSVC) | ||
| target_compile_options(obj2buf_example PRIVATE /W4) | ||
| else() | ||
| target_compile_options(obj2buf_example PRIVATE -Wall -Wextra -Wpedantic) | ||
| endif() | ||
| # Optional: Create a test target if you have tests | ||
| # add_executable(obj2buf_tests tests.cpp) | ||
| # target_link_libraries(obj2buf_tests nlohmann_json::nlohmann_json) | ||
| # target_include_directories(obj2buf_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
| message(STATUS "") | ||
| message(STATUS "obj2buf C++ bindings configured!") | ||
| message(STATUS "Build the example with: cmake --build . --target obj2buf_example") | ||
| message(STATUS "") |
| #include "obj2buf.hpp" | ||
| #include <iostream> | ||
| #include <vector> | ||
| #include <iomanip> | ||
| int main() { | ||
| std::cout << "obj2buf C++ Bindings Example (v" << obj2buf::version() << ")\n"; | ||
| std::cout << "================================================\n\n"; | ||
| try { | ||
| // Example: User profile schema that matches JavaScript side | ||
| // This matches the schema.to_json() output from JavaScript | ||
| nlohmann::json schema_def = { | ||
| {"type", "Schema"}, | ||
| {"root_type", { | ||
| {"type", "MapType"}, | ||
| {"field_pairs", nlohmann::json::array({ | ||
| nlohmann::json::array({"id", nlohmann::json{{"type", "UInt32"}}}), | ||
| nlohmann::json::array({"username", nlohmann::json{{"type", "VarStringType"}, {"max_length", 50}}}), | ||
| nlohmann::json::array({"email", nlohmann::json{{"type", "VarStringType"}, {"max_length", 100}}}), | ||
| nlohmann::json::array({"age", nlohmann::json{{"type", "UInt8"}}}), | ||
| nlohmann::json::array({"is_active", nlohmann::json{{"type", "BooleanType"}}}), | ||
| nlohmann::json::array({"score", nlohmann::json{{"type", "OptionalType"}, {"base_type", nlohmann::json{{"type", "Float32"}}}}}) | ||
| })} | ||
| }} | ||
| }; | ||
| // Create schema | ||
| obj2buf::Schema schema(schema_def["root_type"]); | ||
| // Example binary data generated by JavaScript obj2buf | ||
| // Represents: {id: 12345, username: "alice", email: "alice@example.com", age: 25, is_active: true, score: 95.5} | ||
| std::vector<uint8_t> buffer = { | ||
| 0x39, 0x30, 0x00, 0x00, 0x05, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x11, 0x61, 0x6c, 0x69, 0x63, 0x65, | ||
| 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x19, 0x01, 0x01, 0x00, | ||
| 0x00, 0xbf, 0x42 | ||
| }; | ||
| // Deserialize | ||
| nlohmann::json result = schema.deserialize(buffer); | ||
| // Display results | ||
| std::cout << "Deserialized user data:\n"; | ||
| std::cout << " ID: " << result["id"] << "\n"; | ||
| std::cout << " Username: " << result["username"] << "\n"; | ||
| std::cout << " Email: " << result["email"] << "\n"; | ||
| std::cout << " Age: " << result["age"] << "\n"; | ||
| std::cout << " Active: " << (result["is_active"].get<bool>() ? "true" : "false") << "\n"; | ||
| if (!result["score"].is_null()) { | ||
| std::cout << " Score: " << std::fixed << std::setprecision(1) | ||
| << result["score"] << "\n"; | ||
| } else { | ||
| std::cout << " Score: (not set)\n"; | ||
| } | ||
| // You can also access individual fields with type safety | ||
| uint32_t user_id = result["id"]; | ||
| std::string username = result["username"]; | ||
| uint32_t age = result["age"]; // Changed from uint8_t to uint32_t for JSON compatibility | ||
| bool is_active = result["is_active"]; | ||
| std::cout << "\nType-safe access:\n"; | ||
| std::cout << " User " << username << " (ID: " << user_id << ") is " | ||
| << age << " years old and " | ||
| << (is_active ? "active" : "inactive") << "\n"; | ||
| } catch (const obj2buf::parser_error& e) { | ||
| std::cerr << "Parse error: " << e.what() << std::endl; | ||
| return 1; | ||
| } catch (const std::exception& e) { | ||
| std::cerr << "Error: " << e.what() << std::endl; | ||
| return 1; | ||
| } | ||
| return 0; | ||
| } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
| #pragma once | ||
| /* | ||
| * obj2buf C++ Bindings v1.0.0 | ||
| * | ||
| * Header-only C++11 library for deserializing binary data created by obj2buf JavaScript library. | ||
| * Compatible with obj2buf JavaScript library v1.0.0+ | ||
| * | ||
| * GitHub: https://github.com/Nova-Dynamics/obj2buf-node | ||
| * License: ISC | ||
| */ | ||
| #define OBJ2BUF_VERSION_MAJOR 1 | ||
| #define OBJ2BUF_VERSION_MINOR 0 | ||
| #define OBJ2BUF_VERSION_PATCH 0 | ||
| #define OBJ2BUF_VERSION "1.0.0" | ||
| #include <nlohmann/json.hpp> | ||
| #include <string> | ||
| #include <vector> | ||
| #include <cstring> | ||
| #include <stdexcept> | ||
| #include <memory> | ||
| #include <map> | ||
| namespace obj2buf { | ||
| using json = nlohmann::json; | ||
| // Version information | ||
| inline const char* version() { | ||
| return OBJ2BUF_VERSION; | ||
| } | ||
| inline int version_major() { | ||
| return OBJ2BUF_VERSION_MAJOR; | ||
| } | ||
| inline int version_minor() { | ||
| return OBJ2BUF_VERSION_MINOR; | ||
| } | ||
| inline int version_patch() { | ||
| return OBJ2BUF_VERSION_PATCH; | ||
| } | ||
| // Exception class for parsing errors | ||
| class parser_error : public std::runtime_error { | ||
| public: | ||
| explicit parser_error(const std::string& message) : std::runtime_error(message) {} | ||
| }; | ||
| // Forward declarations | ||
| class Type; | ||
| class Schema; | ||
| // Base Type interface | ||
| class Type { | ||
| public: | ||
| virtual ~Type() = default; | ||
| virtual json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const = 0; | ||
| virtual size_t get_byte_length() const = 0; // Returns 0 for variable-length types | ||
| virtual bool is_static_length() const = 0; | ||
| virtual size_t calculate_byte_length(const json& value) const = 0; | ||
| static std::unique_ptr<Type> from_json(const json& type_def); | ||
| }; | ||
| // Primitive Types | ||
| class UInt8 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset >= buffer_size) { | ||
| throw parser_error("Buffer underflow reading UInt8"); | ||
| } | ||
| uint8_t value = buffer[offset++]; | ||
| return json(static_cast<uint32_t>(value)); | ||
| } | ||
| size_t get_byte_length() const override { return 1; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 1; } | ||
| }; | ||
| class UInt16 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + 2 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading UInt16"); | ||
| } | ||
| uint16_t value = buffer[offset] | (buffer[offset + 1] << 8); | ||
| offset += 2; | ||
| return json(static_cast<uint32_t>(value)); | ||
| } | ||
| size_t get_byte_length() const override { return 2; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 2; } | ||
| }; | ||
| class UInt32 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + 4 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading UInt32"); | ||
| } | ||
| uint32_t value = buffer[offset] | (buffer[offset + 1] << 8) | | ||
| (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24); | ||
| offset += 4; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return 4; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 4; } | ||
| }; | ||
| class Int8 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset >= buffer_size) { | ||
| throw parser_error("Buffer underflow reading Int8"); | ||
| } | ||
| int8_t value = static_cast<int8_t>(buffer[offset++]); | ||
| return json(static_cast<int32_t>(value)); | ||
| } | ||
| size_t get_byte_length() const override { return 1; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 1; } | ||
| }; | ||
| class Int16 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + 2 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading Int16"); | ||
| } | ||
| int16_t value = static_cast<int16_t>(buffer[offset] | (buffer[offset + 1] << 8)); | ||
| offset += 2; | ||
| return json(static_cast<int32_t>(value)); | ||
| } | ||
| size_t get_byte_length() const override { return 2; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 2; } | ||
| }; | ||
| class Int32 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + 4 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading Int32"); | ||
| } | ||
| int32_t value = static_cast<int32_t>(buffer[offset] | (buffer[offset + 1] << 8) | | ||
| (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); | ||
| offset += 4; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return 4; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 4; } | ||
| }; | ||
| class Float32 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + 4 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading Float32"); | ||
| } | ||
| uint32_t bits = buffer[offset] | (buffer[offset + 1] << 8) | | ||
| (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24); | ||
| float value; | ||
| std::memcpy(&value, &bits, sizeof(float)); | ||
| offset += 4; | ||
| return json(static_cast<double>(value)); | ||
| } | ||
| size_t get_byte_length() const override { return 4; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 4; } | ||
| }; | ||
| class Float64 : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + 8 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading Float64"); | ||
| } | ||
| uint64_t bits = static_cast<uint64_t>(buffer[offset]) | | ||
| (static_cast<uint64_t>(buffer[offset + 1]) << 8) | | ||
| (static_cast<uint64_t>(buffer[offset + 2]) << 16) | | ||
| (static_cast<uint64_t>(buffer[offset + 3]) << 24) | | ||
| (static_cast<uint64_t>(buffer[offset + 4]) << 32) | | ||
| (static_cast<uint64_t>(buffer[offset + 5]) << 40) | | ||
| (static_cast<uint64_t>(buffer[offset + 6]) << 48) | | ||
| (static_cast<uint64_t>(buffer[offset + 7]) << 56); | ||
| double value; | ||
| std::memcpy(&value, &bits, sizeof(double)); | ||
| offset += 8; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return 8; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 8; } | ||
| }; | ||
| // Unified Types | ||
| class UInt : public Type { | ||
| private: | ||
| size_t bytes_; | ||
| public: | ||
| explicit UInt(size_t bytes) : bytes_(bytes) { | ||
| if (bytes != 1 && bytes != 2 && bytes != 4) { | ||
| throw parser_error("UInt only supports 1, 2, or 4 bytes"); | ||
| } | ||
| } | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + bytes_ > buffer_size) { | ||
| throw parser_error("Buffer underflow reading UInt(" + std::to_string(bytes_) + ")"); | ||
| } | ||
| uint32_t value = 0; | ||
| for (size_t i = 0; i < bytes_; ++i) { | ||
| value |= (static_cast<uint32_t>(buffer[offset + i]) << (i * 8)); | ||
| } | ||
| offset += bytes_; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return bytes_; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return bytes_; } | ||
| }; | ||
| class Int : public Type { | ||
| private: | ||
| size_t bytes_; | ||
| public: | ||
| explicit Int(size_t bytes) : bytes_(bytes) { | ||
| if (bytes != 1 && bytes != 2 && bytes != 4) { | ||
| throw parser_error("Int only supports 1, 2, or 4 bytes"); | ||
| } | ||
| } | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + bytes_ > buffer_size) { | ||
| throw parser_error("Buffer underflow reading Int(" + std::to_string(bytes_) + ")"); | ||
| } | ||
| int32_t value = 0; | ||
| for (size_t i = 0; i < bytes_; ++i) { | ||
| value |= (static_cast<int32_t>(buffer[offset + i]) << (i * 8)); | ||
| } | ||
| // Sign extend for smaller types | ||
| if (bytes_ == 1 && (value & 0x80)) { | ||
| value |= 0xFFFFFF00; | ||
| } else if (bytes_ == 2 && (value & 0x8000)) { | ||
| value |= 0xFFFF0000; | ||
| } | ||
| offset += bytes_; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return bytes_; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return bytes_; } | ||
| }; | ||
| class Float : public Type { | ||
| private: | ||
| size_t bytes_; | ||
| public: | ||
| explicit Float(size_t bytes) : bytes_(bytes) { | ||
| if (bytes != 4 && bytes != 8) { | ||
| throw parser_error("Float only supports 4 or 8 bytes"); | ||
| } | ||
| } | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + bytes_ > buffer_size) { | ||
| throw parser_error("Buffer underflow reading Float(" + std::to_string(bytes_) + ")"); | ||
| } | ||
| if (bytes_ == 4) { | ||
| uint32_t bits = buffer[offset] | (buffer[offset + 1] << 8) | | ||
| (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24); | ||
| float value; | ||
| std::memcpy(&value, &bits, sizeof(float)); | ||
| offset += 4; | ||
| return json(static_cast<double>(value)); | ||
| } else { // bytes_ == 8 | ||
| uint64_t bits = static_cast<uint64_t>(buffer[offset]) | | ||
| (static_cast<uint64_t>(buffer[offset + 1]) << 8) | | ||
| (static_cast<uint64_t>(buffer[offset + 2]) << 16) | | ||
| (static_cast<uint64_t>(buffer[offset + 3]) << 24) | | ||
| (static_cast<uint64_t>(buffer[offset + 4]) << 32) | | ||
| (static_cast<uint64_t>(buffer[offset + 5]) << 40) | | ||
| (static_cast<uint64_t>(buffer[offset + 6]) << 48) | | ||
| (static_cast<uint64_t>(buffer[offset + 7]) << 56); | ||
| double value; | ||
| std::memcpy(&value, &bits, sizeof(double)); | ||
| offset += 8; | ||
| return json(value); | ||
| } | ||
| } | ||
| size_t get_byte_length() const override { return bytes_; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return bytes_; } | ||
| }; | ||
| class BooleanType : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset >= buffer_size) { | ||
| throw parser_error("Buffer underflow reading BooleanType"); | ||
| } | ||
| bool value = buffer[offset++] != 0; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return 1; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 1; } | ||
| }; | ||
| class Char : public Type { | ||
| public: | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset >= buffer_size) { | ||
| throw parser_error("Buffer underflow reading Char"); | ||
| } | ||
| char value = static_cast<char>(buffer[offset++]); | ||
| return json(std::string(1, value)); | ||
| } | ||
| size_t get_byte_length() const override { return 1; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return 1; } | ||
| }; | ||
| class FixedStringType : public Type { | ||
| private: | ||
| size_t length_; | ||
| public: | ||
| explicit FixedStringType(size_t length) : length_(length) {} | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + length_ > buffer_size) { | ||
| throw parser_error("Buffer underflow reading FixedStringType"); | ||
| } | ||
| // Find null terminator or use full length | ||
| size_t str_len = 0; | ||
| for (size_t i = 0; i < length_; ++i) { | ||
| if (buffer[offset + i] == 0) { | ||
| str_len = i; | ||
| break; | ||
| } | ||
| str_len = i + 1; | ||
| } | ||
| std::string value(reinterpret_cast<const char*>(buffer + offset), str_len); | ||
| offset += length_; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return length_; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return length_; } | ||
| }; | ||
| class VarStringType : public Type { | ||
| private: | ||
| size_t max_length_; | ||
| public: | ||
| explicit VarStringType(size_t max_length = 65535) | ||
| : max_length_(max_length) {} | ||
| // Returns header size (1 or 2 bytes) based on max_length | ||
| size_t header_size() const { | ||
| return max_length_ >= 256 ? 2 : 1; | ||
| } | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| size_t str_length; | ||
| size_t hdr_size = header_size(); | ||
| if (hdr_size == 2) { | ||
| if (offset + 2 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading VarStringType header (2-byte)"); | ||
| } | ||
| str_length = buffer[offset] | (buffer[offset + 1] << 8); | ||
| offset += 2; | ||
| } else { | ||
| if (offset >= buffer_size) { | ||
| throw parser_error("Buffer underflow reading VarStringType header (1-byte)"); | ||
| } | ||
| str_length = buffer[offset++]; | ||
| } | ||
| if (str_length > max_length_) { | ||
| throw parser_error("VarStringType string length " + std::to_string(str_length) + " exceeds max_length " + std::to_string(max_length_)); | ||
| } | ||
| if (offset + str_length > buffer_size) { | ||
| throw parser_error("Buffer underflow reading VarStringType data"); | ||
| } | ||
| std::string value(reinterpret_cast<const char*>(buffer + offset), str_length); | ||
| offset += str_length; | ||
| return json(value); | ||
| } | ||
| size_t get_byte_length() const override { return 0; } // Variable length | ||
| bool is_static_length() const override { return false; } | ||
| size_t calculate_byte_length(const json& value) const override { | ||
| size_t str_len = value.get<std::string>().length(); | ||
| return header_size() + str_len; | ||
| } | ||
| }; | ||
| class ArrayType : public Type { | ||
| private: | ||
| std::unique_ptr<Type> element_type_; | ||
| size_t fixed_length_; | ||
| bool is_variable_length_; | ||
| public: | ||
| ArrayType(std::unique_ptr<Type> element_type, size_t length = 0) | ||
| : element_type_(std::move(element_type)), fixed_length_(length), | ||
| is_variable_length_(length == 0) {} | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| size_t array_length; | ||
| if (is_variable_length_) { | ||
| // Read length header (2 bytes for variable arrays) | ||
| if (offset + 2 > buffer_size) { | ||
| throw parser_error("Buffer underflow reading ArrayType length"); | ||
| } | ||
| array_length = buffer[offset] | (buffer[offset + 1] << 8); | ||
| offset += 2; | ||
| } else { | ||
| array_length = fixed_length_; | ||
| } | ||
| json result = json::array(); | ||
| for (size_t i = 0; i < array_length; ++i) { | ||
| result.push_back(element_type_->deserialize(buffer, offset, buffer_size)); | ||
| } | ||
| return result; | ||
| } | ||
| size_t get_byte_length() const override { | ||
| if (is_variable_length_ || !element_type_->is_static_length()) { | ||
| return 0; // Variable length | ||
| } | ||
| return fixed_length_ * element_type_->get_byte_length(); | ||
| } | ||
| bool is_static_length() const override { | ||
| return !is_variable_length_ && element_type_->is_static_length(); | ||
| } | ||
| size_t calculate_byte_length(const json& value) const override { | ||
| size_t total = is_variable_length_ ? 2 : 0; // Length header for variable arrays | ||
| for (const auto& item : value) { | ||
| total += element_type_->calculate_byte_length(item); | ||
| } | ||
| return total; | ||
| } | ||
| }; | ||
| class TupleType : public Type { | ||
| private: | ||
| std::vector<std::unique_ptr<Type>> element_types_; | ||
| public: | ||
| explicit TupleType(std::vector<std::unique_ptr<Type>> element_types) | ||
| : element_types_(std::move(element_types)) {} | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| json result = json::array(); | ||
| for (const auto& element_type : element_types_) { | ||
| result.push_back(element_type->deserialize(buffer, offset, buffer_size)); | ||
| } | ||
| return result; | ||
| } | ||
| size_t get_byte_length() const override { | ||
| size_t total = 0; | ||
| for (const auto& element_type : element_types_) { | ||
| if (!element_type->is_static_length()) { | ||
| return 0; // Variable length | ||
| } | ||
| total += element_type->get_byte_length(); | ||
| } | ||
| return total; | ||
| } | ||
| bool is_static_length() const override { | ||
| for (const auto& element_type : element_types_) { | ||
| if (!element_type->is_static_length()) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| size_t calculate_byte_length(const json& value) const override { | ||
| size_t total = 0; | ||
| for (size_t i = 0; i < element_types_.size(); ++i) { | ||
| total += element_types_[i]->calculate_byte_length(value[i]); | ||
| } | ||
| return total; | ||
| } | ||
| }; | ||
| class MapType : public Type { | ||
| private: | ||
| std::vector<std::pair<std::string, std::unique_ptr<Type>>> fields_; | ||
| public: | ||
| explicit MapType(std::vector<std::pair<std::string, std::unique_ptr<Type>>> fields) | ||
| : fields_(std::move(fields)) {} | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| json result = json::object(); | ||
| for (const auto& field : fields_) { | ||
| result[field.first] = field.second->deserialize(buffer, offset, buffer_size); | ||
| } | ||
| return result; | ||
| } | ||
| size_t get_byte_length() const override { | ||
| size_t total = 0; | ||
| for (const auto& field : fields_) { | ||
| if (!field.second->is_static_length()) { | ||
| return 0; // Variable length | ||
| } | ||
| total += field.second->get_byte_length(); | ||
| } | ||
| return total; | ||
| } | ||
| bool is_static_length() const override { | ||
| for (const auto& field : fields_) { | ||
| if (!field.second->is_static_length()) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| size_t calculate_byte_length(const json& value) const override { | ||
| size_t total = 0; | ||
| for (const auto& field : fields_) { | ||
| total += field.second->calculate_byte_length(value[field.first]); | ||
| } | ||
| return total; | ||
| } | ||
| }; | ||
| class EnumType : public Type { | ||
| private: | ||
| std::vector<std::string> options_; | ||
| size_t value_size_; | ||
| public: | ||
| explicit EnumType(std::vector<std::string> options) : options_(std::move(options)) { | ||
| if (options_.size() <= 256) { | ||
| value_size_ = 1; | ||
| } else if (options_.size() <= 65536) { | ||
| value_size_ = 2; | ||
| } else { | ||
| value_size_ = 4; | ||
| } | ||
| } | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset + value_size_ > buffer_size) { | ||
| throw parser_error("Buffer underflow reading EnumType"); | ||
| } | ||
| size_t index = 0; | ||
| for (size_t i = 0; i < value_size_; ++i) { | ||
| index |= (static_cast<size_t>(buffer[offset + i]) << (i * 8)); | ||
| } | ||
| offset += value_size_; | ||
| if (index >= options_.size()) { | ||
| throw parser_error("Invalid enum index: " + std::to_string(index)); | ||
| } | ||
| return json(options_[index]); | ||
| } | ||
| size_t get_byte_length() const override { return value_size_; } | ||
| bool is_static_length() const override { return true; } | ||
| size_t calculate_byte_length(const json&) const override { return value_size_; } | ||
| }; | ||
| class OptionalType : public Type { | ||
| private: | ||
| std::unique_ptr<Type> base_type_; | ||
| public: | ||
| explicit OptionalType(std::unique_ptr<Type> base_type) | ||
| : base_type_(std::move(base_type)) {} | ||
| json deserialize(const uint8_t* buffer, size_t& offset, size_t buffer_size) const override { | ||
| if (offset >= buffer_size) { | ||
| throw parser_error("Buffer underflow reading OptionalType presence flag"); | ||
| } | ||
| bool is_present = buffer[offset++] != 0; | ||
| if (is_present) { | ||
| return base_type_->deserialize(buffer, offset, buffer_size); | ||
| } else { | ||
| return json(nullptr); | ||
| } | ||
| } | ||
| size_t get_byte_length() const override { | ||
| if (!base_type_->is_static_length()) { | ||
| return 0; // Variable length | ||
| } | ||
| return 1 + base_type_->get_byte_length(); | ||
| } | ||
| bool is_static_length() const override { return base_type_->is_static_length(); } | ||
| size_t calculate_byte_length(const json& value) const override { | ||
| if (value.is_null()) { | ||
| return 1; // Just the presence flag | ||
| } | ||
| return 1 + base_type_->calculate_byte_length(value); | ||
| } | ||
| }; | ||
| // Schema class | ||
| class Schema { | ||
| private: | ||
| std::unique_ptr<Type> type_; | ||
| public: | ||
| explicit Schema(const json& schema_json) { | ||
| type_ = Type::from_json(schema_json); | ||
| } | ||
| explicit Schema(std::unique_ptr<Type> type) : type_(std::move(type)) {} | ||
| json deserialize(const std::vector<uint8_t>& buffer, size_t offset = 0) const { | ||
| size_t pos = offset; | ||
| return type_->deserialize(buffer.data(), pos, buffer.size()); | ||
| } | ||
| json deserialize(const uint8_t* buffer, size_t buffer_size, size_t offset = 0) const { | ||
| size_t pos = offset; | ||
| return type_->deserialize(buffer, pos, buffer_size); | ||
| } | ||
| size_t get_byte_length() const { | ||
| return type_->get_byte_length(); | ||
| } | ||
| bool is_static_length() const { | ||
| return type_->is_static_length(); | ||
| } | ||
| size_t calculate_byte_length(const json& value) const { | ||
| return type_->calculate_byte_length(value); | ||
| } | ||
| }; | ||
| // Type factory implementation | ||
| std::unique_ptr<Type> Type::from_json(const json& type_def) { | ||
| std::string type_name = type_def["type"]; | ||
| if (type_name == "UInt8") { | ||
| return std::unique_ptr<UInt8>(new UInt8()); | ||
| } else if (type_name == "UInt16") { | ||
| return std::unique_ptr<UInt16>(new UInt16()); | ||
| } else if (type_name == "UInt32") { | ||
| return std::unique_ptr<UInt32>(new UInt32()); | ||
| } else if (type_name == "Int8") { | ||
| return std::unique_ptr<Int8>(new Int8()); | ||
| } else if (type_name == "Int16") { | ||
| return std::unique_ptr<Int16>(new Int16()); | ||
| } else if (type_name == "Int32") { | ||
| return std::unique_ptr<Int32>(new Int32()); | ||
| } else if (type_name == "Float32") { | ||
| return std::unique_ptr<Float32>(new Float32()); | ||
| } else if (type_name == "Float64") { | ||
| return std::unique_ptr<Float64>(new Float64()); | ||
| } else if (type_name == "UInt") { | ||
| size_t bytes = type_def["byte_length"]; | ||
| return std::unique_ptr<UInt>(new UInt(bytes)); | ||
| } else if (type_name == "Int") { | ||
| size_t bytes = type_def["byte_length"]; | ||
| return std::unique_ptr<Int>(new Int(bytes)); | ||
| } else if (type_name == "Float") { | ||
| size_t bytes = type_def["byte_length"]; | ||
| return std::unique_ptr<Float>(new Float(bytes)); | ||
| } else if (type_name == "BooleanType") { | ||
| return std::unique_ptr<BooleanType>(new BooleanType()); | ||
| } else if (type_name == "Char") { | ||
| return std::unique_ptr<Char>(new Char()); | ||
| } else if (type_name == "FixedStringType") { | ||
| size_t length = type_def["length"]; | ||
| return std::unique_ptr<FixedStringType>(new FixedStringType(length)); | ||
| } else if (type_name == "VarStringType") { | ||
| size_t max_length = type_def.value("max_length", 65535); | ||
| return std::unique_ptr<VarStringType>(new VarStringType(max_length)); | ||
| } else if (type_name == "ArrayType") { | ||
| auto element_type = from_json(type_def["element_type"]); | ||
| size_t length = type_def.value("length", 0); | ||
| return std::unique_ptr<ArrayType>(new ArrayType(std::move(element_type), length)); | ||
| } else if (type_name == "TupleType") { | ||
| std::vector<std::unique_ptr<Type>> element_types; | ||
| for (const auto& elem_def : type_def["element_types"]) { | ||
| element_types.push_back(from_json(elem_def)); | ||
| } | ||
| return std::unique_ptr<TupleType>(new TupleType(std::move(element_types))); | ||
| } else if (type_name == "MapType") { | ||
| std::vector<std::pair<std::string, std::unique_ptr<Type>>> fields; | ||
| for (const auto& field_def : type_def["field_pairs"]) { | ||
| std::string field_name = field_def[0]; | ||
| auto field_type = from_json(field_def[1]); | ||
| fields.emplace_back(field_name, std::move(field_type)); | ||
| } | ||
| return std::unique_ptr<MapType>(new MapType(std::move(fields))); | ||
| } else if (type_name == "EnumType") { | ||
| std::vector<std::string> options = type_def["options"]; | ||
| return std::unique_ptr<EnumType>(new EnumType(std::move(options))); | ||
| } else if (type_name == "OptionalType") { | ||
| auto base_type = from_json(type_def["base_type"]); | ||
| return std::unique_ptr<OptionalType>(new OptionalType(std::move(base_type))); | ||
| } else { | ||
| throw parser_error("Unknown type: " + type_name); | ||
| } | ||
| } | ||
| } // namespace obj2buf |
| # obj2buf C++ Bindings v1.0.0 | ||
| C++11 header-only library for deserializing binary data created by the obj2buf JavaScript library. | ||
| Compatible with obj2buf JavaScript library v1.0.0+ | ||
| ## Features | ||
| - **Header-only*// 2. C++ side | ||
| #include "obj2buf.hpp" | ||
| void process_user_data(const std::vector<uint8_t>& buffer, | ||
| const nlohmann::json& schema_json) { | ||
| std::cout << "Using obj2buf C++ bindings v" << obj2buf::version() << std::endl; | ||
| obj2buf::Schema schema(schema_json["root_type"]); | ||
| nlohmann::json user = schema.deserialize(buffer); | ||
| uint32_t id = user["id"]; | ||
| std::string name = user["name"]; | ||
| uint32_t age = user["age"]; | ||
| std::cout << "Processing user: " << name | ||
| << " (ID: " << id << ", Age: " << age << ")" << std::endl; | ||
| }buf.hpp` file, no compilation required | ||
| - **C++11 Compatible**: Works with modern C++ standards | ||
| - **nlohmann/json Integration**: Uses familiar JSON objects for deserialized data | ||
| - **Complete Type Support**: All obj2buf types supported (primitives, strings, arrays, maps, etc.) | ||
| - **Exception Safety**: Proper error handling with descriptive messages | ||
| - **Version Information**: Built-in version checking for compatibility | ||
| ## Dependencies | ||
| - **nlohmann/json**: For JSON object representation | ||
| - **C++11 or later**: Modern C++ standard | ||
| ## Installation | ||
| ### Using vcpkg | ||
| ```bash | ||
| vcpkg install nlohmann-json | ||
| ``` | ||
| ### Using Conan | ||
| ```bash | ||
| conan install nlohmann_json/3.11.2@ | ||
| ``` | ||
| ### Manual Installation | ||
| Download `nlohmann/json` from https://github.com/nlohmann/json | ||
| ## Usage | ||
| ### Basic Example | ||
| ```cpp | ||
| #include "obj2buf.hpp" | ||
| #include <iostream> | ||
| #include <vector> | ||
| int main() { | ||
| // Schema definition (exported from JavaScript schema.to_json()) | ||
| nlohmann::json schema_def = { | ||
| {"type", "Schema"}, | ||
| {"root_type", { | ||
| {"type", "MapType"}, | ||
| {"field_pairs", { | ||
| {"id", {{"type", "UInt32"}}}, | ||
| {"name", {{"type", "VarStringType"}, {"max_length", 100}}}, | ||
| {"age", {{"type", "UInt8"}}}, | ||
| {"is_active", {{"type", "BooleanType"}}} | ||
| }} | ||
| }} | ||
| }; | ||
| // Create schema from the root_type | ||
| obj2buf::Schema schema(schema_def["root_type"]); | ||
| // Your binary data (from JavaScript obj2buf.serialize()) | ||
| std::vector<uint8_t> buffer = {/* ... binary data ... */}; | ||
| // Deserialize | ||
| nlohmann::json result = schema.deserialize(buffer); | ||
| // Access data | ||
| std::cout << "ID: " << result["id"] << std::endl; | ||
| std::cout << "Name: " << result["name"] << std::endl; | ||
| std::cout << "Age: " << result["age"] << std::endl; | ||
| std::cout << "Active: " << result["is_active"] << std::endl; | ||
| return 0; | ||
| } | ||
| ``` | ||
| ### Working with Raw Pointers | ||
| ```cpp | ||
| // If you have raw buffer data | ||
| const uint8_t* raw_buffer = /* ... */; | ||
| size_t buffer_size = /* ... */; | ||
| nlohmann::json result = schema.deserialize(raw_buffer, buffer_size); | ||
| ``` | ||
| ### Schema from JavaScript Export | ||
| ```cpp | ||
| // In JavaScript: | ||
| // const schema_json = schema.to_json(); | ||
| // // Send schema_json to C++ | ||
| // In C++: | ||
| nlohmann::json schema_json = /* received from JavaScript */; | ||
| obj2buf::Schema schema(schema_json); | ||
| ``` | ||
| ## Version Information | ||
| Check the version of the C++ bindings: | ||
| ```cpp | ||
| #include "obj2buf.hpp" | ||
| #include <iostream> | ||
| int main() { | ||
| std::cout << "obj2buf C++ bindings version: " << obj2buf::version() << std::endl; | ||
| std::cout << "Major: " << obj2buf::version_major() << std::endl; | ||
| std::cout << "Minor: " << obj2buf::version_minor() << std::endl; | ||
| std::cout << "Patch: " << obj2buf::version_patch() << std::endl; | ||
| return 0; | ||
| } | ||
| ``` | ||
| ### Compatibility | ||
| - **C++ Bindings v1.0.0**: Compatible with obj2buf JavaScript v1.0.0+ | ||
| - **Breaking changes**: Will be indicated by major version bumps | ||
| - **New features**: Will be indicated by minor version bumps | ||
| - **Bug fixes**: Will be indicated by patch version bumps | ||
| ## Supported Types | ||
| ### Primitive Types | ||
| - `UInt8`, `UInt16`, `UInt32` - Unsigned integers | ||
| - `Int8`, `Int16`, `Int32` - Signed integers | ||
| - `Float32`, `Float64` - IEEE 754 floating point | ||
| - `BooleanType` - Boolean values | ||
| - `Char` - Single characters | ||
| ### Unified Types (Alternative Syntax) | ||
| - `UInt(byte_length)` - Generic unsigned integer (1, 2, or 4 bytes) | ||
| - `Int(byte_length)` - Generic signed integer (1, 2, or 4 bytes) | ||
| - `Float(byte_length)` - Generic floating point (4 or 8 bytes) | ||
| ### String Types | ||
| - `FixedStringType(length)` - Fixed-length strings | ||
| - `VarStringType(max_length)` - Variable-length strings | ||
| ### Container Types | ||
| - `ArrayType(element_type, length?)` - Arrays (fixed or variable length) | ||
| - `TupleType(element_types...)` - Fixed-structure tuples | ||
| - `MapType(field_pairs)` - Objects with named fields | ||
| - `EnumType(options)` - String enumerations | ||
| - `OptionalType(base_type)` - Nullable values | ||
| ## Error Handling | ||
| All parsing errors throw `obj2buf::parser_error`: | ||
| ```cpp | ||
| try { | ||
| nlohmann::json result = schema.deserialize(buffer); | ||
| } catch (const obj2buf::parser_error& e) { | ||
| std::cerr << "Parse error: " << e.what() << std::endl; | ||
| } | ||
| ``` | ||
| ## Performance Notes | ||
| - **Zero-copy**: Strings are constructed directly from buffer data | ||
| - **Minimal allocations**: Efficient memory usage | ||
| - **Little-endian**: Matches JavaScript buffer format | ||
| - **Header-only**: No linking overhead | ||
| ## Type Mapping | ||
| | obj2buf Type | C++ JSON Type | Notes | | ||
| |--------------|---------------|--------| | ||
| | UInt8/16/32 | `uint32_t` | Promoted to 32-bit for JSON compatibility | | ||
| | Int8/16/32 | `int32_t` | Promoted to 32-bit for JSON compatibility | | ||
| | UInt(1/2/4) | `uint32_t` | Generic unsigned integer types | | ||
| | Int(1/2/4) | `int32_t` | Generic signed integer types with sign extension | | ||
| | Float32/64 | `double` | Float32 promoted to double | | ||
| | Float(4/8) | `double` | Generic floating point types | | ||
| | BooleanType | `bool` | | | ||
| | String types | `std::string` | UTF-8 encoded | | ||
| | Arrays | `json::array` | | | ||
| | Maps | `json::object` | | | ||
| | Enums | `std::string` | Enum value as string | | ||
| | Optional | `null` or value | null for absent values | | ||
| ## Integration Example | ||
| ```cpp | ||
| // Complete example showing JavaScript → C++ workflow | ||
| // 1. JavaScript side (Node.js/Browser) | ||
| /* | ||
| const { Schema, types } = require('obj2buf'); | ||
| const user_type = new types.MapType([ | ||
| ['id', new types.UInt32()], | ||
| ['name', new types.VarStringType(50)], | ||
| ['age', new types.UInt8()] | ||
| ]); | ||
| const schema = new Schema(user_type); | ||
| const user_data = { id: 1234, name: 'Alice', age: 30 }; | ||
| // Serialize data | ||
| const buffer = schema.serialize(user_data); | ||
| // Export schema for C++ | ||
| const schema_json = schema.to_json(); | ||
| // Send both buffer and schema_json to C++ | ||
| */ | ||
| // 2. C++ side | ||
| #include "obj2buf.hpp" | ||
| void process_user_data(const std::vector<uint8_t>& buffer, | ||
| const nlohmann::json& schema_json) { | ||
| obj2buf::Schema schema(schema_json["root_type"]); | ||
| nlohmann::json user = schema.deserialize(buffer); | ||
| uint32_t id = user["id"]; | ||
| std::string name = user["name"]; | ||
| uint8_t age = user["age"]; | ||
| std::cout << "Processing user: " << name | ||
| << " (ID: " << id << ", Age: " << age << ")" << std::endl; | ||
| } | ||
| ``` | ||
| ## Building | ||
| ### Quick Start with run.sh | ||
| The easiest way to get started is using the provided `run.sh` script: | ||
| ```bash | ||
| cd bindings/cpp | ||
| ./run.sh | ||
| ``` | ||
| This script will: | ||
| 1. Download nlohmann/json locally (single header) | ||
| 2. Compile the example with the correct flags | ||
| 3. Run the example program | ||
| No system installation required! | ||
| ### Manual Building | ||
| No special build configuration required. Just include the header: | ||
| ```cpp | ||
| #include "obj2buf.hpp" | ||
| ``` | ||
| Make sure nlohmann/json is available in your include path. | ||
| ### CMake Example | ||
| ```cmake | ||
| find_package(nlohmann_json REQUIRED) | ||
| target_link_libraries(your_target nlohmann_json::nlohmann_json) | ||
| ``` | ||
| ## License | ||
| Same as obj2buf JavaScript library (ISC License). |
| #!/bin/bash | ||
| # obj2buf C++ Example Runner | ||
| # This script downloads dependencies, compiles, and runs the example | ||
| set -e # Exit on any error | ||
| echo "🚀 obj2buf C++ Example Runner" | ||
| echo "================================" | ||
| # Create local include directory | ||
| mkdir -p include/nlohmann | ||
| # Check if nlohmann/json is already downloaded | ||
| if [ ! -f "include/nlohmann/json.hpp" ]; then | ||
| echo "📦 Downloading nlohmann/json (single header)..." | ||
| # Try curl first, then wget | ||
| if command -v curl >/dev/null 2>&1; then | ||
| curl -L -o include/nlohmann/json.hpp \ | ||
| https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp | ||
| elif command -v wget >/dev/null 2>&1; then | ||
| wget -O include/nlohmann/json.hpp \ | ||
| https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp | ||
| else | ||
| echo "❌ Error: Neither curl nor wget found. Please install one of them." | ||
| exit 1 | ||
| fi | ||
| echo "✅ nlohmann/json downloaded successfully" | ||
| else | ||
| echo "✅ nlohmann/json already available" | ||
| fi | ||
| # Compile the example | ||
| echo "🔨 Compiling example.cpp..." | ||
| # Compiler flags | ||
| CXXFLAGS="-std=c++11 -Wall -Wextra -O2" | ||
| INCLUDES="-I./include -I." | ||
| OUTPUT="obj2buf_example" | ||
| if command -v g++ >/dev/null 2>&1; then | ||
| CXX="g++" | ||
| elif command -v clang++ >/dev/null 2>&1; then | ||
| CXX="clang++" | ||
| else | ||
| echo "❌ Error: No C++ compiler found (g++ or clang++)" | ||
| exit 1 | ||
| fi | ||
| echo " Using compiler: $CXX" | ||
| echo " Flags: $CXXFLAGS" | ||
| $CXX $CXXFLAGS $INCLUDES example.cpp -o $OUTPUT | ||
| echo "✅ Compilation successful" | ||
| # Run the example | ||
| echo "🎯 Running the example..." | ||
| echo "================================" | ||
| ./$OUTPUT | ||
| echo "" | ||
| echo "================================" | ||
| echo "🎉 Example completed successfully!" | ||
| echo "" | ||
| echo "📝 To run again: ./$OUTPUT" | ||
| echo "🧹 To clean: rm -f $OUTPUT && rm -rf include/" |
+10
-6
@@ -971,4 +971,4 @@ | ||
| super(); | ||
| if ( max_length < 1 || max_length > 65535 ) { | ||
| throw new Error("max_length must be between 1 and 65535"); | ||
| if ( max_length < 1 || max_length > 4294967295 ) { | ||
| throw new Error("max_length must be between 1 and 4294967296"); | ||
| } | ||
@@ -1007,3 +1007,3 @@ /** | ||
| get _header_size() { | ||
| return this.max_length < 256 ? 1 : 2; | ||
| return this.max_length < 256 ? 1 : (this.max_length < 65536 ? 2 : 4); | ||
| } | ||
@@ -1039,3 +1039,5 @@ | ||
| ? buffer.readUInt8(offset) | ||
| : buffer.readUInt16LE(offset); | ||
| : header_size === 2 | ||
| ? buffer.readUInt16LE(offset) | ||
| : buffer.readUInt32LE(offset); | ||
@@ -1076,4 +1078,6 @@ if (buffer.length < offset + header_size + length) { | ||
| buffer.writeUInt8(string_bytes, offset); | ||
| } else if (header_size === 2) { | ||
| buffer.writeUInt16LE(string_bytes, offset); | ||
| } else { | ||
| buffer.writeUInt16LE(string_bytes, offset); | ||
| buffer.writeUInt32LE(string_bytes, offset); | ||
| } | ||
@@ -1106,3 +1110,3 @@ | ||
| // Check header size limits | ||
| const max_header_value = this._header_size === 1 ? 255 : 65535; | ||
| const max_header_value = this._header_size === 1 ? 255 : (this._header_size == 2 ? 65535 : 4294967295); | ||
| if (string_bytes > max_header_value) { | ||
@@ -1109,0 +1113,0 @@ throw new ParserError(`FixedStringType byte length exceeds header maximum: ${string_bytes} > ${max_header_value}`); |
+4
-4
| { | ||
| "name": "obj2buf", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "description": "A type-safe encoder/decoder for structured binary data with snake_case API design", | ||
@@ -10,3 +10,4 @@ "main": "index.js", | ||
| "README.md", | ||
| "LICENSE" | ||
| "LICENSE", | ||
| "bindings/" | ||
| ], | ||
@@ -33,4 +34,3 @@ "keywords": [ | ||
| "test:watch": "mocha test/**/*.test.js --watch", | ||
| "test:coverage": "nyc mocha test/**/*.test.js", | ||
| "prepublishOnly": "npm test" | ||
| "test:coverage": "nyc mocha test/**/*.test.js" | ||
| }, | ||
@@ -37,0 +37,0 @@ "engines": { |
+13
-0
@@ -16,5 +16,13 @@ # obj2buf | ||
| - **Tested**: 243+ tests covering real-world scenarios | ||
| - **Cross-Language**: C++ bindings available for native applications | ||
| ### Versioning | ||
| - **JavaScript Library**: Follows semantic versioning (semver) | ||
| - **C++ Bindings**: Independent versioning, compatibility noted in documentation | ||
| - **Binary Format**: Stable across patch versions, changes documented in major/minor releases | ||
| ## Installation | ||
| ### JavaScript/Node.js | ||
| ```bash | ||
@@ -24,2 +32,7 @@ npm install obj2buf | ||
| ### C++ Bindings | ||
| The package includes C++11 header-only bindings for deserializing data in native applications. See [`bindings/cpp/README.md`](bindings/cpp/README.md) for details. | ||
| **Current version**: C++ bindings v1.0.0 (compatible with obj2buf JavaScript v1.0.0+) | ||
| ## Quick Start | ||
@@ -26,0 +39,0 @@ |
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Mixed license
LicensePackage contains multiple licenses.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
1209507
1445.79%13
116.67%1824
0.22%374
3.6%1
Infinity%1
Infinity%