use crate::reply::{group, server};
use o3::buffer::{self, hold::scope::Deferred, resident, resident::copy};
use std::{array, num::NonZero, ops::Range, str};
#[must_use]
pub(crate) struct Fields<'input, 'd, R: buffer::Source + 'input> {
source: Deferred<'input, 'd, R>,
fields: [[u32; 2]; 2],
len: u32,
}
impl<'input, 'd, R: buffer::Source + 'input> Fields<'input, 'd, R> {
pub(crate) unsafe fn new(source: Deferred<'input, 'd, R>, ranges: [Range<usize>; 2]) -> Self {
let mut len = 0u32;
let fields = ranges.map(|range| {
let start = range.start as u32;
let end = range.end as u32;
len = len.wrapping_add(end.wrapping_sub(start));
[start, end]
});
Self {
source,
fields,
len,
}
}
pub(crate) fn get<const I: usize>(&self) -> &'input str {
const { assert!(I < 2) };
self.field(I)
}
fn field(&self, index: usize) -> &'input str {
let [start, end] = self.fields[index];
let bytes = unsafe {
self.source
.as_slice()
.get_unchecked(start as usize..end as usize)
};
unsafe { str::from_utf8_unchecked(bytes) }
}
pub(crate) fn retained_len(&self) -> usize {
self.len as usize
}
pub(super) fn try_compact(self) -> Result<buffer::raw::Text<'d>, copy::Error> {
let owner = match NonZero::new(self.len) {
None => resident::Exact::new(),
Some(capacity) => {
let slices: [&[u8]; 2] = array::from_fn(|index| self.field(index).as_bytes());
let slices = unsafe { buffer::raw::Slices::new(capacity, slices) };
slices.try_copy(self.source.budget())?
}
};
Ok(unsafe { buffer::raw::Text::from_utf8(owner) })
}
}
const _: () = assert!(
size_of::<Fields<'static, 'static, buffer::storage::Shared>>() == 7 * size_of::<usize>()
);
pub(super) fn group_fields(payload: &[u8], record: group::Record) -> server::Fields<'_> {
let payload = unsafe { payload.get_unchecked(record.start..record.end) };
unsafe { server::Fields::from_valid(payload) }
}
pub(super) fn number(value: server::Number<'_>) -> NonZero<u32> {
let mut number = 0u32;
for byte in value.bytes() {
number = number * 10 + u32::from(byte - b'0');
}
unsafe { NonZero::new_unchecked(number) }
}