blob: 24a4fcd82c9317c12c5c64d301b9cbe1f84f7209 [file] [log] [blame]
Nigel Tao981bf192018-05-23 12:17:55 +10001// Copyright 2018 The Wuffs Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Nigel Tao788479d2021-08-22 10:52:51 +100015//go:build ignore
Nigel Tao981bf192018-05-23 12:17:55 +100016// +build ignore
17
18package main
19
20// make-artificial.go makes test data in various formats.
21//
22// See test/data/artificial/*.make.txt for examples.
23//
24// Usage: go run make-artificial.go < foo.make.txt
25
26import (
27 "bytes"
28 "compress/lzw"
Nigel Tao6c2a5912021-11-16 11:00:48 +110029 "compress/zlib"
Nigel Tao981bf192018-05-23 12:17:55 +100030 "fmt"
Nigel Tao6c2a5912021-11-16 11:00:48 +110031 "hash/crc32"
Nigel Tao226c4762021-08-22 11:05:43 +100032 "io"
Nigel Tao981bf192018-05-23 12:17:55 +100033 "os"
Nigel Tao92ea6312022-01-09 10:41:31 +110034 "sort"
Nigel Tao981bf192018-05-23 12:17:55 +100035 "strconv"
36 "strings"
37)
38
39type stateFunc func(line string) (stateFunc, error)
40
41type repeat struct {
42 count uint32
43 remaining string
44}
45
46var (
47 state stateFunc
48 repeats []repeat
49 out []byte
50 formats = map[string]stateFunc{}
51)
52
53func main() {
54 if err := main1(); err != nil {
55 os.Stderr.WriteString(err.Error() + "\n")
56 os.Exit(1)
57 }
58}
59
60func main1() error {
Nigel Tao226c4762021-08-22 11:05:43 +100061 stdin, err := io.ReadAll(os.Stdin)
Nigel Tao981bf192018-05-23 12:17:55 +100062 if err != nil {
63 return err
64 }
65 s := string(stdin)
66 for remaining := ""; len(s) > 0; s, remaining = remaining, "" {
67 if i := strings.IndexByte(s, '\n'); i >= 0 {
68 s, remaining = s[:i], s[i+1:]
69 }
70 s = strings.TrimSpace(s)
71 if s == "" || s[0] == '#' {
72 continue
73 }
74
75 if state == nil {
76 const m = "make "
77 if !strings.HasPrefix(s, m) {
78 return fmt.Errorf(`input must start with "make foo"`)
79 }
80 s = s[len(m):]
81 state = formats[s]
82 if state == nil {
83 return fmt.Errorf("unsupported format %q", s)
84 }
85 continue
86 }
87
88 const rep = "repeat "
89 if strings.HasPrefix(s, rep) {
90 args := s[len(rep):]
91 count, args, ok := parseNum(args)
92 if !ok || count <= 0 || args != "[" {
93 return fmt.Errorf("bad repeat command: %q", s)
94 }
95 repeats = append(repeats, repeat{
96 count: count,
97 remaining: remaining,
98 })
99 continue
100 }
101
102 if s == "]" {
103 if len(repeats) <= 0 {
104 return fmt.Errorf(`unbalanced close-repeat command: "]"`)
105 }
106 i := len(repeats) - 1
107 repeats[i].count--
108 if repeats[i].count == 0 {
109 repeats = repeats[:i]
110 } else {
111 remaining = repeats[i].remaining
112 }
113 continue
114 }
115
116 state, err = state(s)
117 if err != nil {
118 return err
119 }
120 if state == nil {
121 return fmt.Errorf("bad state transition")
122 }
123 }
124
Nigel Tao12347602019-06-16 23:15:05 +1000125 if state == nil {
126 return fmt.Errorf("no 'make' line")
127 } else {
128 state, err = state("")
129 if err != nil {
130 return err
131 }
132 }
133
Nigel Tao981bf192018-05-23 12:17:55 +1000134 _, err = os.Stdout.Write(out)
135 return err
136}
137
138// ----
139
140func appendU16LE(b []byte, u uint16) []byte {
141 return append(b, uint8(u), uint8(u>>8))
142}
143
Nigel Tao6c2a5912021-11-16 11:00:48 +1100144func appendU32BE(b []byte, u uint32) []byte {
145 return append(b, uint8(u>>24), uint8(u>>16), uint8(u>>8), uint8(u))
146}
147
Nigel Tao981bf192018-05-23 12:17:55 +1000148func log2(u uint32) (i int32) {
149 for i, pow := uint32(0), uint32(1); i < 32; i, pow = i+1, pow<<1 {
150 if u == pow {
151 return int32(i)
152 }
153 if u < pow {
154 break
155 }
156 }
157 return -1
158}
159
160func parseHex(s string) (num uint32, remaining string, ok bool) {
161 if i := strings.IndexByte(s, ' '); i >= 0 {
162 s, remaining = s[:i], s[i+1:]
163 for len(remaining) > 0 && remaining[0] == ' ' {
164 remaining = remaining[1:]
165 }
166 }
167
168 if len(s) < 2 || s[0] != '0' || s[1] != 'x' {
169 return 0, "", false
170 }
171 s = s[2:]
172
173 u, err := strconv.ParseUint(s, 16, 32)
174 if err != nil {
175 return 0, "", false
176 }
177 return uint32(u), remaining, true
178}
179
180func parseNum(s string) (num uint32, remaining string, ok bool) {
181 if i := strings.IndexByte(s, ' '); i >= 0 {
182 s, remaining = s[:i], s[i+1:]
183 for len(remaining) > 0 && remaining[0] == ' ' {
184 remaining = remaining[1:]
185 }
186 }
187
188 u, err := strconv.ParseUint(s, 10, 32)
189 if err != nil {
190 return 0, "", false
191 }
192 return uint32(u), remaining, true
193}
194
195func reverse(x uint32, n uint32) uint32 {
196 x = uint32(reverse8[0xFF&(x>>0)])<<24 |
197 uint32(reverse8[0xFF&(x>>8)])<<16 |
198 uint32(reverse8[0xFF&(x>>16)])<<8 |
199 uint32(reverse8[0xFF&(x>>24)])<<0
200 return x >> (32 - n)
201}
202
203var reverse8 = [256]byte{
204 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, // 0x00 - 0x07
205 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, // 0x08 - 0x0F
206 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, // 0x10 - 0x17
207 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, // 0x18 - 0x1F
208 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, // 0x20 - 0x27
209 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, // 0x28 - 0x2F
210 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, // 0x30 - 0x37
211 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, // 0x38 - 0x3F
212 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, // 0x40 - 0x47
213 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, // 0x48 - 0x4F
214 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, // 0x50 - 0x57
215 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, // 0x58 - 0x5F
216 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, // 0x60 - 0x67
217 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, // 0x68 - 0x6F
218 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, // 0x70 - 0x77
219 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, // 0x78 - 0x7F
220 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, // 0x80 - 0x87
221 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, // 0x88 - 0x8F
222 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, // 0x90 - 0x97
223 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, // 0x98 - 0x9F
224 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, // 0xA0 - 0xA7
225 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, // 0xA8 - 0xAF
226 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, // 0xB0 - 0xB7
227 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, // 0xB8 - 0xBF
228 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, // 0xC0 - 0xC7
229 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, // 0xC8 - 0xCF
230 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, // 0xD0 - 0xD7
231 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, // 0xD8 - 0xDF
232 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, // 0xE0 - 0xE7
233 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, // 0xE8 - 0xEF
234 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, // 0xF0 - 0xF7
235 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, // 0xF8 - 0xFF
236}
237
238// ----
239
240func init() {
241 formats["deflate"] = stateDeflate
242}
243
Nigel Tao12347602019-06-16 23:15:05 +1000244var deflateCodeOrder = [19]uint32{
245 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
246}
247
Nigel Tao92ea6312022-01-09 10:41:31 +1100248var deflateHuffmanNames = [4]string{
249 0: "Unused",
250 1: "CodeLength",
251 2: "Literal/Length",
252 3: "Distance",
253}
254
Nigel Tao12347602019-06-16 23:15:05 +1000255type deflateHuffmanTable map[uint32]string
256
Nigel Tao981bf192018-05-23 12:17:55 +1000257var deflateGlobals struct {
258 bncData []byte
259 stream deflateBitStream
Nigel Tao12347602019-06-16 23:15:05 +1000260
Nigel Tao92ea6312022-01-09 10:41:31 +1100261 // Dynamic Huffman state. whichHuffman indexes the huffmans array. See also
262 // deflateHuffmanNames.
Nigel Taoff7bffc2019-10-13 23:23:48 +1100263 whichHuffman uint32
Nigel Tao92ea6312022-01-09 10:41:31 +1100264 huffmans [4]deflateHuffmanTable
Nigel Taoff7bffc2019-10-13 23:23:48 +1100265
266 // DHH (Dynamic Huffman, inside a Huffman table) state.
Nigel Tao42de49a2022-01-09 10:45:47 +1100267 prevLine string
268 etcetera bool
269 incomplete bool
Nigel Tao12347602019-06-16 23:15:05 +1000270}
271
272func deflateGlobalsClearDynamicHuffmanState() {
Nigel Tao12347602019-06-16 23:15:05 +1000273 deflateGlobals.whichHuffman = 0
274 deflateGlobals.huffmans = [4]deflateHuffmanTable{}
Nigel Taoff7bffc2019-10-13 23:23:48 +1100275 deflateGlobals.prevLine = ""
276 deflateGlobals.etcetera = false
Nigel Tao42de49a2022-01-09 10:45:47 +1100277 deflateGlobals.incomplete = false
Nigel Taoff7bffc2019-10-13 23:23:48 +1100278}
279
280func deflateGlobalsCountCodes() (numLCodes uint32, numDCodes uint32, numCLCodeLengths uint32, retErr error) {
281 for k := range deflateGlobals.huffmans[2] {
282 if numLCodes < (k + 1) {
283 numLCodes = (k + 1)
284 }
285 }
286
287 for k := range deflateGlobals.huffmans[3] {
288 if numDCodes < (k + 1) {
289 numDCodes = (k + 1)
290 }
291 }
292
293 for k := range deflateGlobals.huffmans[1] {
294 if (k < 0) || (18 < k) {
295 return 0, 0, 0, fmt.Errorf("bad CodeLength: %d", k)
296 }
297 }
298 for i := len(deflateCodeOrder) - 1; i >= 0; i-- {
299 cl := deflateCodeOrder[i]
300 if _, ok := deflateGlobals.huffmans[1][cl]; ok {
301 numCLCodeLengths = uint32(i + 1)
302 break
303 }
304 }
305 if numCLCodeLengths < 4 {
306 numCLCodeLengths = 4
307 }
308
309 return numLCodes, numDCodes, numCLCodeLengths, nil
Nigel Tao12347602019-06-16 23:15:05 +1000310}
311
Nigel Tao92ea6312022-01-09 10:41:31 +1100312func deflateGlobalsIsHuffmanCanonical() bool {
313 g := &deflateGlobals
314
315 // Gather the code+bitstring pairs. Deflate bitstrings cannot be longer
316 // than 15 bits.
317 type pair struct {
318 k uint32
319 v string
320 }
321 pairs := []pair{}
322 for k, v := range g.huffmans[g.whichHuffman] {
323 if len(v) > 15 {
324 return false
325 }
326 pairs = append(pairs, pair{k, v})
327 }
328 if len(pairs) == 0 {
329 return false
330 }
331
332 // Sort by bitstring-length and then code.
333 sort.Slice(pairs, func(i, j int) bool {
334 pi, pj := &pairs[i], &pairs[j]
335 if len(pi.v) != len(pj.v) {
336 return len(pi.v) < len(pj.v)
337 }
338 return pi.k < pj.k
339 })
340
341 // Set up a generator for the canonical bitstrings.
342 buf := [15]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}
343 prevV := ""
344 next := func() bool {
345 if i := len(prevV); i > 0 {
346 for {
347 i--
348 if i < 0 {
349 return false
350 }
351 if buf[i] == '0' {
352 buf[i] = '1'
353 break
354 }
355 buf[i] = '0'
356 }
357 }
358 return true
359 }
360
361 // Verify the bitstrings.
362 for _, p := range pairs {
363 if !next() {
364 return false // Over-subscribed.
365 } else if p.v != string(buf[:len(p.v)]) {
366 return false // Non-canonical.
367 }
368 prevV = p.v
369 }
Nigel Tao42de49a2022-01-09 10:45:47 +1100370 if next() != g.incomplete {
371 return false // Under-subscribed.
372 }
Nigel Tao92ea6312022-01-09 10:41:31 +1100373 return true
374}
375
Nigel Tao12347602019-06-16 23:15:05 +1000376func deflateGlobalsWriteDynamicHuffmanTables() error {
377 g := &deflateGlobals
Nigel Taoff7bffc2019-10-13 23:23:48 +1100378 numLCodes, numDCodes, numCLCodeLengths, err := deflateGlobalsCountCodes()
379 if err != nil {
380 return err
Nigel Tao12347602019-06-16 23:15:05 +1000381 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100382 if (numLCodes < 257) || (257+31 < numLCodes) {
383 return fmt.Errorf("bad numLCodes: %d", numLCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000384 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100385 g.stream.writeBits(numLCodes-257, 5)
386 if (numDCodes < 1) || (1+31 < numDCodes) {
387 return fmt.Errorf("bad numDCodes: %d", numDCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000388 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100389 g.stream.writeBits(numDCodes-1, 5)
390 if (numCLCodeLengths < 4) || (4+15 < numCLCodeLengths) {
391 return fmt.Errorf("bad numCLCodeLengths: %d", numCLCodeLengths)
392 }
393 g.stream.writeBits(numCLCodeLengths-4, 4)
Nigel Tao12347602019-06-16 23:15:05 +1000394
395 // Write the Huffman table for CodeLength.
396 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100397 for i := uint32(0); i < numCLCodeLengths; i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000398 n := len(g.huffmans[1][deflateCodeOrder[i]])
399 g.stream.writeBits(uint32(n), 3)
400 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100401 for i := numCLCodeLengths; i < uint32(len(deflateCodeOrder)); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000402 n := len(g.huffmans[1][deflateCodeOrder[i]])
403 if n > 0 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100404 return fmt.Errorf("short numCLCodeLengths: %d", numCLCodeLengths)
Nigel Tao12347602019-06-16 23:15:05 +1000405 }
406 }
407 }
408
Nigel Taodc1218f2019-10-14 09:28:28 +1100409 // Write the Huffman tables for Literal/Length and Distance.
Nigel Tao12347602019-06-16 23:15:05 +1000410 {
411 numZeroes := uint32(0)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100412 for i := uint32(0); i < numLCodes+numDCodes; i++ {
413 codeLen := uint32(0)
414 if i < numLCodes {
415 codeLen = uint32(len(g.huffmans[2][i]))
Nigel Tao12347602019-06-16 23:15:05 +1000416 } else {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100417 codeLen = uint32(len(g.huffmans[3][i-numLCodes]))
Nigel Tao12347602019-06-16 23:15:05 +1000418 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100419 if codeLen == 0 {
Nigel Tao12347602019-06-16 23:15:05 +1000420 numZeroes++
421 continue
422 }
423
424 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
425 return err
426 }
427 numZeroes = 0
428
Nigel Taoff7bffc2019-10-13 23:23:48 +1100429 codeLenCode := g.huffmans[1][codeLen]
430 if codeLenCode == "" {
431 return fmt.Errorf("no code for code-length %d", codeLen)
432 }
433 deflateGlobalsWriteDynamicHuffmanBits(g.huffmans[1][codeLen])
Nigel Tao12347602019-06-16 23:15:05 +1000434 }
435 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
436 return err
437 }
438 }
439
440 return nil
441}
442
443func deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes uint32) error {
444 g := &deflateGlobals
445 if numZeroes == 0 {
446 return nil
447 }
448
449 if s := g.huffmans[1][18]; s != "" {
450 for numZeroes >= 11 {
451 extra := numZeroes - 11
452 if extra > 127 {
453 extra = 127
454 }
455 deflateGlobalsWriteDynamicHuffmanBits(s)
456 g.stream.writeBits(extra, 7)
457 numZeroes -= 11 + extra
458 }
459 }
460
461 if s := g.huffmans[1][17]; s != "" {
462 for numZeroes >= 3 {
463 extra := numZeroes - 3
464 if extra > 7 {
465 extra = 7
466 }
467 deflateGlobalsWriteDynamicHuffmanBits(s)
468 g.stream.writeBits(extra, 3)
469 numZeroes -= 3 + extra
470 }
471 }
472
473 if s := g.huffmans[1][0]; s != "" {
474 for ; numZeroes > 0; numZeroes-- {
475 deflateGlobalsWriteDynamicHuffmanBits(s)
476 }
477 }
478
479 if numZeroes > 0 {
480 return fmt.Errorf("could not write a run of zero-valued code lengths")
481 }
482 return nil
483}
484
485func deflateGlobalsWriteDynamicHuffmanBits(s string) {
486 g := &deflateGlobals
487 for i := 0; i < len(s); i++ {
488 g.stream.writeBits(uint32(s[i]&1), 1)
489 }
490}
491
492func parseDeflateWhichHuffman(s string) (num uint32, remaining string, ok bool) {
493 if i := strings.IndexByte(s, ' '); i >= 0 {
494 s, remaining = s[:i], s[i+1:]
495 for len(remaining) > 0 && remaining[0] == ' ' {
496 remaining = remaining[1:]
497 }
498 }
499
500 switch s {
501 case "CodeLength":
502 return 1, remaining, true
Nigel Taodc1218f2019-10-14 09:28:28 +1100503 case "Literal/Length":
Nigel Tao12347602019-06-16 23:15:05 +1000504 return 2, remaining, true
505 case "Distance":
506 return 3, remaining, true
507 }
508 return 0, "", false
Nigel Tao981bf192018-05-23 12:17:55 +1000509}
510
511func stateDeflate(line string) (stateFunc, error) {
512 g := &deflateGlobals
513 const (
Nigel Tao0cc17982019-05-19 23:43:20 +1000514 cmdB = "bytes "
Nigel Tao12347602019-06-16 23:15:05 +1000515 cmdBDH = "blockDynamicHuffman "
Nigel Tao981bf192018-05-23 12:17:55 +1000516 cmdBFH = "blockFixedHuffman "
Nigel Tao12347602019-06-16 23:15:05 +1000517 cmdBNC = "blockNoCompression "
Nigel Tao981bf192018-05-23 12:17:55 +1000518 )
519 bits := uint32(0)
520 s := ""
521
522 retState := stateFunc(nil)
523 switch {
Nigel Tao12347602019-06-16 23:15:05 +1000524 case line == "":
525 g.stream.flush()
526 return stateDeflate, nil
527
Nigel Tao0cc17982019-05-19 23:43:20 +1000528 case strings.HasPrefix(line, cmdB):
529 s := line[len(cmdB):]
530 for s != "" {
531 x, ok := uint32(0), false
532 x, s, ok = parseHex(s)
533 if !ok {
534 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
535 }
536 out = append(out, uint8(x))
537 }
538 return stateDeflate, nil
539
Nigel Tao981bf192018-05-23 12:17:55 +1000540 case strings.HasPrefix(line, cmdBNC):
541 s = line[len(cmdBNC):]
542 retState = stateDeflateNoCompression
543 bits |= 0 << 1
544 case strings.HasPrefix(line, cmdBFH):
545 s = line[len(cmdBFH):]
546 retState = stateDeflateFixedHuffman
547 bits |= 1 << 1
Nigel Tao12347602019-06-16 23:15:05 +1000548 case strings.HasPrefix(line, cmdBDH):
549 s = line[len(cmdBDH):]
550 retState = stateDeflateDynamicHuffman
551 bits |= 2 << 1
Nigel Tao981bf192018-05-23 12:17:55 +1000552 default:
553 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
554 }
555
556 switch s {
557 case "(final) {":
558 bits |= 1
559 case "(nonFinal) {":
560 // No-op.
561 default:
562 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
563 }
564
565 g.stream.writeBits(bits, 3)
566 return retState, nil
567}
568
569func stateDeflateNoCompression(line string) (stateFunc, error) {
570 g := &deflateGlobals
571 if line == "}" {
572 if len(g.bncData) > 0xFFFF {
573 return nil, fmt.Errorf("bncData is too long")
574 }
575 n := uint32(len(g.bncData))
576 g.stream.flush()
577 g.stream.writeBits(n, 16)
578 g.stream.writeBits(0xFFFF-n, 16)
579 g.stream.writeBytes(g.bncData)
580 g.bncData = g.bncData[:0]
581 return stateDeflate, nil
582 }
583
584 if lit, ok := deflateParseLiteral(line); ok {
585 g.bncData = append(g.bncData, lit...)
586 return stateDeflateNoCompression, nil
587 }
588
589 return nil, fmt.Errorf("bad blockNoCompression command: %q", line)
590}
591
592func stateDeflateFixedHuffman(line string) (stateFunc, error) {
593 g := &deflateGlobals
594 if line == "}" {
Nigel Tao981bf192018-05-23 12:17:55 +1000595 return stateDeflate, nil
596 }
597
598 if line == "endOfBlock" {
599 g.stream.writeBits(0, 7)
600 return stateDeflateFixedHuffman, nil
601 }
602
603 if lit, ok := deflateParseLiteral(line); ok {
Nigel Tao981bf192018-05-23 12:17:55 +1000604 for i := 0; i < len(lit); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000605 g.stream.writeFixedHuffmanLCode(uint32(lit[i]))
Nigel Tao981bf192018-05-23 12:17:55 +1000606 }
607 return stateDeflateFixedHuffman, nil
608 }
609
Nigel Tao11a68192019-02-02 13:04:09 +1100610 if line == "len 3 distCode 31" {
611 lCode, lExtra, lNExtra := deflateEncodeLength(3)
Nigel Tao12347602019-06-16 23:15:05 +1000612 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao11a68192019-02-02 13:04:09 +1100613 g.stream.writeBits(lExtra, lNExtra)
614 dCode, dExtra, dNExtra := uint32(31), uint32(0), uint32(0)
615 g.stream.writeBits(reverse(dCode, 5), 5)
616 g.stream.writeBits(dExtra, dNExtra)
617 return stateDeflateFixedHuffman, nil
618 }
619
Nigel Tao981bf192018-05-23 12:17:55 +1000620 if l, d, ok := deflateParseLenDist(line); ok {
621 lCode, lExtra, lNExtra := deflateEncodeLength(l)
Nigel Tao12347602019-06-16 23:15:05 +1000622 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao981bf192018-05-23 12:17:55 +1000623 g.stream.writeBits(lExtra, lNExtra)
624 dCode, dExtra, dNExtra := deflateEncodeDistance(d)
625 g.stream.writeBits(reverse(dCode, 5), 5)
626 g.stream.writeBits(dExtra, dNExtra)
627 return stateDeflateFixedHuffman, nil
628 }
629
630 return nil, fmt.Errorf("bad stateDeflateFixedHuffman command: %q", line)
631}
632
Nigel Tao12347602019-06-16 23:15:05 +1000633func stateDeflateDynamicHuffman(line string) (stateFunc, error) {
634 g := &deflateGlobals
635 const (
Nigel Taoff7bffc2019-10-13 23:23:48 +1100636 cmdH = "huffman "
Nigel Tao12347602019-06-16 23:15:05 +1000637 )
638 switch {
639 case line == "}":
640 deflateGlobalsClearDynamicHuffmanState()
641 return stateDeflate, nil
642
643 case strings.HasPrefix(line, cmdH):
644 s := line[len(cmdH):]
645 n, s, ok := parseDeflateWhichHuffman(s)
646 if !ok {
647 break
648 }
649 g.whichHuffman = n
650 return stateDeflateDynamicHuffmanHuffman, nil
Nigel Tao12347602019-06-16 23:15:05 +1000651 }
652
653 if lit, ok := deflateParseLiteral(line); ok {
654 for i := 0; i < len(lit); i++ {
655 s := g.huffmans[2][uint32(lit[i])]
656 if s == "" {
657 return nil, fmt.Errorf("no code for literal %q (%d)", lit[i:i+1], lit[i])
658 }
659 deflateGlobalsWriteDynamicHuffmanBits(s)
660 }
661 return stateDeflateDynamicHuffman, nil
Nigel Taoff7bffc2019-10-13 23:23:48 +1100662
663 } else if l, d, ok := deflateParseLenDist(line); ok {
Nigel Taof86c80e2022-01-08 13:46:27 +1100664 if (l > 10) || (d > 4) {
665 return nil, fmt.Errorf("TODO: support len/dist ExtraBits > 0 for dynamic Huffman blocks")
Nigel Taoff7bffc2019-10-13 23:23:48 +1100666 }
Nigel Taof86c80e2022-01-08 13:46:27 +1100667 // len 3 is code 257, len 4 is code 258, etc. All with no ExtraBits.
668 if s := g.huffmans[2][l+254]; s != "" {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100669 deflateGlobalsWriteDynamicHuffmanBits(s)
670 } else {
Nigel Taof86c80e2022-01-08 13:46:27 +1100671 return nil, fmt.Errorf("no code for literal/length symbol %d", l+254)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100672 }
Nigel Taof86c80e2022-01-08 13:46:27 +1100673 // dist 1 is code 0, dist 2 is code 1, etc. All with no ExtraBits.
674 if s := g.huffmans[3][d-1]; s != "" {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100675 deflateGlobalsWriteDynamicHuffmanBits(s)
676 } else {
Nigel Taof86c80e2022-01-08 13:46:27 +1100677 return nil, fmt.Errorf("no code for distance symbol %d", d-1)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100678 }
679 return stateDeflateDynamicHuffman, nil
680
Nigel Tao12347602019-06-16 23:15:05 +1000681 } else if line == "endOfBlock" {
682 s := g.huffmans[2][256]
683 if s == "" {
684 return nil, fmt.Errorf("no code for end-of-block (256)")
685 }
686 deflateGlobalsWriteDynamicHuffmanBits(s)
687 return stateDeflateDynamicHuffman, nil
688 }
689
690 return nil, fmt.Errorf("bad stateDeflateDynamicHuffman command: %q", line)
691}
692
693func stateDeflateDynamicHuffmanHuffman(line string) (stateFunc, error) {
694 g := &deflateGlobals
695outer:
696 switch {
697 case line == "}":
Nigel Tao92ea6312022-01-09 10:41:31 +1100698 if !deflateGlobalsIsHuffmanCanonical() {
699 return nil, fmt.Errorf(`"huffman %s" is non-canonical`, deflateHuffmanNames[g.whichHuffman])
700 }
Nigel Tao12347602019-06-16 23:15:05 +1000701 g.whichHuffman = 0
Nigel Taoff7bffc2019-10-13 23:23:48 +1100702 g.prevLine = ""
703 g.etcetera = false
Nigel Tao42de49a2022-01-09 10:45:47 +1100704 g.incomplete = false
Nigel Tao12347602019-06-16 23:15:05 +1000705
706 // If we have all three Huffman tables, write them.
707 for i := 1; ; i++ {
708 if i == 4 {
709 if err := deflateGlobalsWriteDynamicHuffmanTables(); err != nil {
710 return nil, err
711 }
712 break
713 }
714 if g.huffmans[i] == nil {
715 break
716 }
717 }
718
719 return stateDeflateDynamicHuffman, nil
720
Nigel Taoff7bffc2019-10-13 23:23:48 +1100721 case line == "etcetera":
722 g.etcetera = true
723 return stateDeflateDynamicHuffmanHuffman, nil
724
Nigel Tao42de49a2022-01-09 10:45:47 +1100725 case line == "incomplete":
726 g.incomplete = true
727 return stateDeflateDynamicHuffmanHuffman, nil
728
Nigel Tao12347602019-06-16 23:15:05 +1000729 default:
730 s := line
731 n, s, ok := parseNum(s)
732 if !ok || s == "" {
733 break
734 }
735 for i := 0; i < len(s); i++ {
736 if c := s[i]; c != '0' && c != '1' {
737 break outer
738 }
739 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100740 if (len(s) < 1) || (15 < len(s)) {
741 return nil, fmt.Errorf("%q code length, %d, is out of range", s, len(s))
742 }
743
744 if g.etcetera {
745 g.etcetera = false
746 n0, s0, ok := parseNum(g.prevLine)
747 if !ok {
748 return nil, fmt.Errorf("bad etcetera command")
749 }
750 if err := stateDeflateDHHEtcetera(n0, s0, n, s); err != nil {
751 return nil, err
752 }
753 }
754
Nigel Tao12347602019-06-16 23:15:05 +1000755 if g.huffmans[g.whichHuffman] == nil {
756 g.huffmans[g.whichHuffman] = deflateHuffmanTable{}
757 }
758 g.huffmans[g.whichHuffman][n] = s
Nigel Taoff7bffc2019-10-13 23:23:48 +1100759 g.prevLine = line
Nigel Tao12347602019-06-16 23:15:05 +1000760 return stateDeflateDynamicHuffmanHuffman, nil
761 }
762
763 return nil, fmt.Errorf("bad stateDeflateDynamicHuffmanHuffman command: %q", line)
764}
765
Nigel Taoff7bffc2019-10-13 23:23:48 +1100766// stateDeflateDHHEtcetera expands the "etcetera" line in:
767//
768// 0 0000
769// 1 0001
770// etcetera
771// 7 0111
772//
773// to produce the implicit lines:
774//
775// 0 0000
776// 1 0001
777// 2 0010
778// 3 0011
779// 4 0100
780// 5 0101
781// 6 0110
782// 7 0111
783func stateDeflateDHHEtcetera(n0 uint32, s0 string, n1 uint32, s1 string) error {
784 b := []byte(s0)
785 if !incrementBitstring(b) {
786 return fmt.Errorf("etcetera: could not increment bitstring")
787 }
788 for n := n0 + 1; n < n1; n++ {
789 line := fmt.Sprintf("%d %s", n, b)
790 if _, err := stateDeflateDynamicHuffmanHuffman(line); err != nil {
791 return err
792 }
793 if !incrementBitstring(b) {
794 return fmt.Errorf("etcetera: could not increment bitstring")
795 }
796 }
797 if string(b) != s1 {
798 return fmt.Errorf("etcetera: final bitstring: got %q, want %q", b, s1)
799 }
800 return nil
801}
802
803func incrementBitstring(b []byte) (ok bool) {
804 for i := len(b) - 1; i >= 0; i-- {
805 switch b[i] {
806 case '0':
807 b[i] = '1'
808 return true
809 case '1':
810 b[i] = '0'
811 default:
812 return false
813 }
814 }
815 return false
816}
817
Nigel Tao981bf192018-05-23 12:17:55 +1000818type deflateBitStream struct {
819 bits uint32
820 nBits uint32 // Always within [0, 7].
821}
822
823// writeBits writes the low n bits of b to z.
824func (z *deflateBitStream) writeBits(b uint32, n uint32) {
825 if n > 24 {
826 panic("writeBits: n is too large")
827 }
828 z.bits |= b << z.nBits
829 z.nBits += n
830 for z.nBits >= 8 {
831 out = append(out, uint8(z.bits))
832 z.bits >>= 8
833 z.nBits -= 8
834 }
835}
836
837func (z *deflateBitStream) writeBytes(b []byte) {
838 z.flush()
839 out = append(out, b...)
840}
841
Nigel Tao12347602019-06-16 23:15:05 +1000842func (z *deflateBitStream) writeFixedHuffmanLCode(lCode uint32) {
Nigel Tao981bf192018-05-23 12:17:55 +1000843 switch {
844 case lCode < 144: // 0b._0011_0000 through 0b._1011_1111
845 lCode += 0x030
846 z.writeBits(reverse(lCode, 8), 8)
847 case lCode < 256: // 0b1_1001_0000 through 0b1_1111_1111
848 lCode += 0x190 - 144
849 z.writeBits(reverse(lCode, 9), 9)
850 case lCode < 280: // 0b._.000_0000 through 0b._.001_0111
851 lCode -= 256 - 0x000
852 z.writeBits(reverse(lCode, 7), 7)
853 default: // 0b._1100_0000 through 0b._1100_0111
854 lCode -= 280 - 0x0C0
855 z.writeBits(reverse(lCode, 8), 8)
856 }
857}
858
859func (z *deflateBitStream) flush() {
860 if z.nBits > 0 {
861 out = append(out, uint8(z.bits))
862 z.bits = 0
863 z.nBits = 0
864 }
865}
866
867func deflateEncodeLength(l uint32) (code uint32, extra uint32, nExtra uint32) {
868 switch {
869 case l < 3:
870 // No-op.
871 case l < 11:
872 l -= 3
873 return (l >> 0) + 257, l & 0x0000, 0
874 case l < 19:
875 l -= 11
876 return (l >> 1) + 265, l & 0x0001, 1
877 case l < 35:
878 l -= 19
879 return (l >> 2) + 269, l & 0x0003, 2
880 case l < 67:
881 l -= 35
882 return (l >> 3) + 273, l & 0x0007, 3
883 case l < 131:
884 l -= 67
885 return (l >> 4) + 277, l & 0x000F, 4
886 case l < 258:
887 l -= 131
888 return (l >> 5) + 281, l & 0x001F, 5
889 case l == 258:
890 return 285, 0, 0
891 }
892 panic(fmt.Sprintf("deflateEncodeLength: l=%d", l))
893}
894
895func deflateEncodeDistance(d uint32) (code uint32, extra uint32, nExtra uint32) {
896 switch {
897 case d < 1:
898 // No-op.
899 case d < 5:
900 d -= 1
901 return (d >> 0) + 0, d & 0x0000, 0
902 case d < 9:
903 d -= 5
904 return (d >> 1) + 4, d & 0x0001, 1
905 case d < 17:
906 d -= 9
907 return (d >> 2) + 6, d & 0x0003, 2
908 case d < 33:
909 d -= 17
910 return (d >> 3) + 8, d & 0x0007, 3
911 case d < 65:
912 d -= 33
913 return (d >> 4) + 10, d & 0x000F, 4
914 case d < 129:
915 d -= 65
916 return (d >> 5) + 12, d & 0x001F, 5
917 case d < 257:
918 d -= 129
919 return (d >> 6) + 14, d & 0x003F, 6
920 case d < 513:
921 d -= 257
922 return (d >> 7) + 16, d & 0x007F, 7
923 case d < 1025:
924 d -= 513
925 return (d >> 8) + 18, d & 0x00FF, 8
926 case d < 2049:
927 d -= 1025
928 return (d >> 9) + 20, d & 0x01FF, 9
929 case d < 4097:
930 d -= 2049
931 return (d >> 10) + 22, d & 0x03FF, 10
932 case d < 8193:
933 d -= 4097
934 return (d >> 11) + 24, d & 0x07FF, 11
935 case d < 16385:
936 d -= 8193
937 return (d >> 12) + 26, d & 0x0FFF, 12
938 case d < 32769:
939 d -= 16385
940 return (d >> 13) + 28, d & 0x1FFF, 13
941 }
942 panic(fmt.Sprintf("deflateEncodeDistance: d=%d", d))
943}
944
945func deflateParseLiteral(s string) (lit string, ok bool) {
Nigel Tao12347602019-06-16 23:15:05 +1000946 // TODO: support "\xAB" escape codes in the script?
Nigel Tao981bf192018-05-23 12:17:55 +1000947 const (
948 prefix = `literal "`
949 suffix = `"`
950 )
951 if strings.HasPrefix(s, prefix) {
952 s = s[len(prefix):]
953 if strings.HasSuffix(s, suffix) {
954 return s[:len(s)-len(suffix)], true
955 }
956 }
957 return "", false
958}
959
960func deflateParseLenDist(line string) (l uint32, d uint32, ok bool) {
961 const (
962 lStr = "len "
963 dStr = "dist "
964 )
965 s := line
966 if strings.HasPrefix(s, lStr) {
967 s = s[len(lStr):]
968 if l, s, ok := parseNum(s); ok && 3 <= l && l <= 258 {
969 if strings.HasPrefix(s, dStr) {
970 s = s[len(dStr):]
971 if d, s, ok := parseNum(s); ok && 1 <= d && d <= 32768 && s == "" {
972 return l, d, true
973 }
974 }
975 }
976 }
977 return 0, 0, false
978}
979
980// ----
981
982func init() {
983 formats["gif"] = stateGif
984}
985
986var gifGlobals struct {
Nigel Tao31a2bc92019-05-18 17:32:24 +1000987 imageWidth uint32
988 imageHeight uint32
989 imageBackgroundColorIndex uint32
Nigel Tao981bf192018-05-23 12:17:55 +1000990
Nigel Taoefb18032019-10-05 11:40:24 +1000991 frameInterlaced bool
992 frameLeft uint32
993 frameTop uint32
994 frameWidth uint32
995 frameHeight uint32
Nigel Tao981bf192018-05-23 12:17:55 +1000996
997 globalPalette [][4]uint8
Nigel Tao746681e2019-05-04 13:49:22 +1000998 localPalette [][4]uint8
Nigel Tao981bf192018-05-23 12:17:55 +1000999}
1000
1001func stateGif(line string) (stateFunc, error) {
1002 const (
Nigel Tao6700c722019-05-26 09:41:55 +10001003 cmdB = "bytes "
1004 cmdGC = "graphicControl "
1005 cmdL = "lzw "
1006 cmdLC = "loopCount "
Nigel Tao981bf192018-05-23 12:17:55 +10001007 )
1008outer:
1009 switch {
Nigel Tao12347602019-06-16 23:15:05 +10001010 case line == "":
1011 return stateGif, nil
1012
Nigel Tao981bf192018-05-23 12:17:55 +10001013 case line == "frame {":
Nigel Taoefb18032019-10-05 11:40:24 +10001014 gifGlobals.frameInterlaced = false
1015 gifGlobals.frameLeft = 0
1016 gifGlobals.frameTop = 0
1017 gifGlobals.frameWidth = 0
1018 gifGlobals.frameHeight = 0
Nigel Tao746681e2019-05-04 13:49:22 +10001019 gifGlobals.localPalette = nil
Nigel Tao981bf192018-05-23 12:17:55 +10001020 out = append(out, 0x2C)
1021 return stateGifFrame, nil
1022
1023 case line == "header":
1024 out = append(out, "GIF89a"...)
1025 return stateGif, nil
1026
1027 case line == "image {":
1028 return stateGifImage, nil
1029
1030 case line == "trailer":
1031 out = append(out, 0x3B)
1032 return stateGif, nil
1033
Nigel Tao21f28902019-04-20 16:33:11 +10001034 case strings.HasPrefix(line, cmdB):
1035 s := line[len(cmdB):]
1036 for s != "" {
1037 x, ok := uint32(0), false
1038 x, s, ok = parseHex(s)
1039 if !ok {
1040 break outer
1041 }
1042 out = append(out, uint8(x))
1043 }
1044 return stateGif, nil
1045
Nigel Tao6700c722019-05-26 09:41:55 +10001046 case strings.HasPrefix(line, cmdGC):
1047 s := line[len(cmdGC):]
1048
1049 flags := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +10001050 duration := uint32(0)
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001051 transparentIndex := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +10001052 for s != "" {
1053 term := ""
1054 if i := strings.IndexByte(s, ' '); i >= 0 {
1055 term, s = s[:i], s[i+1:]
1056 } else {
1057 term, s = s, ""
1058 }
1059
1060 const (
1061 ms = "ms"
1062 trans = "transparentIndex="
1063 )
1064 switch {
1065 case term == "animationDisposalNone":
Nigel Tao6700c722019-05-26 09:41:55 +10001066 flags |= 0x00
Nigel Tao344123b2020-09-25 23:00:01 +10001067 case term == "animationDisposalRestoreBackground":
Nigel Tao6700c722019-05-26 09:41:55 +10001068 flags |= 0x08
Nigel Tao344123b2020-09-25 23:00:01 +10001069 case term == "animationDisposalRestorePrevious":
Nigel Tao6700c722019-05-26 09:41:55 +10001070 flags |= 0x0C
Nigel Tao344123b2020-09-25 23:00:01 +10001071 case strings.HasPrefix(term, trans):
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001072 num, err := strconv.ParseUint(term[len(trans):], 0, 8)
1073 if err != nil {
Nigel Tao344123b2020-09-25 23:00:01 +10001074 break outer
1075 }
1076 flags |= 0x01
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001077 transparentIndex = uint8(num)
Nigel Tao344123b2020-09-25 23:00:01 +10001078 case strings.HasSuffix(term, ms):
1079 num, remaining, ok := parseNum(term[:len(term)-len(ms)])
1080 if !ok || remaining != "" {
1081 break outer
1082 }
1083 duration = num / 10 // GIF's unit of time is 10ms.
Nigel Tao6700c722019-05-26 09:41:55 +10001084 default:
1085 break outer
1086 }
Nigel Tao6700c722019-05-26 09:41:55 +10001087 }
1088
Nigel Taob1ff27f2019-05-12 22:00:44 +10001089 out = append(out,
Nigel Tao6700c722019-05-26 09:41:55 +10001090 0x21, 0xF9, 0x04,
1091 flags,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001092 uint8(duration>>0),
1093 uint8(duration>>8),
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001094 transparentIndex,
Nigel Tao6700c722019-05-26 09:41:55 +10001095 0x00,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001096 )
1097 return stateGif, nil
1098
Nigel Tao981bf192018-05-23 12:17:55 +10001099 case strings.HasPrefix(line, cmdL):
1100 s := line[len(cmdL):]
1101 litWidth, s, ok := parseNum(s)
1102 if !ok || litWidth < 2 || 8 < litWidth {
1103 break
1104 }
1105 out = append(out, uint8(litWidth))
1106
1107 uncompressed := []byte(nil)
1108 for s != "" {
1109 x := uint32(0)
1110 x, s, ok = parseHex(s)
1111 if !ok {
1112 break outer
1113 }
1114 uncompressed = append(uncompressed, uint8(x))
1115 }
1116
1117 buf := bytes.NewBuffer(nil)
1118 w := lzw.NewWriter(buf, lzw.LSB, int(litWidth))
1119 if _, err := w.Write(uncompressed); err != nil {
1120 return nil, err
1121 }
1122 if err := w.Close(); err != nil {
1123 return nil, err
1124 }
1125 compressed := buf.Bytes()
1126
1127 for len(compressed) > 0 {
1128 if len(compressed) <= 0xFF {
1129 out = append(out, uint8(len(compressed)))
1130 out = append(out, compressed...)
1131 compressed = nil
1132 } else {
1133 out = append(out, 0xFF)
1134 out = append(out, compressed[:0xFF]...)
1135 compressed = compressed[0xFF:]
1136 }
1137 }
1138 out = append(out, 0x00)
1139 return stateGif, nil
Nigel Tao7be9c392018-10-13 17:00:45 +11001140
1141 case strings.HasPrefix(line, cmdLC):
1142 s := line[len(cmdLC):]
1143 loopCount, _, ok := parseNum(s)
1144 if !ok || 0xFFFF < loopCount {
1145 break
1146 }
1147 out = append(out,
1148 0x21, // Extension Introducer.
1149 0xFF, // Application Extension Label.
1150 0x0B, // Block Size.
1151 )
1152 out = append(out, "NETSCAPE2.0"...)
1153 out = append(out,
1154 0x03, // Block Size.
1155 0x01, // Magic Number.
1156 byte(loopCount),
1157 byte(loopCount>>8),
1158 0x00, // Block Terminator.
1159 )
1160 return stateGif, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001161 }
1162
1163 return nil, fmt.Errorf("bad stateGif command: %q", line)
1164}
1165
1166func stateGifImage(line string) (stateFunc, error) {
1167 g := &gifGlobals
1168 if line == "}" {
1169 out = appendU16LE(out, uint16(g.imageWidth))
1170 out = appendU16LE(out, uint16(g.imageHeight))
1171 if g.globalPalette == nil {
1172 out = append(out, 0x00)
1173 } else if n := log2(uint32(len(g.globalPalette))); n < 2 || 8 < n {
1174 return nil, fmt.Errorf("bad len(g.globalPalette): %d", len(g.globalPalette))
1175 } else {
1176 out = append(out, 0x80|uint8(n-1))
1177 }
Nigel Tao31a2bc92019-05-18 17:32:24 +10001178 out = append(out, uint8(g.imageBackgroundColorIndex))
Nigel Tao981bf192018-05-23 12:17:55 +10001179 out = append(out, 0x00)
1180 for _, x := range g.globalPalette {
1181 out = append(out, x[0], x[1], x[2])
1182 }
1183 return stateGif, nil
1184 }
1185
1186 const (
Nigel Tao31a2bc92019-05-18 17:32:24 +10001187 cmdBCI = "backgroundColorIndex "
Nigel Tao981bf192018-05-23 12:17:55 +10001188 cmdIWH = "imageWidthHeight "
1189 cmdP = "palette {"
1190 )
1191 switch {
Nigel Tao31a2bc92019-05-18 17:32:24 +10001192 case strings.HasPrefix(line, cmdBCI):
1193 s := line[len(cmdBCI):]
1194 if i, _, ok := parseNum(s); ok {
1195 g.imageBackgroundColorIndex = i
1196 }
1197 return stateGifImage, nil
1198
Nigel Tao981bf192018-05-23 12:17:55 +10001199 case strings.HasPrefix(line, cmdIWH):
1200 s := line[len(cmdIWH):]
1201 if w, s, ok := parseNum(s); ok {
1202 if h, _, ok := parseNum(s); ok {
1203 g.imageWidth = w
1204 g.imageHeight = h
1205 return stateGifImage, nil
1206 }
1207 }
1208
1209 case strings.HasPrefix(line, cmdP):
1210 return stateGifImagePalette, nil
1211 }
1212
1213 return nil, fmt.Errorf("bad stateGifImage command: %q", line)
1214}
1215
1216func stateGifFrame(line string) (stateFunc, error) {
1217 g := &gifGlobals
1218 if line == "}" {
1219 out = appendU16LE(out, uint16(g.frameLeft))
1220 out = appendU16LE(out, uint16(g.frameTop))
1221 out = appendU16LE(out, uint16(g.frameWidth))
1222 out = appendU16LE(out, uint16(g.frameHeight))
Nigel Taoefb18032019-10-05 11:40:24 +10001223 flags := byte(0x00)
1224 if g.frameInterlaced {
1225 flags |= 0x40
1226 }
Nigel Tao746681e2019-05-04 13:49:22 +10001227 if g.localPalette == nil {
Nigel Taoefb18032019-10-05 11:40:24 +10001228 out = append(out, flags)
Nigel Tao746681e2019-05-04 13:49:22 +10001229 } else if n := log2(uint32(len(g.localPalette))); n < 2 || 8 < n {
1230 return nil, fmt.Errorf("bad len(g.localPalette): %d", len(g.localPalette))
1231 } else {
Nigel Taoefb18032019-10-05 11:40:24 +10001232 out = append(out, flags|0x80|uint8(n-1))
Nigel Tao746681e2019-05-04 13:49:22 +10001233 }
1234 for _, x := range g.localPalette {
1235 out = append(out, x[0], x[1], x[2])
1236 }
Nigel Tao981bf192018-05-23 12:17:55 +10001237 return stateGif, nil
1238 }
1239
1240 const (
1241 cmdFLTWH = "frameLeftTopWidthHeight "
Nigel Tao746681e2019-05-04 13:49:22 +10001242 cmdP = "palette {"
Nigel Tao981bf192018-05-23 12:17:55 +10001243 )
1244 switch {
Nigel Taoefb18032019-10-05 11:40:24 +10001245 case line == "interlaced":
1246 g.frameInterlaced = true
1247 return stateGifFrame, nil
1248
Nigel Tao981bf192018-05-23 12:17:55 +10001249 case strings.HasPrefix(line, cmdFLTWH):
1250 s := line[len(cmdFLTWH):]
1251 if l, s, ok := parseNum(s); ok {
1252 if t, s, ok := parseNum(s); ok {
1253 if w, s, ok := parseNum(s); ok {
1254 if h, _, ok := parseNum(s); ok {
1255 g.frameLeft = l
1256 g.frameTop = t
1257 g.frameWidth = w
1258 g.frameHeight = h
1259 return stateGifFrame, nil
1260 }
1261 }
1262 }
1263 }
Nigel Tao746681e2019-05-04 13:49:22 +10001264
1265 case strings.HasPrefix(line, cmdP):
1266 return stateGifFramePalette, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001267 }
1268
1269 return nil, fmt.Errorf("bad stateGifFrame command: %q", line)
1270}
1271
1272func stateGifImagePalette(line string) (stateFunc, error) {
1273 g := &gifGlobals
1274 if line == "}" {
1275 return stateGifImage, nil
1276 }
1277
1278 s := line
1279 if rgb0, s, ok := parseHex(s); ok {
1280 if rgb1, s, ok := parseHex(s); ok {
1281 if rgb2, _, ok := parseHex(s); ok {
1282 g.globalPalette = append(g.globalPalette,
1283 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1284 return stateGifImagePalette, nil
1285 }
1286 }
1287 }
1288
1289 return nil, fmt.Errorf("bad stateGifImagePalette command: %q", line)
1290}
Nigel Tao746681e2019-05-04 13:49:22 +10001291
1292func stateGifFramePalette(line string) (stateFunc, error) {
1293 g := &gifGlobals
1294 if line == "}" {
1295 return stateGifFrame, nil
1296 }
1297
1298 s := line
1299 if rgb0, s, ok := parseHex(s); ok {
1300 if rgb1, s, ok := parseHex(s); ok {
1301 if rgb2, _, ok := parseHex(s); ok {
1302 g.localPalette = append(g.localPalette,
1303 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1304 return stateGifFramePalette, nil
1305 }
1306 }
1307 }
1308
1309 return nil, fmt.Errorf("bad stateGifFramePalette command: %q", line)
1310}
Nigel Tao6c2a5912021-11-16 11:00:48 +11001311
1312// ----
1313
1314func init() {
1315 formats["png"] = statePng
1316}
1317
1318var pngGlobals struct {
1319 scratch [4]byte
1320
1321 animSeqNum uint32
1322 chunkData bytes.Buffer
1323 chunkType string
1324 zlibWriter *zlib.Writer
1325}
1326
1327func statePng(line string) (stateFunc, error) {
1328 g := &pngGlobals
1329 switch {
1330 case line == "":
1331 return statePng, nil
1332
1333 case line == "magic":
1334 out = append(out, "\x89PNG\x0D\x0A\x1A\x0A"...)
1335 return statePng, nil
1336
1337 case (len(line) == 6) && (line[4] == ' ') && (line[5] == '{'):
1338 g.chunkData.Reset()
1339 g.chunkType = line[:4]
1340 return statePngChunk, nil
1341 }
1342
1343 return nil, fmt.Errorf("bad statePng command: %q", line)
1344}
1345
1346func statePngChunk(line string) (stateFunc, error) {
1347 g := &pngGlobals
1348 if line == "}" {
1349 if n := g.chunkData.Len(); n > 0xFFFF_FFFF {
1350 return nil, fmt.Errorf("chunkData is too long")
1351 }
1352 out = appendU32BE(out, uint32(g.chunkData.Len()))
1353 n := len(out)
1354 out = append(out, g.chunkType...)
1355 out = append(out, g.chunkData.Bytes()...)
1356 out = appendU32BE(out, crc32.ChecksumIEEE(out[n:]))
1357 return statePng, nil
1358 }
1359
1360 switch {
1361 case line == "animSeqNum++":
1362 g.chunkData.Write(appendU32BE(g.scratch[:0], g.animSeqNum))
1363 g.animSeqNum++
1364 return statePngChunk, nil
1365
1366 case line == "raw {":
1367 return statePngRaw, nil
1368
1369 case line == "zlib {":
1370 g.zlibWriter = zlib.NewWriter(&g.chunkData)
1371 return statePngZlib, nil
1372 }
1373
1374 return nil, fmt.Errorf("bad statePngChunk command: %q", line)
1375}
1376
1377func statePngRaw(line string) (stateFunc, error) {
1378 g := &pngGlobals
1379 if line == "}" {
1380 return statePngChunk, nil
1381 }
1382
1383 for s := line; s != ""; {
1384 if x, remaining, ok := parseHex(s); ok {
1385 g.chunkData.WriteByte(byte(x))
1386 s = remaining
1387 } else {
1388 return nil, fmt.Errorf("bad statePngRaw command: %q", line)
1389 }
1390 }
1391 return statePngRaw, nil
1392}
1393
1394func statePngZlib(line string) (stateFunc, error) {
1395 g := &pngGlobals
1396 if line == "}" {
1397 if err := g.zlibWriter.Close(); err != nil {
1398 return nil, fmt.Errorf("statePngZlib: %w", err)
1399 }
1400 return statePngChunk, nil
1401 }
1402
1403 for s := line; s != ""; {
1404 if x, remaining, ok := parseHex(s); ok {
1405 g.scratch[0] = byte(x)
1406 g.zlibWriter.Write(g.scratch[:1])
1407 s = remaining
1408 } else {
1409 return nil, fmt.Errorf("bad statePngZlib command: %q", line)
1410 }
1411 }
1412 return statePngZlib, nil
1413}