Skip to content

Commit

Permalink
feat(msgpack): add msgpack serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
vyfor committed Dec 6, 2024
1 parent a6697da commit 1f7c2a6
Show file tree
Hide file tree
Showing 4 changed files with 410 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod ipc;
mod json;
mod mappings;
mod messages;
mod msgpack;
mod presence;
mod types;
mod util;
Expand Down
81 changes: 81 additions & 0 deletions src/msgpack/mod.rs
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
}
}
195 changes: 195 additions & 0 deletions src/msgpack/serialize.rs
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)
}
}
Loading

0 comments on commit 1f7c2a6

Please sign in to comment.