package pdf import "core:slice" import "core:testing" // Test vectors come from two places. // // The Group 3 and Group 4 streams named G3_* and G4_* were produced by // libtiff (through Pillow), so they are an independent encoder's idea of what // these images look like; decoding them back to the source pixels checks the // code tables and the 2-D coder against something that is not this file. // The MH_* and MR_* streams are hand-assembled bit by bit from the code words // in ITU-T T.4 tables 1-4, which is the only way to pin down EOL, fill, // byte alignment, the /K > 0 tag bit and the malformed cases. // G4, 8 columns x 12 rows. Between them the rows use every 2-D mode: // pass, horizontal, V0, VR1-VR3 and VL1-VL3, and end with an EOFB. @(private="file", rodata) G4_FULL := [?]byte{ 0x93, 0x51, 0x59, 0x43, 0x94, 0x38, 0x4A, 0xAE, 0xC4, 0x38, 0x2A, 0x3D, 0x8C, 0xDB, 0x07, 0x09, 0x46, 0x00, 0x20, 0x02, } @(private="file", rodata) G4_FULL_OUT := [?]byte{ 0xFF, 0x00, 0xAA, 0x33, 0xFC, 0xE0, 0xCF, 0xFF, 0xF0, 0xFE, 0xF9, 0xFF, } // The same image with /BlackIs1 true: every bit inverted. @(private="file", rodata) G4_FULL_OUT_INVERTED := [?]byte{ 0x00, 0xFF, 0x55, 0xCC, 0x03, 0x1F, 0x30, 0x00, 0x0F, 0x01, 0x06, 0x00, } // 200 columns x 5 rows: runs long enough to need make-up codes. @(private="file", rodata) G4_WIDE := [?]byte{ 0x93, 0x50, 0xC9, 0x14, 0xEC, 0x54, 0x0F, 0x0D, 0x42, 0x6A, 0x07, 0x91, 0x4B, 0x03, 0xC8, 0x4C, 0x9C, 0x37, 0x00, 0x10, 0x01, } @(private="file", rodata) G3_WIDE := [?]byte{ 0x00, 0x15, 0xE6, 0x00, 0x26, 0xA1, 0x92, 0x28, 0x00, 0xEC, 0x54, 0x0F, 0x0D, 0x40, 0x01, 0x35, 0x03, 0xC9, 0x2C, 0x0F, 0x20, 0x01, 0x5E, 0x60, } @(private="file", rodata) WIDE_OUT := [?]byte{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, } // Group 3 1-D of the first four rows of G4_FULL, EOL-separated. @(private="file", rodata) G3_SMALL := [?]byte{ 0x00, 0x19, 0x80, 0x09, 0xA8, 0xA0, 0x02, 0x3A, 0x1D, 0x0E, 0x87, 0x40, 0x02, 0x6B, 0xBE, 0xE0, } @(private="file", rodata) SMALL_OUT := [?]byte{ 0xFF, 0x00, 0xAA, 0x33, } // pass mode: the reference black run ends before the coding line changes. @(private="file", rodata) G4_PASS := [?]byte{ 0x2F, 0xC6, 0x00, 0x20, 0x02, } @(private="file", rodata) G4_PASS_OUT := [?]byte{ 0xCF, 0xFF, } // VL1 then VR1. @(private="file", rodata) G4_VR1 := [?]byte{ 0x2E, 0xE9, 0xC0, 0x04, 0x00, 0x40, } @(private="file", rodata) G4_VR1_OUT := [?]byte{ 0xC3, 0x81, } // VR2 then VL2. @(private="file", rodata) G4_VL2 := [?]byte{ 0x23, 0x94, 0x30, 0xA0, 0x02, 0x00, 0x20, } @(private="file", rodata) G4_VL2_OUT := [?]byte{ 0x81, 0xE7, } // VL2, VL3. @(private="file", rodata) G4_VL3 := [?]byte{ 0x0A, 0x0A, 0x00, 0x20, 0x02, } @(private="file", rodata) G4_VL3_OUT := [?]byte{ 0xFC, 0xE0, } // One 1-D row, eight white pixels, no EOL and no padding to speak of. @(private="file", rodata) MH_WHITE_ROW := [?]byte{ 0x98, } @(private="file", rodata) MH_WHITE_ROW_OUT := [?]byte{ 0xFF, } // A black row still opens with a zero-length white run. @(private="file", rodata) MH_BLACK_ROW := [?]byte{ 0x35, 0x14, } @(private="file", rodata) MH_BLACK_ROW_OUT := [?]byte{ 0x00, } // Alternating pixels: eight runs of one. @(private="file", rodata) MH_ALT_ROW := [?]byte{ 0x1D, 0x0E, 0x87, 0x43, 0xA0, } @(private="file", rodata) MH_ALT_ROW_OUT := [?]byte{ 0xAA, } // EOLs with fill bits in front of them. @(private="file", rodata) MH_EOL_FILL := [?]byte{ 0x00, 0x01, 0x98, 0x00, 0x26, 0xA2, 0x80, } @(private="file", rodata) MH_EOL_FILL_OUT := [?]byte{ 0xFF, 0x00, } // Two rows then a six-EOL return-to-control. @(private="file", rodata) MH_RTC := [?]byte{ 0x00, 0x19, 0x80, 0x09, 0xA8, 0xA0, 0x02, 0x00, 0x20, 0x02, 0x00, 0x20, 0x02, 0x00, 0x20, } @(private="file", rodata) MH_RTC_OUT := [?]byte{ 0xFF, 0x00, } // A 100-pixel run in an 8-pixel row: truncated, not an error. @(private="file", rodata) MH_OVERLONG := [?]byte{ 0xD8, 0xA8, } @(private="file", rodata) MH_OVERLONG_OUT := [?]byte{ 0xFF, } // A row that ends after three white pixels; the colour it changed to runs on. @(private="file", rodata) MH_SHORT := [?]byte{ 0x80, } @(private="file", rodata) MH_SHORT_OUT := [?]byte{ 0xE0, } // 2000 white pixels: exercises the extended make-up codes (>= 1792). @(private="file", rodata) MH_WIDE_WHITE := [?]byte{ 0x01, 0x2A, 0x80, } @(private="file", rodata) MH_WIDE_WHITE_OUT := [250]byte{0 ..= 249 = 0xFF} // 2000 black pixels: the extended make-up codes are shared by both colours. @(private="file", rodata) MH_WIDE_BLACK := [?]byte{ 0x35, 0x01, 0x20, 0x5C, } @(private="file", rodata) MH_WIDE_BLACK_OUT := [250]byte{0 ..= 249 = 0x00} // /K > 0: EOL, 1-D tag, row; EOL, 2-D tag, horizontal-mode row. @(private="file", rodata) MR_MIXED := [?]byte{ 0x00, 0x1C, 0xC0, 0x04, 0x4D, 0x45, } @(private="file", rodata) MR_MIXED_OUT := [?]byte{ 0xFF, 0x00, } // /K > 0 with a V0 row referring back to a 1-D row. @(private="file", rodata) MR_MIXED3 := [?]byte{ 0x00, 0x1C, 0xC0, 0x05, 0x00, 0x11, 0x35, 0x14, } @(private="file", rodata) MR_MIXED3_OUT := [?]byte{ 0xFF, 0xFF, 0x00, } // /EncodedByteAlign: the same two rows, each starting on a byte boundary. @(private="file", rodata) MH_BYTE_ALIGN := [?]byte{ 0x98, 0x35, 0x14, } @(private="file", rodata) MH_BYTE_ALIGN_OUT := [?]byte{ 0xFF, 0x00, } // A page at the standard fax width, 1728 x 6, encoded by libtiff both // ways. Solid rows and 900-pixel runs put the make-up codes, pass mode // and the long-run paths to work at a size a real document uses. @(private="file", rodata) PAGE_G4 := [?]byte{ 0x93, 0x50, 0x32, 0x86, 0xF2, 0x07, 0x26, 0xCD, 0x4B, 0x2D, 0x3B, 0x02, 0x60, 0x16, 0x19, 0x2D, 0x01, 0xE6, 0x76, 0x96, 0x07, 0xE6, 0x4A, 0x41, 0x9C, 0xB7, 0x02, 0x0C, 0x8C, 0xC8, 0x60, 0x18, 0x20, 0x60, 0x5C, 0xA7, 0x22, 0xDC, 0x5C, 0x32, 0x11, 0x32, 0x02, 0xC3, 0x31, 0x44, 0x4A, 0xA0, 0x1E, 0x08, 0x24, 0xC0, 0x2C, 0x06, 0x40, 0xF0, 0xD0, 0xBF, 0x89, 0x04, 0xE0, 0x42, 0x49, 0x86, 0xC3, 0x21, 0x20, 0x3C, 0x30, 0x76, 0x14, 0x06, 0x00, 0x04, 0x00, 0x40, } @(private="file", rodata) PAGE_G3 := [?]byte{ 0x00, 0x14, 0xD9, 0xA8, 0x00, 0x9A, 0x81, 0x94, 0x37, 0x00, 0x13, 0x50, 0x39, 0x36, 0x6A, 0x58, 0x00, 0xB4, 0xEC, 0x09, 0x80, 0x58, 0x00, 0x32, 0x5A, 0x03, 0xCF, 0x69, 0x60, 0x7F, 0x25, 0x20, 0xCE, 0xDC, 0x08, 0x32, 0x39, 0x0C, 0x03, 0x00, 0x60, 0x5D, 0x39, 0x37, 0x17, 0x0C, 0x84, 0x00, 0x26, 0x40, 0x59, 0x31, 0x4D, 0x85, 0xC0, 0xF0, 0x4D, 0x8D, 0x82, 0xC2, 0xE7, 0x03, 0xC3, 0x42, 0xFE, 0x05, 0x9D, 0x92, 0x06, 0xC9, 0x09, 0x01, 0xE1, 0x8D, 0x85, 0x01, 0x80, } @(private="file", rodata) PAGE_OUT := [?]byte{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @(private="file") expect_decode :: proc(t: ^testing.T, name: string, data: []byte, p: Ccitt_Params, want: []byte) { got, err := ccitt_decode(data, p) defer delete(got) testing.expectf(t, err == .None, "%s: %v", name, error_string(err)) testing.expectf(t, slice.equal(got, want), "%s: got % 02X, want % 02X", name, got, want) } // --------------------------------------------------------------------------- // Bit reader // --------------------------------------------------------------------------- @(test) test_ccitt_bits_peek :: proc(t: ^testing.T) { data := [?]byte{0b1011_0010, 0b0100_1111, 0b1000_0001} b := ccitt_bits_make(data[:]) testing.expect_value(t, ccitt_bits_left(&b), 24) testing.expect_value(t, ccitt_bits_peek(&b, 1), u32(1)) testing.expect_value(t, ccitt_bits_peek(&b, 4), u32(0b1011)) testing.expect_value(t, ccitt_bits_peek(&b, 8), u32(0b1011_0010)) testing.expect_value(t, ccitt_bits_peek(&b, 16), u32(0b1011_0010_0100_1111)) testing.expect_value(t, ccitt_bits_peek(&b, 0), u32(0)) // A peek that straddles two bytes, from an unaligned position. ccitt_bits_skip(&b, 5) testing.expect_value(t, ccitt_bits_peek(&b, 6), u32(0b010_010)) testing.expect_value(t, ccitt_bits_left(&b), 19) // Past the end reads as zero bits, and skipping past the end clamps. ccitt_bits_skip(&b, 18) testing.expect_value(t, ccitt_bits_left(&b), 1) testing.expect_value(t, ccitt_bits_peek(&b, 8), u32(0b1000_0000)) ccitt_bits_skip(&b, 1000) testing.expect_value(t, ccitt_bits_left(&b), 0) testing.expect_value(t, ccitt_bits_peek(&b, 12), u32(0)) } @(test) test_ccitt_bits_align :: proc(t: ^testing.T) { data := [?]byte{0xFF, 0xFF, 0xFF} b := ccitt_bits_make(data[:]) ccitt_bits_align(&b) testing.expect_value(t, b.pos, 0) // already aligned: no movement ccitt_bits_skip(&b, 1) ccitt_bits_align(&b) testing.expect_value(t, b.pos, 8) ccitt_bits_skip(&b, 7) ccitt_bits_align(&b) testing.expect_value(t, b.pos, 16) ccitt_bits_skip(&b, 7) ccitt_bits_align(&b) testing.expect_value(t, b.pos, 24) // clamped to the end, not past it } // --------------------------------------------------------------------------- // Code tables // --------------------------------------------------------------------------- @(test) test_ccitt_tables_are_prefix_free :: proc(t: ^testing.T) { // The decoder walks the tables in length order and takes the first match, // which is only the right answer because no code is a prefix of another. // It also relies on no code having eight leading zeros, so that fill bits // and EOLs can never be confused with data. check :: proc(t: ^testing.T, table: []Ccitt_Code, name: string) { for a, i in table { testing.expectf(t, a.bits >= 2 && a.bits <= 13, "%s[%d]: bad length %d", name, i, a.bits) testing.expectf(t, u32(a.code) < 1 << uint(a.bits), "%s[%d]: code wider than its length", name, i) if a.bits >= 8 { lead := u32(a.code) >> uint(int(a.bits) - 8) testing.expectf(t, lead != 0, "%s[%d]: eight leading zeros collide with EOL", name, i) } if i > 0 { prev := table[i - 1] ordered := prev.bits < a.bits || (prev.bits == a.bits && prev.code < a.code) testing.expectf(t, ordered, "%s[%d]: table is not sorted by (bits, code)", name, i) } for b, j in table { if i == j || b.bits < a.bits { continue } shifted := u32(b.code) >> uint(int(b.bits) - int(a.bits)) testing.expectf(t, !(b.bits > a.bits && shifted == u32(a.code)), "%s: run %d is a prefix of run %d", name, a.run, b.run) testing.expectf(t, !(b.bits == a.bits && b.code == a.code), "%s: runs %d and %d share a code", name, a.run, b.run) } } } check(t, CCITT_WHITE[:], "white") check(t, CCITT_BLACK[:], "black") } @(test) test_ccitt_tables_are_complete :: proc(t: ^testing.T) { // Terminating codes 0-63, make-up codes 64-1728 in steps of 64, and the // extended make-up codes 1792-2560, in both tables. check :: proc(t: ^testing.T, table: []Ccitt_Code, name: string) { seen: map[int]bool defer delete(seen) for e in table { seen[int(e.run)] = true } for r in 0 ..< 64 { testing.expectf(t, seen[r], "%s: terminating run %d missing", name, r) } for r := 64; r <= 2560; r += 64 { if r > 1728 && r < 1792 { continue } testing.expectf(t, seen[r], "%s: make-up run %d missing", name, r) } testing.expectf(t, len(table) == 104, "%s: %d codes, want 104", name, len(table)) } check(t, CCITT_WHITE[:], "white") check(t, CCITT_BLACK[:], "black") } @(test) test_ccitt_lookup :: proc(t: ^testing.T) { Case :: struct { bits: []byte, avail: int, // bits of `bits` that are really there; 0 means all black: bool, run: int, used: int, ok: bool, } cases := []Case{ // Terminating codes, shortest and longest of each colour. {{0b0111_0000}, 0, false, 2, 4, true}, // white 2: 0111 {{0b0011_0101}, 0, false, 0, 8, true}, // white 0: 00110101 {{0b1100_0000}, 0, true, 2, 2, true}, // black 2: 11 {{0b0000_1101, 0b1100_0000}, 0, true, 0, 10, true}, // black 0: 0000110111 // Make-up codes. {{0b1101_1000}, 0, false, 64, 5, true}, // white 64: 11011 {{0b0100_1101, 0b1000_0000}, 0, false, 1728, 9, true}, // white 1728: 010011011 {{0b0000_0011, 0b1100_0000}, 0, true, 64, 10, true}, // black 64: 0000001111 // Extended make-up codes, shared by both colours. {{0b0000_0001, 0b0000_0000}, 0, false, 1792, 11, true}, {{0b0000_0001, 0b0000_0000}, 0, true, 1792, 11, true}, {{0b0000_0001, 0b1111_0000}, 0, false, 2560, 12, true}, {{0b0000_0001, 0b1111_0000}, 0, true, 2560, 12, true}, // No code is all zeros, so fill and EOL never look like a run. {{0x00, 0x00}, 0, false, 0, 0, false}, {{0x00, 0x00}, 0, true, 0, 0, false}, // Codes the zero padding would otherwise complete: only the first // four bits of white 0 (00110101) and of black 0 (0000110111) are // really present, and neither may be invented. {{0b0011_0101}, 4, false, 0, 0, false}, {{0b0000_1101, 0b1100_0000}, 4, true, 0, 0, false}, } for c, i in cases { b := ccitt_bits_make(c.bits) if c.avail > 0 { b.end = c.avail } table := c.black ? CCITT_BLACK[:] : CCITT_WHITE[:] run, ok := ccitt_lookup(&b, table) testing.expectf(t, ok == c.ok, "case %d: ok %v, want %v", i, ok, c.ok) if c.ok { testing.expectf(t, run == c.run, "case %d: run %d, want %d", i, run, c.run) testing.expectf(t, b.pos == c.used, "case %d: used %d bits, want %d", i, b.pos, c.used) } } } @(test) test_ccitt_run_chains_makeups :: proc(t: ^testing.T) { // white 1728 (010011011) + white 63 (00110100) = 1791, the largest run // T.4 can code without the extended table. data := [?]byte{0b0100_1101, 0b1001_1010, 0b0000_0000} b := ccitt_bits_make(data[:]) run, ok := ccitt_run(&b, false, 4096) testing.expect(t, ok) testing.expect_value(t, run, 1791) testing.expect_value(t, b.pos, 17) // The same run clamped by the row width. b = ccitt_bits_make(data[:]) run, ok = ccitt_run(&b, false, 100) testing.expect(t, ok) testing.expect_value(t, run, 100) } // --------------------------------------------------------------------------- // 2-D mode codes and EOL // --------------------------------------------------------------------------- @(test) test_ccitt_mode :: proc(t: ^testing.T) { Case :: struct { byte: byte, mode: Ccitt_Mode, used: int, } cases := []Case{ {0b1000_0000, .V0, 1}, {0b0110_0000, .VR1, 3}, {0b0100_0000, .VL1, 3}, {0b0010_0000, .Horizontal, 3}, {0b0001_0000, .Pass, 4}, {0b0000_1100, .VR2, 6}, {0b0000_1000, .VL2, 6}, {0b0000_0110, .VR3, 7}, {0b0000_0100, .VL3, 7}, {0b0000_0010, .Extension, 7}, } for c, i in cases { data := [?]byte{c.byte, 0xFF} b := ccitt_bits_make(data[:]) mode, ok := ccitt_mode(&b) testing.expectf(t, ok, "case %d: not decoded", i) testing.expectf(t, mode == c.mode, "case %d: %v, want %v", i, mode, c.mode) testing.expectf(t, b.pos == c.used, "case %d: used %d bits, want %d", i, b.pos, c.used) } // Seven zero bits is not a mode; it is an EOL or garbage. zeros := [?]byte{0x00, 0x00} b := ccitt_bits_make(zeros[:]) _, ok := ccitt_mode(&b) testing.expect(t, !ok) testing.expect_value(t, b.pos, 0) // A mode the zero padding would complete is refused. short := [?]byte{0b0100_0000} b = ccitt_bits_make(short[:]) b.end = 2 _, ok = ccitt_mode(&b) testing.expect(t, !ok) } @(test) test_ccitt_try_eol :: proc(t: ^testing.T) { // A bare EOL. eol := [?]byte{0b0000_0000, 0b0001_0000} b := ccitt_bits_make(eol[:]) testing.expect(t, ccitt_try_eol(&b)) testing.expect_value(t, b.pos, 12) // Fill bits in front of it are absorbed. filled := [?]byte{0x00, 0x00, 0b0000_0001, 0b0000_0000} b = ccitt_bits_make(filled[:]) testing.expect(t, ccitt_try_eol(&b)) testing.expect_value(t, b.pos, 24) // A run code is not an EOL, and the reader must not have moved. code := [?]byte{0b0000_0100, 0b0000_0000} // white 45: 00000100 b = ccitt_bits_make(code[:]) testing.expect(t, !ccitt_try_eol(&b)) testing.expect_value(t, b.pos, 0) // Trailing zero fill at the end of the data is not an EOL either: there // is no terminating one bit, so the position is restored. tail := [?]byte{0x00, 0x00, 0x00} b = ccitt_bits_make(tail[:]) testing.expect(t, !ccitt_try_eol(&b)) testing.expect_value(t, b.pos, 0) } // --------------------------------------------------------------------------- // Painting // --------------------------------------------------------------------------- @(test) test_ccitt_paint :: proc(t: ^testing.T) { row := [3]byte{} for i in 0 ..< 3 { row[i] = 0xFF } ccitt_paint(row[:], 3, 5, false) testing.expect_value(t, row[0], byte(0b1110_0111)) ccitt_paint(row[:], 8, 24, false) testing.expect_value(t, row[1], byte(0x00)) testing.expect_value(t, row[2], byte(0x00)) ccitt_paint(row[:], 12, 20, true) testing.expect_value(t, row[1], byte(0b0000_1111)) testing.expect_value(t, row[2], byte(0b1111_0000)) // Out-of-range spans are clipped rather than trusted. ccitt_paint(row[:], -100, 100, true) testing.expect_value(t, row[0], byte(0xFF)) testing.expect_value(t, row[2], byte(0xFF)) ccitt_paint(row[:], 5, 5, false) testing.expect_value(t, row[0], byte(0xFF)) } // --------------------------------------------------------------------------- // Whole images // --------------------------------------------------------------------------- @(test) test_ccitt_g4 :: proc(t: ^testing.T) { Case :: struct { name: string, data: []byte, columns: int, rows: int, want: []byte, } cases := []Case{ {"full", G4_FULL[:], 8, 12, G4_FULL_OUT[:]}, {"full, rows unknown", G4_FULL[:], 8, 0, G4_FULL_OUT[:]}, {"pass mode", G4_PASS[:], 8, 2, G4_PASS_OUT[:]}, {"vr1", G4_VR1[:], 8, 2, G4_VR1_OUT[:]}, {"vl2", G4_VL2[:], 8, 2, G4_VL2_OUT[:]}, {"vl3", G4_VL3[:], 8, 2, G4_VL3_OUT[:]}, {"wide, make-up codes", G4_WIDE[:], 200, 5, WIDE_OUT[:]}, {"wide, rows unknown", G4_WIDE[:], 200, 0, WIDE_OUT[:]}, } for c in cases { p := ccitt_default_params() p.k = -1 p.columns = c.columns p.rows = c.rows expect_decode(t, c.name, c.data, p, c.want) } } @(test) test_ccitt_g3_1d :: proc(t: ^testing.T) { Case :: struct { name: string, data: []byte, columns: int, rows: int, want: []byte, } cases := []Case{ {"white row", MH_WHITE_ROW[:], 8, 1, MH_WHITE_ROW_OUT[:]}, {"black row", MH_BLACK_ROW[:], 8, 1, MH_BLACK_ROW_OUT[:]}, {"alternating pixels", MH_ALT_ROW[:], 8, 1, MH_ALT_ROW_OUT[:]}, {"eol and fill", MH_EOL_FILL[:], 8, 0, MH_EOL_FILL_OUT[:]}, {"return to control", MH_RTC[:], 8, 0, MH_RTC_OUT[:]}, {"libtiff group 3", G3_SMALL[:], 8, 0, SMALL_OUT[:]}, {"libtiff group 3, wide", G3_WIDE[:], 200, 0, WIDE_OUT[:]}, {"extended make-up, white", MH_WIDE_WHITE[:], 2000, 1, MH_WIDE_WHITE_OUT[:]}, {"extended make-up, black", MH_WIDE_BLACK[:], 2000, 1, MH_WIDE_BLACK_OUT[:]}, } for c in cases { p := ccitt_default_params() p.k = 0 p.columns = c.columns p.rows = c.rows expect_decode(t, c.name, c.data, p, c.want) } } @(test) test_ccitt_fax_page :: proc(t: ^testing.T) { // The same page encoded two different ways by libtiff must come back out // of the two decoding paths identically. A mistake in one run-length // table shows up here even when the 2-D coder is stepping over it. p := ccitt_default_params() p.columns = 1728 p.rows = 6 p.k = -1 expect_decode(t, "1728-wide page, group 4", PAGE_G4[:], p, PAGE_OUT[:]) p.k = 0 expect_decode(t, "1728-wide page, group 3", PAGE_G3[:], p, PAGE_OUT[:]) // /Rows absent: the EOFB and RTC that libtiff appends have to end it in // the right place on their own. p.rows = 0 p.k = -1 expect_decode(t, "group 4, rows unknown", PAGE_G4[:], p, PAGE_OUT[:]) } @(test) test_ccitt_max_changing_elements :: proc(t: ^testing.T) { // 1728 alternating pixels is the most changing elements a standard fax // row can hold: 1728 of them, against scratch lines sized for the row. // Anything that miscounted those lines overruns here. data: [dynamic]byte defer delete(data) acc: u32 = 0 n_bits := 0 put :: proc(data: ^[dynamic]byte, acc: ^u32, n_bits: ^int, code: u32, n: int) { acc^ = acc^ << uint(n) | code n_bits^ += n for n_bits^ >= 8 { append(data, byte(acc^ >> uint(n_bits^ - 8))) n_bits^ -= 8 } } for _ in 0 ..< 864 { put(&data, &acc, &n_bits, 0b000111, 6) // white 1 put(&data, &acc, &n_bits, 0b010, 3) // black 1 } if n_bits > 0 { append(&data, byte(acc << uint(8 - n_bits))) } p := ccitt_default_params() p.k = 0 p.columns = 1728 p.rows = 1 got, err := ccitt_decode(data[:], p) defer delete(got) testing.expect_value(t, err, Error.None) testing.expect_value(t, len(got), 216) for b, i in got { testing.expectf(t, b == 0xAA, "byte %d is %02X, want AA", i, b) } } @(test) test_ccitt_mixed_k :: proc(t: ^testing.T) { p := ccitt_default_params() p.k = 4 p.columns = 8 expect_decode(t, "1-D then 2-D", MR_MIXED[:], p, MR_MIXED_OUT[:]) expect_decode(t, "1-D, V0, horizontal", MR_MIXED3[:], p, MR_MIXED3_OUT[:]) } @(test) test_ccitt_byte_align :: proc(t: ^testing.T) { p := ccitt_default_params() p.k = 0 p.columns = 8 p.byte_align = true expect_decode(t, "byte aligned rows", MH_BYTE_ALIGN[:], p, MH_BYTE_ALIGN_OUT[:]) // Without the flag the second row is read starting at bit 5, where the // padding of the first row is, so it decodes to something else entirely. // The point is only that it still terminates and returns rows. p.byte_align = false got, err := ccitt_decode(MH_BYTE_ALIGN[:], p) defer delete(got) testing.expect_value(t, err, Error.None) testing.expect(t, len(got) > 0) } @(test) test_ccitt_black_is_1 :: proc(t: ^testing.T) { p := ccitt_default_params() p.k = -1 p.columns = 8 p.rows = 12 p.black_is_1 = true expect_decode(t, "black is 1", G4_FULL[:], p, G4_FULL_OUT_INVERTED[:]) } @(test) test_ccitt_default_columns :: proc(t: ^testing.T) { // /Columns absent means 1728, so one all-white row is 216 bytes. // 1728 = 1728 make-up + 0 terminating. data := [?]byte{0b0100_1101, 0b1001_1010, 0b1000_0000} p := ccitt_default_params() p.columns = 0 p.rows = 1 got, err := ccitt_decode(data[:], p) defer delete(got) testing.expect_value(t, err, Error.None) testing.expect_value(t, len(got), 216) for b, i in got { testing.expectf(t, b == 0xFF, "byte %d is %02X, want FF", i, b) } } // --------------------------------------------------------------------------- // Tolerances // --------------------------------------------------------------------------- @(test) test_ccitt_row_length_tolerance :: proc(t: ^testing.T) { p := ccitt_default_params() p.k = 0 p.columns = 8 // A run longer than the row is clipped and the row is still produced. expect_decode(t, "over-long run", MH_OVERLONG[:], p, MH_OVERLONG_OUT[:]) // A row whose data ends early keeps the colour it had reached. expect_decode(t, "short row", MH_SHORT[:], p, MH_SHORT_OUT[:]) } @(test) test_ccitt_rows_limit :: proc(t: ^testing.T) { p := ccitt_default_params() p.k = -1 p.columns = 8 // Fewer rows than the data holds: decoding stops at /Rows. p.rows = 3 expect_decode(t, "rows = 3", G4_FULL[:], p, G4_FULL_OUT[:3]) // More rows than the data holds: the rows that decoded are returned // rather than an error, and the caller can tell from the length. p.rows = 500 got, err := ccitt_decode(G4_FULL[:], p) defer delete(got) testing.expect_value(t, err, Error.None) testing.expect_value(t, len(got), 12) // An absurd /Rows is a clamp, not an allocation. p.rows = 1 << 40 got2, err2 := ccitt_decode(G4_FULL[:], p) defer delete(got2) testing.expect_value(t, err2, Error.None) testing.expect_value(t, len(got2), 12) } @(test) test_ccitt_absurd_columns :: proc(t: ^testing.T) { // /Columns is attacker-controlled: it must be clamped before it sizes // either the scratch lines or the output. for columns in ([?]int{-1, 0, 1, 1 << 30, max(int)}) { p := ccitt_default_params() p.k = -1 p.columns = columns p.rows = 1 got, err := ccitt_decode(G4_FULL[:], p) defer delete(got) testing.expectf(t, err == .None || err == .Bad_Ccitt_Data, "columns %d: %v", columns, error_string(err)) testing.expectf(t, len(got) <= MAX_CCITT_BYTES, "columns %d: %d bytes", columns, len(got)) } } @(test) test_ccitt_no_data :: proc(t: ^testing.T) { p := ccitt_default_params() p.columns = 8 // Nothing at all. got, err := ccitt_decode(nil, p) testing.expect_value(t, err, Error.Bad_Ccitt_Data) testing.expect_value(t, len(got), 0) // All zeros: no run code is all zeros, so not one row can be made. zeros := [16]byte{} got2, err2 := ccitt_decode(zeros[:], p) defer delete(got2) testing.expect_value(t, err2, Error.Bad_Ccitt_Data) // The same in Group 4, where seven zero bits is not a mode code. p.k = -1 got3, err3 := ccitt_decode(zeros[:], p) defer delete(got3) testing.expect_value(t, err3, Error.Bad_Ccitt_Data) } @(test) test_ccitt_truncated :: proc(t: ^testing.T) { // Every prefix of a good image must decode to some prefix of the rows, // never hang and never return more than the whole image. for n in 0 ..= len(G4_FULL) { p := ccitt_default_params() p.k = -1 p.columns = 8 p.rows = 12 got, err := ccitt_decode(G4_FULL[:n], p) defer delete(got) testing.expectf(t, err == .None || err == .Bad_Ccitt_Data, "prefix %d: %v", n, error_string(err)) testing.expectf(t, len(got) <= 12, "prefix %d: %d rows", n, len(got)) // Whatever survived is a prefix of the real image, except possibly // the last row, which may have been cut off mid-way. whole := max(len(got) - 1, 0) testing.expectf(t, slice.equal(got[:whole], G4_FULL_OUT[:whole]), "prefix %d: % 02X", n, got) } } @(test) test_ccitt_bit_flips :: proc(t: ^testing.T) { // Corrupt data must still terminate, must not read out of bounds and must // not hand back more rows than were asked for. What it decodes to is not // interesting; that it always answers is. for k in ([?]int{-1, 0, 4}) { for i in 0 ..< len(G4_FULL) * 8 { data: [len(G4_FULL)]byte copy(data[:], G4_FULL[:]) data[i / 8] ~= 1 << uint(7 - i % 8) p := ccitt_default_params() p.k = k p.columns = 8 p.rows = 12 p.byte_align = i % 3 == 0 got, err := ccitt_decode(data[:], p) defer delete(got) testing.expectf(t, err == .None || err == .Bad_Ccitt_Data, "k %d bit %d: %v", k, i, error_string(err)) testing.expectf(t, len(got) <= 12, "k %d bit %d: %d bytes", k, i, len(got)) } } } @(test) test_ccitt_hostile_streams :: proc(t: ^testing.T) { Case :: struct { name: string, data: []byte, k: int, } // 0xFF is V0 eight times over, 0x11 is pass mode, 0x24 is horizontal // mode with the shortest black codes, 0x08 is VL2/VL3 territory. Each of // these repeated is a stream that only a fuel counter stops. all_v0 := [64]byte{} all_pass := [64]byte{} all_h := [64]byte{} all_vl := [64]byte{} for i in 0 ..< 64 { all_v0[i] = 0xFF all_pass[i] = 0x11 all_h[i] = 0x24 all_vl[i] = 0x08 } cases := []Case{ {"all V0", all_v0[:], -1}, {"all pass", all_pass[:], -1}, {"all horizontal", all_h[:], -1}, {"all VL", all_vl[:], -1}, {"all V0 as 1-D", all_v0[:], 0}, {"all pass as mixed", all_pass[:], 4}, } for c in cases { for columns in ([?]int{1, 8, 1728}) { p := ccitt_default_params() p.k = c.k p.columns = columns p.rows = 0 got, err := ccitt_decode(c.data, p) defer delete(got) testing.expectf(t, err == .None || err == .Bad_Ccitt_Data, "%s at %d columns: %v", c.name, columns, error_string(err)) row_bytes := (columns + 7) / 8 testing.expectf(t, len(got) <= len(c.data) * 8 * row_bytes, "%s at %d columns: %d bytes from %d", c.name, columns, len(got), len(c.data)) } } } @(test) test_ccitt_extension_mode_stops :: proc(t: ^testing.T) { // 0000001 is the 2-D extension escape (uncompressed mode). No PDF // producer emits it, and guessing at what follows would desynchronise // the whole image, so the row it appears in ends the image. // Here: one V0 row, then an extension. data := [?]byte{0b1000_0001, 0b0000_0000} p := ccitt_default_params() p.k = -1 p.columns = 8 got, err := ccitt_decode(data[:], p) defer delete(got) testing.expect_value(t, err, Error.None) testing.expect_value(t, len(got), 1) testing.expect_value(t, got[0], byte(0xFF)) } @(test) test_ccitt_allocator_is_honoured :: proc(t: ^testing.T) { // The result must come from the allocator the caller named and not from // context.allocator. filters.odin names a scratch allocator it then frees, // so a result that quietly came from somewhere else would be a leak. got, err := ccitt_decode(G4_FULL[:], {k = -1, columns = 8, rows = 12, end_of_block = true}, context.allocator) defer delete(got) testing.expect_value(t, err, Error.None) testing.expect(t, slice.equal(got, G4_FULL_OUT[:])) } // /Rows is a claim, not a measurement: ccitt_decode reserves a buffer for the // rows the dictionary promises and then decodes the rows the data actually // holds. A file that promises twenty thousand and delivers twelve would leave // the difference in the Document arena for good, since an arena cannot take a // buffer back and its `delete` is a silent no-op. apply_filter is where that // is prevented -- the decode runs against scratch and only the finished rows // are copied across -- so the test sits on the boundary rather than on // ccitt_decode, which is entitled to over-reserve as long as it does so with // memory somebody can free. @(test) test_ccitt_row_buffer_stays_out_of_the_document_arena :: proc(t: ^testing.T) { doc := filter_test_doc() defer filter_test_doc_free(doc) // The G4 image padded with trailing zeroes, so that the row cap the // decoder derives from the data length does not clamp /Rows back down. // The EOFB inside the image still ends it after twelve rows. data := make([]byte, 4096) defer delete(data) copy(data, G4_FULL[:]) parms := new_dict(doc.allocator) dict_set(parms, "K", i64(-1)) dict_set(parms, "Columns", i64(8)) dict_set(parms, "Rows", i64(20000)) before := document_memory_used(doc) out, err := apply_filter(doc, "CCITTFaxDecode", data, parms, doc.allocator) testing.expect_value(t, err, Error.None) testing.expect(t, slice.equal(out, G4_FULL_OUT[:]), "the image did not decode") growth := document_memory_used(doc) - before testing.expectf(t, growth <= len(out) + FT_ARENA_SLACK, "the arena grew by %d bytes for %d bytes of output", growth, len(out)) }