From f6a554e6843f154c568e48c535a12ae7e6b9d93f Mon Sep 17 00:00:00 2001 From: Artemy Egorov Date: Fri, 2 Aug 2024 00:39:06 +0300 Subject: [PATCH] feat: daletpack encode --- libs/rust/.gitignore | 3 +- libs/rust/Cargo.toml | 3 +- libs/rust/src/abstractions.rs | 64 ++++++++------- libs/rust/src/daletl.rs | 28 ++++++- libs/rust/src/daletpack/encode.rs | 127 ++++++++++++++++++++++++++++++ libs/rust/src/daletpack/mod.rs | 6 ++ libs/rust/src/daletpack/types.rs | 6 ++ libs/rust/src/daletpack/utils.rs | 9 +++ libs/rust/src/lib.rs | 3 + libs/rust/src/main.rs | 25 ++++-- 10 files changed, 235 insertions(+), 39 deletions(-) create mode 100644 libs/rust/src/daletpack/encode.rs create mode 100644 libs/rust/src/daletpack/mod.rs create mode 100644 libs/rust/src/daletpack/types.rs create mode 100644 libs/rust/src/daletpack/utils.rs diff --git a/libs/rust/.gitignore b/libs/rust/.gitignore index c41cc9e..20e8ea8 100644 --- a/libs/rust/.gitignore +++ b/libs/rust/.gitignore @@ -1 +1,2 @@ -/target \ No newline at end of file +/target +test.daletpack diff --git a/libs/rust/Cargo.toml b/libs/rust/Cargo.toml index 48b0cfb..7ff425b 100644 --- a/libs/rust/Cargo.toml +++ b/libs/rust/Cargo.toml @@ -18,5 +18,6 @@ serde = { version = "1.0", features = ["derive"] } serde_repr = "0.1" [features] -default = ["types"] +default = ["types", "daletpack"] types = [] +daletpack = ["types"] diff --git a/libs/rust/src/abstractions.rs b/libs/rust/src/abstractions.rs index 68aa44f..e4312a7 100644 --- a/libs/rust/src/abstractions.rs +++ b/libs/rust/src/abstractions.rs @@ -2,8 +2,8 @@ use num_enum::TryFromPrimitive; use crate::daletl::{self, t_new, Tid}; -const BN: daletl::Body = daletl::Body::Null; -const AN: daletl::Argument = daletl::Argument::Null; +const NB: daletl::Body = daletl::Body::Null; +const NA: daletl::Argument = daletl::Argument::Null; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Tag { @@ -37,6 +37,10 @@ pub enum Tag { Carousel(Vec), } +pub trait ToDaletl { + fn to_daletl(self) -> Vec; +} + pub trait ToDaletlTag { fn to_daletl_tag(self) -> daletl::Tag; } @@ -44,34 +48,34 @@ pub trait ToDaletlTag { impl ToDaletlTag for Tag { fn to_daletl_tag(self) -> daletl::Tag { match self { - Tag::El(b) => t_new(Tid::El, b.to_daletl_body(), AN), + Tag::El(b) => t_new(Tid::El, b.to_daletl_body(), NA), Tag::H(b, a) => t_new(Tid::H, b.to_daletl_body(), a.to_daletl_argument()), - Tag::P(b) => t_new(Tid::P, b.to_daletl_body(), AN), - Tag::Br => t_new(Tid::Br, BN, AN), - Tag::Ul(b) => t_new(Tid::Ul, b.to_daletl_body(), AN), - Tag::Ol(b) => t_new(Tid::Ol, b.to_daletl_body(), AN), + Tag::P(b) => t_new(Tid::P, b.to_daletl_body(), NA), + Tag::Br => t_new(Tid::Br, NB, NA), + Tag::Ul(b) => t_new(Tid::Ul, b.to_daletl_body(), NA), + Tag::Ol(b) => t_new(Tid::Ol, b.to_daletl_body(), NA), Tag::Row(b, a) => t_new(Tid::Row, b.to_daletl_body(), a.to_daletl_argument()), Tag::Link(b, a) => t_new(Tid::Link, b.to_daletl_body(), a.to_daletl_argument()), Tag::Navlink(b, a) => t_new(Tid::Navlink, b.to_daletl_body(), a.to_daletl_argument()), Tag::Btn(b, a) => t_new(Tid::Btn, b.to_daletl_body(), a.to_daletl_argument()), Tag::Navbtn(b, a) => t_new(Tid::Navbtn, b.to_daletl_body(), a.to_daletl_argument()), - Tag::Img(a) => t_new(Tid::Img, BN, a.to_daletl_argument()), - Tag::Table(b) => t_new(Tid::Table, b.to_daletl_body(), AN), - Tag::Tcol(b) => t_new(Tid::Tcol, b.to_daletl_body(), AN), - Tag::Tpcol(b) => t_new(Tid::Tpcol, b.to_daletl_body(), AN), - Tag::Hr => t_new(Tid::Hr, BN, AN), - Tag::B(b) => t_new(Tid::B, b.to_daletl_body(), AN), - Tag::I(b) => t_new(Tid::I, b.to_daletl_body(), AN), - Tag::Bq(b) => t_new(Tid::Bq, b.to_daletl_body(), AN), - Tag::Footlnk(a) => t_new(Tid::Footlnk, BN, a.to_daletl_argument()), + Tag::Img(a) => t_new(Tid::Img, NB, a.to_daletl_argument()), + Tag::Table(b) => t_new(Tid::Table, b.to_daletl_body(), NA), + Tag::Tcol(b) => t_new(Tid::Tcol, b.to_daletl_body(), NA), + Tag::Tpcol(b) => t_new(Tid::Tpcol, b.to_daletl_body(), NA), + Tag::Hr => t_new(Tid::Hr, NB, NA), + Tag::B(b) => t_new(Tid::B, b.to_daletl_body(), NA), + Tag::I(b) => t_new(Tid::I, b.to_daletl_body(), NA), + Tag::Bq(b) => t_new(Tid::Bq, b.to_daletl_body(), NA), + Tag::Footlnk(a) => t_new(Tid::Footlnk, NB, a.to_daletl_argument()), Tag::Footn(b, a) => t_new(Tid::Footn, b.to_daletl_body(), a.to_daletl_argument()), - Tag::A(a) => t_new(Tid::A, BN, a.to_daletl_argument()), - Tag::S(b) => t_new(Tid::S, b.to_daletl_body(), AN), - Tag::Sup(b) => t_new(Tid::Sup, b.to_daletl_body(), AN), - Tag::Sub(b) => t_new(Tid::Sub, b.to_daletl_body(), AN), - Tag::Disc(b) => t_new(Tid::Disc, b.to_daletl_body(), AN), + Tag::A(a) => t_new(Tid::A, NB, a.to_daletl_argument()), + Tag::S(b) => t_new(Tid::S, b.to_daletl_body(), NA), + Tag::Sup(b) => t_new(Tid::Sup, b.to_daletl_body(), NA), + Tag::Sub(b) => t_new(Tid::Sub, b.to_daletl_body(), NA), + Tag::Disc(b) => t_new(Tid::Disc, b.to_daletl_body(), NA), Tag::Bl(b, a) => t_new(Tid::Bl, b.to_daletl_body(), a.to_daletl_argument()), - Tag::Carousel(b) => t_new(Tid::Disc, b.to_daletl_body(), AN), + Tag::Carousel(b) => t_new(Tid::Carousel, b.to_daletl_body(), NA), } } } @@ -102,7 +106,7 @@ pub enum HeadingLevel { impl ToDaletlArgument for HeadingLevel { fn to_daletl_argument(self) -> daletl::Argument { match self { - HeadingLevel::One => 1u8.to_daletl_argument(), + HeadingLevel::One => NA, HeadingLevel::Two => 2u8.to_daletl_argument(), HeadingLevel::Three => 3u8.to_daletl_argument(), HeadingLevel::Four => 4u8.to_daletl_argument(), @@ -127,7 +131,7 @@ pub enum AlignArgument { impl ToDaletlArgument for AlignArgument { fn to_daletl_argument(self) -> daletl::Argument { match self { - Self::Start => 0u8.to_daletl_argument(), + Self::Start => NA, Self::Center => 1u8.to_daletl_argument(), Self::End => 2u8.to_daletl_argument(), } @@ -159,7 +163,7 @@ pub enum Body { impl ToDaletlBody for Body { fn to_daletl_body(self) -> daletl::Body { match self { - Body::Null => daletl::Body::Null, + Body::Null => NB, Body::Tags(v) => v.to_daletl_body(), Body::Text(v) => v.to_daletl_body(), } @@ -176,7 +180,7 @@ pub enum Argument { impl ToDaletlArgument for Argument { fn to_daletl_argument(self) -> daletl::Argument { match self { - Argument::Null => daletl::Argument::Null, + Argument::Null => NA, Argument::Number(v) => v.to_daletl_argument(), Argument::Text(v) => v.to_daletl_argument(), } @@ -215,7 +219,13 @@ impl ToDaletlBody for NotNullBody { impl ToDaletlBody for Vec { fn to_daletl_body(self) -> daletl::Body { - daletl::Body::Tags(self.into_iter().map(|tag| tag.to_daletl_tag()).collect()) + daletl::Body::Tags(self.to_daletl()) + } +} + +impl ToDaletl for Vec { + fn to_daletl(self) -> Vec { + self.into_iter().map(|tag| tag.to_daletl_tag()).collect() } } diff --git a/libs/rust/src/daletl.rs b/libs/rust/src/daletl.rs index 52bcf8c..1430a34 100644 --- a/libs/rust/src/daletl.rs +++ b/libs/rust/src/daletl.rs @@ -5,9 +5,9 @@ use num_enum::TryFromPrimitive; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Tag { - id: Tid, - body: Body, - argument: Argument, + pub id: Tid, + pub body: Body, + pub argument: Argument, } impl Tag { @@ -21,6 +21,10 @@ pub fn t_new(id: Tid, body: Body, argument: Argument) -> Tag { Tag::new(id, body, argument) } +pub trait IsNull { + fn is_null(&self) -> bool; +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum Body { @@ -29,6 +33,15 @@ pub enum Body { Null, } +impl IsNull for Body { + fn is_null(&self) -> bool { + match self { + Self::Null => true, + _ => false, + } + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum Argument { @@ -37,6 +50,15 @@ pub enum Argument { Null, } +impl IsNull for Argument { + fn is_null(&self) -> bool { + match self { + Self::Null => true, + _ => false, + } + } +} + #[derive(Serialize_repr, Deserialize_repr, Debug, Clone, PartialEq, Eq, TryFromPrimitive)] #[repr(u8)] /// Tag Id diff --git a/libs/rust/src/daletpack/encode.rs b/libs/rust/src/daletpack/encode.rs new file mode 100644 index 0000000..d087dc6 --- /dev/null +++ b/libs/rust/src/daletpack/encode.rs @@ -0,0 +1,127 @@ +use bitvec::{ + bits, + order::Msb0, + prelude::{BitVec, Lsb0}, + view::{AsBits, BitView}, +}; + +use crate::daletl::{Argument, Body, IsNull, Tag}; + +use super::utils::*; +use super::DaletPackError; + +pub fn encode(root: Vec) -> Result, DaletPackError> { + if root.len() > 2usize.pow(32) { + return Err(DaletPackError::RootMaxSizeExceeded); + } + + let mut bv: BitVec = BitVec::new(); + + for tag in root { + write_tag(&mut bv, tag)?; + } + + bv.set_uninitialized(false); + Ok(bv.into_vec()) +} + +fn write_int(bv: &mut BitVec, n: u8) { + if n < 16 { + write_4bit(bv, 0); + write_4bit(bv, n); + } else { + write_4bit(bv, 1); + bv.extend_from_raw_slice(&[n]); + } +} + +fn write_str(bv: &mut BitVec, string: String) -> Result<(), DaletPackError> { + let size = string.len(); + + if size > 2usize.pow(32) { + return Err(DaletPackError::StrMaxSizeExceeded); + } + + if size <= 8 { + write_4bit(bv, 2); + write_3bit(bv, (size - 1) as u8); + } else if size <= 16 { + write_4bit(bv, 3); + write_4bit(bv, (size - 1) as u8); + } else if size <= 256 { + write_4bit(bv, 4); + bv.extend_from_raw_slice(&[(size - 1) as u8]); + } else if size <= 65536 { + write_4bit(bv, 5); + bv.extend_from_bitslice(&((size - 1) as u16).view_bits::()); + } else { + write_4bit(bv, 6); + bv.extend_from_bitslice(&((size - 1) as u32).view_bits::()); + } + + bv.extend_from_bitslice(&string.as_bits::()); + + Ok(()) +} + +fn write_array(bv: &mut BitVec, arr: Vec) -> Result<(), DaletPackError> { + if arr.len() > 2usize.pow(32) { + return Err(DaletPackError::ArrMaxSizeExceeded); + } + + write_4bit(bv, 7); + + for tag in arr { + write_tag(bv, tag)?; + } + + bv.extend_from_bitslice(&bits![1, 0]); + + Ok(()) +} + +fn write_tag(bv: &mut BitVec, tag: Tag) -> Result<(), DaletPackError> { + if tag.body.is_null() && tag.argument.is_null() { + write_4bit(bv, 15); + write_tag_id(bv, tag.id as u8); + } else if tag.argument.is_null() { + write_4bit(bv, 13); + write_tag_id(bv, tag.id as u8); + write_tag_body(bv, tag.body)?; + } else if tag.body.is_null() { + write_4bit(bv, 14); + write_tag_id(bv, tag.id as u8); + write_tag_argument(bv, tag.argument)?; + } else { + write_4bit(bv, 15); + write_tag_id(bv, tag.id as u8); + write_tag_body(bv, tag.body)?; + write_tag_argument(bv, tag.argument)?; + } + + Ok(()) +} + +fn write_tag_id(bv: &mut BitVec, n: u8) { + bv.extend_from_bitslice(&n.view_bits::()[3..=7]); +} + +fn write_tag_body(bv: &mut BitVec, body: Body) -> Result<(), DaletPackError> { + match body { + Body::Text(s) => write_str(bv, s)?, + Body::Tags(tags) => write_array(bv, tags)?, + Body::Null => unreachable!("This function cannot be called with this value"), + }; + + Ok(()) +} + +fn write_tag_argument(bv: &mut BitVec, argument: Argument) -> Result<(), DaletPackError> { + match argument { + Argument::Text(s) => write_str(bv, s)?, + Argument::Number(n) => write_int(bv, n), + Argument::Null => unreachable!("This function cannot be called with this value"), + }; + + Ok(()) +} diff --git a/libs/rust/src/daletpack/mod.rs b/libs/rust/src/daletpack/mod.rs new file mode 100644 index 0000000..12140f5 --- /dev/null +++ b/libs/rust/src/daletpack/mod.rs @@ -0,0 +1,6 @@ +mod encode; +mod types; + +pub mod utils; +pub use encode::encode; +pub use types::*; diff --git a/libs/rust/src/daletpack/types.rs b/libs/rust/src/daletpack/types.rs new file mode 100644 index 0000000..371a638 --- /dev/null +++ b/libs/rust/src/daletpack/types.rs @@ -0,0 +1,6 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DaletPackError { + StrMaxSizeExceeded, + ArrMaxSizeExceeded, + RootMaxSizeExceeded, +} diff --git a/libs/rust/src/daletpack/utils.rs b/libs/rust/src/daletpack/utils.rs new file mode 100644 index 0000000..4154b89 --- /dev/null +++ b/libs/rust/src/daletpack/utils.rs @@ -0,0 +1,9 @@ +use bitvec::{order::Msb0, prelude::BitVec, view::BitView}; + +pub fn write_3bit(bv: &mut BitVec, n: u8) { + bv.extend_from_bitslice(&n.view_bits::()[5..=7]); +} + +pub fn write_4bit(bv: &mut BitVec, n: u8) { + bv.extend_from_bitslice(&n.view_bits::()[4..=7]); +} diff --git a/libs/rust/src/lib.rs b/libs/rust/src/lib.rs index 1b0a58c..ed28b94 100644 --- a/libs/rust/src/lib.rs +++ b/libs/rust/src/lib.rs @@ -3,3 +3,6 @@ pub mod daletl; #[cfg(feature = "types")] pub mod abstractions; + +#[cfg(feature = "daletpack")] +pub mod daletpack; diff --git a/libs/rust/src/main.rs b/libs/rust/src/main.rs index a97cdb5..490674e 100644 --- a/libs/rust/src/main.rs +++ b/libs/rust/src/main.rs @@ -1,10 +1,21 @@ -use dalet::daletl::{Argument, Body, Tag, Tid}; +use std::fs; + +use dalet::{ + abstractions::{HeadingLevel, Tag, ToDaletl}, + daletpack::*, +}; fn main() { - let _ = Tag::new( - Tid::H, - Body::Text("I am Heading".to_string()), - Argument::Null, - ); - println!("Hello, world!"); + let dalet_page: Vec = vec![Tag::H("I am heading".to_owned(), HeadingLevel::One)]; + + let data = encode(dalet_page.to_daletl()).unwrap(); + + println!("{:#?}", data); + println!("{}", data.len()); + + let bits: Vec<_> = data.iter().map(|n| format!("{:b}", n)).collect(); + + println!("{}", bits.join("")); + // 11010000100111011010010010010000110000101101101001000011010011001010110000101100101101001011011111001111111111 + fs::write("./test.daletpack", data).unwrap(); }