+variable-length signed support

This commit is contained in:
Sergey Chernov 2023-10-12 00:40:15 +01:00
parent 588446cd42
commit e0ac1b1094
5 changed files with 90 additions and 15 deletions

View File

@ -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."

View File

@ -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::<u8>::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`.

View File

@ -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 {

View File

@ -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<i64> {
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<u64> {

View File

@ -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!";