Giter Site home page Giter Site logo

bs58-rs's Introduction

version-badge license-badge rust-version-badge

Another Rust Base58 codec implementation.

Compared to base58 this is significantly faster at decoding (about 2.4x as fast when decoding 32 bytes), almost the same speed for encoding (about 3% slower when encoding 32 bytes), doesn't have the 128 byte limitation and supports a configurable alphabet.

Compared to rust-base58 this is massively faster (over ten times as fast when decoding 32 bytes, almost 40 times as fast when encoding 32 bytes), has no external dependencies and supports a configurable alphabet.

Rust Version Policy

This crate only supports the current stable version of Rust, patch releases may use new features at any time.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.

bs58-rs's People

Contributors

bors[bot] avatar dependabot-preview[bot] avatar fanatid avatar ilblackdragon avatar joncinque avatar karrq avatar koushiro avatar kpcyrd avatar madninja avatar mina86 avatar nemo157 avatar t-nelson avatar twchn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

bs58-rs's Issues

Doesn't decode 32 bytes of 0x00

Due to line let mut output = vec![0; (self.input.as_ref().len() / 8 + 1) * 6]; in into_vec it only allocates 30 bytes (encoding is 11111111111111111111111111111111 which has length of 32).

Simple unit test:

let data = vec![0; 32];
let s = b58::encode(&data).to_string();
let result = b58::decode(&s).into_vec().unwrap(); // crash
assert_eq!(result, data);

Truncating behavior is confusing and forces allocations

I expect the following test to pass:

#[test]
fn append() {
    let mut buf = "hello world".to_string();
    bs58::encode(&[92]).into(&mut buf).unwrap();
    assert_eq!("hello world2b", buf.as_str());
}

Instead, it fails, as the buf contains just "2b". That is, encoding discards existing data, rather than appending to it.

There are two problems with it:

  • it is surprising behavior. Standard library APIs like read_line always append. If overwriting is desired, the caller can call .clear()
  • it forces can force an allocation, if the user actually wants to append data to some existing buffer. This comes up when, for exmple, using sri-encoding hashes: "<algo-name>-<base58 encoded bytes>".

encode() is O(n^2)

    for &val in input.clone() {
        let mut carry = val as usize;
        for byte in &mut output[..] {
            carry += (*byte as usize) << 8;
            *byte = (carry % 58) as u8;
            carry /= 58;
        }
        while carry > 0 {
            output.push((carry % 58) as u8);
            carry /= 58;
        }
}

This is insanely slow on large inputs

crates release?

Could I ask for a release and crates.io publish to pick up the sha2 changes? It'll avoid a few duplicate crates in constrained environments downstream

Consider simplifying the API

As a new user of base58, I need to understand how it works and how to use it. The current setup with EncodeBuilder struct and EncodeTarget is not trivial -- it took me some time to wrap my head around the crate's API. I suggest taking inspiration from base64 API:

https://docs.rs/base64/0.13.0/base64/index.html#functions

it is based around functions and is much easier for me presonaly to understand.

Release 0.2.3?

It looks like the crate version was bumped back in April, but the crate was not published to crates.io.

Unsound encoding when passing in a non-ascii alphabet

Nothing is checking that the alphabet is ASCII when encoding, the bytes are then directly inserted into the backing Vec<u8> of the output string, if the alphabet included any bytes >127 then this would create an invalid UTF-8 string.

Mark error variants as nonexhaustive

#12 adds in some new bs58::decode::DecodeError variants. Technically this is a breaking change, but I have looked at every dependent crate on crates.io and all usage I could find on github and nobody is currently depending on the variants. So I'm ok with releasing a patch-version bump to extend the error variants and mark them as nonexhaustive to avoid any future breakage if there need to be new variants.

Enhance Large Input Decoding Performance

Using rug or num_bigint for larger inputs significantly increases performance. BigUint is slower for 10 bytes, but for 255 bytes it is ~10x faster, and for 10k bytes, it is ~50x faster

let translated_input = vec![1u8, 1, 1];
let b58_biguint = BigUint::from_radix_be(&translated_input, 58).unwrap();
let decoded_vec = b58_biguint.to_bytes_be();
assert_eq!(decoded_vec, "\r_".as_bytes());

Related issue: kevinheavey/based58#5

Support encoding to a fixed-size buffer

Currently the only way to avoid allocating memory while encoding is to use EncodeBuilder::into(), but this requires a String, which still requires allocation when called from a (nested) function that doesn't happen to have an unused String buffer.

The Write trait is more generic and can be used to write to a stack-allocated buffer. It can also be passed other objects. This can be used, for example, for an efficient implementation of the Display trait for a data type that should be represented in Base58 - this implementation could pass the std::fmt::Formatter directly to the encoder.

Inconsistent return for `with_check`

I'll write only about decode, but same relevant for encode too.

  1. new and with_alphabet return new DecodeBuilder, but with_check instead change DecodeBuilder itself -- this looks inconsistent. For consistency with_check should return new DecodeBuilder or with_alphabet should change DecodeBuilder itself.

  2. Does feature checks make any sense here? https://github.com/mycorrhiza/bs58-rs/blob/18cad1c914eb817cd90317b6c861b27f89123ac2/src/decode.rs#L178
    It's not possible change check if feature disabled (except through unsafe), maybe:

    pub fn into<O: AsMut<[u8]>>(self, mut output: O) -> Result<usize> {
        #[cfg(feature = "check")]
        {
            if self.check {
                return decode_check_into(
                    self.input.as_ref(),
                    output.as_mut(),
                    self.alpha,
                    self.expected_ver,
                );
            }
        }

        decode_into(self.input.as_ref(), output.as_mut(), self.alpha)
    }
  1. Does it makes sense define different structures for different features?
#[cfg(feature = "check")]
{
    #[allow(missing_debug_implementations)]
    pub struct DecodeBuilder<'a, I: AsRef<[u8]>> {
        input: I,
        alpha: &'a [u8; 58],
        check: bool,
        expected_ver: Option<u8>,
    }
}
#[cfg(not(feature = "check"))]
{
    #[allow(missing_debug_implementations)]
    pub struct DecodeBuilder<'a, I: AsRef<[u8]>> {
        input: I,
        alpha: &'a [u8; 58],
    }
}

Thanks.

Invalid import of function via decode::{self} in tests

Useful error message from 1.27:

error: `self` no longer imports values
   --> bs58-rs/src/decode.rs:150:19
    |
150 |     use decode::{ self, DecodeError };
    |                   ^^^^
    |
    = note: #[deny(legacy_imports)] on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #38260 <https://github.com/rust-lang/rust/issues/38260>

From 1.28 onwards this specific error disappears and instead there's just an error about the function decode not being found.

cc rust-lang/rust#51845

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.