Skip to main content

protocache_core/
serialize.rs

1//! Serialization surface matching `serialize.h`.
2
3use std::time::{SystemTime, UNIX_EPOCH};
4
5pub use crate::Buffer;
6
7use crate::Scalar;
8use crate::hash::hash128;
9
10#[derive(Clone, Copy, Debug, Default)]
11/// A contiguous range stored in a reverse-growing [`Buffer`].
12pub struct Segment {
13    /// Position measured from the end of the buffer.
14    pub pos: usize,
15    /// Segment length in `u32` words.
16    pub len: usize,
17}
18
19impl Segment {
20    #[inline(always)]
21    pub fn end(self) -> usize {
22        self.pos - self.len
23    }
24}
25
26#[derive(Clone, Copy, Debug, Default)]
27/// Intermediate representation of an encoded field.
28///
29/// Values up to three words can remain inline; larger values refer to a
30/// [`Segment`] already written into a [`Buffer`].
31pub struct Unit {
32    inline_len: usize,
33    inline_data: [u32; 3],
34    segment: Segment,
35}
36
37impl Unit {
38    #[inline(always)]
39    pub fn empty() -> Self {
40        Self::default()
41    }
42
43    #[inline(always)]
44    pub fn inline(words: &[u32]) -> Self {
45        assert!(words.len() <= 3);
46        let mut data = [0u32; 3];
47        data[..words.len()].copy_from_slice(words);
48        Self {
49            inline_len: words.len(),
50            inline_data: data,
51            segment: Segment::default(),
52        }
53    }
54
55    #[inline(always)]
56    pub fn segment(last: usize, now: usize) -> Self {
57        Self {
58            inline_len: 0,
59            inline_data: [0; 3],
60            segment: Segment {
61                pos: now,
62                len: now - last,
63            },
64        }
65    }
66
67    #[inline(always)]
68    pub fn size(&self) -> usize {
69        if self.inline_len != 0 {
70            self.inline_len
71        } else {
72            self.segment.len
73        }
74    }
75
76    #[inline(always)]
77    pub fn is_empty(&self) -> bool {
78        self.size() == 0
79    }
80
81    #[inline(always)]
82    pub fn is_segment(&self) -> bool {
83        self.inline_len == 0 && self.segment.len != 0
84    }
85
86    #[inline(always)]
87    pub fn inline_words(&self) -> &[u32] {
88        &self.inline_data[..self.inline_len]
89    }
90
91    #[inline(always)]
92    pub fn segment_info(&self) -> Segment {
93        self.segment
94    }
95}
96
97#[inline(always)]
98fn write_varint(buf: &mut [u8; 5], mut n: u32) -> usize {
99    let mut written = 0usize;
100    while (n & !0x7f) != 0 {
101        buf[written] = 0x80 | (n as u8 & 0x7f);
102        written += 1;
103        n >>= 7;
104    }
105    buf[written] = n as u8;
106    written + 1
107}
108
109#[inline(always)]
110fn offset(off: usize) -> u32 {
111    ((off as u32) << 2) | 3
112}
113
114#[inline(always)]
115fn copy_inline(body: &mut [u32], body_index: &mut usize, pos: &mut usize, field: &Unit) {
116    let len = field.inline_len;
117    body[*body_index..*body_index + len].copy_from_slice(field.inline_words());
118    *body_index += len;
119    *pos -= len;
120}
121
122#[inline(always)]
123fn unit_words<'a>(unit: &'a Unit, buffer: &'a Buffer) -> Option<&'a [u32]> {
124    if unit.inline_len != 0 {
125        return Some(unit.inline_words());
126    }
127    if unit.segment.len == 0 {
128        return Some(&[]);
129    }
130    let start = buffer.len().checked_sub(unit.segment.pos)?;
131    buffer.view().get(start..start + unit.segment.len)
132}
133
134#[inline(always)]
135fn pick_unit(unit: &mut Unit, buffer: &mut Buffer, tail: &mut usize, width: usize) {
136    if unit.inline_len != 0 {
137        return;
138    }
139
140    let seg = unit.segment;
141    if seg.len <= width {
142        let start = buffer.len() - seg.pos;
143        let words = &buffer.head()[start..start + seg.len];
144        unit.inline_len = seg.len;
145        unit.inline_data[..seg.len].copy_from_slice(words);
146        return;
147    }
148
149    let start = buffer.len() - seg.pos;
150    let end = start + seg.len;
151    if *tail > end {
152        let new_start = *tail - seg.len;
153        buffer.head_mut().copy_within(start..end, new_start);
154        unit.segment.pos -= new_start - start;
155        *tail = new_start;
156    } else {
157        debug_assert!(*tail >= seg.len);
158        *tail -= seg.len;
159    }
160}
161
162#[inline(always)]
163fn mark_unit(unit: &Unit, buffer: &mut Buffer, width: usize) {
164    let expanded_len = buffer.len() + width;
165    let cell = buffer.expand(width);
166    if unit.inline_len != 0 {
167        cell[..unit.inline_len].copy_from_slice(unit.inline_words());
168        for word in &mut cell[unit.inline_len..] {
169            *word = 0;
170        }
171    } else {
172        cell[0] = offset(expanded_len - unit.segment.pos);
173        for word in &mut cell[1..] {
174            *word = 0;
175        }
176    }
177}
178
179#[inline(always)]
180fn best_array_size(elements: &[Unit]) -> (usize, usize) {
181    let mut sizes = [0usize; 3];
182    for element in elements {
183        sizes[0] += 1;
184        sizes[1] += 2;
185        sizes[2] += 3;
186        let len = element.size();
187        if len <= 1 {
188            continue;
189        }
190        sizes[0] += len;
191        if len <= 2 {
192            continue;
193        }
194        sizes[1] += len;
195        if len <= 3 {
196            continue;
197        }
198        sizes[2] += len;
199    }
200
201    let mut mode = 0usize;
202    for idx in 1..3 {
203        if sizes[idx] < sizes[mode] {
204            mode = idx;
205        }
206    }
207    (sizes[mode], mode + 1)
208}
209
210#[inline(always)]
211fn best_array_size_pairs(elements: &[(Unit, Unit)]) -> ((usize, usize), (usize, usize)) {
212    let mut key_sizes = [0usize; 3];
213    let mut value_sizes = [0usize; 3];
214    for (key, value) in elements {
215        for sizes in [&mut key_sizes, &mut value_sizes] {
216            sizes[0] += 1;
217            sizes[1] += 2;
218            sizes[2] += 3;
219        }
220
221        let key_len = key.size();
222        if key_len > 1 {
223            key_sizes[0] += key_len;
224            if key_len > 2 {
225                key_sizes[1] += key_len;
226                if key_len > 3 {
227                    key_sizes[2] += key_len;
228                }
229            }
230        }
231
232        let value_len = value.size();
233        if value_len > 1 {
234            value_sizes[0] += value_len;
235            if value_len > 2 {
236                value_sizes[1] += value_len;
237                if value_len > 3 {
238                    value_sizes[2] += value_len;
239                }
240            }
241        }
242    }
243
244    let mut key_mode = 0usize;
245    let mut value_mode = 0usize;
246    for idx in 1..3 {
247        if key_sizes[idx] < key_sizes[key_mode] {
248            key_mode = idx;
249        }
250        if value_sizes[idx] < value_sizes[value_mode] {
251            value_mode = idx;
252        }
253    }
254
255    (
256        (key_sizes[key_mode], key_mode + 1),
257        (value_sizes[value_mode], value_mode + 1),
258    )
259}
260
261#[inline(always)]
262fn perfect_hash_section(size: usize) -> usize {
263    ((size * 105).saturating_add(255) / 256).max(10)
264}
265
266#[inline(always)]
267fn perfect_hash_bitmap_size(section: usize) -> usize {
268    ((section * 3 + 31) & !31) / 4
269}
270
271#[inline(always)]
272fn set_bit2(vec: &mut [u8], pos: usize, val: u8) {
273    let shift = ((pos & 3) << 1) as u8;
274    let idx = pos >> 2;
275    vec[idx] &= !(3u8 << shift);
276    vec[idx] |= (val & 3) << shift;
277}
278
279#[inline(always)]
280fn get_bit2(vec: &[u8], pos: usize) -> u8 {
281    (vec[pos >> 2] >> ((pos & 3) << 1)) & 3
282}
283
284#[derive(Clone, Copy)]
285struct Edge {
286    slots: [usize; 3],
287}
288
289#[inline(always)]
290fn next_seed(state: &mut [u32; 4]) -> u32 {
291    let t = state[0] ^ (state[0] << 11);
292    state[0] = state[1];
293    state[1] = state[2];
294    state[2] = state[3];
295    state[3] ^= (state[3] >> 19) ^ t ^ (t >> 8);
296    state[3]
297}
298
299#[inline(always)]
300fn peel_graph(edges: &[Edge], slot_cnt: usize) -> Option<Vec<usize>> {
301    const NONE: usize = usize::MAX;
302
303    #[derive(Clone, Copy)]
304    struct Vertex {
305        slot: usize,
306        prev: usize,
307        next: usize,
308    }
309
310    let mut heads = vec![NONE; slot_cnt];
311    let mut vertices = vec![
312        Vertex {
313            slot: NONE,
314            prev: NONE,
315            next: NONE,
316        };
317        edges.len() * 3
318    ];
319
320    for (edge_idx, edge) in edges.iter().enumerate() {
321        for (slot_index, &slot) in edge.slots.iter().enumerate() {
322            let node = edge_idx * 3 + slot_index;
323            let head = heads[slot];
324            vertices[node] = Vertex {
325                slot,
326                prev: NONE,
327                next: head,
328            };
329            if head != NONE {
330                vertices[head].prev = node;
331            }
332            heads[slot] = node;
333        }
334    }
335
336    let mut queue = Vec::with_capacity(slot_cnt);
337    for (slot, &head) in heads.iter().enumerate() {
338        if head != NONE && vertices[head].next == NONE {
339            queue.push(slot);
340        }
341    }
342    let mut queue_head = 0usize;
343
344    let mut order = Vec::with_capacity(edges.len());
345
346    while let Some(&slot) = queue.get(queue_head) {
347        queue_head += 1;
348        let head = heads[slot];
349        if head == NONE || vertices[head].next != NONE {
350            continue;
351        }
352        let edge_idx = head / 3;
353        order.push(edge_idx);
354
355        for offset in 0..3 {
356            let node = edge_idx * 3 + offset;
357            let vertex = vertices[node];
358            if vertex.slot == NONE {
359                continue;
360            }
361
362            if vertex.prev != NONE {
363                vertices[vertex.prev].next = vertex.next;
364            } else {
365                heads[vertex.slot] = vertex.next;
366            }
367            if vertex.next != NONE {
368                vertices[vertex.next].prev = vertex.prev;
369            }
370
371            let head = heads[vertex.slot];
372            if head != NONE && vertices[head].next == NONE {
373                queue.push(vertex.slot);
374            }
375            vertices[node].slot = NONE;
376        }
377    }
378
379    if order.len() == edges.len() {
380        Some(order)
381    } else {
382        None
383    }
384}
385
386#[inline(always)]
387fn count_valid_slots(bitmap: &[u8], block: usize) -> usize {
388    let start = block * 8;
389    let bits = u64::from_le_bytes(
390        bitmap[start..start + 8]
391            .try_into()
392            .expect("bitmap block must fit"),
393    );
394    count_valid_slots_in_word(bits)
395}
396
397#[inline(always)]
398fn locate_in_perfect_hash(index: &[u8], key: &[u8]) -> Option<usize> {
399    let size = u32::from_le_bytes(index.get(..4)?.try_into().ok()?) as usize & 0x0fff_ffff;
400    if size < 2 {
401        return Some(0);
402    }
403    let section = perfect_hash_section(size);
404    let section_u32 = section as u32;
405    let section_magic = fast_mod_magic(section_u32);
406    let bitmap_size = perfect_hash_bitmap_size(section);
407    let bitmap = index.get(8..8 + bitmap_size)?;
408    let table = index.get(8 + bitmap_size..)?;
409    let seed = u32::from_le_bytes(index.get(4..8)?.try_into().ok()?) as u64;
410    let code = hash128(key, seed);
411    let slots = [
412        fast_mod_u32(code[0], section_u32, section_magic) as usize,
413        fast_mod_u32(code[1], section_u32, section_magic) as usize + section,
414        fast_mod_u32(code[2], section_u32, section_magic) as usize + section * 2,
415    ];
416    let m = get_bit2(bitmap, slots[0]) as usize
417        + get_bit2(bitmap, slots[1]) as usize
418        + get_bit2(bitmap, slots[2]) as usize;
419    let slot = slots[m % 3];
420    let block = slot >> 5;
421    let bit = slot & 31;
422
423    let off = if size > u16::MAX as usize {
424        u32::from_le_bytes(table.get(block * 4..block * 4 + 4)?.try_into().ok()?) as usize
425    } else if size > u8::MAX as usize {
426        u16::from_le_bytes(table.get(block * 2..block * 2 + 2)?.try_into().ok()?) as usize
427    } else if size > 24 {
428        *table.get(block)? as usize
429    } else {
430        0
431    };
432
433    let word_start = block * 8;
434    let bits = u64::from_le_bytes(bitmap.get(word_start..word_start + 8)?.try_into().ok()?);
435    let masked = bits | (u64::MAX << (bit << 1));
436    Some(off + count_valid_slots_in_word(masked))
437}
438
439#[inline(always)]
440fn count_valid_slots_in_word(bits: u64) -> usize {
441    let invalid = ((bits & 0x5555_5555_5555_5555) & (bits >> 1)).count_ones() as usize;
442    32 - invalid
443}
444
445#[inline(always)]
446fn fast_mod_magic(divisor: u32) -> u64 {
447    u64::MAX / divisor as u64 + 1
448}
449
450#[inline(always)]
451fn fast_mod_u32(value: u32, divisor: u32, magic: u64) -> u32 {
452    let low = magic.wrapping_mul(value as u64);
453    (((low as u128) * divisor as u128) >> 64) as u32
454}
455
456#[inline(always)]
457/// Builds a ProtoCache perfect-hash index for unique encoded keys.
458///
459/// Returns `None` if no valid index can be constructed, including duplicate
460/// key layouts that cannot be represented.
461pub fn build_perfect_hash_index<K: AsRef<[u8]>>(keys: &[K]) -> Option<Vec<u8>> {
462    Some(build_perfect_hash_index_with_positions(keys)?.0)
463}
464
465#[inline(always)]
466/// Builds a perfect-hash index and reports each input key's storage position.
467pub fn build_perfect_hash_index_with_positions<K: AsRef<[u8]>>(
468    keys: &[K],
469) -> Option<(Vec<u8>, Vec<usize>)> {
470    let total = keys.len();
471    if total >= (1usize << 28) {
472        return None;
473    }
474    if total <= 1 {
475        return Some(((total as u32).to_le_bytes().to_vec(), vec![0; total]));
476    }
477
478    let section = perfect_hash_section(total);
479    let section_u32 = section as u32;
480    let section_magic = fast_mod_magic(section_u32);
481    let slot_cnt = section * 3;
482    let bitmap_size = perfect_hash_bitmap_size(section);
483
484    let clock_seed = SystemTime::now()
485        .duration_since(UNIX_EPOCH)
486        .unwrap_or_else(|error| error.duration())
487        .as_nanos() as u32;
488    let mut rand32 = [0x6c07_8965, 0x9908_b0df, 0x9d2c_5680, clock_seed];
489    let tries = if total <= u8::MAX as usize { 40 } else { 16 };
490    let mut result = None;
491    for _ in 0..tries {
492        let seed = next_seed(&mut rand32);
493        let edges: Vec<_> = keys
494            .iter()
495            .map(|key| {
496                let code = hash128(key.as_ref(), seed as u64);
497                Edge {
498                    slots: [
499                        fast_mod_u32(code[0], section_u32, section_magic) as usize,
500                        fast_mod_u32(code[1], section_u32, section_magic) as usize + section,
501                        fast_mod_u32(code[2], section_u32, section_magic) as usize + section * 2,
502                    ],
503                }
504            })
505            .collect();
506        if let Some(order) = peel_graph(&edges, slot_cnt) {
507            result = Some((seed, edges, order));
508            break;
509        }
510    }
511    let (seed, edges, order) = result?;
512
513    let mut bitmap = vec![0xffu8; bitmap_size];
514    let mut taken = vec![false; slot_cnt];
515    for &edge_idx in order.iter().rev() {
516        let [a, b, c] = edges[edge_idx].slots;
517        let (chosen, target) = if !taken[a] {
518            taken[a] = true;
519            taken[b] = true;
520            taken[c] = true;
521            (a, 0u8)
522        } else if !taken[b] {
523            taken[b] = true;
524            taken[c] = true;
525            (b, 1u8)
526        } else {
527            taken[c] = true;
528            (c, 2u8)
529        };
530
531        let sum = match chosen {
532            x if x == a => get_bit2(&bitmap, b) as u16 + get_bit2(&bitmap, c) as u16,
533            x if x == b => get_bit2(&bitmap, a) as u16 + get_bit2(&bitmap, c) as u16,
534            _ => get_bit2(&bitmap, a) as u16 + get_bit2(&bitmap, b) as u16,
535        };
536        let chosen_val = ((target as i16 - (sum % 3) as i16 + 3) % 3) as u8;
537        set_bit2(&mut bitmap, chosen, chosen_val);
538    }
539
540    let mut out = Vec::with_capacity(8 + bitmap_size + bitmap_size / 2);
541    out.extend_from_slice(&(total as u32).to_le_bytes());
542    out.extend_from_slice(&seed.to_le_bytes());
543    out.extend_from_slice(&bitmap);
544
545    if total > u16::MAX as usize {
546        let mut cnt = 0u32;
547        for block in 0..(bitmap_size / 8) {
548            out.extend_from_slice(&cnt.to_le_bytes());
549            cnt += count_valid_slots(&bitmap, block) as u32;
550        }
551    } else if total > u8::MAX as usize {
552        let mut cnt = 0u16;
553        for block in 0..(bitmap_size / 8) {
554            out.extend_from_slice(&cnt.to_le_bytes());
555            cnt += count_valid_slots(&bitmap, block) as u16;
556        }
557    } else if total > 24 {
558        let mut cnt = 0u8;
559        for block in 0..(bitmap_size / 8) {
560            out.push(cnt);
561            cnt += count_valid_slots(&bitmap, block) as u8;
562        }
563    }
564
565    let mut positions = Vec::with_capacity(keys.len());
566    for key in keys.iter() {
567        positions.push(locate_in_perfect_hash(&out, key.as_ref())?);
568    }
569
570    Some((out, positions))
571}
572
573#[inline(always)]
574/// Moves a small buffered segment into the inline storage of `unit` when possible.
575pub fn fold_field(buffer: &mut Buffer, unit: &mut Unit) {
576    if !unit.is_segment() || unit.segment.len >= 4 || unit.segment.pos != buffer.len() {
577        return;
578    }
579    let seg = unit.segment;
580    let head = buffer.view();
581    unit.inline_len = seg.len;
582    unit.inline_data[..seg.len].copy_from_slice(&head[..seg.len]);
583    unit.segment = Segment::default();
584    buffer.shrink(seg.len);
585}
586
587#[inline(always)]
588/// Encodes a scalar as an inline field unit.
589pub fn serialize_scalar<T: Scalar>(value: T) -> Unit {
590    let mut words = [0u32; 3];
591    let word_len = T::WIDTH;
592    value
593        .write_words(&mut words[..word_len])
594        .expect("scalar width must match");
595    Unit {
596        inline_len: word_len,
597        inline_data: words,
598        segment: Segment::default(),
599    }
600}
601
602#[inline(always)]
603/// Encodes a boolean as an inline field unit.
604pub fn serialize_bool(value: bool) -> Unit {
605    Unit::inline(&[u32::from(value)])
606}
607
608#[inline(always)]
609/// Encodes a byte string into `buffer`.
610///
611/// Short payloads may be returned inline. `None` indicates an encoded-size or
612/// offset overflow.
613pub fn serialize_bytes(bytes: &[u8], buffer: &mut Buffer) -> Option<Unit> {
614    if bytes.len() >= (1usize << 30) {
615        return None;
616    }
617    let mark = (bytes.len() as u32) << 2;
618    let mut header = [0u8; 5];
619    let header_size = write_varint(&mut header, mark);
620    let total_words = (header_size + bytes.len()).div_ceil(4);
621
622    let last = buffer.len();
623    let mut inline = [0u32; 3];
624    if total_words == 1 {
625        let raw = unsafe { core::slice::from_raw_parts_mut(inline.as_mut_ptr().cast::<u8>(), 4) };
626        raw.fill(0);
627        raw[..header_size].copy_from_slice(&header[..header_size]);
628        raw[header_size..header_size + bytes.len()].copy_from_slice(bytes);
629        Some(Unit {
630            inline_len: 1,
631            inline_data: inline,
632            segment: Segment::default(),
633        })
634    } else {
635        let words = buffer.expand(total_words);
636        words[total_words - 1] = 0;
637        let raw = unsafe {
638            core::slice::from_raw_parts_mut(words.as_mut_ptr().cast::<u8>(), total_words * 4)
639        };
640        raw[..header_size].copy_from_slice(&header[..header_size]);
641        raw[header_size..header_size + bytes.len()].copy_from_slice(bytes);
642        Some(Unit::segment(last, buffer.len()))
643    }
644}
645
646#[inline(always)]
647/// Encodes a UTF-8 string using the ProtoCache byte-string representation.
648pub fn serialize_str(value: &str, buffer: &mut Buffer) -> Option<Unit> {
649    serialize_bytes(value.as_bytes(), buffer)
650}
651
652#[inline(always)]
653/// Encodes message fields, restricting referenced segments to data written
654/// since the caller-provided `last` buffer length.
655pub fn serialize_message_at(fields: &mut [Unit], buffer: &mut Buffer, last: usize) -> Option<Unit> {
656    if fields.is_empty() {
657        return None;
658    }
659    let used_len = fields
660        .iter()
661        .rposition(|unit| !unit.is_empty())
662        .map(|index| index + 1)
663        .unwrap_or(0);
664    if used_len == 0 {
665        let last = buffer.len();
666        buffer.put(0);
667        return Some(Unit::segment(last, buffer.len()));
668    }
669    let fields = &mut fields[..used_len];
670
671    let mut body_size = 0usize;
672    let mut size = 0usize;
673    for field in fields.iter().rev() {
674        if field.inline_len != 0 {
675            body_size += field.inline_len;
676            size += field.inline_len;
677        } else if field.segment.len != 0 {
678            body_size += 1;
679            size += field.segment.len;
680        }
681    }
682    if size >= (1usize << 30) {
683        return None;
684    }
685
686    let section = (fields.len() + 12) / 25;
687    if section > 0xff {
688        return None;
689    }
690
691    let head_size = 1 + section * 2;
692    let current_size = buffer.len();
693    let total_size = current_size + head_size + body_size;
694    let block = buffer.expand(head_size + body_size);
695    let (head, body) = block.split_at_mut(head_size);
696    let mut body_index = 0usize;
697    let mut pos = total_size - head_size;
698    head.fill(0);
699    head[0] = section as u32;
700
701    let mut cnt = 0u32;
702    for (i, field) in fields.iter().take(12).enumerate() {
703        if field.inline_len != 0 {
704            head[0] |= (field.inline_len as u32) << (8 + i * 2);
705            cnt += field.inline_len as u32;
706            copy_inline(body, &mut body_index, &mut pos, field);
707        } else if field.segment.len != 0 {
708            head[0] |= 1u32 << (8 + i * 2);
709            cnt += 1;
710            body[body_index] = offset(pos - field.segment.pos);
711            body_index += 1;
712            pos -= 1;
713        }
714    }
715
716    for sec in 0..section {
717        let start = 12 + sec * 25;
718        let end = fields.len().min(start + 25);
719        let mut mark = (cnt as u64) << 50;
720        let mut shift = 0usize;
721        for field in &fields[start..end] {
722            if field.inline_len != 0 {
723                mark |= (field.inline_len as u64) << shift;
724                cnt += field.inline_len as u32;
725                copy_inline(body, &mut body_index, &mut pos, field);
726            } else if field.segment.len != 0 {
727                mark |= 1u64 << shift;
728                cnt += 1;
729                body[body_index] = offset(pos - field.segment.pos);
730                body_index += 1;
731                pos -= 1;
732            }
733            shift += 2;
734        }
735        head[1 + sec * 2] = mark as u32;
736        head[1 + sec * 2 + 1] = (mark >> 32) as u32;
737    }
738
739    Some(Unit::segment(last, buffer.len()))
740}
741
742#[inline(always)]
743/// Encodes a message from zero-based field units.
744pub fn serialize_message(fields: &mut [Unit], buffer: &mut Buffer) -> Option<Unit> {
745    serialize_message_at(fields, buffer, buffer.len())
746}
747
748#[inline(always)]
749/// Encodes array elements written since `last`, updating their segment metadata.
750pub fn serialize_array_at_mut(
751    elements: &mut [Unit],
752    buffer: &mut Buffer,
753    last: usize,
754) -> Option<Unit> {
755    if elements.is_empty() {
756        return Some(Unit::inline(&[1]));
757    }
758
759    let (size, width) = best_array_size(elements);
760    if size >= (1usize << 30) {
761        return None;
762    }
763
764    let mut tail = buffer.len().checked_sub(last)?;
765    for unit in elements.iter_mut().rev() {
766        pick_unit(unit, buffer, &mut tail, width);
767    }
768    buffer.shrink(tail);
769    for unit in elements.iter().rev() {
770        mark_unit(unit, buffer, width);
771    }
772    buffer.put(((elements.len() as u32) << 2) | width as u32);
773    Some(Unit::segment(last, buffer.len()))
774}
775
776#[inline(always)]
777/// Encodes array elements written since the caller-provided `last` length.
778pub fn serialize_array_at(elements: &[Unit], buffer: &mut Buffer, last: usize) -> Option<Unit> {
779    if elements.is_empty() {
780        return Some(Unit::inline(&[1]));
781    }
782
783    let (size, width) = best_array_size(elements);
784    if size >= (1usize << 30) {
785        return None;
786    }
787
788    let mut payloads = Vec::with_capacity(size.saturating_sub(elements.len() * width));
789    let mut cells = vec![0u32; elements.len() * width];
790    let cells_len = cells.len();
791    for (index, unit) in elements.iter().enumerate() {
792        let words = unit_words(unit, buffer)?;
793        let cell = &mut cells[index * width..(index + 1) * width];
794        if words.len() <= width {
795            cell[..words.len()].copy_from_slice(words);
796        } else {
797            let payload_start = cells_len.checked_add(payloads.len())?;
798            let cell_start = index.checked_mul(width)?;
799            cell[0] = offset(payload_start.checked_sub(cell_start)?);
800            payloads.extend_from_slice(words);
801        }
802    }
803    buffer.shrink(buffer.len().checked_sub(last)?);
804    buffer.put_words(&payloads);
805    buffer.put_words(&cells);
806    buffer.put(((elements.len() as u32) << 2) | width as u32);
807    Some(Unit::segment(last, buffer.len()))
808}
809
810#[inline(always)]
811/// Encodes an array from element units.
812pub fn serialize_array(elements: &[Unit], buffer: &mut Buffer) -> Option<Unit> {
813    serialize_array_at(elements, buffer, buffer.len())
814}
815
816#[inline(always)]
817/// Encodes map key/value units and their perfect-hash index from mutable inputs.
818pub fn serialize_map_at_mut(
819    index: &[u8],
820    keys: &mut [Unit],
821    values: &mut [Unit],
822    buffer: &mut Buffer,
823    last: usize,
824) -> Option<Unit> {
825    if keys.len() != values.len() {
826        return None;
827    }
828    if keys.is_empty() {
829        return Some(Unit::inline(&[5u32 << 28]));
830    }
831
832    let (key_size, key_width) = best_array_size(keys);
833    let (value_size, value_width) = best_array_size(values);
834    let index_words = index.len().div_ceil(4);
835    let size = index_words + key_size + value_size;
836    if size >= (1usize << 30) {
837        return None;
838    }
839
840    let mut tail = buffer.len().checked_sub(last)?;
841    for index in (0..keys.len()).rev() {
842        pick_unit(&mut values[index], buffer, &mut tail, value_width);
843        pick_unit(&mut keys[index], buffer, &mut tail, key_width);
844    }
845    buffer.shrink(tail);
846    for index in (0..keys.len()).rev() {
847        mark_unit(&values[index], buffer, value_width);
848        mark_unit(&keys[index], buffer, key_width);
849    }
850
851    let head = buffer.expand(index_words);
852    head.fill(0);
853    let raw =
854        unsafe { core::slice::from_raw_parts_mut(head.as_mut_ptr().cast::<u8>(), index_words * 4) };
855    raw[..index.len()].copy_from_slice(index);
856    head[0] |= (key_width as u32) << 30 | (value_width as u32) << 28;
857    Some(Unit::segment(last, buffer.len()))
858}
859
860#[inline(always)]
861pub(crate) fn serialize_map_pairs_at_mut(
862    index: &[u8],
863    pairs: &mut [(Unit, Unit)],
864    buffer: &mut Buffer,
865    last: usize,
866) -> Option<Unit> {
867    if pairs.is_empty() {
868        return Some(Unit::inline(&[5u32 << 28]));
869    }
870
871    let ((key_size, key_width), (value_size, value_width)) = best_array_size_pairs(pairs);
872    let index_words = index.len().div_ceil(4);
873    let size = index_words + key_size + value_size;
874    if size >= (1usize << 30) {
875        return None;
876    }
877
878    let mut tail = buffer.len().checked_sub(last)?;
879    for (key, value) in pairs.iter_mut().rev() {
880        pick_unit(value, buffer, &mut tail, value_width);
881        pick_unit(key, buffer, &mut tail, key_width);
882    }
883    buffer.shrink(tail);
884    for (key, value) in pairs.iter().rev() {
885        mark_unit(value, buffer, value_width);
886        mark_unit(key, buffer, key_width);
887    }
888
889    let head = buffer.expand(index_words);
890    head.fill(0);
891    let raw =
892        unsafe { core::slice::from_raw_parts_mut(head.as_mut_ptr().cast::<u8>(), index_words * 4) };
893    raw[..index.len()].copy_from_slice(index);
894    head[0] |= (key_width as u32) << 30 | (value_width as u32) << 28;
895    Some(Unit::segment(last, buffer.len()))
896}
897
898#[inline(always)]
899/// Encodes map key/value units and their perfect-hash index using `last` as the
900/// boundary for referenced buffer segments.
901pub fn serialize_map_at(
902    index: &[u8],
903    keys: &[Unit],
904    values: &[Unit],
905    buffer: &mut Buffer,
906    last: usize,
907) -> Option<Unit> {
908    if keys.len() != values.len() {
909        return None;
910    }
911    if keys.is_empty() {
912        return Some(Unit::inline(&[5u32 << 28]));
913    }
914
915    let (key_size, key_width) = best_array_size(keys);
916    let (value_size, value_width) = best_array_size(values);
917    let index_words = index.len().div_ceil(4);
918    let size = index_words + key_size + value_size;
919    if size >= (1usize << 30) {
920        return None;
921    }
922
923    let pair_width = key_width + value_width;
924    let mut payloads = Vec::with_capacity(size.saturating_sub(keys.len() * pair_width));
925    let mut cells = vec![0u32; keys.len() * pair_width];
926    let cells_len = cells.len();
927    for index in 0..keys.len() {
928        let key_words = unit_words(&keys[index], buffer)?;
929        let value_words = unit_words(&values[index], buffer)?;
930        let cell_start = index.checked_mul(pair_width)?;
931        let (key_cell, value_cell) =
932            cells[cell_start..cell_start + pair_width].split_at_mut(key_width);
933
934        if key_words.len() <= key_width {
935            key_cell[..key_words.len()].copy_from_slice(key_words);
936        } else {
937            let payload_start = cells_len.checked_add(payloads.len())?;
938            key_cell[0] = offset(payload_start.checked_sub(cell_start)?);
939            payloads.extend_from_slice(key_words);
940        }
941
942        let value_cell_start = cell_start + key_width;
943        if value_words.len() <= value_width {
944            value_cell[..value_words.len()].copy_from_slice(value_words);
945        } else {
946            let payload_start = cells_len.checked_add(payloads.len())?;
947            value_cell[0] = offset(payload_start.checked_sub(value_cell_start)?);
948            payloads.extend_from_slice(value_words);
949        }
950    }
951    buffer.shrink(buffer.len().checked_sub(last)?);
952    buffer.put_words(&payloads);
953    buffer.put_words(&cells);
954
955    let head = buffer.expand(index_words);
956    head.fill(0);
957    let raw =
958        unsafe { core::slice::from_raw_parts_mut(head.as_mut_ptr().cast::<u8>(), index_words * 4) };
959    raw[..index.len()].copy_from_slice(index);
960    head[0] |= (key_width as u32) << 30 | (value_width as u32) << 28;
961    Some(Unit::segment(last, buffer.len()))
962}
963
964#[inline(always)]
965/// Encodes a map from canonical key bytes and key/value field units.
966pub fn serialize_map(
967    index: &[u8],
968    keys: &[Unit],
969    values: &[Unit],
970    buffer: &mut Buffer,
971) -> Option<Unit> {
972    serialize_map_at(index, keys, values, buffer, buffer.len())
973}
974
975#[cfg(test)]
976mod tests {
977    use super::{
978        Unit, build_perfect_hash_index, build_perfect_hash_index_with_positions, fold_field,
979        serialize_array, serialize_array_at, serialize_bool, serialize_map, serialize_message,
980        serialize_scalar, serialize_str,
981    };
982    use crate::{ArrayView, Buffer, MapView, MessageView, StringView, ViewArray};
983
984    #[test]
985    fn serializes_inline_string() {
986        let mut buffer = Buffer::new();
987        let unit = serialize_str("hi", &mut buffer).unwrap();
988        assert_eq!(unit.inline_words().len(), 1);
989        assert!(buffer.is_empty());
990    }
991
992    #[test]
993    fn folds_segmented_field_when_small() {
994        let mut buffer = Buffer::new();
995        let mut unit = serialize_str("hello", &mut buffer).unwrap();
996        assert!(unit.is_segment());
997        fold_field(&mut buffer, &mut unit);
998        assert_eq!(unit.inline_words().len(), 2);
999        assert!(buffer.is_empty());
1000
1001        let mut small = serialize_str("abc", &mut buffer).unwrap();
1002        assert_eq!(small.inline_words().len(), 1);
1003        fold_field(&mut buffer, &mut small);
1004        assert_eq!(small.inline_words().len(), 1);
1005    }
1006
1007    #[test]
1008    fn serializes_simple_message_roundtrip() {
1009        let mut buffer = Buffer::new();
1010        let field0 = serialize_scalar::<i32>(42);
1011        let field1 = serialize_bool(true);
1012        let field3 = serialize_str("hi", &mut buffer).unwrap();
1013        let mut fields = vec![field0, field1, Unit::empty(), field3];
1014        let _message = serialize_message(&mut fields, &mut buffer).unwrap();
1015
1016        let view = MessageView::new(buffer.view()).unwrap();
1017        assert_eq!(view.scalar::<i32>(0), Some(42));
1018        assert_eq!(view.scalar::<bool>(1), Some(true));
1019        assert_eq!(view.string(3).unwrap().as_str(), Some("hi"));
1020        assert!(view.scalar::<i32>(2).is_none());
1021    }
1022
1023    #[test]
1024    fn serializes_all_empty_fields_as_empty_message() {
1025        let mut buffer = Buffer::new();
1026        let mut fields = vec![Unit::empty(), Unit::empty()];
1027        let _message = serialize_message(&mut fields, &mut buffer).unwrap();
1028        let view = MessageView::new(buffer.view()).unwrap();
1029        assert!(!view.has_field(0));
1030        assert!(!view.has_field(1));
1031    }
1032
1033    #[test]
1034    fn serializes_scalar_array_roundtrip() {
1035        let mut buffer = Buffer::new();
1036        let elements = vec![serialize_scalar::<i32>(1), serialize_scalar::<i32>(2)];
1037        let _array = serialize_array(&elements, &mut buffer).unwrap();
1038        let view = ArrayView::new(buffer.view()).unwrap();
1039        let scalars = view.scalars::<i32>().unwrap();
1040        assert_eq!(scalars.get(0), Some(1));
1041        assert_eq!(scalars.get(1), Some(2));
1042    }
1043
1044    #[test]
1045    fn serializes_string_array_roundtrip() {
1046        let mut buffer = Buffer::new();
1047        let a = serialize_str("abc", &mut buffer).unwrap();
1048        let b = serialize_str("apple", &mut buffer).unwrap();
1049        let elements = vec![a, b];
1050        let _array = serialize_array(&elements, &mut buffer).unwrap();
1051        let view = ArrayView::new(buffer.view()).unwrap();
1052        let strings = ViewArray::<StringView<'_>>::new(view);
1053        assert_eq!(strings.get(0).unwrap().as_str(), Some("abc"));
1054        assert_eq!(strings.get(1).unwrap().as_str(), Some("apple"));
1055    }
1056
1057    #[test]
1058    fn builds_index_and_serializes_string_int_map_roundtrip() {
1059        let keys_bytes = vec![b"abc-1".to_vec(), b"abc-2".to_vec()];
1060        let (index, positions) = build_perfect_hash_index_with_positions(&keys_bytes).unwrap();
1061
1062        let mut buffer = Buffer::new();
1063        let key0 = serialize_str("abc-1", &mut buffer).unwrap();
1064        let key1 = serialize_str("abc-2", &mut buffer).unwrap();
1065        let mut keys = vec![Unit::empty(); 2];
1066        let mut values = vec![Unit::empty(); 2];
1067        keys[positions[0]] = key0;
1068        keys[positions[1]] = key1;
1069        values[positions[0]] = serialize_scalar::<i32>(1);
1070        values[positions[1]] = serialize_scalar::<i32>(2);
1071        let _map = serialize_map(&index, &keys, &values, &mut buffer).unwrap();
1072
1073        let view = MapView::new(buffer.view()).unwrap();
1074        assert_eq!(
1075            view.find_str("abc-1").unwrap().value().scalar::<i32>(),
1076            Some(1)
1077        );
1078        assert_eq!(
1079            view.find_str("abc-2").unwrap().value().scalar::<i32>(),
1080            Some(2)
1081        );
1082        assert!(view.find_str("abc-3").is_none());
1083    }
1084
1085    #[test]
1086    fn index_builder_matches_reader_expectations() {
1087        let keys = vec![b"abc-1".to_vec(), b"abc-2".to_vec(), b"abc-4".to_vec()];
1088        let index = build_perfect_hash_index(&keys).unwrap();
1089        let mut seen = std::collections::BTreeSet::new();
1090        for key in &keys {
1091            seen.insert(super::locate_in_perfect_hash(&index, key).unwrap());
1092        }
1093        assert_eq!(seen.len(), keys.len());
1094    }
1095
1096    #[test]
1097    fn index_builder_rejects_duplicate_keys() {
1098        let keys = [b"duplicate".as_slice(), b"duplicate".as_slice()];
1099        assert!(build_perfect_hash_index(&keys).is_none());
1100        assert!(build_perfect_hash_index_with_positions(&keys).is_none());
1101    }
1102
1103    #[test]
1104    fn serializes_short_string_key_float_array_map_roundtrip() {
1105        let keys_bytes = vec![b"lv5".to_vec(), b"lv9".to_vec()];
1106        let (index, positions) = build_perfect_hash_index_with_positions(&keys_bytes).unwrap();
1107
1108        let mut buffer = Buffer::new();
1109        let key0 = serialize_str("lv5", &mut buffer).unwrap();
1110        let key1 = serialize_str("lv9", &mut buffer).unwrap();
1111
1112        let last = buffer.len();
1113        let value0 = serialize_array_at(
1114            &[
1115                serialize_scalar::<f32>(51.0),
1116                serialize_scalar::<f32>(52.0),
1117                serialize_scalar::<f32>(53.0),
1118            ],
1119            &mut buffer,
1120            last,
1121        )
1122        .unwrap();
1123        let last = buffer.len();
1124        let value1 = serialize_array_at(
1125            &[serialize_scalar::<f32>(91.0), serialize_scalar::<f32>(92.0)],
1126            &mut buffer,
1127            last,
1128        )
1129        .unwrap();
1130
1131        let mut keys = vec![Unit::empty(); 2];
1132        let mut values = vec![Unit::empty(); 2];
1133        keys[positions[0]] = key0;
1134        keys[positions[1]] = key1;
1135        values[positions[0]] = value0;
1136        values[positions[1]] = value1;
1137        let _map = serialize_map(&index, &keys, &values, &mut buffer).unwrap();
1138
1139        let view = MapView::new(buffer.view()).unwrap();
1140        let lv5 = view.find_str("lv5").unwrap().value().array().unwrap();
1141        assert_eq!(
1142            lv5.scalars::<f32>().unwrap().iter().collect::<Vec<_>>(),
1143            vec![51.0, 52.0, 53.0]
1144        );
1145        let lv9 = view.find_str("lv9").unwrap().value().array().unwrap();
1146        assert_eq!(
1147            lv9.scalars::<f32>().unwrap().iter().collect::<Vec<_>>(),
1148            vec![91.0, 92.0]
1149        );
1150    }
1151
1152    #[test]
1153    fn serializes_multi_entry_short_string_key_float_array_map_roundtrip() {
1154        let keys = ["lv1", "lv2", "lv3", "lv4", "lv5", "lv9"];
1155        let key_bytes = keys
1156            .iter()
1157            .map(|key| key.as_bytes().to_vec())
1158            .collect::<Vec<_>>();
1159        let (index, positions) = build_perfect_hash_index_with_positions(&key_bytes).unwrap();
1160
1161        let values_src = [
1162            vec![11.0f32, 12.0],
1163            vec![21.0, 22.0],
1164            vec![31.0, 32.0],
1165            vec![41.0, 42.0],
1166            vec![51.0, 52.0, 53.0],
1167            vec![91.0, 92.0],
1168        ];
1169
1170        let mut buffer = Buffer::new();
1171        let encoded_keys = keys
1172            .iter()
1173            .map(|key| serialize_str(key, &mut buffer).unwrap())
1174            .collect::<Vec<_>>();
1175        let encoded_values = values_src
1176            .iter()
1177            .map(|values| {
1178                let units = values
1179                    .iter()
1180                    .map(|value| serialize_scalar::<f32>(*value))
1181                    .collect::<Vec<_>>();
1182                serialize_array(&units, &mut buffer).unwrap()
1183            })
1184            .collect::<Vec<_>>();
1185
1186        let mut keys = vec![Unit::empty(); positions.len()];
1187        let mut values = vec![Unit::empty(); positions.len()];
1188        for (idx, pos) in positions.iter().copied().enumerate() {
1189            keys[pos] = encoded_keys[idx];
1190            values[pos] = encoded_values[idx];
1191        }
1192
1193        let _map = serialize_map(&index, &keys, &values, &mut buffer).unwrap();
1194        let view = MapView::new(buffer.view()).unwrap();
1195        for (key, expected) in ["lv1", "lv2", "lv3", "lv4", "lv5", "lv9"]
1196            .into_iter()
1197            .zip(values_src.iter())
1198        {
1199            let array = view.find_str(key).unwrap().value().array().unwrap();
1200            assert_eq!(
1201                array.scalars::<f32>().unwrap().iter().collect::<Vec<_>>(),
1202                expected.clone()
1203            );
1204        }
1205    }
1206}