Skip to main content

oxihuman_core/
packed_array.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5pub struct PackedArray {
6    pub data: Vec<u64>,
7    pub bits: usize,
8    pub len: usize,
9}
10
11pub fn new_packed_array(len: usize, bits: usize) -> PackedArray {
12    let bits = bits.clamp(1, 64);
13    let total_bits = len * bits;
14    let words = total_bits.div_ceil(64);
15    PackedArray {
16        data: vec![0u64; words.max(1)],
17        bits,
18        len,
19    }
20}
21
22pub fn packed_get(a: &PackedArray, i: usize) -> u64 {
23    if i >= a.len {
24        return 0;
25    }
26    let bit_idx = i * a.bits;
27    let word = bit_idx / 64;
28    let offset = bit_idx % 64;
29    let mask = if a.bits == 64 {
30        u64::MAX
31    } else {
32        (1u64 << a.bits) - 1
33    };
34    if offset + a.bits <= 64 {
35        (a.data[word] >> offset) & mask
36    } else {
37        let lo = a.data[word] >> offset;
38        let hi_bits = a.bits - (64 - offset);
39        let hi = if word + 1 < a.data.len() {
40            a.data[word + 1] << (64 - offset)
41        } else {
42            0
43        };
44        let hi_mask = (1u64 << hi_bits) - 1;
45        lo | ((hi >> (64 - offset - hi_bits)) & hi_mask) << (64 - offset)
46    }
47}
48
49pub fn packed_set(a: &mut PackedArray, i: usize, val: u64) {
50    if i >= a.len {
51        return;
52    }
53    let bit_idx = i * a.bits;
54    let word = bit_idx / 64;
55    let offset = bit_idx % 64;
56    let mask = if a.bits == 64 {
57        u64::MAX
58    } else {
59        (1u64 << a.bits) - 1
60    };
61    let val = val & mask;
62    a.data[word] &= !(mask << offset);
63    a.data[word] |= val << offset;
64    if offset + a.bits > 64 && word + 1 < a.data.len() {
65        let spill = a.bits - (64 - offset);
66        let spill_mask = (1u64 << spill) - 1;
67        a.data[word + 1] &= !spill_mask;
68        a.data[word + 1] |= val >> (a.bits - spill);
69    }
70}
71
72pub fn packed_len(a: &PackedArray) -> usize {
73    a.len
74}
75pub fn packed_bits_per_elem(a: &PackedArray) -> usize {
76    a.bits
77}
78pub fn packed_storage_bytes(a: &PackedArray) -> usize {
79    a.data.len() * 8
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn set_and_get() {
88        /* set a value and read it back */
89        let mut a = new_packed_array(10, 4);
90        packed_set(&mut a, 3, 0b1010);
91        assert_eq!(packed_get(&a, 3), 0b1010);
92    }
93
94    #[test]
95    fn initial_zero() {
96        /* all elements are zero after creation */
97        let a = new_packed_array(8, 3);
98        for i in 0..8 {
99            assert_eq!(packed_get(&a, i), 0);
100        }
101    }
102
103    #[test]
104    fn multiple_elements() {
105        /* multiple elements stored independently */
106        let mut a = new_packed_array(4, 8);
107        packed_set(&mut a, 0, 10);
108        packed_set(&mut a, 1, 20);
109        assert_eq!(packed_get(&a, 0), 10);
110        assert_eq!(packed_get(&a, 1), 20);
111    }
112
113    #[test]
114    fn len_correct() {
115        /* packed_len returns the configured length */
116        let a = new_packed_array(7, 5);
117        assert_eq!(packed_len(&a), 7);
118    }
119
120    #[test]
121    fn bits_per_elem() {
122        /* bits_per_elem returns configured bit width */
123        let a = new_packed_array(5, 6);
124        assert_eq!(packed_bits_per_elem(&a), 6);
125    }
126
127    #[test]
128    fn storage_bytes_positive() {
129        /* storage is non-zero for non-empty array */
130        let a = new_packed_array(10, 4);
131        assert!(packed_storage_bytes(&a) > 0);
132    }
133
134    #[test]
135    fn out_of_bounds_get() {
136        /* out-of-bounds get returns 0 safely */
137        let a = new_packed_array(4, 4);
138        assert_eq!(packed_get(&a, 100), 0);
139    }
140}