nx_gif_animation_gate_t139.nx source
↩ module page · 153 lines · 13245 B
1// nx_gif_animation_gate_t139.nx -- Generates and hashes a 4x4 interlaced GIF image with a predefined color palette.
2import "nx_syscalls.nx"
3import "nx_gate_verdict.nx"
4import "nx_sha256.nx"
5import "nx_gif_decode_t139.nx"
6// append one 3-bit LZW code, LSB-first (GIF bit order); buf must be zeroed (OR-only)
7func gg_putcode(buf: *u8, bitpos: *i64, code: i64) -> i64 {
8 var i: i64=0
9 while i<3 {
10 let bit: i64=(code>>i)&1
11 let bp: i64=bitpos[0]
12 if bit==1 { let by: i64=bp/8; let bo: i64=bp%8; buf[by]=((buf[by] as i64)|(1<<bo)) as u8 }
13 bitpos[0]=bitpos[0]+1
14 i=i+1
15 }
16 return 0
17}
18
19// build a 4x4 GIF89a with a 4-entry GCT (red,green,blue,white) whose pixel-index
20// STORAGE sequence is pixseq[16]; interlaced sets descriptor bit 6. returns length.
21func gg_build_gif(out: *u8, pixseq: *u8, interlaced: i64) -> i64 {
22 out[0]=0x47 as u8; out[1]=0x49 as u8; out[2]=0x46 as u8
23 out[3]=0x38 as u8; out[4]=0x39 as u8; out[5]=0x61 as u8
24 out[6]=4 as u8; out[7]=0 as u8; out[8]=4 as u8; out[9]=0 as u8
25 out[10]=0x81 as u8; out[11]=0 as u8; out[12]=0 as u8
26 out[13]=255 as u8; out[14]=0 as u8; out[15]=0 as u8
27 out[16]=0 as u8; out[17]=255 as u8; out[18]=0 as u8
28 out[19]=0 as u8; out[20]=0 as u8; out[21]=255 as u8
29 out[22]=255 as u8; out[23]=255 as u8; out[24]=255 as u8
30 var o: i64=25
31 out[o]=0x2C as u8
32 out[o+1]=0 as u8; out[o+2]=0 as u8; out[o+3]=0 as u8; out[o+4]=0 as u8
33 out[o+5]=4 as u8; out[o+6]=0 as u8; out[o+7]=4 as u8; out[o+8]=0 as u8
34 if interlaced==1 { out[o+9]=0x40 as u8 } else { out[o+9]=0 as u8 }
35 o=o+10
36 out[o]=2 as u8; o=o+1
37 let lzbuf: *u8=sys_mmap(256)
38 let bitpos: *i64=sys_mmap(8) as *i64
39 bitpos[0]=0
40 var p: i64=0
41 while p<16 { gg_putcode(lzbuf,bitpos,4); gg_putcode(lzbuf,bitpos,pixseq[p] as i64); p=p+1 }
42 gg_putcode(lzbuf,bitpos,5)
43 let nbytes: i64=(bitpos[0]+7)/8
44 out[o]=nbytes as u8; o=o+1
45 var q: i64=0
46 while q<nbytes { out[o]=lzbuf[q]; o=o+1; q=q+1 }
47 out[o]=0 as u8; o=o+1
48 out[o]=0x3B as u8; o=o+1
49 return o
50}
51
52
53func gb_hash(raw: *u8, n: i64, expected: *u8) -> i64 {
54 let wn: i64=sha256_workspace_bytes(); let ws: *u8=gif_own(wn+64)
55 if ws==(0 as *u8) { return 0 }
56 let digest: *u8=((ws as i64)+wn) as *u8
57 var ok: i64=1
58 if sha256_init_workspace(ws,wn)!=0 { ok=0 } else { sha256_update(ws as *Sha256,raw,n); sha256_final(ws as *Sha256,digest)
59 let hex: *u8="0123456789abcdef";var i: i64=0;while i<32 { let v: i64=digest[i] as i64;if hex[v>>4]!=expected[i*2] || hex[v&15]!=expected[i*2+1] { ok=0 } i=i+1 }
60 }
61 if gif_release(ws,wn+64)!=0 { ok=0 } return ok
62}
63func gb_pages() -> i64 {
64 let buf: *u8=gif_own(512);if buf==(0 as *u8) { return 0-1 }
65 let fd: i64=sys_openat_rd("/proc/self/statm");if fd<0 { gif_release(buf,512);return 0-1 }
66 let n: i64=sys_read(fd,buf,511);let rc: i64=sys_close(fd);var value: i64=0;var i: i64=0
67 if n<=0 || rc!=0 { gif_release(buf,512);return 0-1 }
68 while i<n {let c: i64=buf[i] as i64;if c<48 || c>57 {break}value=value*10+c-48;i=i+1}
69 if gif_release(buf,512)!=0 {return 0-1}return value
70}
71func ga_expected(i: i64) -> *u8 {
72 if i==0 { return "0ded56809f61d65a9a4d343d10525372240cc9a98a4e49d284209e238697f1f2" }
73 if i==1 { return "d21eb13e117ec0956a87ce5cd90908e12632a3e3f2c5c4d6918c93143a5e29a2" }
74 if i==2 { return "90b2bdbba8c3f592c312dbbaa310bb27c876af74b2e66f8f762228ed5a976f2a" }
75 if i==3 { return "a80fc9e1df0246afa6b397bcbd2446c445db2084a43b5565a6bafd8c6393d101" }
76 if i==4 { return "2c46a66e6fcd532d0f5fdbcab7de389675b64341b8e3b7e77c8e7aee2072ce10" }
77 if i==5 { return "4d3ef2c0e2b55dac0ba3c2aa2dd74937473d9455848d3b373bfb2afd0878c03b" }
78 if i==6 { return "d52826bf8bd8f9653c0132ae06cb15299a5f09e5e5a4d8404a111ec7e4a28e1a" }
79 if i==7 { return "f7e5f4ef5dc5e8c4229048df3a7b83665905789fd764b90901d0b38830f353f9" }
80 if i==8 { return "97987ecdf546862abcb6ae1a8e13fe8dff1fc8b31a9e04d463f4216b6e825fb1" }
81 if i==9 { return "aa3bc62e68f002f084da15d701c81f5d52563164e226eb57a285b48b5b88d88b" }
82 if i==10 { return "67b1b79ce8ca40957d1b3c458fbcd1995d9c2dc6b6eaf04085ded917e6a798c1" }
83 if i==11 { return "ce8b45188857abfe3ee99bf1e2a9105b130671ce8ef6acd5eedcabfc233ed264" }
84 if i==12 { return "fe3dab0220824008f242d905ef6e461363a27994c676127be9df142bd07e6534" }
85 if i==13 { return "12de180db9edcd443a8f38ffe2f35a32ef69ce80bf968eb0a60f928e6189bc13" }
86 if i==14 { return "632e1a7c9d63bd8ca9ca8ad827b9533e75ed55cc0d4f69379adad18274247dfc" }
87 if i==15 { return "1a344c4cd14fb3f3c2db8810ca11f862727c3e441430ac52ff5e3d071fe891f7" }
88 if i==16 { return "251a2085c7c069b30362ea577c79940736eaad2f54c0fce03f8cda20de517ef8" }
89 if i==17 { return "55c472a245e976617cb3dcbb3e18b55e6f4a7aaf93d3fe3a1e46e64fa9b6c009" }
90 if i==18 { return "eabf25b076dd86d5e691bb9bbec43f2cc52b4eeadcb264b2c7d6f30611336bb2" }
91 if i==19 { return "8ebcbd5cc3ab5cd55dd792eaa897a3b07c8063f7ed9aac4f798ef9a3fb9f809c" }
92 if i==20 { return "5cadb23aa92ca56d5dbf4b3f1609583e07c23fed1d237a82ceadca15fafc0ed2" }
93 if i==21 { return "ab8f21ab682af3011888261f8e49f25cfd8a75ad973488fd739b64a7ddba4148" }
94 if i==22 { return "7200d810ea5d6e9d9ceb67344c08e85049cbe7553871b705c13ef45e193a7666" }
95 if i==23 { return "bc63c823e61e677c49d03517c20bbe76b5bcbcf61ea8f346abbbabf28fb5b70d" }
96 if i==24 { return "b28e97f50ee5434ebd7c8d8d1338320c5ebb063b4c3aa68af25174a507cc7db1" }
97 if i==25 { return "0fc365b7c0d39e8cca2e211d6e6091a3d2b4a943fde43f2a17937f0fe22f62c6" }
98 if i==26 { return "9f13beb1e157aee36772a6b3328ae19ffbe9cc9a3a5b06554308ea2175c6e8cd" }
99 if i==27 { return "226d18da05cfa17139b67aa54452d86785a328a89701444a98c7cb17e8370bc5" }
100 if i==28 { return "626ea16be88064b78131eeef96fa86c6045dcfcb51ae1f2a743a00c241c1f61f" }
101 if i==29 { return "98a6792b43a8ba5510bd3ff9d65523882fd4cee3cd2a8347746f820cfdf838e1" }
102 if i==30 { return "cea0d3f72d74bee2ef48637d9f005650451aca312391106420834f93439f3d8a" }
103 if i==31 { return "12be5de77b484a79bfd3fd6085f2fb1f2d5751e684d88112be1dd3ffe4e93f4a" }
104 if i==32 { return "05e54c519c42d53e3e1b0a310cd3bd4fd7ea99b86af94e57be19296ace25fdc9" }
105 if i==33 { return "aa6a0b59322e43c0b2c0e74b9aa7a0f980cfa2fb56f99553906ae2074fad42f2" }
106 if i==34 { return "094f3fcffae3363a901385887b1c364917276df8c881b7137c1e79d749a8f217" }
107 if i==35 { return "9f4fe23c9f861b1976960e668e6929c1b493a13a2e59cd823a1dda4b7fb0a9cd" }
108 return "invalid"
109}
110func main() -> i64 {
111 let ctr: *i64=sys_mmap(32) as *i64
112 let st: *i64=gif_own(GIF_ANIM_WORDS*8) as *i64; let info: *i64=gif_own(128) as *i64
113 let raw: *u8=gif_own(139);
114 raw[0]=71 as u8;raw[1]=73 as u8;raw[2]=70 as u8;raw[3]=56 as u8;raw[4]=57 as u8;raw[5]=97 as u8;raw[6]=2 as u8;raw[7]=0 as u8;raw[8]=2 as u8;raw[9]=0 as u8;raw[10]=129 as u8;raw[11]=0 as u8;raw[12]=0 as u8;raw[13]=0 as u8;raw[14]=0 as u8;raw[15]=0 as u8;raw[16]=255 as u8;raw[17]=0 as u8;raw[18]=0 as u8;raw[19]=0 as u8;raw[20]=255 as u8;raw[21]=0 as u8;raw[22]=0 as u8;raw[23]=0 as u8;raw[24]=255 as u8;raw[25]=33 as u8;raw[26]=255 as u8;raw[27]=11 as u8;raw[28]=78 as u8;raw[29]=69 as u8;raw[30]=84 as u8;raw[31]=83 as u8;raw[32]=67 as u8;raw[33]=65 as u8;raw[34]=80 as u8;raw[35]=69 as u8;raw[36]=50 as u8;raw[37]=46 as u8;raw[38]=48 as u8;raw[39]=3 as u8;raw[40]=1 as u8;raw[41]=3 as u8;raw[42]=0 as u8;raw[43]=0 as u8;raw[44]=33 as u8;raw[45]=249 as u8;raw[46]=4 as u8;raw[47]=5 as u8;raw[48]=2 as u8;raw[49]=0 as u8;raw[50]=0 as u8;raw[51]=0 as u8;raw[52]=44 as u8;raw[53]=0 as u8;raw[54]=0 as u8;raw[55]=0 as u8;raw[56]=0 as u8;raw[57]=2 as u8;raw[58]=0 as u8;raw[59]=2 as u8;raw[60]=0 as u8;raw[61]=0 as u8;raw[62]=2 as u8;raw[63]=4 as u8;raw[64]=12 as u8;raw[65]=195 as u8;raw[66]=48 as u8;raw[67]=5 as u8;raw[68]=0 as u8;raw[69]=33 as u8;raw[70]=249 as u8;raw[71]=4 as u8;raw[72]=13 as u8;raw[73]=3 as u8;raw[74]=0 as u8;raw[75]=0 as u8;raw[76]=0 as u8;raw[77]=44 as u8;raw[78]=0 as u8;raw[79]=0 as u8;raw[80]=0 as u8;raw[81]=0 as u8;raw[82]=1 as u8;raw[83]=0 as u8;raw[84]=1 as u8;raw[85]=0 as u8;raw[86]=0 as u8;raw[87]=2 as u8;raw[88]=2 as u8;raw[89]=84 as u8;raw[90]=1 as u8;raw[91]=0 as u8;raw[92]=33 as u8;raw[93]=249 as u8;raw[94]=4 as u8;raw[95]=9 as u8;raw[96]=4 as u8;raw[97]=0 as u8;raw[98]=0 as u8;raw[99]=0 as u8;raw[100]=44 as u8;raw[101]=1 as u8;raw[102]=0 as u8;raw[103]=1 as u8;raw[104]=0 as u8;raw[105]=1 as u8;raw[106]=0 as u8;raw[107]=1 as u8;raw[108]=0 as u8;raw[109]=0 as u8;raw[110]=2 as u8;raw[111]=2 as u8;raw[112]=92 as u8;raw[113]=1 as u8;raw[114]=0 as u8;raw[115]=33 as u8;raw[116]=249 as u8;raw[117]=4 as u8;raw[118]=5 as u8;raw[119]=5 as u8;raw[120]=0 as u8;raw[121]=0 as u8;raw[122]=0 as u8;raw[123]=44 as u8;raw[124]=0 as u8;raw[125]=0 as u8;raw[126]=0 as u8;raw[127]=0 as u8;raw[128]=1 as u8;raw[129]=0 as u8;raw[130]=1 as u8;raw[131]=0 as u8;raw[132]=0 as u8;raw[133]=2 as u8;raw[134]=2 as u8;raw[135]=68 as u8;raw[136]=1 as u8;raw[137]=0 as u8;raw[138]=59 as u8;
115 let expected: *u8=gif_own(64);
116 expected[0]=255 as u8;expected[1]=0 as u8;expected[2]=0 as u8;expected[3]=255 as u8;expected[4]=255 as u8;expected[5]=0 as u8;expected[6]=0 as u8;expected[7]=255 as u8;expected[8]=255 as u8;expected[9]=0 as u8;expected[10]=0 as u8;expected[11]=255 as u8;expected[12]=255 as u8;expected[13]=0 as u8;expected[14]=0 as u8;expected[15]=255 as u8;expected[16]=0 as u8;expected[17]=255 as u8;expected[18]=0 as u8;expected[19]=255 as u8;expected[20]=255 as u8;expected[21]=0 as u8;expected[22]=0 as u8;expected[23]=255 as u8;expected[24]=255 as u8;expected[25]=0 as u8;expected[26]=0 as u8;expected[27]=255 as u8;expected[28]=255 as u8;expected[29]=0 as u8;expected[30]=0 as u8;expected[31]=255 as u8;expected[32]=255 as u8;expected[33]=0 as u8;expected[34]=0 as u8;expected[35]=255 as u8;expected[36]=255 as u8;expected[37]=0 as u8;expected[38]=0 as u8;expected[39]=255 as u8;expected[40]=255 as u8;expected[41]=0 as u8;expected[42]=0 as u8;expected[43]=255 as u8;expected[44]=0 as u8;expected[45]=0 as u8;expected[46]=255 as u8;expected[47]=255 as u8;expected[48]=255 as u8;expected[49]=0 as u8;expected[50]=0 as u8;expected[51]=255 as u8;expected[52]=255 as u8;expected[53]=0 as u8;expected[54]=0 as u8;expected[55]=255 as u8;expected[56]=255 as u8;expected[57]=0 as u8;expected[58]=0 as u8;expected[59]=255 as u8;expected[60]=0 as u8;expected[61]=0 as u8;expected[62]=0 as u8;expected[63]=0 as u8;
117 var rc: i64=gif_anim_init(raw,139,st,0)
118 gv_check("animation initializes explicit transparent canvas",rc==1,ctr)
119 var i: i64=0; var good: i64=1; while i<4 {
120 rc=gif_anim_next(st,info); var ok: i64=1
121 if rc!=1 { ok=0 } else { let px: *u8=st[3] as *u8; var p: i64=0; while p<16 { if px[p]!=expected[i*16+p] { ok=0 } p=p+1 } if info[9]!=i+2 { ok=0 } }
122 if ok==0 { good=0 } gv_check("composited synthetic frame equals libvips including disposal",ok,ctr); i=i+1
123 }
124 gv_check("finite raw loop count3 and exact14cs cumulative duration",st[17]==3 && st[14]==14,ctr)
125 gv_check("trailer completes iteration",gif_anim_next(st,info)==GIF_DONE,ctr)
126 gv_check("completed iterator is stable",gif_anim_next(st,info)==GIF_DONE,ctr)
127 gv_check("close releases owned animation buffers",gif_anim_close(st)==0,ctr)
128 gv_check("close is idempotent with caller state alive",gif_anim_close(st)==0,ctr)
129 rc=gif_anim_init(raw,139-1,st,0);i=0;while i<4 && rc==1 { rc=gif_anim_next(st,info);i=i+1 }
130 gv_check("missing trailer refuses complete-animation acceptance",gif_anim_next(st,info)==GIF_BAD_FORMAT,ctr);gif_anim_close(st)
131 let wh: *i64=gif_own(16) as *i64; let bad: *u8=gif_convert_checked(raw,139,wh,info,2)
132 gv_check("unsupported channel count refuses before decode/write",bad==(0 as *u8) && info[15]==GIF_BAD_ARGUMENT && wh[0]==0 && wh[1]==0,ctr)
133 let before: i64=gb_pages();i=0;good=1;while i<100 {
134 rc=gif_anim_init(raw,139,st,0);if rc!=1 {good=0};var j: i64=0;while j<4 && rc==1{rc=gif_anim_next(st,info);j=j+1};if gif_anim_close(st)!=0{good=0};i=i+1
135 }let after: i64=gb_pages();gv_puts("animation pages before=");gv_num(before);gv_puts(" after=");gv_num(after);gv_puts("\n")
136 gv_check("100 four-frame caller cycles reclaim canvas restore and frame scratch",good==1 && before==after,ctr)
137 let lb: *i64=sys_mmap(16) as *i64;let original: *u8=sys_read_file("/tmp/strut-2d17175184e088f9.gif",lb)
138 good=0;if original!=(0 as *u8){good=gb_hash(original,lb[0],"2d17175184e088f93cff9f165644c93f0a9bbfb77757ae154d28e5602b747811")}
139 gv_check("original encoded identity pinned",good==1 && lb[0]==1339248,ctr)
140 if good==1 {
141 rc=gif_anim_init(original,lb[0],st,0);gv_check("original animation initializes",rc==1,ctr)
142 i=0;while i<36 {
143 rc=gif_anim_next(st,info);var ok: i64=0;if rc==1{ok=gb_hash(st[3] as *u8,st[7],ga_expected(i));if st[5]!=268 || st[6]!=402 || info[9]!=5 {ok=0}}
144 gv_puts("original frame=");gv_num(i);gv_puts(" ");gv_check("complete RGBA bytes and50ms agree with libvips",ok,ctr);i=i+1
145 }
146 gv_check("original36frames180cs infinite-loopmetadata",st[13]==36 && st[14]==180 && st[17]==0,ctr)
147 gv_check("original exact trailer and no extra frame",gif_anim_next(st,info)==GIF_DONE,ctr)
148 gv_check("original animation release succeeds",gif_anim_close(st)==0,ctr)
149 }
150 gif_release(raw,139);gif_release(expected,64);gif_release(info as *u8,128);gif_release(st as *u8,GIF_ANIM_WORDS*8);gif_release(wh as *u8,16)
151 gv_puts("GIF-ANIMATION-GATE ");gv_num(ctr[0]);gv_puts("/");gv_num(ctr[1]);gv_puts("\n")
152 if ctr[0]!=ctr[1] {return 1}return 0
153}