1054 lines
41 KiB
Odin
1054 lines
41 KiB
Odin
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))
|
||
|
|
}
|