Repository navigation
Releases: homer6/automerge-cpp
Release list
v0.6.0 release
automerge-cpp v0.6.0 — Full JSON Ecosystem & Logic Refinement
This release completes the extensive nlohmann/json integration (Phases 12B–12G), making Automerge documents first-class citizens in the JSON ecosystem. It also introduces critical stability fixes for type conversions and pointer-based navigation.
Complete nlohmann/json Integration
Automerge documents now support the full suite of JSON standards for path-based access and batch manipulation.
JSON Pointer, Patch, & Merge Patch
-
JSON Pointer (RFC 6901): Deeply address any value using string paths (e.g.,
/users/0/name). Includes full support for~0and~1escaping. -
am::json::get_pointer,am::json::put_pointer,am::json::delete_pointer. -
JSON Patch (RFC 6902): Apply atomic batches of
add,remove,replace,move,copy, andtestoperations. -
am::json::apply_json_patchandam::json::diff_json_patch. -
JSON Merge Patch (RFC 7386): Update documents using simple "null-means-delete" semantics, perfect for partial state synchronization.
-
am::json::apply_merge_patchandam::json::generate_merge_patch.
Lossless Import & Export
- Recursive Export:
am::json::export_jsonnow fully resolves nestedObjIds, materializing complex Automerge trees into standard JSON objects and arrays. - Flatten/Unflatten: Convert nested documents into flat maps of
pointer_path -> valuefor easier debugging or structured configuration management.
Namespace & API Refactoring
To ensure long-term maintainability and prevent name collisions, the JSON API has been structured into two distinct layers:
namespace automerge_cpp(am::): Contains only ADL-basedto_jsonandfrom_jsonoverloads. This is required for C++ Argument-Dependent Lookup to find converters automatically when writingjson j = my_value;.namespace automerge_cpp::json(am::json::): Contains all explicit interop functions (Import, Export, Pointer, Patch).
Critical Fixes & Stability
Core Logic Improvements
- Uninitialized Index Fix: Resolved a critical bug in
add_value_at_pointerwhere an uninitializedstd::size_tvariable could lead to non-deterministic list insertion points. - Loop Variable Overflow: Fixed a potential crash in Base64 decoding where an
unsigned charloop variable was too narrow for the character lookup table. - Recursive Deadlock Prevention: Fixed a "Resource deadlock avoided" error. All JSON write functions now use internal Transaction read methods that bypass
shared_mutexacquisition when already inside a transaction.
Serializer Fidelity
- Unsigned Integer ADL: Reordered type inference in
from_jsonto correctly distinguish betweenuint64andint64based on value range, ensuring larger-than-int64 values are not truncated.
Release Statistics
- Version: 0.6.0
- Tests: 583 passing tests (an increase of 98 tests covering RFC compliance and edge cases).
- New Documentation: Added the JSON Integration Guide covering all new features with usage examples.
v0.5.0 release
automerge-cpp v0.5.0 — Modern API & JSON Interoperability
The 0.5.0 release introduces a significant evolution of the automerge-cpp API, focusing on developer ergonomics and seamless integration with the modern C++ ecosystem. This version bridges the gap between CRDT complexity and the intuitive patterns popularized by libraries like nlohmann/json.
Modern C++ API (Phase 12A)
This release significantly reduces boilerplate by introducing type-aware overloads and template-driven accessors.
Ergonomic Transactions
The transact and transact_with_patches methods are now templated, allowing lambdas to return values directly to the caller. This eliminates the need for capturing external variables just to retrieve a new ObjId.
auto list_id = doc.transact([](am::Transaction& tx) {
tx.put(am::root, "title", "Shopping List");
return tx.put_object(am::root, "items", am::ObjType::list);
});
Initializer Lists & STL Containers
Mutations now support bare initializer lists and standard STL containers. The API automatically disambiguates between list and map structures based on the input type.
- List Creation:
tx.put(root, "items", {"Milk", "Eggs", "Bread"}) - Map Creation:
tx.put(root, "config", {{"port", 8080}, {"host", "localhost"}}) - STL Support: Directly pass
std::vector,std::set, orstd::maptoputandinsert.
Simplified Data Access
New accessors make navigating the document tree as simple as working with a standard JSON object.
- Typed
get<T>: Retrieve values directly as C++ types:doc.get<std::string>(root, "name"). - Variadic
get_path: Navigate nested structures in a single call:doc.get_path("config", "database", "port"). - Bracket Operator: Quick access to the root map:
doc["title"].
nlohmann/json Interoperability
Automerge documents can now be easily initialized from or exported to nlohmann/json structures.
- Recursive Import/Export: Utilities to convert between
nlohmann::jsonand AutomergeDocumentinstances. - ADL Serialization: Provides
to_jsonandfrom_jsonoverloads for all core Automerge types (ActorId,ChangeHash,OpId,Change, etc.). - Submodule Integration: The library now includes
nlohmann/jsonas an upstream reference.
Critical Fixes & Improvements
Serializer & Encoder Stability
- Fixed Actor Table Incompleteness: The actor table now recursively scans
obj,insert_after, andpredfields, ensuring that documents with complex causal histories (like nested increments) remain loadable. - Object Type Preservation: Fixed a bug where
insert_objectoperations into lists lost their type identity during serialization. - Parsing Ambiguity: Resolved a critical bug where RLE literal control bytes could be misidentified as column headers.
RLE Engine Refinement
- Null Coalescing: Fixed a logic error in
append_null()that caused consecutive null values to be encoded inefficiently. - Exhaustive Testing: Added 59 new RLE-specific tests, covering edge cases for null interleaving, transition patterns, and large run compression.
Release Statistics & CI
- Total Tests: 485 passing tests (up from 347 in v0.4.1).
- CI Configuration: Optimized GitHub Actions to trigger on
pushonly to streamline the development cycle. - Doxygen: Added GitHub Pages workflow to automatically deploy API documentation generated from Doxygen-annotated headers.
Would you like me to generate a code sample showing how to use the new get_path variadic template to safely access deeply nested data?
v0.4.1 release
automerge-cpp v0.4.1
This maintenance release focuses on storage reliability, encoding correctness, and exhaustive validation of the Run-Length Encoding (RLE) engine. It resolves critical bugs in the binary format that previously led to document corruption and incomplete data persistence.
This release achieves test suite parity with the upstream library's test suite.
RLE Engine & Columnar Fixes
A comprehensive overhaul of the RLE (Run-Length Encoding) and columnar storage layers ensures full data integrity across complex concurrent editing scenarios.
Bug Fixes
- RLE Null Coalescing: Fixed a logic error where
append_null()triggered a premature flush of pending runs. This caused consecutive null values to be encoded individually (taking 2 bytes each) rather than as a compact run (2 bytes total), and occasionally led to misaligned buffers. - Column Header Ambiguity: Resolved a critical parsing bug where RLE literal control bytes (specifically
0x7F) were misidentified as ascending column specifications. This caused the parser to consume data as headers, shifting all subsequent data and breakingDocument::load(). - Explicit Column Count: Introduced an explicit column count prefix to the raw column format to replace the previous ascending-order heuristic, making the parser immune to data-payload overlap.
Improved Serializer Support
- Complete Actor Table:
ensure_actor_table()now recursively extracts actor IDs fromobj,insert_after, andpredfields. This ensures that documents containingincrementoperations on nested objects or complex causal histories remain loadable. - Object Type Round-Trips: Fixed a limitation where
insert_objectoperations into lists lost theirObjTypeidentity during serialization. These are now correctly encoded using action codes 0 (map) or 2 (list) to preserve their identity during load.
Testing & Validation
This release represents a major leap in test coverage, achieving closer parity with the upstream Rust implementation.
-
Expanded RLE Test Suite: 59 new tests (increasing the count from 10 to 69) cover exhaustive edge cases, including:
-
Null positioning and interleaving (nulls at start/middle/end).
-
Signed integer transition patterns (run literal null).
-
Compactness verification and stress tests for unique value runs.
-
New Integration Tests: Added 23 new integration tests to
document_test.cppfocusing on: -
List Conflicts: Conflict resolution and deterministic insertion ordering across multiple actors.
-
Counter Edge Cases: Verification that independent counters do not combine incorrectly and survive nested map operations.
-
Large Data Stress: Validated O(n) scan performance with 500-element lists and 100+ put/delete cycles.
Performance & Parallelism Refinements
The threading model introduced in v0.4.0 has been tightened for production stability.
-
Thread Pool API Integration: The
parallel_perf_demohas been rewritten to use the built-inparallelize_loopAPI. It now demonstrates lock-free parallel reads by leveragingset_read_locking(false)to eliminateshared_mutexcontention. -
API Enhancements:
-
Added
Document::get_thread_pool()to allow sharing pools between documents. -
Added
Document::read_locking()to query the current synchronization state. -
Platform Compatibility: Replaced
std::jthreadwithstd::threadand explicit join loops to improve compatibility with older toolchains and the FreeBSD environment. -
CMake Infrastructure: Added
Threadsas a public dependency, ensuring proper linking ofpthreadacross all consumer targets.
Statistics & Environment
- Version: 0.4.1
- Total Tests: 347 passing tests.
- Dependencies: CMake 3.28+, zlib, and C++23.
v0.4.0 release
automerge-cpp v0.4.0 — Performance & Parallelism
The 0.4.0 release introduces robust multi-threading, execution model. This version enables near-linear scaling for read-heavy workloads and significant throughput improvements for batch mutations across multi-core systems.
Thread Safety & Concurrent Access
The Document class is now fully thread-safe, allowing safe usage in multi-threaded environments like high-concurrency servers.
- Read-Write Locking: Implemented internal synchronization using std::shared_mutex. Public API methods automatically manage locking:
- N Concurrent Readers: Methods like get, text, keys, and save utilize shared locks.
- Exclusive Writers: Mutation methods like transact and merge utilize exclusive locks.
- Lock-Free Read Path: Introduced set_read_locking(false) for scenarios where the caller can guarantee no concurrent writers. This eliminates shared_mutex cache-line contention, delivering up to 13.5x parallel scaling on 30-core systems.
- Shared Thread Pool: Documents can now share a work-stealing thread pool via the new constructor Document(std::shared_ptr<thread_pool>).
Parallel Performance Improvements
This release focuses on "Phase 11: Performance and Parallelism," delivering massive speedups for historical data access and document persistence.
Key Optimization Metrics (30-core Linux Xeon)
| Operation | Sequential | Parallel | Speedup |
|---|---|---|---|
| Get (100K keys, lock-free) | 7.1 M ops/s | 95.0 M ops/s | 13.5x |
| Save (500 documents) | 95 K docs/s | 806 K docs/s | 8.4x |
| Time Travel (get_at) | 130 K ops/s | 2.88 M ops/s | 22.1x |
| Sync Protocol | 4.7 K ops/s | 26.4 K ops/s | 5.6x |
Internal Parallelism
- Work-Stealing Infrastructure: Integrated Barak Shoshany's header-only BS::thread_pool.
- Performance Caching: * Change Hash Cache: Eliminates redundant SHA-256 computations during sync and time travel (22x faster historical reads).
- Actor Table Cache: Speeds up large document saves by 1.2x.
- Low-Latency Dispatch: Configured internal workers to yield instead of sleep, reducing latency for high-frequency small tasks.
New Demonstrations & Benchmarks
Two new focus-built examples showcase the v0.4.0 threading architecture:
- thread_safe_demo: Illustrates simultaneous readers and writers on a single document, including simultaneous text appends and deterministic concurrent saves.
- parallel_perf_demo: Demonstrates the power of monoid-associative merging through parallel tree-reduction of 100 peers and parallel sync of 50 document pairs.
The benchmark suite has expanded from 26 to 32 benchmarks, now covering parallel scaling efficiency and hardware utilization.
Infrastructure & Maintenance
- Code Quality: Added // NOLINT annotations and explanatory documentation for intentional edge cases in the thread pool logic.
- CI Updates: Replaced std::jthread usage with std::thread and explicit join loops to maintain maximum compatibility across older toolchains and the FreeBSD CI environment.
- Dependency Management: Removed the Taskflow dependency in favor of the lightweight, header-only thread pool.
Project Status
- Tests: 281 passing tests (7 new thread-safety specific tests).
-
Next Steps: Remaining Phase 11 items including Fenwick tree integration for
$O(\log n)$ list operations and hardware-accelerated SHA-256.
v0.3.0 release
automerge-cpp v0.3.0
This release focuses on quality assurance and rigorous performance analysis. It introduces automated fuzz testing, static analysis, and comprehensive benchmark results for Linux server environments, ensuring the library is both reliable and highly optimized for production deployments.
Linux Server Benchmarks
Detailed performance measurements were conducted on a high-end Linux server (Intel Xeon Platinum 8358, 30 vCPUs). The results demonstrate high-throughput capabilities for core operations:
- Map Operations: ~32.4 Million gets/sec and ~2.46 Million batched puts/sec.
- Cursor Operations: ~3 Million resolves/sec.
- Synchronization: ~1.48 Million sync messages generated per second.
- Microarchitecture Comparison: A detailed comparison between Linux (Xeon) and macOS (M3 Max) is included in
docs/benchmark-results-linux.md, highlighting performance differences in zlib/DEFLATE execution across compilers and architectures.
Automated Testing & Safety (Phase 9)
This release completes Phase 9, adding multiple layers of defense against bugs and security vulnerabilities:
- Fuzz Testing: Introduced three
libFuzzertargets (fuzz_load,fuzz_leb128,fuzz_change_chunk) to stress-test deserialization paths. - Seed Corpus: Integrated 8 upstream "crasher" files from the Rust implementation to ensure cross-platform robustness.
- Memory Safety CI: Added a dedicated GitHub Actions job running all 274 tests under AddressSanitizer (ASan) and UndefinedBehaviorSanitizer (UBSan) on Linux, macOS, and FreeBSD.
- Static Analysis: Implemented a project-wide
.clang-tidyconfiguration. CI now enforces zero warnings acrossbugprone-*,performance-*, andclang-analyzer-*check suites.
Code Quality Improvements
In preparation for the Phase 9 CI requirements, several architectural refinements were made to eliminate potential issues identified by static analysis:
- Logic Simplification: Collapsed redundant branches in OBJ decoding and value metadata encoding to improve maintainability and performance.
- Type Safety: Fixed potential implicit widening issues in multiplication results within the SHA-256 and compression logic.
- Modernization: Removed unnecessary
std::movecalls on trivially-copyable types and convertedstd::functionparameters toconst &to avoid redundant allocations.
CI & Workflow Updates
- Development Rules: Updated
CLAUDE.mdto mandate that all tests must pass locally before any commit. - Doxygen Foundation: Introduced the initial
Doxyfileconfiguration to support future automated API documentation generation (Phase 10). - FreeBSD Support: Refined the FreeBSD CI workflow to utilize the system's native zlib, resolving previous package installation conflicts.
Release Statistics
- Version: 0.3.0
- Tests: 274 passing tests.
- Platforms: Linux (GCC/Clang), macOS (Apple Clang), Windows (MSVC), FreeBSD (Clang).
v0.2.0 release
automerge-cpp v0.2.0
This release marks the completion of Phase 8, introducing a new columnar binary format that is compatible with the upstream Rust Automerge implementation. It includes significant upgrades to storage efficiency, integrity validation, and architectural performance.
Core Storage Upgrades
- Columnar Binary Format: Replaced the previous row-based binary format (v1) with an upstream-compatible columnar encoding. This reorganization groups similar data types together, significantly improving compression ratios and read performance.
- Raw DEFLATE Compression: Integrated raw DEFLATE compression (no zlib/gzip headers, windowBits=-15) for individual columns. Compression is automatically applied to any column exceeding a 256-byte threshold to minimize the document footprint.
- SHA-256 Integrity Validation: Implemented a new chunk-based envelope system. Each data chunk is now prefixed with a SHA-256 checksum, ensuring the integrity of the document during storage and transmission.
- Backward Compatibility: Added automatic format detection to
Document::load(). The library can now seamlessly detect and load legacy v1 (row-based) documents, migrating them to the modern format during the next save operation.
New Codecs and Primitives
- RLE (Run-Length Encoding): Added high-performance encoders and decoders for runs of identical values, literal sequences, and null runs.
- Delta Encoding: Implemented delta encoders to efficiently store monotonic sequences and consecutive differences.
- Boolean Codec: Added an alternating run-length encoder specifically optimized for boolean sequences.
- SHA-256 Primitive: Integrated a FIPS 180-4 compliant SHA-256 implementation for change hashing and chunk verification.
CI and Platform Support
- Platform Specific Fixes: Optimized the FreeBSD CI pipeline by utilizing the system-provided zlib instead of attempting a redundant package installation.
- Windows Builds: Updated Windows CI to utilize vcpkg for zlib dependency management.
- Development Rules: Added a strict requirement to the development workflow (CLAUDE.md) ensuring all tests pass before any commit.
Architecture and Refinement
- Value Metadata Optimization: Refined the internal ULEB128 encoding of value metadata to properly support arbitrary value sizes by placing type tags in the low 4 bits and byte lengths in the upper bits.
- Heads Recomputation: Fixed a bug in legacy format migration where concurrent heads from merged documents were not properly preserved during SHA-256 recomputation.
- Actor Lookup: Added robust assertions for actor indexing during serialization to prevent document corruption.
Project Statistics
- Test Coverage: Expanded the test suite to 274 passing tests across 13 test files, including exhaustive round-trip validation for all new columnar codecs and op types.
- Build Requirements: CMake 3.28+ and zlib are now required for building the library.
v0.1.0 release
automerge-cpp v0.1.0 — Initial Release
This release marks the debut of automerge-cpp, a from-scratch C++23 reimplementation of the Automerge CRDT. This version provides a robust, high-performance foundation for building collaborative applications with a modern, type-safe API.
Unlike a simple wrapper, this library embraces idiomatic C++23 features—such as ranges, algebraic types, and strong typing—to ensure that illegal states are unrepresentable at compile time.
Core Features
Data Model & Containers
Support for the primary Automerge data types, allowing for complex nested structures:
- Maps & Tables: Key-value storage with deterministic conflict resolution.
- Lists: Ordered sequences of values.
- Text: Specialized RGA-based sequences for character-level collaborative editing.
- Counters: CRDT counters that merge by accumulating increments/decrements.
Rich Text Marks
Comprehensive support for rich text annotations (bold, italic, links, etc.):
- Identity-Anchored: Marks are anchored to element
OpIds rather than indices, ensuring they survive concurrent insertions or deletions. - Dynamic Resolution: Indices are resolved at read-time, automatically adjusting as the document changes.
Time Travel & Historical Reads
The document state is stored as a directed acyclic graph (DAG) of changes:
- Heads Tracking: Retrieve the "heads" (leaf hashes) of the document.
- Historical Queries: Read any part of the document at any specific point in history using
get_at(),text_at(),keys_at(), andlength_at().
Stable Cursors
- Cursors provide a way to track a specific position in a list or text object that remains stable across merges and edits, unlike traditional integer indices.
Fork, Merge, & Sync
- Fork/Merge: Deeply copy a document into an independent instance and merge it back later.
- Efficient Sync: A Bloom filter-based synchronization protocol for minimal-bandwidth peer-to-peer updates.
- Change Tracking: Atomic
transact()blocks produce discreteChangeobjects with causal dependency tracking.
Binary Persistence
- Efficient Storage: Full
save()andload()support using LEB128 variable-length encoding for compact binary representation. - Verification: Robust loading that reconstructs document state by replaying change history.
Performance
Optimized for high-throughput environments. Benchmarks performed on Apple M3 Max (Release build):
- Map Operations: ~3.3 Million puts/sec | ~29.3 Million gets/sec.
- Cursor Resolution: ~6.1 Million resolves/sec.
- Time Travel: ~1.3 Million historical reads/sec.
Developer Experience
Modern C++23 Architecture
Built on the principles of "Easy to use, Hard to misuse":
- No Raw Loops: Traversal is handled via
std::rangespipelines and algorithms. - Strong IDs:
ActorId,ObjId, andOpIdare distinct types to prevent accidental mix-ups. - Algebraic Values: Values are modeled as
std::variant, ensuring safe access to scalars and objects. - No Exceptions: Uses
std::optionalandstd::expected(planned) for predictable error handling.
Multi-Platform Support
Continuous Integration (CI) ensures compatibility across:
- Linux (GCC 14+, Clang 18+)
- macOS (Apple Clang)
- Windows (MSVC 19.38+)
- FreeBSD
Project Resources
- Documentation: Comprehensive style guides and API references are available in the
docs/folder. - Examples: Four sample programs (
basic_usage,collaborative_todo,text_editor,sync_demo) are included to help you get started.