chunkedge_binary/
var_long.rs1use std::io::Write;
2
3use anyhow::bail;
4use byteorder::ReadBytesExt;
5use derive_more::{From, Into};
6use serde::{Deserialize, Serialize};
7
8use crate::{Decode, Encode};
9
10#[derive(
12 Clone,
13 Copy,
14 Default,
15 PartialEq,
16 Eq,
17 PartialOrd,
18 Ord,
19 Hash,
20 Debug,
21 From,
22 Into,
23 Serialize,
24 Deserialize,
25)]
26#[serde(transparent)]
27#[repr(transparent)]
28pub struct VarLong(pub i64);
29
30impl VarLong {
31 pub const MAX_SIZE: usize = 10;
34
35 pub fn written_size(self) -> usize {
45 match self.0 {
46 0 => 1,
47 n => (63 - n.leading_zeros() as usize) / 7 + 1,
48 }
49 }
50}
51
52impl Encode for VarLong {
53 #[cfg(all(
56 any(target_arch = "x86", target_arch = "x86_64"),
57 not(target_os = "macos")
58 ))]
59 fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
60 #[cfg(target_arch = "x86")]
61 use std::arch::x86::*;
62 #[cfg(target_arch = "x86_64")]
63 use std::arch::x86_64::*;
64
65 let mut res = [0_u64; 2];
67 {
68 let x = self.0 as u64;
69
70 res[0] = unsafe { _pdep_u64(x, 0x7f7f7f7f7f7f7f7f) };
71 res[1] = unsafe { _pdep_u64(x >> 56, 0x000000000000017f) }
72 };
73 let stage1: __m128i = unsafe { std::mem::transmute(res) };
74
75 let minimum =
79 unsafe { _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff_u8 as i8) };
80 let exists = unsafe { _mm_or_si128(_mm_cmpgt_epi8(stage1, _mm_setzero_si128()), minimum) };
81 let bits = unsafe { _mm_movemask_epi8(exists) };
82
83 let bytes_needed = 32 - bits.leading_zeros() as u8; let ascend = unsafe { _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) };
88 let mask = unsafe { _mm_cmplt_epi8(ascend, _mm_set1_epi8(bytes_needed as i8)) };
89
90 let shift = unsafe { _mm_bsrli_si128(mask, 1) };
93 let msbmask = unsafe { _mm_and_si128(shift, _mm_set1_epi8(128_u8 as i8)) };
94
95 let merged = unsafe { _mm_or_si128(stage1, msbmask) };
97 let bytes = unsafe { std::mem::transmute::<__m128i, [u8; 16]>(merged) };
98
99 w.write_all(unsafe { bytes.get_unchecked(..bytes_needed as usize) })?;
100
101 Ok(())
102 }
103
104 #[cfg(any(
105 not(any(target_arch = "x86", target_arch = "x86_64")),
106 target_os = "macos"
107 ))]
108 fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
109 use byteorder::WriteBytesExt;
110
111 let mut val = self.0 as u64;
112 loop {
113 if val & 0b1111111111111111111111111111111111111111111111111111111110000000 == 0 {
114 w.write_u8(val as u8)?;
115 return Ok(());
116 }
117 w.write_u8(val as u8 & 0b01111111 | 0b10000000)?;
118 val >>= 7;
119 }
120 }
121}
122
123impl Decode<'_> for VarLong {
124 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
125 let mut val = 0;
126 for i in 0..Self::MAX_SIZE {
127 let byte = r.read_u8()?;
128 val |= (i64::from(byte) & 0b01111111) << (i * 7);
129 if byte & 0b10000000 == 0 {
130 return Ok(VarLong(val));
131 }
132 }
133 bail!("VarInt is too large")
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use rand::{RngExt, rng};
140
141 use super::*;
142
143 #[test]
144 fn encode_decode() {
145 let mut rng = rng();
146 let mut buf = vec![];
147
148 for n in (0..1_000_000)
149 .map(|_| rng.random::<i64>())
150 .chain([0, i64::MIN, i64::MAX])
151 {
152 VarLong(n).encode(&mut buf).unwrap();
153
154 let mut slice = buf.as_slice();
155 assert!(slice.len() <= VarLong::MAX_SIZE);
156
157 assert_eq!(n, VarLong::decode(&mut slice).unwrap().0);
158 assert!(slice.is_empty());
159 buf.clear();
160 }
161 }
162}