New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

obj2buf

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

obj2buf - npm Package Compare versions

Comparing version
1.0.0
to
1.1.0
+62
bindings/cpp/CMakeLists.txt
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}`);

{
"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": {

@@ -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 @@