code wiki / (root) / nx_png_dynamic_huffman_test.nx

nx_png_dynamic_huffman_test.nx source

↩ module page · 166 lines · 5225 B

1// nx_png_dynamic_huffman_test.nx -- regression smoke proving nx_png_decode 2// can consume a real-world dynamic-Huffman zlib stream (the format 3// every gen-img PNG uses). 4// 5// SURFACED 2026-05-18 via empirical run: nx_image_grade_cli reports 6// IGV2_ERR_DECODER=3 on any real PNG. The companion smoke 7// nx_deflate_huffman_test.nx proved nx_deflate_inflate handles the 8// SAME 61-byte raw DEFLATE bytes when called directly. So the bug 9// is in nx_png_decode (or in how it hands IDAT bytes to nx_zlib_inflate). 10// 11// Fixture: 124-byte PNG built by python zlib.compress(raw, 6) where 12// raw = 50 scanlines of "0x00 [200,170,150]*50". IDAT inner zlib 13// stream = 67 bytes (0x78 0x9c header + 61 raw DEFLATE + 4 adler). 14// Expected decode: width=50, height=50, pixels_size = 50*50*3 = 7500. 15 16import "nx_syscalls.nx" 17import "nx_runtime.nx" 18import "nx_png_decoder.nx" 19 20func main() -> nx_int { 21 let png_in: *u8 = (sys_mmap(128)) as *u8 22 23 png_in[0] = 0x89 as u8 24 png_in[1] = 0x50 as u8 25 png_in[2] = 0x4e as u8 26 png_in[3] = 0x47 as u8 27 png_in[4] = 0x0d as u8 28 png_in[5] = 0x0a as u8 29 png_in[6] = 0x1a as u8 30 png_in[7] = 0x0a as u8 31 png_in[8] = 0x00 as u8 32 png_in[9] = 0x00 as u8 33 png_in[10] = 0x00 as u8 34 png_in[11] = 0x0d as u8 35 png_in[12] = 0x49 as u8 36 png_in[13] = 0x48 as u8 37 png_in[14] = 0x44 as u8 38 png_in[15] = 0x52 as u8 39 png_in[16] = 0x00 as u8 40 png_in[17] = 0x00 as u8 41 png_in[18] = 0x00 as u8 42 png_in[19] = 0x32 as u8 43 png_in[20] = 0x00 as u8 44 png_in[21] = 0x00 as u8 45 png_in[22] = 0x00 as u8 46 png_in[23] = 0x32 as u8 47 png_in[24] = 0x08 as u8 48 png_in[25] = 0x02 as u8 49 png_in[26] = 0x00 as u8 50 png_in[27] = 0x00 as u8 51 png_in[28] = 0x00 as u8 52 png_in[29] = 0x91 as u8 53 png_in[30] = 0x5d as u8 54 png_in[31] = 0x1f as u8 55 png_in[32] = 0xe6 as u8 56 png_in[33] = 0x00 as u8 57 png_in[34] = 0x00 as u8 58 png_in[35] = 0x00 as u8 59 png_in[36] = 0x43 as u8 60 png_in[37] = 0x49 as u8 61 png_in[38] = 0x44 as u8 62 png_in[39] = 0x41 as u8 63 png_in[40] = 0x54 as u8 64 png_in[41] = 0x78 as u8 65 png_in[42] = 0x9c as u8 66 png_in[43] = 0xed as u8 67 png_in[44] = 0xce as u8 68 png_in[45] = 0x41 as u8 69 png_in[46] = 0x0d as u8 70 png_in[47] = 0x00 as u8 71 png_in[48] = 0x20 as u8 72 png_in[49] = 0x0c as u8 73 png_in[50] = 0x00 as u8 74 png_in[51] = 0xb1 as u8 75 png_in[52] = 0xf9 as u8 76 png_in[53] = 0x17 as u8 77 png_in[54] = 0x80 as u8 78 png_in[55] = 0x08 as u8 79 png_in[56] = 0x94 as u8 80 png_in[57] = 0x4c as u8 81 png_in[58] = 0x16 as u8 82 png_in[59] = 0x32 as u8 83 png_in[60] = 0xb8 as u8 84 png_in[61] = 0x47 as u8 85 png_in[62] = 0x93 as u8 86 png_in[63] = 0x0a as u8 87 png_in[64] = 0xe8 as u8 88 png_in[65] = 0xec as u8 89 png_in[66] = 0x3d as u8 90 png_in[67] = 0x41 as u8 91 png_in[68] = 0xf3 as u8 92 png_in[69] = 0x7d as u8 93 png_in[70] = 0xa0 as u8 94 png_in[71] = 0xa5 as u8 95 png_in[72] = 0xa5 as u8 96 png_in[73] = 0xa5 as u8 97 png_in[74] = 0xa5 as u8 98 png_in[75] = 0xa5 as u8 99 png_in[76] = 0xd5 as u8 100 png_in[77] = 0xa0 as u8 101 png_in[78] = 0xa5 as u8 102 png_in[79] = 0xa5 as u8 103 png_in[80] = 0x55 as u8 104 png_in[81] = 0xa0 as u8 105 png_in[82] = 0xa5 as u8 106 png_in[83] = 0xa5 as u8 107 png_in[84] = 0x55 as u8 108 png_in[85] = 0xa0 as u8 109 png_in[86] = 0xa5 as u8 110 png_in[87] = 0xa5 as u8 111 png_in[88] = 0x55 as u8 112 png_in[89] = 0xa0 as u8 113 png_in[90] = 0xa5 as u8 114 png_in[91] = 0xa5 as u8 115 png_in[92] = 0x55 as u8 116 png_in[93] = 0xa0 as u8 117 png_in[94] = 0xa5 as u8 118 png_in[95] = 0xa5 as u8 119 png_in[96] = 0x55 as u8 120 png_in[97] = 0xa0 as u8 121 png_in[98] = 0xa5 as u8 122 png_in[99] = 0xa5 as u8 123 png_in[100] = 0x55 as u8 124 png_in[101] = 0x10 as u8 125 png_in[102] = 0x6d as u8 126 png_in[103] = 0x3d as u8 127 png_in[104] = 0xac as u8 128 png_in[105] = 0x61 as u8 129 png_in[106] = 0xd7 as u8 130 png_in[107] = 0x3e as u8 131 png_in[108] = 0x53 as u8 132 png_in[109] = 0xb2 as u8 133 png_in[110] = 0x87 as u8 134 png_in[111] = 0xed as u8 135 png_in[112] = 0x00 as u8 136 png_in[113] = 0x00 as u8 137 png_in[114] = 0x00 as u8 138 png_in[115] = 0x00 as u8 139 png_in[116] = 0x49 as u8 140 png_in[117] = 0x45 as u8 141 png_in[118] = 0x4e as u8 142 png_in[119] = 0x44 as u8 143 png_in[120] = 0xae as u8 144 png_in[121] = 0x42 as u8 145 png_in[122] = 0x60 as u8 146 png_in[123] = 0x82 as u8 147 148 let r: *NxPngResult = nx_png_decode(png_in, 124) 149 if r == (0 as *NxPngResult) { return 1 } 150 // Return the actual error code so we can see exactly what failed. 151 // Add 100 so non-zero error codes don't collide with our test 152 // assertion numbers. 153 if r.error_code != NX_PNG_OK { return 100 + r.error_code } 154 155 if r.header == (0 as *NxPngHeader) { return 2 } 156 if r.header.width != 50 { return 3 } 157 if r.header.height != 50 { return 4 } 158 if r.n_channels != 3 { return 5 } 159 if r.pixels_size != 7500 { return 6 } 160 // First unfiltered pixel: (200, 170, 150) = (0xc8, 0xaa, 0x96) 161 if (r.pixels[0] as nx_int) & 255 != 0xc8 { return 10 } 162 if (r.pixels[1] as nx_int) & 255 != 0xaa { return 11 } 163 if (r.pixels[2] as nx_int) & 255 != 0x96 { return 12 } 164 165 return 0 166}