-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(msgpack): add msgpack serialization
- Loading branch information
Showing
4 changed files
with
410 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ mod ipc; | |
mod json; | ||
mod mappings; | ||
mod messages; | ||
mod msgpack; | ||
mod presence; | ||
mod types; | ||
mod util; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#![allow(unused)] | ||
|
||
pub mod deserialize; | ||
pub mod serialize; | ||
pub mod value; | ||
|
||
pub use deserialize::Deserialize; | ||
pub use serialize::Serialize; | ||
pub use serialize::SerializeFn; | ||
pub use serialize::SerializeObj; | ||
pub use serialize::SerializeState; | ||
pub use value::Value; | ||
pub use value::ValueRef; | ||
|
||
#[derive(Debug)] | ||
pub struct MsgPack; | ||
|
||
pub const NIL: u8 = 0xc0; | ||
pub const FALSE: u8 = 0xc2; | ||
pub const TRUE: u8 = 0xc3; | ||
pub const INT8: u8 = 0xd0; | ||
pub const INT16: u8 = 0xd1; | ||
pub const INT32: u8 = 0xd2; | ||
pub const INT64: u8 = 0xd3; | ||
pub const FLOAT32: u8 = 0xca; | ||
pub const FLOAT64: u8 = 0xcb; | ||
pub const STR16: u8 = 0xda; | ||
pub const STR32: u8 = 0xdb; | ||
pub const ARRAY16: u8 = 0xdc; | ||
pub const ARRAY32: u8 = 0xdd; | ||
pub const MAP16: u8 = 0xde; | ||
pub const MAP32: u8 = 0xdf; | ||
pub const UINT8: u8 = 0xcc; | ||
pub const UINT16: u8 = 0xcd; | ||
pub const UINT32: u8 = 0xce; | ||
pub const UINT64: u8 = 0xcf; | ||
|
||
pub const POSITIVE_FIXINT_MASK: u8 = 0x80; | ||
pub const POSITIVE_FIXINT_VALUE: u8 = 0x00; | ||
|
||
pub const NEGATIVE_FIXINT_MASK: u8 = 0xe0; | ||
pub const NEGATIVE_FIXINT_VALUE: u8 = 0xe0; | ||
|
||
pub const FIXSTR_MASK: u8 = 0xe0; | ||
pub const FIXSTR_VALUE: u8 = 0xa0; | ||
pub const FIXSTR_SIZE_MASK: u8 = 0x1f; | ||
|
||
pub const FIXARRAY_MASK: u8 = 0xf0; | ||
pub const FIXARRAY_VALUE: u8 = 0x90; | ||
pub const FIXARRAY_SIZE_MASK: u8 = 0x0f; | ||
|
||
pub const FIXMAP_MASK: u8 = 0xf0; | ||
pub const FIXMAP_VALUE: u8 = 0x80; | ||
pub const FIXMAP_SIZE_MASK: u8 = 0x0f; | ||
|
||
#[derive(Debug)] | ||
pub enum Error { | ||
InvalidUtf8, | ||
InvalidLength, | ||
InvalidMapKey, | ||
UnexpectedByte(u8), | ||
} | ||
|
||
impl std::fmt::Display for Error { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Error::InvalidUtf8 => write!(f, "invalid utf8"), | ||
Error::InvalidLength => write!(f, "invalid length"), | ||
Error::InvalidMapKey => write!(f, "map key must be string"), | ||
Error::UnexpectedByte(b) => write!(f, "unexpected byte: {:#x}", b), | ||
} | ||
} | ||
} | ||
|
||
impl std::error::Error for Error {} | ||
|
||
impl From<std::string::FromUtf8Error> for Error { | ||
fn from(_: std::string::FromUtf8Error) -> Self { | ||
Error::InvalidUtf8 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
#![allow(dead_code)] | ||
|
||
use super::{ | ||
value::ValueRef, Error, MsgPack, ARRAY16, ARRAY32, FALSE, FIXARRAY_VALUE, FIXSTR_VALUE, | ||
FLOAT64, INT64, NIL, STR16, STR32, TRUE, UINT16, UINT32, UINT64, UINT8, | ||
}; | ||
|
||
pub type SerializeFn<'a> = fn(&'a str, ValueRef<'a>, &mut SerializeState) -> crate::Result<()>; | ||
|
||
pub trait Serialize { | ||
fn serialize<'a>(&'a self, f: SerializeFn<'a>, state: &mut SerializeState) | ||
-> crate::Result<()>; | ||
} | ||
|
||
pub trait SerializeObj: Serialize + std::fmt::Debug {} | ||
impl<T: Serialize + std::fmt::Debug> SerializeObj for T {} | ||
|
||
pub struct SerializeState { | ||
buf: Vec<u8>, | ||
stack: Vec<usize>, | ||
} | ||
|
||
impl SerializeState { | ||
fn new() -> Self { | ||
Self { | ||
buf: Vec::with_capacity(512), | ||
stack: Vec::with_capacity(8), | ||
} | ||
} | ||
|
||
fn push_scope(&mut self) { | ||
self.stack.push(self.buf.len()); | ||
} | ||
|
||
fn write_u8(&mut self, v: u8) { | ||
self.buf.push(v); | ||
} | ||
|
||
fn write_u16(&mut self, v: u16) { | ||
self.buf.extend_from_slice(&v.to_be_bytes()); | ||
} | ||
|
||
fn write_u32(&mut self, v: u32) { | ||
self.buf.extend_from_slice(&v.to_be_bytes()); | ||
} | ||
|
||
fn write_i64(&mut self, v: i64) { | ||
self.buf.extend_from_slice(&v.to_be_bytes()); | ||
} | ||
|
||
fn write_f64(&mut self, v: f64) { | ||
self.buf.extend_from_slice(&v.to_be_bytes()); | ||
} | ||
|
||
fn write_u64(&mut self, v: u64) { | ||
self.buf.extend_from_slice(&v.to_be_bytes()); | ||
} | ||
|
||
fn write_bytes(&mut self, bytes: &[u8]) { | ||
self.buf.extend_from_slice(bytes); | ||
} | ||
} | ||
|
||
impl<'a> std::fmt::Debug for ValueRef<'a> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
ValueRef::String(s) => write!(f, "String({:?})", s), | ||
ValueRef::Float(n) => write!(f, "Number({:?})", n), | ||
ValueRef::Integer(i) => write!(f, "Integer({:?})", i), | ||
ValueRef::UInteger(u) => write!(f, "UInteger({:?})", u), | ||
ValueRef::Boolean(b) => write!(f, "Boolean({:?})", b), | ||
ValueRef::Array(arr) => f.debug_tuple("Array").field(arr).finish(), | ||
ValueRef::Object(_) => write!(f, "Object(function)"), | ||
ValueRef::Nil => write!(f, "Nil"), | ||
} | ||
} | ||
} | ||
|
||
impl MsgPack { | ||
pub fn serialize(value: &dyn Serialize) -> crate::Result<Vec<u8>> { | ||
let mut state = SerializeState::new(); | ||
state.write_u8(0x81); | ||
|
||
fn serialize_adapter( | ||
key: &str, | ||
value: ValueRef, | ||
state: &mut SerializeState, | ||
) -> crate::Result<()> { | ||
MsgPack::write_kv(key, &value, state) | ||
} | ||
|
||
value.serialize(serialize_adapter, &mut state)?; | ||
Ok(state.buf) | ||
} | ||
|
||
fn write_str(s: &str, state: &mut SerializeState) -> crate::Result<()> { | ||
let bytes = s.as_bytes(); | ||
let len = bytes.len(); | ||
match len { | ||
0..=31 => { | ||
state.write_u8(FIXSTR_VALUE | (len as u8)); | ||
} | ||
32..=65535 => { | ||
state.write_u8(STR16); | ||
state.write_u16(len as u16); | ||
} | ||
_ => { | ||
if len > u32::MAX as usize { | ||
return Err(Error::InvalidLength.into()); | ||
} | ||
state.write_u8(STR32); | ||
state.write_u32(len as u32); | ||
} | ||
} | ||
state.write_bytes(bytes); | ||
Ok(()) | ||
} | ||
|
||
fn write_value(value: &ValueRef, state: &mut SerializeState) -> crate::Result<()> { | ||
match value { | ||
ValueRef::Nil => { | ||
state.write_u8(NIL); | ||
Ok(()) | ||
} | ||
ValueRef::String(s) => Self::write_str(s, state), | ||
ValueRef::Float(n) => { | ||
state.write_u8(FLOAT64); | ||
state.write_f64(*n); | ||
Ok(()) | ||
} | ||
ValueRef::Integer(n) => { | ||
if *n >= -(1 << 5) && *n < (1 << 7) { | ||
state.write_u8(*n as u8); | ||
} else { | ||
state.write_u8(INT64); | ||
state.write_i64(*n); | ||
} | ||
Ok(()) | ||
} | ||
ValueRef::UInteger(n) => { | ||
if *n < (1 << 8) { | ||
state.write_u8(UINT8); | ||
state.write_u8(*n as u8); | ||
} else if *n < (1 << 16) { | ||
state.write_u8(UINT16); | ||
state.write_u16(*n as u16); | ||
} else if *n < (1 << 32) { | ||
state.write_u8(UINT32); | ||
state.write_u32(*n as u32); | ||
} else { | ||
state.write_u8(UINT64); | ||
state.write_u64(*n); | ||
} | ||
Ok(()) | ||
} | ||
ValueRef::Boolean(b) => { | ||
state.write_u8(if *b { TRUE } else { FALSE }); | ||
Ok(()) | ||
} | ||
ValueRef::Array(arr) => { | ||
let len = arr.len(); | ||
match len { | ||
0..=15 => { | ||
state.write_u8(FIXARRAY_VALUE | (len as u8)); | ||
} | ||
16..=65535 => { | ||
state.write_u8(ARRAY16); | ||
state.write_u16(len as u16); | ||
} | ||
_ => { | ||
if len > u32::MAX as usize { | ||
return Err(Error::InvalidLength.into()); | ||
} | ||
state.write_u8(ARRAY32); | ||
state.write_u32(len as u32); | ||
} | ||
} | ||
for item in arr { | ||
Self::write_value(item, state)?; | ||
} | ||
Ok(()) | ||
} | ||
ValueRef::Object(obj) => { | ||
state.push_scope(); | ||
obj.serialize(|k, v, s| Self::write_kv(k, &v, s), state)?; | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
fn write_kv(key: &str, value: &ValueRef, state: &mut SerializeState) -> crate::Result<()> { | ||
Self::write_str(key, state)?; | ||
Self::write_value(value, state) | ||
} | ||
} |
Oops, something went wrong.