mchr 0.1.7

Lenient implementations of encodings. Zero allocations, zero dependencies!
Documentation

use std::marker::PhantomData;
use std::mem::MaybeUninit;

pub const BUFFER_SIZE: usize = 16;

// Pullable

pub trait Pullable {
	type Item;
	fn pull(&mut self, buffer: &mut PushBuffer<Self::Item>);
	
	fn buffered(self) -> PullBuffer<Self> where Self: Sized {
		PullBuffer::new(self)
	}
	
	fn transcode<K, E>(self, encoding: E) -> Transcoder<K, E, Self>
		where Self: Sized,
			K: TranscoderKind<Input = Self::Item>,
			E: TranscoderImpl<K, Self> {
		Transcoder::new(encoding, self)
	}
}

impl<I> Pullable for I where I: Iterator {
	type Item = I::Item;
	fn pull(&mut self, buffer: &mut PushBuffer<Self::Item>) {
		while buffer.fits(1) {
			if let Some(x) = self.next() {
				buffer.push([ x ]);
			} else {
				break;
			}
		}
	}
}

// Pull Buffer

pub struct PullBuffer<I> where I: Pullable {
	inner: I,
	head: usize,
	tail: usize,
	buffer: [MaybeUninit<I::Item>; BUFFER_SIZE],
}

impl<I> PullBuffer<I> where I: Pullable {
	pub fn new(inner: I) -> Self {
		Self { inner, head: 0, tail: 0, buffer: std::array::from_fn(|_| MaybeUninit::uninit()) }
	}
	pub fn view(&mut self, amount: usize) -> &[I::Item] {
		assert!(amount <= BUFFER_SIZE);
		if self.head + amount > self.tail {
			if self.head == self.tail {
				self.head = 0;
				self.tail = 0;
			} else {
				self.buffer.rotate_left(self.head);
				self.tail -= self.head;
				self.head = 0;
			}
			let mut buffer = PushBuffer::new(&mut self.buffer[self.tail..]);
			self.inner.pull(&mut buffer);
			self.tail += buffer.len();
		}
		let view = &self.buffer[self.head..self.tail.min(self.head + amount)];
		unsafe { std::mem::transmute(view) } // TODO replace with `MaybeUninit::slice_assume_init()` once stabilized
	}
	pub fn advance(&mut self, amount: usize) {
		assert!(self.head + amount <= self.tail);
		for i in 0..amount {
			unsafe {
				std::mem::replace(&mut self.buffer[self.head + i], MaybeUninit::uninit()).assume_init_drop();
			}
		}
		self.head += amount;
	}
}

impl<I> Iterator for PullBuffer<I> where I: Pullable {
	type Item = I::Item;
	fn next(&mut self) -> Option<Self::Item> {
		if self.head >= self.tail {
			let mut buffer = PushBuffer::new(&mut self.buffer);
			self.inner.pull(&mut buffer);
			self.head = 0;
			self.tail = buffer.len();
		}
		if self.head < self.tail {
			let item = std::mem::replace(&mut self.buffer[self.head], MaybeUninit::uninit());
			self.head += 1;
			Some(unsafe { item.assume_init() })
		} else {
			None
		}
	}
}

impl<I> Drop for PullBuffer<I> where I: Pullable {
	fn drop(&mut self) {
		for i in self.head..self.tail {
			unsafe {
				std::mem::replace(&mut self.buffer[i], MaybeUninit::uninit()).assume_init_drop();
			}
		}
		self.head = 0;
		self.tail = 0;
	}
}

// Push Buffer

pub struct PushBuffer<'a, T> {
	count: usize,
	buffer: &'a mut [MaybeUninit<T>],
}

impl<'a, T> PushBuffer<'a, T> {
	pub fn new(buffer: &'a mut [MaybeUninit<T>]) -> Self {
		Self { count: 0, buffer }
	}
	pub fn len(&self) -> usize { self.count }
	pub fn is_empty(&self) -> bool { self.count == 0 }
	pub fn capacity(&self) -> usize { self.buffer.len() }
	pub fn fits(&self, count: usize) -> bool { self.count + count <= self.buffer.len() }
	pub fn push<const N: usize>(&mut self, items: [T; N]) {
		assert!(self.fits(N));
		for (i, x) in items.into_iter().enumerate() {
			self.buffer[self.count + i].write(x);
		}
		self.count += N;
	}
}

// Transcoder

pub trait TranscoderKind {
	type Input;
	type Output;
}

pub trait TranscoderImpl<K, I> where K: TranscoderKind, I: Pullable<Item = K::Input> {
	fn transcode(&self, input: &mut PullBuffer<I>, output: &mut PushBuffer<K::Output>);
}

pub struct Transcoder<K, E, I> where K: TranscoderKind, I: Pullable<Item = K::Input>, E: TranscoderImpl<K, I> {
	encoding: E,
	input: PullBuffer<I>,
	phantom: PhantomData<K>,
}

impl<K, E, I> Transcoder<K, E, I> where K: TranscoderKind, I: Pullable<Item = K::Input>, E: TranscoderImpl<K, I> {
	pub fn new(encoding: E, input: I) -> Self {
		Self { encoding, input: input.buffered(), phantom: PhantomData }
	}
}

impl<K, E, I> Pullable for Transcoder<K, E, I> where K: TranscoderKind, I: Pullable<Item = K::Input>, E: TranscoderImpl<K, I> {
	type Item = K::Output;
	fn pull(&mut self, buffer: &mut PushBuffer<Self::Item>) {
		self.encoding.transcode(&mut self.input, buffer)
	}
}

// Modules

pub mod str;

/* TODO
// Dynamic Dispatch

/// Any implementation of `Encoding` used for dynamic dispatch.
pub type Any = dyn Encoding;

/// Encoder using dynamic dispatch.
pub type AnyEncoder<'a> = Encoder<'a, Any>;

/// Decoder using dynamic dispatch.
pub type AnyDecoder<'a> = Decoder<'a, Any>;

impl Any {
	pub fn encoder<'a>(&'a self, text: &'a str) -> AnyEncoder<'a> { Encoder::new(self, text) }
	pub fn decoder<'a>(&'a self, bytes: &'a [u8]) -> AnyDecoder<'a> { Decoder::new(self, bytes) }
}
*/