diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2019-09-17 00:18:17 +0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2019-12-10 13:37:23 +0100 |
commit | cce218f95050e82587f376d51e5f732d1f68eab3 (patch) | |
tree | ff3ef7377ae4d322bc02007d3d7df593ad1b90ed /nix-rust/src | |
parent | a1ff43045b3f1d075c67c262ff6f623b2b5d569e (diff) |
Add base32 encoder/decoder
Diffstat (limited to 'nix-rust/src')
-rw-r--r-- | nix-rust/src/error.rs | 2 | ||||
-rw-r--r-- | nix-rust/src/lib.rs | 11 | ||||
-rw-r--r-- | nix-rust/src/store/store.rs | 2 | ||||
-rw-r--r-- | nix-rust/src/util/base32.rs | 140 | ||||
-rw-r--r-- | nix-rust/src/util/mod.rs | 1 |
5 files changed, 155 insertions, 1 deletions
diff --git a/nix-rust/src/error.rs b/nix-rust/src/error.rs index 08b9bcac2..9d6fa4c51 100644 --- a/nix-rust/src/error.rs +++ b/nix-rust/src/error.rs @@ -3,6 +3,7 @@ pub enum Error { InvalidPath(crate::store::StorePath), BadStorePath(std::path::PathBuf), BadNarInfo, + BadBase32, IOError(std::io::Error), HttpError(reqwest::Error), Misc(String), @@ -29,6 +30,7 @@ impl From<Error> for CppException { Error::BadStorePath(path) => unsafe { make_error(&format!("path '{}' is not a store path", path.display())) }, // FIXME + Error::BadBase32 => unsafe { make_error("invalid base32 string") }, // FIXME Error::IOError(err) => unsafe { make_error(&err.to_string()) }, Error::HttpError(err) => unsafe { make_error(&err.to_string()) }, Error::Foreign(ex) => ex, diff --git a/nix-rust/src/lib.rs b/nix-rust/src/lib.rs index 2dbe6a217..635c00aff 100644 --- a/nix-rust/src/lib.rs +++ b/nix-rust/src/lib.rs @@ -1,5 +1,16 @@ #![feature(await_macro, async_await)] +#[macro_use] +extern crate lazy_static; + +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + +#[cfg(test)] +#[macro_use] +extern crate proptest; + mod c; mod error; mod foreign; diff --git a/nix-rust/src/store/store.rs b/nix-rust/src/store/store.rs index ceaed6648..32949a078 100644 --- a/nix-rust/src/store/store.rs +++ b/nix-rust/src/store/store.rs @@ -12,7 +12,7 @@ pub struct StorePath { pub const STORE_PATH_HASH_CHARS: usize = 32; impl StorePath { - pub fn new(path: &Path, store_dir: &str) -> Result<Self, Error> { + pub fn new(path: &Path, _store_dir: &str) -> Result<Self, Error> { // FIXME: check store_dir Self::new_short( path.file_name() diff --git a/nix-rust/src/util/base32.rs b/nix-rust/src/util/base32.rs new file mode 100644 index 000000000..ee42a0362 --- /dev/null +++ b/nix-rust/src/util/base32.rs @@ -0,0 +1,140 @@ +use crate::error::Error; +use std::collections::HashMap; + +pub fn encoded_len(input_len: usize) -> usize { + if input_len == 0 { + 0 + } else { + (input_len * 8 - 1) / 5 + 1 + } +} + +static BASE32_CHARS: &'static [u8; 32] = &b"0123456789abcdfghijklmnpqrsvwxyz"; + +lazy_static! { + static ref BASE32_CHARS_REVERSE: HashMap<char, u8> = BASE32_CHARS + .iter() + .enumerate() + .map(|(n, c)| (*c as char, n as u8)) + .collect(); +} + +pub fn encode(input: &[u8]) -> String { + let mut res = String::new(); + res.reserve(encoded_len(input.len())); + + let mut nr_bits_left: usize = 0; + let mut bits_left: u16 = 0; + + for b in input { + bits_left |= (*b as u16) << nr_bits_left; + nr_bits_left += 8; + while nr_bits_left > 5 { + res.push(BASE32_CHARS[(bits_left & 0x1f) as usize] as char); + bits_left >>= 5; + nr_bits_left -= 5; + } + } + + if nr_bits_left > 0 { + res.push(BASE32_CHARS[(bits_left & 0x1f) as usize] as char); + } + + res.chars().rev().collect() +} + +pub fn decode(input: &str) -> Result<Vec<u8>, crate::Error> { + let mut res = Vec::new(); + + let mut nr_bits_left: usize = 0; + let mut bits_left: u16 = 0; + + for c in input.chars().rev() { + let x = BASE32_CHARS_REVERSE.get(&c).ok_or(Error::BadBase32)?; + bits_left |= (*x as u16) << nr_bits_left; + nr_bits_left += 5; + if nr_bits_left >= 8 { + res.push((bits_left & 0xff) as u8); + bits_left >>= 8; + nr_bits_left -= 8; + } + } + + if nr_bits_left > 0 && bits_left != 0 { + return Err(Error::BadBase32); + } + + Ok(res) +} + +#[cfg(test)] +mod tests { + use super::*; + use hex; + + #[test] + fn test_encode() { + assert_eq!(encode(&[]), ""); + + assert_eq!( + encode(&hex::decode("0839703786356bca59b0f4a32987eb2e6de43ae8").unwrap()), + "x0xf8v9fxf3jk8zln1cwlsrmhqvp0f88" + ); + + assert_eq!( + encode( + &hex::decode("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + .unwrap() + ), + "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" + ); + + assert_eq!( + encode( + &hex::decode("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f") + .unwrap() + ), + "2gs8k559z4rlahfx0y688s49m2vvszylcikrfinm30ly9rak69236nkam5ydvly1ai7xac99vxfc4ii84hawjbk876blyk1jfhkbbyx" + ); + } + + #[test] + fn test_decode() { + assert_eq!(hex::encode(decode("").unwrap()), ""); + + assert_eq!( + hex::encode(decode("x0xf8v9fxf3jk8zln1cwlsrmhqvp0f88").unwrap()), + "0839703786356bca59b0f4a32987eb2e6de43ae8" + ); + + assert_eq!( + hex::encode(decode("1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s").unwrap()), + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + ); + + assert_eq!( + hex::encode(decode("2gs8k559z4rlahfx0y688s49m2vvszylcikrfinm30ly9rak69236nkam5ydvly1ai7xac99vxfc4ii84hawjbk876blyk1jfhkbbyx").unwrap()), + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" + ); + + assert_matches!( + decode("xoxf8v9fxf3jk8zln1cwlsrmhqvp0f88"), + Err(Error::BadBase32) + ); + assert_matches!( + decode("2b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"), + Err(Error::BadBase32) + ); + assert_matches!(decode("2"), Err(Error::BadBase32)); + assert_matches!(decode("2gs"), Err(Error::BadBase32)); + assert_matches!(decode("2gs8"), Err(Error::BadBase32)); + } + + proptest! { + + #[test] + fn roundtrip(s: Vec<u8>) { + assert_eq!(s, decode(&encode(&s)).unwrap()); + } + } +} diff --git a/nix-rust/src/util/mod.rs b/nix-rust/src/util/mod.rs index 209627893..cd852c55f 100644 --- a/nix-rust/src/util/mod.rs +++ b/nix-rust/src/util/mod.rs @@ -1 +1,2 @@ +pub mod base32; pub mod tarfile; |