From 797c7f794038146a503fa6aeb0765658a99aa1bf Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sun, 18 Jun 2017 01:34:45 -0400 Subject: [PATCH 01/16] Implement preliminary Code128 type Working checksum digit calculation and validation with basic tests covering hard-coded cases, pending a more complete solution. --- src/code128.rs | 417 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 11 +- 2 files changed, 422 insertions(+), 6 deletions(-) create mode 100644 src/code128.rs diff --git a/src/code128.rs b/src/code128.rs new file mode 100644 index 0000000..ba997cb --- /dev/null +++ b/src/code128.rs @@ -0,0 +1,417 @@ +//! Barcode 128 standard as data representation + +/// Representation of Code128 patterns +/// +/// Representation of the symbols used in Code128; depending on the active code +/// set, each symbol maps to one of 3 ASCII values in official Code128. +/// +/// code | set A | set B | set C +/// :----|:-----:|:-----:|:------: +/// C0 | space | space | 00 +/// C0 | ! | ! | 01 +/// C1 | " | " | 02 +/// C2 | # | # | 03 +/// C3 | $ | $ | 04 +/// C5 | % | % | 05 +/// C6 | & | & | 06 +/// C7 | ' | ' | 07 +/// C8 | ( | ( | 08 +/// C9 | ) | ) | 09 +/// C10 | \* | \* | 10 +/// C11 | + | + | 11 +/// C12 | , | , | 12 +/// C13 | - | - | 13 +/// C14 | . | . | 14 +/// C15 | / | / | 15 +/// C16 | 0 | 0 | 16 +/// C17 | 1 | 1 | 17 +/// C18 | 2 | 2 | 18 +/// C19 | 3 | 3 | 19 +/// C20 | 4 | 4 | 20 +/// C21 | 5 | 5 | 21 +/// C22 | 6 | 6 | 22 +/// C23 | 7 | 7 | 23 +/// C24 | 8 | 8 | 24 +/// C25 | 9 | 9 | 25 +/// C26 | : | : | 26 +/// C27 | ; | ; | 27 +/// C28 | < | < | 28 +/// C29 | = | = | 29 +/// C30 | > | > | 30 +/// C31 | ? | ? | 31 +/// C32 | @ | @ | 32 +/// C33 | A | A | 33 +/// C34 | B | B | 34 +/// C35 | C | C | 35 +/// C36 | D | D | 36 +/// C37 | E | E | 37 +/// C38 | F | F | 38 +/// C39 | G | G | 39 +/// C40 | H | H | 40 +/// C41 | I | I | 41 +/// C42 | J | J | 42 +/// C43 | K | K | 43 +/// C44 | L | L | 44 +/// C45 | M | M | 45 +/// C46 | N | N | 46 +/// C47 | O | O | 47 +/// C48 | P | P | 48 +/// C49 | Q | Q | 49 +/// C50 | R | R | 50 +/// C51 | S | S | 51 +/// C52 | T | T | 52 +/// C53 | U | U | 53 +/// C54 | V | V | 54 +/// C55 | W | W | 55 +/// C56 | X | X | 56 +/// C57 | Y | Y | 57 +/// C58 | Z | Z | 58 +/// C59 | [ | [ | 59 +/// C60 | \\ | \\ | 60 +/// C61 | ] | ] | 61 +/// C62 | ^ | ^ | 62 +/// C63 | \_ | \_ | 63 +/// C64 | NUL | ` | 64 +/// C65 | SOH | a | 65 +/// C66 | STX | b | 66 +/// C67 | ETX | c | 67 +/// C68 | EOT | d | 68 +/// C69 | ENQ | e | 69 +/// C70 | ACK | f | 70 +/// C71 | BEL | g | 71 +/// C72 | BS | h | 72 +/// C73 | HT | i | 73 +/// C74 | LF | j | 74 +/// C75 | VT | k | 75 +/// C76 | FF | l | 76 +/// C77 | CR | m | 77 +/// C78 | SO | n | 78 +/// C79 | SI | o | 79 +/// C80 | DLE | p | 80 +/// C81 | DC1 | q | 81 +/// C82 | DC2 | r | 82 +/// C83 | DC3 | s | 83 +/// C84 | DC4 | t | 84 +/// C85 | NAK | u | 85 +/// C86 | SYN | v | 86 +/// C87 | ETB | w | 87 +/// C88 | CAN | x | 88 +/// C89 | EM | y | 89 +/// C90 | SUB | z | 90 +/// C91 | ESC | { | 91 +/// C92 | FS | \| | 92 +/// C93 | GS | } | 93 +/// C94 | RS | ~ | 94 +/// C95 | US | DEL | 95 +/// C96 | FNC 3 | FNC 3 | 96 +/// C97 | FNC 2 | FNC 2 | 97 +/// C98 |Shift B|Shift A| 98 +/// C99 |Code C |Code C | 99 +/// C100 |Code B | FNC 4 | Code B +/// C101 | FNC 4 |Code A | Code A +/// C102 | FNC 1 | FNC 1 | FNC 1 +/// C106 | stop | stop | stop +#[derive(PartialEq,Eq,Debug,Clone,Copy)] +pub enum Pattern { + C0, + C1, + C2, + C3, + C4, + C5, + C6, + C7, + C8, + C9, + C10, + C11, + C12, + C13, + C14, + C15, + C16, + C17, + C18, + C19, + C20, + C21, + C22, + C23, + C24, + C25, + C26, + C27, + C28, + C29, + C30, + C31, + C32, + C33, + C34, + C35, + C36, + C37, + C38, + C39, + C40, + C41, + C42, + C43, + C44, + C45, + C46, + C47, + C48, + C49, + C50, + C51, + C52, + C53, + C54, + C55, + C56, + C57, + C58, + C59, + C60, + C61, + C62, + C63, + C64, + C65, + C66, + C67, + C68, + C69, + C70, + C71, + C72, + C73, + C74, + C75, + C76, + C77, + C78, + C79, + C80, + C81, + C82, + C83, + C84, + C85, + C86, + C87, + C88, + C89, + C90, + C91, + C92, + C93, + C94, + C95, + C96, + C97, + C98, + C99, + C100, + C101, + C102, + C106, +} + +impl From for Pattern { + fn from(u: u8) -> Pattern { + use code128::Pattern::*; + match u { + 0 => C0, + 1 => C1, + 2 => C2, + 3 => C3, + 4 => C4, + 5 => C5, + 6 => C6, + 7 => C7, + 8 => C8, + 9 => C9, + 10 => C10, + 11 => C11, + 12 => C12, + 13 => C13, + 14 => C14, + 15 => C15, + 16 => C16, + 17 => C17, + 18 => C18, + 19 => C19, + 20 => C20, + 21 => C21, + 22 => C22, + 23 => C23, + 24 => C24, + 25 => C25, + 26 => C26, + 27 => C27, + 28 => C28, + 29 => C29, + 30 => C30, + 31 => C31, + 32 => C32, + 33 => C33, + 34 => C34, + 35 => C35, + 36 => C36, + 37 => C37, + 38 => C38, + 39 => C39, + 40 => C40, + 41 => C41, + 42 => C42, + 43 => C43, + 44 => C44, + 45 => C45, + 46 => C46, + 47 => C47, + 48 => C48, + 49 => C49, + 50 => C50, + 51 => C51, + 52 => C52, + 53 => C53, + 54 => C54, + 55 => C55, + 56 => C56, + 57 => C57, + 58 => C58, + 59 => C59, + 60 => C60, + 61 => C61, + 62 => C62, + 63 => C63, + 64 => C64, + 65 => C65, + 66 => C66, + 67 => C67, + 68 => C68, + 69 => C69, + 70 => C70, + 71 => C71, + 72 => C72, + 73 => C73, + 74 => C74, + 75 => C75, + 76 => C76, + 77 => C77, + 78 => C78, + 79 => C79, + 80 => C80, + 81 => C81, + 82 => C82, + 83 => C83, + 84 => C84, + 85 => C85, + 86 => C86, + 87 => C87, + 88 => C88, + 89 => C89, + 90 => C90, + 91 => C91, + 92 => C92, + 93 => C93, + 94 => C94, + 95 => C95, + 96 => C96, + 97 => C97, + 98 => C98, + 99 => C99, + 100 => C100, + 101 => C101, + 102 => C102, + 106 => C106, + _ => C106, + } + } +} + +/// Represents the different Code128 alphabets +/// +/// Refer to `Pattern` for more detail +#[derive(PartialEq,Eq,Debug)] +pub enum Symbology { A = 104, B = 105, C = 106 } + +/// A full code 128 encoded datum +/// +/// # Symbology +/// +/// Code128 encodes the full 128 characters of ASCII using three different +/// Symbologies which represent different subsets of ASCII. +/// +/// - `CodeA`: A-Z, 0-9, and special characters (ASCII 00 to 95) +/// - `CodeB`: a-z, A-Z, and 0-9 (ASCII 32-127) +/// - `CodeC`: high density, number pair encoding +/// +/// The 107 different patterns map differently to ASCII characters for each +/// symbology, see documentation for `Pattern` or references for more details. +/// +/// # References +/// - [Code 128 on Wikipedia](https://en.wikipedia.org/wiki/Code_128) +#[derive(PartialEq,Eq,Debug)] +pub struct Code128 { + /// Start code which indicates the initial symbology + start: Symbology, + /// Collection of symbols which encode a string + symbols: Vec, + /// A raw pattern from C0 to C102 calculated from the raw pattern values + checksum: Pattern, +} + +impl Code128 { + /// Verify that the checksum digit matches the expected checksum + pub fn verify_checksum(&self) -> bool { + self.calc_checksum() == self.checksum + } + + /// Calculate checksum digit from raw encoding pattern + pub fn calc_checksum(&self) -> Pattern { + use code128::Symbology::*; + // Sum raw numerical values from each symbol multiplied by its position + let sum: u64 = { + let mut pos: u64 = 0; + self.symbols.iter() + .fold(0, |sum, pat| { + pos += 1; + sum + (*pat as u64) * pos + }) + } + match self.start { // Add start codes + A => 103, + B => 104, + C => 105, + }; + + // Checksum is the remainder after dividing the raw code sum by 103 + Pattern::from((sum % 103) as u8) + } +} + +#[test] +fn checksum_works() { + use code128::Symbology::{A, C}; + use code128::Pattern::*; + + let code = Code128 { + start: A, + symbols: vec![C48, C42, C42, C17, C18, C19, C35], + checksum: C54, + }; + + assert_eq!(code.calc_checksum(), C54); + assert!(code.verify_checksum()); + + let code = Code128 { + start: C, + symbols: vec![C102, C42, C18, C40, C20, C50, C101, C16], + checksum: C92, + }; + + assert_eq!(code.calc_checksum(), C92); + assert!(code.verify_checksum()); +} diff --git a/src/lib.rs b/src/lib.rs index c54e76d..a86b31d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,9 @@ +//! # Feather Code +//! +//! Custom visual encoding format extended from code128 barcode encoding. + // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - } -} +pub mod code128; From 434be5fb1f6170a8087ed7aedc76ea84910d6dc9 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Thu, 22 Jun 2017 11:55:17 -0400 Subject: [PATCH 02/16] Improve documentation style and change stop code Change module-level documentation to block-comment style for easier line reading, and change `From` to use stop code as wild-card conversion to catch all unxpectd values. Add variant documentation for `Symbology` enum, and fix values; stop codes had associated start code values shifted by one. --- src/code128.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index ba997cb..06d7abe 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -1,11 +1,15 @@ -//! Barcode 128 standard as data representation +/*! + +Barcode 128 standard as data representation + +*/ /// Representation of Code128 patterns /// /// Representation of the symbols used in Code128; depending on the active code /// set, each symbol maps to one of 3 ASCII values in official Code128. /// -/// code | set A | set B | set C +/// code | A | B | C /// :----|:-----:|:-----:|:------: /// C0 | space | space | 00 /// C0 | ! | ! | 01 @@ -326,17 +330,23 @@ impl From for Pattern { 100 => C100, 101 => C101, 102 => C102, - 106 => C106, _ => C106, } } } -/// Represents the different Code128 alphabets +/// Tag to represent different Code128 alphabets (symbologies) /// /// Refer to `Pattern` for more detail -#[derive(PartialEq,Eq,Debug)] -pub enum Symbology { A = 104, B = 105, C = 106 } +#[derive(PartialEq,Eq,Debug,Clone,Copy)] +pub enum Symbology { + /// A-Z, 0-9, and special characters (ASCII 00 to 95) + A = 103, + /// a-z, A-Z, and 0-9 (ASCII 32-127) + B = 104, + /// high density, number pair encoding + C = 105, +} /// A full code 128 encoded datum /// From 5d8bc166764359a3cf8c93cea804b13b23b353e6 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Thu, 22 Jun 2017 12:05:44 -0400 Subject: [PATCH 03/16] Move Code128 tests to test module Better seperation between tests and base module, allows easier definition of macros or functions used only in the tests. --- src/code128.rs | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index 06d7abe..1d38e4c 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -401,27 +401,31 @@ impl Code128 { Pattern::from((sum % 103) as u8) } } +#[cfg(test)] +mod code128 { -#[test] -fn checksum_works() { - use code128::Symbology::{A, C}; - use code128::Pattern::*; + #[test] + fn checksum_works() { + use code128::Symbology::*; + use code128::Pattern::*; + use code128::Code128; - let code = Code128 { - start: A, - symbols: vec![C48, C42, C42, C17, C18, C19, C35], - checksum: C54, - }; + let code = Code128 { + start: A, + symbols: vec![C48, C42, C42, C17, C18, C19, C35], + checksum: C54, + }; - assert_eq!(code.calc_checksum(), C54); - assert!(code.verify_checksum()); + assert_eq!(code.calc_checksum(), C54); + assert!(code.verify_checksum()); - let code = Code128 { - start: C, - symbols: vec![C102, C42, C18, C40, C20, C50, C101, C16], - checksum: C92, - }; + let code = Code128 { + start: C, + symbols: vec![C102, C42, C18, C40, C20, C50, C101, C16], + checksum: C92, + }; - assert_eq!(code.calc_checksum(), C92); - assert!(code.verify_checksum()); + assert_eq!(code.calc_checksum(), C92); + assert!(code.verify_checksum()); + } } From 956c2a53f3bf1677b50a6315ae748ea9d7e4f662 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Thu, 22 Jun 2017 12:28:56 -0400 Subject: [PATCH 04/16] Implement basic conversion to string Simply panics if it recieves an unexpected value, and does not implement shift-codes. Uses a rudimentary finite state machine to model the change in behavior for each symbology, pushing characters symbol-per-symbol to build the encoded string. --- src/code128.rs | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/code128.rs b/src/code128.rs index 1d38e4c..b6b306c 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -400,6 +400,83 @@ impl Code128 { // Checksum is the remainder after dividing the raw code sum by 103 Pattern::from((sum % 103) as u8) } + + /// Convert to string, reading the symbology to decode values + pub fn to_string(&self) -> String { + use code128::Symbology::*; + let mut encoded: String = "".to_string(); + let mut symbology = self.start; + let mut index: usize = 0; + + 'parser: while index < self.symbols.len() { + // Convert current symbol to its u8 value to allow for efficitient + // conversion to char as an ASCII code, simply specifying a + // different offset for the ASCII values in each symbology + let symbol = self.symbols[index] as u8; + + // Perform symbology specific behavior, working essentially like a + // rudimentary finite state machine + symbology = match symbology { + A => { + match symbol { + _ if symbol < 64 => { + // Codes C0 to C63 encode ASCII values 32 -> 95 + encoded.push((symbol + 32) as char); + A + }, + _ if symbol < 96 => { + // Codes C64 -> C95 encode ASCII values 0 -> 32 + encoded.push((symbol - 64) as char); + A + }, + 96 | 97 | 101 | 102 => A, // Functions 1-4, disabled + 98 => unimplemented!(), // Single code shift to B + 99 => C, // Switch to symbology C + 100 => B, // Switch to symbology B + 106 => break 'parser, + _ => unimplemented!(), // Unexpected value + } + }, + B => { + match symbol { + _ if symbol < 96 => { + // Codes C0 -> C95 encode ASCII values 32 -> 127 + encoded.push((symbol + 32) as char); + B + }, + 96 | 97 | 100 | 102 => B, // Functions 1-4, disabled + 98 => unimplemented!(), // Single code shift to A + 99 => C, // Switch to symbology C + 101 => A, // Switch to symbology A + 106 => break 'parser, + _ => unimplemented!(), // Unexpected value + } + }, + C => { + match symbol { + _ if symbol < 100 => { + // Calculate the tens and unit digits for string + // conversion + let unit = symbol % 10; + let tens = (symbol - unit) / 10; + // ASCII number codes start at 48, add 48 to offset + // the codes to get the numbers + encoded.push((tens + 48) as char); + encoded.push((unit + 48) as char); + C + }, + 100 => B, // Switch to symbology C + 101 => A, // Switch to symbology A + 102 => C, // Function 1, disabled + 106 => break 'parser, + _ => unimplemented!(), // Unexpected value + } + }, + }; + index += 1; + } + encoded + } } #[cfg(test)] mod code128 { @@ -428,4 +505,35 @@ mod code128 { assert_eq!(code.calc_checksum(), C92); assert!(code.verify_checksum()); } + + #[test] + fn to_string_conversion() { + use code128::Symbology::*; + use code128::Pattern::*; + use code128::Code128; + + let pjj123_c = Code128 { + start: A, + symbols: vec![C48, C42, C42, C17, C18, C19, C35], + checksum: C54, + }; + + assert_eq!(pjj123_c.to_string(), "PJJ123C".to_string()); + + let country_code = Code128 { + start: C, + symbols: vec![C102, C42, C18, C40, C20, C50, C101, C16], + checksum: C92, + }; + + assert_eq!(country_code.to_string(), "42184020500".to_string()); + + let hello_world = Code128 { + start: B, + symbols: vec![C40, C69, C76, C76, C79, C0, C55, C79, C82, C76, C68], + checksum: C43, + }; + + assert_eq!(hello_world.to_string(), "Hello World".to_string()); + } } From db9f33c7e212a89fa82f039bb73c839f827fd80c Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Thu, 22 Jun 2017 12:53:01 -0400 Subject: [PATCH 05/16] Convert while loop anti-pattern to for loop Preferable to use iterators and expression-based for loop to avoid carying arround an extra mutable value and manually manipulating indecies. --- src/code128.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index b6b306c..91d5a61 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -406,13 +406,12 @@ impl Code128 { use code128::Symbology::*; let mut encoded: String = "".to_string(); let mut symbology = self.start; - let mut index: usize = 0; - 'parser: while index < self.symbols.len() { + + 'parser: for symbol in self.symbols.iter().map(|sym| *sym as u8) { // Convert current symbol to its u8 value to allow for efficitient // conversion to char as an ASCII code, simply specifying a // different offset for the ASCII values in each symbology - let symbol = self.symbols[index] as u8; // Perform symbology specific behavior, working essentially like a // rudimentary finite state machine @@ -421,7 +420,7 @@ impl Code128 { match symbol { _ if symbol < 64 => { // Codes C0 to C63 encode ASCII values 32 -> 95 - encoded.push((symbol + 32) as char); + encoded.push((symbol + 32u8) as char); A }, _ if symbol < 96 => { @@ -473,7 +472,6 @@ impl Code128 { } }, }; - index += 1; } encoded } From 59c59c54f238d698948dd814c6094810f0161c57 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Fri, 23 Jun 2017 12:05:22 -0400 Subject: [PATCH 06/16] Change name of string conversion method to decode `to_string` implies an implementation of `Display` or `ToString` for the type, so to disambiguate the method is now called `decode` I am considering an `Into` implementation, but since the conversion may fail if the checksum digit fails, `TryInto` would be a better option. However, since `TryInto` is only in Nightly, that decision is pushed down the road. --- src/code128.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index 91d5a61..1ba3102 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -401,13 +401,12 @@ impl Code128 { Pattern::from((sum % 103) as u8) } - /// Convert to string, reading the symbology to decode values - pub fn to_string(&self) -> String { + /// Convert to string, reading the symbology to decode values to a string + pub fn decode(&self) -> String { use code128::Symbology::*; let mut encoded: String = "".to_string(); let mut symbology = self.start; - 'parser: for symbol in self.symbols.iter().map(|sym| *sym as u8) { // Convert current symbol to its u8 value to allow for efficitient // conversion to char as an ASCII code, simply specifying a @@ -476,6 +475,7 @@ impl Code128 { encoded } } + #[cfg(test)] mod code128 { @@ -516,7 +516,7 @@ mod code128 { checksum: C54, }; - assert_eq!(pjj123_c.to_string(), "PJJ123C".to_string()); + assert_eq!(pjj123_c.decode(), "PJJ123C".to_string()); let country_code = Code128 { start: C, @@ -524,7 +524,7 @@ mod code128 { checksum: C92, }; - assert_eq!(country_code.to_string(), "42184020500".to_string()); + assert_eq!(country_code.decode(), "42184020500".to_string()); let hello_world = Code128 { start: B, @@ -532,6 +532,6 @@ mod code128 { checksum: C43, }; - assert_eq!(hello_world.to_string(), "Hello World".to_string()); + assert_eq!(hello_world.decode(), "Hello World".to_string()); } } From b2f690e8f48d14adbc5ebb685a50d627238412f3 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 24 Jun 2017 00:36:11 -0400 Subject: [PATCH 07/16] Add deny missing docs attribute and fix docs --- src/code128.rs | 9 +++++---- src/lib.rs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index 1ba3102..04ed7a0 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -12,10 +12,10 @@ Barcode 128 standard as data representation /// code | A | B | C /// :----|:-----:|:-----:|:------: /// C0 | space | space | 00 -/// C0 | ! | ! | 01 -/// C1 | " | " | 02 -/// C2 | # | # | 03 -/// C3 | $ | $ | 04 +/// C1 | ! | ! | 01 +/// C2 | " | " | 02 +/// C3 | # | # | 03 +/// C4 | $ | $ | 04 /// C5 | % | % | 05 /// C6 | & | & | 06 /// C7 | ' | ' | 07 @@ -115,6 +115,7 @@ Barcode 128 standard as data representation /// C101 | FNC 4 |Code A | Code A /// C102 | FNC 1 | FNC 1 | FNC 1 /// C106 | stop | stop | stop +#[allow(missing_docs)] #[derive(PartialEq,Eq,Debug,Clone,Copy)] pub enum Pattern { C0, diff --git a/src/lib.rs b/src/lib.rs index a86b31d..4124a5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,10 @@ -//! # Feather Code -//! -//! Custom visual encoding format extended from code128 barcode encoding. - // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#![deny(missing_docs)] + +//! # Feather Code +//! +//! Custom visual encoding format extended from code128 barcode encoding. pub mod code128; From d3df28455abe53f245adcc89d831a0de76717b14 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 24 Jun 2017 00:37:26 -0400 Subject: [PATCH 08/16] Remove early cast from Patterns to u8 Improves ability for error handling, as the possible values are now constrained more strictly for each symbol, making impossible states impossible to represent. Deriving PartialOrd and Ord make it easy to keep the exact same behaviour as before, replacing only the u8 literals with Pattern literals. --- src/code128.rs | 62 ++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index 04ed7a0..8858316 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -116,7 +116,7 @@ Barcode 128 standard as data representation /// C102 | FNC 1 | FNC 1 | FNC 1 /// C106 | stop | stop | stop #[allow(missing_docs)] -#[derive(PartialEq,Eq,Debug,Clone,Copy)] +#[derive(PartialEq,Eq,PartialOrd,Ord,Debug,Clone,Copy)] pub enum Pattern { C0, C1, @@ -405,70 +405,68 @@ impl Code128 { /// Convert to string, reading the symbology to decode values to a string pub fn decode(&self) -> String { use code128::Symbology::*; + use code128::Pattern::*; let mut encoded: String = "".to_string(); let mut symbology = self.start; - 'parser: for symbol in self.symbols.iter().map(|sym| *sym as u8) { - // Convert current symbol to its u8 value to allow for efficitient - // conversion to char as an ASCII code, simply specifying a - // different offset for the ASCII values in each symbology - + 'parser: for symbol in &self.symbols { + let symbol = *symbol; // Perform symbology specific behavior, working essentially like a // rudimentary finite state machine symbology = match symbology { A => { match symbol { - _ if symbol < 64 => { + _ if symbol < C64 => { // Codes C0 to C63 encode ASCII values 32 -> 95 - encoded.push((symbol + 32u8) as char); + encoded.push((symbol as u8 + 32) as char); A }, - _ if symbol < 96 => { + _ if symbol < C96 => { // Codes C64 -> C95 encode ASCII values 0 -> 32 - encoded.push((symbol - 64) as char); + encoded.push((symbol as u8 - 64) as char); A }, - 96 | 97 | 101 | 102 => A, // Functions 1-4, disabled - 98 => unimplemented!(), // Single code shift to B - 99 => C, // Switch to symbology C - 100 => B, // Switch to symbology B - 106 => break 'parser, - _ => unimplemented!(), // Unexpected value + C96 | C97 | C101 | C102 => A, // Functions 1-4, disabled + C98 => unimplemented!(), // Single code shift to B + C99 => C, // Switch to symbology C + C100 => B, // Switch to symbology B + C106 => break 'parser, + _ => unimplemented!(), } }, B => { match symbol { - _ if symbol < 96 => { + _ if symbol < C96 => { // Codes C0 -> C95 encode ASCII values 32 -> 127 - encoded.push((symbol + 32) as char); + encoded.push((symbol as u8 + 32) as char); B }, - 96 | 97 | 100 | 102 => B, // Functions 1-4, disabled - 98 => unimplemented!(), // Single code shift to A - 99 => C, // Switch to symbology C - 101 => A, // Switch to symbology A - 106 => break 'parser, - _ => unimplemented!(), // Unexpected value + C96 | C97 | C100 | C102 => B, // Functions 1-4, disabled + C98 => unimplemented!(), // Single code shift to A + C99 => C, // Switch to symbology C + C101 => A, // Switch to symbology A + C106 => break 'parser, + _ => unimplemented!(), } }, C => { match symbol { - _ if symbol < 100 => { + _ if symbol < C100 => { // Calculate the tens and unit digits for string // conversion - let unit = symbol % 10; - let tens = (symbol - unit) / 10; + let unit = symbol as u8 % 10; + let tens = (symbol as u8 - unit) / 10; // ASCII number codes start at 48, add 48 to offset // the codes to get the numbers encoded.push((tens + 48) as char); encoded.push((unit + 48) as char); C }, - 100 => B, // Switch to symbology C - 101 => A, // Switch to symbology A - 102 => C, // Function 1, disabled - 106 => break 'parser, - _ => unimplemented!(), // Unexpected value + C100 => B, // Switch to symbology C + C101 => A, // Switch to symbology A + C102 => C, // Function 1, disabled + C106 => break 'parser, + _ => unimplemented!(), } }, }; From 8a1dc9175bf92e84af2b4f4c608c41992fa6e7a1 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 24 Jun 2017 00:57:33 -0400 Subject: [PATCH 09/16] Add shift-code handling with state machine enum Rather than just matching on possible symbologies, also match on shift codes to perform shift-code specific behaviour. --- src/code128.rs | 69 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/src/code128.rs b/src/code128.rs index 8858316..7ba59bd 100644 --- a/src/code128.rs +++ b/src/code128.rs @@ -404,52 +404,71 @@ impl Code128 { /// Convert to string, reading the symbology to decode values to a string pub fn decode(&self) -> String { - use code128::Symbology::*; use code128::Pattern::*; let mut encoded: String = "".to_string(); - let mut symbology = self.start; + + #[derive(PartialEq,Eq)] + enum Parser { A, B, C, ShiftA, ShiftB }; + + let mut state = match self.start { + Symbology::A => Parser::A, + Symbology::B => Parser::B, + Symbology::C => Parser::C, + }; 'parser: for symbol in &self.symbols { let symbol = *symbol; // Perform symbology specific behavior, working essentially like a // rudimentary finite state machine - symbology = match symbology { - A => { + state = match state { + Parser::A | Parser::ShiftA => { match symbol { _ if symbol < C64 => { // Codes C0 to C63 encode ASCII values 32 -> 95 encoded.push((symbol as u8 + 32) as char); - A + if state == Parser::ShiftA { + Parser::B + } else { + Parser::A + } }, _ if symbol < C96 => { // Codes C64 -> C95 encode ASCII values 0 -> 32 encoded.push((symbol as u8 - 64) as char); - A + if state == Parser::ShiftA { + Parser::B + } else { + Parser::A + } }, - C96 | C97 | C101 | C102 => A, // Functions 1-4, disabled - C98 => unimplemented!(), // Single code shift to B - C99 => C, // Switch to symbology C - C100 => B, // Switch to symbology B + C96 | C97 | C101 | C102 => Parser::A, // Functions 1-4 + C98 => Parser::ShiftB, // Single code shift to B + C99 => Parser::C, // Switch to symbology C + C100 => Parser::B, // Switch to symbology B C106 => break 'parser, _ => unimplemented!(), } }, - B => { + Parser::B | Parser::ShiftB => { match symbol { _ if symbol < C96 => { // Codes C0 -> C95 encode ASCII values 32 -> 127 encoded.push((symbol as u8 + 32) as char); - B + if state == Parser::ShiftB { + Parser::A + } else { + Parser::B + } }, - C96 | C97 | C100 | C102 => B, // Functions 1-4, disabled - C98 => unimplemented!(), // Single code shift to A - C99 => C, // Switch to symbology C - C101 => A, // Switch to symbology A + C96 | C97 | C100 | C102 => Parser::B, // Functions 1-4 + C98 => Parser::ShiftA, // Single code shift to A + C99 => Parser::C, // Switch to symbology C + C101 => Parser::A, // Switch to symbology A C106 => break 'parser, _ => unimplemented!(), } }, - C => { + Parser::C => { match symbol { _ if symbol < C100 => { // Calculate the tens and unit digits for string @@ -460,11 +479,11 @@ impl Code128 { // the codes to get the numbers encoded.push((tens + 48) as char); encoded.push((unit + 48) as char); - C + Parser::C }, - C100 => B, // Switch to symbology C - C101 => A, // Switch to symbology A - C102 => C, // Function 1, disabled + C100 => Parser::B, // Switch to symbology C + C101 => Parser::A, // Switch to symbology A + C102 => Parser::C, // Function 1, disabled C106 => break 'parser, _ => unimplemented!(), } @@ -532,5 +551,13 @@ mod code128 { }; assert_eq!(hello_world.decode(), "Hello World".to_string()); + + let shift_codes = Code128 { + start: A, + symbols: vec![C51, C40, C98, C73, C38, C52, C100, C98, C1], + checksum: C99, + }; + + assert_eq!(shift_codes.decode(), "SHiFT!".to_string()) } } From f6d6781a3ed1ea787df4a56d7d824e13e70c9ea7 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 8 Jul 2017 20:14:19 -0400 Subject: [PATCH 10/16] Create internals module to contain code128 logic Allows for module to be public for integration and benchmark testing while making it clear that the contained modules are not meant for normal module users. --- src/{ => internals}/code128.rs | 24 ++++++++++++------------ src/internals/mod.rs | 1 + src/lib.rs | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) rename src/{ => internals}/code128.rs (96%) create mode 100644 src/internals/mod.rs diff --git a/src/code128.rs b/src/internals/code128.rs similarity index 96% rename from src/code128.rs rename to src/internals/code128.rs index 7ba59bd..064a319 100644 --- a/src/code128.rs +++ b/src/internals/code128.rs @@ -226,7 +226,7 @@ pub enum Pattern { impl From for Pattern { fn from(u: u8) -> Pattern { - use code128::Pattern::*; + use internals::code128::Pattern::*; match u { 0 => C0, 1 => C1, @@ -368,11 +368,11 @@ pub enum Symbology { #[derive(PartialEq,Eq,Debug)] pub struct Code128 { /// Start code which indicates the initial symbology - start: Symbology, + pub start: Symbology, /// Collection of symbols which encode a string - symbols: Vec, + pub symbols: Vec, /// A raw pattern from C0 to C102 calculated from the raw pattern values - checksum: Pattern, + pub checksum: Pattern, } impl Code128 { @@ -383,7 +383,7 @@ impl Code128 { /// Calculate checksum digit from raw encoding pattern pub fn calc_checksum(&self) -> Pattern { - use code128::Symbology::*; + use internals::code128::Symbology::*; // Sum raw numerical values from each symbol multiplied by its position let sum: u64 = { let mut pos: u64 = 0; @@ -404,7 +404,7 @@ impl Code128 { /// Convert to string, reading the symbology to decode values to a string pub fn decode(&self) -> String { - use code128::Pattern::*; + use internals::code128::Pattern::*; let mut encoded: String = "".to_string(); #[derive(PartialEq,Eq)] @@ -499,9 +499,9 @@ mod code128 { #[test] fn checksum_works() { - use code128::Symbology::*; - use code128::Pattern::*; - use code128::Code128; + use internals::code128::Symbology::*; + use internals::code128::Pattern::*; + use internals::code128::Code128; let code = Code128 { start: A, @@ -524,9 +524,9 @@ mod code128 { #[test] fn to_string_conversion() { - use code128::Symbology::*; - use code128::Pattern::*; - use code128::Code128; + use internals::code128::Symbology::*; + use internals::code128::Pattern::*; + use internals::code128::Code128; let pjj123_c = Code128 { start: A, diff --git a/src/internals/mod.rs b/src/internals/mod.rs new file mode 100644 index 0000000..2fa099f --- /dev/null +++ b/src/internals/mod.rs @@ -0,0 +1 @@ +mod code128; diff --git a/src/lib.rs b/src/lib.rs index 4124a5b..3c35da1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,4 +7,4 @@ //! //! Custom visual encoding format extended from code128 barcode encoding. -pub mod code128; +pub mod internals; From 6bb3bb9096254f252a18784475f6623d161afaf2 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sun, 9 Jul 2017 02:40:35 -0400 Subject: [PATCH 11/16] Add bench command to commit testing May end up making things take a long time, consider removing in the future if it causes problems. --- hooks/pre-commit.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hooks/pre-commit.sh b/hooks/pre-commit.sh index 1855221..0c86b24 100755 --- a/hooks/pre-commit.sh +++ b/hooks/pre-commit.sh @@ -30,7 +30,8 @@ then cargo doc --no-deps && cargo build && - cargo test --all + cargo test --all && + rustup run nightly cargo bench # Capture exit code from tests status=$? From 13f8839baaf07d9b29ca02832c9e22f4095f766f Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 22 Jul 2017 00:49:25 -0400 Subject: [PATCH 12/16] Add trait encapsulating barcode encode and decode Generic over formats, allowing support for decoding a variety of barcode types to a variety of target types, and encoding to formats from a variety of types. The ultimate purpose is to allow conversions to rust strings and c strings in the future, and to support the direct conversion of arbitrary encoding types directly to a supported Decode type (for a given format). --- src/internals/format.rs | 37 +++++++++++++++++++++++++++++++++++++ src/internals/mod.rs | 5 ++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/internals/format.rs diff --git a/src/internals/format.rs b/src/internals/format.rs new file mode 100644 index 0000000..1e79302 --- /dev/null +++ b/src/internals/format.rs @@ -0,0 +1,37 @@ +//! Traits to encapsulate encoding and decoding + +/// Representation of a barcode format +pub trait Format { + + /// Perform format specific validation to verify data integrity + fn checksum(&self) -> bool; +} + + +/// Support decoding a particular format to the target type +pub trait Decode where Self: Sized { + + /// Convert a formatted data value + fn decode(&F) -> Result; +} + + +/// Support encoding the target type as a particular format +pub trait Encode { + + /// Convert to a given format + fn encode(&self) -> Result; +} + +/// Describes failure cases for encoding and decoding barcodes +#[derive(Debug,PartialEq)] +pub enum FormatErr { + /// Barcode is too short or too long + InvalidLength(usize), + /// Invalid internal format + BadFormat(String), + /// Encode failure + EncodeErr(String), + /// Decode failure + DecodeErr(String), +} diff --git a/src/internals/mod.rs b/src/internals/mod.rs index 2fa099f..bd6d9d3 100644 --- a/src/internals/mod.rs +++ b/src/internals/mod.rs @@ -1 +1,4 @@ -mod code128; +//! Implementation of barcode logic, not meant for normal library use + +pub mod code128; +pub mod format; From 89076b919f12e1cd3a47c8e96b21570db42e48a7 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 22 Jul 2017 15:46:03 -0400 Subject: [PATCH 13/16] Use traits to have encoding independent decode Use `Encoding` trait to represent different encoding types, starting with `u8` and `Pattern`. Use u8 in doc tests where appropriate. --- Cargo.toml | 3 + src/internals/code128.rs | 563 ----------------------------- src/internals/code128/encodings.rs | 318 ++++++++++++++++ src/internals/code128/mod.rs | 506 ++++++++++++++++++++++++++ src/internals/format.rs | 39 +- src/lib.rs | 4 + 6 files changed, 853 insertions(+), 580 deletions(-) delete mode 100644 src/internals/code128.rs create mode 100644 src/internals/code128/encodings.rs create mode 100644 src/internals/code128/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 6fbc3c6..a41e281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,6 @@ license = "MPL-2.0" description = "Encoding and Decoding for HackFSU's custom visual data encoding format." [dependencies] + +[dev-dependencies] +quickcheck = "0.3" diff --git a/src/internals/code128.rs b/src/internals/code128.rs deleted file mode 100644 index 064a319..0000000 --- a/src/internals/code128.rs +++ /dev/null @@ -1,563 +0,0 @@ -/*! - -Barcode 128 standard as data representation - -*/ - -/// Representation of Code128 patterns -/// -/// Representation of the symbols used in Code128; depending on the active code -/// set, each symbol maps to one of 3 ASCII values in official Code128. -/// -/// code | A | B | C -/// :----|:-----:|:-----:|:------: -/// C0 | space | space | 00 -/// C1 | ! | ! | 01 -/// C2 | " | " | 02 -/// C3 | # | # | 03 -/// C4 | $ | $ | 04 -/// C5 | % | % | 05 -/// C6 | & | & | 06 -/// C7 | ' | ' | 07 -/// C8 | ( | ( | 08 -/// C9 | ) | ) | 09 -/// C10 | \* | \* | 10 -/// C11 | + | + | 11 -/// C12 | , | , | 12 -/// C13 | - | - | 13 -/// C14 | . | . | 14 -/// C15 | / | / | 15 -/// C16 | 0 | 0 | 16 -/// C17 | 1 | 1 | 17 -/// C18 | 2 | 2 | 18 -/// C19 | 3 | 3 | 19 -/// C20 | 4 | 4 | 20 -/// C21 | 5 | 5 | 21 -/// C22 | 6 | 6 | 22 -/// C23 | 7 | 7 | 23 -/// C24 | 8 | 8 | 24 -/// C25 | 9 | 9 | 25 -/// C26 | : | : | 26 -/// C27 | ; | ; | 27 -/// C28 | < | < | 28 -/// C29 | = | = | 29 -/// C30 | > | > | 30 -/// C31 | ? | ? | 31 -/// C32 | @ | @ | 32 -/// C33 | A | A | 33 -/// C34 | B | B | 34 -/// C35 | C | C | 35 -/// C36 | D | D | 36 -/// C37 | E | E | 37 -/// C38 | F | F | 38 -/// C39 | G | G | 39 -/// C40 | H | H | 40 -/// C41 | I | I | 41 -/// C42 | J | J | 42 -/// C43 | K | K | 43 -/// C44 | L | L | 44 -/// C45 | M | M | 45 -/// C46 | N | N | 46 -/// C47 | O | O | 47 -/// C48 | P | P | 48 -/// C49 | Q | Q | 49 -/// C50 | R | R | 50 -/// C51 | S | S | 51 -/// C52 | T | T | 52 -/// C53 | U | U | 53 -/// C54 | V | V | 54 -/// C55 | W | W | 55 -/// C56 | X | X | 56 -/// C57 | Y | Y | 57 -/// C58 | Z | Z | 58 -/// C59 | [ | [ | 59 -/// C60 | \\ | \\ | 60 -/// C61 | ] | ] | 61 -/// C62 | ^ | ^ | 62 -/// C63 | \_ | \_ | 63 -/// C64 | NUL | ` | 64 -/// C65 | SOH | a | 65 -/// C66 | STX | b | 66 -/// C67 | ETX | c | 67 -/// C68 | EOT | d | 68 -/// C69 | ENQ | e | 69 -/// C70 | ACK | f | 70 -/// C71 | BEL | g | 71 -/// C72 | BS | h | 72 -/// C73 | HT | i | 73 -/// C74 | LF | j | 74 -/// C75 | VT | k | 75 -/// C76 | FF | l | 76 -/// C77 | CR | m | 77 -/// C78 | SO | n | 78 -/// C79 | SI | o | 79 -/// C80 | DLE | p | 80 -/// C81 | DC1 | q | 81 -/// C82 | DC2 | r | 82 -/// C83 | DC3 | s | 83 -/// C84 | DC4 | t | 84 -/// C85 | NAK | u | 85 -/// C86 | SYN | v | 86 -/// C87 | ETB | w | 87 -/// C88 | CAN | x | 88 -/// C89 | EM | y | 89 -/// C90 | SUB | z | 90 -/// C91 | ESC | { | 91 -/// C92 | FS | \| | 92 -/// C93 | GS | } | 93 -/// C94 | RS | ~ | 94 -/// C95 | US | DEL | 95 -/// C96 | FNC 3 | FNC 3 | 96 -/// C97 | FNC 2 | FNC 2 | 97 -/// C98 |Shift B|Shift A| 98 -/// C99 |Code C |Code C | 99 -/// C100 |Code B | FNC 4 | Code B -/// C101 | FNC 4 |Code A | Code A -/// C102 | FNC 1 | FNC 1 | FNC 1 -/// C106 | stop | stop | stop -#[allow(missing_docs)] -#[derive(PartialEq,Eq,PartialOrd,Ord,Debug,Clone,Copy)] -pub enum Pattern { - C0, - C1, - C2, - C3, - C4, - C5, - C6, - C7, - C8, - C9, - C10, - C11, - C12, - C13, - C14, - C15, - C16, - C17, - C18, - C19, - C20, - C21, - C22, - C23, - C24, - C25, - C26, - C27, - C28, - C29, - C30, - C31, - C32, - C33, - C34, - C35, - C36, - C37, - C38, - C39, - C40, - C41, - C42, - C43, - C44, - C45, - C46, - C47, - C48, - C49, - C50, - C51, - C52, - C53, - C54, - C55, - C56, - C57, - C58, - C59, - C60, - C61, - C62, - C63, - C64, - C65, - C66, - C67, - C68, - C69, - C70, - C71, - C72, - C73, - C74, - C75, - C76, - C77, - C78, - C79, - C80, - C81, - C82, - C83, - C84, - C85, - C86, - C87, - C88, - C89, - C90, - C91, - C92, - C93, - C94, - C95, - C96, - C97, - C98, - C99, - C100, - C101, - C102, - C106, -} - -impl From for Pattern { - fn from(u: u8) -> Pattern { - use internals::code128::Pattern::*; - match u { - 0 => C0, - 1 => C1, - 2 => C2, - 3 => C3, - 4 => C4, - 5 => C5, - 6 => C6, - 7 => C7, - 8 => C8, - 9 => C9, - 10 => C10, - 11 => C11, - 12 => C12, - 13 => C13, - 14 => C14, - 15 => C15, - 16 => C16, - 17 => C17, - 18 => C18, - 19 => C19, - 20 => C20, - 21 => C21, - 22 => C22, - 23 => C23, - 24 => C24, - 25 => C25, - 26 => C26, - 27 => C27, - 28 => C28, - 29 => C29, - 30 => C30, - 31 => C31, - 32 => C32, - 33 => C33, - 34 => C34, - 35 => C35, - 36 => C36, - 37 => C37, - 38 => C38, - 39 => C39, - 40 => C40, - 41 => C41, - 42 => C42, - 43 => C43, - 44 => C44, - 45 => C45, - 46 => C46, - 47 => C47, - 48 => C48, - 49 => C49, - 50 => C50, - 51 => C51, - 52 => C52, - 53 => C53, - 54 => C54, - 55 => C55, - 56 => C56, - 57 => C57, - 58 => C58, - 59 => C59, - 60 => C60, - 61 => C61, - 62 => C62, - 63 => C63, - 64 => C64, - 65 => C65, - 66 => C66, - 67 => C67, - 68 => C68, - 69 => C69, - 70 => C70, - 71 => C71, - 72 => C72, - 73 => C73, - 74 => C74, - 75 => C75, - 76 => C76, - 77 => C77, - 78 => C78, - 79 => C79, - 80 => C80, - 81 => C81, - 82 => C82, - 83 => C83, - 84 => C84, - 85 => C85, - 86 => C86, - 87 => C87, - 88 => C88, - 89 => C89, - 90 => C90, - 91 => C91, - 92 => C92, - 93 => C93, - 94 => C94, - 95 => C95, - 96 => C96, - 97 => C97, - 98 => C98, - 99 => C99, - 100 => C100, - 101 => C101, - 102 => C102, - _ => C106, - } - } -} - -/// Tag to represent different Code128 alphabets (symbologies) -/// -/// Refer to `Pattern` for more detail -#[derive(PartialEq,Eq,Debug,Clone,Copy)] -pub enum Symbology { - /// A-Z, 0-9, and special characters (ASCII 00 to 95) - A = 103, - /// a-z, A-Z, and 0-9 (ASCII 32-127) - B = 104, - /// high density, number pair encoding - C = 105, -} - -/// A full code 128 encoded datum -/// -/// # Symbology -/// -/// Code128 encodes the full 128 characters of ASCII using three different -/// Symbologies which represent different subsets of ASCII. -/// -/// - `CodeA`: A-Z, 0-9, and special characters (ASCII 00 to 95) -/// - `CodeB`: a-z, A-Z, and 0-9 (ASCII 32-127) -/// - `CodeC`: high density, number pair encoding -/// -/// The 107 different patterns map differently to ASCII characters for each -/// symbology, see documentation for `Pattern` or references for more details. -/// -/// # References -/// - [Code 128 on Wikipedia](https://en.wikipedia.org/wiki/Code_128) -#[derive(PartialEq,Eq,Debug)] -pub struct Code128 { - /// Start code which indicates the initial symbology - pub start: Symbology, - /// Collection of symbols which encode a string - pub symbols: Vec, - /// A raw pattern from C0 to C102 calculated from the raw pattern values - pub checksum: Pattern, -} - -impl Code128 { - /// Verify that the checksum digit matches the expected checksum - pub fn verify_checksum(&self) -> bool { - self.calc_checksum() == self.checksum - } - - /// Calculate checksum digit from raw encoding pattern - pub fn calc_checksum(&self) -> Pattern { - use internals::code128::Symbology::*; - // Sum raw numerical values from each symbol multiplied by its position - let sum: u64 = { - let mut pos: u64 = 0; - self.symbols.iter() - .fold(0, |sum, pat| { - pos += 1; - sum + (*pat as u64) * pos - }) - } + match self.start { // Add start codes - A => 103, - B => 104, - C => 105, - }; - - // Checksum is the remainder after dividing the raw code sum by 103 - Pattern::from((sum % 103) as u8) - } - - /// Convert to string, reading the symbology to decode values to a string - pub fn decode(&self) -> String { - use internals::code128::Pattern::*; - let mut encoded: String = "".to_string(); - - #[derive(PartialEq,Eq)] - enum Parser { A, B, C, ShiftA, ShiftB }; - - let mut state = match self.start { - Symbology::A => Parser::A, - Symbology::B => Parser::B, - Symbology::C => Parser::C, - }; - - 'parser: for symbol in &self.symbols { - let symbol = *symbol; - // Perform symbology specific behavior, working essentially like a - // rudimentary finite state machine - state = match state { - Parser::A | Parser::ShiftA => { - match symbol { - _ if symbol < C64 => { - // Codes C0 to C63 encode ASCII values 32 -> 95 - encoded.push((symbol as u8 + 32) as char); - if state == Parser::ShiftA { - Parser::B - } else { - Parser::A - } - }, - _ if symbol < C96 => { - // Codes C64 -> C95 encode ASCII values 0 -> 32 - encoded.push((symbol as u8 - 64) as char); - if state == Parser::ShiftA { - Parser::B - } else { - Parser::A - } - }, - C96 | C97 | C101 | C102 => Parser::A, // Functions 1-4 - C98 => Parser::ShiftB, // Single code shift to B - C99 => Parser::C, // Switch to symbology C - C100 => Parser::B, // Switch to symbology B - C106 => break 'parser, - _ => unimplemented!(), - } - }, - Parser::B | Parser::ShiftB => { - match symbol { - _ if symbol < C96 => { - // Codes C0 -> C95 encode ASCII values 32 -> 127 - encoded.push((symbol as u8 + 32) as char); - if state == Parser::ShiftB { - Parser::A - } else { - Parser::B - } - }, - C96 | C97 | C100 | C102 => Parser::B, // Functions 1-4 - C98 => Parser::ShiftA, // Single code shift to A - C99 => Parser::C, // Switch to symbology C - C101 => Parser::A, // Switch to symbology A - C106 => break 'parser, - _ => unimplemented!(), - } - }, - Parser::C => { - match symbol { - _ if symbol < C100 => { - // Calculate the tens and unit digits for string - // conversion - let unit = symbol as u8 % 10; - let tens = (symbol as u8 - unit) / 10; - // ASCII number codes start at 48, add 48 to offset - // the codes to get the numbers - encoded.push((tens + 48) as char); - encoded.push((unit + 48) as char); - Parser::C - }, - C100 => Parser::B, // Switch to symbology C - C101 => Parser::A, // Switch to symbology A - C102 => Parser::C, // Function 1, disabled - C106 => break 'parser, - _ => unimplemented!(), - } - }, - }; - } - encoded - } -} - -#[cfg(test)] -mod code128 { - - #[test] - fn checksum_works() { - use internals::code128::Symbology::*; - use internals::code128::Pattern::*; - use internals::code128::Code128; - - let code = Code128 { - start: A, - symbols: vec![C48, C42, C42, C17, C18, C19, C35], - checksum: C54, - }; - - assert_eq!(code.calc_checksum(), C54); - assert!(code.verify_checksum()); - - let code = Code128 { - start: C, - symbols: vec![C102, C42, C18, C40, C20, C50, C101, C16], - checksum: C92, - }; - - assert_eq!(code.calc_checksum(), C92); - assert!(code.verify_checksum()); - } - - #[test] - fn to_string_conversion() { - use internals::code128::Symbology::*; - use internals::code128::Pattern::*; - use internals::code128::Code128; - - let pjj123_c = Code128 { - start: A, - symbols: vec![C48, C42, C42, C17, C18, C19, C35], - checksum: C54, - }; - - assert_eq!(pjj123_c.decode(), "PJJ123C".to_string()); - - let country_code = Code128 { - start: C, - symbols: vec![C102, C42, C18, C40, C20, C50, C101, C16], - checksum: C92, - }; - - assert_eq!(country_code.decode(), "42184020500".to_string()); - - let hello_world = Code128 { - start: B, - symbols: vec![C40, C69, C76, C76, C79, C0, C55, C79, C82, C76, C68], - checksum: C43, - }; - - assert_eq!(hello_world.decode(), "Hello World".to_string()); - - let shift_codes = Code128 { - start: A, - symbols: vec![C51, C40, C98, C73, C38, C52, C100, C98, C1], - checksum: C99, - }; - - assert_eq!(shift_codes.decode(), "SHiFT!".to_string()) - } -} diff --git a/src/internals/code128/encodings.rs b/src/internals/code128/encodings.rs new file mode 100644 index 0000000..12cc2e0 --- /dev/null +++ b/src/internals/code128/encodings.rs @@ -0,0 +1,318 @@ +//! Implementation of Code128 barcode encodings + +use super::{Encoding, Symbology}; + +/// Representation of Code128 patterns +/// +/// Representation of the symbols used in Code128; depending on the active code +/// set, each symbol maps to one of 3 ASCII values in official Code128. +/// +/// Pattern | A | B | C +/// :-------|:-----:|:-----:|:------: +/// C0 | space | space | 00 +/// C1 | ! | ! | 01 +/// C2 | " | " | 02 +/// C3 | # | # | 03 +/// C4 | $ | $ | 04 +/// C5 | % | % | 05 +/// C6 | & | & | 06 +/// C7 | ' | ' | 07 +/// C8 | ( | ( | 08 +/// C9 | ) | ) | 09 +/// C10 | \* | \* | 10 +/// C11 | + | + | 11 +/// C12 | , | , | 12 +/// C13 | - | - | 13 +/// C14 | . | . | 14 +/// C15 | / | / | 15 +/// C16 | 0 | 0 | 16 +/// C17 | 1 | 1 | 17 +/// C18 | 2 | 2 | 18 +/// C19 | 3 | 3 | 19 +/// C20 | 4 | 4 | 20 +/// C21 | 5 | 5 | 21 +/// C22 | 6 | 6 | 22 +/// C23 | 7 | 7 | 23 +/// C24 | 8 | 8 | 24 +/// C25 | 9 | 9 | 25 +/// C26 | : | : | 26 +/// C27 | ; | ; | 27 +/// C28 | < | < | 28 +/// C29 | = | = | 29 +/// C30 | > | > | 30 +/// C31 | ? | ? | 31 +/// C32 | @ | @ | 32 +/// C33 | A | A | 33 +/// C34 | B | B | 34 +/// C35 | C | C | 35 +/// C36 | D | D | 36 +/// C37 | E | E | 37 +/// C38 | F | F | 38 +/// C39 | G | G | 39 +/// C40 | H | H | 40 +/// C41 | I | I | 41 +/// C42 | J | J | 42 +/// C43 | K | K | 43 +/// C44 | L | L | 44 +/// C45 | M | M | 45 +/// C46 | N | N | 46 +/// C47 | O | O | 47 +/// C48 | P | P | 48 +/// C49 | Q | Q | 49 +/// C50 | R | R | 50 +/// C51 | S | S | 51 +/// C52 | T | T | 52 +/// C53 | U | U | 53 +/// C54 | V | V | 54 +/// C55 | W | W | 55 +/// C56 | X | X | 56 +/// C57 | Y | Y | 57 +/// C58 | Z | Z | 58 +/// C59 | [ | [ | 59 +/// C60 | \\ | \\ | 60 +/// C61 | ] | ] | 61 +/// C62 | ^ | ^ | 62 +/// C63 | \_ | \_ | 63 +/// C64 | NUL | ` | 64 +/// C65 | SOH | a | 65 +/// C66 | STX | b | 66 +/// C67 | ETX | c | 67 +/// C68 | EOT | d | 68 +/// C69 | ENQ | e | 69 +/// C70 | ACK | f | 70 +/// C71 | BEL | g | 71 +/// C72 | BS | h | 72 +/// C73 | HT | i | 73 +/// C74 | LF | j | 74 +/// C75 | VT | k | 75 +/// C76 | FF | l | 76 +/// C77 | CR | m | 77 +/// C78 | SO | n | 78 +/// C79 | SI | o | 79 +/// C80 | DLE | p | 80 +/// C81 | DC1 | q | 81 +/// C82 | DC2 | r | 82 +/// C83 | DC3 | s | 83 +/// C84 | DC4 | t | 84 +/// C85 | NAK | u | 85 +/// C86 | SYN | v | 86 +/// C87 | ETB | w | 87 +/// C88 | CAN | x | 88 +/// C89 | EM | y | 89 +/// C90 | SUB | z | 90 +/// C91 | ESC | { | 91 +/// C92 | FS | \| | 92 +/// C93 | GS | } | 93 +/// C94 | RS | ~ | 94 +/// C95 | US | DEL | 95 +/// C96 | FNC 3 | FNC 3 | 96 +/// C97 | FNC 2 | FNC 2 | 97 +/// C98 |Shift B|Shift A| 98 +/// C99 |Code C |Code C | 99 +/// C100 |Code B | FNC 4 | Code B +/// C101 | FNC 4 |Code A | Code A +/// C102 | FNC 1 | FNC 1 | FNC 1 +/// C103 |Start A|Start A| Start A +/// C104 |Start B|Start B| Start B +/// C105 |Start C|Start C| Start C +/// C106 | stop | stop | stop +#[allow(missing_docs)] +#[derive(PartialEq,Eq,PartialOrd,Ord,Debug,Clone,Copy)] +pub enum Pattern { + C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, + C11, C12, C13, C14, C15, C16, C17, C18, C19, + C20, C21, C22, C23, C24, C25, C26, C27, C28, + C29, C30, C31, C32, C33, C34, C35, C36, C37, + C38, C39, C40, C41, C42, C43, C44, C45, C46, + C47, C48, C49, C50, C51, C52, C53, C54, C55, + C56, C57, C58, C59, C60, C61, C62, C63, C64, + C65, C66, C67, C68, C69, C70, C71, C72, C73, + C74, C75, C76, C77, C78, C79, C80, C81, C82, + C83, C84, C85, C86, C87, C88, C89, C90, C91, + C92, C93, C94, C95, C96, C97, C98, C99, C100, + C101, C102, C103, C104, C105, C106, +} + +// Jump table conversion +impl From for Pattern { + fn from(u: u8) -> Pattern { + use self::Pattern::*; + match u { + 0 => C0, 1 => C1, 2 => C2, 3 => C3, 4 => C4, 5 => C5, 6 => C6, + 7 => C7, 8 => C8, 9 => C9, 10 => C10, 11 => C11, 12 => C12, 13 => C13, + 14 => C14, 15 => C15, 16 => C16, 17 => C17, 18 => C18, 19 => C19, + 20 => C20, 21 => C21, 22 => C22, 23 => C23, 24 => C24, 25 => C25, + 26 => C26, 27 => C27, 28 => C28, 29 => C29, 30 => C30, 31 => C31, + 32 => C32, 33 => C33, 34 => C34, 35 => C35, 36 => C36, 37 => C37, + 38 => C38, 39 => C39, 40 => C40, 41 => C41, 42 => C42, 43 => C43, + 44 => C44, 45 => C45, 46 => C46, 47 => C47, 48 => C48, 49 => C49, + 50 => C50, 51 => C51, 52 => C52, 53 => C53, 54 => C54, 55 => C55, + 56 => C56, 57 => C57, 58 => C58, 59 => C59, 60 => C60, 61 => C61, + 62 => C62, 63 => C63, 64 => C64, 65 => C65, 66 => C66, 67 => C67, + 68 => C68, 69 => C69, 70 => C70, 71 => C71, 72 => C72, 73 => C73, + 74 => C74, 75 => C75, 76 => C76, 77 => C77, 78 => C78, 79 => C79, + 80 => C80, 81 => C81, 82 => C82, 83 => C83, 84 => C84, 85 => C85, + 86 => C86, 87 => C87, 88 => C88, 89 => C89, 90 => C90, 91 => C91, + 92 => C92, 93 => C93, 94 => C94, 95 => C95, 96 => C96, 97 => C97, + 98 => C98, 99 => C99, 100 => C100, 101 => C101, 102 => C102, + 103 => C103, 104 => C104, 105 => C105, _ => C106, + } + } +} + +// Simple numerical cast from enum to u8 +impl Into for Pattern { + fn into(self) -> u8 { + self as u8 + } +} + +impl Encoding for Pattern { + #[inline] + fn repr(&self, s: Symbology) -> String { + match s { + Symbology::A => { + if (*self as u8) < 64u8 { + ((*self as u8 + 32) as char).to_string() + } else { + ((*self as u8 - 64) as char).to_string() + } + }, + Symbology::B => ((*self as u8 + 32) as char).to_string(), + Symbology::C => (*self as u8).to_string(), + } + } + + #[inline] + fn stop() -> Self {Pattern::C106} + + #[inline] + fn switch(s: Symbology) -> Self { + match s { + Symbology::A => Pattern::C101, + Symbology::B => Pattern::C100, + Symbology::C => Pattern::C99, + } + } + + #[inline] + fn start(s: Symbology) -> Self { + match s { + Symbology::A => Pattern::C103, + Symbology::B => Pattern::C104, + Symbology::C => Pattern::C105, + } + } + + #[inline] + fn shift() -> Self {Pattern::C98} + + #[inline] + fn fnc1() -> Self {Pattern::C102} + + #[inline] + fn fnc2() -> Self {Pattern::C97} + + #[inline] + fn fnc3() -> Self {Pattern::C96} + + #[inline] + fn fnc4(s: Symbology) -> Option { + match s { + Symbology::A => Some(Pattern::C101), + Symbology::B => Some(Pattern::C100), + _ => None, + } + } + + #[inline] + fn as_u8(&self) -> u8 { + *self as u8 + } +} + +impl Encoding for u8 { + #[inline] + fn repr(&self, s: Symbology) -> String { + match s { + Symbology::A => { + if *self < 64u8 { + ((*self + 32) as char).to_string() + } else { + ((*self - 64) as char).to_string() + } + }, + Symbology::B => ((*self + 32) as char).to_string(), + Symbology::C => (*self).to_string(), + } + } + + #[inline] + fn stop() -> Self {106} + + #[inline] + fn switch(s: Symbology) -> Self { + match s { + Symbology::A => 101, + Symbology::B => 100, + Symbology::C => 99, + } + } + + #[inline] + fn start(s: Symbology) -> Self { + match s { + Symbology::A => 103, + Symbology::B => 104, + Symbology::C => 105, + } + } + + #[inline] + fn shift() -> Self {98} + + #[inline] + fn fnc1() -> Self {102} + + #[inline] + fn fnc2() -> Self {97} + + #[inline] + fn fnc3() -> Self {96} + + #[inline] + fn fnc4(s: Symbology) -> Option { + match s { + Symbology::A => Some(101), + Symbology::B => Some(100), + _ => None, + } + } + + #[inline] + fn as_u8(&self) -> u8 { + *self + } +} + +#[cfg(test)] +mod test { + quickcheck! { + fn pattern_from_u8_to_u8(p: u8) -> bool { + use super::Encoding; + p == super::Pattern::from(p).as_u8() + } + + fn pattern_repr_matches_u8_repr(p: u8) -> bool { + use super::Encoding; + use super::Symbology; + if p < 107 { + super::Pattern::from(p).repr(Symbology::A) == p.repr(Symbology::A) && + super::Pattern::from(p).repr(Symbology::B) == p.repr(Symbology::B) && + super::Pattern::from(p).repr(Symbology::C) == p.repr(Symbology::C) + } else { + true + } + } + } +} diff --git a/src/internals/code128/mod.rs b/src/internals/code128/mod.rs new file mode 100644 index 0000000..c4eaae1 --- /dev/null +++ b/src/internals/code128/mod.rs @@ -0,0 +1,506 @@ +//! Barcode 128 standard as data representation +//! +//! ![barcode example](https://upload.wikimedia.org/wikipedia/commons/4/41/9494-RI476394652CH.jpg) +//! +//! Encapsulates logic for encoding and decoding barcodes using the [`Format`], [`Decode`], and +//! [`Encode`] traits. +//! +//! [`Format`]: ../format/trait.Format.html +//! [`Encode`]: ../format/trait.Encode.html +//! [`Decode`]: ../format/trait.Decode.html + +use std::fmt::Debug; +use super::format; +use super::format::{Format, Decode}; +pub mod encodings; + +/// Code128 alphabets (symbologies) which specify how [patterns][`Encoding`] map to characters +/// +/// [`Encoding`]: trait.Encoding.html +#[derive(PartialEq,Eq,Debug,Clone,Copy)] +pub enum Symbology { + /// (ASCII 00 to 95) A-Z, 0-9, and special characters + A = 103, + /// (ASCII 32-127) a-z, A-Z, and 0-9 + B = 104, + /// High density, number pair encoding + C = 105, +} + +/// Interface for types which represent Code128 encodings +/// +/// Code128 encodings are patterns whch reprsent numerical values from 0 to 106. Each encoding +/// maps to a differet subset of ascii values depending on the [`Symbology`]: +/// +/// u8 | [`A`] | [`B`] | [`C`] +/// :----|:-----:|:-----:|:------: +/// 0 | space | space | 00 +/// 1 | ! | ! | 01 +/// 2 | " | " | 02 +/// 3 | # | # | 03 +/// 4 | $ | $ | 04 +/// 5 | % | % | 05 +/// 6 | & | & | 06 +/// 7 | ' | ' | 07 +/// 8 | ( | ( | 08 +/// 9 | ) | ) | 09 +/// 10 | \* | \* | 10 +/// 11 | + | + | 11 +/// 12 | , | , | 12 +/// 13 | - | - | 13 +/// 14 | . | . | 14 +/// 15 | / | / | 15 +/// 16 | 0 | 0 | 16 +/// 17 | 1 | 1 | 17 +/// 18 | 2 | 2 | 18 +/// 19 | 3 | 3 | 19 +/// 20 | 4 | 4 | 20 +/// 21 | 5 | 5 | 21 +/// 22 | 6 | 6 | 22 +/// 23 | 7 | 7 | 23 +/// 24 | 8 | 8 | 24 +/// 25 | 9 | 9 | 25 +/// 26 | : | : | 26 +/// 27 | ; | ; | 27 +/// 28 | < | < | 28 +/// 29 | = | = | 29 +/// 30 | > | > | 30 +/// 31 | ? | ? | 31 +/// 32 | @ | @ | 32 +/// 33 | A | A | 33 +/// 34 | B | B | 34 +/// 35 | C | C | 35 +/// 36 | D | D | 36 +/// 37 | E | E | 37 +/// 38 | F | F | 38 +/// 39 | G | G | 39 +/// 40 | H | H | 40 +/// 41 | I | I | 41 +/// 42 | J | J | 42 +/// 43 | K | K | 43 +/// 44 | L | L | 44 +/// 45 | M | M | 45 +/// 46 | N | N | 46 +/// 47 | O | O | 47 +/// 48 | P | P | 48 +/// 49 | Q | Q | 49 +/// 50 | R | R | 50 +/// 51 | S | S | 51 +/// 52 | T | T | 52 +/// 53 | U | U | 53 +/// 54 | V | V | 54 +/// 55 | W | W | 55 +/// 56 | X | X | 56 +/// 57 | Y | Y | 57 +/// 58 | Z | Z | 58 +/// 59 | [ | [ | 59 +/// 60 | \\ | \\ | 60 +/// 61 | ] | ] | 61 +/// 62 | ^ | ^ | 62 +/// 63 | \_ | \_ | 63 +/// 64 | NUL | ` | 64 +/// 65 | SOH | a | 65 +/// 66 | STX | b | 66 +/// 67 | ETX | c | 67 +/// 68 | EOT | d | 68 +/// 69 | ENQ | e | 69 +/// 70 | ACK | f | 70 +/// 71 | BEL | g | 71 +/// 72 | BS | h | 72 +/// 73 | HT | i | 73 +/// 74 | LF | j | 74 +/// 75 | VT | k | 75 +/// 76 | FF | l | 76 +/// 77 | CR | m | 77 +/// 78 | SO | n | 78 +/// 79 | SI | o | 79 +/// 80 | DLE | p | 80 +/// 81 | DC1 | q | 81 +/// 82 | DC2 | r | 82 +/// 83 | DC3 | s | 83 +/// 84 | DC4 | t | 84 +/// 85 | NAK | u | 85 +/// 86 | SYN | v | 86 +/// 87 | ETB | w | 87 +/// 88 | CAN | x | 88 +/// 89 | EM | y | 89 +/// 90 | SUB | z | 90 +/// 91 | ESC | { | 91 +/// 92 | FS | \| | 92 +/// 93 | GS | } | 93 +/// 94 | RS | ~ | 94 +/// 95 | US | DEL | 95 +/// 96 | FNC 3 | FNC 3 | 96 +/// 97 | FNC 2 | FNC 2 | 97 +/// 98 |Shift B|Shift A| 98 +/// 99 |Code C |Code C | 99 +/// 100 |Code B | FNC 4 | Code B +/// 101 | FNC 4 |Code A | Code A +/// 102 | FNC 1 | FNC 1 | FNC 1 +/// 103 |Start A|Start A| Start A +/// 104 |Start B|Start B| Start B +/// 105 |Start C|Start C| Start C +/// 106 | stop | stop | stop +/// +/// [`A`]: enum.Symbology.html#variant.A +/// [`B`]: enum.Symbology.html#variant.B +/// [`C`]: enum.Symbology.html#variant.C +pub trait Encoding: From + Into + PartialOrd { + /// Convert an encoding to its string representation in a given symbology + fn repr(&self, Symbology) -> String; + + /// Get the stop value in the particular encoding format + /// + /// Correspond to numerical values such that: + /// + /// u8 | [`A`] | [`B`] | [`C`] + /// :----|:-----:|:-----:|:------: + /// 106 | stop | stop | stop + /// + /// [`A`]: enum.Symbology.html#variant.A + /// [`B`]: enum.Symbology.html#variant.B + /// [`C`]: enum.Symbology.html#variant.C + fn stop() -> Self; + + /// Switch symbol for a given symbology + /// + /// Correspond to numerical values such that: + /// + /// u8 | [`A`] | [`B`] | [`C`] + /// :----|:-----:|:-----:|:------: + /// 99 |Code C |Code C | ... + /// 100 |Code B | ... | Code B + /// 101 | ... |Code A | Code A + /// + /// # Examples + /// + /// ``` + /// # use feather_code::barcode::code128::Encoding; + /// # use feather_code::barcode::code128::Symbology; + /// + /// assert_eq!(u8::switch(Symbology::A), 101); + /// assert_eq!(u8::switch(Symbology::B), 100); + /// assert_eq!(u8::switch(Symbology::C), 99); + /// ``` + /// + /// [`A`]: enum.Symbology.html#variant.A + /// [`B`]: enum.Symbology.html#variant.B + /// [`C`]: enum.Symbology.html#variant.C + fn switch(Symbology) -> Self; + + /// Start symbol for a given symbology + /// + /// Correspond to numerical values such that + /// + /// u8 | [`A`] | [`B`] | [`C`] + /// :----|:-----:|:-----:|:------: + /// 103 |Start A|Start A| Start A + /// 104 |Start B|Start B| Start B + /// 105 |Start C|Start C| Start C + /// + /// [`A`]: enum.Symbology.html#variant.A + /// [`B`]: enum.Symbology.html#variant.B + /// [`C`]: enum.Symbology.html#variant.C + fn start(Symbology) -> Self; + + /// Shift code wich indicates that the next encoding uses the shifted symbology + /// + /// When in symbology A or B, the shift code indicates the next encoding will use the other + /// symbology to parse. If we have an encoded value which encodes a lot of lowercase + /// characters, we may use Symbology [`B`]. If we need to add a single special character to + /// the sequence though, we can shift into symbology [`A`] for a single encoding to add the + /// special character. + /// + /// Correspond to numerical values such that + /// + /// u8 | [`A`] | [`B`] + /// :----|:-----:|:-----: + /// 98 |Shift B|Shift A + /// + /// [`A`]: enum.Symbology.html#variant.A + /// [`B`]: enum.Symbology.html#variant.B + fn shift() -> Self; + + /// Function 1 encoding, indicates special behaviour, ignored in the spec + fn fnc1() -> Self; + + /// Reserved encoding for function 2, currently in the spec but not used + fn fnc2() -> Self; + + /// Reserved encoding for function 3, currently in the spec but not used + fn fnc3() -> Self; + + /// Reserved encoding for function 4, currently in the spec but not used + /// + /// Correspond to numerical values such that: + /// + /// u8 | [`A`] | [`B`] + /// :----|:-----:|:-----: + /// 100 | ... | FNC 4 + /// 101 | FNC 4 | ... + /// + /// [`A`]: enum.Symbology.html#variant.A + /// [`B`]: enum.Symbology.html#variant.B + fn fnc4(Symbology) -> Option; + + /// Representation as a u8 for non-copy types to calculate checksum + fn as_u8(&self) -> u8; +} + +/// [Code128][wiki] barcode format +/// +/// # [`Symbology`] +/// +/// Code128 encodes the full 128 characters of ASCII using three different symbologies which +/// represent different subsets of ASCII. +/// +/// - [`A`]: A-Z, 0-9, and special characters (ASCII 00 to 95) +/// - [`B`]: a-z, A-Z, and 0-9 (ASCII 32-127) +/// - [`C`]: high density, number pair encoding +/// +/// The 107 different patterns map differently to ASCII characters for each +/// symbology, see documentation for `Pattern` or references for more details. +/// +/// # References +/// - [Code 128 on Wikipedia][wiki] +/// +/// [wiki]: https://en.wikipedia.org/wiki/Code_128 +/// [`Symbology`]: enum.Symbology.html +/// [`A`]: enum.Symbology.html#variant.A +/// [`B`]: enum.Symbology.html#variant.B +/// [`C`]: enum.Symbology.html#variant.C +#[derive(PartialEq,Eq,Debug)] +pub struct Code128<'a, E>(pub &'a [E]) where E: 'a + Encoding; + +impl<'a, E> Code128<'a, E> where E: 'a + Encoding { + /// If the encoding has valid format, returns the parts of the datum + /// + /// A valid [`Code128`] datum has a start encoding which determines the proper start symbology, + /// a series of encodings which represent data, a checksum digit to ensure that the data is + /// properly formatted, and a stop encoding which tells the barcode scanner to stop scanning. + fn data(&self) -> Option<(Symbology, &'a [E], &E)> { + let (start, rest) = match self.0.split_first() { + Some(x) => x, + None => return None, + }; + let rest = match rest.split_last() { + Some((stop,x)) if *stop == E::stop() => x, + _ => return None, + }; + let (check, symbols) = match rest.split_last() { + Some(x) => x, + None => return None, + }; + + match start { + _ if *start == E::start(Symbology::A) => Some((Symbology::A, symbols, &check)), + _ if *start == E::start(Symbology::B) => Some((Symbology::B, symbols, &check)), + _ if *start == E::start(Symbology::C) => Some((Symbology::C, symbols, &check)), + _ => None, + } + } +} + +impl<'a, E: 'a + Encoding> Format for Code128<'a, E> { + /// Verify data integrity, returning true if data is properly formatted, has the + /// proper length, and has the right modulo 103 checksum digit. + /// + /// # Example + /// + /// ``` + /// # use feather_code::barcode::code128::Code128; + /// # use feather_code::barcode::format::Format; + /// + /// assert!(Code128(&[103, 48, 42, 42, 17, 18, 19, 35, 54, 106]).checksum()); + /// ``` + fn checksum(&self) -> bool { + // split slice into respective parts, and return false indicating invalid + // data if the formatted data isn't the proper length + let (start, symbols, check) = match self.data() { + Some(x) => x, + None => return false, + }; + + // Sum raw numerical values from each symbol multiplied by its position + let sum: u64 = { + let mut pos: u64 = 0; + symbols.iter() + .fold(0, |sum, pat| { + let raw: u64 = pat.as_u8() as u64; + pos += 1; + sum + raw * pos + }) + } + start as u64; + + // Checksum is the remainder after dividing the raw code sum by 103 + E::from((sum % 103) as u8) == *check + } +} + +impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { + + /// Convert Code128 barcode representation to a string + /// + /// In case of badly formatted data, will return a [`format::Result`] detailing the issue. + /// + /// # Example + /// + /// ```rust + /// # use feather_code::internals::format::Decode; + /// # use feather_code::internals::code128::Code128; + /// # use feather_code::internals::code128::encodings::Pattern::*; + /// let buffer = [C105, C102, C42, C18, C40, C20, C50, C101, C16, C92, C106]; + /// let country_code: String = Code128(buffer.as_ref()).decode().unwrap(); + /// + /// assert_eq!(country_code, "42184020500".to_string()); + /// ``` + fn decode(&self) -> format::Result { + use super::format::Error::*; + use super::code128::Symbology::*; + + if self.0.len() < 4 { return Err(InvalidLength(self.0.len())) } + + let mut decoded: String = "".to_string(); + // Grab start code or return with error in case of bad format + let (state, symbols, _) = match self.data() { + Some(x) => x, + _ => return Err(BadFormat("unrecognized format".into())), + }; + + enum Parser { A, B, C, ShiftA, ShiftB } + + let mut state = match state { + A => Parser::A, + B => Parser::B, + C => Parser::C, + }; + + // Use finite state machine to parse Code128 to a String + 'parser: for e in symbols.iter() { + state = match state { + Parser::A => { + match e.as_u8() { + n if n < 98 => {decoded.push_str(&e.repr(A)); Parser::A}, + 100 => Parser::B, // Switch to symbology B + 99 => Parser::C, // Switch to symbology C + 98 => Parser::ShiftB, // shift code + 106 => break 'parser, + 102 | 97 | 96 | 101 => Parser::A, // function 1, 2, 3, 4, disabled + _ => return Err(DecodeErr(format!("unrecognized encoding {:?}", *e))), + } + }, + Parser::B => { + match e.as_u8() { + n if n < 98 => {decoded.push_str(&e.repr(B)); Parser::B}, + 101 => Parser::B, // Switch to symbology A + 99 => Parser::B, // Switch to symbology C + 106 => break 'parser, + 98 => Parser::ShiftA, // shift code + 102 | 97 | 96 | 100 => Parser::B, // function 1, 2, 3, 4, disabled + _ => return Err(DecodeErr(format!("unrecognized encoding {:?}", *e))), + } + }, + Parser::C => { + match e.as_u8() { + n if n < 100 => {decoded.push_str(&e.repr(C)); Parser::C} + 100 => Parser::B, // Switch to symbology B + 101 => Parser::A, // Switch to symbology A + 106 => break 'parser, + 102 => Parser::C, // function 1, disabled + _ => return Err(DecodeErr(format!("unexpected encoding {:?}", *e))), + } + }, + Parser::ShiftA => { + match e.as_u8() { + n if n < 98 => {decoded.push_str(&e.repr(A)); Parser::B}, + _ => return Err(DecodeErr(format!("unexpected shifted encoding {:?}", *e))), + } + }, + Parser::ShiftB => { + match e.as_u8() { + n if n < 98 => {decoded.push_str(&e.repr(B)); Parser::A}, + _ => return Err(DecodeErr(format!("unexpected shifted encoding {:?}", *e))), + } + }, + }; + }; + Ok(decoded) + } +} + +#[cfg(test)] +mod test { + + #[test] + fn checksum() { + use internals::format::Format; + use internals::code128::encodings::Pattern::*; + use internals::code128::Code128; + + assert!(Code128(&[C103, C48, C42, C42, C17, C18, C19, C35, C54, C106]).checksum()); + + assert!(Code128(&[C105, C102, C42, C18, C40, C20, C50, C101, C16, C92, C106]).checksum()); + assert!(Code128(&[105, 102, 42, 18, 40, 20, 50, 101, 16, 92, 106]).checksum()); + } + + #[test] + fn decode() { + use internals::code128::Code128; + use internals::format::Decode; + + let pjj123_c = [103, 48, 42, 42, 17, 18, 19, 35, 54, 106]; + + assert_eq!(Code128(&pjj123_c).decode().unwrap(), "PJJ123C".to_string()); + + let country_code = [105, 102, 42, 18, 40, 20, 50, 101, 16, 92, 106]; + + assert_eq!(Code128(&country_code).decode().unwrap(), "42184020500".to_string()); + + let hello_world = [104, 40, 69, 76, 76, 79, 0, 55, 79, 82, 76, 68, 43, 106]; + + assert_eq!(Code128(&hello_world).decode().unwrap(), "Hello World".to_string()); + + let shift_codes = [103, 51, 40, 98, 73, 38, 52, 100, 98, 1, 34, 106]; + + assert_eq!(Code128(&shift_codes).decode().unwrap(), "SHiFT!".to_string()) + } + + #[test] + fn split_data() { + use internals::code128::Code128; + use internals::code128::encodings::Pattern::*; + use internals::code128::Symbology::*; + + let symbols = [C103, C48, C42, C42, C17, C18, C19, C35, C54, C106]; + let code = Code128(&symbols); + + assert_eq!(code.data(), Some((A, [C48, C42, C42, C17, C18, C19, C35].as_ref(), &C54))); + + let symbols = [C103]; + let code = Code128(&symbols); + + assert_eq!(code.data(), None); + + let symbols = [C103, C106]; + let code = Code128(&symbols); + + assert_eq!(code.data(), None); + + let symbols = [C103, C48, C15, C106]; + let code = Code128(&symbols); + + assert_eq!(code.data(), Some((A, [C48].as_ref(), &C15))); + } + + quickcheck! { + fn short_data_is_invalid(symbols: Vec) -> bool { + use internals::code128::Code128; + + if symbols.len() < 4 { + Code128(symbols.as_ref()).data() == None + } else { + true + } + } + } +} diff --git a/src/internals/format.rs b/src/internals/format.rs index 1e79302..04d9d52 100644 --- a/src/internals/format.rs +++ b/src/internals/format.rs @@ -1,4 +1,7 @@ -//! Traits to encapsulate encoding and decoding +//! Abstract representation of barcode, or other, data encoding formats +//! +//! [`Decode`] and [`Encode`] must be idempotent. +use std::result; /// Representation of a barcode format pub trait Format { @@ -7,25 +10,12 @@ pub trait Format { fn checksum(&self) -> bool; } - -/// Support decoding a particular format to the target type -pub trait Decode where Self: Sized { - - /// Convert a formatted data value - fn decode(&F) -> Result; -} - - -/// Support encoding the target type as a particular format -pub trait Encode { - - /// Convert to a given format - fn encode(&self) -> Result; -} +/// Specialized result type for errors in barcode conversions +pub type Result = result::Result; /// Describes failure cases for encoding and decoding barcodes #[derive(Debug,PartialEq)] -pub enum FormatErr { +pub enum Error { /// Barcode is too short or too long InvalidLength(usize), /// Invalid internal format @@ -35,3 +25,18 @@ pub enum FormatErr { /// Decode failure DecodeErr(String), } + +/// Support decoding a particular format to the target type +pub trait Decode where Self: Format { + + /// Convert a formatted data value + fn decode(&self) -> Result; +} + + +/// Support encoding the target type as a particular format +pub trait Encode { + + /// Convert to a given format + fn encode(&self) -> Result; +} diff --git a/src/lib.rs b/src/lib.rs index 3c35da1..6e3d596 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,4 +7,8 @@ //! //! Custom visual encoding format extended from code128 barcode encoding. +#[cfg(test)] +#[macro_use] +extern crate quickcheck; + pub mod internals; From 68703e20efe592d91332ba160bb162e75849e85e Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 22 Jul 2017 16:17:11 -0400 Subject: [PATCH 14/16] Add rudimentary benchmark tests Benchmark testing allows me to check for performance regressions and make performance improvements over time. Essential for having real time capability. --- benches/code128_decode.rs | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 benches/code128_decode.rs diff --git a/benches/code128_decode.rs b/benches/code128_decode.rs new file mode 100644 index 0000000..55ac8b4 --- /dev/null +++ b/benches/code128_decode.rs @@ -0,0 +1,40 @@ +#![feature(test)] + +extern crate feather_code as fc; +extern crate test; + +use fc::internals::code128::Code128; +use fc::internals::format::Decode; +use fc::internals::code128::encodings; +use test::Bencher; + +#[bench] +fn decode_u8(bencher: &mut Bencher) { + let buffer = [105, 102, 42, 18, 40, 20, 50, 101, 16, 92, 106]; + let code = Code128(buffer.as_ref()); + + bencher.iter(|| { + code.decode().unwrap(); + }) +} + +#[bench] +fn decode_vec(bencher: &mut Bencher) { + let buffer = vec![105, 102, 42, 18, 40, 20, 50, 101, 16, 92, 106]; + let code = Code128(buffer.as_ref()); + + bencher.iter(|| { + code.decode().unwrap(); + }) +} + +#[bench] +fn decode_pattern(bencher: &mut Bencher) { + use encodings::Pattern::*; + let buffer = [C105, C102, C42, C18, C40, C20, C50, C101, C16, C92, C106]; + let code = Code128(buffer.as_ref()); + + bencher.iter(|| { + code.decode().unwrap(); + }) +} From 299e88811831c7e19bd4b003b8073caeb2a02a1b Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 22 Jul 2017 16:29:13 -0400 Subject: [PATCH 15/16] Rename internals module to barcode Better description of what the module actually does, as it mainly encapsulates all of the barcode logic. --- benches/code128_decode.rs | 6 ++--- .../code128/encodings.rs | 0 src/{internals => barcode}/code128/mod.rs | 24 +++++++++---------- src/{internals => barcode}/format.rs | 0 src/{internals => barcode}/mod.rs | 0 src/lib.rs | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) rename src/{internals => barcode}/code128/encodings.rs (100%) rename src/{internals => barcode}/code128/mod.rs (96%) rename src/{internals => barcode}/format.rs (100%) rename src/{internals => barcode}/mod.rs (100%) diff --git a/benches/code128_decode.rs b/benches/code128_decode.rs index 55ac8b4..028729e 100644 --- a/benches/code128_decode.rs +++ b/benches/code128_decode.rs @@ -3,9 +3,9 @@ extern crate feather_code as fc; extern crate test; -use fc::internals::code128::Code128; -use fc::internals::format::Decode; -use fc::internals::code128::encodings; +use fc::barcode::code128::Code128; +use fc::barcode::format::Decode; +use fc::barcode::code128::encodings; use test::Bencher; #[bench] diff --git a/src/internals/code128/encodings.rs b/src/barcode/code128/encodings.rs similarity index 100% rename from src/internals/code128/encodings.rs rename to src/barcode/code128/encodings.rs diff --git a/src/internals/code128/mod.rs b/src/barcode/code128/mod.rs similarity index 96% rename from src/internals/code128/mod.rs rename to src/barcode/code128/mod.rs index c4eaae1..26c4662 100644 --- a/src/internals/code128/mod.rs +++ b/src/barcode/code128/mod.rs @@ -346,9 +346,9 @@ impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { /// # Example /// /// ```rust - /// # use feather_code::internals::format::Decode; - /// # use feather_code::internals::code128::Code128; - /// # use feather_code::internals::code128::encodings::Pattern::*; + /// # use feather_code::barcode::format::Decode; + /// # use feather_code::barcode::code128::Code128; + /// # use feather_code::barcode::code128::encodings::Pattern::*; /// let buffer = [C105, C102, C42, C18, C40, C20, C50, C101, C16, C92, C106]; /// let country_code: String = Code128(buffer.as_ref()).decode().unwrap(); /// @@ -433,9 +433,9 @@ mod test { #[test] fn checksum() { - use internals::format::Format; - use internals::code128::encodings::Pattern::*; - use internals::code128::Code128; + use barcode::format::Format; + use barcode::code128::encodings::Pattern::*; + use barcode::code128::Code128; assert!(Code128(&[C103, C48, C42, C42, C17, C18, C19, C35, C54, C106]).checksum()); @@ -445,8 +445,8 @@ mod test { #[test] fn decode() { - use internals::code128::Code128; - use internals::format::Decode; + use barcode::code128::Code128; + use barcode::format::Decode; let pjj123_c = [103, 48, 42, 42, 17, 18, 19, 35, 54, 106]; @@ -467,9 +467,9 @@ mod test { #[test] fn split_data() { - use internals::code128::Code128; - use internals::code128::encodings::Pattern::*; - use internals::code128::Symbology::*; + use barcode::code128::Code128; + use barcode::code128::encodings::Pattern::*; + use barcode::code128::Symbology::*; let symbols = [C103, C48, C42, C42, C17, C18, C19, C35, C54, C106]; let code = Code128(&symbols); @@ -494,7 +494,7 @@ mod test { quickcheck! { fn short_data_is_invalid(symbols: Vec) -> bool { - use internals::code128::Code128; + use barcode::code128::Code128; if symbols.len() < 4 { Code128(symbols.as_ref()).data() == None diff --git a/src/internals/format.rs b/src/barcode/format.rs similarity index 100% rename from src/internals/format.rs rename to src/barcode/format.rs diff --git a/src/internals/mod.rs b/src/barcode/mod.rs similarity index 100% rename from src/internals/mod.rs rename to src/barcode/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 6e3d596..f38793d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,4 +11,4 @@ #[macro_use] extern crate quickcheck; -pub mod internals; +pub mod barcode; From 1c5a760d79a5d174a4c23f0d26c5efc71e0656d1 Mon Sep 17 00:00:00 2001 From: Gwen Lofman Date: Sat, 22 Jul 2017 22:11:16 -0400 Subject: [PATCH 16/16] Remove uneccesary copies by using u8 Since I was using u8 to perform the actual string conversions anyway, simply use u8 directly and remove the repr() method from Encoding trait. Saves about 100 ns/iter on benchmarks. --- src/barcode/code128/encodings.rs | 41 -------------------------------- src/barcode/code128/mod.rs | 18 +++++++------- 2 files changed, 9 insertions(+), 50 deletions(-) diff --git a/src/barcode/code128/encodings.rs b/src/barcode/code128/encodings.rs index 12cc2e0..56452a6 100644 --- a/src/barcode/code128/encodings.rs +++ b/src/barcode/code128/encodings.rs @@ -168,21 +168,6 @@ impl Into for Pattern { } impl Encoding for Pattern { - #[inline] - fn repr(&self, s: Symbology) -> String { - match s { - Symbology::A => { - if (*self as u8) < 64u8 { - ((*self as u8 + 32) as char).to_string() - } else { - ((*self as u8 - 64) as char).to_string() - } - }, - Symbology::B => ((*self as u8 + 32) as char).to_string(), - Symbology::C => (*self as u8).to_string(), - } - } - #[inline] fn stop() -> Self {Pattern::C106} @@ -232,20 +217,6 @@ impl Encoding for Pattern { } impl Encoding for u8 { - #[inline] - fn repr(&self, s: Symbology) -> String { - match s { - Symbology::A => { - if *self < 64u8 { - ((*self + 32) as char).to_string() - } else { - ((*self - 64) as char).to_string() - } - }, - Symbology::B => ((*self + 32) as char).to_string(), - Symbology::C => (*self).to_string(), - } - } #[inline] fn stop() -> Self {106} @@ -302,17 +273,5 @@ mod test { use super::Encoding; p == super::Pattern::from(p).as_u8() } - - fn pattern_repr_matches_u8_repr(p: u8) -> bool { - use super::Encoding; - use super::Symbology; - if p < 107 { - super::Pattern::from(p).repr(Symbology::A) == p.repr(Symbology::A) && - super::Pattern::from(p).repr(Symbology::B) == p.repr(Symbology::B) && - super::Pattern::from(p).repr(Symbology::C) == p.repr(Symbology::C) - } else { - true - } - } } } diff --git a/src/barcode/code128/mod.rs b/src/barcode/code128/mod.rs index 26c4662..9099f60 100644 --- a/src/barcode/code128/mod.rs +++ b/src/barcode/code128/mod.rs @@ -146,8 +146,6 @@ pub enum Symbology { /// [`B`]: enum.Symbology.html#variant.B /// [`C`]: enum.Symbology.html#variant.C pub trait Encoding: From + Into + PartialOrd { - /// Convert an encoding to its string representation in a given symbology - fn repr(&self, Symbology) -> String; /// Get the stop value in the particular encoding format /// @@ -362,14 +360,14 @@ impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { let mut decoded: String = "".to_string(); // Grab start code or return with error in case of bad format - let (state, symbols, _) = match self.data() { + let (start, symbols, _) = match self.data() { Some(x) => x, _ => return Err(BadFormat("unrecognized format".into())), }; enum Parser { A, B, C, ShiftA, ShiftB } - let mut state = match state { + let mut state = match start { A => Parser::A, B => Parser::B, C => Parser::C, @@ -380,7 +378,8 @@ impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { state = match state { Parser::A => { match e.as_u8() { - n if n < 98 => {decoded.push_str(&e.repr(A)); Parser::A}, + n if n < 64 => {decoded.push((n+32) as char); Parser::A}, + n if n < 98 => {decoded.push((n-64) as char); Parser::A}, 100 => Parser::B, // Switch to symbology B 99 => Parser::C, // Switch to symbology C 98 => Parser::ShiftB, // shift code @@ -391,7 +390,7 @@ impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { }, Parser::B => { match e.as_u8() { - n if n < 98 => {decoded.push_str(&e.repr(B)); Parser::B}, + n if n < 98 => {decoded.push((n+32) as char); Parser::B}, 101 => Parser::B, // Switch to symbology A 99 => Parser::B, // Switch to symbology C 106 => break 'parser, @@ -402,7 +401,7 @@ impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { }, Parser::C => { match e.as_u8() { - n if n < 100 => {decoded.push_str(&e.repr(C)); Parser::C} + n if n < 100 => {decoded.push_str(&n.to_string()); Parser::C} 100 => Parser::B, // Switch to symbology B 101 => Parser::A, // Switch to symbology A 106 => break 'parser, @@ -412,13 +411,14 @@ impl<'a, E> Decode for Code128<'a, E> where E: 'a + Encoding + Debug { }, Parser::ShiftA => { match e.as_u8() { - n if n < 98 => {decoded.push_str(&e.repr(A)); Parser::B}, + n if n < 64 => {decoded.push((n+32) as char); Parser::B}, + n if n < 98 => {decoded.push((n-64) as char); Parser::B}, _ => return Err(DecodeErr(format!("unexpected shifted encoding {:?}", *e))), } }, Parser::ShiftB => { match e.as_u8() { - n if n < 98 => {decoded.push_str(&e.repr(B)); Parser::A}, + n if n < 98 => {decoded.push((n+32) as char); Parser::A}, _ => return Err(DecodeErr(format!("unexpected shifted encoding {:?}", *e))), } },