Skip to content

Commit

Permalink
implement encode_varint
Browse files Browse the repository at this point in the history
  • Loading branch information
TheButlah committed May 18, 2024
1 parent 8eb6df5 commit 6c6da1b
Showing 1 changed file with 58 additions and 11 deletions.
69 changes: 58 additions & 11 deletions crates/did-simple/src/varint.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,61 @@
pub const fn decode_varint(encoded: &[u8]) -> Result<u16, DecodeError> {
/// bitmask for 7 least significant bits
const LSB_7: u8 = u8::MAX / 2;
/// bitmask for most significant bit
const MSB: u8 = !LSB_7;

#[inline]
const fn msb_is_1(val: u8) -> bool {
val & MSB == MSB
}

/// Encodes a value as a varint.
pub(crate) const fn encode_varint(value: u16) -> (usize, [u8; 3]) {
let mut out_buf = [0; 3];
// ilog2 can be used to get the (0-indexed from LSB position) of the MSB.
// We then add one to it, since we are trying to indicate length, not position.
let in_bit_length: u16 = if let Some(x) = value.checked_ilog2() {
(x + 1) as u16
} else {
return (0, out_buf); // empty buffer
};

let mut out = 0u32;
let mut in_bit_pos = 0u16;
// Each time we need to write a carry bit into the MSB, we increment this.
let mut carry_counter = 0;
// Have to use macro because consts can't use closures or &mut in functions :(
macro_rules! copy_chunk {
() => {
let retrieved_bits = value & ((LSB_7 as u16) << in_bit_pos);
// if the carry bits weren't a thing, we could keep the position intact
// and just directly copy the chunk. But we need to shift left to account
// for prior carry bits
out |= (retrieved_bits as u32) << carry_counter;
in_bit_pos += 7;
};
}
copy_chunk!();
while in_bit_pos < in_bit_length {
carry_counter += 1;
out |= 1 << (carry_counter * 8 - 1); // turns on MSB to indicate carry.
copy_chunk!();
}

// the number of bytes to write is equivalent to the carry counter + 1.
let num_output_bytes = carry_counter + 1;

// No for loops or copy_from_slice in const fn :(
let mut idx = 0;
let output_bytes = out.to_le_bytes();
while idx < num_output_bytes {
out_buf[idx] = output_bytes[idx];
idx += 1;
}

(num_output_bytes, out_buf)
}

pub(crate) const fn decode_varint(encoded: &[u8]) -> Result<u16, DecodeError> {
// TODO: Technically, some three byte encodings could fit into a u16, we
// should support those in the future.
// Luckily none of them are used for did:key afaik.
Expand All @@ -9,16 +66,6 @@ pub const fn decode_varint(encoded: &[u8]) -> Result<u16, DecodeError> {
return Err(DecodeError::MissingBytes);
}

/// bitmask for 7 least significant bits
const LSB_7: u8 = u8::MAX / 2;
/// bitmask for most significant bit
const MSB: u8 = !LSB_7;

#[inline]
const fn msb_is_1(val: u8) -> bool {
val & MSB == MSB
}

let a = encoded[0];
let mut result: u16 = (a & LSB_7) as u16;
if msb_is_1(a) {
Expand Down

0 comments on commit 6c6da1b

Please sign in to comment.