serde_v8vs 0.1.0

serde support for V8's ValueSerializer format
Documentation
//! `serde` support for V8's `ValueSerializer` format used by Node.js/Deno's [Serialization API](https://nodejs.org/api/v8.html#serialization-api).
//!
//! - **Serialize**: [`to_boxed_slice`], [`to_writer`]
//! - **Deserialize**: [`from_slice`], [`from_reader`]
//! - **Compatibility**: [`as_date`], [`as_object`], [`as_set`]

#![deny(clippy::std_instead_of_alloc, clippy::std_instead_of_core)]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]

extern crate alloc;
extern crate core;

use alloc::string::{String, ToString};
use core::{convert::Infallible, fmt};
#[cfg(feature = "std")]
use std::io;

mod de;
mod segmented_buf;
mod ser;
#[cfg(test)]
mod tests;
mod types;
mod utf;
mod util;

#[cfg(feature = "std")]
pub use self::{de::from_reader, ser::to_writer};
pub use self::{
	de::{DeserializeOptions, from_slice},
	segmented_buf::GrowStrategy,
	ser::{SerializeOptions, to_boxed_slice},
	types::{as_date, as_object, as_set}
};

macro_rules! define_tags {
	(
        $(#[$attr:meta])*
		$vis:vis enum $name:ident {
			$($variant:ident = $x:literal),*
		}
	) => {
		$(#[$attr])*
		$vis enum $name {
			$($variant = $x,)*
		}

		impl TryFrom<u8> for $name {
			type Error = u8;

			fn try_from(value: u8) -> Result<Self, u8> {
				match value {
					$($x => Ok(Self::$variant),)*
					_ => Err(value)
				}
			}
		}
	};
}

define_tags! {
	#[repr(u8)]
	#[derive(Debug, Clone, Copy, PartialEq, Eq)]
	#[doc(hidden)] // only pub because its used in Error
	pub enum SerializationTag {
		Version = 0xFF,
		Padding = 0x00,

		VerifyObjectCount = b'?',
		TheHole = b'-',
		Undefined = b'_',
		Null = b'0',
		True = b'T',
		False = b'F',
		Int32 = b'I',
		Uint32 = b'U',
		Double = b'N',
		BigInt = b'Z',
		Utf8String = b'S',
		OneByteString = b'"',
		TwoByteString = b'c',
		ObjectReference = b'^',
		BeginJSObject = b'o',
		EndJSObject = b'{', // ah yes, the symbol used to end objects, of course
		BeginSparseJSArray = b'a',
		EndSparseJSArray = b'@',
		BeginDenseJSArray = b'A',
		EndDenseJSArray = b'$',
		Date = b'D',
		TrueObject = b'y',
		FalseObject = b'x',
		NumberObject = b'n',
		BigIntObject = b'z',
		StringObject = b's',
		RegExp = b'R',
		BeginJSMap = b';',
		EndJSMap = b':',
		BeginJSSet = b'\'',
		EndJSSet = b',',
		ArrayBuffer = b'B',
		ResizableArrayBuffer = b'~',
		ArrayBufferTransfer = b't',
		ArrayBufferView = b'V',
		SharedArrayBuffer = b'u',
		SharedObject = b'p',
		WasmModuleTransfer = b'w',
		HostObject = b'\\',
		WasmMemoryTransfer = b'm',
		Error = b'r'
	}
}

define_tags! {
	#[repr(u8)]
	#[derive(Debug, Clone, Copy, PartialEq, Eq)]
	enum ArrayBufferViewTag {
		Int8Array = b'b',
		Uint8Array = b'B',
		Uint8ClampedArray = b'C',
		Int16Array = b'w',
		Uint16Array = b'W',
		Int32Array = b'd',
		Uint32Array = b'D',
		Float16Array = b'h',
		Float32Array = b'f',
		Float64Array = b'F',
		BigInt64Array = b'q',
		BigUint64Array = b'Q',
		DataView = b'?'
	}
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ErrorTag {
	EvalErrorPrototype = b'E',
	RangeErrorPrototype = b'R',
	ReferenceErrorPrototype = b'F',
	SyntaxErrorPrototype = b'S',
	TypeErrorPrototype = b'T',
	UriErrorPrototype = b'U',

	Message = b'm',
	Cause = b'c',
	Stack = b's',
	End = b'.'
}

const SMI_MIN: i64 = -2_i64.pow(30);
const SMI_MAX: i64 = 2_i64.pow(30) - 1;

const REGEXP_FLAG_GLOBAL: u32 = 1 << 0;
const REGEXP_FLAG_IGNORE_CASE: u32 = 1 << 1;
const REGEXP_FLAG_MULTILINE: u32 = 1 << 2;
const REGEXP_FLAG_STICKY: u32 = 1 << 3;
const REGEXP_FLAG_UNICODE: u32 = 1 << 4;
const REGEXP_FLAG_DOT_ALL: u32 = 1 << 5;

#[derive(Debug)]
pub enum Error {
	UnexpectedEof,
	BadUtf8,
	BadUtf16,
	BadVersion(u8),
	UnknownTag(u8),
	UnknownTypedArrayType(u8),
	UnhandledTag(SerializationTag),
	ArrayWithProperties,
	BytesTooBig,
	BigIntTooBig,
	LengthMismatch {
		head: u64,
		tail: u64
	},
	Custom(String),
	#[cfg(feature = "std")]
	Io(io::Error)
}

#[cfg(feature = "std")]
impl From<io::Error> for Error {
	fn from(value: io::Error) -> Self {
		if value.kind() == io::ErrorKind::UnexpectedEof {
			Self::UnexpectedEof
		} else {
			Error::Io(value)
		}
	}
}

impl From<Infallible> for Error {
	fn from(_: Infallible) -> Self {
		unsafe { core::hint::unreachable_unchecked() }
	}
}

impl fmt::Display for Error {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Error::UnexpectedEof => f.write_str("unexpected end of stream"),
			Error::BadUtf8 => f.write_str("malformed UTF-8 string"),
			Error::BadUtf16 => f.write_str("malformed UTF-16 string"),
			Error::BadVersion(v) => f.write_fmt(format_args!("unacceptable format version {v}")),
			Error::BytesTooBig => f.write_str("byte arrays larger than 4 GB can only be serialized in format versions 16+"),
			Error::BigIntTooBig => f.write_str("serialized BigInt exceeds i128"),
			Error::UnknownTag(c) => f.write_fmt(format_args!("unknown tag {c:02X}")),
			Error::UnknownTypedArrayType(c) => f.write_fmt(format_args!("unknown or unhandled typed array type {c:02X}")),
			Error::ArrayWithProperties => f.write_str("arrays with properties are not supported"),
			Error::UnhandledTag(c) => f.write_fmt(format_args!("unhandled tag {c:?}")),
			Error::LengthMismatch { head, tail } => f.write_fmt(format_args!("array length mismatch: header says {head} elements, but tail says {tail}")),
			Error::Custom(c) => f.write_str(c),
			#[cfg(feature = "std")]
			Error::Io(io) => f.write_fmt(format_args!("IO error: {io}"))
		}
	}
}

impl core::error::Error for Error {}

impl serde_core::ser::Error for Error {
	fn custom<T>(msg: T) -> Self
	where
		T: fmt::Display
	{
		Error::Custom(msg.to_string())
	}
}

impl serde_core::de::Error for Error {
	fn custom<T>(msg: T) -> Self
	where
		T: fmt::Display
	{
		Error::Custom(msg.to_string())
	}
}