blob: 575d0c0819fb56e35f9d4f3d0ef45dd5bf5d1a9d [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"
34 "strconv"
35 "strings"
36)
37
38type stateFunc func(line string) (stateFunc, error)
39
40type repeat struct {
41 count uint32
42 remaining string
43}
44
45var (
46 state stateFunc
47 repeats []repeat
48 out []byte
49 formats = map[string]stateFunc{}
50)
51
52func main() {
53 if err := main1(); err != nil {
54 os.Stderr.WriteString(err.Error() + "\n")
55 os.Exit(1)
56 }
57}
58
59func main1() error {
Nigel Tao226c4762021-08-22 11:05:43 +100060 stdin, err := io.ReadAll(os.Stdin)
Nigel Tao981bf192018-05-23 12:17:55 +100061 if err != nil {
62 return err
63 }
64 s := string(stdin)
65 for remaining := ""; len(s) > 0; s, remaining = remaining, "" {
66 if i := strings.IndexByte(s, '\n'); i >= 0 {
67 s, remaining = s[:i], s[i+1:]
68 }
69 s = strings.TrimSpace(s)
70 if s == "" || s[0] == '#' {
71 continue
72 }
73
74 if state == nil {
75 const m = "make "
76 if !strings.HasPrefix(s, m) {
77 return fmt.Errorf(`input must start with "make foo"`)
78 }
79 s = s[len(m):]
80 state = formats[s]
81 if state == nil {
82 return fmt.Errorf("unsupported format %q", s)
83 }
84 continue
85 }
86
87 const rep = "repeat "
88 if strings.HasPrefix(s, rep) {
89 args := s[len(rep):]
90 count, args, ok := parseNum(args)
91 if !ok || count <= 0 || args != "[" {
92 return fmt.Errorf("bad repeat command: %q", s)
93 }
94 repeats = append(repeats, repeat{
95 count: count,
96 remaining: remaining,
97 })
98 continue
99 }
100
101 if s == "]" {
102 if len(repeats) <= 0 {
103 return fmt.Errorf(`unbalanced close-repeat command: "]"`)
104 }
105 i := len(repeats) - 1
106 repeats[i].count--
107 if repeats[i].count == 0 {
108 repeats = repeats[:i]
109 } else {
110 remaining = repeats[i].remaining
111 }
112 continue
113 }
114
115 state, err = state(s)
116 if err != nil {
117 return err
118 }
119 if state == nil {
120 return fmt.Errorf("bad state transition")
121 }
122 }
123
Nigel Tao12347602019-06-16 23:15:05 +1000124 if state == nil {
125 return fmt.Errorf("no 'make' line")
126 } else {
127 state, err = state("")
128 if err != nil {
129 return err
130 }
131 }
132
Nigel Tao981bf192018-05-23 12:17:55 +1000133 _, err = os.Stdout.Write(out)
134 return err
135}
136
137// ----
138
139func appendU16LE(b []byte, u uint16) []byte {
140 return append(b, uint8(u), uint8(u>>8))
141}
142
Nigel Tao6c2a5912021-11-16 11:00:48 +1100143func appendU32BE(b []byte, u uint32) []byte {
144 return append(b, uint8(u>>24), uint8(u>>16), uint8(u>>8), uint8(u))
145}
146
Nigel Tao981bf192018-05-23 12:17:55 +1000147func log2(u uint32) (i int32) {
148 for i, pow := uint32(0), uint32(1); i < 32; i, pow = i+1, pow<<1 {
149 if u == pow {
150 return int32(i)
151 }
152 if u < pow {
153 break
154 }
155 }
156 return -1
157}
158
159func parseHex(s string) (num uint32, remaining string, ok bool) {
160 if i := strings.IndexByte(s, ' '); i >= 0 {
161 s, remaining = s[:i], s[i+1:]
162 for len(remaining) > 0 && remaining[0] == ' ' {
163 remaining = remaining[1:]
164 }
165 }
166
167 if len(s) < 2 || s[0] != '0' || s[1] != 'x' {
168 return 0, "", false
169 }
170 s = s[2:]
171
172 u, err := strconv.ParseUint(s, 16, 32)
173 if err != nil {
174 return 0, "", false
175 }
176 return uint32(u), remaining, true
177}
178
179func parseNum(s string) (num uint32, remaining string, ok bool) {
180 if i := strings.IndexByte(s, ' '); i >= 0 {
181 s, remaining = s[:i], s[i+1:]
182 for len(remaining) > 0 && remaining[0] == ' ' {
183 remaining = remaining[1:]
184 }
185 }
186
187 u, err := strconv.ParseUint(s, 10, 32)
188 if err != nil {
189 return 0, "", false
190 }
191 return uint32(u), remaining, true
192}
193
194func reverse(x uint32, n uint32) uint32 {
195 x = uint32(reverse8[0xFF&(x>>0)])<<24 |
196 uint32(reverse8[0xFF&(x>>8)])<<16 |
197 uint32(reverse8[0xFF&(x>>16)])<<8 |
198 uint32(reverse8[0xFF&(x>>24)])<<0
199 return x >> (32 - n)
200}
201
202var reverse8 = [256]byte{
203 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, // 0x00 - 0x07
204 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, // 0x08 - 0x0F
205 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, // 0x10 - 0x17
206 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, // 0x18 - 0x1F
207 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, // 0x20 - 0x27
208 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, // 0x28 - 0x2F
209 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, // 0x30 - 0x37
210 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, // 0x38 - 0x3F
211 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, // 0x40 - 0x47
212 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, // 0x48 - 0x4F
213 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, // 0x50 - 0x57
214 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, // 0x58 - 0x5F
215 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, // 0x60 - 0x67
216 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, // 0x68 - 0x6F
217 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, // 0x70 - 0x77
218 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, // 0x78 - 0x7F
219 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, // 0x80 - 0x87
220 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, // 0x88 - 0x8F
221 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, // 0x90 - 0x97
222 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, // 0x98 - 0x9F
223 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, // 0xA0 - 0xA7
224 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, // 0xA8 - 0xAF
225 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, // 0xB0 - 0xB7
226 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, // 0xB8 - 0xBF
227 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, // 0xC0 - 0xC7
228 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, // 0xC8 - 0xCF
229 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, // 0xD0 - 0xD7
230 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, // 0xD8 - 0xDF
231 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, // 0xE0 - 0xE7
232 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, // 0xE8 - 0xEF
233 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, // 0xF0 - 0xF7
234 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, // 0xF8 - 0xFF
235}
236
237// ----
238
239func init() {
240 formats["deflate"] = stateDeflate
241}
242
Nigel Tao12347602019-06-16 23:15:05 +1000243var deflateCodeOrder = [19]uint32{
244 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
245}
246
247type deflateHuffmanTable map[uint32]string
248
Nigel Tao981bf192018-05-23 12:17:55 +1000249var deflateGlobals struct {
250 bncData []byte
251 stream deflateBitStream
Nigel Tao12347602019-06-16 23:15:05 +1000252
253 // Dynamic Huffman state.
Nigel Taoff7bffc2019-10-13 23:23:48 +1100254 whichHuffman uint32
Nigel Taodc1218f2019-10-14 09:28:28 +1100255 // 0=Unused, 1=CodeLength, 2=Literal/Length, 3=Distance.
Nigel Tao12347602019-06-16 23:15:05 +1000256 huffmans [4]deflateHuffmanTable
Nigel Taoff7bffc2019-10-13 23:23:48 +1100257
258 // DHH (Dynamic Huffman, inside a Huffman table) state.
259 prevLine string
260 etcetera bool
Nigel Tao12347602019-06-16 23:15:05 +1000261}
262
263func deflateGlobalsClearDynamicHuffmanState() {
Nigel Tao12347602019-06-16 23:15:05 +1000264 deflateGlobals.whichHuffman = 0
265 deflateGlobals.huffmans = [4]deflateHuffmanTable{}
Nigel Taoff7bffc2019-10-13 23:23:48 +1100266 deflateGlobals.prevLine = ""
267 deflateGlobals.etcetera = false
268}
269
270func deflateGlobalsCountCodes() (numLCodes uint32, numDCodes uint32, numCLCodeLengths uint32, retErr error) {
271 for k := range deflateGlobals.huffmans[2] {
272 if numLCodes < (k + 1) {
273 numLCodes = (k + 1)
274 }
275 }
276
277 for k := range deflateGlobals.huffmans[3] {
278 if numDCodes < (k + 1) {
279 numDCodes = (k + 1)
280 }
281 }
282
283 for k := range deflateGlobals.huffmans[1] {
284 if (k < 0) || (18 < k) {
285 return 0, 0, 0, fmt.Errorf("bad CodeLength: %d", k)
286 }
287 }
288 for i := len(deflateCodeOrder) - 1; i >= 0; i-- {
289 cl := deflateCodeOrder[i]
290 if _, ok := deflateGlobals.huffmans[1][cl]; ok {
291 numCLCodeLengths = uint32(i + 1)
292 break
293 }
294 }
295 if numCLCodeLengths < 4 {
296 numCLCodeLengths = 4
297 }
298
299 return numLCodes, numDCodes, numCLCodeLengths, nil
Nigel Tao12347602019-06-16 23:15:05 +1000300}
301
302func deflateGlobalsWriteDynamicHuffmanTables() error {
303 g := &deflateGlobals
Nigel Taoff7bffc2019-10-13 23:23:48 +1100304 numLCodes, numDCodes, numCLCodeLengths, err := deflateGlobalsCountCodes()
305 if err != nil {
306 return err
Nigel Tao12347602019-06-16 23:15:05 +1000307 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100308 if (numLCodes < 257) || (257+31 < numLCodes) {
309 return fmt.Errorf("bad numLCodes: %d", numLCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000310 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100311 g.stream.writeBits(numLCodes-257, 5)
312 if (numDCodes < 1) || (1+31 < numDCodes) {
313 return fmt.Errorf("bad numDCodes: %d", numDCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000314 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100315 g.stream.writeBits(numDCodes-1, 5)
316 if (numCLCodeLengths < 4) || (4+15 < numCLCodeLengths) {
317 return fmt.Errorf("bad numCLCodeLengths: %d", numCLCodeLengths)
318 }
319 g.stream.writeBits(numCLCodeLengths-4, 4)
Nigel Tao12347602019-06-16 23:15:05 +1000320
321 // Write the Huffman table for CodeLength.
322 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100323 for i := uint32(0); i < numCLCodeLengths; i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000324 n := len(g.huffmans[1][deflateCodeOrder[i]])
325 g.stream.writeBits(uint32(n), 3)
326 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100327 for i := numCLCodeLengths; i < uint32(len(deflateCodeOrder)); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000328 n := len(g.huffmans[1][deflateCodeOrder[i]])
329 if n > 0 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100330 return fmt.Errorf("short numCLCodeLengths: %d", numCLCodeLengths)
Nigel Tao12347602019-06-16 23:15:05 +1000331 }
332 }
333 }
334
Nigel Taodc1218f2019-10-14 09:28:28 +1100335 // Write the Huffman tables for Literal/Length and Distance.
Nigel Tao12347602019-06-16 23:15:05 +1000336 {
337 numZeroes := uint32(0)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100338 for i := uint32(0); i < numLCodes+numDCodes; i++ {
339 codeLen := uint32(0)
340 if i < numLCodes {
341 codeLen = uint32(len(g.huffmans[2][i]))
Nigel Tao12347602019-06-16 23:15:05 +1000342 } else {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100343 codeLen = uint32(len(g.huffmans[3][i-numLCodes]))
Nigel Tao12347602019-06-16 23:15:05 +1000344 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100345 if codeLen == 0 {
Nigel Tao12347602019-06-16 23:15:05 +1000346 numZeroes++
347 continue
348 }
349
350 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
351 return err
352 }
353 numZeroes = 0
354
Nigel Taoff7bffc2019-10-13 23:23:48 +1100355 codeLenCode := g.huffmans[1][codeLen]
356 if codeLenCode == "" {
357 return fmt.Errorf("no code for code-length %d", codeLen)
358 }
359 deflateGlobalsWriteDynamicHuffmanBits(g.huffmans[1][codeLen])
Nigel Tao12347602019-06-16 23:15:05 +1000360 }
361 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
362 return err
363 }
364 }
365
366 return nil
367}
368
369func deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes uint32) error {
370 g := &deflateGlobals
371 if numZeroes == 0 {
372 return nil
373 }
374
375 if s := g.huffmans[1][18]; s != "" {
376 for numZeroes >= 11 {
377 extra := numZeroes - 11
378 if extra > 127 {
379 extra = 127
380 }
381 deflateGlobalsWriteDynamicHuffmanBits(s)
382 g.stream.writeBits(extra, 7)
383 numZeroes -= 11 + extra
384 }
385 }
386
387 if s := g.huffmans[1][17]; s != "" {
388 for numZeroes >= 3 {
389 extra := numZeroes - 3
390 if extra > 7 {
391 extra = 7
392 }
393 deflateGlobalsWriteDynamicHuffmanBits(s)
394 g.stream.writeBits(extra, 3)
395 numZeroes -= 3 + extra
396 }
397 }
398
399 if s := g.huffmans[1][0]; s != "" {
400 for ; numZeroes > 0; numZeroes-- {
401 deflateGlobalsWriteDynamicHuffmanBits(s)
402 }
403 }
404
405 if numZeroes > 0 {
406 return fmt.Errorf("could not write a run of zero-valued code lengths")
407 }
408 return nil
409}
410
411func deflateGlobalsWriteDynamicHuffmanBits(s string) {
412 g := &deflateGlobals
413 for i := 0; i < len(s); i++ {
414 g.stream.writeBits(uint32(s[i]&1), 1)
415 }
416}
417
418func parseDeflateWhichHuffman(s string) (num uint32, remaining string, ok bool) {
419 if i := strings.IndexByte(s, ' '); i >= 0 {
420 s, remaining = s[:i], s[i+1:]
421 for len(remaining) > 0 && remaining[0] == ' ' {
422 remaining = remaining[1:]
423 }
424 }
425
426 switch s {
427 case "CodeLength":
428 return 1, remaining, true
Nigel Taodc1218f2019-10-14 09:28:28 +1100429 case "Literal/Length":
Nigel Tao12347602019-06-16 23:15:05 +1000430 return 2, remaining, true
431 case "Distance":
432 return 3, remaining, true
433 }
434 return 0, "", false
Nigel Tao981bf192018-05-23 12:17:55 +1000435}
436
437func stateDeflate(line string) (stateFunc, error) {
438 g := &deflateGlobals
439 const (
Nigel Tao0cc17982019-05-19 23:43:20 +1000440 cmdB = "bytes "
Nigel Tao12347602019-06-16 23:15:05 +1000441 cmdBDH = "blockDynamicHuffman "
Nigel Tao981bf192018-05-23 12:17:55 +1000442 cmdBFH = "blockFixedHuffman "
Nigel Tao12347602019-06-16 23:15:05 +1000443 cmdBNC = "blockNoCompression "
Nigel Tao981bf192018-05-23 12:17:55 +1000444 )
445 bits := uint32(0)
446 s := ""
447
448 retState := stateFunc(nil)
449 switch {
Nigel Tao12347602019-06-16 23:15:05 +1000450 case line == "":
451 g.stream.flush()
452 return stateDeflate, nil
453
Nigel Tao0cc17982019-05-19 23:43:20 +1000454 case strings.HasPrefix(line, cmdB):
455 s := line[len(cmdB):]
456 for s != "" {
457 x, ok := uint32(0), false
458 x, s, ok = parseHex(s)
459 if !ok {
460 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
461 }
462 out = append(out, uint8(x))
463 }
464 return stateDeflate, nil
465
Nigel Tao981bf192018-05-23 12:17:55 +1000466 case strings.HasPrefix(line, cmdBNC):
467 s = line[len(cmdBNC):]
468 retState = stateDeflateNoCompression
469 bits |= 0 << 1
470 case strings.HasPrefix(line, cmdBFH):
471 s = line[len(cmdBFH):]
472 retState = stateDeflateFixedHuffman
473 bits |= 1 << 1
Nigel Tao12347602019-06-16 23:15:05 +1000474 case strings.HasPrefix(line, cmdBDH):
475 s = line[len(cmdBDH):]
476 retState = stateDeflateDynamicHuffman
477 bits |= 2 << 1
Nigel Tao981bf192018-05-23 12:17:55 +1000478 default:
479 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
480 }
481
482 switch s {
483 case "(final) {":
484 bits |= 1
485 case "(nonFinal) {":
486 // No-op.
487 default:
488 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
489 }
490
491 g.stream.writeBits(bits, 3)
492 return retState, nil
493}
494
495func stateDeflateNoCompression(line string) (stateFunc, error) {
496 g := &deflateGlobals
497 if line == "}" {
498 if len(g.bncData) > 0xFFFF {
499 return nil, fmt.Errorf("bncData is too long")
500 }
501 n := uint32(len(g.bncData))
502 g.stream.flush()
503 g.stream.writeBits(n, 16)
504 g.stream.writeBits(0xFFFF-n, 16)
505 g.stream.writeBytes(g.bncData)
506 g.bncData = g.bncData[:0]
507 return stateDeflate, nil
508 }
509
510 if lit, ok := deflateParseLiteral(line); ok {
511 g.bncData = append(g.bncData, lit...)
512 return stateDeflateNoCompression, nil
513 }
514
515 return nil, fmt.Errorf("bad blockNoCompression command: %q", line)
516}
517
518func stateDeflateFixedHuffman(line string) (stateFunc, error) {
519 g := &deflateGlobals
520 if line == "}" {
Nigel Tao981bf192018-05-23 12:17:55 +1000521 return stateDeflate, nil
522 }
523
524 if line == "endOfBlock" {
525 g.stream.writeBits(0, 7)
526 return stateDeflateFixedHuffman, nil
527 }
528
529 if lit, ok := deflateParseLiteral(line); ok {
Nigel Tao981bf192018-05-23 12:17:55 +1000530 for i := 0; i < len(lit); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000531 g.stream.writeFixedHuffmanLCode(uint32(lit[i]))
Nigel Tao981bf192018-05-23 12:17:55 +1000532 }
533 return stateDeflateFixedHuffman, nil
534 }
535
Nigel Tao11a68192019-02-02 13:04:09 +1100536 if line == "len 3 distCode 31" {
537 lCode, lExtra, lNExtra := deflateEncodeLength(3)
Nigel Tao12347602019-06-16 23:15:05 +1000538 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao11a68192019-02-02 13:04:09 +1100539 g.stream.writeBits(lExtra, lNExtra)
540 dCode, dExtra, dNExtra := uint32(31), uint32(0), uint32(0)
541 g.stream.writeBits(reverse(dCode, 5), 5)
542 g.stream.writeBits(dExtra, dNExtra)
543 return stateDeflateFixedHuffman, nil
544 }
545
Nigel Tao981bf192018-05-23 12:17:55 +1000546 if l, d, ok := deflateParseLenDist(line); ok {
547 lCode, lExtra, lNExtra := deflateEncodeLength(l)
Nigel Tao12347602019-06-16 23:15:05 +1000548 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao981bf192018-05-23 12:17:55 +1000549 g.stream.writeBits(lExtra, lNExtra)
550 dCode, dExtra, dNExtra := deflateEncodeDistance(d)
551 g.stream.writeBits(reverse(dCode, 5), 5)
552 g.stream.writeBits(dExtra, dNExtra)
553 return stateDeflateFixedHuffman, nil
554 }
555
556 return nil, fmt.Errorf("bad stateDeflateFixedHuffman command: %q", line)
557}
558
Nigel Tao12347602019-06-16 23:15:05 +1000559func stateDeflateDynamicHuffman(line string) (stateFunc, error) {
560 g := &deflateGlobals
561 const (
Nigel Taoff7bffc2019-10-13 23:23:48 +1100562 cmdH = "huffman "
Nigel Tao12347602019-06-16 23:15:05 +1000563 )
564 switch {
565 case line == "}":
566 deflateGlobalsClearDynamicHuffmanState()
567 return stateDeflate, nil
568
569 case strings.HasPrefix(line, cmdH):
570 s := line[len(cmdH):]
571 n, s, ok := parseDeflateWhichHuffman(s)
572 if !ok {
573 break
574 }
575 g.whichHuffman = n
576 return stateDeflateDynamicHuffmanHuffman, nil
Nigel Tao12347602019-06-16 23:15:05 +1000577 }
578
579 if lit, ok := deflateParseLiteral(line); ok {
580 for i := 0; i < len(lit); i++ {
581 s := g.huffmans[2][uint32(lit[i])]
582 if s == "" {
583 return nil, fmt.Errorf("no code for literal %q (%d)", lit[i:i+1], lit[i])
584 }
585 deflateGlobalsWriteDynamicHuffmanBits(s)
586 }
587 return stateDeflateDynamicHuffman, nil
Nigel Taoff7bffc2019-10-13 23:23:48 +1100588
589 } else if l, d, ok := deflateParseLenDist(line); ok {
590 if (l != 3) || (d != 2) {
591 return nil, fmt.Errorf("TODO: support len/dist pairs for dynamic Huffman blocks")
592 }
593 // len 3 is code 257, with no extra bits.
594 if s := g.huffmans[2][257]; s != "" {
595 deflateGlobalsWriteDynamicHuffmanBits(s)
596 } else {
597 return nil, fmt.Errorf("no code for literal/length symbol 257")
598 }
599 // dist 2 is code 1, with no extra bits.
600 if s := g.huffmans[3][1]; s != "" {
601 deflateGlobalsWriteDynamicHuffmanBits(s)
602 } else {
603 return nil, fmt.Errorf("no code for distance symbol 2")
604 }
605 return stateDeflateDynamicHuffman, nil
606
Nigel Tao12347602019-06-16 23:15:05 +1000607 } else if line == "endOfBlock" {
608 s := g.huffmans[2][256]
609 if s == "" {
610 return nil, fmt.Errorf("no code for end-of-block (256)")
611 }
612 deflateGlobalsWriteDynamicHuffmanBits(s)
613 return stateDeflateDynamicHuffman, nil
614 }
615
616 return nil, fmt.Errorf("bad stateDeflateDynamicHuffman command: %q", line)
617}
618
619func stateDeflateDynamicHuffmanHuffman(line string) (stateFunc, error) {
620 g := &deflateGlobals
621outer:
622 switch {
623 case line == "}":
624 g.whichHuffman = 0
Nigel Taoff7bffc2019-10-13 23:23:48 +1100625 g.prevLine = ""
626 g.etcetera = false
Nigel Tao12347602019-06-16 23:15:05 +1000627
628 // If we have all three Huffman tables, write them.
629 for i := 1; ; i++ {
630 if i == 4 {
631 if err := deflateGlobalsWriteDynamicHuffmanTables(); err != nil {
632 return nil, err
633 }
634 break
635 }
636 if g.huffmans[i] == nil {
637 break
638 }
639 }
640
641 return stateDeflateDynamicHuffman, nil
642
Nigel Taoff7bffc2019-10-13 23:23:48 +1100643 case line == "etcetera":
644 g.etcetera = true
645 return stateDeflateDynamicHuffmanHuffman, nil
646
Nigel Tao12347602019-06-16 23:15:05 +1000647 default:
648 s := line
649 n, s, ok := parseNum(s)
650 if !ok || s == "" {
651 break
652 }
653 for i := 0; i < len(s); i++ {
654 if c := s[i]; c != '0' && c != '1' {
655 break outer
656 }
657 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100658 if (len(s) < 1) || (15 < len(s)) {
659 return nil, fmt.Errorf("%q code length, %d, is out of range", s, len(s))
660 }
661
662 if g.etcetera {
663 g.etcetera = false
664 n0, s0, ok := parseNum(g.prevLine)
665 if !ok {
666 return nil, fmt.Errorf("bad etcetera command")
667 }
668 if err := stateDeflateDHHEtcetera(n0, s0, n, s); err != nil {
669 return nil, err
670 }
671 }
672
Nigel Tao12347602019-06-16 23:15:05 +1000673 if g.huffmans[g.whichHuffman] == nil {
674 g.huffmans[g.whichHuffman] = deflateHuffmanTable{}
675 }
676 g.huffmans[g.whichHuffman][n] = s
Nigel Taoff7bffc2019-10-13 23:23:48 +1100677 g.prevLine = line
Nigel Tao12347602019-06-16 23:15:05 +1000678 return stateDeflateDynamicHuffmanHuffman, nil
679 }
680
681 return nil, fmt.Errorf("bad stateDeflateDynamicHuffmanHuffman command: %q", line)
682}
683
Nigel Taoff7bffc2019-10-13 23:23:48 +1100684// stateDeflateDHHEtcetera expands the "etcetera" line in:
685//
686// 0 0000
687// 1 0001
688// etcetera
689// 7 0111
690//
691// to produce the implicit lines:
692//
693// 0 0000
694// 1 0001
695// 2 0010
696// 3 0011
697// 4 0100
698// 5 0101
699// 6 0110
700// 7 0111
701func stateDeflateDHHEtcetera(n0 uint32, s0 string, n1 uint32, s1 string) error {
702 b := []byte(s0)
703 if !incrementBitstring(b) {
704 return fmt.Errorf("etcetera: could not increment bitstring")
705 }
706 for n := n0 + 1; n < n1; n++ {
707 line := fmt.Sprintf("%d %s", n, b)
708 if _, err := stateDeflateDynamicHuffmanHuffman(line); err != nil {
709 return err
710 }
711 if !incrementBitstring(b) {
712 return fmt.Errorf("etcetera: could not increment bitstring")
713 }
714 }
715 if string(b) != s1 {
716 return fmt.Errorf("etcetera: final bitstring: got %q, want %q", b, s1)
717 }
718 return nil
719}
720
721func incrementBitstring(b []byte) (ok bool) {
722 for i := len(b) - 1; i >= 0; i-- {
723 switch b[i] {
724 case '0':
725 b[i] = '1'
726 return true
727 case '1':
728 b[i] = '0'
729 default:
730 return false
731 }
732 }
733 return false
734}
735
Nigel Tao981bf192018-05-23 12:17:55 +1000736type deflateBitStream struct {
737 bits uint32
738 nBits uint32 // Always within [0, 7].
739}
740
741// writeBits writes the low n bits of b to z.
742func (z *deflateBitStream) writeBits(b uint32, n uint32) {
743 if n > 24 {
744 panic("writeBits: n is too large")
745 }
746 z.bits |= b << z.nBits
747 z.nBits += n
748 for z.nBits >= 8 {
749 out = append(out, uint8(z.bits))
750 z.bits >>= 8
751 z.nBits -= 8
752 }
753}
754
755func (z *deflateBitStream) writeBytes(b []byte) {
756 z.flush()
757 out = append(out, b...)
758}
759
Nigel Tao12347602019-06-16 23:15:05 +1000760func (z *deflateBitStream) writeFixedHuffmanLCode(lCode uint32) {
Nigel Tao981bf192018-05-23 12:17:55 +1000761 switch {
762 case lCode < 144: // 0b._0011_0000 through 0b._1011_1111
763 lCode += 0x030
764 z.writeBits(reverse(lCode, 8), 8)
765 case lCode < 256: // 0b1_1001_0000 through 0b1_1111_1111
766 lCode += 0x190 - 144
767 z.writeBits(reverse(lCode, 9), 9)
768 case lCode < 280: // 0b._.000_0000 through 0b._.001_0111
769 lCode -= 256 - 0x000
770 z.writeBits(reverse(lCode, 7), 7)
771 default: // 0b._1100_0000 through 0b._1100_0111
772 lCode -= 280 - 0x0C0
773 z.writeBits(reverse(lCode, 8), 8)
774 }
775}
776
777func (z *deflateBitStream) flush() {
778 if z.nBits > 0 {
779 out = append(out, uint8(z.bits))
780 z.bits = 0
781 z.nBits = 0
782 }
783}
784
785func deflateEncodeLength(l uint32) (code uint32, extra uint32, nExtra uint32) {
786 switch {
787 case l < 3:
788 // No-op.
789 case l < 11:
790 l -= 3
791 return (l >> 0) + 257, l & 0x0000, 0
792 case l < 19:
793 l -= 11
794 return (l >> 1) + 265, l & 0x0001, 1
795 case l < 35:
796 l -= 19
797 return (l >> 2) + 269, l & 0x0003, 2
798 case l < 67:
799 l -= 35
800 return (l >> 3) + 273, l & 0x0007, 3
801 case l < 131:
802 l -= 67
803 return (l >> 4) + 277, l & 0x000F, 4
804 case l < 258:
805 l -= 131
806 return (l >> 5) + 281, l & 0x001F, 5
807 case l == 258:
808 return 285, 0, 0
809 }
810 panic(fmt.Sprintf("deflateEncodeLength: l=%d", l))
811}
812
813func deflateEncodeDistance(d uint32) (code uint32, extra uint32, nExtra uint32) {
814 switch {
815 case d < 1:
816 // No-op.
817 case d < 5:
818 d -= 1
819 return (d >> 0) + 0, d & 0x0000, 0
820 case d < 9:
821 d -= 5
822 return (d >> 1) + 4, d & 0x0001, 1
823 case d < 17:
824 d -= 9
825 return (d >> 2) + 6, d & 0x0003, 2
826 case d < 33:
827 d -= 17
828 return (d >> 3) + 8, d & 0x0007, 3
829 case d < 65:
830 d -= 33
831 return (d >> 4) + 10, d & 0x000F, 4
832 case d < 129:
833 d -= 65
834 return (d >> 5) + 12, d & 0x001F, 5
835 case d < 257:
836 d -= 129
837 return (d >> 6) + 14, d & 0x003F, 6
838 case d < 513:
839 d -= 257
840 return (d >> 7) + 16, d & 0x007F, 7
841 case d < 1025:
842 d -= 513
843 return (d >> 8) + 18, d & 0x00FF, 8
844 case d < 2049:
845 d -= 1025
846 return (d >> 9) + 20, d & 0x01FF, 9
847 case d < 4097:
848 d -= 2049
849 return (d >> 10) + 22, d & 0x03FF, 10
850 case d < 8193:
851 d -= 4097
852 return (d >> 11) + 24, d & 0x07FF, 11
853 case d < 16385:
854 d -= 8193
855 return (d >> 12) + 26, d & 0x0FFF, 12
856 case d < 32769:
857 d -= 16385
858 return (d >> 13) + 28, d & 0x1FFF, 13
859 }
860 panic(fmt.Sprintf("deflateEncodeDistance: d=%d", d))
861}
862
863func deflateParseLiteral(s string) (lit string, ok bool) {
Nigel Tao12347602019-06-16 23:15:05 +1000864 // TODO: support "\xAB" escape codes in the script?
Nigel Tao981bf192018-05-23 12:17:55 +1000865 const (
866 prefix = `literal "`
867 suffix = `"`
868 )
869 if strings.HasPrefix(s, prefix) {
870 s = s[len(prefix):]
871 if strings.HasSuffix(s, suffix) {
872 return s[:len(s)-len(suffix)], true
873 }
874 }
875 return "", false
876}
877
878func deflateParseLenDist(line string) (l uint32, d uint32, ok bool) {
879 const (
880 lStr = "len "
881 dStr = "dist "
882 )
883 s := line
884 if strings.HasPrefix(s, lStr) {
885 s = s[len(lStr):]
886 if l, s, ok := parseNum(s); ok && 3 <= l && l <= 258 {
887 if strings.HasPrefix(s, dStr) {
888 s = s[len(dStr):]
889 if d, s, ok := parseNum(s); ok && 1 <= d && d <= 32768 && s == "" {
890 return l, d, true
891 }
892 }
893 }
894 }
895 return 0, 0, false
896}
897
898// ----
899
900func init() {
901 formats["gif"] = stateGif
902}
903
904var gifGlobals struct {
Nigel Tao31a2bc92019-05-18 17:32:24 +1000905 imageWidth uint32
906 imageHeight uint32
907 imageBackgroundColorIndex uint32
Nigel Tao981bf192018-05-23 12:17:55 +1000908
Nigel Taoefb18032019-10-05 11:40:24 +1000909 frameInterlaced bool
910 frameLeft uint32
911 frameTop uint32
912 frameWidth uint32
913 frameHeight uint32
Nigel Tao981bf192018-05-23 12:17:55 +1000914
915 globalPalette [][4]uint8
Nigel Tao746681e2019-05-04 13:49:22 +1000916 localPalette [][4]uint8
Nigel Tao981bf192018-05-23 12:17:55 +1000917}
918
919func stateGif(line string) (stateFunc, error) {
920 const (
Nigel Tao6700c722019-05-26 09:41:55 +1000921 cmdB = "bytes "
922 cmdGC = "graphicControl "
923 cmdL = "lzw "
924 cmdLC = "loopCount "
Nigel Tao981bf192018-05-23 12:17:55 +1000925 )
926outer:
927 switch {
Nigel Tao12347602019-06-16 23:15:05 +1000928 case line == "":
929 return stateGif, nil
930
Nigel Tao981bf192018-05-23 12:17:55 +1000931 case line == "frame {":
Nigel Taoefb18032019-10-05 11:40:24 +1000932 gifGlobals.frameInterlaced = false
933 gifGlobals.frameLeft = 0
934 gifGlobals.frameTop = 0
935 gifGlobals.frameWidth = 0
936 gifGlobals.frameHeight = 0
Nigel Tao746681e2019-05-04 13:49:22 +1000937 gifGlobals.localPalette = nil
Nigel Tao981bf192018-05-23 12:17:55 +1000938 out = append(out, 0x2C)
939 return stateGifFrame, nil
940
941 case line == "header":
942 out = append(out, "GIF89a"...)
943 return stateGif, nil
944
945 case line == "image {":
946 return stateGifImage, nil
947
948 case line == "trailer":
949 out = append(out, 0x3B)
950 return stateGif, nil
951
Nigel Tao21f28902019-04-20 16:33:11 +1000952 case strings.HasPrefix(line, cmdB):
953 s := line[len(cmdB):]
954 for s != "" {
955 x, ok := uint32(0), false
956 x, s, ok = parseHex(s)
957 if !ok {
958 break outer
959 }
960 out = append(out, uint8(x))
961 }
962 return stateGif, nil
963
Nigel Tao6700c722019-05-26 09:41:55 +1000964 case strings.HasPrefix(line, cmdGC):
965 s := line[len(cmdGC):]
966
967 flags := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +1000968 duration := uint32(0)
Nigel Tao6c2fb9a2020-09-27 00:09:25 +1000969 transparentIndex := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +1000970 for s != "" {
971 term := ""
972 if i := strings.IndexByte(s, ' '); i >= 0 {
973 term, s = s[:i], s[i+1:]
974 } else {
975 term, s = s, ""
976 }
977
978 const (
979 ms = "ms"
980 trans = "transparentIndex="
981 )
982 switch {
983 case term == "animationDisposalNone":
Nigel Tao6700c722019-05-26 09:41:55 +1000984 flags |= 0x00
Nigel Tao344123b2020-09-25 23:00:01 +1000985 case term == "animationDisposalRestoreBackground":
Nigel Tao6700c722019-05-26 09:41:55 +1000986 flags |= 0x08
Nigel Tao344123b2020-09-25 23:00:01 +1000987 case term == "animationDisposalRestorePrevious":
Nigel Tao6700c722019-05-26 09:41:55 +1000988 flags |= 0x0C
Nigel Tao344123b2020-09-25 23:00:01 +1000989 case strings.HasPrefix(term, trans):
Nigel Tao6c2fb9a2020-09-27 00:09:25 +1000990 num, err := strconv.ParseUint(term[len(trans):], 0, 8)
991 if err != nil {
Nigel Tao344123b2020-09-25 23:00:01 +1000992 break outer
993 }
994 flags |= 0x01
Nigel Tao6c2fb9a2020-09-27 00:09:25 +1000995 transparentIndex = uint8(num)
Nigel Tao344123b2020-09-25 23:00:01 +1000996 case strings.HasSuffix(term, ms):
997 num, remaining, ok := parseNum(term[:len(term)-len(ms)])
998 if !ok || remaining != "" {
999 break outer
1000 }
1001 duration = num / 10 // GIF's unit of time is 10ms.
Nigel Tao6700c722019-05-26 09:41:55 +10001002 default:
1003 break outer
1004 }
Nigel Tao6700c722019-05-26 09:41:55 +10001005 }
1006
Nigel Taob1ff27f2019-05-12 22:00:44 +10001007 out = append(out,
Nigel Tao6700c722019-05-26 09:41:55 +10001008 0x21, 0xF9, 0x04,
1009 flags,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001010 uint8(duration>>0),
1011 uint8(duration>>8),
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001012 transparentIndex,
Nigel Tao6700c722019-05-26 09:41:55 +10001013 0x00,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001014 )
1015 return stateGif, nil
1016
Nigel Tao981bf192018-05-23 12:17:55 +10001017 case strings.HasPrefix(line, cmdL):
1018 s := line[len(cmdL):]
1019 litWidth, s, ok := parseNum(s)
1020 if !ok || litWidth < 2 || 8 < litWidth {
1021 break
1022 }
1023 out = append(out, uint8(litWidth))
1024
1025 uncompressed := []byte(nil)
1026 for s != "" {
1027 x := uint32(0)
1028 x, s, ok = parseHex(s)
1029 if !ok {
1030 break outer
1031 }
1032 uncompressed = append(uncompressed, uint8(x))
1033 }
1034
1035 buf := bytes.NewBuffer(nil)
1036 w := lzw.NewWriter(buf, lzw.LSB, int(litWidth))
1037 if _, err := w.Write(uncompressed); err != nil {
1038 return nil, err
1039 }
1040 if err := w.Close(); err != nil {
1041 return nil, err
1042 }
1043 compressed := buf.Bytes()
1044
1045 for len(compressed) > 0 {
1046 if len(compressed) <= 0xFF {
1047 out = append(out, uint8(len(compressed)))
1048 out = append(out, compressed...)
1049 compressed = nil
1050 } else {
1051 out = append(out, 0xFF)
1052 out = append(out, compressed[:0xFF]...)
1053 compressed = compressed[0xFF:]
1054 }
1055 }
1056 out = append(out, 0x00)
1057 return stateGif, nil
Nigel Tao7be9c392018-10-13 17:00:45 +11001058
1059 case strings.HasPrefix(line, cmdLC):
1060 s := line[len(cmdLC):]
1061 loopCount, _, ok := parseNum(s)
1062 if !ok || 0xFFFF < loopCount {
1063 break
1064 }
1065 out = append(out,
1066 0x21, // Extension Introducer.
1067 0xFF, // Application Extension Label.
1068 0x0B, // Block Size.
1069 )
1070 out = append(out, "NETSCAPE2.0"...)
1071 out = append(out,
1072 0x03, // Block Size.
1073 0x01, // Magic Number.
1074 byte(loopCount),
1075 byte(loopCount>>8),
1076 0x00, // Block Terminator.
1077 )
1078 return stateGif, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001079 }
1080
1081 return nil, fmt.Errorf("bad stateGif command: %q", line)
1082}
1083
1084func stateGifImage(line string) (stateFunc, error) {
1085 g := &gifGlobals
1086 if line == "}" {
1087 out = appendU16LE(out, uint16(g.imageWidth))
1088 out = appendU16LE(out, uint16(g.imageHeight))
1089 if g.globalPalette == nil {
1090 out = append(out, 0x00)
1091 } else if n := log2(uint32(len(g.globalPalette))); n < 2 || 8 < n {
1092 return nil, fmt.Errorf("bad len(g.globalPalette): %d", len(g.globalPalette))
1093 } else {
1094 out = append(out, 0x80|uint8(n-1))
1095 }
Nigel Tao31a2bc92019-05-18 17:32:24 +10001096 out = append(out, uint8(g.imageBackgroundColorIndex))
Nigel Tao981bf192018-05-23 12:17:55 +10001097 out = append(out, 0x00)
1098 for _, x := range g.globalPalette {
1099 out = append(out, x[0], x[1], x[2])
1100 }
1101 return stateGif, nil
1102 }
1103
1104 const (
Nigel Tao31a2bc92019-05-18 17:32:24 +10001105 cmdBCI = "backgroundColorIndex "
Nigel Tao981bf192018-05-23 12:17:55 +10001106 cmdIWH = "imageWidthHeight "
1107 cmdP = "palette {"
1108 )
1109 switch {
Nigel Tao31a2bc92019-05-18 17:32:24 +10001110 case strings.HasPrefix(line, cmdBCI):
1111 s := line[len(cmdBCI):]
1112 if i, _, ok := parseNum(s); ok {
1113 g.imageBackgroundColorIndex = i
1114 }
1115 return stateGifImage, nil
1116
Nigel Tao981bf192018-05-23 12:17:55 +10001117 case strings.HasPrefix(line, cmdIWH):
1118 s := line[len(cmdIWH):]
1119 if w, s, ok := parseNum(s); ok {
1120 if h, _, ok := parseNum(s); ok {
1121 g.imageWidth = w
1122 g.imageHeight = h
1123 return stateGifImage, nil
1124 }
1125 }
1126
1127 case strings.HasPrefix(line, cmdP):
1128 return stateGifImagePalette, nil
1129 }
1130
1131 return nil, fmt.Errorf("bad stateGifImage command: %q", line)
1132}
1133
1134func stateGifFrame(line string) (stateFunc, error) {
1135 g := &gifGlobals
1136 if line == "}" {
1137 out = appendU16LE(out, uint16(g.frameLeft))
1138 out = appendU16LE(out, uint16(g.frameTop))
1139 out = appendU16LE(out, uint16(g.frameWidth))
1140 out = appendU16LE(out, uint16(g.frameHeight))
Nigel Taoefb18032019-10-05 11:40:24 +10001141 flags := byte(0x00)
1142 if g.frameInterlaced {
1143 flags |= 0x40
1144 }
Nigel Tao746681e2019-05-04 13:49:22 +10001145 if g.localPalette == nil {
Nigel Taoefb18032019-10-05 11:40:24 +10001146 out = append(out, flags)
Nigel Tao746681e2019-05-04 13:49:22 +10001147 } else if n := log2(uint32(len(g.localPalette))); n < 2 || 8 < n {
1148 return nil, fmt.Errorf("bad len(g.localPalette): %d", len(g.localPalette))
1149 } else {
Nigel Taoefb18032019-10-05 11:40:24 +10001150 out = append(out, flags|0x80|uint8(n-1))
Nigel Tao746681e2019-05-04 13:49:22 +10001151 }
1152 for _, x := range g.localPalette {
1153 out = append(out, x[0], x[1], x[2])
1154 }
Nigel Tao981bf192018-05-23 12:17:55 +10001155 return stateGif, nil
1156 }
1157
1158 const (
1159 cmdFLTWH = "frameLeftTopWidthHeight "
Nigel Tao746681e2019-05-04 13:49:22 +10001160 cmdP = "palette {"
Nigel Tao981bf192018-05-23 12:17:55 +10001161 )
1162 switch {
Nigel Taoefb18032019-10-05 11:40:24 +10001163 case line == "interlaced":
1164 g.frameInterlaced = true
1165 return stateGifFrame, nil
1166
Nigel Tao981bf192018-05-23 12:17:55 +10001167 case strings.HasPrefix(line, cmdFLTWH):
1168 s := line[len(cmdFLTWH):]
1169 if l, s, ok := parseNum(s); ok {
1170 if t, s, ok := parseNum(s); ok {
1171 if w, s, ok := parseNum(s); ok {
1172 if h, _, ok := parseNum(s); ok {
1173 g.frameLeft = l
1174 g.frameTop = t
1175 g.frameWidth = w
1176 g.frameHeight = h
1177 return stateGifFrame, nil
1178 }
1179 }
1180 }
1181 }
Nigel Tao746681e2019-05-04 13:49:22 +10001182
1183 case strings.HasPrefix(line, cmdP):
1184 return stateGifFramePalette, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001185 }
1186
1187 return nil, fmt.Errorf("bad stateGifFrame command: %q", line)
1188}
1189
1190func stateGifImagePalette(line string) (stateFunc, error) {
1191 g := &gifGlobals
1192 if line == "}" {
1193 return stateGifImage, nil
1194 }
1195
1196 s := line
1197 if rgb0, s, ok := parseHex(s); ok {
1198 if rgb1, s, ok := parseHex(s); ok {
1199 if rgb2, _, ok := parseHex(s); ok {
1200 g.globalPalette = append(g.globalPalette,
1201 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1202 return stateGifImagePalette, nil
1203 }
1204 }
1205 }
1206
1207 return nil, fmt.Errorf("bad stateGifImagePalette command: %q", line)
1208}
Nigel Tao746681e2019-05-04 13:49:22 +10001209
1210func stateGifFramePalette(line string) (stateFunc, error) {
1211 g := &gifGlobals
1212 if line == "}" {
1213 return stateGifFrame, nil
1214 }
1215
1216 s := line
1217 if rgb0, s, ok := parseHex(s); ok {
1218 if rgb1, s, ok := parseHex(s); ok {
1219 if rgb2, _, ok := parseHex(s); ok {
1220 g.localPalette = append(g.localPalette,
1221 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1222 return stateGifFramePalette, nil
1223 }
1224 }
1225 }
1226
1227 return nil, fmt.Errorf("bad stateGifFramePalette command: %q", line)
1228}
Nigel Tao6c2a5912021-11-16 11:00:48 +11001229
1230// ----
1231
1232func init() {
1233 formats["png"] = statePng
1234}
1235
1236var pngGlobals struct {
1237 scratch [4]byte
1238
1239 animSeqNum uint32
1240 chunkData bytes.Buffer
1241 chunkType string
1242 zlibWriter *zlib.Writer
1243}
1244
1245func statePng(line string) (stateFunc, error) {
1246 g := &pngGlobals
1247 switch {
1248 case line == "":
1249 return statePng, nil
1250
1251 case line == "magic":
1252 out = append(out, "\x89PNG\x0D\x0A\x1A\x0A"...)
1253 return statePng, nil
1254
1255 case (len(line) == 6) && (line[4] == ' ') && (line[5] == '{'):
1256 g.chunkData.Reset()
1257 g.chunkType = line[:4]
1258 return statePngChunk, nil
1259 }
1260
1261 return nil, fmt.Errorf("bad statePng command: %q", line)
1262}
1263
1264func statePngChunk(line string) (stateFunc, error) {
1265 g := &pngGlobals
1266 if line == "}" {
1267 if n := g.chunkData.Len(); n > 0xFFFF_FFFF {
1268 return nil, fmt.Errorf("chunkData is too long")
1269 }
1270 out = appendU32BE(out, uint32(g.chunkData.Len()))
1271 n := len(out)
1272 out = append(out, g.chunkType...)
1273 out = append(out, g.chunkData.Bytes()...)
1274 out = appendU32BE(out, crc32.ChecksumIEEE(out[n:]))
1275 return statePng, nil
1276 }
1277
1278 switch {
1279 case line == "animSeqNum++":
1280 g.chunkData.Write(appendU32BE(g.scratch[:0], g.animSeqNum))
1281 g.animSeqNum++
1282 return statePngChunk, nil
1283
1284 case line == "raw {":
1285 return statePngRaw, nil
1286
1287 case line == "zlib {":
1288 g.zlibWriter = zlib.NewWriter(&g.chunkData)
1289 return statePngZlib, nil
1290 }
1291
1292 return nil, fmt.Errorf("bad statePngChunk command: %q", line)
1293}
1294
1295func statePngRaw(line string) (stateFunc, error) {
1296 g := &pngGlobals
1297 if line == "}" {
1298 return statePngChunk, nil
1299 }
1300
1301 for s := line; s != ""; {
1302 if x, remaining, ok := parseHex(s); ok {
1303 g.chunkData.WriteByte(byte(x))
1304 s = remaining
1305 } else {
1306 return nil, fmt.Errorf("bad statePngRaw command: %q", line)
1307 }
1308 }
1309 return statePngRaw, nil
1310}
1311
1312func statePngZlib(line string) (stateFunc, error) {
1313 g := &pngGlobals
1314 if line == "}" {
1315 if err := g.zlibWriter.Close(); err != nil {
1316 return nil, fmt.Errorf("statePngZlib: %w", err)
1317 }
1318 return statePngChunk, nil
1319 }
1320
1321 for s := line; s != ""; {
1322 if x, remaining, ok := parseHex(s); ok {
1323 g.scratch[0] = byte(x)
1324 g.zlibWriter.Write(g.scratch[:1])
1325 s = remaining
1326 } else {
1327 return nil, fmt.Errorf("bad statePngZlib command: %q", line)
1328 }
1329 }
1330 return statePngZlib, nil
1331}