1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
//! # compress-json-rs
//!
//! [](https://crates.io/crates/compress-json-rs)
//! [](https://docs.rs/compress-json-rs)
//! [](https://github.com/web-mech/compress-json-rs/blob/main/LICENSE)
//!
//! A space-efficient JSON compression library with **lossless round-trip** compression and decompression.
//!
//! This crate compresses JSON data by deduplicating values and keys, storing them in a compact
//! format with base-62 encoded references. It's particularly effective for JSON with repetitive
//! structures like API responses, configuration files, and data collections.
//!
//! # Features
//!
//! | Feature | Description |
//! |---------|-------------|
//! | **Full JSON Support** | Objects, arrays, strings, numbers, booleans, and null |
//! | **Value Deduplication** | Repeated values stored once with reference keys |
//! | **Schema Deduplication** | Objects with identical keys share schemas |
//! | **Compact Keys** | Base-62 encoding for minimal key size |
//! | **UTF-8 Safe** | Full Unicode support for all string values |
//! | **Zero Dependencies** | Only requires `serde_json` |
//!
//! # Quick Start
//!
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! compress-json-rs = "0.1"
//! serde_json = "1.0"
//! ```
//!
//! Basic usage:
//!
//! ```rust
//! use compress_json_rs::{compress, decompress};
//! use serde_json::json;
//!
//! // Original JSON data
//! let data = json!({
//! "user": "Alice",
//! "active": true,
//! "roles": ["admin", "user"]
//! });
//!
//! // Compress into (values, root_key)
//! let compressed = compress(&data);
//!
//! // Decompress back to original
//! let restored = decompress(compressed);
//!
//! assert_eq!(data, restored);
//! ```
//!
//! # API Overview
//!
//! ## Core Functions
//!
//! | Function | Description |
//! |----------|-------------|
//! | [`compress`] | Compress a JSON value into [`Compressed`] format |
//! | [`decompress`] | Restore original JSON from [`Compressed`] format |
//! | [`decode`] | Decode a single key from the values array |
//!
//! ## Types
//!
//! | Type | Description |
//! |------|-------------|
//! | [`Compressed`] | Tuple type `(Vec<String>, String)` for compressed data |
//! | [`Key`] | String type alias for base-62 encoded references |
//! | [`CONFIG`] | Global configuration constants |
//!
//! ## Helper Functions
//!
//! | Function | Description |
//! |----------|-------------|
//! | [`trim_undefined`] | Remove null values from object (shallow) |
//! | [`trim_undefined_recursively`] | Remove null values from nested objects |
//!
//! ## Low-Level API
//!
//! | Function | Description |
//! |----------|-------------|
//! | [`make_memory`] | Create a new compression memory store |
//! | [`add_value`] | Add a value to memory, get its key |
//! | [`mem_to_values`] | Extract values array from memory |
//!
//! # Compression Format
//!
//! The [`Compressed`] type is a tuple `(Vec<String>, String)`:
//! - First element: Deduplicated value store
//! - Second element: Base-62 key pointing to root value
//!
//! ## Value Encoding
//!
//! Values are encoded with type prefixes:
//!
//! | Prefix | Type | Example Encoded | Original Value |
//! |--------|------|-----------------|----------------|
//! | `b\|T` | Boolean | `b\|T` | `true` |
//! | `b\|F` | Boolean | `b\|F` | `false` |
//! | `n\|` | Number | `n\|42.5` | `42.5` |
//! | `N\|+` | Infinity | `N\|+` | `Infinity` |
//! | `N\|-` | -Infinity | `N\|-` | `-Infinity` |
//! | `N\|0` | NaN | `N\|0` | `NaN` |
//! | `s\|` | Escaped string | `s\|n\|foo` | `"n\|foo"` |
//! | `a\|` | Array | `a\|0\|1\|2` | `[val0, val1, val2]` |
//! | `o\|` | Object | `o\|0\|1\|2` | `{schema0: val1, ...}` |
//! | _(none)_ | String | `hello` | `"hello"` |
//! | `""` / `_` | Null | | `null` |
//!
//! ## Base-62 Keys
//!
//! Keys use characters `0-9`, `A-Z`, `a-z` for compact representation:
//!
//! ```text
//! Index: 0 1 2 ... 9 10 11 ... 35 36 37 ... 61 62 63
//! Key: "0" "1" "2" "9" "A" "B" "Z" "a" "b" "z" "10" "11"
//! ```
//!
//! # Examples
//!
//! ## Serialize for Storage
//!
//! ```rust
//! use compress_json_rs::{compress, decompress, Compressed};
//! use serde_json::json;
//!
//! let data = json!({"items": [1, 2, 3]});
//!
//! // Compress and serialize to JSON string
//! let compressed = compress(&data);
//! let json_str = serde_json::to_string(&compressed).unwrap();
//!
//! // Store json_str to file/database/network...
//!
//! // Later: deserialize and decompress
//! let loaded: Compressed = serde_json::from_str(&json_str).unwrap();
//! let restored = decompress(loaded);
//!
//! assert_eq!(data, restored);
//! ```
//!
//! ## Arrays of Similar Objects
//!
//! Compression is most effective for repetitive data:
//!
//! ```rust
//! use compress_json_rs::{compress, decompress};
//! use serde_json::json;
//!
//! // Data with repeated schema and values
//! let users = json!([
//! { "id": 1, "name": "Alice", "role": "user" },
//! { "id": 2, "name": "Bob", "role": "user" },
//! { "id": 3, "name": "Charlie", "role": "admin" },
//! ]);
//!
//! let (values, root) = compress(&users);
//!
//! // Schema ["id", "name", "role"] stored once
//! // Value "user" stored once, referenced twice
//! println!("Compressed to {} unique values", values.len());
//!
//! let restored = decompress((values, root));
//! assert_eq!(users, restored);
//! ```
//!
//! ## Clean Data Before Compression
//!
//! ```rust
//! use compress_json_rs::{compress, trim_undefined_recursively};
//! use serde_json::{json, Map, Value};
//!
//! let mut data: Map<String, Value> = serde_json::from_value(json!({
//! "name": "Alice",
//! "middleName": null, // Will be removed
//! "profile": {
//! "bio": "Developer",
//! "website": null // Will be removed
//! }
//! })).unwrap();
//!
//! // Remove all null values recursively
//! trim_undefined_recursively(&mut data);
//!
//! // Now compress the cleaned data
//! let compressed = compress(&Value::Object(data));
//! ```
//!
//! ## Low-Level API Usage
//!
//! For custom compression workflows:
//!
//! ```rust
//! use compress_json_rs::{make_memory, add_value, mem_to_values, decode};
//! use serde_json::json;
//!
//! let mut mem = make_memory();
//!
//! // Add values - duplicates return same key
//! let key1 = add_value(&mut mem, &json!("repeated"));
//! let key2 = add_value(&mut mem, &json!("repeated"));
//! assert_eq!(key1, key2); // Same key!
//!
//! // Add more values
//! let key3 = add_value(&mut mem, &json!(42));
//! let key4 = add_value(&mut mem, &json!({"nested": "object"}));
//!
//! // Extract final values
//! let values = mem_to_values(&mem);
//!
//! // Decode any key
//! assert_eq!(decode(&values, &key1), json!("repeated"));
//! assert_eq!(decode(&values, &key3), json!(42));
//! ```
//!
//! # Performance
//!
//! ## Best Use Cases
//!
//! - **API responses** with arrays of similar objects
//! - **Configuration files** with repeated values
//! - **Data exports** with consistent schemas
//! - **Cache storage** where size matters
//!
//! ## Compression Ratios
//!
//! | Data Type | Typical Ratio |
//! |-----------|---------------|
//! | Arrays of similar objects | 30-50% of original |
//! | Highly repetitive data | 20-40% of original |
//! | Mixed data | 50-70% of original |
//! | Unique values only | ~100% (no benefit) |
//!
//! # Special Values (v3.4.0+)
//!
//! This crate supports special floating-point values for cross-platform compatibility.
//! Handling depends on configuration options:
//!
//! | Config Option | Default | Effect |
//! |---------------|---------|--------|
//! | `preserve_nan` | `false` | When `true`, NaN encoded as `N\|0` |
//! | `preserve_infinite` | `false` | When `true`, ±Infinity encoded as `N\|+`/`N\|-` |
//! | `error_on_nan` | `false` | When `true` (and preserve=false), panic on NaN |
//! | `error_on_infinite` | `false` | When `true` (and preserve=false), panic on Infinity |
//!
//! By default (preserve options = false), special values become `null` like `JSON.stringify`.
//!
//! When preserved, the encoding is:
//! - `Infinity` → `N|+`
//! - `-Infinity` → `N|-`
//! - `NaN` → `N|0`
//!
//! Note: JSON doesn't natively support these values, so they become `null` when
//! decompressed to `serde_json::Value`. The compressed representation preserves
//! them for compatibility with JavaScript and Python implementations that have
//! `preserve_*` enabled.
//!
//! # Compatibility
//!
//! This crate is a Rust port of the JavaScript [compress-json](https://github.com/beenotung/compress-json)
//! library (v3.4.0+). Compressed data is compatible between implementations, allowing cross-platform
//! data exchange.
//!
//! # License
//!
//! Licensed under BSD-2-Clause. See [LICENSE](https://github.com/web-mech/compress-json-rs/blob/main/LICENSE).
// Module declarations
// Re-export core functionality
pub use ;
// Expose lower-level APIs
pub use ;
pub use ;
pub use ;
// Expose encoding functions for special values (v3.2.0+)
pub use ;