diff --git a/Cargo.toml b/Cargo.toml index 184c14a..3586c9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bipack_ru" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "Apache-2.0" description = "binary size-effective format used in Divan smart contracts, wasm bindings, network protocols, etc." diff --git a/README.md b/README.md index a2441d9..7c8f719 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,45 @@ # bipack_ru -> This is yet an alpha. We are still experimenting with the interface. 0.1.* could -> be backward incompatible! +This is Bipack format implementation, minimalistic by purpose. + +> work in progress. + +## Already implemented: + +The following parts are already safe to use + +- u8, u16, u32, u64, `smartint` variable-length unsigned +- i8, i16, i32, i64, `smartint` variable-length signed +- strings (utf8, variable length) +- fixed byte arrays +- variable length byte arrays + +The sample code (see `src/lib.rs` for more:) +```rust +fn test() { + let mut data = Vec::::new(); + data.put_str("Hello, rupack!"); + println!("size ${}\n{}", data.len(), to_dump(&data)); + let mut src = SliceSource::from(&data); + assert_eq!("Hello, rupack!", src.get_str().unwrap()); +} +``` + +## Tools and macros + +- `to_dump` to convert binary slice into human-readable dump +- 'StringBuilder' super minimalistic string builder (footprint). -Bipack format implementation, minimalistic by purpose. At the moment it does not include `serde` module as it is yet unclear how much it will increase .wasm size. Could be added later. The autodoc documentation is good enough already, so we do not repeat it here now. +## How to + +- just ad this package to your dependencies, it is on crates.io. + # License For compliance with other modules this work is provided under APACHE 2.0 license a copy of which is included in the file `LICENSE`. \ No newline at end of file diff --git a/src/bipack_sink.rs b/src/bipack_sink.rs index bb8c5df..55839e3 100644 --- a/src/bipack_sink.rs +++ b/src/bipack_sink.rs @@ -54,12 +54,12 @@ pub trait BipackSink { for b in data { self.put_u8(*b); } } - fn put_var_bytes(self: &mut Self,data: &[u8]) { + fn put_var_bytes(self: &mut Self, data: &[u8]) { self.put_unsigned(data.len()); self.put_fixed_bytes(data); } - fn put_str(self: &mut Self,str: &str) { + fn put_str(self: &mut Self, str: &str) { self.put_var_bytes(str.as_bytes()); } @@ -118,20 +118,30 @@ pub trait BipackSink { }; if value < V0LIMIT { - encode_seq(0, &[value]); - } - else if value < V1LIMIT { - encode_seq( 1, &[value & 0x3F, value >> 6]); - } - else if value < V2LIMIT { - encode_seq( 2, &[value & 0x3f, value >> 6, value >> 14]); - } - else { + encode_seq(0, &[value]); + } else if value < V1LIMIT { + encode_seq(1, &[value & 0x3F, value >> 6]); + } else if value < V2LIMIT { + encode_seq(2, &[value & 0x3f, value >> 6, value >> 14]); + } else { encode_seq(3, &[value & 0x3f, value >> 6, value >> 14]); self.put_var_unsigned(value >> 22); } } + /// Put variable-length encoded integer value. it is packed just like variable-length + /// unsigned value except that LSB (bit 0) is used as negative number flag (when set, + /// the encoded number is negative). + /// + /// Note that because of this the range of supported integers is one bit smaller than + /// i64, only 30 bits for value and one for a sign. This will probably be fixed later + /// but please note that it is impractical to store really big numbers in variable-length + /// format, consider using [put_i64] instead which has no such limitation. + fn put_signed(self: &mut Self, val: i64) { + let (neg, val) = if val < 0 { (1, -val) } else { (0, val) }; + self.put_unsigned( (neg as u64) | ((val as u64) << 1) ); + } + fn put_var_unsigned(self: &mut Self, value: u64) { let mut rest = value; loop { diff --git a/src/bipack_source.rs b/src/bipack_source.rs index f04185f..cf0cae6 100644 --- a/src/bipack_source.rs +++ b/src/bipack_source.rs @@ -97,6 +97,14 @@ pub trait BipackSource { Ok(result | (self.get_varint_unsigned()? << 22)) } + /// Unpack variable-length signed value, packed with + /// [crate::bipack_sink::BipackSink::put_signed], see it for the packing details. + fn get_signed(self: &mut Self) -> Result { + let value = self.get_unsigned()?; + let result = (value >> 1) as i64; + Ok(if value & 1 != 0 { -result } else { result } ) + } + /// read 8-bytes varint-packed unsigned value from the source. We dont' recommend /// using it directly; use [BipackSource::get_unsigned] instead. fn get_varint_unsigned(self: &mut Self) -> Result { diff --git a/src/lib.rs b/src/lib.rs index f9a4a25..543aab0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -245,6 +245,33 @@ mod tests { } #[test] + fn test_varsigned() -> Result<()> { + fn test(value: i64) -> Result<()> { + let mut x = Vec::new(); + x.put_signed(value); + assert_eq!(value, SliceSource::from(&x).get_signed()?); + Ok(()) + } + fn test2(value: i64) -> Result<()> { + test(value)?; + test(-value)?; + Ok(()) + } + test(0)?; + test2(1)?; + test2(2)?; + test2(64)?; + test2(65)?; + test2(127)?; + test2(128)?; + test2(255)?; + test2(256)?; + test2(2147483647)?; + test2(2222147483647)?; + Ok(()) + } + + #[test] fn test_packer() -> Result<()>{ let a = 177u32; let b = "hello!";