serde_v8vs 0.1.0

serde support for V8's ValueSerializer format
Documentation
use alloc::{
	alloc::{Layout, alloc, dealloc, handle_alloc_error},
	boxed::Box,
	vec::Vec
};
use core::{
	ptr::{self, NonNull},
	slice
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GrowStrategy {
	Linear { size: usize },
	Exponential { size: usize }
}

impl GrowStrategy {
	#[inline]
	const fn current(&self) -> usize {
		match self {
			Self::Linear { size } => *size,
			Self::Exponential { size: current } => *current
		}
	}

	#[inline]
	fn next(&mut self) -> usize {
		match self {
			Self::Linear { size } => *size,
			Self::Exponential { size: current } => {
				*current = current.saturating_mul(2);
				*current
			}
		}
	}
}

struct Segment {
	ptr: NonNull<u8>,
	cap: usize,
	len: usize
}

unsafe impl Send for Segment {}
unsafe impl Sync for Segment {}

impl Segment {
	fn new(cap: usize) -> Self {
		if cap == 0 {
			return Segment {
				ptr: NonNull::dangling(),
				cap: 0,
				len: 0
			};
		}

		let layout = Layout::array::<u8>(cap).expect("infallible");
		let Some(ptr) = NonNull::new(unsafe { alloc(layout) }) else {
			handle_alloc_error(layout);
		};
		Segment { ptr, cap, len: 0 }
	}

	#[inline]
	fn remaining(&self) -> usize {
		self.cap - self.len
	}

	#[inline]
	fn as_slice(&self) -> &[u8] {
		unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
	}

	fn push(&mut self, byte: u8) -> bool {
		if self.len == self.cap {
			return false;
		}

		unsafe { *self.ptr.as_ptr().add(self.len) = byte };
		self.len += 1;
		true
	}

	fn extend_from_slice(&mut self, data: &[u8]) -> usize {
		let n = data.len().min(self.remaining());
		if n > 0 {
			unsafe { ptr::copy_nonoverlapping(data.as_ptr(), self.ptr.as_ptr().add(self.len), n) };
			self.len += n;
		}
		n
	}
}

impl Drop for Segment {
	fn drop(&mut self) {
		if self.cap != 0 {
			let layout = Layout::array::<u8>(self.cap).expect("infallible");
			unsafe { dealloc(self.ptr.as_ptr(), layout) };
		}
	}
}

pub struct SegmentedBuf {
	segments: Vec<Segment>,
	total_len: usize,
	grow_strategy: GrowStrategy
}

impl SegmentedBuf {
	pub const fn new(grow_strategy: GrowStrategy) -> Self {
		SegmentedBuf {
			segments: Vec::new(),
			total_len: 0,
			grow_strategy
		}
	}

	#[inline]
	fn has_space(&self) -> bool {
		self.segments.last().is_some_and(|s| s.len < s.cap)
	}

	fn alloc_segment(&mut self) {
		let cap = if self.segments.is_empty() { self.grow_strategy.current() } else { self.grow_strategy.next() };
		self.segments.push(Segment::new(cap));
	}

	pub fn push(&mut self, byte: u8) {
		if !self.has_space() {
			self.alloc_segment();
		}

		let seg = self.segments.last_mut().expect("infallible");
		let res = seg.push(byte);
		debug_assert!(res);
		self.total_len += 1;
	}

	pub fn extend_from_slice(&mut self, mut data: &[u8]) {
		if data.is_empty() {
			return;
		}

		if let Some(seg) = self.segments.last_mut() {
			if seg.remaining() > 0 {
				let n = seg.extend_from_slice(data);
				self.total_len += n;
				data = &data[n..];
			}
		}
		if data.is_empty() {
			return;
		}

		if data.len() >= self.grow_strategy.current() {
			let mut seg = Segment::new(data.len());
			let n = seg.extend_from_slice(data);
			debug_assert_eq!(n, data.len());
			self.total_len += n;
			self.segments.push(seg);
			return;
		}

		while !data.is_empty() {
			self.alloc_segment();
			let seg = self.segments.last_mut().expect("infallible");
			let n = seg.extend_from_slice(data);
			self.total_len += n;
			data = &data[n..];
		}
	}

	pub fn take_from(&mut self, other: SegmentedBuf) {
		self.segments.extend(other.segments);
		self.total_len += other.total_len;
	}

	#[cfg(feature = "std")]
	pub fn segments(&self) -> impl Iterator<Item = &[u8]> + '_ {
		self.segments.iter().map(|s| s.as_slice())
	}

	pub fn into_contiguous(self) -> Box<[u8]> {
		let mut buf = Vec::with_capacity(self.total_len);
		for seg in self.segments {
			buf.extend_from_slice(seg.as_slice());
		}
		buf.into_boxed_slice()
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn test_push_linear() {
		let mut v = SegmentedBuf::new(GrowStrategy::Linear { size: 4 });
		for b in 0u8..10 {
			v.push(b);
		}
		assert_eq!(v.segments.len(), 3);
		assert_eq!(&*v.into_contiguous(), &(0u8..10).collect::<Vec<_>>()[..]);
	}

	#[test]
	fn test_push_exponential() {
		let mut v = SegmentedBuf::new(GrowStrategy::Exponential { size: 2 });
		for b in 0u8..20 {
			v.push(b);
		}
		assert_eq!(v.segments.len(), 4);
		assert_eq!(&*v.into_contiguous(), &(0u8..20).collect::<Vec<_>>()[..]);
	}

	#[test]
	fn test_extend_small() {
		let mut v = SegmentedBuf::new(GrowStrategy::Linear { size: 4 });
		v.push(1);
		v.push(2);
		v.extend_from_slice(&[3, 4, 5, 6]);
		assert_eq!(v.segments.len(), 2);
		assert_eq!(&*v.into_contiguous(), &[1, 2, 3, 4, 5, 6]);
	}

	#[test]
	fn test_extend_large() {
		let mut v = SegmentedBuf::new(GrowStrategy::Linear { size: 8 });
		v.push(0xAA);
		let x: Vec<u8> = (0u8..100).collect();
		v.extend_from_slice(&x);
		assert_eq!(v.segments.len(), 2);
		let y: Vec<u8> = core::iter::once(0xAA).chain(x).collect();
		assert_eq!(&*v.into_contiguous(), &y[..]);
	}
}