array-fusion 0.2.0

Array merging and splitting facilities
Documentation
#![no_std]
//! Array fusion and fission utilies.
//!
//! This crate contains utilities to combine multiple arrays together
//! (aka fusion) as well as splitting them (aka fission).
//!
//! This crate is based on the [`hybrid_array`] crate, which provides
//! all the building blocks for this crate. The `hybrid_array` crate
//! has its own [`Array`] type for an array type that uses the
//! `typenum` type parameters to represent the size of the array,
//! instead of the const generic array size of classic "core" arrays.
//!
//! The reason of using `typenum` is that you can use `typenum`s
//! type parameters in generic context unlike the classic const
//! generics which at the time of writing (2025) have still many
//! limitations that limits the usability of them in generic context.
//!
//! However when working within the limited scopes (and in
//! non-generic context) the "core" arrays have much better ergonomics.
//! Due to this ambivalence, this crate offers most functions in two variants
//! the "normal" way is baed on `hybrid_array`s `Array` and the "core" variant
//! the work with the ordinary arrays.
//!
//! This crate also offers convenient functions for converting between the
//! "core" and `hybrid_array` arrays via [`array_from_core`] and
//! [`array_to_core`]. Their trait bounds demonstrate the complexity of
//! interchanging between these type. Tho, if you have a concrete array all
//! these bounds can be inferred, and if you dont have a concrete array e.g.
//! due to generic context, you better use the `hybrid_array` array.
//!

pub use hybrid_array;
// Hidden import used by macros.
#[doc(hidden)]
pub use hybrid_array::Array;
use hybrid_array::ArraySize;
use hybrid_array::AssocArraySize;
// Hidden import used by macros.
#[doc(hidden)]
pub use hybrid_array::sizes;
// Use statement for documentation.
#[cfg(doc)]
use hybrid_array::typenum;



pub mod bytes;
pub mod chunks;
mod macros;



/// Convert a core array into an [`Array`].
///
/// This is mostly equivalent to `Array(array)`, but it also infers the size of
/// the `Array`.
///
/// # Example
///
/// ```
/// use hybrid_array::Array;
/// use array_fusion::array_from_core;
///
/// let data = [0, 42, 0x0, 0x0, 0x12, 0x34];
/// // Notice that the size is inferred
/// let hybird: Array<u8, _> = array_from_core(data);
///
/// assert_eq!(hybird, Array(data));
/// ```
///
pub fn array_from_core<T, const N: usize>(
	array: [T; N],
) -> Array<T, <[T; N] as AssocArraySize>::Size>
where
	[T; N]: AssocArraySize,
	<[T; N] as AssocArraySize>::Size: ArraySize<ArrayType<T> = [T; N]>,
{
	Array(array)
}

/// Convert an `Array` into a core array.
///
/// This is mostly equivalent to `array.0`, but it also infers the size of
/// the `Array` from the returned core array, if possible.
///
/// # Example
///
/// ```
/// use hybrid_array::Array;
/// use array_fusion::array_to_core;
///
/// let data = [0, 42, 0x0, 0x0, 0x12, 0x34];
/// // Notice that the size is inferred due to the use of `array_to_core` below
/// let hybird: Array<u8, _> = Array(data);
///
/// // Allows to infer the size of `hybird` based on the type of `core`
/// let core: [u8; 6] = array_to_core(hybird);
///
/// assert_eq!(core, data);
/// ```
///
pub fn array_to_core<T, const N: usize>(array: Array<T, <[T; N] as AssocArraySize>::Size>) -> [T; N]
where
	[T; N]: AssocArraySize,
	<[T; N] as AssocArraySize>::Size: ArraySize<ArrayType<T> = [T; N]>,
{
	array.0
}



#[cfg(test)]
mod test {
	use hybrid_array::sizes::U0;
	use hybrid_array::sizes::U1;
	use hybrid_array::sizes::U2;
	use hybrid_array::sizes::U3;
	use hybrid_array::sizes::U4;
	use hybrid_array::sizes::U10;

	use super::*;

	#[test]
	fn test_split_array_manual() {
		let data = [
			0, 42, // foo
			0x0, 0x0, 0x12, 0x34, // bar
			0,    // padding
			b'a', b'b', b'c', // alpha
		];
		let data = Array::<_, U10>(data);

		let (foo, rest) = data.split::<U2>();
		assert_eq!(u16::from_be_bytes(foo.0), 42);

		let (bar, rest) = rest.split::<U4>();
		assert_eq!(u32::from_be_bytes(bar.0), 0x1234);

		let (_pad, rest) = rest.split::<U1>();

		let (alpha, rest) = rest.split::<U3>();
		assert_eq!(alpha.0, *b"abc");

		// Assert that we split the entire array
		let _rest: Array<_, U0> = rest;
	}

	#[test]
	fn test_split_array() {
		let data = [
			0, 42, // foo
			0x0, 0x0, 0x12, 0x34, // bar
			0,    // padding
			b'a', b'b', b'c', // alpha
		];
		split_array! {
			let split [
				foo,
				bar,
				_pad: array U1,
				alpha: array U3,
			] = Array::<_, U10>(data);
		}
		assert_eq!(u16::from_be_bytes(array_to_core(foo)), 42);
		assert_eq!(u32::from_be_bytes(array_to_core(bar)), 0x1234);
		assert_eq!(alpha.0, *b"abc");
	}

	#[test]
	fn test_split_core_array() {
		let data = [
			0, 42, // foo
			0x0, 0x0, 0x12, 0x34, // bar
			0,    // padding
			b'a', b'b', b'c', // alpha
		];
		split_core_array! {
			let split [
				foo,
				bar,
				_pad: [u8;1],
				alpha,
			] = data;
		}
		assert_eq!(u16::from_be_bytes(foo), 42);
		assert_eq!(u32::from_be_bytes(bar), 0x1234);
		assert_eq!(alpha, *b"abc");
	}

	#[test]
	fn test_merge_array() {
		let foo = Array::<_, U2>(u16::to_be_bytes(42));
		let bar = Array::<_, U4>(u32::to_be_bytes(0x1234));
		let padding = Array::<_, U1>([0]);
		let alpha = Array::<_, U3>(*b"abc");

		let data = merge_array!([foo, bar, padding, alpha]);

		assert_eq!(
			data,
			Array([
				0, 42, // foo
				0x0, 0x0, 0x12, 0x34, // bar
				0,    // padding
				b'a', b'b', b'c', // alpha
			])
		);
	}

	#[test]
	fn test_merge_core_array() {
		let foo = u16::to_be_bytes(42);
		let bar = u32::to_be_bytes(0x1234);
		let padding = [0];
		let alpha = *b"abc";

		let data = merge_core_array!([foo, bar, padding, alpha]);

		assert_eq!(
			data,
			[
				0, 42, // foo
				0x0, 0x0, 0x12, 0x34, // bar
				0,    // padding
				b'a', b'b', b'c', // alpha
			]
		);
	}

	#[test]
	fn test_split_emtpy_array() {
		let data: Array<u8, U0> = Array([]);

		split_array! {
			let split [ ] = data;
		}
	}

	#[test]
	fn test_split_emtpy_core_array() {
		let data: [u8; 0] = [];

		split_core_array! {
			let split [ ] = data;
		}
	}
}