use std::sync::Arc;
use byteorder::{ByteOrder, LittleEndian};
use crate::{array::RealmRef, realm::Realm};
pub(crate) fn read_array_value(payload: &[u8], width: u8, index: usize) -> u64 {
match width {
0 => 0,
1 => {
let offset = index >> 3;
((payload[offset] >> (index & 7)) & 0x01) as u64
}
2 => {
let offset = index >> 2;
((payload[offset] >> ((index & 3) << 1)) & 0x03) as u64
}
4 => {
let offset = index >> 1;
((payload[offset] >> ((index & 1) << 2)) & 0x0F) as u64
}
8 => payload[index] as u64,
16 => {
let offset = index * 2;
LittleEndian::read_u16(&payload[offset..offset + 2]) as u64
}
32 => {
let offset = index * 4;
LittleEndian::read_u32(&payload[offset..offset + 4]) as u64
}
64 => {
let offset = index * 8;
LittleEndian::read_u64(&payload[offset..offset + 8])
}
_ => {
panic!("invalid width {width}");
}
}
}
#[inline]
fn find_child_from_offsets(
realm: Arc<Realm>,
offsets_header: RealmRef,
width: u8,
elem_ndx: usize,
) -> crate::RealmResult<(usize, usize)> {
let header = realm.header(offsets_header)?;
let offsets_data = realm.payload(offsets_header, header.payload_len());
let offsets_size = header.size;
let child_index = upper_bound(offsets_data, width, offsets_size as usize, elem_ndx as u64);
let elem_ndx_offset = if child_index == 0 {
0
} else {
read_array_value(offsets_data, width, child_index - 1) as usize
};
let index_in_child = elem_ndx - elem_ndx_offset;
Ok((child_index, index_in_child))
}
fn find_bptree_child(
realm: Arc<Realm>,
first_value: u64,
index: usize,
) -> crate::RealmResult<(usize, usize)> {
if first_value % 2 != 0 {
let elems_per_child = (first_value / 2) as usize;
let child_ndx = index / elems_per_child;
let ndx_in_child = index % elems_per_child;
return Ok((child_ndx, ndx_in_child));
}
let offsets_ref = RealmRef::new(first_value as usize);
let offsets_header = realm.header(offsets_ref)?;
let offsets_width = offsets_header.width();
let (child_index, index_in_child) =
find_child_from_offsets(realm.clone(), offsets_ref, offsets_width, index)?;
Ok((child_index, index_in_child))
}
pub(crate) fn find_bptree_child_in_payload(
realm: Arc<Realm>,
payload: &[u8],
width: u8,
index: usize,
) -> crate::RealmResult<(RealmRef, usize)> {
let first_value = read_array_value(payload, width, 0);
let (child_index, index_in_child) = find_bptree_child(realm, first_value, index)?;
let child_ref = RealmRef::new(read_array_value(payload, width, 1 + child_index) as usize);
Ok((child_ref, index_in_child))
}
pub(crate) fn string_from_bytes(mut bytes: Vec<u8>) -> String {
assert!(
!bytes.is_empty(),
"string cannot be empty (should have a trailing \\0"
);
assert!(
bytes[bytes.len() - 1] == 0,
"string must end with a \\0 byte"
);
bytes.pop();
unsafe { String::from_utf8_unchecked(bytes) }
}
#[inline]
pub(crate) fn lower_bound(data: &[u8], width: u8, mut size: usize, value: u64) -> usize {
let mut low = 0;
while size >= 8 {
let mut half = size / 2;
let mut other_half = size - half;
let mut probe = low + half;
let mut other_low = low + other_half;
let mut v = read_array_value(data, width, probe);
size = half;
low = if v < value { other_low } else { low };
half = size / 2;
other_half = size - half;
probe = low + half;
other_low = low + other_half;
v = read_array_value(data, width, probe);
size = half;
low = if v < value { other_low } else { low };
half = size / 2;
other_half = size - half;
probe = low + half;
other_low = low + other_half;
v = read_array_value(data, width, probe);
size = half;
low = if v < value { other_low } else { low };
}
while size > 0 {
let half = size / 2;
let other_half = size - half;
let probe = low + half;
let other_low = low + other_half;
let v = read_array_value(data, width, probe);
size = half;
low = if v < value { other_low } else { low };
}
low
}
#[inline]
fn upper_bound(data: &[u8], width: u8, mut size: usize, value: u64) -> usize {
let mut low = 0;
while size >= 8 {
let mut half = size / 2;
let mut other_half = size - half;
let mut probe = low + half;
let mut other_low = low + other_half;
let mut v = read_array_value(data, width, probe);
size = half;
low = if value >= v { other_low } else { low };
half = size / 2;
other_half = size - half;
probe = low + half;
other_low = low + other_half;
v = read_array_value(data, width, probe);
size = half;
low = if value >= v { other_low } else { low };
half = size / 2;
other_half = size - half;
probe = low + half;
other_low = low + other_half;
v = read_array_value(data, width, probe);
size = half;
low = if value >= v { other_low } else { low };
}
while size > 0 {
let half = size / 2;
let other_half = size - half;
let probe = low + half;
let other_low = low + other_half;
let v = read_array_value(data, width, probe);
size = half;
low = if value >= v { other_low } else { low };
}
low
}