Rustfmt and clippy.

This commit is contained in:
kennytm 2017-11-07 16:23:50 +08:00
parent 8be25fa56a
commit 21efb6205e
18 changed files with 1948 additions and 1324 deletions

View file

@ -15,20 +15,16 @@ addons:
matrix:
include:
- os: linux
rust: 1.17.0
rust: 1.20.0
- os: linux
rust: 1.21.0
rust: stable
- os: osx
rust: 1.21.0
rust: stable
- os: linux
rust: beta
- os: linux
rust: nightly
matrix:
allow_failures:
- rust: beta
install:
- if [ "$TRAVIS_OS_NAME" = 'linux' ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi
- rustup target add i686-$OS

View file

@ -21,6 +21,7 @@ maintenance = { status = "passively-maintained" }
[dependencies]
image = { version = "0.17", optional = true }
checked_int_cast = "1"
[features]
default = ["image", "svg"]

View file

@ -13,14 +13,14 @@ Cargo.toml
```toml
[dependencies]
qrcode = "0.4"
qrcode = "0.5"
```
The default settings will depend on the `image` crate. If you don't need image generation capability, disable the `default-features`:
```toml
[dependencies]
qrcode = { version = "0.4", default-features = false }
qrcode = { version = "0.5", default-features = false }
```
Example

View file

@ -1,5 +1,5 @@
extern crate qrcode;
extern crate image;
extern crate qrcode;
use qrcode::QrCode;
use image::Luma;

View file

@ -1,6 +1,6 @@
extern crate qrcode;
use qrcode::{QrCode, Version, EcLevel};
use qrcode::{EcLevel, QrCode, Version};
use qrcode::render::svg;
fn main() {

8
rustfmt.toml Normal file
View file

@ -0,0 +1,8 @@
array_width = 100
chain_width = 100
fn_call_width = 100
# single_line_if_else_max_width = 100
struct_lit_width = 100
struct_variant_width = 100
error_on_line_overflow = false

View file

@ -6,9 +6,11 @@ pub fn main() {
let arg = env::args().nth(1).unwrap();
let code = qrcode::QrCode::new(arg.as_bytes()).unwrap();
print!("{}", code.render()
print!(
"{}",
code.render()
.dark_color("\x1b[7m \x1b[0m")
.light_color("\x1b[49m \x1b[0m")
.build());
.build()
);
}

View file

@ -5,8 +5,9 @@ use std::cmp::min;
#[cfg(feature = "bench")]
use test::Bencher;
use types::{QrResult, QrError, Mode, EcLevel, Version};
use optimize::{Parser, Optimizer, total_encoded_len, Segment};
use types::{EcLevel, Mode, QrError, QrResult, Version};
use optimize::{total_encoded_len, Optimizer, Parser, Segment};
use cast::{As, Truncate};
//------------------------------------------------------------------------------
//{{{ Bits
@ -20,8 +21,8 @@ pub struct Bits {
impl Bits {
/// Constructs a new, empty bits structure.
pub fn new(version: Version) -> Bits {
Bits { data: Vec::new(), bit_offset: 0, version: version }
pub fn new(version: Version) -> Self {
Self { data: Vec::new(), bit_offset: 0, version: version }
}
/// Pushes an N-bit big-endian integer to the end of the bits.
@ -30,29 +31,34 @@ impl Bits {
/// `n` bit in size. Otherwise the excess bits may stomp on the existing
/// ones.
fn push_number(&mut self, n: usize, number: u16) {
debug_assert!(n == 16 || n < 16 && number < (1 << n),
"{} is too big as a {}-bit number", number, n);
debug_assert!(
n == 16 || n < 16 && number < (1 << n),
"{} is too big as a {}-bit number",
number,
n
);
let b = self.bit_offset + n;
let last_index = self.data.len().wrapping_sub(1);
match (self.bit_offset, b) {
(0, 0...8) => {
self.data.push((number << (8-b)) as u8);
self.data.push((number << (8 - b)).truncate_as_u8());
}
(0, _) => {
self.data.push((number >> (b-8)) as u8);
self.data.push((number << (16-b)) as u8);
self.data.push((number >> (b - 8)).truncate_as_u8());
self.data.push((number << (16 - b)).truncate_as_u8());
}
(_, 0...8) => {
*self.data.last_mut().unwrap() |= (number << (8-b)) as u8;
self.data[last_index] |= (number << (8 - b)).truncate_as_u8();
}
(_, 9...16) => {
*self.data.last_mut().unwrap() |= (number >> (b-8)) as u8;
self.data.push((number << (16-b)) as u8);
self.data[last_index] |= (number >> (b - 8)).truncate_as_u8();
self.data.push((number << (16 - b)).truncate_as_u8());
}
_ => {
*self.data.last_mut().unwrap() |= (number >> (b-8)) as u8;
self.data.push((number >> (b-16)) as u8);
self.data.push((number << (24-b)) as u8);
self.data[last_index] |= (number >> (b - 8)).truncate_as_u8();
self.data.push((number >> (b - 16)).truncate_as_u8());
self.data.push((number << (24 - b)).truncate_as_u8());
}
}
self.bit_offset = b & 7;
@ -66,7 +72,7 @@ impl Bits {
if n > 16 || number >= (1 << n) {
Err(QrError::DataTooLong)
} else {
self.push_number(n, number as u16);
self.push_number(n, number.as_u16());
Ok(())
}
}
@ -91,6 +97,11 @@ impl Bits {
}
}
/// Whether there are any bits pushed.
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
/// The maximum number of bits allowed by the provided QR code version and
/// error correction level.
pub fn max_len(&self, ec_level: EcLevel) -> QrResult<usize> {
@ -118,14 +129,19 @@ fn test_push_number() {
let bytes = bits.into_bytes();
assert_eq!(bytes, vec![0b010__110__10, // 90
assert_eq!(
bytes,
vec![
0b010__110__10, // 90
0b1__001_1010, // 154
0b1100__1011, // 203
0b0110_1101, // 109
0b01_1001_00, // 100
0b01__111_001, // 121
0b0_1110_001, // 113
0b1__0000000]); // 128
0b1__0000000, // 128
]
);
}
#[cfg(feature = "bench")]
@ -171,6 +187,7 @@ impl Bits {
/// If the mode is not supported in the provided version, this method
/// returns `Err(QrError::UnsupportedCharacterSet)`.
pub fn push_mode_indicator(&mut self, mode: ExtendedMode) -> QrResult<()> {
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
let number = match (self.version, mode) {
(Version::Micro(1), ExtendedMode::Data(Mode::Numeric)) => return Ok(()),
(Version::Micro(_), ExtendedMode::Data(Mode::Numeric)) => 0,
@ -188,7 +205,8 @@ impl Bits {
(_, ExtendedMode::StructuredAppend) => 0b0011,
};
let bits = self.version.mode_bits_count();
self.push_number_checked(bits, number).or(Err(QrError::UnsupportedCharacterSet))
self.push_number_checked(bits, number)
.or(Err(QrError::UnsupportedCharacterSet))
}
}
@ -234,19 +252,19 @@ impl Bits {
/// return `Err(QrError::InvalidECIDesignator)`.
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
self.reserve(12); // assume the common case that eci_designator <= 127.
try!(self.push_mode_indicator(ExtendedMode::Eci));
self.push_mode_indicator(ExtendedMode::Eci)?;
match eci_designator {
0...127 => {
self.push_number(8, eci_designator as u16);
self.push_number(8, eci_designator.as_u16());
}
128...16383 => {
self.push_number(2, 0b10);
self.push_number(14, eci_designator as u16);
self.push_number(14, eci_designator.as_u16());
}
16384...999999 => {
self.push_number(3, 0b110);
self.push_number(5, (eci_designator >> 16) as u16);
self.push_number(16, (eci_designator & 0xffff) as u16);
self.push_number(5, (eci_designator >> 16).as_u16());
self.push_number(16, (eci_designator & 0xffff).as_u16());
}
_ => return Err(QrError::InvalidEciDesignator),
}
@ -257,7 +275,7 @@ impl Bits {
#[cfg(test)]
mod eci_tests {
use bits::Bits;
use types::{Version, QrError};
use types::{QrError, Version};
#[test]
fn test_9() {
@ -277,10 +295,7 @@ mod eci_tests {
fn test_999999() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_eci_designator(999999), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0111__110_0,
0b11110100,
0b00100011,
0b1111__0000]);
assert_eq!(bits.into_bytes(), vec![0b0111__110_0, 0b11110100, 0b00100011, 0b1111__0000]);
}
#[test]
@ -304,8 +319,8 @@ impl Bits {
fn push_header(&mut self, mode: Mode, raw_data_len: usize) -> QrResult<()> {
let length_bits = mode.length_bits_count(self.version);
self.reserve(length_bits + 4 + mode.data_bits_count(raw_data_len));
try!(self.push_mode_indicator(ExtendedMode::Data(mode)));
try!(self.push_number_checked(length_bits, raw_data_len));
self.push_mode_indicator(ExtendedMode::Data(mode))?;
self.push_number_checked(length_bits, raw_data_len)?;
Ok(())
}
@ -313,9 +328,12 @@ impl Bits {
///
/// The data should only contain the characters 0 to 9.
pub fn push_numeric_data(&mut self, data: &[u8]) -> QrResult<()> {
try!(self.push_header(Mode::Numeric, data.len()));
self.push_header(Mode::Numeric, data.len())?;
for chunk in data.chunks(3) {
let number = chunk.iter().map(|b| (*b - b'0') as u16).fold(0, |a, b| a*10 + b);
let number = chunk
.iter()
.map(|b| u16::from(*b - b'0'))
.fold(0, |a, b| a * 10 + b);
let length = chunk.len() * 3 + 1;
self.push_number(length, number);
}
@ -326,25 +344,26 @@ impl Bits {
#[cfg(test)]
mod numeric_tests {
use bits::Bits;
use types::{Version, QrError};
use types::{QrError, Version};
#[test]
fn test_iso_18004_2006_example_1() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0001_0000,
0b001000_00,
0b00001100,
0b01010110,
0b01_100001,
0b1__0000000]);
assert_eq!(
bits.into_bytes(),
vec![0b0001_0000, 0b001000_00, 0b00001100, 0b01010110, 0b01_100001, 0b1__0000000]
);
}
#[test]
fn test_iso_18004_2000_example_2() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0001_0000,
assert_eq!(
bits.into_bytes(),
vec![
0b0001_0000,
0b010000_00,
0b00001100,
0b01010110,
@ -352,21 +371,28 @@ mod numeric_tests {
0b0110_1110,
0b000101_00,
0b11101010,
0b0101__0000]);
0b0101__0000,
]
);
}
#[test]
fn test_iso_18004_2006_example_2() {
let mut bits = Bits::new(Version::Micro(3));
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b00_10000_0,
assert_eq!(
bits.into_bytes(),
vec![
0b00_10000_0,
0b00000110,
0b0_0101011,
0b001_10101,
0b00110_111,
0b0000101_0,
0b01110101,
0b00101__000]);
0b00101__000,
]
);
}
#[test]
@ -380,16 +406,16 @@ mod numeric_tests {
//------------------------------------------------------------------------------
//{{{ Mode::Alphanumeric mode
/// In QR code "Mode::Alphanumeric" mode, a pair of alphanumeric characters will be
/// encoded as a base-45 integer. `alphanumeric_digit` converts each character
/// into its corresponding base-45 digit.
/// In QR code `Mode::Alphanumeric` mode, a pair of alphanumeric characters will
/// be encoded as a base-45 integer. `alphanumeric_digit` converts each
/// character into its corresponding base-45 digit.
///
/// The conversion is specified in ISO/IEC 18004:2006, §8.4.3, Table 5.
#[inline]
fn alphanumeric_digit(character: u8) -> u16 {
match character {
b'0' ... b'9' => (character - b'0') as u16,
b'A' ... b'Z' => (character - b'A') as u16 + 10,
b'0'...b'9' => u16::from(character - b'0'),
b'A'...b'Z' => u16::from(character - b'A') + 10,
b' ' => 36,
b'$' => 37,
b'%' => 38,
@ -409,9 +435,12 @@ impl Bits {
/// The data should only contain the charaters A to Z (excluding lowercase),
/// 0 to 9, space, `$`, `%`, `*`, `+`, `-`, `.`, `/` or `:`.
pub fn push_alphanumeric_data(&mut self, data: &[u8]) -> QrResult<()> {
try!(self.push_header(Mode::Alphanumeric, data.len()));
self.push_header(Mode::Alphanumeric, data.len())?;
for chunk in data.chunks(2) {
let number = chunk.iter().map(|b| alphanumeric_digit(*b)).fold(0, |a, b| a*45 + b);
let number = chunk
.iter()
.map(|b| alphanumeric_digit(*b))
.fold(0, |a, b| a * 45 + b);
let length = chunk.len() * 5 + 1;
self.push_number(length, number);
}
@ -422,18 +451,16 @@ impl Bits {
#[cfg(test)]
mod alphanumeric_tests {
use bits::Bits;
use types::{Version, QrError};
use types::{QrError, Version};
#[test]
fn test_iso_18004_2006_example() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0010_0000,
0b00101_001,
0b11001110,
0b11100111,
0b001_00001,
0b0__0000000]);
assert_eq!(
bits.into_bytes(),
vec![0b0010_0000, 0b00101_001, 0b11001110, 0b11100111, 0b001_00001, 0b0__0000000]
);
}
#[test]
@ -456,9 +483,9 @@ mod alphanumeric_tests {
impl Bits {
/// Encodes 8-bit byte data to the bits.
pub fn push_byte_data(&mut self, data: &[u8]) -> QrResult<()> {
try!(self.push_header(Mode::Byte, data.len()));
self.push_header(Mode::Byte, data.len())?;
for b in data {
self.push_number(8, *b as u16);
self.push_number(8, u16::from(*b));
}
Ok(())
}
@ -467,13 +494,16 @@ impl Bits {
#[cfg(test)]
mod byte_tests {
use bits::Bits;
use types::{Version, QrError};
use types::{QrError, Version};
#[test]
fn test() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_byte_data(b"\x12\x34\x56\x78\x9a\xbc\xde\xf0"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0100_0000,
assert_eq!(
bits.into_bytes(),
vec![
0b0100_0000,
0b1000_0001,
0b0010_0011,
0b0100_0101,
@ -482,7 +512,9 @@ mod byte_tests {
0b1010_1011,
0b1100_1101,
0b1110_1111,
0b0000__0000]);
0b0000__0000,
]
);
}
#[test]
@ -505,13 +537,17 @@ mod byte_tests {
impl Bits {
/// Encodes Shift JIS double-byte data to the bits.
pub fn push_kanji_data(&mut self, data: &[u8]) -> QrResult<()> {
try!(self.push_header(Mode::Kanji, data.len()/2));
self.push_header(Mode::Kanji, data.len() / 2)?;
for kanji in data.chunks(2) {
if kanji.len() != 2 {
return Err(QrError::InvalidCharacter);
}
let cp = (kanji[0] as u16) * 256 + (kanji[1] as u16);
let bytes = if cp < 0xe040 { cp - 0x8140 } else { cp - 0xc140 };
let cp = u16::from(kanji[0]) * 256 + u16::from(kanji[1]);
let bytes = if cp < 0xe040 {
cp - 0x8140
} else {
cp - 0xc140
};
let number = (bytes >> 8) * 0xc0 + (bytes & 0xff);
self.push_number(13, number);
}
@ -522,17 +558,16 @@ impl Bits {
#[cfg(test)]
mod kanji_tests {
use bits::Bits;
use types::{Version, QrError};
use types::{QrError, Version};
#[test]
fn test_iso_18004_example() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_kanji_data(b"\x93\x5f\xe4\xaa"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b1000_0000,
0b0010_0110,
0b11001111,
0b1_1101010,
0b101010__00]);
assert_eq!(
bits.into_bytes(),
vec![0b1000_0000, 0b0010_0110, 0b11001111, 0b1_1101010, 0b101010__00]
);
}
#[test]
@ -544,8 +579,10 @@ mod kanji_tests {
#[test]
fn test_data_too_long() {
let mut bits = Bits::new(Version::Micro(3));
assert_eq!(bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"),
Err(QrError::DataTooLong));
assert_eq!(
bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"),
Err(QrError::DataTooLong)
);
}
}
@ -593,8 +630,8 @@ impl Bits {
/// bits.push_fnc1_second_position(b'A' + 100);
/// ```
pub fn push_fnc1_second_position(&mut self, application_indicator: u8) -> QrResult<()> {
try!(self.push_mode_indicator(ExtendedMode::Fnc1Second));
self.push_number(8, application_indicator as u16);
self.push_mode_indicator(ExtendedMode::Fnc1Second)?;
self.push_number(8, u16::from(application_indicator));
Ok(())
}
}
@ -646,7 +683,6 @@ static DATA_LENGTHS: [[usize; 4]; 44] = [
[21616, 16816, 12016, 9136],
[22496, 17728, 12656, 9776],
[23648, 18672, 13328, 10208],
// Micro versions
[20, 0, 0, 0],
[40, 32, 0, 0],
@ -658,12 +694,12 @@ impl Bits {
/// Pushes the ending bits to indicate no more data.
pub fn push_terminator(&mut self, ec_level: EcLevel) -> QrResult<()> {
let terminator_size = match self.version {
Version::Micro(a) => (a as usize) * 2 + 1,
Version::Micro(a) => a.as_usize() * 2 + 1,
_ => 4,
};
let cur_length = self.len();
let data_length = try!(self.max_len(ec_level));
let data_length = self.max_len(ec_level)?;
if cur_length > data_length {
return Err(QrError::DataTooLong);
}
@ -679,7 +715,11 @@ impl Bits {
self.bit_offset = 0;
let data_bytes_length = data_length / 8;
let padding_bytes_count = data_bytes_length - self.data.len();
let padding = PADDING_BYTES.iter().cloned().cycle().take(padding_bytes_count);
let padding = PADDING_BYTES
.iter()
.cloned()
.cycle()
.take(padding_bytes_count);
self.data.extend(padding);
}
@ -694,18 +734,31 @@ impl Bits {
#[cfg(test)]
mod finish_tests {
use bits::Bits;
use types::{Version, EcLevel, QrError};
use types::{EcLevel, QrError, Version};
#[test]
fn test_hello_world() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_alphanumeric_data(b"HELLO WORLD"), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::Q), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b00100000, 0b01011011, 0b00001011,
0b01111000, 0b11010001, 0b01110010,
0b11011100, 0b01001101, 0b01000011,
0b01000000, 0b11101100, 0b00010001,
0b11101100]);
assert_eq!(
bits.into_bytes(),
vec![
0b00100000,
0b01011011,
0b00001011,
0b01111000,
0b11010001,
0b01110010,
0b11011100,
0b01001101,
0b01000011,
0b01000000,
0b11101100,
0b00010001,
0b11101100,
]
);
}
#[test]
@ -755,16 +808,17 @@ mod finish_tests {
impl Bits {
/// Push a segmented data to the bits, and then terminate it.
pub fn push_segments<I>(&mut self, data: &[u8], segments_iter: I) -> QrResult<()>
where I: Iterator<Item=Segment>
where
I: Iterator<Item = Segment>,
{
for segment in segments_iter {
let slice = &data[segment.begin..segment.end];
try!(match segment.mode {
match segment.mode {
Mode::Numeric => self.push_numeric_data(slice),
Mode::Alphanumeric => self.push_alphanumeric_data(slice),
Mode::Byte => self.push_byte_data(slice),
Mode::Kanji => self.push_kanji_data(slice),
});
}?;
}
Ok(())
}
@ -774,36 +828,50 @@ impl Bits {
let segments = Parser::new(data).optimize(self.version);
self.push_segments(data, segments)
}
}
#[cfg(test)]
mod encode_tests {
use bits::Bits;
use types::{Version, QrError, QrResult, EcLevel};
use types::{EcLevel, QrError, QrResult, Version};
fn encode(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult<Vec<u8>> {
let mut bits = Bits::new(version);
try!(bits.push_optimal_data(data));
try!(bits.push_terminator(ec_level));
bits.push_optimal_data(data)?;
bits.push_terminator(ec_level)?;
Ok(bits.into_bytes())
}
#[test]
fn test_alphanumeric() {
let res = encode(b"HELLO WORLD", Version::Normal(1), EcLevel::Q);
assert_eq!(res, Ok(vec![0b00100000, 0b01011011, 0b00001011,
0b01111000, 0b11010001, 0b01110010,
0b11011100, 0b01001101, 0b01000011,
0b01000000, 0b11101100, 0b00010001,
0b11101100]));
assert_eq!(
res,
Ok(vec![
0b00100000,
0b01011011,
0b00001011,
0b01111000,
0b11010001,
0b01110010,
0b11011100,
0b01001101,
0b01000011,
0b01000000,
0b11101100,
0b00010001,
0b11101100,
])
);
}
#[test]
fn test_auto_mode_switch() {
let res = encode(b"123A", Version::Micro(2), EcLevel::L);
assert_eq!(res, Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101,
0b0_00000__00, 0b11101100]));
assert_eq!(
res,
Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101, 0b0_00000__00, 0b11101100])
);
}
#[test]
@ -824,15 +892,17 @@ mod encode_tests {
pub fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult<Bits> {
let segments = Parser::new(data).collect::<Vec<Segment>>();
for version in &[Version::Normal(9), Version::Normal(26), Version::Normal(40)] {
let opt_segments = Optimizer::new(segments.iter().map(|s| *s), *version).collect::<Vec<_>>();
let opt_segments = Optimizer::new(segments.iter().cloned(), *version).collect::<Vec<_>>();
let total_len = total_encoded_len(&*opt_segments, *version);
let data_capacity = version.fetch(ec_level, &DATA_LENGTHS).unwrap();
let data_capacity = version
.fetch(ec_level, &DATA_LENGTHS)
.expect("invalid DATA_LENGTHS");
if total_len <= data_capacity {
let min_version = find_min_version(total_len, ec_level);
let mut bits = Bits::new(min_version);
bits.reserve(total_len);
try!(bits.push_segments(data, opt_segments.into_iter()));
try!(bits.push_terminator(ec_level));
bits.push_segments(data, opt_segments.into_iter())?;
bits.push_terminator(ec_level)?;
return Ok(bits);
}
}
@ -852,13 +922,13 @@ fn find_min_version(length: usize, ec_level: EcLevel) -> Version {
max = half;
}
}
Version::Normal((min + 1) as i16)
Version::Normal((min + 1).as_i16())
}
#[cfg(test)]
mod encode_auto_tests {
use bits::{find_min_version, encode_auto};
use types::{Version, EcLevel};
use bits::{encode_auto, find_min_version};
use types::{EcLevel, Version};
#[test]
fn test_find_min_version() {
@ -893,5 +963,3 @@ mod encode_auto_tests {
//}}}
//------------------------------------------------------------------------------

File diff suppressed because it is too large Load diff

90
src/cast.rs Normal file
View file

@ -0,0 +1,90 @@
use std::fmt::Display;
#[cfg(debug_assertions)]
use checked_int_cast::CheckedIntCast;
pub trait Truncate {
fn truncate_as_u8(self) -> u8;
}
impl Truncate for u16 {
#[cfg_attr(feature = "cargo-clippy", allow(cast_possible_truncation))]
fn truncate_as_u8(self) -> u8 {
(self & 0xff) as u8
}
}
pub trait As {
fn as_u16(self) -> u16;
fn as_i16(self) -> i16;
fn as_u32(self) -> u32;
fn as_usize(self) -> usize;
fn as_isize(self) -> isize;
}
trait ExpectOrOverflow {
type Output;
fn expect_or_overflow<D: Display>(self, value: D, ty: &str) -> Self::Output;
}
impl<T> ExpectOrOverflow for Option<T> {
type Output = T;
fn expect_or_overflow<D: Display>(self, value: D, ty: &str) -> Self::Output {
match self {
Some(v) => v,
None => panic!("{} overflows {}", value, ty),
}
}
}
macro_rules! impl_as {
($ty:ty) => {
#[cfg(debug_assertions)]
impl As for $ty {
fn as_u16(self) -> u16 {
self.as_u16_checked().expect_or_overflow(self, "u16")
}
fn as_i16(self) -> i16 {
self.as_i16_checked().expect_or_overflow(self, "i16")
}
fn as_u32(self) -> u32 {
self.as_u32_checked().expect_or_overflow(self, "u32")
}
fn as_usize(self) -> usize {
self.as_usize_checked().expect_or_overflow(self, "usize")
}
fn as_isize(self) -> isize {
self.as_isize_checked().expect_or_overflow(self, "usize")
}
}
#[cfg(not(debug_assertions))]
#[cfg_attr(feature = "cargo-clippy", allow(cast_possible_truncation))]
impl As for $ty {
fn as_u16(self) -> u16 {
self as u16
}
fn as_i16(self) -> i16 {
self as i16
}
fn as_u32(self) -> u32 {
self as u32
}
fn as_usize(self) -> usize {
self as usize
}
fn as_isize(self) -> isize {
self as isize
}
}
}
}
impl_as!(i16);
impl_as!(u32);
impl_as!(usize);
impl_as!(isize);

116
src/ec.rs
View file

@ -1,9 +1,8 @@
//! The `ec` module applies the Reed-Solomon error correction codes.
use std::iter::repeat;
use std::ops::Deref;
use types::{QrResult, Version, EcLevel};
use types::{EcLevel, QrResult, Version};
//------------------------------------------------------------------------------
//{{{ Error correction primitive
@ -18,25 +17,27 @@ use types::{QrResult, Version, EcLevel};
/// GF(256), and then computes the polynomial modulus with a generator
/// polynomial of degree N.
pub fn create_error_correction_code(data: &[u8], ec_code_size: usize) -> Vec<u8> {
let mut res = data.to_vec();
res.extend(repeat(0).take(ec_code_size));
let data_len = data.len();
let log_den = GENERATOR_POLYNOMIALS[ec_code_size];
let mut res = data.to_vec();
res.resize(ec_code_size + data_len, 0);
// rust-lang-nursery/rust-clippy#2213
#[cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))]
for i in 0..data_len {
let lead_coeff = res[i] as usize;
if lead_coeff == 0 {
continue;
}
let log_lead_coeff = LOG_TABLE[lead_coeff] as usize;
let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]);
for (u, v) in res[i + 1..].iter_mut().zip(log_den.iter()) {
*u ^= EXP_TABLE[((*v as usize + log_lead_coeff) % 255) as usize];
*u ^= EXP_TABLE[(usize::from(*v) + log_lead_coeff) % 255];
}
}
res.into_iter().skip(data_len).collect()
res.split_off(data_len)
}
#[cfg(test)]
@ -73,8 +74,8 @@ mod ec_tests {
///
/// The longest slice must be at the last of `blocks`, and `blocks` must not be
/// empty.
fn interleave<T: Copy, V: Deref<Target=[T]>>(blocks: &Vec<V>) -> Vec<T> {
let last_block_len = blocks.last().unwrap().len();
fn interleave<T: Copy, V: Deref<Target = [T]>>(blocks: &[V]) -> Vec<T> {
let last_block_len = blocks.last().expect("non-empty blocks").len();
let mut res = Vec::with_capacity(last_block_len * blocks.len());
for i in 0..last_block_len {
for t in blocks {
@ -88,7 +89,7 @@ fn interleave<T: Copy, V: Deref<Target=[T]>>(blocks: &Vec<V>) -> Vec<T> {
#[test]
fn test_interleave() {
let res = interleave(&vec![&b"1234"[..], b"5678", b"abcdef", b"ghijkl"]);
let res = interleave(&[&b"1234"[..], b"5678", b"abcdef", b"ghijkl"]);
assert_eq!(&*res, b"15ag26bh37ci48djekfl");
}
@ -98,11 +99,13 @@ fn test_interleave() {
/// Constructs data and error correction codewords ready to be put in the QR
/// code matrix.
pub fn construct_codewords(rawbits: &[u8],
pub fn construct_codewords(
rawbits: &[u8],
version: Version,
ec_level: EcLevel) -> QrResult<(Vec<u8>, Vec<u8>)> {
ec_level: EcLevel,
) -> QrResult<(Vec<u8>, Vec<u8>)> {
let (block_1_size, block_1_count, block_2_size, block_2_count) =
try!(version.fetch(ec_level, &DATA_BYTES_PER_BLOCK));
version.fetch(ec_level, &DATA_BYTES_PER_BLOCK)?;
let blocks_count = block_1_count + block_2_count;
let block_1_end = block_1_size * block_1_count;
@ -118,8 +121,9 @@ pub fn construct_codewords(rawbits: &[u8],
}
// Generate EC codes.
let ec_bytes = try!(version.fetch(ec_level, &EC_BYTES_PER_BLOCK));
let ec_codes = blocks.iter()
let ec_bytes = version.fetch(ec_level, &EC_BYTES_PER_BLOCK)?;
let ec_codes = blocks
.iter()
.map(|block| create_error_correction_code(*block, ec_bytes))
.collect::<Vec<Vec<u8>>>();
@ -132,24 +136,34 @@ pub fn construct_codewords(rawbits: &[u8],
#[cfg(test)]
mod construct_codewords_test {
use ec::construct_codewords;
use types::{Version, EcLevel};
use types::{EcLevel, Version};
#[test]
fn test_add_ec_simple() {
let msg = b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11";
let (blocks_vec, ec_vec) = construct_codewords(msg, Version::Normal(1), EcLevel::M).unwrap();
let (blocks_vec, ec_vec) =
construct_codewords(msg, Version::Normal(1), EcLevel::M).unwrap();
assert_eq!(&*blocks_vec, msg);
assert_eq!(&*ec_vec, b"\xc4#'w\xeb\xd7\xe7\xe2]\x17");
}
#[test]
fn test_add_ec_complex() {
let msg = b"CUF\x86W&U\xc2w2\x06\x12\x06g&\xf6\xf6B\x07v\x86\xf2\x07&V\x16\xc6\xc7\x92\x06\xb6\xe6\xf7w2\x07v\x86W&R\x06\x86\x972\x07F\xf7vV\xc2\x06\x972\x10\xec\x11\xec\x11\xec\x11\xec";
let (blocks_vec, ec_vec) = construct_codewords(msg, Version::Normal(5), EcLevel::Q).unwrap();
assert_eq!(&*blocks_vec,
&b"C\xf6\xb6FU\xf6\xe6\xf7FB\xf7v\x86\x07wVWv2\xc2&\x86\x07\x06U\xf2v\x97\xc2\x07\x862w&W\x102V&\xec\x06\x16R\x11\x12\xc6\x06\xec\x06\xc7\x86\x11g\x92\x97\xec&\x062\x11\x07\xec"[..]);
assert_eq!(&*ec_vec,
&b"\xd5W\x94\xeb\xc7\xcct\x9f\x0b`\xb1\x05-<\xd4\xads\xcaL\x18\xf7\xb6\x85\x93\xf1|K;\xdf\x9d\xf2!\xe5\xc8\xeej\xf8\x86L(\x9a\x1b\xc3\xffu\x81\xe6\xac\x9a\xd1\xbdRo\x11\n\x02V\xa3l\x83\xa1\xa3\xf0 ox\xc0\xb2'\x85\x8d\xec"[..]);
let msg = b"CUF\x86W&U\xc2w2\x06\x12\x06g&\xf6\xf6B\x07v\x86\xf2\x07&V\x16\xc6\xc7\x92\x06\
\xb6\xe6\xf7w2\x07v\x86W&R\x06\x86\x972\x07F\xf7vV\xc2\x06\x972\x10\xec\x11\xec\
\x11\xec\x11\xec";
let expected_blocks = b"C\xf6\xb6FU\xf6\xe6\xf7FB\xf7v\x86\x07wVWv2\xc2&\x86\x07\x06U\xf2v\
\x97\xc2\x07\x862w&W\x102V&\xec\x06\x16R\x11\x12\xc6\x06\xec\x06\
\xc7\x86\x11g\x92\x97\xec&\x062\x11\x07\xec";
let expected_ec = b"\xd5W\x94\xeb\xc7\xcct\x9f\x0b`\xb1\x05-<\xd4\xads\xcaL\x18\xf7\xb6\x85\
\x93\xf1|K;\xdf\x9d\xf2!\xe5\xc8\xeej\xf8\x86L(\x9a\x1b\xc3\xffu\x81\
\xe6\xac\x9a\xd1\xbdRo\x11\n\x02V\xa3l\x83\xa1\xa3\xf0 ox\xc0\xb2'\x85\
\x8d\xec";
let (blocks_vec, ec_vec) =
construct_codewords(msg, Version::Normal(5), EcLevel::Q).unwrap();
assert_eq!(&*blocks_vec, &expected_blocks[..]);
assert_eq!(&*ec_vec, &expected_ec[..]);
}
}
@ -170,8 +184,8 @@ pub fn max_allowed_errors(version: Version, ec_level: EcLevel) -> QrResult<usize
_ => 0,
};
let ec_bytes_per_block = try!(version.fetch(ec_level, &EC_BYTES_PER_BLOCK));
let (_, count1, _, count2) = try!(version.fetch(ec_level, &DATA_BYTES_PER_BLOCK));
let ec_bytes_per_block = version.fetch(ec_level, &EC_BYTES_PER_BLOCK)?;
let (_, count1, _, count2) = version.fetch(ec_level, &DATA_BYTES_PER_BLOCK)?;
let ec_bytes = (count1 + count2) * ec_bytes_per_block;
Ok((ec_bytes - p) / 2)
@ -180,7 +194,7 @@ pub fn max_allowed_errors(version: Version, ec_level: EcLevel) -> QrResult<usize
#[cfg(test)]
mod max_allowed_errors_test {
use ec::max_allowed_errors;
use types::{Version, EcLevel};
use types::{EcLevel, Version};
#[test]
fn test_low_versions() {
@ -231,10 +245,42 @@ mod max_allowed_errors_test {
//{{{ Precomputed tables for GF(256).
/// `EXP_TABLE` encodes the value of 2<sup>n</sup> in the Galois Field GF(256).
static EXP_TABLE: &'static [u8] = b"\x01\x02\x04\x08\x10\x20\x40\x80\x1d\x3a\x74\xe8\xcd\x87\x13\x26\x4c\x98\x2d\x5a\xb4\x75\xea\xc9\x8f\x03\x06\x0c\x18\x30\x60\xc0\x9d\x27\x4e\x9c\x25\x4a\x94\x35\x6a\xd4\xb5\x77\xee\xc1\x9f\x23\x46\x8c\x05\x0a\x14\x28\x50\xa0\x5d\xba\x69\xd2\xb9\x6f\xde\xa1\x5f\xbe\x61\xc2\x99\x2f\x5e\xbc\x65\xca\x89\x0f\x1e\x3c\x78\xf0\xfd\xe7\xd3\xbb\x6b\xd6\xb1\x7f\xfe\xe1\xdf\xa3\x5b\xb6\x71\xe2\xd9\xaf\x43\x86\x11\x22\x44\x88\x0d\x1a\x34\x68\xd0\xbd\x67\xce\x81\x1f\x3e\x7c\xf8\xed\xc7\x93\x3b\x76\xec\xc5\x97\x33\x66\xcc\x85\x17\x2e\x5c\xb8\x6d\xda\xa9\x4f\x9e\x21\x42\x84\x15\x2a\x54\xa8\x4d\x9a\x29\x52\xa4\x55\xaa\x49\x92\x39\x72\xe4\xd5\xb7\x73\xe6\xd1\xbf\x63\xc6\x91\x3f\x7e\xfc\xe5\xd7\xb3\x7b\xf6\xf1\xff\xe3\xdb\xab\x4b\x96\x31\x62\xc4\x95\x37\x6e\xdc\xa5\x57\xae\x41\x82\x19\x32\x64\xc8\x8d\x07\x0e\x1c\x38\x70\xe0\xdd\xa7\x53\xa6\x51\xa2\x59\xb2\x79\xf2\xf9\xef\xc3\x9b\x2b\x56\xac\x45\x8a\x09\x12\x24\x48\x90\x3d\x7a\xf4\xf5\xf7\xf3\xfb\xeb\xcb\x8b\x0b\x16\x2c\x58\xb0\x7d\xfa\xe9\xcf\x83\x1b\x36\x6c\xd8\xad\x47\x8e\x01";
static EXP_TABLE: &'static [u8] = b"\
\x01\x02\x04\x08\x10\x20\x40\x80\x1d\x3a\x74\xe8\xcd\x87\x13\x26\
\x4c\x98\x2d\x5a\xb4\x75\xea\xc9\x8f\x03\x06\x0c\x18\x30\x60\xc0\
\x9d\x27\x4e\x9c\x25\x4a\x94\x35\x6a\xd4\xb5\x77\xee\xc1\x9f\x23\
\x46\x8c\x05\x0a\x14\x28\x50\xa0\x5d\xba\x69\xd2\xb9\x6f\xde\xa1\
\x5f\xbe\x61\xc2\x99\x2f\x5e\xbc\x65\xca\x89\x0f\x1e\x3c\x78\xf0\
\xfd\xe7\xd3\xbb\x6b\xd6\xb1\x7f\xfe\xe1\xdf\xa3\x5b\xb6\x71\xe2\
\xd9\xaf\x43\x86\x11\x22\x44\x88\x0d\x1a\x34\x68\xd0\xbd\x67\xce\
\x81\x1f\x3e\x7c\xf8\xed\xc7\x93\x3b\x76\xec\xc5\x97\x33\x66\xcc\
\x85\x17\x2e\x5c\xb8\x6d\xda\xa9\x4f\x9e\x21\x42\x84\x15\x2a\x54\
\xa8\x4d\x9a\x29\x52\xa4\x55\xaa\x49\x92\x39\x72\xe4\xd5\xb7\x73\
\xe6\xd1\xbf\x63\xc6\x91\x3f\x7e\xfc\xe5\xd7\xb3\x7b\xf6\xf1\xff\
\xe3\xdb\xab\x4b\x96\x31\x62\xc4\x95\x37\x6e\xdc\xa5\x57\xae\x41\
\x82\x19\x32\x64\xc8\x8d\x07\x0e\x1c\x38\x70\xe0\xdd\xa7\x53\xa6\
\x51\xa2\x59\xb2\x79\xf2\xf9\xef\xc3\x9b\x2b\x56\xac\x45\x8a\x09\
\x12\x24\x48\x90\x3d\x7a\xf4\xf5\xf7\xf3\xfb\xeb\xcb\x8b\x0b\x16\
\x2c\x58\xb0\x7d\xfa\xe9\xcf\x83\x1b\x36\x6c\xd8\xad\x47\x8e\x01";
/// `LOG_TABLE` is the inverse function of `EXP_TABLE`.
static LOG_TABLE: &'static [u8] = b"\xff\x00\x01\x19\x02\x32\x1a\xc6\x03\xdf\x33\xee\x1b\x68\xc7\x4b\x04\x64\xe0\x0e\x34\x8d\xef\x81\x1c\xc1\x69\xf8\xc8\x08\x4c\x71\x05\x8a\x65\x2f\xe1\x24\x0f\x21\x35\x93\x8e\xda\xf0\x12\x82\x45\x1d\xb5\xc2\x7d\x6a\x27\xf9\xb9\xc9\x9a\x09\x78\x4d\xe4\x72\xa6\x06\xbf\x8b\x62\x66\xdd\x30\xfd\xe2\x98\x25\xb3\x10\x91\x22\x88\x36\xd0\x94\xce\x8f\x96\xdb\xbd\xf1\xd2\x13\x5c\x83\x38\x46\x40\x1e\x42\xb6\xa3\xc3\x48\x7e\x6e\x6b\x3a\x28\x54\xfa\x85\xba\x3d\xca\x5e\x9b\x9f\x0a\x15\x79\x2b\x4e\xd4\xe5\xac\x73\xf3\xa7\x57\x07\x70\xc0\xf7\x8c\x80\x63\x0d\x67\x4a\xde\xed\x31\xc5\xfe\x18\xe3\xa5\x99\x77\x26\xb8\xb4\x7c\x11\x44\x92\xd9\x23\x20\x89\x2e\x37\x3f\xd1\x5b\x95\xbc\xcf\xcd\x90\x87\x97\xb2\xdc\xfc\xbe\x61\xf2\x56\xd3\xab\x14\x2a\x5d\x9e\x84\x3c\x39\x53\x47\x6d\x41\xa2\x1f\x2d\x43\xd8\xb7\x7b\xa4\x76\xc4\x17\x49\xec\x7f\x0c\x6f\xf6\x6c\xa1\x3b\x52\x29\x9d\x55\xaa\xfb\x60\x86\xb1\xbb\xcc\x3e\x5a\xcb\x59\x5f\xb0\x9c\xa9\xa0\x51\x0b\xf5\x16\xeb\x7a\x75\x2c\xd7\x4f\xae\xd5\xe9\xe6\xe7\xad\xe8\x74\xd6\xf4\xea\xa8\x50\x58\xaf";
static LOG_TABLE: &'static [u8] = b"\
\xff\x00\x01\x19\x02\x32\x1a\xc6\x03\xdf\x33\xee\x1b\x68\xc7\x4b\
\x04\x64\xe0\x0e\x34\x8d\xef\x81\x1c\xc1\x69\xf8\xc8\x08\x4c\x71\
\x05\x8a\x65\x2f\xe1\x24\x0f\x21\x35\x93\x8e\xda\xf0\x12\x82\x45\
\x1d\xb5\xc2\x7d\x6a\x27\xf9\xb9\xc9\x9a\x09\x78\x4d\xe4\x72\xa6\
\x06\xbf\x8b\x62\x66\xdd\x30\xfd\xe2\x98\x25\xb3\x10\x91\x22\x88\
\x36\xd0\x94\xce\x8f\x96\xdb\xbd\xf1\xd2\x13\x5c\x83\x38\x46\x40\
\x1e\x42\xb6\xa3\xc3\x48\x7e\x6e\x6b\x3a\x28\x54\xfa\x85\xba\x3d\
\xca\x5e\x9b\x9f\x0a\x15\x79\x2b\x4e\xd4\xe5\xac\x73\xf3\xa7\x57\
\x07\x70\xc0\xf7\x8c\x80\x63\x0d\x67\x4a\xde\xed\x31\xc5\xfe\x18\
\xe3\xa5\x99\x77\x26\xb8\xb4\x7c\x11\x44\x92\xd9\x23\x20\x89\x2e\
\x37\x3f\xd1\x5b\x95\xbc\xcf\xcd\x90\x87\x97\xb2\xdc\xfc\xbe\x61\
\xf2\x56\xd3\xab\x14\x2a\x5d\x9e\x84\x3c\x39\x53\x47\x6d\x41\xa2\
\x1f\x2d\x43\xd8\xb7\x7b\xa4\x76\xc4\x17\x49\xec\x7f\x0c\x6f\xf6\
\x6c\xa1\x3b\x52\x29\x9d\x55\xaa\xfb\x60\x86\xb1\xbb\xcc\x3e\x5a\
\xcb\x59\x5f\xb0\x9c\xa9\xa0\x51\x0b\xf5\x16\xeb\x7a\x75\x2c\xd7\
\x4f\xae\xd5\xe9\xe6\xe7\xad\xe8\x74\xd6\xf4\xea\xa8\x50\x58\xaf";
/// The generator polynomial list.
///
@ -245,6 +291,8 @@ static LOG_TABLE: &'static [u8] = b"\xff\x00\x01\x19\x02\x32\x1a\xc6\x03\xdf\x33
/// is the Reed-Solomon error correction code.
///
/// A partial list can be found from ISO/IEC 18004:2006 Annex A.
#[cfg_attr(rustfmt, rustfmt_skip)]
// ^ this attribute is currently useless, see rust-lang-nursery/rustfmt#1080 and 1298
static GENERATOR_POLYNOMIALS: [&'static [u8]; 70] = [
b"",
b"\x00",
@ -339,7 +387,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
[24, 22, 22, 26], // 8
[30, 22, 20, 24], // 9
[18, 26, 24, 28], // 10
[20, 30, 28, 24], // 11
[24, 22, 26, 28], // 12
[26, 22, 24, 22], // 13
@ -350,7 +397,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
[30, 26, 28, 28], // 18
[28, 26, 26, 26], // 19
[28, 26, 30, 28], // 20
[28, 26, 28, 30], // 21
[28, 28, 30, 24], // 22
[30, 28, 30, 30], // 23
@ -361,7 +407,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
[30, 28, 30, 30], // 28
[30, 28, 30, 30], // 29
[30, 28, 30, 30], // 30
[30, 28, 30, 30], // 31
[30, 28, 30, 30], // 32
[30, 28, 30, 30], // 33
@ -372,7 +417,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
[30, 28, 30, 30], // 38
[30, 28, 30, 30], // 39
[30, 28, 30, 30], // 40
// Micro versions.
[2, 0, 0, 0], // M1
[5, 6, 0, 0], // M2
@ -401,7 +445,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
[(97, 2, 0, 0), (38, 2, 39, 2), (18, 4, 19, 2), (14, 4, 15, 2)], // 8
[(116, 2, 0, 0), (36, 3, 37, 2), (16, 4, 17, 4), (12, 4, 13, 4)], // 9
[(68, 2, 69, 2), (43, 4, 44, 1), (19, 6, 20, 2), (15, 6, 16, 2)], // 10
[(81, 4, 0, 0), (50, 1, 51, 4), (22, 4, 23, 4), (12, 3, 13, 8)], // 11
[(92, 2, 93, 2), (36, 6, 37, 2), (20, 4, 21, 6), (14, 7, 15, 4)], // 12
[(107, 4, 0, 0), (37, 8, 38, 1), (20, 8, 21, 4), (11, 12, 12, 4)], // 13
@ -412,7 +455,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
[(120, 5, 121, 1), (43, 9, 44, 4), (22, 17, 23, 1), (14, 2, 15, 19)], // 18
[(113, 3, 114, 4), (44, 3, 45, 11), (21, 17, 22, 4), (13, 9, 14, 16)], // 19
[(107, 3, 108, 5), (41, 3, 42, 13), (24, 15, 25, 5), (15, 15, 16, 10)], // 20
[(116, 4, 117, 4), (42, 17, 0, 0), (22, 17, 23, 6), (16, 19, 17, 6)], // 21
[(111, 2, 112, 7), (46, 17, 0, 0), (24, 7, 25, 16), (13, 34, 0, 0)], // 22
[(121, 4, 122, 5), (47, 4, 48, 14), (24, 11, 25, 14), (15, 16, 16, 14)], // 23
@ -423,7 +465,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
[(117, 3, 118, 10), (45, 3, 46, 23), (24, 4, 25, 31), (15, 11, 16, 31)], // 28
[(116, 7, 117, 7), (45, 21, 46, 7), (23, 1, 24, 37), (15, 19, 16, 26)], // 29
[(115, 5, 116, 10), (47, 19, 48, 10), (24, 15, 25, 25), (15, 23, 16, 25)], // 30
[(115, 13, 116, 3), (46, 2, 47, 29), (24, 42, 25, 1), (15, 23, 16, 28)], // 31
[(115, 17, 0, 0), (46, 10, 47, 23), (24, 10, 25, 35), (15, 19, 16, 35)], // 32
[(115, 17, 116, 1), (46, 14, 47, 21), (24, 29, 25, 19), (15, 11, 16, 46)], // 33
@ -434,7 +475,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
[(122, 4, 123, 18), (46, 13, 47, 32), (24, 48, 25, 14), (15, 42, 16, 32)], // 38
[(117, 20, 118, 4), (47, 40, 48, 7), (24, 43, 25, 22), (15, 10, 16, 67)], // 39
[(118, 19, 119, 6), (47, 18, 48, 31), (24, 34, 25, 34), (15, 20, 16, 61)], // 40
// Micro versions.
[(3, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M1
[(5, 1, 0, 0), (4, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M2
@ -443,5 +483,3 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
];
//}}}

View file

@ -30,9 +30,16 @@
//! ```
#![cfg_attr(feature = "bench", feature(test))] // Unstable libraries
#![cfg_attr(feature = "cargo-clippy", deny(warnings, clippy_pedantic))]
#![cfg_attr(feature = "cargo-clippy",
allow(unreadable_literal, missing_docs_in_private_items, shadow_reuse,
range_plus_one))]
#[cfg(feature="bench")] extern crate test;
#[cfg(feature="image")] extern crate image;
extern crate checked_int_cast;
#[cfg(feature = "image")]
extern crate image;
#[cfg(feature = "bench")]
extern crate test;
use std::ops::Index;
@ -42,10 +49,13 @@ pub mod optimize;
pub mod ec;
pub mod canvas;
pub mod render;
mod cast;
pub use types::{QrResult, Color, EcLevel, Version};
pub use types::{Color, EcLevel, QrResult, Version};
use render::{Pixel, Renderer};
use checked_int_cast::CheckedIntCast;
use cast::As;
/// The encoded QR code symbol.
#[derive(Clone)]
@ -66,8 +76,8 @@ impl QrCode {
///
/// let code = QrCode::new(b"Some data").unwrap();
///
pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<QrCode> {
QrCode::with_error_correction_level(data, EcLevel::M)
pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
Self::with_error_correction_level(data, EcLevel::M)
}
/// Constructs a new QR code which automatically encodes the given data at a
@ -79,9 +89,12 @@ impl QrCode {
///
/// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
///
pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<QrCode> {
let bits = try!(bits::encode_auto(data.as_ref(), ec_level));
QrCode::with_bits(bits, ec_level)
pub fn with_error_correction_level<D: AsRef<[u8]>>(
data: D,
ec_level: EcLevel,
) -> QrResult<Self> {
let bits = bits::encode_auto(data.as_ref(), ec_level)?;
Self::with_bits(bits, ec_level)
}
/// Constructs a new QR code for the given version and error correction
@ -97,11 +110,15 @@ impl QrCode {
///
/// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
///
pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<QrCode> {
pub fn with_version<D: AsRef<[u8]>>(
data: D,
version: Version,
ec_level: EcLevel,
) -> QrResult<Self> {
let mut bits = bits::Bits::new(version);
try!(bits.push_optimal_data(data.as_ref()));
try!(bits.push_terminator(ec_level));
QrCode::with_bits(bits, ec_level)
bits.push_optimal_data(data.as_ref())?;
bits.push_terminator(ec_level)?;
Self::with_bits(bits, ec_level)
}
/// Constructs a new QR code with encoded bits.
@ -126,19 +143,19 @@ impl QrCode {
/// bits.push_terminator(EcLevel::L);
/// let qrcode = QrCode::with_bits(bits, EcLevel::L);
///
pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<QrCode> {
pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
let version = bits.version();
let data = bits.into_bytes();
let (encoded_data, ec_data) = try!(ec::construct_codewords(&*data, version, ec_level));
let (encoded_data, ec_data) = ec::construct_codewords(&*data, version, ec_level)?;
let mut canvas = canvas::Canvas::new(version, ec_level);
canvas.draw_all_functional_patterns();
canvas.draw_data(&*encoded_data, &*ec_data);
let canvas = canvas.apply_best_mask();
Ok(QrCode {
Ok(Self {
content: canvas.into_colors(),
version: version,
ec_level: ec_level,
width: version.width() as usize,
width: version.width().as_usize(),
})
}
@ -163,13 +180,17 @@ impl QrCode {
/// before the data becomes corrupted. Note that errors should not be
/// introduced to functional modules.
pub fn max_allowed_errors(&self) -> usize {
ec::max_allowed_errors(self.version, self.ec_level).unwrap()
ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
}
/// Checks whether a module at coordinate (x, y) is a functional module or
/// not.
pub fn is_functional(&self, x: usize, y: usize) -> bool {
canvas::is_functional(self.version, self.version.width(), x as i16, y as i16)
let x = x.as_i16_checked()
.expect("coordinate is too large for QR code");
let y = y.as_i16_checked()
.expect("coordinate is too large for QR code");
canvas::is_functional(self.version, self.version.width(), x, y)
}
/// Converts the QR code into a human-readable string. This is mainly for
@ -193,7 +214,10 @@ impl QrCode {
/// color of the module, with "true" means dark and "false" means light.
#[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
pub fn into_vec(self) -> Vec<bool> {
self.content.into_iter().map(|c| c != Color::Light).collect()
self.content
.into_iter()
.map(|c| c != Color::Light)
.collect()
}
/// Converts the QR code to a vector of colors.
@ -250,13 +274,15 @@ impl Index<(usize, usize)> for QrCode {
#[cfg(test)]
mod tests {
use {QrCode, Version, EcLevel};
use {EcLevel, QrCode, Version};
#[test]
fn test_annex_i_qr() {
// This uses the ISO Annex I as test vector.
let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
assert_eq!(&*code.to_debug_str('#', '.'), "\
assert_eq!(
&*code.to_debug_str('#', '.'),
"\
#######..#.##.#######\n\
#.....#..####.#.....#\n\
#.###.#.#.....#.###.#\n\
@ -277,13 +303,16 @@ mod tests {
#.###.#.##..#..#.....\n\
#.###.#.#.##.#..#.#..\n\
#.....#........##.##.\n\
#######.####.#..#.#..");
#######.####.#..#.#.."
);
}
#[test]
fn test_annex_i_micro_qr() {
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
assert_eq!(&*code.to_debug_str('#', '.'), "\
assert_eq!(
&*code.to_debug_str('#', '.'),
"\
#######.#.#.#\n\
#.....#.###.#\n\
#.###.#..##.#\n\
@ -296,20 +325,23 @@ mod tests {
.##.#.#.#.#.#\n\
###..#######.\n\
...#.#....##.\n\
###.#..##.###");
###.#..##.###"
);
}
}
#[cfg(all(test, feature = "image"))]
mod image_tests {
use image::{Luma, Rgb, load_from_memory};
use {QrCode, Version, EcLevel};
use image::{load_from_memory, Luma, Rgb};
use {EcLevel, QrCode, Version};
#[test]
fn test_annex_i_qr_as_image() {
let code = QrCode::new(b"01234567").unwrap();
let image = code.render::<Luma<u8>>().build();
let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png")).unwrap().to_luma();
let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png"))
.unwrap()
.to_luma();
assert_eq!(image.dimensions(), expected.dimensions());
assert_eq!(image.into_raw(), expected.into_raw());
}
@ -322,7 +354,9 @@ mod image_tests {
.dark_color(Rgb { data: [128, 0, 0] })
.light_color(Rgb { data: [255, 255, 128] })
.build();
let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb();
let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png"))
.unwrap()
.to_rgb();
assert_eq!(image.dimensions(), expected.dimensions());
assert_eq!(image.into_raw(), expected.into_raw());
}
@ -331,7 +365,7 @@ mod image_tests {
#[cfg(all(test, feature = "svg"))]
mod svg_tests {
use render::svg::Color as SvgColor;
use {QrCode, Version, EcLevel};
use {EcLevel, QrCode, Version};
#[test]
fn test_annex_i_qr_as_svg() {
@ -353,4 +387,3 @@ mod svg_tests {
assert_eq!(&image, expected);
}
}

View file

@ -26,7 +26,11 @@ impl Segment {
/// length bits) when this segment is encoded.
pub fn encoded_len(&self, version: Version) -> usize {
let byte_size = self.end - self.begin;
let chars_count = if self.mode == Mode::Kanji { byte_size / 2 } else { byte_size };
let chars_count = if self.mode == Mode::Kanji {
byte_size / 2
} else {
byte_size
};
let mode_bits_count = version.mode_bits_count();
let length_bits_count = self.mode.length_bits_count(version);
@ -115,24 +119,20 @@ impl<'a> Iterator for Parser<'a> {
if self.pending_single_byte {
self.pending_single_byte = false;
self.begin += 1;
return Some(Segment {
mode: Mode::Byte,
begin: self.begin-1,
end: self.begin,
});
return Some(Segment { mode: Mode::Byte, begin: self.begin - 1, end: self.begin });
}
loop {
let (i, ecs) = match self.ecs_iter.next() {
None => { return None; },
Some(a) => a
None => return None,
Some(a) => a,
};
let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize];
self.state = next_state;
let old_begin = self.begin;
let push_mode = match action {
Action::Idle => { continue; }
Action::Idle => continue,
Action::Numeric => Mode::Numeric,
Action::Alpha => Mode::Alphanumeric,
Action::Byte => Mode::Byte,
@ -144,21 +144,15 @@ impl<'a> Iterator for Parser<'a> {
} else {
self.pending_single_byte = true;
self.begin = next_begin;
return Some(Segment {
mode: Mode::Kanji,
begin: old_begin,
end: next_begin,
});
return Some(
Segment { mode: Mode::Kanji, begin: old_begin, end: next_begin },
);
}
}
};
self.begin = i;
return Some(Segment {
mode: push_mode,
begin: old_begin,
end: i,
});
return Some(Segment { mode: push_mode, begin: old_begin, end: i });
}
}
}
@ -175,39 +169,59 @@ mod parse_tests {
#[test]
fn test_parse_1() {
let segs = parse(b"01049123451234591597033130128%10ABC123");
assert_eq!(segs, vec![Segment { mode: Mode::Numeric, begin: 0, end: 29 },
assert_eq!(
segs,
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 29 },
Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
Segment { mode: Mode::Numeric, begin: 30, end: 32 },
Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
Segment { mode: Mode::Numeric, begin: 35, end: 38 }]);
Segment { mode: Mode::Numeric, begin: 35, end: 38 },
]
);
}
#[test]
fn test_parse_shift_jis_example_1() {
let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ"
assert_eq!(segs, vec![Segment { mode: Mode::Kanji, begin: 0, end: 4 },
assert_eq!(
segs,
vec![
Segment { mode: Mode::Kanji, begin: 0, end: 4 },
Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
Segment { mode: Mode::Byte, begin: 5, end: 6 },
Segment { mode: Mode::Kanji, begin: 6, end: 8 }]);
Segment { mode: Mode::Kanji, begin: 6, end: 8 },
]
);
}
#[test]
fn test_parse_utf_8() {
// Mojibake?
let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
assert_eq!(segs, vec![Segment { mode: Mode::Kanji, begin: 0, end: 4 },
assert_eq!(
segs,
vec![
Segment { mode: Mode::Kanji, begin: 0, end: 4 },
Segment { mode: Mode::Byte, begin: 4, end: 5 },
Segment { mode: Mode::Kanji, begin: 5, end: 7 },
Segment { mode: Mode::Byte, begin: 7, end: 10 },
Segment { mode: Mode::Kanji, begin: 10, end: 12 },
Segment { mode: Mode::Byte, begin: 12, end: 13 }]);
Segment { mode: Mode::Byte, begin: 12, end: 13 },
]
);
}
#[test]
fn test_not_kanji_1() {
let segs = parse(b"\x81\x30");
assert_eq!(segs, vec![Segment { mode: Mode::Byte, begin: 0, end: 1 },
Segment { mode: Mode::Numeric, begin: 1, end: 2 }]);
assert_eq!(
segs,
vec![
Segment { mode: Mode::Byte, begin: 0, end: 1 },
Segment { mode: Mode::Numeric, begin: 1, end: 2 },
]
);
}
#[test]
@ -215,22 +229,37 @@ mod parse_tests {
// Note that it's implementation detail that the byte seq is split into
// two. Perhaps adjust the test to check for this.
let segs = parse(b"\xeb\xc0");
assert_eq!(segs, vec![Segment { mode: Mode::Byte, begin: 0, end: 1 },
Segment { mode: Mode::Byte, begin: 1, end: 2 }]);
assert_eq!(
segs,
vec![
Segment { mode: Mode::Byte, begin: 0, end: 1 },
Segment { mode: Mode::Byte, begin: 1, end: 2 },
]
);
}
#[test]
fn test_not_kanji_3() {
let segs = parse(b"\x81\x7f");
assert_eq!(segs, vec![Segment { mode: Mode::Byte, begin: 0, end: 1 },
Segment { mode: Mode::Byte, begin: 1, end: 2 }]);
assert_eq!(
segs,
vec![
Segment { mode: Mode::Byte, begin: 0, end: 1 },
Segment { mode: Mode::Byte, begin: 1, end: 2 },
]
);
}
#[test]
fn test_not_kanji_4() {
let segs = parse(b"\x81\x40\x81");
assert_eq!(segs, vec![Segment { mode: Mode::Kanji, begin: 0, end: 2 },
Segment { mode: Mode::Byte, begin: 2, end: 3 }]);
assert_eq!(
segs,
vec![
Segment { mode: Mode::Kanji, begin: 0, end: 2 },
Segment { mode: Mode::Byte, begin: 2, end: 3 },
]
);
}
}
@ -238,6 +267,7 @@ mod parse_tests {
//------------------------------------------------------------------------------
//{{{ Optimizer
#[cfg_attr(feature = "cargo-clippy", allow(stutter))] // rust-lang-nursery/rust-clippy#2212 ಠ_ಠ
pub struct Optimizer<I> {
parser: I,
last_segment: Segment,
@ -253,16 +283,16 @@ impl<I: Iterator<Item=Segment>> Optimizer<I> {
/// left to right until the new segment is longer than before. This method
/// does *not* use Annex J from the ISO standard.
///
pub fn new(mut segments: I, version: Version) -> Optimizer<I> {
pub fn new(mut segments: I, version: Version) -> Self {
match segments.next() {
None => Optimizer {
None => Self {
parser: segments,
last_segment: Segment { mode: Mode::Numeric, begin: 0, end: 0 },
last_segment_size: 0,
version: version,
ended: true,
},
Some(segment) => Optimizer {
Some(segment) => Self {
parser: segments,
last_segment: segment,
last_segment_size: segment.encoded_len(version),
@ -321,12 +351,14 @@ impl<I: Iterator<Item=Segment>> Iterator for Optimizer<I> {
/// Computes the total encoded length of all segments.
pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize {
// TODO revert to `.map().sum()` after `sum()` is stable.
segments.iter().fold(0, |acc, seg| acc + seg.encoded_len(version))
segments
.iter()
.fold(0, |acc, seg| acc + seg.encoded_len(version))
}
#[cfg(test)]
mod optimize_tests {
use optimize::{Optimizer, total_encoded_len, Segment};
use optimize::{total_encoded_len, Optimizer, Segment};
use types::{Mode, Version};
fn test_optimization_result(given: Vec<Segment>, expected: Vec<Segment>, version: Version) {
@ -336,116 +368,114 @@ mod optimize_tests {
if given != opt_segs {
assert!(prev_len > new_len, "{} > {}", prev_len, new_len);
}
assert!(opt_segs == expected,
assert!(
opt_segs == expected,
"Optimization gave something better: {} < {} ({:?})",
new_len, total_encoded_len(&*expected, version), opt_segs);
new_len,
total_encoded_len(&*expected, version),
opt_segs
);
}
#[test]
fn test_example_1() {
test_optimization_result(
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
vec![
Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
Segment { mode: Mode::Numeric, begin: 3, end: 6 },
Segment { mode: Mode::Byte, begin: 6, end: 10 }],
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 },
Segment { mode: Mode::Byte, begin: 6, end: 10 }],
Version::Normal(1)
Segment { mode: Mode::Byte, begin: 6, end: 10 },
],
vec![
Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 },
Segment { mode: Mode::Byte, begin: 6, end: 10 },
],
Version::Normal(1),
);
}
#[test]
fn test_example_2() {
test_optimization_result(
vec![Segment { mode: Mode::Numeric, begin: 0, end: 29 },
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 29 },
Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
Segment { mode: Mode::Numeric, begin: 30, end: 32 },
Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
Segment { mode: Mode::Numeric, begin: 35, end: 38 }],
vec![Segment { mode: Mode::Numeric, begin: 0, end: 29 },
Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 }],
Version::Normal(9)
Segment { mode: Mode::Numeric, begin: 35, end: 38 },
],
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 29 },
Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 },
],
Version::Normal(9),
);
}
#[test]
fn test_example_3() {
test_optimization_result(
vec![Segment { mode: Mode::Kanji, begin: 0, end: 4 },
vec![
Segment { mode: Mode::Kanji, begin: 0, end: 4 },
Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
Segment { mode: Mode::Byte, begin: 5, end: 6 },
Segment { mode: Mode::Kanji, begin: 6, end: 8 }],
Segment { mode: Mode::Kanji, begin: 6, end: 8 },
],
vec![Segment { mode: Mode::Byte, begin: 0, end: 8 }],
Version::Normal(1)
Version::Normal(1),
);
}
#[test]
fn test_example_4() {
test_optimization_result(
vec![Segment { mode: Mode::Kanji, begin: 0, end: 10 },
Segment { mode: Mode::Byte, begin: 10, end: 11 }],
vec![Segment { mode: Mode::Kanji, begin: 0, end: 10 },
Segment { mode: Mode::Byte, begin: 10, end: 11 }],
Version::Normal(1)
vec![
Segment { mode: Mode::Kanji, begin: 0, end: 10 },
Segment { mode: Mode::Byte, begin: 10, end: 11 },
],
vec![
Segment { mode: Mode::Kanji, begin: 0, end: 10 },
Segment { mode: Mode::Byte, begin: 10, end: 11 },
],
Version::Normal(1),
);
}
#[test]
fn test_annex_j_guideline_1a() {
test_optimization_result(
vec![Segment { mode: Mode::Numeric, begin: 0, end: 3 },
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }],
vec![Segment { mode: Mode::Numeric, begin: 0, end: 3 },
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }],
Version::Micro(2)
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 3 },
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
],
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 3 },
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
],
Version::Micro(2),
);
}
#[test]
fn test_annex_j_guideline_1b() {
test_optimization_result(
vec![Segment { mode: Mode::Numeric, begin: 0, end: 2 },
Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 }],
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 2 },
Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 },
],
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
Version::Micro(2)
Version::Micro(2),
);
}
#[test]
fn test_annex_j_guideline_1c() {
test_optimization_result(
vec![Segment { mode: Mode::Numeric, begin: 0, end: 3 },
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }],
vec![
Segment { mode: Mode::Numeric, begin: 0, end: 3 },
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
],
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
Version::Micro(3)
Version::Micro(3),
);
}
@ -456,10 +486,25 @@ mod optimize_tests {
fn bench_optimize(bencher: &mut Bencher) {
use types::Version;
let data = b"QR\x83R\x81[\x83h\x81i\x83L\x83\x85\x81[\x83A\x81[\x83\x8b\x83R\x81[\x83h\x81j\x82\xc6\x82\xcd\x81A1994\x94N\x82\xc9\x83f\x83\x93\x83\\\x81[\x82\xcc\x8aJ\x94\xad\x95\x94\x96\xe5\x81i\x8c\xbb\x8d\xdd\x82\xcd\x95\xaa\x97\xa3\x82\xb5\x83f\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x81j\x82\xaa\x8aJ\x94\xad\x82\xb5\x82\xbd\x83}\x83g\x83\x8a\x83b\x83N\x83X\x8c^\x93\xf1\x8e\x9f\x8c\xb3\x83R\x81[\x83h\x82\xc5\x82\xa0\x82\xe9\x81B\x82\xc8\x82\xa8\x81AQR\x83R\x81[\x83h\x82\xc6\x82\xa2\x82\xa4\x96\xbc\x8f\xcc\x81i\x82\xa8\x82\xe6\x82\xd1\x92P\x8c\xea\x81j\x82\xcd\x83f\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x82\xcc\x93o\x98^\x8f\xa4\x95W\x81i\x91\xe64075066\x8d\x86\x81j\x82\xc5\x82\xa0\x82\xe9\x81BQR\x82\xcdQuick Response\x82\xc9\x97R\x97\x88\x82\xb5\x81A\x8d\x82\x91\xac\x93\xc7\x82\xdd\x8e\xe6\x82\xe8\x82\xaa\x82\xc5\x82\xab\x82\xe9\x82\xe6\x82\xa4\x82\xc9\x8aJ\x94\xad\x82\xb3\x82\xea\x82\xbd\x81B\x93\x96\x8f\x89\x82\xcd\x8e\xa9\x93\xae\x8e\xd4\x95\x94\x95i\x8dH\x8f\xea\x82\xe2\x94z\x91\x97\x83Z\x83\x93\x83^\x81[\x82\xc8\x82\xc7\x82\xc5\x82\xcc\x8eg\x97p\x82\xf0\x94O\x93\xaa\x82\xc9\x8aJ\x94\xad\x82\xb3\x82\xea\x82\xbd\x82\xaa\x81A\x8c\xbb\x8d\xdd\x82\xc5\x82\xcd\x83X\x83}\x81[\x83g\x83t\x83H\x83\x93\x82\xcc\x95\x81\x8by\x82\xc8\x82\xc7\x82\xc9\x82\xe6\x82\xe8\x93\xfa\x96{\x82\xc9\x8c\xc0\x82\xe7\x82\xb8\x90\xa2\x8aE\x93I\x82\xc9\x95\x81\x8by\x82\xb5\x82\xc4\x82\xa2\x82\xe9\x81B";
bencher.iter(|| {
Parser::new(data).optimize(Version::Normal(15))
});
let data = b"QR\x83R\x81[\x83h\x81i\x83L\x83\x85\x81[\x83A\x81[\x83\x8b\x83R\x81[\x83h\x81j\
\x82\xc6\x82\xcd\x81A1994\x94N\x82\xc9\x83f\x83\x93\x83\\\x81[\x82\xcc\x8aJ\
\x94\xad\x95\x94\x96\xe5\x81i\x8c\xbb\x8d\xdd\x82\xcd\x95\xaa\x97\xa3\x82\xb5\x83f\
\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x81j\x82\xaa\x8aJ\x94\xad\x82\xb5\x82\xbd\
\x83}\x83g\x83\x8a\x83b\x83N\x83X\x8c^\x93\xf1\x8e\x9f\x8c\xb3\x83R\x81[\x83h\
\x82\xc5\x82\xa0\x82\xe9\x81B\x82\xc8\x82\xa8\x81AQR\x83R\x81[\x83h\x82\xc6\
\x82\xa2\x82\xa4\x96\xbc\x8f\xcc\x81i\x82\xa8\x82\xe6\x82\xd1\x92P\x8c\xea\x81j\
\x82\xcd\x83f\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x82\xcc\x93o\x98^\x8f\xa4\
\x95W\x81i\x91\xe64075066\x8d\x86\x81j\x82\xc5\x82\xa0\x82\xe9\x81BQR\x82\xcd\
Quick Response\x82\xc9\x97R\x97\x88\x82\xb5\x81A\x8d\x82\x91\xac\x93\xc7\x82\xdd\
\x8e\xe6\x82\xe8\x82\xaa\x82\xc5\x82\xab\x82\xe9\x82\xe6\x82\xa4\x82\xc9\x8aJ\
\x94\xad\x82\xb3\x82\xea\x82\xbd\x81B\x93\x96\x8f\x89\x82\xcd\x8e\xa9\x93\xae\
\x8e\xd4\x95\x94\x95i\x8dH\x8f\xea\x82\xe2\x94z\x91\x97\x83Z\x83\x93\x83^\x81[\
\x82\xc8\x82\xc7\x82\xc5\x82\xcc\x8eg\x97p\x82\xf0\x94O\x93\xaa\x82\xc9\x8aJ\
\x94\xad\x82\xb3\x82\xea\x82\xbd\x82\xaa\x81A\x8c\xbb\x8d\xdd\x82\xc5\x82\xcd\x83X\
\x83}\x81[\x83g\x83t\x83H\x83\x93\x82\xcc\x95\x81\x8by\x82\xc8\x82\xc7\x82\xc9\
\x82\xe6\x82\xe8\x93\xfa\x96{\x82\xc9\x8c\xc0\x82\xe7\x82\xb8\x90\xa2\x8aE\x93I\
\x82\xc9\x95\x81\x8by\x82\xb5\x82\xc4\x82\xa2\x82\xe9\x81B";
bencher.iter(|| Parser::new(data).optimize(Version::Normal(15)));
}
@ -514,7 +559,7 @@ enum ExclCharSet {
impl ExclCharSet {
/// Determines which character set a byte is in.
fn from_u8(c: u8) -> ExclCharSet {
fn from_u8(c: u8) -> Self {
match c {
0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d...0x2f | 0x3a => ExclCharSet::Symbol,
0x30...0x39 => ExclCharSet::Numeric,
@ -524,7 +569,7 @@ impl ExclCharSet {
0xeb => ExclCharSet::KanjiHi3,
0x40 | 0x5b...0x7e | 0x80 | 0xa0...0xbf => ExclCharSet::KanjiLo1,
0xc0...0xdf | 0xec...0xfc => ExclCharSet::KanjiLo2,
_ => ExclCharSet::Byte
_ => ExclCharSet::Byte,
}
}
}
@ -583,7 +628,6 @@ static STATE_TRANSITION: [(State, Action); 70] = [
// STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do)
// Init state:
(State::Init, Action::Idle), // End
(State::Alpha, Action::Idle), // Symbol
(State::Numeric, Action::Idle), // Numeric
@ -594,9 +638,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
(State::Byte, Action::Idle), // KanjiLo1
(State::Byte, Action::Idle), // KanjiLo2
(State::Byte, Action::Idle), // Byte
// Numeric state:
(State::Init, Action::Numeric), // End
(State::Alpha, Action::Numeric), // Symbol
(State::Numeric, Action::Idle), // Numeric
@ -607,9 +649,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
(State::Byte, Action::Numeric), // KanjiLo1
(State::Byte, Action::Numeric), // KanjiLo2
(State::Byte, Action::Numeric), // Byte
// Alpha state:
(State::Init, Action::Alpha), // End
(State::Alpha, Action::Idle), // Symbol
(State::Numeric, Action::Alpha), // Numeric
@ -620,9 +660,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
(State::Byte, Action::Alpha), // KanjiLo1
(State::Byte, Action::Alpha), // KanjiLo2
(State::Byte, Action::Alpha), // Byte
// Byte state:
(State::Init, Action::Byte), // End
(State::Alpha, Action::Byte), // Symbol
(State::Numeric, Action::Byte), // Numeric
@ -633,9 +671,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
(State::Byte, Action::Idle), // KanjiLo1
(State::Byte, Action::Idle), // KanjiLo2
(State::Byte, Action::Idle), // Byte
// KanjiHi12 state:
(State::Init, Action::KanjiAndSingleByte), // End
(State::Alpha, Action::KanjiAndSingleByte), // Symbol
(State::Numeric, Action::KanjiAndSingleByte), // Numeric
@ -646,9 +682,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
(State::Kanji, Action::Idle), // KanjiLo1
(State::Kanji, Action::Idle), // KanjiLo2
(State::Byte, Action::KanjiAndSingleByte), // Byte
// KanjiHi3 state:
(State::Init, Action::KanjiAndSingleByte), // End
(State::Alpha, Action::KanjiAndSingleByte), // Symbol
(State::Numeric, Action::KanjiAndSingleByte), // Numeric
@ -659,9 +693,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
(State::Kanji, Action::Idle), // KanjiLo1
(State::Byte, Action::KanjiAndSingleByte), // KanjiLo2
(State::Byte, Action::KanjiAndSingleByte), // Byte
// Kanji state:
(State::Init, Action::Kanji), // End
(State::Alpha, Action::Kanji), // Symbol
(State::Numeric, Action::Kanji), // Numeric
@ -675,5 +707,3 @@ static STATE_TRANSITION: [(State, Action); 70] = [
];
//}}}

View file

@ -1,9 +1,9 @@
#![cfg(feature="image")]
use render::{Pixel, Canvas};
use render::{Canvas, Pixel};
use types::Color;
use image::{Pixel as ImagePixel, Rgb, Rgba, Luma, LumaA, Primitive, ImageBuffer};
use image::{ImageBuffer, Luma, LumaA, Pixel as ImagePixel, Primitive, Rgb, Rgba};
macro_rules! impl_pixel_for_image_pixel {
($p:ident<$s:ident>: $c:pat => $d:expr) => {
@ -50,12 +50,26 @@ mod render_tests {
#[test]
fn test_render_luma8_unsized() {
let image = Renderer::<Luma<u8>>::new(&[
Color::Light, Color::Dark, Color::Dark,
Color::Dark, Color::Light, Color::Light,
Color::Light, Color::Dark, Color::Light,
], 3, 1).module_dimensions(1, 1).build();
let image = Renderer::<Luma<u8>>::new(
&[
Color::Light,
Color::Dark,
Color::Dark,
//
Color::Dark,
Color::Light,
Color::Light,
//
Color::Light,
Color::Dark,
Color::Light,
],
3,
1,
).module_dimensions(1, 1)
.build();
#[cfg_attr(rustfmt, rustfmt_skip)]
let expected = [
255, 255, 255, 255, 255,
255, 255, 0, 0, 255,
@ -68,11 +82,12 @@ mod render_tests {
#[test]
fn test_render_rgba_unsized() {
let image = Renderer::<Rgba<u8>>::new(&[
Color::Light, Color::Dark,
Color::Dark, Color::Dark,
], 2, 1).module_dimensions(1, 1).build();
let image =
Renderer::<Rgba<u8>>::new(&[Color::Light, Color::Dark, Color::Dark, Color::Dark], 2, 1)
.module_dimensions(1, 1)
.build();
#[cfg_attr(rustfmt, rustfmt_skip)]
let expected: &[u8] = &[
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 0, 0, 0,255, 255,255,255,255,
@ -85,11 +100,14 @@ mod render_tests {
#[test]
fn test_render_resized_min() {
let image = Renderer::<Luma<u8>>::new(&[
Color::Dark, Color::Light,
Color::Light, Color::Dark,
], 2, 1).min_dimensions(10, 10).build();
let image = Renderer::<Luma<u8>>::new(
&[Color::Dark, Color::Light, Color::Light, Color::Dark],
2,
1,
).min_dimensions(10, 10)
.build();
#[cfg_attr(rustfmt, rustfmt_skip)]
let expected: &[u8] = &[
255,255,255, 255,255,255, 255,255,255, 255,255,255,
255,255,255, 255,255,255, 255,255,255, 255,255,255,
@ -114,11 +132,14 @@ mod render_tests {
#[test]
fn test_render_resized_max() {
let image = Renderer::<Luma<u8>>::new(&[
Color::Dark, Color::Light,
Color::Light, Color::Dark,
], 2, 1).max_dimensions(10, 5).build();
let image = Renderer::<Luma<u8>>::new(
&[Color::Dark, Color::Light, Color::Light, Color::Dark],
2,
1,
).max_dimensions(10, 5)
.build();
#[cfg_attr(rustfmt, rustfmt_skip)]
let expected: &[u8] = &[
255,255, 255,255, 255,255, 255,255,

View file

@ -2,6 +2,7 @@
use std::cmp::max;
use types::Color;
use cast::As;
pub mod image;
pub mod string;
@ -57,6 +58,7 @@ pub trait Canvas: Sized {
/// A QR code renderer. This is a builder type which converts a bool-vector into
/// an image.
#[cfg_attr(feature = "cargo-clippy", allow(stutter))] // rust-lang-nursery/rust-clippy#2212 ಠ_ಠ
pub struct Renderer<'a, P: Pixel> {
content: &'a [Color],
modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
@ -74,7 +76,7 @@ impl<'a, P: Pixel> Renderer<'a, P> {
assert!(modules_count * modules_count == content.len());
Renderer {
content,
modules_count: modules_count as u32,
modules_count: modules_count.as_u32(),
quiet_zone,
module_size: P::default_unit_size(),
dark_color: P::default_color(Color::Dark),
@ -154,7 +156,8 @@ impl<'a, P: Pixel> Renderer<'a, P> {
}
/// Renders the QR code into an image.
#[deprecated(since="0.4.0", note="renamed to `.build()` to de-emphasize the image connection")]
#[deprecated(since = "0.4.0",
note = "renamed to `.build()` to de-emphasize the image connection")]
pub fn to_image(&self) -> P::Image {
self.build()
}
@ -162,7 +165,11 @@ impl<'a, P: Pixel> Renderer<'a, P> {
/// Renders the QR code into an image.
pub fn build(&self) -> P::Image {
let w = self.modules_count;
let qz = if self.has_quiet_zone { self.quiet_zone } else { 0 };
let qz = if self.has_quiet_zone {
self.quiet_zone
} else {
0
};
let width = w + 2 * qz;
let (mw, mh) = self.module_size;

View file

@ -1,7 +1,8 @@
//! String rendering support.
use render::{Pixel, Canvas as RenderCanvas};
use render::{Canvas as RenderCanvas, Pixel};
use types::Color;
use cast::As;
pub trait Element: Copy {
fn default_color(color: Color) -> Self;
@ -11,7 +12,7 @@ pub trait Element: Copy {
impl Element for char {
fn default_color(color: Color) -> Self {
color.select('', ' ')
color.select('\u{2588}', ' ')
}
fn strlen(self) -> usize {
@ -25,7 +26,7 @@ impl Element for char {
impl<'a> Element for &'a str {
fn default_color(color: Color) -> Self {
color.select("", " ")
color.select("\u{2588}", " ")
}
fn strlen(self) -> usize {
@ -47,7 +48,7 @@ pub struct Canvas<P: Element> {
}
impl<P: Element> Pixel for P {
type Canvas = Canvas<P>;
type Canvas = Canvas<Self>;
type Image = String;
fn default_unit_size() -> (u32, u32) {
@ -64,29 +65,29 @@ impl<P: Element> RenderCanvas for Canvas<P> {
type Image = String;
fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self {
let width = width as usize;
let height = height as isize;
let dark_cap = dark_pixel.strlen() as isize;
let light_cap = light_pixel.strlen() as isize;
Canvas {
buffer: vec![light_pixel; width * (height as usize)],
let width = width.as_usize();
let height = height.as_isize();
let dark_cap = dark_pixel.strlen().as_isize();
let light_cap = light_pixel.strlen().as_isize();
Self {
buffer: vec![light_pixel; width * height.as_usize()],
width,
dark_pixel,
dark_cap_inc: dark_cap - light_cap,
capacity: light_cap * (width as isize) * height + (height - 1),
capacity: light_cap * width.as_isize() * height + (height - 1),
}
}
fn draw_dark_pixel(&mut self, x: u32, y: u32) {
let x = x as usize;
let y = y as usize;
let x = x.as_usize();
let y = y.as_usize();
self.capacity += self.dark_cap_inc;
self.buffer[x + y * self.width] = self.dark_pixel;
}
fn into_image(self) -> String {
let mut result = String::with_capacity(self.capacity as usize);
let mut result = String::with_capacity(self.capacity.as_usize());
for (i, pixel) in self.buffer.into_iter().enumerate() {
if i != 0 && i % self.width == 0 {
result.push('\n');
@ -101,12 +102,9 @@ impl<P: Element> RenderCanvas for Canvas<P> {
fn test_render_to_string() {
use render::Renderer;
let colors = &[
Color::Dark, Color::Light,
Color::Light, Color::Dark,
];
let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
let image: String = Renderer::<char>::new(colors, 2, 1).build();
assert_eq!(&image, " \n \n \n ");
assert_eq!(&image, " \n \u{2588} \n \u{2588} \n ");
let image2 = Renderer::new(colors, 2, 1)
.light_color("A")
@ -114,13 +112,15 @@ fn test_render_to_string() {
.module_dimensions(2, 2)
.build();
assert_eq!(&image2, "\
AAAAAAAA\n\
assert_eq!(
&image2,
"AAAAAAAA\n\
AAAAAAAA\n\
AA!B!!B!AAAA\n\
AA!B!!B!AAAA\n\
AAAA!B!!B!AA\n\
AAAA!B!!B!AA\n\
AAAAAAAA\n\
AAAAAAAA");
AAAAAAAA"
);
}

View file

@ -19,7 +19,7 @@
use std::fmt::Write;
use std::marker::PhantomData;
use render::{Pixel, Canvas as RenderCanvas};
use render::{Canvas as RenderCanvas, Pixel};
use types::Color as ModuleColor;
/// An SVG color.
@ -48,8 +48,17 @@ impl<'a> RenderCanvas for Canvas<'a> {
fn new(width: u32, height: u32, dark_pixel: Color<'a>, light_pixel: Color<'a>) -> Self {
Canvas {
svg: format!(
r#"<?xml version="1.0" standalone="yes"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{w}" height="{h}" shape-rendering="crispEdges"><rect x="0" y="0" width="{w}" height="{h}" fill="{bg}"/><path fill="{fg}" d=""#,
w=width, h=height, fg=dark_pixel.0, bg=light_pixel.0
concat!(
r#"<?xml version="1.0" standalone="yes"?>"#,
r#"<svg xmlns="http://www.w3.org/2000/svg""#,
r#" version="1.1" width="{w}" height="{h}" shape-rendering="crispEdges">"#,
r#"<rect x="0" y="0" width="{w}" height="{h}" fill="{bg}"/>"#,
r#"<path fill="{fg}" d=""#,
),
w = width,
h = height,
fg = dark_pixel.0,
bg = light_pixel.0
),
marker: PhantomData,
}
@ -60,10 +69,12 @@ impl<'a> RenderCanvas for Canvas<'a> {
}
fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
write!(self.svg, "M{l} {t}h{w}v{h}H{l}V{t}", l=left, t=top, w=width, h=height).unwrap();
write!(self.svg, "M{l} {t}h{w}v{h}H{l}V{t}", l = left, t = top, w = width, h = height)
.unwrap();
}
fn into_image(self) -> String {
self.svg + r#""/></svg>"#
fn into_image(mut self) -> String {
self.svg.push_str(r#""/></svg>"#);
self.svg
}
}

View file

@ -1,7 +1,8 @@
use std::default::Default;
use std::cmp::{PartialOrd, Ordering};
use std::fmt::{Display, Formatter, Error};
use std::cmp::{Ordering, PartialOrd};
use std::fmt::{Display, Error, Formatter};
use std::ops::Not;
use cast::As;
//------------------------------------------------------------------------------
//{{{ QrResult
@ -76,8 +77,8 @@ impl Color {
}
impl Not for Color {
type Output = Color;
fn not(self) -> Color {
type Output = Self;
fn not(self) -> Self {
match self {
Color::Light => Color::Dark,
Color::Dark => Color::Light,
@ -146,26 +147,28 @@ impl Version {
/// If the entry compares equal to the default value of T, this method
/// returns `Err(QrError::InvalidVersion)`.
pub fn fetch<T>(&self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult<T>
where T: PartialEq + Default + Copy
where
T: PartialEq + Default + Copy,
{
match *self {
Version::Normal(v @ 1...40) => Ok(table[v as usize - 1][ec_level as usize]),
Version::Normal(v @ 1...40) => {
return Ok(table[(v - 1).as_usize()][ec_level as usize]);
}
Version::Micro(v @ 1...4) => {
let obj = table[v as usize + 39][ec_level as usize];
if obj != Default::default() {
Ok(obj)
} else {
let obj = table[(v + 39).as_usize()][ec_level as usize];
if obj != T::default() {
return Ok(obj);
}
}
_ => {}
}
Err(QrError::InvalidVersion)
}
}
_ => Err(QrError::InvalidVersion)
}
}
/// The number of bits needed to encode the mode indicator.
pub fn mode_bits_count(&self) -> usize {
match *self {
Version::Micro(a) => (a - 1) as usize,
Version::Micro(a) => (a - 1).as_usize(),
_ => 4,
}
}
@ -213,7 +216,7 @@ impl Mode {
pub fn length_bits_count(&self, version: Version) -> usize {
match version {
Version::Micro(a) => {
let a = a as usize;
let a = a.as_usize();
match *self {
Mode::Numeric => 2 + a,
Mode::Alphanumeric | Mode::Byte => 1 + a,
@ -223,8 +226,7 @@ impl Mode {
Version::Normal(1...9) => match *self {
Mode::Numeric => 10,
Mode::Alphanumeric => 9,
Mode::Byte => 8,
Mode::Kanji => 8,
Mode::Byte | Mode::Kanji => 8,
},
Version::Normal(10...26) => match *self {
Mode::Numeric => 12,
@ -268,7 +270,7 @@ impl Mode {
/// assert!(a <= c);
/// assert!(b <= c);
///
pub fn max(&self, other: Mode) -> Mode {
pub fn max(&self, other: Self) -> Self {
match self.partial_cmp(&other) {
Some(Ordering::Less) | Some(Ordering::Equal) => other,
Some(Ordering::Greater) => *self,
@ -280,15 +282,15 @@ impl Mode {
impl PartialOrd for Mode {
/// Defines a partial ordering between modes. If `a <= b`, then `b` contains
/// a superset of all characters supported by `a`.
fn partial_cmp(&self, other: &Mode) -> Option<Ordering> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (*self, *other) {
(Mode::Numeric, Mode::Alphanumeric) => Some(Ordering::Less),
(Mode::Alphanumeric, Mode::Numeric) => Some(Ordering::Greater),
(Mode::Numeric, Mode::Byte) => Some(Ordering::Less),
(Mode::Byte, Mode::Numeric) => Some(Ordering::Greater),
(Mode::Alphanumeric, Mode::Byte) => Some(Ordering::Less),
(Mode::Byte, Mode::Alphanumeric) => Some(Ordering::Greater),
(Mode::Numeric, Mode::Alphanumeric) |
(Mode::Numeric, Mode::Byte) |
(Mode::Alphanumeric, Mode::Byte) |
(Mode::Kanji, Mode::Byte) => Some(Ordering::Less),
(Mode::Alphanumeric, Mode::Numeric) |
(Mode::Byte, Mode::Numeric) |
(Mode::Byte, Mode::Alphanumeric) |
(Mode::Byte, Mode::Kanji) => Some(Ordering::Greater),
(a, b) if a == b => Some(Ordering::Equal),
_ => None,
@ -298,7 +300,7 @@ impl PartialOrd for Mode {
#[cfg(test)]
mod mode_tests {
use types::Mode::{Numeric, Alphanumeric, Byte, Kanji};
use types::Mode::{Alphanumeric, Byte, Kanji, Numeric};
#[test]
fn test_mode_order() {
@ -321,5 +323,3 @@ mod mode_tests {
}
//}}}