@@ -231,10 +231,13 @@ fn deinterleave_and_verify(
231231 } else {
232232 & block[ data_len..data_len + ecc_len]
233233 } ;
234- if !reed_solomon:: check_segments ( & block[ ..data_len] , ecc_slice, ecc_len) {
234+ let mut corrected = Vec :: with_capacity ( data_len + ecc_len) ;
235+ corrected. extend_from_slice ( & block[ ..data_len] ) ;
236+ corrected. extend_from_slice ( ecc_slice) ;
237+ if !reed_solomon:: correct ( & mut corrected, ecc_len) {
235238 return Err ( QrError :: Checksum ) ;
236239 }
237- data. extend_from_slice ( & block [ ..data_len] ) ;
240+ data. extend_from_slice ( & corrected [ ..data_len] ) ;
238241 }
239242
240243 Ok ( data)
@@ -448,9 +451,69 @@ impl<'a> BitReader<'a> {
448451
449452#[ cfg( test) ]
450453mod tests {
451- use crate :: { BitGrid , QrError } ;
454+ use crate :: {
455+ BitGrid , EncodeOptions , ErrorCorrectionLevel , MaskPattern , QrError , Version , encode_text,
456+ tables:: num_data_codewords,
457+ } ;
458+
459+ use super :: { build_function_modules, decode_modules, decode_payload} ;
460+
461+ fn version_one_options ( error_correction : ErrorCorrectionLevel ) -> EncodeOptions {
462+ EncodeOptions {
463+ min_version : Version :: MIN ,
464+ max_version : Version :: MIN ,
465+ min_error_correction : error_correction,
466+ boost_error_correction : false ,
467+ mask : Some ( MaskPattern :: new ( 0 ) . expect ( "valid mask" ) ) ,
468+ }
469+ }
452470
453- use super :: decode_modules;
471+ fn fixed_version_options (
472+ version : Version ,
473+ error_correction : ErrorCorrectionLevel ,
474+ ) -> EncodeOptions {
475+ EncodeOptions {
476+ min_version : version,
477+ max_version : version,
478+ min_error_correction : error_correction,
479+ boost_error_correction : false ,
480+ mask : Some ( MaskPattern :: new ( 0 ) . expect ( "valid mask" ) ) ,
481+ }
482+ }
483+
484+ fn flip_codeword_bit ( modules : & mut BitGrid , version : Version , codeword : usize , bit : usize ) {
485+ let functions = build_function_modules ( version) ;
486+ let size = modules. size ( ) ;
487+ let target_bit = codeword * 8 + bit;
488+ let mut bit_index = 0_usize ;
489+ let mut right = size as isize - 1 ;
490+ while right >= 1 {
491+ if right == 6 {
492+ right = 5 ;
493+ }
494+ for vertical in 0 ..size {
495+ for delta in 0 ..2 {
496+ let x = ( right - delta) as usize ;
497+ let upward = ( ( right + 1 ) & 2 ) == 0 ;
498+ let y = if upward {
499+ size - 1 - vertical
500+ } else {
501+ vertical
502+ } ;
503+ if functions. get ( x, y) {
504+ continue ;
505+ }
506+ if bit_index == target_bit {
507+ modules. invert ( x, y) ;
508+ return ;
509+ }
510+ bit_index += 1 ;
511+ }
512+ }
513+ right -= 2 ;
514+ }
515+ panic ! ( "codeword bit {target_bit} is outside the matrix payload" ) ;
516+ }
454517
455518 #[ test]
456519 fn rejects_matrix_sizes_above_qr_version_40 ( ) {
@@ -469,4 +532,106 @@ mod tests {
469532 Err ( QrError :: InvalidMatrixSize ( 1045 ) ) ,
470533 ) ;
471534 }
535+
536+ #[ test]
537+ fn rejects_matrix_sizes_below_qr_version_1 ( ) {
538+ let undersized = BitGrid :: new ( 20 ) ;
539+ assert_eq ! (
540+ decode_modules( & undersized) ,
541+ Err ( QrError :: InvalidMatrixSize ( 20 ) ) ,
542+ ) ;
543+ }
544+
545+ #[ test]
546+ fn rejects_malformed_format_information ( ) {
547+ let matrix = BitGrid :: new ( Version :: MIN . size ( ) ) ;
548+ assert_eq ! (
549+ decode_modules( & matrix) ,
550+ Err ( QrError :: InvalidFormatInformation ) ,
551+ ) ;
552+ }
553+
554+ #[ test]
555+ fn rejects_unsupported_payload_mode ( ) {
556+ assert_eq ! (
557+ decode_payload( & [ 0b0011_0000 ] , Version :: MIN ) ,
558+ Err ( QrError :: UnsupportedMode ( 0x3 ) ) ,
559+ ) ;
560+ }
561+
562+ #[ test]
563+ fn rejects_truncated_byte_payload ( ) {
564+ assert_eq ! (
565+ decode_payload( & [ 0x40 , 0x10 ] , Version :: MIN ) ,
566+ Err ( QrError :: MissingTerminator ) ,
567+ ) ;
568+ }
569+
570+ #[ test]
571+ fn rejects_payload_values_that_fail_mode_checksum ( ) {
572+ assert_eq ! (
573+ decode_payload( & [ 0x10 , 0x0F , 0xE8 ] , Version :: MIN ) ,
574+ Err ( QrError :: Checksum ) ,
575+ ) ;
576+ }
577+
578+ #[ test]
579+ fn rejects_codeword_damage_beyond_error_correction_capacity ( ) {
580+ let code =
581+ encode_text ( "HELLO" , version_one_options ( ErrorCorrectionLevel :: Low ) ) . expect ( "encodes" ) ;
582+ let mut modules = code. modules ( ) . clone ( ) ;
583+
584+ for codeword in 0 ..4 {
585+ flip_codeword_bit ( & mut modules, code. version ( ) , codeword, 0 ) ;
586+ }
587+
588+ assert_eq ! ( decode_modules( & modules) , Err ( QrError :: Checksum ) ) ;
589+ }
590+
591+ #[ test]
592+ fn decodes_with_correctable_data_codeword_damage ( ) {
593+ let code =
594+ encode_text ( "HELLO" , version_one_options ( ErrorCorrectionLevel :: High ) ) . expect ( "encodes" ) ;
595+ let mut modules = code. modules ( ) . clone ( ) ;
596+
597+ for codeword in [ 0 , 2 , 4 ] {
598+ flip_codeword_bit ( & mut modules, code. version ( ) , codeword, 0 ) ;
599+ }
600+
601+ let decoded = decode_modules ( & modules) . expect ( "corrects damaged data codewords" ) ;
602+ assert_eq ! ( decoded. text. as_deref( ) , Some ( "HELLO" ) ) ;
603+ }
604+
605+ #[ test]
606+ fn decodes_with_correctable_ecc_codeword_damage ( ) {
607+ let code =
608+ encode_text ( "HELLO" , version_one_options ( ErrorCorrectionLevel :: High ) ) . expect ( "encodes" ) ;
609+ let mut modules = code. modules ( ) . clone ( ) ;
610+ let first_ecc_codeword = num_data_codewords ( code. version ( ) , code. error_correction ( ) ) ;
611+
612+ for codeword in first_ecc_codeword..first_ecc_codeword + 3 {
613+ flip_codeword_bit ( & mut modules, code. version ( ) , codeword, 0 ) ;
614+ }
615+
616+ let decoded = decode_modules ( & modules) . expect ( "corrects damaged ecc codewords" ) ;
617+ assert_eq ! ( decoded. text. as_deref( ) , Some ( "HELLO" ) ) ;
618+ }
619+
620+ #[ test]
621+ fn decodes_with_correctable_damage_across_interleaved_blocks ( ) {
622+ let version = Version :: new ( 4 ) . expect ( "valid version" ) ;
623+ let code = encode_text (
624+ "INTERLEAVED BLOCKS" ,
625+ fixed_version_options ( version, ErrorCorrectionLevel :: High ) ,
626+ )
627+ . expect ( "encodes" ) ;
628+ let mut modules = code. modules ( ) . clone ( ) ;
629+
630+ for codeword in 0 ..4 {
631+ flip_codeword_bit ( & mut modules, code. version ( ) , codeword, 0 ) ;
632+ }
633+
634+ let decoded = decode_modules ( & modules) . expect ( "corrects damaged interleaved blocks" ) ;
635+ assert_eq ! ( decoded. text. as_deref( ) , Some ( "INTERLEAVED BLOCKS" ) ) ;
636+ }
472637}
0 commit comments