aboutsummaryrefslogtreecommitdiff
path: root/nix-rust/src
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-09-17 00:18:17 +0200
committerEelco Dolstra <edolstra@gmail.com>2019-12-10 13:37:23 +0100
commitcce218f95050e82587f376d51e5f732d1f68eab3 (patch)
treeff3ef7377ae4d322bc02007d3d7df593ad1b90ed /nix-rust/src
parenta1ff43045b3f1d075c67c262ff6f623b2b5d569e (diff)
Add base32 encoder/decoder
Diffstat (limited to 'nix-rust/src')
-rw-r--r--nix-rust/src/error.rs2
-rw-r--r--nix-rust/src/lib.rs11
-rw-r--r--nix-rust/src/store/store.rs2
-rw-r--r--nix-rust/src/util/base32.rs140
-rw-r--r--nix-rust/src/util/mod.rs1
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;