From e7b4323b8ce3e476588ac16f5e724a4559e62685 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Mon, 27 Jun 2016 19:26:13 +0200 Subject: [PATCH] Implement Variable length exercise including tests and sample implementation Most test data is taken from the Markdown file describing the exercise --- config.json | 1 + exercises/variable-length-quantity/Cargo.lock | 4 + exercises/variable-length-quantity/Cargo.toml | 3 + exercises/variable-length-quantity/example.rs | 96 +++++++++++++++++ exercises/variable-length-quantity/src/lib.rs | 9 ++ .../tests/variable-length-quantity.rs | 102 ++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 exercises/variable-length-quantity/Cargo.lock create mode 100644 exercises/variable-length-quantity/Cargo.toml create mode 100644 exercises/variable-length-quantity/example.rs create mode 100644 exercises/variable-length-quantity/src/lib.rs create mode 100644 exercises/variable-length-quantity/tests/variable-length-quantity.rs diff --git a/config.json b/config.json index 328573d17..b73820007 100644 --- a/config.json +++ b/config.json @@ -27,6 +27,7 @@ "queen-attack", "sublist", "allergies", + "variable-length-quantity", "phone-number", "wordy", "custom-set", diff --git a/exercises/variable-length-quantity/Cargo.lock b/exercises/variable-length-quantity/Cargo.lock new file mode 100644 index 000000000..87ae648d9 --- /dev/null +++ b/exercises/variable-length-quantity/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "variable-length-quantity" +version = "0.0.0" + diff --git a/exercises/variable-length-quantity/Cargo.toml b/exercises/variable-length-quantity/Cargo.toml new file mode 100644 index 000000000..e5a2a73bc --- /dev/null +++ b/exercises/variable-length-quantity/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "variable-length-quantity" +version = "0.0.0" diff --git a/exercises/variable-length-quantity/example.rs b/exercises/variable-length-quantity/example.rs new file mode 100644 index 000000000..1a82b000c --- /dev/null +++ b/exercises/variable-length-quantity/example.rs @@ -0,0 +1,96 @@ +/// Convert a list of numbers to a stream of bytes encoded with variable length encoding. +pub fn to_bytes(values: &[u32]) -> Vec { + let mut res = vec![]; + + for value in values { + res.append(&mut to_bytes_single(*value)); + } + res +} + +fn to_bytes_single(mut value: u32) -> Vec { + // over allocates, but avoids growth + let mut res = Vec::with_capacity(4); + + // 0 must be handeled specially, because we need to push one byte + if value == 0 { + return vec![0]; + } + + while value > 0 { + // take the lower 7 bits + let mut tmp = (value & 0x7f) as u8; + // remove them from the original value + value >>= 7; + + // set continuation bit + if !res.is_empty() { + tmp |= 0x80; + } + + res.push(tmp); + } + + // order is wrong due to the way we pushed the data onto it + res.reverse(); + res +} + +// Alternative solution with hardcoded borders +// /// Convert a list of numbers to a stream of bytes encoded with variable length encoding. +// pub fn to_bytes(values: &[u32]) -> Vec { +// let mut res = vec![]; +// +// for &value in values { +// if value <= 0x7f { +// res.push(value as u8); +// } else if value <= 0x3fff { +// res.push(((value >> 7) & 0xff) as u8 | 0x80); +// res.push((value & 0x7f) as u8); +// } else if value <= 0x1f_ffff { +// res.push(((value >> 14) & 0xff) as u8 | 0x80); +// res.push(((value >> 7) & 0xff) as u8 | 0x80); +// res.push((value & 0x7f) as u8); +// } else if value <= 0x0fff_ffff { +// res.push(((value >> 21) & 0xff) as u8 | 0x80); +// res.push(((value >> 14) & 0xff) as u8 | 0x80); +// res.push(((value >> 7) & 0xff) as u8 | 0x80); +// res.push((value & 0x7f) as u8); +// } else { +// res.push(((value >> 28) & 0xff) as u8 | 0x80); +// res.push(((value >> 21) & 0xff) as u8 | 0x80); +// res.push(((value >> 14) & 0xff) as u8 | 0x80); +// res.push(((value >> 7) & 0xff) as u8 | 0x80); +// res.push((value & 0x7f) as u8); +// } +// } +// res +// } + +/// Given a stream of bytes, extract all numbers which are encoded in there. +pub fn from_bytes(bytes: &[u8]) -> Result, &'static str> { + let mut res = vec![]; + let mut tmp = 0; + for b in bytes { + // test if first 7 bit are set, to check for overflow + if (tmp & 0xfe_00_00_00) > 0 { + return Err("Would overflow"); + } + + // append bytes of b to tmp + tmp = (tmp << 7) | (b & 0x7f) as u32; + + if 0x80 & b == 0 { + // continuation bit not set, number if complete + res.push(tmp); + tmp = 0; + } + } + + // check for incomplete bytes + if tmp != 0 { + return Err("Incomplete byte sequence"); + } + + Ok(res) +} diff --git a/exercises/variable-length-quantity/src/lib.rs b/exercises/variable-length-quantity/src/lib.rs new file mode 100644 index 000000000..bdcc332be --- /dev/null +++ b/exercises/variable-length-quantity/src/lib.rs @@ -0,0 +1,9 @@ +/// Convert a list of numbers to a stream of bytes encoded with variable length encoding. +pub fn to_bytes(values: &[u32]) -> Vec { + unimplemented!() +} + +/// Given a stream of bytes, extract all numbers which are encoded in there. +pub fn from_bytes(bytes: &[u8]) -> Result, &'static str> { + unimplemented!() +} diff --git a/exercises/variable-length-quantity/tests/variable-length-quantity.rs b/exercises/variable-length-quantity/tests/variable-length-quantity.rs new file mode 100644 index 000000000..d16773b72 --- /dev/null +++ b/exercises/variable-length-quantity/tests/variable-length-quantity.rs @@ -0,0 +1,102 @@ +extern crate variable_length_quantity as vlq; + +#[test] +fn to_single_byte() { + assert_eq!(&[0x00], vlq::to_bytes(&[0x00]).as_slice()); + assert_eq!(&[0x40], vlq::to_bytes(&[0x40]).as_slice()); + assert_eq!(&[0x7f], vlq::to_bytes(&[0x7f]).as_slice()); +} + +#[test] +#[ignore] +fn to_double_byte() { + assert_eq!(&[0x81, 0x00], vlq::to_bytes(&[0x80]).as_slice()); + assert_eq!(&[0xc0, 0x00], vlq::to_bytes(&[0x2000]).as_slice()); + assert_eq!(&[0xff, 0x7f], vlq::to_bytes(&[0x3fff]).as_slice()); +} + +#[test] +#[ignore] +fn to_triple_byte() { + assert_eq!(&[0x81, 0x80, 0x00], vlq::to_bytes(&[0x4000]).as_slice()); + assert_eq!(&[0xc0, 0x80, 0x00], vlq::to_bytes(&[0x10_0000]).as_slice()); + assert_eq!(&[0xff, 0xff, 0x7f], vlq::to_bytes(&[0x1f_ffff]).as_slice()); +} + +#[test] +#[ignore] +fn to_quadruple_byte() { + assert_eq!(&[0x81, 0x80, 0x80, 0x00], + vlq::to_bytes(&[0x20_0000]).as_slice()); + assert_eq!(&[0xc0, 0x80, 0x80, 0x00], + vlq::to_bytes(&[0x0800_0000]).as_slice()); + assert_eq!(&[0xff, 0xff, 0xff, 0x7f], + vlq::to_bytes(&[0x0fff_ffff]).as_slice()); +} + +#[test] +#[ignore] +fn to_quintuple_byte() { + assert_eq!(&[0x81, 0x80, 0x80, 0x80, 0x00], + vlq::to_bytes(&[0x1000_0000]).as_slice()); + assert_eq!(&[0x8f, 0xf8, 0x80, 0x80, 0x00], + vlq::to_bytes(&[0xff00_0000]).as_slice()); + assert_eq!(&[0x8f, 0xff, 0xff, 0xff, 0x7f], + vlq::to_bytes(&[0xffff_ffff]).as_slice()); +} + +#[test] +#[ignore] +fn from_bytes() { + assert_eq!(&[0x7f], vlq::from_bytes(&[0x7f]).unwrap().as_slice()); + assert_eq!(&[0x2000], + vlq::from_bytes(&[0xc0, 0x00]).unwrap().as_slice()); + assert_eq!(&[0x1f_ffff], + vlq::from_bytes(&[0xff, 0xff, 0x7f]).unwrap().as_slice()); + assert_eq!(&[0x20_0000], + vlq::from_bytes(&[0x81, 0x80, 0x80, 0x00]).unwrap().as_slice()); + assert_eq!(&[0xffff_ffff], + vlq::from_bytes(&[0x8f, 0xff, 0xff, 0xff, 0x7f]).unwrap().as_slice()); +} + + +#[test] +#[ignore] +fn to_bytes_multiple_values() { + assert_eq!(&[0x40, 0x7f], vlq::to_bytes(&[0x40, 0x7f]).as_slice()); + assert_eq!(&[0x81, 0x80, 0x00, 0xc8, 0xe8, 0x56], + vlq::to_bytes(&[0x4000, 0x12_3456]).as_slice()); + assert_eq!(&[0xc0, 0x00, 0xc8, 0xe8, 0x56, 0xff, 0xff, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0x81, + 0x80, 0x00], + vlq::to_bytes(&[0x2000, 0x12_3456, 0x0fff_ffff, 0x00, 0x3fff, 0x4000]).as_slice()); +} + +#[test] +#[ignore] +fn from_bytes_multiple_values() { + assert_eq!(&[0x2000, 0x12_3456, 0x0fff_ffff, 0x00, 0x3fff, 0x4000], + vlq::from_bytes(&[0xc0, 0x00, 0xc8, 0xe8, 0x56, 0xff, 0xff, 0xff, 0x7f, 0x00, + 0xff, 0x7f, 0x81, 0x80, 0x00]) + .unwrap() + .as_slice()); +} + +#[test] +#[ignore] +fn incomplete_byte_sequence() { + assert!(vlq::from_bytes(&[0xff]).is_err()); +} + +#[test] +#[ignore] +fn overflow_u32() { + assert!(vlq::from_bytes(&[0xff, 0xff, 0xff, 0xff, 0x7f]).is_err()); +} + +#[test] +#[ignore] +fn chained_execution_is_identity() { + let test = &[0xf2, 0xf6, 0x96, 0x9c, 0x3b, 0x39, 0x2e, 0x30, 0xb3, 0x24]; + assert_eq!(test, + vlq::from_bytes(&vlq::to_bytes(test)).unwrap().as_slice()); +}