#![no_std]
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
extern crate alloc;
pub use fastserial_derive::Decode;
pub use fastserial_derive::Encode;
pub mod codec;
pub mod io;
pub mod schema;
pub mod simd;
mod error;
mod format;
pub mod value;
pub use error::Error;
pub use format::Format;
pub use value::Value;
pub trait Encode {
const SCHEMA_HASH: u64;
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error>;
fn encode_with_format<F: Format, W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
F::encode_struct(self, w)
}
}
pub trait Decode<'de>: Sized {
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error>;
}
pub mod json {
use super::*;
pub fn encode<T: Encode>(val: &T) -> Result<alloc::vec::Vec<u8>, Error> {
let mut buf = alloc::vec::Vec::with_capacity(256);
val.encode(&mut buf)?;
Ok(buf)
}
pub fn encode_into<T: Encode>(val: &T, buf: &mut alloc::vec::Vec<u8>) -> Result<(), Error> {
val.encode(buf)
}
pub fn decode<'de, T: Decode<'de>>(input: &'de [u8]) -> Result<T, Error> {
let mut r = io::ReadBuffer::new(input);
let val = T::decode(&mut r)?;
codec::json::skip_whitespace(&mut r);
if !r.is_eof() {
return Err(Error::TrailingData);
}
Ok(val)
}
pub fn decode_str<'de, T: Decode<'de>>(input: &'de str) -> Result<T, Error> {
decode(input.as_bytes())
}
pub fn encode_pretty<T: Encode>(val: &T) -> Result<alloc::vec::Vec<u8>, Error> {
let compact = encode(val)?;
let value: crate::Value = decode(&compact)?;
let mut buf = alloc::vec::Vec::with_capacity(compact.len() * 2);
pretty_print_value(&value, &mut buf, 0)?;
Ok(buf)
}
fn pretty_print_value(
val: &crate::Value,
buf: &mut alloc::vec::Vec<u8>,
indent: usize,
) -> Result<(), Error> {
use crate::io::WriteBuffer;
match val {
crate::Value::Null => buf.write_bytes(b"null"),
crate::Value::Bool(true) => buf.write_bytes(b"true"),
crate::Value::Bool(false) => buf.write_bytes(b"false"),
crate::Value::Number(_) | crate::Value::String(_) => val.encode(buf),
crate::Value::Array(arr) => {
if arr.is_empty() {
return buf.write_bytes(b"[]");
}
buf.write_bytes(b"[\n")?;
let child_indent = indent + 2;
for (i, item) in arr.iter().enumerate() {
for _ in 0..child_indent {
buf.write_byte(b' ')?;
}
pretty_print_value(item, buf, child_indent)?;
if i + 1 < arr.len() {
buf.write_byte(b',')?;
}
buf.write_byte(b'\n')?;
}
for _ in 0..indent {
buf.write_byte(b' ')?;
}
buf.write_byte(b']')
}
crate::Value::Object(map) => {
if map.is_empty() {
return buf.write_bytes(b"{}");
}
buf.write_bytes(b"{\n")?;
let child_indent = indent + 2;
let len = map.len();
for (i, (k, v)) in map.iter().enumerate() {
for _ in 0..child_indent {
buf.write_byte(b' ')?;
}
codec::json::write_str(k, buf)?;
buf.write_bytes(b": ")?;
pretty_print_value(v, buf, child_indent)?;
if i + 1 < len {
buf.write_byte(b',')?;
}
buf.write_byte(b'\n')?;
}
for _ in 0..indent {
buf.write_byte(b' ')?;
}
buf.write_byte(b'}')
}
}
}
}
pub mod binary {
use super::*;
const MAGIC: [u8; 4] = *b"FBIN";
const VERSION: u16 = 0x0001;
pub fn encode<T: Encode>(val: &T) -> Result<alloc::vec::Vec<u8>, Error> {
let mut buf = alloc::vec::Vec::with_capacity(256);
buf.extend_from_slice(&MAGIC);
buf.extend_from_slice(&VERSION.to_le_bytes());
buf.extend_from_slice(&T::SCHEMA_HASH.to_le_bytes());
buf.extend_from_slice(&[0, 0]);
val.encode(&mut buf)?;
Ok(buf)
}
pub fn decode<'de, T: Decode<'de>>(input: &'de [u8]) -> Result<T, Error> {
if input.len() < 16 {
return Err(Error::UnexpectedEof);
}
if input[0..4] != MAGIC {
return Err(Error::InvalidMagic);
}
let version = u16::from_le_bytes([input[4], input[5]]);
if version != VERSION {
return Err(Error::UnsupportedVersion { version });
}
let mut r = io::ReadBuffer::new(&input[16..]);
let val = T::decode(&mut r)?;
if !r.is_eof() {
return Err(Error::TrailingData);
}
Ok(val)
}
pub fn encode_raw<T: Encode>(val: &T) -> Result<alloc::vec::Vec<u8>, Error> {
let mut buf = alloc::vec::Vec::with_capacity(256);
val.encode(&mut buf)?;
Ok(buf)
}
pub fn decode_raw<'de, T: Decode<'de>>(input: &'de [u8]) -> Result<T, Error> {
let mut r = io::ReadBuffer::new(input);
let val = T::decode(&mut r)?;
if !r.is_eof() {
return Err(Error::TrailingData);
}
Ok(val)
}
}
mod option_impl {
use super::*;
impl<T: Encode> Encode for Option<T> {
const SCHEMA_HASH: u64 = 0;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
match self {
None => w.write_bytes(b"null"),
Some(v) => v.encode(w),
}
}
#[inline]
fn encode_with_format<F: Format, W: io::WriteBuffer>(
&self,
w: &mut W,
) -> Result<(), Error> {
match self {
None => F::write_null(w),
Some(v) => v.encode_with_format::<F, W>(w),
}
}
}
impl<'de, T: Decode<'de>> Decode<'de> for Option<T> {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::skip_whitespace(r);
if r.peek() == b'n' {
r.expect_bytes(b"null")?;
Ok(None)
} else {
Ok(Some(T::decode(r)?))
}
}
}
}
mod vec_impl {
use super::*;
impl<T: Encode> Encode for alloc::vec::Vec<T> {
const SCHEMA_HASH: u64 = 0;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
w.write_byte(b'[')?;
let mut iter = self.iter();
if let Some(item) = iter.next() {
item.encode(w)?;
for item in iter {
w.write_byte(b',')?;
item.encode(w)?;
}
}
w.write_byte(b']')
}
#[inline]
fn encode_with_format<F: Format, W: io::WriteBuffer>(
&self,
w: &mut W,
) -> Result<(), Error> {
F::begin_array(self.len(), w)?;
let mut iter = self.iter();
if let Some(item) = iter.next() {
item.encode_with_format::<F, W>(w)?;
for item in iter {
F::array_separator(w)?;
item.encode_with_format::<F, W>(w)?;
}
}
F::end_array(w)
}
}
impl<'de, T: Decode<'de>> Decode<'de> for alloc::vec::Vec<T> {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
r.expect_byte(b'[')?;
let mut vec = alloc::vec::Vec::new();
codec::json::skip_whitespace(r);
if r.peek() == b']' {
r.advance(1);
return Ok(vec);
}
loop {
vec.push(T::decode(r)?);
codec::json::skip_comma_or_close(r, b']')?;
if r.peek() == b']' {
r.advance(1);
break;
}
}
Ok(vec)
}
}
}
macro_rules! impl_primitive {
($($ty:ty => $write_fn:ident, $read_fn:ident, $hash:literal),* $(,)?) => {
$(
impl Encode for $ty {
const SCHEMA_HASH: u64 = $hash;
#[inline(always)]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
codec::json::$write_fn(*self, w)
}
#[inline(always)]
fn encode_with_format<F: Format, W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
F::write_u64(*self as u64, w)
}
}
impl<'de> Decode<'de> for $ty {
#[inline(always)]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::$read_fn(r)
}
}
)*
};
}
impl_primitive! {
u8 => write_u8, read_u8, 0x7a3c8d2e,
u16 => write_u16, read_u16, 0x8b4d9e3f,
u32 => write_u32, read_u32, 0x9c5e0f4a,
u64 => write_u64, read_u64, 0xad6f1a5b,
i8 => write_i8, read_i8, 0xbe7a2b6c,
i16 => write_i16, read_i16, 0xcf8b3c7d,
i32 => write_i32, read_i32, 0xde9c4d8e,
i64 => write_i64, read_i64, 0xefad5e9f,
f32 => write_f32, read_f32, 0xf0be6faa,
f64 => write_f64, read_f64, 0x01cf8fbb,
bool => write_bool, read_bool, 0x12d090cc,
}
impl Encode for () {
const SCHEMA_HASH: u64 = 0x23e1a1dd;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
codec::json::write_null(w)
}
}
impl<'de> Decode<'de> for () {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::read_null(r)
}
}
impl Encode for alloc::string::String {
const SCHEMA_HASH: u64 = 0x34f2b2ee3d6e8cbb;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
codec::json::write_str(self, w)
}
#[inline]
fn encode_with_format<F: Format, W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
F::write_str(self, w)
}
}
impl<'de> Decode<'de> for alloc::string::String {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
Ok(codec::json::read_string_cow(r)?.into_owned())
}
}
impl Encode for &str {
const SCHEMA_HASH: u64 = 0x45c3c3ff4e7d9dcc;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
codec::json::write_str(self, w)
}
#[inline]
fn encode_with_format<F: Format, W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
F::write_str(self, w)
}
}
impl<'de> Decode<'de> for &'de str {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::read_string(r)
}
}
impl Encode for &[u8] {
const SCHEMA_HASH: u64 = 0x56d4d4ff5f8deed;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
codec::json::write_bytes(self, w)
}
#[inline]
fn encode_with_format<F: Format, W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
F::write_bytes(self, w)
}
}
impl<'de> Decode<'de> for &'de [u8] {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::read_bytes(r)
}
}
#[cfg(feature = "chrono")]
mod chrono_impl {
use super::*;
use chrono::{DateTime, Utc};
impl Encode for DateTime<Utc> {
const SCHEMA_HASH: u64 = 0x67e5e5ff6f9efffe;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
let s = self.to_rfc3339();
codec::json::write_str(&s, w)
}
#[inline]
fn encode_with_format<F: Format, W: io::WriteBuffer>(
&self,
w: &mut W,
) -> Result<(), Error> {
let s = self.to_rfc3339();
F::write_str(&s, w)
}
}
impl<'de> Decode<'de> for DateTime<Utc> {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
let s = codec::json::read_string(r)?;
DateTime::parse_from_rfc3339(s)
.map(|dt| dt.with_timezone(&Utc))
.map_err(|_| Error::InvalidUtf8 {
byte_offset: r.get_pos(),
})
}
}
}
#[cfg(feature = "std")]
mod hashmap_impl {
use super::*;
use std::collections::HashMap;
impl<K: Encode + core::fmt::Display, V: Encode> Encode for HashMap<K, V> {
const SCHEMA_HASH: u64 = 0;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
w.write_byte(b'{')?;
let mut first = true;
for (k, v) in self {
if !first {
w.write_byte(b',')?;
}
first = false;
let key_str = alloc::format!("{}", k);
codec::json::write_str(&key_str, w)?;
w.write_byte(b':')?;
v.encode(w)?;
}
w.write_byte(b'}')
}
}
impl<'de, V: Decode<'de>> Decode<'de> for HashMap<alloc::string::String, V> {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::skip_whitespace(r);
r.expect_byte(b'{')?;
let mut map = HashMap::new();
codec::json::skip_whitespace(r);
if r.peek() == b'}' {
r.advance(1);
return Ok(map);
}
loop {
codec::json::skip_whitespace(r);
let key = codec::json::read_string_cow(r)?.into_owned();
codec::json::skip_whitespace(r);
r.expect_byte(b':')?;
let val = V::decode(r)?;
map.insert(key, val);
codec::json::skip_comma_or_close(r, b'}')?;
if r.peek() == b'}' {
r.advance(1);
break;
}
}
Ok(map)
}
}
}
mod btreemap_impl {
use super::*;
use alloc::collections::BTreeMap;
impl<K: Encode + core::fmt::Display, V: Encode> Encode for BTreeMap<K, V> {
const SCHEMA_HASH: u64 = 0;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
w.write_byte(b'{')?;
let mut first = true;
for (k, v) in self {
if !first {
w.write_byte(b',')?;
}
first = false;
let key_str = alloc::format!("{}", k);
codec::json::write_str(&key_str, w)?;
w.write_byte(b':')?;
v.encode(w)?;
}
w.write_byte(b'}')
}
}
impl<'de, V: Decode<'de>> Decode<'de> for BTreeMap<alloc::string::String, V> {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::skip_whitespace(r);
r.expect_byte(b'{')?;
let mut map = BTreeMap::new();
codec::json::skip_whitespace(r);
if r.peek() == b'}' {
r.advance(1);
return Ok(map);
}
loop {
codec::json::skip_whitespace(r);
let key = codec::json::read_string_cow(r)?.into_owned();
codec::json::skip_whitespace(r);
r.expect_byte(b':')?;
let val = V::decode(r)?;
map.insert(key, val);
codec::json::skip_comma_or_close(r, b'}')?;
if r.peek() == b'}' {
r.advance(1);
break;
}
}
Ok(map)
}
}
}
macro_rules! impl_tuple {
($($idx:tt $T:ident),+) => {
impl<$($T: Encode),+> Encode for ($($T,)+) {
const SCHEMA_HASH: u64 = 0;
#[inline]
fn encode<W: io::WriteBuffer>(&self, w: &mut W) -> Result<(), Error> {
w.write_byte(b'[')?;
impl_tuple!(@encode self w $($idx $T),+);
w.write_byte(b']')
}
}
impl<'de, $($T: Decode<'de>),+> Decode<'de> for ($($T,)+) {
#[inline]
fn decode(r: &mut io::ReadBuffer<'de>) -> Result<Self, Error> {
codec::json::skip_whitespace(r);
r.expect_byte(b'[')?;
impl_tuple!(@decode r $($idx $T),+);
codec::json::skip_whitespace(r);
r.expect_byte(b']')?;
Ok(($($T,)+))
}
}
};
(@encode $self:ident $w:ident $first_idx:tt $first_T:ident $(, $idx:tt $T:ident)*) => {
$self.$first_idx.encode($w)?;
$(
$w.write_byte(b',')?;
$self.$idx.encode($w)?;
)*
};
(@decode $r:ident $first_idx:tt $first_T:ident $(, $idx:tt $T:ident)*) => {
codec::json::skip_whitespace($r);
#[allow(non_snake_case)]
let $first_T = $first_T::decode($r)?;
$(
codec::json::skip_comma_or_close($r, b']')?;
#[allow(non_snake_case)]
let $T = $T::decode($r)?;
)*
};
}
impl_tuple!(0 A);
impl_tuple!(0 A, 1 B);
impl_tuple!(0 A, 1 B, 2 C);
impl_tuple!(0 A, 1 B, 2 C, 3 D);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2, 6 G);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2, 6 G, 7 H);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2, 6 G, 7 H, 8 I2);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2, 6 G, 7 H, 8 I2, 9 J);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2, 6 G, 7 H, 8 I2, 9 J, 10 K);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E2, 5 F2, 6 G, 7 H, 8 I2, 9 J, 10 K, 11 L);
#[cfg(feature = "msgpack")]
pub mod msgpack {
pub use crate::codec::msgpack::*;
}