//! Varint spec for ZeroTrie: //! //! - Lead byte: top M (2 or 3) bits are metadata; next is varint extender; rest is value //! - Trail bytes: top bit is varint extender; rest are low bits of value //! - Guaranteed uniqueness of varint by adding "latent value" for each extender byte //! - No maximum, but high bits will be dropped if they don't fit in the platform's `usize` //! //! This is best shown by examples. //! //! ```txt //! xxx0'1010 = 10 //! xxx0'1111 = 15 (largest single-byte value with M=3) //! xxx1'0000 0000'0000 must be 16 (smallest two-byte value with M=3) //! xxx1'0000 0000'0001 = 17 //! xxx1'1111 0111'1111 = 2063 (largest two-byte value with M=3) //! xxx1'0000 1000'0000 0000'0000 must be 2064 (smallest three-byte value with M=3) //! xxx1'0000 1000'0000 0000'0001 = 2065 //! ``` //! //! The latent values by number of bytes for M=3 are: //! //! - 1 byte: 0 //! - 2 bytes: 16 = 0x10 = 0b10000 //! - 3 bytes: 2064 = 0x810 = 0b100000010000 //! - 4 bytes: 264208 = 0x40810 = 0b1000000100000010000 //! - 5 bytes: 33818640 = 0x2040810 = 0b10000001000000100000010000 //! - … //! //! For M=2, the latent values are: //! //! - 1 byte: 0 //! - 2 bytes: 32 = 0x20 = 0b100000 //! - 3 bytes: 4128 = 0x1020 = 0b1000000100000 //! - 4 bytes: 524320 = 0x81020 = 0b10000001000000100000 //! - 5 bytes: 67637280 = 0x4081020 = 0b100000010000001000000100000 //! - …
/// Reads a varint with 2 bits of metadata in the lead byte. /// /// Returns the varint value and a subslice of `remainder` with the varint bytes removed. /// /// If the varint spills off the end of the slice, a debug assertion will fail, /// and the function will return the value up to that point. pubconstfn read_varint_meta2(start: u8, remainder: &[u8]) -> (usize, &[u8]) { letmut value = (start & 0b00011111) as usize; letmut remainder = remainder; if (start & 0b00100000) != 0 { loop { let next;
(next, remainder) = debug_unwrap!(remainder.split_first(), break, "invalid varint"); // Note: value << 7 could drop high bits. The first addition can't overflow. // The second addition could overflow; in such a case we just inform the // developer via the debug assertion.
value = (value << 7) + ((*next & 0b01111111) as usize) + 32; if (*next & 0b10000000) == 0 { break;
}
}
}
(value, remainder)
}
/// Reads a varint with 3 bits of metadata in the lead byte. /// /// Returns the varint value and a subslice of `remainder` with the varint bytes removed. /// /// If the varint spills off the end of the slice, a debug assertion will fail, /// and the function will return the value up to that point. pubconstfn read_varint_meta3(start: u8, remainder: &[u8]) -> (usize, &[u8]) { letmut value = (start & 0b00001111) as usize; letmut remainder = remainder; if (start & 0b00010000) != 0 { loop { let next;
(next, remainder) = debug_unwrap!(remainder.split_first(), break, "invalid varint"); // Note: value << 7 could drop high bits. The first addition can't overflow. // The second addition could overflow; in such a case we just inform the // developer via the debug assertion.
value = (value << 7) + ((*next & 0b01111111) as usize) + 16; if (*next & 0b10000000) == 0 { break;
}
}
}
(value, remainder)
}
/// Reads and removes a varint with 3 bits of metadata from a [`TrieBuilderStore`]. /// /// Returns the varint value. #[cfg(feature = "alloc")] pub(crate) fn try_read_varint_meta3_from_tstore<S: TrieBuilderStore>(
start: u8,
remainder: &mut S,
) -> Option<usize> { letmut value = (start & 0b00001111) as usize; if (start & 0b00010000) != 0 { loop { let next = remainder.atbs_pop_front()?; // Note: value << 7 could drop high bits. The first addition can't overflow. // The second addition could overflow; in such a case we just inform the // developer via the debug assertion.
value = (value << 7) + ((next & 0b01111111) as usize) + 16; if (next & 0b10000000) == 0 { break;
}
}
}
Some(value)
}
// *Upper Bound:* Each trail byte stores 7 bits of data, plus the latent value. // Add an extra 1 since the lead byte holds only 5 bits of data. const MAX_VARINT_LENGTH: usize = 1 + core::mem::size_of::<usize>() * 8 / 7;
/// Returns a new [`ConstArrayBuilder`] containing a varint with 2 bits of metadata. pub(crate) constfn write_varint_meta2(value: usize) -> ConstArrayBuilder<MAX_VARINT_LENGTH, u8> { letmut result = [0; MAX_VARINT_LENGTH]; letmut i = MAX_VARINT_LENGTH - 1; letmut value = value; letmut last = true; loop { if value < 32 {
result[i] = value as u8; if !last {
result[i] |= 0b00100000;
} break;
}
value -= 32;
result[i] = (value as u8) & 0b01111111; if !last {
result[i] |= 0b10000000;
} else {
last = false;
}
value >>= 7;
i -= 1;
} // The bytes are from i to the end.
ConstArrayBuilder::from_manual_slice(result, i, MAX_VARINT_LENGTH)
}
/// Returns a new [`ConstArrayBuilder`] containing a varint with 3 bits of metadata. pub(crate) constfn write_varint_meta3(value: usize) -> ConstArrayBuilder<MAX_VARINT_LENGTH, u8> { letmut result = [0; MAX_VARINT_LENGTH]; letmut i = MAX_VARINT_LENGTH - 1; letmut value = value; letmut last = true; loop { if value < 16 {
result[i] = value as u8; if !last {
result[i] |= 0b00010000;
} break;
}
value -= 16;
result[i] = (value as u8) & 0b01111111; if !last {
result[i] |= 0b10000000;
} else {
last = false;
}
value >>= 7;
i -= 1;
} // The bytes are from i to the end.
ConstArrayBuilder::from_manual_slice(result, i, MAX_VARINT_LENGTH)
}
/// A secondary implementation that separates the latent value while computing the varint. #[cfg(test)] pub(crate) constfn write_varint_reference(
value: usize,
) -> ConstArrayBuilder<MAX_VARINT_LENGTH, u8> { letmut result = [0; MAX_VARINT_LENGTH]; if value < 32 {
result[0] = value as u8; return ConstArrayBuilder::from_manual_slice(result, 0, 1);
}
result[0] = 32; letmut latent = 32; letmut steps = 2; loop { let next_latent = (latent << 7) + 32; if value < next_latent || next_latent == latent { break;
}
latent = next_latent;
steps += 1;
} letmut value = value - latent; letmut i = steps; while i > 0 {
i -= 1;
result[i] |= (value as u8) & 0b01111111;
value >>= 7; if i > 0 && i < steps - 1 {
result[i] |= 0b10000000;
}
} // The bytes are from 0 to `steps`.
ConstArrayBuilder::from_manual_slice(result, 0, steps)
}
#[test] fn test_read() { for cas in CASES { let recovered = read_varint_meta2(cas.bytes[0], &cas.bytes[1..]);
assert_eq!(recovered, (cas.value, cas.remainder), "{:?}", cas);
}
}
#[test] fn test_read_write() { for cas in CASES { let reference_bytes = write_varint_reference(cas.value);
assert_eq!(
reference_bytes.len(),
cas.bytes.len() - cas.remainder.len(), "{:?}",
cas
);
assert_eq!(
reference_bytes.as_slice(),
&cas.bytes[0..reference_bytes.len()], "{:?}",
cas
); let recovered = read_varint_meta2(cas.bytes[0], &cas.bytes[1..]);
assert_eq!(recovered, (cas.value, cas.remainder), "{:?}", cas); let write_bytes = write_varint_meta2(cas.value);
assert_eq!(
reference_bytes.as_slice(),
write_bytes.as_slice(), "{:?}",
cas
);
}
}
#[test] fn test_roundtrip() { letmut i = 0usize; while i < MAX_VARINT { let bytes = write_varint_meta2(i); let recovered = read_varint_meta2(bytes.as_slice()[0], &bytes.as_slice()[1..]);
assert_eq!(i, recovered.0, "{:?}", bytes.as_slice());
i <<= 1;
i += 1;
}
}
#[test] fn test_extended_roundtrip() { letmut i = 0usize; while i < MAX_VARINT { let bytes = write_varint_meta3(i); let recovered = read_varint_meta3(bytes.as_slice()[0], &bytes.as_slice()[1..]);
assert_eq!(i, recovered.0, "{:?}", bytes.as_slice());
i <<= 1;
i += 1;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.