Common Types and Definitions¶
Overview¶
The common.h
header provides foundational type definitions, macros, and compatibility layers for the Hakka JSON C API. It establishes a consistent interface for both C and C++ consumers while maintaining zero-overhead abstractions and cross-language interoperability.
Key Features:
- C/C++ Compatibility: Automatic detection and appropriate header inclusion
- Type Safety: Strong typing for handles and iterators via
uint64_t
base type - FFI-Ready: 64-bit handle encoding suitable for foreign function interfaces
- Namespace Isolation: Prefixed types (
Hakka*
) prevent naming collisions - Linkage Control: C linkage macros for seamless C++ integration
Architecture¶
Handle System¶
All Hakka JSON objects are accessed through opaque 64-bit handles. This design provides:
- Memory Safety: Handles are tokens, not raw pointers/preventing direct memory access
- ABI Stability: Handle representation remains constant across library versions
- Cross-Language Support: Fixed-width integers (64-bit) ensure consistent layout in Python, Rust, Go FFIs
- Type Discrimination: Four distinct handle types for different object categories
// Handle type hierarchy
HakkaHandle // Base handle: all JSON values (int, float, string, array, object, etc.)
HakkaStringIter // String iteration handle
HakkaArrayIter // Array iteration handle
HakkaObjectIter // Object iteration handle
Design Rationale: While the internal C++ implementation uses 32-bit handles for memory efficiency, the C API exposes 64-bit handles to:
- Align with native pointer sizes on 64-bit architectures
- Reserve upper bits for future extensions (versioning, flags, generation counters)
- Simplify FFI integration (most bindings expect pointer-sized integers)
C/C++ Compatibility Layer¶
The header uses conditional compilation to provide appropriate types:
C++ Mode (__cplusplus
defined):
#include <cstddef> // size_t, nullptr
#include <cstdint> // uint64_t, int64_t, etc.
using HakkaHandle = uint64_t;
using HakkaStringIter = uint64_t;
// ... type aliases
C Mode (C99/C11):
#include <stdbool.h> // bool, true, false
#include <stddef.h> // size_t, NULL
#include <stdint.h> // uint64_t, int64_t, etc.
typedef uint64_t HakkaHandle;
typedef uint64_t HakkaStringIter;
// ... typedefs
This dual approach ensures:
- Zero overhead: Type aliases in C++, typedefs in C
- Standard compliance: Uses standard library headers (no custom implementations)
- Consistency: Identical binary layout regardless of compilation mode
Type Definitions¶
HakkaHandle¶
typedef uint64_t HakkaHandle;
Purpose: Universal handle type for all JSON values (primitives and containers).
Usage:
- Return type for creation functions:
hakka_int_create()
,hakka_array_create()
, etc. - Parameter type for all value manipulation functions
- Storage type for array elements and object values
Encoding (internal implementation detail):
- Lower 32 bits: Index into type-specific manager
- Upper 32 bits: Reserved (currently unused, available for future extensions)
Invalid Handle: The value 0
represents an invalid/uninitialized handle. All valid handles are non-zero.
HakkaStringIter¶
typedef uint64_t HakkaStringIter;
Purpose: Opaque iterator for UTF-8 string traversal.
Usage:
- Iterate over grapheme clusters (user-perceived characters)
- Support forward and reverse iteration
- Enable substring extraction and codepoint access
Lifecycle:
- Created via
hakka_string_iter_create(HakkaHandle string)
- Advanced via
hakka_string_iter_next()
,hakka_string_iter_prev()
- Destroyed via
hakka_string_iter_destroy(HakkaStringIter iter)
HakkaArrayIter¶
typedef uint64_t HakkaArrayIter;
Purpose: Opaque iterator for array traversal.
Usage: - Forward and reverse iteration over array elements - Range-based iteration support - Random access via index-based positioning
Lifecycle:
1. Created via hakka_array_iter_create(HakkaHandle array)
2. Advanced via hakka_array_iter_next()
, hakka_array_iter_prev()
3. Destroyed via hakka_array_iter_destroy(HakkaArrayIter iter)
HakkaObjectIter¶
typedef uint64_t HakkaObjectIter;
Purpose: Opaque iterator for object key-value pair traversal.
Usage:
- Iterate over object entries (key-value pairs)
- Retrieve current key and value via iterator
- Support insertion-order traversal
Lifecycle:
- Created via
hakka_object_iter_create(HakkaHandle object)
- Advanced via
hakka_object_iter_next()
,hakka_object_iter_prev()
- Destroyed via
hakka_object_iter_destroy(HakkaObjectIter iter)
Macros¶
extern_c¶
#define extern_c extern "C"
Purpose: Declares C linkage for C++ compilers.
Usage:
extern_c HakkaHandle hakka_int_create(int64_t value);
Effect:
- Prevents C++ name mangling for exported functions
- Enables C code to link against C++ compiled libraries
- No-op in C mode (C compilers ignore extern "C"
)
C_BOOL¶
#define C_BOOL uint8_t
Purpose: Portable boolean type for C interfaces.
Rationale:
- C99
_Bool
has implementation-defined size (usually 1 byte, but not guaranteed) - C++
bool
is guaranteed 1 byte, but may differ from C_Bool
in ABI uint8_t
ensures consistent 8-bit representation across all platforms
Usage:
C_BOOL hakka_object_contains(HakkaHandle object, const char* key);
// Returns: 1 (true) if key exists, 0 (false) otherwise
Convention:
0
= false- Non-zero (typically
1
) = true
Dependencies¶
hakka_json_enum.h¶
Provides enumeration types for:
- HakkaType: JSON type discrimination (
HAKKA_TYPE_INT
,HAKKA_TYPE_ARRAY
, etc.) - HakkaError: Error codes for exception-less error reporting
- HakkaCompareResult: Comparison results for sorting and ordering
Inclusion Order:
#include <hakka_json_enum.h> // Enums first
#include "common.h" // Common types second
#include "int.h" // Specific type APIs third
Thread Safety¶
All type definitions in common.h
are thread-safe as they are:
- Compile-time constants: Macros and typedefs have no runtime state
- Immutable: Type aliases cannot be modified after definition
- Header-only: No shared global state introduced
Note: Thread safety of handle operations depends on the underlying C++ implementation's manager synchronization (documented per function).
Portability¶
Platform Support¶
- Operating Systems: Linux, macOS, Windows
- Architectures: x86-64, ARM64, RISC-V (any platform with C99/C++17 support)
- Compilers: GCC 7+, Clang 10+, MSVC 2019+
ABI Considerations¶
- Handle Size: Always 64 bits (8 bytes) on all platforms
- Alignment: Natural alignment (8-byte boundaries)
- Endianness: Handle encoding is endian-agnostic (opaque tokens, not bit-fields)
Example Usage¶
C API Consumer¶
#include <hakka_json/common.h>
#include <hakka_json/int.h>
#include <hakka_json/array.h>
void example(void) {
// Create integer handle
HakkaHandle int_val = hakka_int_create(42);
// Create array handle
HakkaHandle arr = hakka_array_create();
hakka_array_push_back(arr, int_val);
// Iterate using array iterator
HakkaArrayIter iter = hakka_array_iter_create(arr);
while (!hakka_array_iter_at_end(iter)) {
HakkaHandle elem = hakka_array_iter_get(iter);
// Process element...
hakka_array_iter_next(iter);
}
hakka_array_iter_destroy(iter);
// Cleanup
hakka_handle_destroy(arr);
hakka_handle_destroy(int_val);
}
C++ API Consumer (using C API)¶
extern "C" {
#include <hakka_json/common.h>
#include <hakka_json/int.h>
}
void example_cpp() {
HakkaHandle value = hakka_int_create(100);
// C++ can use C API via extern "C" linkage
int64_t num = hakka_int_get_value(value);
hakka_handle_destroy(value);
}
Design Decisions¶
Why 64-bit Handles?¶
Question: The C++ implementation uses 32-bit handles internally. Why does the C API expose 64-bit handles?
Answer:
- FFI Alignment: Most scripting languages (Python, Ruby, Lua) expect pointer-sized integers (64-bit on modern systems)
-
Future-Proofing: Upper 32 bits reserved for:
- Generation counters (detect use-after-free)
- Version tags (ABI evolution)
- Handle flags (metadata without extra lookups)
-
Performance: 64-bit handles avoid truncation/extension overhead on 64-bit architectures
- Simplicity: One handle size for all use cases (no small vs large handle variants)
Trade-off: 2x memory overhead for handle storage is acceptable given:
- Handles are transient (stack/register allocated in most code)
- Actual JSON data dominates memory usage (strings, arrays, objects)
- Memory safety and clarity outweigh 4-byte savings per handle
Why Separate Iterator Types?¶
Question: Why not use HakkaHandle
for iterators?
Answer:
- Type Safety: Prevents accidentally passing array iterators to string functions
- API Clarity: Function signatures self-document iterator requirements
- Evolution: Iterator implementations can diverge from handle encoding independently
- Compiler Diagnostics: C++ compilers catch type mismatches at compile-time
See Also¶
- Array API - Array container operations
- Object API - Object container operations