lib_tcstring 0.5.0

IAB TCF v2 TCString utilities
Documentation
use crate::decode::{
    error::TcsError,
    model::{RangeSection, RangeSectionType},
};

pub(crate) fn parse_from_bytes(val: &[u8], absolute_start_bit: usize, bit_length: usize) -> u64 {
    let first_byte_start_bit = (absolute_start_bit % 8) as u8;
    let relative_end_bit = bit_length - 1;
    let absolute_end_bit = absolute_start_bit + relative_end_bit;
    let last_byte_end_bit = (absolute_end_bit % 8) as u64;
    let last_byte_index = absolute_end_bit / 8;
    let remaining_bits_in_first_byte =
        (7i64 - (first_byte_start_bit as i64 + (relative_end_bit as i64))).max(0) as u8;
    let mut bit_mask: u64 = (0xff << first_byte_start_bit) & 0xff;
    let mut current_byte = absolute_start_bit / 8;

    bit_mask = (bit_mask >> (first_byte_start_bit + remaining_bits_in_first_byte))
        << remaining_bits_in_first_byte;

    let mut return_value = (val[current_byte] as u64 & bit_mask) >> remaining_bits_in_first_byte;

    if current_byte >= last_byte_index {
        return return_value;
    }

    current_byte += 1;

    while current_byte < last_byte_index {
        return_value = (return_value << 8) | (val[current_byte] as u64);
        current_byte += 1;
    }

    let bit_shift = 7 - last_byte_end_bit;

    (return_value << (last_byte_end_bit + 1))
        | ((val[current_byte] as u64 & (0xff << bit_shift)) >> bit_shift)
}

pub(crate) fn parse_string_from_bytes(
    val: &[u8],
    bit_start: usize,
    bit_width: usize,
    char_count: usize,
) -> Result<String, TcsError> {
    byte_list_bit_boundary_check!(val, bit_start + (char_count * bit_width));

    let mut result = String::with_capacity(char_count);
    let mut offset = 0;

    for _ in 0..char_count {
        let alphabet_offset = parse_from_bytes(val, bit_start + offset, bit_width) as u8;

        if alphabet_offset > 25 {
            return Err(TcsError::InvalidAlphabetOffset);
        }

        result.push((b'A' + alphabet_offset) as char);
        offset += bit_width;
    }

    Ok(result)
}

pub(crate) fn parse_vendor_range_from_bytes(
    val: &[u8],
    bit_start: usize,
    value_type: &dyn Fn(Vec<u16>) -> RangeSectionType,
) -> Result<RangeSection, TcsError> {
    let mut bit_index = bit_start + 12;

    byte_list_bit_boundary_check!(val, bit_index);

    let num_entries = parse_from_bytes(val, bit_start, 12) as u16;
    let mut entry_list: Vec<u16> = Vec::new();
    let mut count = 0u16;

    while count < num_entries {
        if parse_from_bytes(val, bit_index, 1) as u8 == 1 {
            byte_list_bit_boundary_check!(val, bit_index + 33);

            let start_vendor_id = parse_from_bytes(val, bit_index + 1, 16) as u16;
            let end_vendor_id = parse_from_bytes(val, bit_index + 17, 16) as u16;

            for vendor_id in start_vendor_id..end_vendor_id + 1 {
                entry_list.push(vendor_id);
            }

            bit_index += 33;
        } else {
            byte_list_bit_boundary_check!(val, bit_index + 17);

            entry_list.push(parse_from_bytes(val, bit_index + 1, 16) as u16);
            bit_index += 17;
        }

        count += 1;
    }

    Ok(RangeSection {
        last_bit: bit_index,
        value: value_type(entry_list),
    })
}

parse_bitfield_from_bytes!(parse_u8_bitfield_from_bytes, u8);
parse_bitfield_from_bytes!(parse_u16_bitfield_from_bytes, u16);