blob: ebbfedf6bb86cee8c1789d1e482bf5df95258c1a [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.
267 prevLine string
268 etcetera bool
Nigel Tao12347602019-06-16 23:15:05 +1000269}
270
271func deflateGlobalsClearDynamicHuffmanState() {
Nigel Tao12347602019-06-16 23:15:05 +1000272 deflateGlobals.whichHuffman = 0
273 deflateGlobals.huffmans = [4]deflateHuffmanTable{}
Nigel Taoff7bffc2019-10-13 23:23:48 +1100274 deflateGlobals.prevLine = ""
275 deflateGlobals.etcetera = false
276}
277
278func deflateGlobalsCountCodes() (numLCodes uint32, numDCodes uint32, numCLCodeLengths uint32, retErr error) {
279 for k := range deflateGlobals.huffmans[2] {
280 if numLCodes < (k + 1) {
281 numLCodes = (k + 1)
282 }
283 }
284
285 for k := range deflateGlobals.huffmans[3] {
286 if numDCodes < (k + 1) {
287 numDCodes = (k + 1)
288 }
289 }
290
291 for k := range deflateGlobals.huffmans[1] {
292 if (k < 0) || (18 < k) {
293 return 0, 0, 0, fmt.Errorf("bad CodeLength: %d", k)
294 }
295 }
296 for i := len(deflateCodeOrder) - 1; i >= 0; i-- {
297 cl := deflateCodeOrder[i]
298 if _, ok := deflateGlobals.huffmans[1][cl]; ok {
299 numCLCodeLengths = uint32(i + 1)
300 break
301 }
302 }
303 if numCLCodeLengths < 4 {
304 numCLCodeLengths = 4
305 }
306
307 return numLCodes, numDCodes, numCLCodeLengths, nil
Nigel Tao12347602019-06-16 23:15:05 +1000308}
309
Nigel Tao92ea6312022-01-09 10:41:31 +1100310func deflateGlobalsIsHuffmanCanonical() bool {
311 g := &deflateGlobals
312
313 // Gather the code+bitstring pairs. Deflate bitstrings cannot be longer
314 // than 15 bits.
315 type pair struct {
316 k uint32
317 v string
318 }
319 pairs := []pair{}
320 for k, v := range g.huffmans[g.whichHuffman] {
321 if len(v) > 15 {
322 return false
323 }
324 pairs = append(pairs, pair{k, v})
325 }
326 if len(pairs) == 0 {
327 return false
328 }
329
330 // Sort by bitstring-length and then code.
331 sort.Slice(pairs, func(i, j int) bool {
332 pi, pj := &pairs[i], &pairs[j]
333 if len(pi.v) != len(pj.v) {
334 return len(pi.v) < len(pj.v)
335 }
336 return pi.k < pj.k
337 })
338
339 // Set up a generator for the canonical bitstrings.
340 buf := [15]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}
341 prevV := ""
342 next := func() bool {
343 if i := len(prevV); i > 0 {
344 for {
345 i--
346 if i < 0 {
347 return false
348 }
349 if buf[i] == '0' {
350 buf[i] = '1'
351 break
352 }
353 buf[i] = '0'
354 }
355 }
356 return true
357 }
358
359 // Verify the bitstrings.
360 for _, p := range pairs {
361 if !next() {
362 return false // Over-subscribed.
363 } else if p.v != string(buf[:len(p.v)]) {
364 return false // Non-canonical.
365 }
366 prevV = p.v
367 }
368 return true
369}
370
Nigel Tao12347602019-06-16 23:15:05 +1000371func deflateGlobalsWriteDynamicHuffmanTables() error {
372 g := &deflateGlobals
Nigel Taoff7bffc2019-10-13 23:23:48 +1100373 numLCodes, numDCodes, numCLCodeLengths, err := deflateGlobalsCountCodes()
374 if err != nil {
375 return err
Nigel Tao12347602019-06-16 23:15:05 +1000376 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100377 if (numLCodes < 257) || (257+31 < numLCodes) {
378 return fmt.Errorf("bad numLCodes: %d", numLCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000379 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100380 g.stream.writeBits(numLCodes-257, 5)
381 if (numDCodes < 1) || (1+31 < numDCodes) {
382 return fmt.Errorf("bad numDCodes: %d", numDCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000383 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100384 g.stream.writeBits(numDCodes-1, 5)
385 if (numCLCodeLengths < 4) || (4+15 < numCLCodeLengths) {
386 return fmt.Errorf("bad numCLCodeLengths: %d", numCLCodeLengths)
387 }
388 g.stream.writeBits(numCLCodeLengths-4, 4)
Nigel Tao12347602019-06-16 23:15:05 +1000389
390 // Write the Huffman table for CodeLength.
391 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100392 for i := uint32(0); i < numCLCodeLengths; i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000393 n := len(g.huffmans[1][deflateCodeOrder[i]])
394 g.stream.writeBits(uint32(n), 3)
395 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100396 for i := numCLCodeLengths; i < uint32(len(deflateCodeOrder)); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000397 n := len(g.huffmans[1][deflateCodeOrder[i]])
398 if n > 0 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100399 return fmt.Errorf("short numCLCodeLengths: %d", numCLCodeLengths)
Nigel Tao12347602019-06-16 23:15:05 +1000400 }
401 }
402 }
403
Nigel Taodc1218f2019-10-14 09:28:28 +1100404 // Write the Huffman tables for Literal/Length and Distance.
Nigel Tao12347602019-06-16 23:15:05 +1000405 {
406 numZeroes := uint32(0)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100407 for i := uint32(0); i < numLCodes+numDCodes; i++ {
408 codeLen := uint32(0)
409 if i < numLCodes {
410 codeLen = uint32(len(g.huffmans[2][i]))
Nigel Tao12347602019-06-16 23:15:05 +1000411 } else {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100412 codeLen = uint32(len(g.huffmans[3][i-numLCodes]))
Nigel Tao12347602019-06-16 23:15:05 +1000413 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100414 if codeLen == 0 {
Nigel Tao12347602019-06-16 23:15:05 +1000415 numZeroes++
416 continue
417 }
418
419 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
420 return err
421 }
422 numZeroes = 0
423
Nigel Taoff7bffc2019-10-13 23:23:48 +1100424 codeLenCode := g.huffmans[1][codeLen]
425 if codeLenCode == "" {
426 return fmt.Errorf("no code for code-length %d", codeLen)
427 }
428 deflateGlobalsWriteDynamicHuffmanBits(g.huffmans[1][codeLen])
Nigel Tao12347602019-06-16 23:15:05 +1000429 }
430 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
431 return err
432 }
433 }
434
435 return nil
436}
437
438func deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes uint32) error {
439 g := &deflateGlobals
440 if numZeroes == 0 {
441 return nil
442 }
443
444 if s := g.huffmans[1][18]; s != "" {
445 for numZeroes >= 11 {
446 extra := numZeroes - 11
447 if extra > 127 {
448 extra = 127
449 }
450 deflateGlobalsWriteDynamicHuffmanBits(s)
451 g.stream.writeBits(extra, 7)
452 numZeroes -= 11 + extra
453 }
454 }
455
456 if s := g.huffmans[1][17]; s != "" {
457 for numZeroes >= 3 {
458 extra := numZeroes - 3
459 if extra > 7 {
460 extra = 7
461 }
462 deflateGlobalsWriteDynamicHuffmanBits(s)
463 g.stream.writeBits(extra, 3)
464 numZeroes -= 3 + extra
465 }
466 }
467
468 if s := g.huffmans[1][0]; s != "" {
469 for ; numZeroes > 0; numZeroes-- {
470 deflateGlobalsWriteDynamicHuffmanBits(s)
471 }
472 }
473
474 if numZeroes > 0 {
475 return fmt.Errorf("could not write a run of zero-valued code lengths")
476 }
477 return nil
478}
479
480func deflateGlobalsWriteDynamicHuffmanBits(s string) {
481 g := &deflateGlobals
482 for i := 0; i < len(s); i++ {
483 g.stream.writeBits(uint32(s[i]&1), 1)
484 }
485}
486
487func parseDeflateWhichHuffman(s string) (num uint32, remaining string, ok bool) {
488 if i := strings.IndexByte(s, ' '); i >= 0 {
489 s, remaining = s[:i], s[i+1:]
490 for len(remaining) > 0 && remaining[0] == ' ' {
491 remaining = remaining[1:]
492 }
493 }
494
495 switch s {
496 case "CodeLength":
497 return 1, remaining, true
Nigel Taodc1218f2019-10-14 09:28:28 +1100498 case "Literal/Length":
Nigel Tao12347602019-06-16 23:15:05 +1000499 return 2, remaining, true
500 case "Distance":
501 return 3, remaining, true
502 }
503 return 0, "", false
Nigel Tao981bf192018-05-23 12:17:55 +1000504}
505
506func stateDeflate(line string) (stateFunc, error) {
507 g := &deflateGlobals
508 const (
Nigel Tao0cc17982019-05-19 23:43:20 +1000509 cmdB = "bytes "
Nigel Tao12347602019-06-16 23:15:05 +1000510 cmdBDH = "blockDynamicHuffman "
Nigel Tao981bf192018-05-23 12:17:55 +1000511 cmdBFH = "blockFixedHuffman "
Nigel Tao12347602019-06-16 23:15:05 +1000512 cmdBNC = "blockNoCompression "
Nigel Tao981bf192018-05-23 12:17:55 +1000513 )
514 bits := uint32(0)
515 s := ""
516
517 retState := stateFunc(nil)
518 switch {
Nigel Tao12347602019-06-16 23:15:05 +1000519 case line == "":
520 g.stream.flush()
521 return stateDeflate, nil
522
Nigel Tao0cc17982019-05-19 23:43:20 +1000523 case strings.HasPrefix(line, cmdB):
524 s := line[len(cmdB):]
525 for s != "" {
526 x, ok := uint32(0), false
527 x, s, ok = parseHex(s)
528 if !ok {
529 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
530 }
531 out = append(out, uint8(x))
532 }
533 return stateDeflate, nil
534
Nigel Tao981bf192018-05-23 12:17:55 +1000535 case strings.HasPrefix(line, cmdBNC):
536 s = line[len(cmdBNC):]
537 retState = stateDeflateNoCompression
538 bits |= 0 << 1
539 case strings.HasPrefix(line, cmdBFH):
540 s = line[len(cmdBFH):]
541 retState = stateDeflateFixedHuffman
542 bits |= 1 << 1
Nigel Tao12347602019-06-16 23:15:05 +1000543 case strings.HasPrefix(line, cmdBDH):
544 s = line[len(cmdBDH):]
545 retState = stateDeflateDynamicHuffman
546 bits |= 2 << 1
Nigel Tao981bf192018-05-23 12:17:55 +1000547 default:
548 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
549 }
550
551 switch s {
552 case "(final) {":
553 bits |= 1
554 case "(nonFinal) {":
555 // No-op.
556 default:
557 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
558 }
559
560 g.stream.writeBits(bits, 3)
561 return retState, nil
562}
563
564func stateDeflateNoCompression(line string) (stateFunc, error) {
565 g := &deflateGlobals
566 if line == "}" {
567 if len(g.bncData) > 0xFFFF {
568 return nil, fmt.Errorf("bncData is too long")
569 }
570 n := uint32(len(g.bncData))
571 g.stream.flush()
572 g.stream.writeBits(n, 16)
573 g.stream.writeBits(0xFFFF-n, 16)
574 g.stream.writeBytes(g.bncData)
575 g.bncData = g.bncData[:0]
576 return stateDeflate, nil
577 }
578
579 if lit, ok := deflateParseLiteral(line); ok {
580 g.bncData = append(g.bncData, lit...)
581 return stateDeflateNoCompression, nil
582 }
583
584 return nil, fmt.Errorf("bad blockNoCompression command: %q", line)
585}
586
587func stateDeflateFixedHuffman(line string) (stateFunc, error) {
588 g := &deflateGlobals
589 if line == "}" {
Nigel Tao981bf192018-05-23 12:17:55 +1000590 return stateDeflate, nil
591 }
592
593 if line == "endOfBlock" {
594 g.stream.writeBits(0, 7)
595 return stateDeflateFixedHuffman, nil
596 }
597
598 if lit, ok := deflateParseLiteral(line); ok {
Nigel Tao981bf192018-05-23 12:17:55 +1000599 for i := 0; i < len(lit); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000600 g.stream.writeFixedHuffmanLCode(uint32(lit[i]))
Nigel Tao981bf192018-05-23 12:17:55 +1000601 }
602 return stateDeflateFixedHuffman, nil
603 }
604
Nigel Tao11a68192019-02-02 13:04:09 +1100605 if line == "len 3 distCode 31" {
606 lCode, lExtra, lNExtra := deflateEncodeLength(3)
Nigel Tao12347602019-06-16 23:15:05 +1000607 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao11a68192019-02-02 13:04:09 +1100608 g.stream.writeBits(lExtra, lNExtra)
609 dCode, dExtra, dNExtra := uint32(31), uint32(0), uint32(0)
610 g.stream.writeBits(reverse(dCode, 5), 5)
611 g.stream.writeBits(dExtra, dNExtra)
612 return stateDeflateFixedHuffman, nil
613 }
614
Nigel Tao981bf192018-05-23 12:17:55 +1000615 if l, d, ok := deflateParseLenDist(line); ok {
616 lCode, lExtra, lNExtra := deflateEncodeLength(l)
Nigel Tao12347602019-06-16 23:15:05 +1000617 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao981bf192018-05-23 12:17:55 +1000618 g.stream.writeBits(lExtra, lNExtra)
619 dCode, dExtra, dNExtra := deflateEncodeDistance(d)
620 g.stream.writeBits(reverse(dCode, 5), 5)
621 g.stream.writeBits(dExtra, dNExtra)
622 return stateDeflateFixedHuffman, nil
623 }
624
625 return nil, fmt.Errorf("bad stateDeflateFixedHuffman command: %q", line)
626}
627
Nigel Tao12347602019-06-16 23:15:05 +1000628func stateDeflateDynamicHuffman(line string) (stateFunc, error) {
629 g := &deflateGlobals
630 const (
Nigel Taoff7bffc2019-10-13 23:23:48 +1100631 cmdH = "huffman "
Nigel Tao12347602019-06-16 23:15:05 +1000632 )
633 switch {
634 case line == "}":
635 deflateGlobalsClearDynamicHuffmanState()
636 return stateDeflate, nil
637
638 case strings.HasPrefix(line, cmdH):
639 s := line[len(cmdH):]
640 n, s, ok := parseDeflateWhichHuffman(s)
641 if !ok {
642 break
643 }
644 g.whichHuffman = n
645 return stateDeflateDynamicHuffmanHuffman, nil
Nigel Tao12347602019-06-16 23:15:05 +1000646 }
647
648 if lit, ok := deflateParseLiteral(line); ok {
649 for i := 0; i < len(lit); i++ {
650 s := g.huffmans[2][uint32(lit[i])]
651 if s == "" {
652 return nil, fmt.Errorf("no code for literal %q (%d)", lit[i:i+1], lit[i])
653 }
654 deflateGlobalsWriteDynamicHuffmanBits(s)
655 }
656 return stateDeflateDynamicHuffman, nil
Nigel Taoff7bffc2019-10-13 23:23:48 +1100657
658 } else if l, d, ok := deflateParseLenDist(line); ok {
Nigel Taof86c80e2022-01-08 13:46:27 +1100659 if (l > 10) || (d > 4) {
660 return nil, fmt.Errorf("TODO: support len/dist ExtraBits > 0 for dynamic Huffman blocks")
Nigel Taoff7bffc2019-10-13 23:23:48 +1100661 }
Nigel Taof86c80e2022-01-08 13:46:27 +1100662 // len 3 is code 257, len 4 is code 258, etc. All with no ExtraBits.
663 if s := g.huffmans[2][l+254]; s != "" {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100664 deflateGlobalsWriteDynamicHuffmanBits(s)
665 } else {
Nigel Taof86c80e2022-01-08 13:46:27 +1100666 return nil, fmt.Errorf("no code for literal/length symbol %d", l+254)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100667 }
Nigel Taof86c80e2022-01-08 13:46:27 +1100668 // dist 1 is code 0, dist 2 is code 1, etc. All with no ExtraBits.
669 if s := g.huffmans[3][d-1]; s != "" {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100670 deflateGlobalsWriteDynamicHuffmanBits(s)
671 } else {
Nigel Taof86c80e2022-01-08 13:46:27 +1100672 return nil, fmt.Errorf("no code for distance symbol %d", d-1)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100673 }
674 return stateDeflateDynamicHuffman, nil
675
Nigel Tao12347602019-06-16 23:15:05 +1000676 } else if line == "endOfBlock" {
677 s := g.huffmans[2][256]
678 if s == "" {
679 return nil, fmt.Errorf("no code for end-of-block (256)")
680 }
681 deflateGlobalsWriteDynamicHuffmanBits(s)
682 return stateDeflateDynamicHuffman, nil
683 }
684
685 return nil, fmt.Errorf("bad stateDeflateDynamicHuffman command: %q", line)
686}
687
688func stateDeflateDynamicHuffmanHuffman(line string) (stateFunc, error) {
689 g := &deflateGlobals
690outer:
691 switch {
692 case line == "}":
Nigel Tao92ea6312022-01-09 10:41:31 +1100693 if !deflateGlobalsIsHuffmanCanonical() {
694 return nil, fmt.Errorf(`"huffman %s" is non-canonical`, deflateHuffmanNames[g.whichHuffman])
695 }
Nigel Tao12347602019-06-16 23:15:05 +1000696 g.whichHuffman = 0
Nigel Taoff7bffc2019-10-13 23:23:48 +1100697 g.prevLine = ""
698 g.etcetera = false
Nigel Tao12347602019-06-16 23:15:05 +1000699
700 // If we have all three Huffman tables, write them.
701 for i := 1; ; i++ {
702 if i == 4 {
703 if err := deflateGlobalsWriteDynamicHuffmanTables(); err != nil {
704 return nil, err
705 }
706 break
707 }
708 if g.huffmans[i] == nil {
709 break
710 }
711 }
712
713 return stateDeflateDynamicHuffman, nil
714
Nigel Taoff7bffc2019-10-13 23:23:48 +1100715 case line == "etcetera":
716 g.etcetera = true
717 return stateDeflateDynamicHuffmanHuffman, nil
718
Nigel Tao12347602019-06-16 23:15:05 +1000719 default:
720 s := line
721 n, s, ok := parseNum(s)
722 if !ok || s == "" {
723 break
724 }
725 for i := 0; i < len(s); i++ {
726 if c := s[i]; c != '0' && c != '1' {
727 break outer
728 }
729 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100730 if (len(s) < 1) || (15 < len(s)) {
731 return nil, fmt.Errorf("%q code length, %d, is out of range", s, len(s))
732 }
733
734 if g.etcetera {
735 g.etcetera = false
736 n0, s0, ok := parseNum(g.prevLine)
737 if !ok {
738 return nil, fmt.Errorf("bad etcetera command")
739 }
740 if err := stateDeflateDHHEtcetera(n0, s0, n, s); err != nil {
741 return nil, err
742 }
743 }
744
Nigel Tao12347602019-06-16 23:15:05 +1000745 if g.huffmans[g.whichHuffman] == nil {
746 g.huffmans[g.whichHuffman] = deflateHuffmanTable{}
747 }
748 g.huffmans[g.whichHuffman][n] = s
Nigel Taoff7bffc2019-10-13 23:23:48 +1100749 g.prevLine = line
Nigel Tao12347602019-06-16 23:15:05 +1000750 return stateDeflateDynamicHuffmanHuffman, nil
751 }
752
753 return nil, fmt.Errorf("bad stateDeflateDynamicHuffmanHuffman command: %q", line)
754}
755
Nigel Taoff7bffc2019-10-13 23:23:48 +1100756// stateDeflateDHHEtcetera expands the "etcetera" line in:
757//
758// 0 0000
759// 1 0001
760// etcetera
761// 7 0111
762//
763// to produce the implicit lines:
764//
765// 0 0000
766// 1 0001
767// 2 0010
768// 3 0011
769// 4 0100
770// 5 0101
771// 6 0110
772// 7 0111
773func stateDeflateDHHEtcetera(n0 uint32, s0 string, n1 uint32, s1 string) error {
774 b := []byte(s0)
775 if !incrementBitstring(b) {
776 return fmt.Errorf("etcetera: could not increment bitstring")
777 }
778 for n := n0 + 1; n < n1; n++ {
779 line := fmt.Sprintf("%d %s", n, b)
780 if _, err := stateDeflateDynamicHuffmanHuffman(line); err != nil {
781 return err
782 }
783 if !incrementBitstring(b) {
784 return fmt.Errorf("etcetera: could not increment bitstring")
785 }
786 }
787 if string(b) != s1 {
788 return fmt.Errorf("etcetera: final bitstring: got %q, want %q", b, s1)
789 }
790 return nil
791}
792
793func incrementBitstring(b []byte) (ok bool) {
794 for i := len(b) - 1; i >= 0; i-- {
795 switch b[i] {
796 case '0':
797 b[i] = '1'
798 return true
799 case '1':
800 b[i] = '0'
801 default:
802 return false
803 }
804 }
805 return false
806}
807
Nigel Tao981bf192018-05-23 12:17:55 +1000808type deflateBitStream struct {
809 bits uint32
810 nBits uint32 // Always within [0, 7].
811}
812
813// writeBits writes the low n bits of b to z.
814func (z *deflateBitStream) writeBits(b uint32, n uint32) {
815 if n > 24 {
816 panic("writeBits: n is too large")
817 }
818 z.bits |= b << z.nBits
819 z.nBits += n
820 for z.nBits >= 8 {
821 out = append(out, uint8(z.bits))
822 z.bits >>= 8
823 z.nBits -= 8
824 }
825}
826
827func (z *deflateBitStream) writeBytes(b []byte) {
828 z.flush()
829 out = append(out, b...)
830}
831
Nigel Tao12347602019-06-16 23:15:05 +1000832func (z *deflateBitStream) writeFixedHuffmanLCode(lCode uint32) {
Nigel Tao981bf192018-05-23 12:17:55 +1000833 switch {
834 case lCode < 144: // 0b._0011_0000 through 0b._1011_1111
835 lCode += 0x030
836 z.writeBits(reverse(lCode, 8), 8)
837 case lCode < 256: // 0b1_1001_0000 through 0b1_1111_1111
838 lCode += 0x190 - 144
839 z.writeBits(reverse(lCode, 9), 9)
840 case lCode < 280: // 0b._.000_0000 through 0b._.001_0111
841 lCode -= 256 - 0x000
842 z.writeBits(reverse(lCode, 7), 7)
843 default: // 0b._1100_0000 through 0b._1100_0111
844 lCode -= 280 - 0x0C0
845 z.writeBits(reverse(lCode, 8), 8)
846 }
847}
848
849func (z *deflateBitStream) flush() {
850 if z.nBits > 0 {
851 out = append(out, uint8(z.bits))
852 z.bits = 0
853 z.nBits = 0
854 }
855}
856
857func deflateEncodeLength(l uint32) (code uint32, extra uint32, nExtra uint32) {
858 switch {
859 case l < 3:
860 // No-op.
861 case l < 11:
862 l -= 3
863 return (l >> 0) + 257, l & 0x0000, 0
864 case l < 19:
865 l -= 11
866 return (l >> 1) + 265, l & 0x0001, 1
867 case l < 35:
868 l -= 19
869 return (l >> 2) + 269, l & 0x0003, 2
870 case l < 67:
871 l -= 35
872 return (l >> 3) + 273, l & 0x0007, 3
873 case l < 131:
874 l -= 67
875 return (l >> 4) + 277, l & 0x000F, 4
876 case l < 258:
877 l -= 131
878 return (l >> 5) + 281, l & 0x001F, 5
879 case l == 258:
880 return 285, 0, 0
881 }
882 panic(fmt.Sprintf("deflateEncodeLength: l=%d", l))
883}
884
885func deflateEncodeDistance(d uint32) (code uint32, extra uint32, nExtra uint32) {
886 switch {
887 case d < 1:
888 // No-op.
889 case d < 5:
890 d -= 1
891 return (d >> 0) + 0, d & 0x0000, 0
892 case d < 9:
893 d -= 5
894 return (d >> 1) + 4, d & 0x0001, 1
895 case d < 17:
896 d -= 9
897 return (d >> 2) + 6, d & 0x0003, 2
898 case d < 33:
899 d -= 17
900 return (d >> 3) + 8, d & 0x0007, 3
901 case d < 65:
902 d -= 33
903 return (d >> 4) + 10, d & 0x000F, 4
904 case d < 129:
905 d -= 65
906 return (d >> 5) + 12, d & 0x001F, 5
907 case d < 257:
908 d -= 129
909 return (d >> 6) + 14, d & 0x003F, 6
910 case d < 513:
911 d -= 257
912 return (d >> 7) + 16, d & 0x007F, 7
913 case d < 1025:
914 d -= 513
915 return (d >> 8) + 18, d & 0x00FF, 8
916 case d < 2049:
917 d -= 1025
918 return (d >> 9) + 20, d & 0x01FF, 9
919 case d < 4097:
920 d -= 2049
921 return (d >> 10) + 22, d & 0x03FF, 10
922 case d < 8193:
923 d -= 4097
924 return (d >> 11) + 24, d & 0x07FF, 11
925 case d < 16385:
926 d -= 8193
927 return (d >> 12) + 26, d & 0x0FFF, 12
928 case d < 32769:
929 d -= 16385
930 return (d >> 13) + 28, d & 0x1FFF, 13
931 }
932 panic(fmt.Sprintf("deflateEncodeDistance: d=%d", d))
933}
934
935func deflateParseLiteral(s string) (lit string, ok bool) {
Nigel Tao12347602019-06-16 23:15:05 +1000936 // TODO: support "\xAB" escape codes in the script?
Nigel Tao981bf192018-05-23 12:17:55 +1000937 const (
938 prefix = `literal "`
939 suffix = `"`
940 )
941 if strings.HasPrefix(s, prefix) {
942 s = s[len(prefix):]
943 if strings.HasSuffix(s, suffix) {
944 return s[:len(s)-len(suffix)], true
945 }
946 }
947 return "", false
948}
949
950func deflateParseLenDist(line string) (l uint32, d uint32, ok bool) {
951 const (
952 lStr = "len "
953 dStr = "dist "
954 )
955 s := line
956 if strings.HasPrefix(s, lStr) {
957 s = s[len(lStr):]
958 if l, s, ok := parseNum(s); ok && 3 <= l && l <= 258 {
959 if strings.HasPrefix(s, dStr) {
960 s = s[len(dStr):]
961 if d, s, ok := parseNum(s); ok && 1 <= d && d <= 32768 && s == "" {
962 return l, d, true
963 }
964 }
965 }
966 }
967 return 0, 0, false
968}
969
970// ----
971
972func init() {
973 formats["gif"] = stateGif
974}
975
976var gifGlobals struct {
Nigel Tao31a2bc92019-05-18 17:32:24 +1000977 imageWidth uint32
978 imageHeight uint32
979 imageBackgroundColorIndex uint32
Nigel Tao981bf192018-05-23 12:17:55 +1000980
Nigel Taoefb18032019-10-05 11:40:24 +1000981 frameInterlaced bool
982 frameLeft uint32
983 frameTop uint32
984 frameWidth uint32
985 frameHeight uint32
Nigel Tao981bf192018-05-23 12:17:55 +1000986
987 globalPalette [][4]uint8
Nigel Tao746681e2019-05-04 13:49:22 +1000988 localPalette [][4]uint8
Nigel Tao981bf192018-05-23 12:17:55 +1000989}
990
991func stateGif(line string) (stateFunc, error) {
992 const (
Nigel Tao6700c722019-05-26 09:41:55 +1000993 cmdB = "bytes "
994 cmdGC = "graphicControl "
995 cmdL = "lzw "
996 cmdLC = "loopCount "
Nigel Tao981bf192018-05-23 12:17:55 +1000997 )
998outer:
999 switch {
Nigel Tao12347602019-06-16 23:15:05 +10001000 case line == "":
1001 return stateGif, nil
1002
Nigel Tao981bf192018-05-23 12:17:55 +10001003 case line == "frame {":
Nigel Taoefb18032019-10-05 11:40:24 +10001004 gifGlobals.frameInterlaced = false
1005 gifGlobals.frameLeft = 0
1006 gifGlobals.frameTop = 0
1007 gifGlobals.frameWidth = 0
1008 gifGlobals.frameHeight = 0
Nigel Tao746681e2019-05-04 13:49:22 +10001009 gifGlobals.localPalette = nil
Nigel Tao981bf192018-05-23 12:17:55 +10001010 out = append(out, 0x2C)
1011 return stateGifFrame, nil
1012
1013 case line == "header":
1014 out = append(out, "GIF89a"...)
1015 return stateGif, nil
1016
1017 case line == "image {":
1018 return stateGifImage, nil
1019
1020 case line == "trailer":
1021 out = append(out, 0x3B)
1022 return stateGif, nil
1023
Nigel Tao21f28902019-04-20 16:33:11 +10001024 case strings.HasPrefix(line, cmdB):
1025 s := line[len(cmdB):]
1026 for s != "" {
1027 x, ok := uint32(0), false
1028 x, s, ok = parseHex(s)
1029 if !ok {
1030 break outer
1031 }
1032 out = append(out, uint8(x))
1033 }
1034 return stateGif, nil
1035
Nigel Tao6700c722019-05-26 09:41:55 +10001036 case strings.HasPrefix(line, cmdGC):
1037 s := line[len(cmdGC):]
1038
1039 flags := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +10001040 duration := uint32(0)
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001041 transparentIndex := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +10001042 for s != "" {
1043 term := ""
1044 if i := strings.IndexByte(s, ' '); i >= 0 {
1045 term, s = s[:i], s[i+1:]
1046 } else {
1047 term, s = s, ""
1048 }
1049
1050 const (
1051 ms = "ms"
1052 trans = "transparentIndex="
1053 )
1054 switch {
1055 case term == "animationDisposalNone":
Nigel Tao6700c722019-05-26 09:41:55 +10001056 flags |= 0x00
Nigel Tao344123b2020-09-25 23:00:01 +10001057 case term == "animationDisposalRestoreBackground":
Nigel Tao6700c722019-05-26 09:41:55 +10001058 flags |= 0x08
Nigel Tao344123b2020-09-25 23:00:01 +10001059 case term == "animationDisposalRestorePrevious":
Nigel Tao6700c722019-05-26 09:41:55 +10001060 flags |= 0x0C
Nigel Tao344123b2020-09-25 23:00:01 +10001061 case strings.HasPrefix(term, trans):
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001062 num, err := strconv.ParseUint(term[len(trans):], 0, 8)
1063 if err != nil {
Nigel Tao344123b2020-09-25 23:00:01 +10001064 break outer
1065 }
1066 flags |= 0x01
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001067 transparentIndex = uint8(num)
Nigel Tao344123b2020-09-25 23:00:01 +10001068 case strings.HasSuffix(term, ms):
1069 num, remaining, ok := parseNum(term[:len(term)-len(ms)])
1070 if !ok || remaining != "" {
1071 break outer
1072 }
1073 duration = num / 10 // GIF's unit of time is 10ms.
Nigel Tao6700c722019-05-26 09:41:55 +10001074 default:
1075 break outer
1076 }
Nigel Tao6700c722019-05-26 09:41:55 +10001077 }
1078
Nigel Taob1ff27f2019-05-12 22:00:44 +10001079 out = append(out,
Nigel Tao6700c722019-05-26 09:41:55 +10001080 0x21, 0xF9, 0x04,
1081 flags,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001082 uint8(duration>>0),
1083 uint8(duration>>8),
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001084 transparentIndex,
Nigel Tao6700c722019-05-26 09:41:55 +10001085 0x00,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001086 )
1087 return stateGif, nil
1088
Nigel Tao981bf192018-05-23 12:17:55 +10001089 case strings.HasPrefix(line, cmdL):
1090 s := line[len(cmdL):]
1091 litWidth, s, ok := parseNum(s)
1092 if !ok || litWidth < 2 || 8 < litWidth {
1093 break
1094 }
1095 out = append(out, uint8(litWidth))
1096
1097 uncompressed := []byte(nil)
1098 for s != "" {
1099 x := uint32(0)
1100 x, s, ok = parseHex(s)
1101 if !ok {
1102 break outer
1103 }
1104 uncompressed = append(uncompressed, uint8(x))
1105 }
1106
1107 buf := bytes.NewBuffer(nil)
1108 w := lzw.NewWriter(buf, lzw.LSB, int(litWidth))
1109 if _, err := w.Write(uncompressed); err != nil {
1110 return nil, err
1111 }
1112 if err := w.Close(); err != nil {
1113 return nil, err
1114 }
1115 compressed := buf.Bytes()
1116
1117 for len(compressed) > 0 {
1118 if len(compressed) <= 0xFF {
1119 out = append(out, uint8(len(compressed)))
1120 out = append(out, compressed...)
1121 compressed = nil
1122 } else {
1123 out = append(out, 0xFF)
1124 out = append(out, compressed[:0xFF]...)
1125 compressed = compressed[0xFF:]
1126 }
1127 }
1128 out = append(out, 0x00)
1129 return stateGif, nil
Nigel Tao7be9c392018-10-13 17:00:45 +11001130
1131 case strings.HasPrefix(line, cmdLC):
1132 s := line[len(cmdLC):]
1133 loopCount, _, ok := parseNum(s)
1134 if !ok || 0xFFFF < loopCount {
1135 break
1136 }
1137 out = append(out,
1138 0x21, // Extension Introducer.
1139 0xFF, // Application Extension Label.
1140 0x0B, // Block Size.
1141 )
1142 out = append(out, "NETSCAPE2.0"...)
1143 out = append(out,
1144 0x03, // Block Size.
1145 0x01, // Magic Number.
1146 byte(loopCount),
1147 byte(loopCount>>8),
1148 0x00, // Block Terminator.
1149 )
1150 return stateGif, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001151 }
1152
1153 return nil, fmt.Errorf("bad stateGif command: %q", line)
1154}
1155
1156func stateGifImage(line string) (stateFunc, error) {
1157 g := &gifGlobals
1158 if line == "}" {
1159 out = appendU16LE(out, uint16(g.imageWidth))
1160 out = appendU16LE(out, uint16(g.imageHeight))
1161 if g.globalPalette == nil {
1162 out = append(out, 0x00)
1163 } else if n := log2(uint32(len(g.globalPalette))); n < 2 || 8 < n {
1164 return nil, fmt.Errorf("bad len(g.globalPalette): %d", len(g.globalPalette))
1165 } else {
1166 out = append(out, 0x80|uint8(n-1))
1167 }
Nigel Tao31a2bc92019-05-18 17:32:24 +10001168 out = append(out, uint8(g.imageBackgroundColorIndex))
Nigel Tao981bf192018-05-23 12:17:55 +10001169 out = append(out, 0x00)
1170 for _, x := range g.globalPalette {
1171 out = append(out, x[0], x[1], x[2])
1172 }
1173 return stateGif, nil
1174 }
1175
1176 const (
Nigel Tao31a2bc92019-05-18 17:32:24 +10001177 cmdBCI = "backgroundColorIndex "
Nigel Tao981bf192018-05-23 12:17:55 +10001178 cmdIWH = "imageWidthHeight "
1179 cmdP = "palette {"
1180 )
1181 switch {
Nigel Tao31a2bc92019-05-18 17:32:24 +10001182 case strings.HasPrefix(line, cmdBCI):
1183 s := line[len(cmdBCI):]
1184 if i, _, ok := parseNum(s); ok {
1185 g.imageBackgroundColorIndex = i
1186 }
1187 return stateGifImage, nil
1188
Nigel Tao981bf192018-05-23 12:17:55 +10001189 case strings.HasPrefix(line, cmdIWH):
1190 s := line[len(cmdIWH):]
1191 if w, s, ok := parseNum(s); ok {
1192 if h, _, ok := parseNum(s); ok {
1193 g.imageWidth = w
1194 g.imageHeight = h
1195 return stateGifImage, nil
1196 }
1197 }
1198
1199 case strings.HasPrefix(line, cmdP):
1200 return stateGifImagePalette, nil
1201 }
1202
1203 return nil, fmt.Errorf("bad stateGifImage command: %q", line)
1204}
1205
1206func stateGifFrame(line string) (stateFunc, error) {
1207 g := &gifGlobals
1208 if line == "}" {
1209 out = appendU16LE(out, uint16(g.frameLeft))
1210 out = appendU16LE(out, uint16(g.frameTop))
1211 out = appendU16LE(out, uint16(g.frameWidth))
1212 out = appendU16LE(out, uint16(g.frameHeight))
Nigel Taoefb18032019-10-05 11:40:24 +10001213 flags := byte(0x00)
1214 if g.frameInterlaced {
1215 flags |= 0x40
1216 }
Nigel Tao746681e2019-05-04 13:49:22 +10001217 if g.localPalette == nil {
Nigel Taoefb18032019-10-05 11:40:24 +10001218 out = append(out, flags)
Nigel Tao746681e2019-05-04 13:49:22 +10001219 } else if n := log2(uint32(len(g.localPalette))); n < 2 || 8 < n {
1220 return nil, fmt.Errorf("bad len(g.localPalette): %d", len(g.localPalette))
1221 } else {
Nigel Taoefb18032019-10-05 11:40:24 +10001222 out = append(out, flags|0x80|uint8(n-1))
Nigel Tao746681e2019-05-04 13:49:22 +10001223 }
1224 for _, x := range g.localPalette {
1225 out = append(out, x[0], x[1], x[2])
1226 }
Nigel Tao981bf192018-05-23 12:17:55 +10001227 return stateGif, nil
1228 }
1229
1230 const (
1231 cmdFLTWH = "frameLeftTopWidthHeight "
Nigel Tao746681e2019-05-04 13:49:22 +10001232 cmdP = "palette {"
Nigel Tao981bf192018-05-23 12:17:55 +10001233 )
1234 switch {
Nigel Taoefb18032019-10-05 11:40:24 +10001235 case line == "interlaced":
1236 g.frameInterlaced = true
1237 return stateGifFrame, nil
1238
Nigel Tao981bf192018-05-23 12:17:55 +10001239 case strings.HasPrefix(line, cmdFLTWH):
1240 s := line[len(cmdFLTWH):]
1241 if l, s, ok := parseNum(s); ok {
1242 if t, s, ok := parseNum(s); ok {
1243 if w, s, ok := parseNum(s); ok {
1244 if h, _, ok := parseNum(s); ok {
1245 g.frameLeft = l
1246 g.frameTop = t
1247 g.frameWidth = w
1248 g.frameHeight = h
1249 return stateGifFrame, nil
1250 }
1251 }
1252 }
1253 }
Nigel Tao746681e2019-05-04 13:49:22 +10001254
1255 case strings.HasPrefix(line, cmdP):
1256 return stateGifFramePalette, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001257 }
1258
1259 return nil, fmt.Errorf("bad stateGifFrame command: %q", line)
1260}
1261
1262func stateGifImagePalette(line string) (stateFunc, error) {
1263 g := &gifGlobals
1264 if line == "}" {
1265 return stateGifImage, nil
1266 }
1267
1268 s := line
1269 if rgb0, s, ok := parseHex(s); ok {
1270 if rgb1, s, ok := parseHex(s); ok {
1271 if rgb2, _, ok := parseHex(s); ok {
1272 g.globalPalette = append(g.globalPalette,
1273 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1274 return stateGifImagePalette, nil
1275 }
1276 }
1277 }
1278
1279 return nil, fmt.Errorf("bad stateGifImagePalette command: %q", line)
1280}
Nigel Tao746681e2019-05-04 13:49:22 +10001281
1282func stateGifFramePalette(line string) (stateFunc, error) {
1283 g := &gifGlobals
1284 if line == "}" {
1285 return stateGifFrame, nil
1286 }
1287
1288 s := line
1289 if rgb0, s, ok := parseHex(s); ok {
1290 if rgb1, s, ok := parseHex(s); ok {
1291 if rgb2, _, ok := parseHex(s); ok {
1292 g.localPalette = append(g.localPalette,
1293 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1294 return stateGifFramePalette, nil
1295 }
1296 }
1297 }
1298
1299 return nil, fmt.Errorf("bad stateGifFramePalette command: %q", line)
1300}
Nigel Tao6c2a5912021-11-16 11:00:48 +11001301
1302// ----
1303
1304func init() {
1305 formats["png"] = statePng
1306}
1307
1308var pngGlobals struct {
1309 scratch [4]byte
1310
1311 animSeqNum uint32
1312 chunkData bytes.Buffer
1313 chunkType string
1314 zlibWriter *zlib.Writer
1315}
1316
1317func statePng(line string) (stateFunc, error) {
1318 g := &pngGlobals
1319 switch {
1320 case line == "":
1321 return statePng, nil
1322
1323 case line == "magic":
1324 out = append(out, "\x89PNG\x0D\x0A\x1A\x0A"...)
1325 return statePng, nil
1326
1327 case (len(line) == 6) && (line[4] == ' ') && (line[5] == '{'):
1328 g.chunkData.Reset()
1329 g.chunkType = line[:4]
1330 return statePngChunk, nil
1331 }
1332
1333 return nil, fmt.Errorf("bad statePng command: %q", line)
1334}
1335
1336func statePngChunk(line string) (stateFunc, error) {
1337 g := &pngGlobals
1338 if line == "}" {
1339 if n := g.chunkData.Len(); n > 0xFFFF_FFFF {
1340 return nil, fmt.Errorf("chunkData is too long")
1341 }
1342 out = appendU32BE(out, uint32(g.chunkData.Len()))
1343 n := len(out)
1344 out = append(out, g.chunkType...)
1345 out = append(out, g.chunkData.Bytes()...)
1346 out = appendU32BE(out, crc32.ChecksumIEEE(out[n:]))
1347 return statePng, nil
1348 }
1349
1350 switch {
1351 case line == "animSeqNum++":
1352 g.chunkData.Write(appendU32BE(g.scratch[:0], g.animSeqNum))
1353 g.animSeqNum++
1354 return statePngChunk, nil
1355
1356 case line == "raw {":
1357 return statePngRaw, nil
1358
1359 case line == "zlib {":
1360 g.zlibWriter = zlib.NewWriter(&g.chunkData)
1361 return statePngZlib, nil
1362 }
1363
1364 return nil, fmt.Errorf("bad statePngChunk command: %q", line)
1365}
1366
1367func statePngRaw(line string) (stateFunc, error) {
1368 g := &pngGlobals
1369 if line == "}" {
1370 return statePngChunk, nil
1371 }
1372
1373 for s := line; s != ""; {
1374 if x, remaining, ok := parseHex(s); ok {
1375 g.chunkData.WriteByte(byte(x))
1376 s = remaining
1377 } else {
1378 return nil, fmt.Errorf("bad statePngRaw command: %q", line)
1379 }
1380 }
1381 return statePngRaw, nil
1382}
1383
1384func statePngZlib(line string) (stateFunc, error) {
1385 g := &pngGlobals
1386 if line == "}" {
1387 if err := g.zlibWriter.Close(); err != nil {
1388 return nil, fmt.Errorf("statePngZlib: %w", err)
1389 }
1390 return statePngChunk, nil
1391 }
1392
1393 for s := line; s != ""; {
1394 if x, remaining, ok := parseHex(s); ok {
1395 g.scratch[0] = byte(x)
1396 g.zlibWriter.Write(g.scratch[:1])
1397 s = remaining
1398 } else {
1399 return nil, fmt.Errorf("bad statePngZlib command: %q", line)
1400 }
1401 }
1402 return statePngZlib, nil
1403}