recurram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,900 @@
1
+ use std::fmt;
2
+
3
+ use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
4
+ use recurram::model::SchemaField;
5
+ use recurram::{
6
+ create_session_encoder, decode, encode, encode_batch, encode_with_schema, RecurramError, Schema,
7
+ SessionEncoder, SessionOptions, UnknownReferencePolicy, Value,
8
+ };
9
+ use serde::de::{self, MapAccess, Visitor};
10
+ use serde::{Deserialize, Deserializer, Serialize};
11
+ use serde_json::value::RawValue;
12
+
13
+ // ── SIMD-JSON helpers ───────────────────────────────────────────────────────
14
+ // simd_json::from_slice requires &mut [u8]. We accept owned Strings from N-API
15
+ // and convert via into_bytes() — zero extra allocation.
16
+
17
+ /// Parse JSON using simd-json (fast SIMD path). Falls back gracefully since
18
+ /// simd_json implements serde's Deserializer trait.
19
+ #[inline(always)]
20
+ fn simd_from_mut_slice<'de, T: Deserialize<'de>>(bytes: &'de mut [u8]) -> Result<T> {
21
+ simd_json::from_slice(bytes)
22
+ .map_err(|e| BridgeError::new(format!("simd-json parse error: {e}")))
23
+ }
24
+
25
+ pub type Result<T> = std::result::Result<T, BridgeError>;
26
+
27
+ #[derive(Debug, Clone, PartialEq, Eq)]
28
+ pub struct BridgeError {
29
+ message: String,
30
+ }
31
+
32
+ impl BridgeError {
33
+ fn new(message: impl Into<String>) -> Self {
34
+ Self {
35
+ message: message.into(),
36
+ }
37
+ }
38
+ }
39
+
40
+ impl fmt::Display for BridgeError {
41
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42
+ write!(f, "{}", self.message)
43
+ }
44
+ }
45
+
46
+ impl std::error::Error for BridgeError {}
47
+
48
+ impl From<serde_json::Error> for BridgeError {
49
+ fn from(value: serde_json::Error) -> Self {
50
+ Self::new(format!("invalid json payload: {value}"))
51
+ }
52
+ }
53
+
54
+ impl From<RecurramError> for BridgeError {
55
+ fn from(value: RecurramError) -> Self {
56
+ Self::new(value.to_string())
57
+ }
58
+ }
59
+
60
+ #[derive(Debug, Clone, Serialize, Deserialize)]
61
+ #[serde(tag = "t", content = "v")]
62
+ pub enum TransportValue {
63
+ #[serde(rename = "null")]
64
+ Null,
65
+ #[serde(rename = "bool")]
66
+ Bool(bool),
67
+ #[serde(rename = "i64")]
68
+ I64(String),
69
+ #[serde(rename = "u64")]
70
+ U64(String),
71
+ #[serde(rename = "f64")]
72
+ F64(f64),
73
+ #[serde(rename = "string")]
74
+ String(String),
75
+ #[serde(rename = "binary")]
76
+ Binary(String),
77
+ #[serde(rename = "array")]
78
+ Array(Vec<TransportValue>),
79
+ #[serde(rename = "map")]
80
+ Map(Vec<(String, TransportValue)>),
81
+ }
82
+
83
+ #[derive(Debug, Clone, Deserialize)]
84
+ #[serde(untagged)]
85
+ enum U64Like {
86
+ Number(u64),
87
+ String(String),
88
+ }
89
+
90
+ impl U64Like {
91
+ fn parse(self, field_name: &'static str) -> Result<u64> {
92
+ match self {
93
+ Self::Number(value) => Ok(value),
94
+ Self::String(raw) => raw
95
+ .parse::<u64>()
96
+ .map_err(|_| BridgeError::new(format!("invalid {field_name}: expected u64"))),
97
+ }
98
+ }
99
+ }
100
+
101
+ #[derive(Debug, Clone, Deserialize)]
102
+ #[serde(untagged)]
103
+ enum I64Like {
104
+ Number(i64),
105
+ String(String),
106
+ }
107
+
108
+ impl I64Like {
109
+ fn parse(self, field_name: &'static str) -> Result<i64> {
110
+ match self {
111
+ Self::Number(value) => Ok(value),
112
+ Self::String(raw) => raw
113
+ .parse::<i64>()
114
+ .map_err(|_| BridgeError::new(format!("invalid {field_name}: expected i64"))),
115
+ }
116
+ }
117
+ }
118
+
119
+ #[derive(Debug, Clone, Deserialize)]
120
+ #[serde(rename_all = "camelCase")]
121
+ struct TransportSchema {
122
+ schema_id: U64Like,
123
+ name: String,
124
+ fields: Vec<TransportSchemaField>,
125
+ }
126
+
127
+ #[derive(Debug, Clone, Deserialize)]
128
+ #[serde(rename_all = "camelCase")]
129
+ struct TransportSchemaField {
130
+ number: U64Like,
131
+ name: String,
132
+ logical_type: String,
133
+ required: bool,
134
+ #[serde(default)]
135
+ default_value: Option<TransportValue>,
136
+ #[serde(default)]
137
+ min: Option<I64Like>,
138
+ #[serde(default)]
139
+ max: Option<I64Like>,
140
+ #[serde(default)]
141
+ enum_values: Vec<String>,
142
+ }
143
+
144
+ #[derive(Debug, Clone, Deserialize, Default)]
145
+ #[serde(rename_all = "camelCase")]
146
+ struct TransportSessionOptions {
147
+ #[serde(default)]
148
+ max_base_snapshots: Option<usize>,
149
+ #[serde(default)]
150
+ enable_state_patch: Option<bool>,
151
+ #[serde(default)]
152
+ enable_template_batch: Option<bool>,
153
+ #[serde(default)]
154
+ enable_trained_dictionary: Option<bool>,
155
+ #[serde(default)]
156
+ unknown_reference_policy: Option<String>,
157
+ }
158
+
159
+ pub fn encode_transport_json(mut value_json: String) -> Result<Vec<u8>> {
160
+ let bytes = unsafe { value_json.as_bytes_mut() };
161
+ let transport: TransportValue = simd_from_mut_slice(bytes)?;
162
+ let value = transport_to_value(transport)?;
163
+ encode(&value).map_err(Into::into)
164
+ }
165
+
166
+ pub fn decode_to_transport_json(bytes: &[u8]) -> Result<String> {
167
+ let value = decode(bytes)?;
168
+ let transport = value_to_transport(value);
169
+ serde_json::to_string(&transport).map_err(Into::into)
170
+ }
171
+
172
+ pub fn decode_to_compact_json(bytes: &[u8]) -> Result<String> {
173
+ let value = decode(bytes)?;
174
+ let mut out = String::with_capacity(256);
175
+ value_to_compact_json_str(&value, &mut out);
176
+ Ok(out)
177
+ }
178
+
179
+ pub fn encode_with_schema_transport_json(
180
+ mut schema_json: String,
181
+ mut value_json: String,
182
+ ) -> Result<Vec<u8>> {
183
+ let schema = parse_schema_json(&mut schema_json)?;
184
+ let vbytes = unsafe { value_json.as_bytes_mut() };
185
+ let transport: TransportValue = simd_from_mut_slice(vbytes)?;
186
+ let value = transport_to_value(transport)?;
187
+ encode_with_schema(&schema, &value).map_err(Into::into)
188
+ }
189
+
190
+ pub fn encode_batch_transport_json(mut values_json: String) -> Result<Vec<u8>> {
191
+ let bytes = unsafe { values_json.as_bytes_mut() };
192
+ let transports: Vec<TransportValue> = simd_from_mut_slice(bytes)?;
193
+ let values: Vec<Value> = transports
194
+ .into_iter()
195
+ .map(transport_to_value)
196
+ .collect::<Result<Vec<_>>>()?;
197
+ encode_batch(&values).map_err(Into::into)
198
+ }
199
+
200
+ pub struct BridgeSessionEncoder {
201
+ inner: SessionEncoder,
202
+ }
203
+
204
+ impl BridgeSessionEncoder {
205
+ pub fn new(options_json: Option<&str>) -> Result<Self> {
206
+ let options = parse_session_options_json(options_json)?;
207
+ Ok(Self {
208
+ inner: create_session_encoder(options),
209
+ })
210
+ }
211
+
212
+ pub fn encode_transport_json(&mut self, mut value_json: String) -> Result<Vec<u8>> {
213
+ let bytes = unsafe { value_json.as_bytes_mut() };
214
+ let transport: TransportValue = simd_from_mut_slice(bytes)?;
215
+ let value = transport_to_value(transport)?;
216
+ self.inner.encode(&value).map_err(Into::into)
217
+ }
218
+
219
+ pub fn encode_with_schema_transport_json(
220
+ &mut self,
221
+ mut schema_json: String,
222
+ mut value_json: String,
223
+ ) -> Result<Vec<u8>> {
224
+ let schema = parse_schema_json(&mut schema_json)?;
225
+ let vbytes = unsafe { value_json.as_bytes_mut() };
226
+ let transport: TransportValue = simd_from_mut_slice(vbytes)?;
227
+ let value = transport_to_value(transport)?;
228
+ self.inner
229
+ .encode_with_schema(&schema, &value)
230
+ .map_err(Into::into)
231
+ }
232
+
233
+ pub fn encode_batch_transport_json(&mut self, mut values_json: String) -> Result<Vec<u8>> {
234
+ let bytes = unsafe { values_json.as_bytes_mut() };
235
+ let transports: Vec<TransportValue> = simd_from_mut_slice(bytes)?;
236
+ let values: Vec<Value> = transports
237
+ .into_iter()
238
+ .map(transport_to_value)
239
+ .collect::<Result<Vec<_>>>()?;
240
+ self.inner.encode_batch(&values).map_err(Into::into)
241
+ }
242
+
243
+ pub fn encode_patch_transport_json(&mut self, mut value_json: String) -> Result<Vec<u8>> {
244
+ let bytes = unsafe { value_json.as_bytes_mut() };
245
+ let transport: TransportValue = simd_from_mut_slice(bytes)?;
246
+ let value = transport_to_value(transport)?;
247
+ self.inner.encode_patch(&value).map_err(Into::into)
248
+ }
249
+
250
+ pub fn encode_micro_batch_transport_json(
251
+ &mut self,
252
+ mut values_json: String,
253
+ ) -> Result<Vec<u8>> {
254
+ let bytes = unsafe { values_json.as_bytes_mut() };
255
+ let transports: Vec<TransportValue> = simd_from_mut_slice(bytes)?;
256
+ let values: Vec<Value> = transports
257
+ .into_iter()
258
+ .map(transport_to_value)
259
+ .collect::<Result<Vec<_>>>()?;
260
+ self.inner.encode_micro_batch(&values).map_err(Into::into)
261
+ }
262
+
263
+ pub fn reset(&mut self) {
264
+ self.inner.reset();
265
+ }
266
+ }
267
+
268
+ fn parse_schema_json(schema_json: &mut String) -> Result<Schema> {
269
+ let bytes = unsafe { schema_json.as_bytes_mut() };
270
+ let schema: TransportSchema = simd_from_mut_slice(bytes)?;
271
+ transport_schema_to_schema(schema)
272
+ }
273
+
274
+ fn parse_session_options_json(options_json: Option<&str>) -> Result<SessionOptions> {
275
+ let Some(raw) = options_json else {
276
+ return Ok(SessionOptions::default());
277
+ };
278
+ // Session options parsing is cold path, use serde_json
279
+ let options: TransportSessionOptions = serde_json::from_str(raw)?;
280
+ transport_session_options_to_options(options)
281
+ }
282
+
283
+ fn transport_schema_to_schema(schema: TransportSchema) -> Result<Schema> {
284
+ let schema_id = schema.schema_id.parse("schemaId")?;
285
+ let fields = schema
286
+ .fields
287
+ .into_iter()
288
+ .map(|field| {
289
+ Ok(SchemaField {
290
+ number: field.number.parse("field.number")?,
291
+ name: field.name,
292
+ logical_type: field.logical_type,
293
+ required: field.required,
294
+ default_value: field.default_value.map(transport_to_value).transpose()?,
295
+ min: field.min.map(|v| v.parse("field.min")).transpose()?,
296
+ max: field.max.map(|v| v.parse("field.max")).transpose()?,
297
+ enum_values: field.enum_values,
298
+ })
299
+ })
300
+ .collect::<Result<Vec<_>>>()?;
301
+ Ok(Schema {
302
+ schema_id,
303
+ name: schema.name,
304
+ fields,
305
+ })
306
+ }
307
+
308
+ fn transport_session_options_to_options(
309
+ options: TransportSessionOptions,
310
+ ) -> Result<SessionOptions> {
311
+ let mut parsed = SessionOptions::default();
312
+ if let Some(value) = options.max_base_snapshots {
313
+ parsed.max_base_snapshots = value;
314
+ }
315
+ if let Some(value) = options.enable_state_patch {
316
+ parsed.enable_state_patch = value;
317
+ }
318
+ if let Some(value) = options.enable_template_batch {
319
+ parsed.enable_template_batch = value;
320
+ }
321
+ if let Some(value) = options.enable_trained_dictionary {
322
+ parsed.enable_trained_dictionary = value;
323
+ }
324
+ if let Some(policy) = options.unknown_reference_policy {
325
+ parsed.unknown_reference_policy = parse_unknown_reference_policy(&policy)?;
326
+ }
327
+ Ok(parsed)
328
+ }
329
+
330
+ fn parse_unknown_reference_policy(value: &str) -> Result<UnknownReferencePolicy> {
331
+ match value {
332
+ "failFast" | "fail_fast" | "FailFast" => Ok(UnknownReferencePolicy::FailFast),
333
+ "statelessRetry" | "stateless_retry" | "StatelessRetry" => {
334
+ Ok(UnknownReferencePolicy::StatelessRetry)
335
+ }
336
+ _ => Err(BridgeError::new(
337
+ "unknownReferencePolicy must be failFast or statelessRetry",
338
+ )),
339
+ }
340
+ }
341
+
342
+ // ── Direct API (bypasses JSON string intermediate) ──────────────────────────
343
+
344
+ pub fn encode_direct(transport: TransportValue) -> Result<Vec<u8>> {
345
+ let value = transport_to_value(transport)?;
346
+ encode(&value).map_err(Into::into)
347
+ }
348
+
349
+ pub fn encode_direct_from_json(jv: serde_json::Value) -> Result<Vec<u8>> {
350
+ let value = parse_value_from_json_value(jv)?;
351
+ encode(&value).map_err(Into::into)
352
+ }
353
+
354
+ pub fn decode_direct(bytes: &[u8]) -> Result<TransportValue> {
355
+ let value = decode(bytes)?;
356
+ Ok(value_to_transport(value))
357
+ }
358
+
359
+ pub fn encode_batch_direct(transports: Vec<TransportValue>) -> Result<Vec<u8>> {
360
+ let values: Vec<Value> = transports
361
+ .into_iter()
362
+ .map(transport_to_value)
363
+ .collect::<Result<Vec<_>>>()?;
364
+ encode_batch(&values).map_err(Into::into)
365
+ }
366
+
367
+ pub fn encode_batch_direct_from_json(jv: serde_json::Value) -> Result<Vec<u8>> {
368
+ let values = parse_values_from_json_value(jv)?;
369
+ encode_batch(&values).map_err(Into::into)
370
+ }
371
+
372
+ impl BridgeSessionEncoder {
373
+ pub fn encode_direct(&mut self, transport: TransportValue) -> Result<Vec<u8>> {
374
+ let value = transport_to_value(transport)?;
375
+ self.inner.encode(&value).map_err(Into::into)
376
+ }
377
+
378
+ pub fn encode_direct_from_json(&mut self, jv: serde_json::Value) -> Result<Vec<u8>> {
379
+ let value = parse_value_from_json_value(jv)?;
380
+ self.inner.encode(&value).map_err(Into::into)
381
+ }
382
+
383
+ pub fn encode_batch_direct(&mut self, transports: Vec<TransportValue>) -> Result<Vec<u8>> {
384
+ let values: Vec<Value> = transports
385
+ .into_iter()
386
+ .map(transport_to_value)
387
+ .collect::<Result<Vec<_>>>()?;
388
+ self.inner.encode_batch(&values).map_err(Into::into)
389
+ }
390
+
391
+ pub fn encode_batch_direct_from_json(&mut self, jv: serde_json::Value) -> Result<Vec<u8>> {
392
+ let values = parse_values_from_json_value(jv)?;
393
+ self.inner.encode_batch(&values).map_err(Into::into)
394
+ }
395
+
396
+ pub fn encode_patch_direct(&mut self, transport: TransportValue) -> Result<Vec<u8>> {
397
+ let value = transport_to_value(transport)?;
398
+ self.inner.encode_patch(&value).map_err(Into::into)
399
+ }
400
+
401
+ pub fn encode_patch_direct_from_json(&mut self, jv: serde_json::Value) -> Result<Vec<u8>> {
402
+ let value = parse_value_from_json_value(jv)?;
403
+ self.inner.encode_patch(&value).map_err(Into::into)
404
+ }
405
+
406
+ pub fn encode_micro_batch_direct(
407
+ &mut self,
408
+ transports: Vec<TransportValue>,
409
+ ) -> Result<Vec<u8>> {
410
+ let values: Vec<Value> = transports
411
+ .into_iter()
412
+ .map(transport_to_value)
413
+ .collect::<Result<Vec<_>>>()?;
414
+ self.inner.encode_micro_batch(&values).map_err(Into::into)
415
+ }
416
+
417
+ pub fn encode_micro_batch_direct_from_json(
418
+ &mut self,
419
+ jv: serde_json::Value,
420
+ ) -> Result<Vec<u8>> {
421
+ let values = parse_values_from_json_value(jv)?;
422
+ self.inner.encode_micro_batch(&values).map_err(Into::into)
423
+ }
424
+ }
425
+
426
+ fn transport_to_value(value: TransportValue) -> Result<Value> {
427
+ match value {
428
+ TransportValue::Null => Ok(Value::Null),
429
+ TransportValue::Bool(v) => Ok(Value::Bool(v)),
430
+ TransportValue::I64(raw) => raw
431
+ .parse::<i64>()
432
+ .map(Value::I64)
433
+ .map_err(|_| BridgeError::new("invalid i64 value")),
434
+ TransportValue::U64(raw) => raw
435
+ .parse::<u64>()
436
+ .map(Value::U64)
437
+ .map_err(|_| BridgeError::new("invalid u64 value")),
438
+ TransportValue::F64(v) => Ok(Value::F64(v)),
439
+ TransportValue::String(v) => Ok(Value::String(v)),
440
+ TransportValue::Binary(raw) => BASE64
441
+ .decode(raw)
442
+ .map(Value::Binary)
443
+ .map_err(|_| BridgeError::new("invalid base64 binary payload")),
444
+ TransportValue::Array(values) => values
445
+ .into_iter()
446
+ .map(transport_to_value)
447
+ .collect::<Result<Vec<_>>>()
448
+ .map(Value::Array),
449
+ TransportValue::Map(entries) => entries
450
+ .into_iter()
451
+ .map(|(k, v)| transport_to_value(v).map(|vv| (k, vv)))
452
+ .collect::<Result<Vec<_>>>()
453
+ .map(Value::Map),
454
+ }
455
+ }
456
+
457
+ fn value_to_transport(value: Value) -> TransportValue {
458
+ match value {
459
+ Value::Null => TransportValue::Null,
460
+ Value::Bool(v) => TransportValue::Bool(v),
461
+ Value::I64(v) => TransportValue::I64(v.to_string()),
462
+ Value::U64(v) => TransportValue::U64(v.to_string()),
463
+ Value::F64(v) => TransportValue::F64(v),
464
+ Value::String(v) => TransportValue::String(v),
465
+ Value::Binary(v) => TransportValue::Binary(BASE64.encode(v)),
466
+ Value::Array(values) => {
467
+ TransportValue::Array(values.into_iter().map(value_to_transport).collect())
468
+ }
469
+ Value::Map(entries) => TransportValue::Map(
470
+ entries
471
+ .into_iter()
472
+ .map(|(k, v)| (k, value_to_transport(v)))
473
+ .collect(),
474
+ ),
475
+ }
476
+ }
477
+
478
+ // ── Compact transport format ────────────────────────────────────────────────
479
+ //
480
+ // Tags: 0=null, 1=bool, 2=i64, 3=u64, 4=f64, 5=string, 6=binary, 7=array, 8=map
481
+ // Format: [tag] for null, [tag, value] for everything else.
482
+ // Map value is a flat array: [key1, val1, key2, val2, ...]
483
+ //
484
+ // Uses a custom serde Deserialize impl for single-pass JSON → Value conversion.
485
+
486
+ /// Serialize a recurram::Value to compact JSON format, writing directly to a String.
487
+ /// This avoids building any intermediate serde or transport objects.
488
+ fn value_to_compact_json_str(value: &Value, out: &mut String) {
489
+ match value {
490
+ Value::Null => out.push_str("[0]"),
491
+ Value::Bool(true) => out.push_str("[1,true]"),
492
+ Value::Bool(false) => out.push_str("[1,false]"),
493
+ Value::I64(v) => {
494
+ out.push_str("[2,\"");
495
+ let mut buf = itoa::Buffer::new();
496
+ out.push_str(buf.format(*v));
497
+ out.push_str("\"]");
498
+ }
499
+ Value::U64(v) => {
500
+ out.push_str("[3,\"");
501
+ let mut buf = itoa::Buffer::new();
502
+ out.push_str(buf.format(*v));
503
+ out.push_str("\"]");
504
+ }
505
+ Value::F64(v) => {
506
+ out.push_str("[4,");
507
+ // Use ryu for fast f64 formatting
508
+ let mut buf = ryu::Buffer::new();
509
+ out.push_str(buf.format(*v));
510
+ out.push(']');
511
+ }
512
+ Value::String(s) => {
513
+ out.push_str("[5,");
514
+ // JSON-escape the string
515
+ write_json_string(s, out);
516
+ out.push(']');
517
+ }
518
+ Value::Binary(b) => {
519
+ out.push_str("[6,\"");
520
+ out.push_str(&BASE64.encode(b));
521
+ out.push_str("\"]");
522
+ }
523
+ Value::Array(items) => {
524
+ out.push_str("[7,[");
525
+ for (i, item) in items.iter().enumerate() {
526
+ if i > 0 {
527
+ out.push(',');
528
+ }
529
+ value_to_compact_json_str(item, out);
530
+ }
531
+ out.push_str("]]");
532
+ }
533
+ Value::Map(entries) => {
534
+ out.push_str("[8,[");
535
+ for (i, (key, val)) in entries.iter().enumerate() {
536
+ if i > 0 {
537
+ out.push(',');
538
+ }
539
+ write_json_string(key, out);
540
+ out.push(',');
541
+ value_to_compact_json_str(val, out);
542
+ }
543
+ out.push_str("]]");
544
+ }
545
+ }
546
+ }
547
+
548
+ /// Write a JSON-escaped string (with surrounding quotes) to the output.
549
+ /// Optimized for the common case of ASCII strings with no special characters.
550
+ #[inline]
551
+ fn write_json_string(s: &str, out: &mut String) {
552
+ out.push('"');
553
+ let bytes = s.as_bytes();
554
+ let mut start = 0;
555
+ for (i, &b) in bytes.iter().enumerate() {
556
+ let escape = match b {
557
+ b'"' => "\\\"",
558
+ b'\\' => "\\\\",
559
+ b'\n' => "\\n",
560
+ b'\r' => "\\r",
561
+ b'\t' => "\\t",
562
+ b if b < 0x20 => {
563
+ // Flush any buffered bytes
564
+ if start < i {
565
+ out.push_str(&s[start..i]);
566
+ }
567
+ // Write \u00XX escape
568
+ static HEX: &[u8; 16] = b"0123456789abcdef";
569
+ let hi = HEX[(b >> 4) as usize] as char;
570
+ let lo = HEX[(b & 0xf) as usize] as char;
571
+ out.push_str("\\u00");
572
+ out.push(hi);
573
+ out.push(lo);
574
+ start = i + 1;
575
+ continue;
576
+ }
577
+ _ => {
578
+ continue;
579
+ }
580
+ };
581
+ // Flush any buffered bytes before the escape
582
+ if start < i {
583
+ out.push_str(&s[start..i]);
584
+ }
585
+ out.push_str(escape);
586
+ start = i + 1;
587
+ }
588
+ // Flush remaining
589
+ if start < bytes.len() {
590
+ out.push_str(&s[start..]);
591
+ }
592
+ out.push('"');
593
+ }
594
+
595
+ struct CompactValue(Value);
596
+
597
+ impl<'de> Deserialize<'de> for CompactValue {
598
+ fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
599
+ deserializer
600
+ .deserialize_seq(CompactValueVisitor)
601
+ .map(CompactValue)
602
+ }
603
+ }
604
+
605
+ struct CompactValueVisitor;
606
+
607
+ impl<'de> Visitor<'de> for CompactValueVisitor {
608
+ type Value = Value;
609
+
610
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
611
+ formatter.write_str("a compact value array [tag, value?]")
612
+ }
613
+
614
+ fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> std::result::Result<Value, A::Error> {
615
+ let tag: u64 = seq
616
+ .next_element()?
617
+ .ok_or_else(|| de::Error::custom("compact value missing tag"))?;
618
+
619
+ match tag {
620
+ 0 => {
621
+ // null — no content element
622
+ Ok(Value::Null)
623
+ }
624
+ 1 => {
625
+ // bool
626
+ let v: bool = seq
627
+ .next_element()?
628
+ .ok_or_else(|| de::Error::custom("compact bool missing value"))?;
629
+ Ok(Value::Bool(v))
630
+ }
631
+ 2 => {
632
+ // i64 (as string)
633
+ let s: &str = seq
634
+ .next_element()?
635
+ .ok_or_else(|| de::Error::custom("compact i64 missing value"))?;
636
+ let v: i64 = s
637
+ .parse()
638
+ .map_err(|_| de::Error::custom("compact i64 invalid"))?;
639
+ Ok(Value::I64(v))
640
+ }
641
+ 3 => {
642
+ // u64 (as string)
643
+ let s: &str = seq
644
+ .next_element()?
645
+ .ok_or_else(|| de::Error::custom("compact u64 missing value"))?;
646
+ let v: u64 = s
647
+ .parse()
648
+ .map_err(|_| de::Error::custom("compact u64 invalid"))?;
649
+ Ok(Value::U64(v))
650
+ }
651
+ 4 => {
652
+ // f64
653
+ let v: f64 = seq
654
+ .next_element()?
655
+ .ok_or_else(|| de::Error::custom("compact f64 missing value"))?;
656
+ Ok(Value::F64(v))
657
+ }
658
+ 5 => {
659
+ // string
660
+ let v: String = seq
661
+ .next_element()?
662
+ .ok_or_else(|| de::Error::custom("compact string missing value"))?;
663
+ Ok(Value::String(v))
664
+ }
665
+ 6 => {
666
+ // binary (base64)
667
+ let encoded: &str = seq
668
+ .next_element()?
669
+ .ok_or_else(|| de::Error::custom("compact binary missing value"))?;
670
+ let bytes = BASE64
671
+ .decode(encoded)
672
+ .map_err(|_| de::Error::custom("compact binary invalid base64"))?;
673
+ Ok(Value::Binary(bytes))
674
+ }
675
+ 7 => {
676
+ // array of CompactValues
677
+ let items: Vec<CompactValue> = seq
678
+ .next_element()?
679
+ .ok_or_else(|| de::Error::custom("compact array missing value"))?;
680
+ Ok(Value::Array(items.into_iter().map(|v| v.0).collect()))
681
+ }
682
+ 8 => {
683
+ // map: flat array [key1, val1, key2, val2, ...]
684
+ // We deserialize this as a custom visitor that reads alternating string/CompactValue pairs
685
+ let entries: CompactMapEntries = seq
686
+ .next_element()?
687
+ .ok_or_else(|| de::Error::custom("compact map missing value"))?;
688
+ Ok(Value::Map(entries.0))
689
+ }
690
+ _ => Err(de::Error::custom(format!("compact unknown tag: {tag}"))),
691
+ }
692
+ }
693
+ }
694
+
695
+ /// Deserializes a flat array [key1, val1, key2, val2, ...] into Vec<(String, Value)>
696
+ struct CompactMapEntries(Vec<(String, Value)>);
697
+
698
+ impl<'de> Deserialize<'de> for CompactMapEntries {
699
+ fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
700
+ deserializer.deserialize_seq(CompactMapEntriesVisitor)
701
+ }
702
+ }
703
+
704
+ struct CompactMapEntriesVisitor;
705
+
706
+ impl<'de> Visitor<'de> for CompactMapEntriesVisitor {
707
+ type Value = CompactMapEntries;
708
+
709
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
710
+ formatter.write_str("a flat array of alternating key/value pairs")
711
+ }
712
+
713
+ fn visit_seq<A: de::SeqAccess<'de>>(
714
+ self,
715
+ mut seq: A,
716
+ ) -> std::result::Result<CompactMapEntries, A::Error> {
717
+ let mut entries = Vec::with_capacity(seq.size_hint().unwrap_or(0) / 2);
718
+ loop {
719
+ let key: Option<String> = seq.next_element()?;
720
+ let Some(key) = key else { break };
721
+ let val: CompactValue = seq
722
+ .next_element()?
723
+ .ok_or_else(|| de::Error::custom("compact map missing value for key"))?;
724
+ entries.push((key, val.0));
725
+ }
726
+ Ok(CompactMapEntries(entries))
727
+ }
728
+ }
729
+
730
+ fn parse_compact_str(json: &mut [u8]) -> Result<Value> {
731
+ let compact: CompactValue = simd_json::from_slice(json)
732
+ .map_err(|e| BridgeError::new(format!("simd-json compact parse error: {e}")))?;
733
+ Ok(compact.0)
734
+ }
735
+
736
+ fn parse_compact_batch_str(json: &mut [u8]) -> Result<Vec<Value>> {
737
+ let items: Vec<CompactValue> = simd_json::from_slice(json)
738
+ .map_err(|e| BridgeError::new(format!("simd-json compact batch parse error: {e}")))?;
739
+ Ok(items.into_iter().map(|v| v.0).collect())
740
+ }
741
+
742
+ pub fn encode_compact_json(mut json: String) -> Result<Vec<u8>> {
743
+ let bytes = unsafe { json.as_bytes_mut() };
744
+ let value = parse_compact_str(bytes)?;
745
+ encode(&value).map_err(Into::into)
746
+ }
747
+
748
+ pub fn encode_batch_compact_json(mut json: String) -> Result<Vec<u8>> {
749
+ let bytes = unsafe { json.as_bytes_mut() };
750
+ let values = parse_compact_batch_str(bytes)?;
751
+ encode_batch(&values).map_err(Into::into)
752
+ }
753
+
754
+ impl BridgeSessionEncoder {
755
+ pub fn encode_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
756
+ let bytes = unsafe { json.as_bytes_mut() };
757
+ let value = parse_compact_str(bytes)?;
758
+ self.inner.encode(&value).map_err(Into::into)
759
+ }
760
+
761
+ pub fn encode_batch_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
762
+ let bytes = unsafe { json.as_bytes_mut() };
763
+ let values = parse_compact_batch_str(bytes)?;
764
+ self.inner.encode_batch(&values).map_err(Into::into)
765
+ }
766
+
767
+ pub fn encode_patch_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
768
+ let bytes = unsafe { json.as_bytes_mut() };
769
+ let value = parse_compact_str(bytes)?;
770
+ self.inner.encode_patch(&value).map_err(Into::into)
771
+ }
772
+
773
+ pub fn encode_micro_batch_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
774
+ let bytes = unsafe { json.as_bytes_mut() };
775
+ let values = parse_compact_batch_str(bytes)?;
776
+ self.inner.encode_micro_batch(&values).map_err(Into::into)
777
+ }
778
+ }
779
+ //
780
+ // Deserializes the transport JSON format directly into recurram::Value,
781
+ // skipping the TransportValue intermediate entirely. This avoids:
782
+ // 1. Allocating TransportValue tree
783
+ // 2. Walking the tree a second time in transport_to_value()
784
+ // 3. String allocations for i64/u64 intermediates
785
+
786
+ struct FastValueVisitor;
787
+
788
+ impl<'de> Visitor<'de> for FastValueVisitor {
789
+ type Value = Value;
790
+
791
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
792
+ formatter.write_str("a transport value object with 't' and optional 'v' fields")
793
+ }
794
+
795
+ fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> std::result::Result<Value, A::Error> {
796
+ let mut tag: Option<String> = None;
797
+ let mut content_value: Option<Box<RawValue>> = None;
798
+
799
+ while let Some(key) = map.next_key::<String>()? {
800
+ match key.as_str() {
801
+ "t" => {
802
+ tag = Some(map.next_value()?);
803
+ }
804
+ "v" => {
805
+ content_value = Some(map.next_value()?);
806
+ }
807
+ _ => {
808
+ map.next_value::<serde::de::IgnoredAny>()?;
809
+ }
810
+ }
811
+ }
812
+
813
+ let tag = tag.ok_or_else(|| de::Error::missing_field("t"))?;
814
+
815
+ match tag.as_str() {
816
+ "null" => Ok(Value::Null),
817
+ "bool" => {
818
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
819
+ let v: bool = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
820
+ Ok(Value::Bool(v))
821
+ }
822
+ "i64" => {
823
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
824
+ let s: &str = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
825
+ let v: i64 = s.parse().map_err(|_| de::Error::custom("invalid i64"))?;
826
+ Ok(Value::I64(v))
827
+ }
828
+ "u64" => {
829
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
830
+ let s: &str = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
831
+ let v: u64 = s.parse().map_err(|_| de::Error::custom("invalid u64"))?;
832
+ Ok(Value::U64(v))
833
+ }
834
+ "f64" => {
835
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
836
+ let v: f64 = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
837
+ Ok(Value::F64(v))
838
+ }
839
+ "string" => {
840
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
841
+ let v: String =
842
+ serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
843
+ Ok(Value::String(v))
844
+ }
845
+ "binary" => {
846
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
847
+ let encoded: String =
848
+ serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
849
+ let bytes = BASE64
850
+ .decode(&encoded)
851
+ .map_err(|_| de::Error::custom("invalid base64"))?;
852
+ Ok(Value::Binary(bytes))
853
+ }
854
+ "array" => {
855
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
856
+ let items: Vec<FastValue> =
857
+ serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
858
+ Ok(Value::Array(items.into_iter().map(|v| v.0).collect()))
859
+ }
860
+ "map" => {
861
+ let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
862
+ let entries: Vec<(String, FastValue)> =
863
+ serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
864
+ Ok(Value::Map(
865
+ entries.into_iter().map(|(k, v)| (k, v.0)).collect(),
866
+ ))
867
+ }
868
+ other => Err(de::Error::unknown_variant(
869
+ other,
870
+ &[
871
+ "null", "bool", "i64", "u64", "f64", "string", "binary", "array", "map",
872
+ ],
873
+ )),
874
+ }
875
+ }
876
+ }
877
+
878
+ /// A newtype wrapper around `Value` that implements `Deserialize` using the
879
+ /// fast single-pass visitor that converts transport JSON directly to `Value`.
880
+ struct FastValue(Value);
881
+
882
+ impl<'de> Deserialize<'de> for FastValue {
883
+ fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
884
+ deserializer
885
+ .deserialize_map(FastValueVisitor)
886
+ .map(FastValue)
887
+ }
888
+ }
889
+
890
+ /// Parse a serde_json::Value transport representation directly into recurram::Value.
891
+ fn parse_value_from_json_value(jv: serde_json::Value) -> Result<Value> {
892
+ let fast: FastValue = serde_json::from_value(jv)?;
893
+ Ok(fast.0)
894
+ }
895
+
896
+ /// Parse a serde_json::Value array of transport values.
897
+ fn parse_values_from_json_value(jv: serde_json::Value) -> Result<Vec<Value>> {
898
+ let items: Vec<FastValue> = serde_json::from_value(jv)?;
899
+ Ok(items.into_iter().map(|v| v.0).collect())
900
+ }