blob: fe41d8cc67f470869f8516aa6817506c5690fc7c [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):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +100091 count, args, ok := parseNum32(args)
Nigel Tao981bf192018-05-23 12:17:55 +100092 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
Nigel Taoe1b43cd2022-06-08 14:04:09 +1000160func parseHex32(s string) (num uint32, remaining string, ok bool) {
Nigel Tao981bf192018-05-23 12:17:55 +1000161 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
Nigel Taoe1b43cd2022-06-08 14:04:09 +1000180func parseHex64(s string) (num uint64, 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 if len(s) < 2 || s[0] != '0' || s[1] != 'x' {
189 return 0, "", false
190 }
191 s = s[2:]
192
193 u, err := strconv.ParseUint(s, 16, 64)
194 if err != nil {
195 return 0, "", false
196 }
197 return u, remaining, true
198}
199
200func parseNum32(s string) (num uint32, remaining string, ok bool) {
Nigel Tao981bf192018-05-23 12:17:55 +1000201 if i := strings.IndexByte(s, ' '); i >= 0 {
202 s, remaining = s[:i], s[i+1:]
203 for len(remaining) > 0 && remaining[0] == ' ' {
204 remaining = remaining[1:]
205 }
206 }
207
208 u, err := strconv.ParseUint(s, 10, 32)
209 if err != nil {
210 return 0, "", false
211 }
212 return uint32(u), remaining, true
213}
214
215func reverse(x uint32, n uint32) uint32 {
216 x = uint32(reverse8[0xFF&(x>>0)])<<24 |
217 uint32(reverse8[0xFF&(x>>8)])<<16 |
218 uint32(reverse8[0xFF&(x>>16)])<<8 |
219 uint32(reverse8[0xFF&(x>>24)])<<0
220 return x >> (32 - n)
221}
222
223var reverse8 = [256]byte{
224 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, // 0x00 - 0x07
225 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, // 0x08 - 0x0F
226 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, // 0x10 - 0x17
227 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, // 0x18 - 0x1F
228 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, // 0x20 - 0x27
229 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, // 0x28 - 0x2F
230 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, // 0x30 - 0x37
231 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, // 0x38 - 0x3F
232 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, // 0x40 - 0x47
233 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, // 0x48 - 0x4F
234 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, // 0x50 - 0x57
235 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, // 0x58 - 0x5F
236 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, // 0x60 - 0x67
237 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, // 0x68 - 0x6F
238 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, // 0x70 - 0x77
239 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, // 0x78 - 0x7F
240 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, // 0x80 - 0x87
241 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, // 0x88 - 0x8F
242 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, // 0x90 - 0x97
243 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, // 0x98 - 0x9F
244 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, // 0xA0 - 0xA7
245 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, // 0xA8 - 0xAF
246 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, // 0xB0 - 0xB7
247 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, // 0xB8 - 0xBF
248 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, // 0xC0 - 0xC7
249 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, // 0xC8 - 0xCF
250 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, // 0xD0 - 0xD7
251 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, // 0xD8 - 0xDF
252 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, // 0xE0 - 0xE7
253 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, // 0xE8 - 0xEF
254 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, // 0xF0 - 0xF7
255 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, // 0xF8 - 0xFF
256}
257
258// ----
259
260func init() {
Nigel Taoe1b43cd2022-06-08 14:04:09 +1000261 formats["bzip2"] = stateBzip2
262}
263
264var bzip2Globals struct {
265 stream bzip2BitStream
266}
267
268func stateBzip2(line string) (stateFunc, error) {
269 g := &bzip2Globals
270 const (
271 cmdB = "bits "
272 )
273 switch {
274 case line == "":
275 g.stream.flush()
276 return stateBzip2, nil
277
278 case strings.HasPrefix(line, cmdB):
279 s := strings.TrimSpace(line[len(cmdB):])
280 n, s, ok := parseNum32(s)
281 if !ok || s == "" {
282 break
283 }
284 x, s, ok := parseHex64(s)
285 if !ok {
286 break
287 }
288 g.stream.writeBits64(x, n)
289 return stateBzip2, nil
290 }
291
292 return nil, fmt.Errorf("bad stateBzip2 command: %q", line)
293}
294
295type bzip2BitStream struct {
296 bits uint64
297 nBits uint32 // Always within [0, 7].
298}
299
300// writeBits64 writes the low n bits of b to z.
301func (z *bzip2BitStream) writeBits64(b uint64, n uint32) {
302 if n > 56 {
303 panic("writeBits64: n is too large")
304 }
305 z.bits |= b << (64 - n - z.nBits)
306 z.nBits += n
307 for z.nBits >= 8 {
308 out = append(out, uint8(z.bits>>56))
309 z.bits <<= 8
310 z.nBits -= 8
311 }
312}
313
314func (z *bzip2BitStream) flush() {
315 if z.nBits > 0 {
316 out = append(out, uint8(z.bits>>56))
317 z.bits = 0
318 z.nBits = 0
319 }
320}
321
322// ----
323
324func init() {
Nigel Tao981bf192018-05-23 12:17:55 +1000325 formats["deflate"] = stateDeflate
326}
327
Nigel Tao12347602019-06-16 23:15:05 +1000328var deflateCodeOrder = [19]uint32{
329 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
330}
331
Nigel Tao92ea6312022-01-09 10:41:31 +1100332var deflateHuffmanNames = [4]string{
333 0: "Unused",
334 1: "CodeLength",
335 2: "Literal/Length",
336 3: "Distance",
337}
338
Nigel Tao12347602019-06-16 23:15:05 +1000339type deflateHuffmanTable map[uint32]string
340
Nigel Tao981bf192018-05-23 12:17:55 +1000341var deflateGlobals struct {
342 bncData []byte
343 stream deflateBitStream
Nigel Tao12347602019-06-16 23:15:05 +1000344
Nigel Tao92ea6312022-01-09 10:41:31 +1100345 // Dynamic Huffman state. whichHuffman indexes the huffmans array. See also
346 // deflateHuffmanNames.
Nigel Taoff7bffc2019-10-13 23:23:48 +1100347 whichHuffman uint32
Nigel Tao92ea6312022-01-09 10:41:31 +1100348 huffmans [4]deflateHuffmanTable
Nigel Taoff7bffc2019-10-13 23:23:48 +1100349
350 // DHH (Dynamic Huffman, inside a Huffman table) state.
Nigel Tao42de49a2022-01-09 10:45:47 +1100351 prevLine string
352 etcetera bool
353 incomplete bool
Nigel Tao12347602019-06-16 23:15:05 +1000354}
355
356func deflateGlobalsClearDynamicHuffmanState() {
Nigel Tao12347602019-06-16 23:15:05 +1000357 deflateGlobals.whichHuffman = 0
358 deflateGlobals.huffmans = [4]deflateHuffmanTable{}
Nigel Taoff7bffc2019-10-13 23:23:48 +1100359 deflateGlobals.prevLine = ""
360 deflateGlobals.etcetera = false
Nigel Tao42de49a2022-01-09 10:45:47 +1100361 deflateGlobals.incomplete = false
Nigel Taoff7bffc2019-10-13 23:23:48 +1100362}
363
364func deflateGlobalsCountCodes() (numLCodes uint32, numDCodes uint32, numCLCodeLengths uint32, retErr error) {
365 for k := range deflateGlobals.huffmans[2] {
366 if numLCodes < (k + 1) {
367 numLCodes = (k + 1)
368 }
369 }
370
371 for k := range deflateGlobals.huffmans[3] {
372 if numDCodes < (k + 1) {
373 numDCodes = (k + 1)
374 }
375 }
376
377 for k := range deflateGlobals.huffmans[1] {
378 if (k < 0) || (18 < k) {
379 return 0, 0, 0, fmt.Errorf("bad CodeLength: %d", k)
380 }
381 }
382 for i := len(deflateCodeOrder) - 1; i >= 0; i-- {
383 cl := deflateCodeOrder[i]
384 if _, ok := deflateGlobals.huffmans[1][cl]; ok {
385 numCLCodeLengths = uint32(i + 1)
386 break
387 }
388 }
389 if numCLCodeLengths < 4 {
390 numCLCodeLengths = 4
391 }
392
393 return numLCodes, numDCodes, numCLCodeLengths, nil
Nigel Tao12347602019-06-16 23:15:05 +1000394}
395
Nigel Tao92ea6312022-01-09 10:41:31 +1100396func deflateGlobalsIsHuffmanCanonical() bool {
397 g := &deflateGlobals
398
399 // Gather the code+bitstring pairs. Deflate bitstrings cannot be longer
400 // than 15 bits.
401 type pair struct {
402 k uint32
403 v string
404 }
405 pairs := []pair{}
406 for k, v := range g.huffmans[g.whichHuffman] {
407 if len(v) > 15 {
408 return false
409 }
410 pairs = append(pairs, pair{k, v})
411 }
412 if len(pairs) == 0 {
413 return false
414 }
415
416 // Sort by bitstring-length and then code.
417 sort.Slice(pairs, func(i, j int) bool {
418 pi, pj := &pairs[i], &pairs[j]
419 if len(pi.v) != len(pj.v) {
420 return len(pi.v) < len(pj.v)
421 }
422 return pi.k < pj.k
423 })
424
425 // Set up a generator for the canonical bitstrings.
426 buf := [15]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}
427 prevV := ""
428 next := func() bool {
429 if i := len(prevV); i > 0 {
430 for {
431 i--
432 if i < 0 {
433 return false
434 }
435 if buf[i] == '0' {
436 buf[i] = '1'
437 break
438 }
439 buf[i] = '0'
440 }
441 }
442 return true
443 }
444
445 // Verify the bitstrings.
446 for _, p := range pairs {
447 if !next() {
448 return false // Over-subscribed.
449 } else if p.v != string(buf[:len(p.v)]) {
450 return false // Non-canonical.
451 }
452 prevV = p.v
453 }
Nigel Tao42de49a2022-01-09 10:45:47 +1100454 if next() != g.incomplete {
455 return false // Under-subscribed.
456 }
Nigel Tao92ea6312022-01-09 10:41:31 +1100457 return true
458}
459
Nigel Tao12347602019-06-16 23:15:05 +1000460func deflateGlobalsWriteDynamicHuffmanTables() error {
461 g := &deflateGlobals
Nigel Taoff7bffc2019-10-13 23:23:48 +1100462 numLCodes, numDCodes, numCLCodeLengths, err := deflateGlobalsCountCodes()
463 if err != nil {
464 return err
Nigel Tao12347602019-06-16 23:15:05 +1000465 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100466 if (numLCodes < 257) || (257+31 < numLCodes) {
467 return fmt.Errorf("bad numLCodes: %d", numLCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000468 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100469 g.stream.writeBits(numLCodes-257, 5)
470 if (numDCodes < 1) || (1+31 < numDCodes) {
471 return fmt.Errorf("bad numDCodes: %d", numDCodes)
Nigel Tao12347602019-06-16 23:15:05 +1000472 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100473 g.stream.writeBits(numDCodes-1, 5)
474 if (numCLCodeLengths < 4) || (4+15 < numCLCodeLengths) {
475 return fmt.Errorf("bad numCLCodeLengths: %d", numCLCodeLengths)
476 }
477 g.stream.writeBits(numCLCodeLengths-4, 4)
Nigel Tao12347602019-06-16 23:15:05 +1000478
479 // Write the Huffman table for CodeLength.
480 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100481 for i := uint32(0); i < numCLCodeLengths; i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000482 n := len(g.huffmans[1][deflateCodeOrder[i]])
483 g.stream.writeBits(uint32(n), 3)
484 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100485 for i := numCLCodeLengths; i < uint32(len(deflateCodeOrder)); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000486 n := len(g.huffmans[1][deflateCodeOrder[i]])
487 if n > 0 {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100488 return fmt.Errorf("short numCLCodeLengths: %d", numCLCodeLengths)
Nigel Tao12347602019-06-16 23:15:05 +1000489 }
490 }
491 }
492
Nigel Taodc1218f2019-10-14 09:28:28 +1100493 // Write the Huffman tables for Literal/Length and Distance.
Nigel Tao12347602019-06-16 23:15:05 +1000494 {
495 numZeroes := uint32(0)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100496 for i := uint32(0); i < numLCodes+numDCodes; i++ {
497 codeLen := uint32(0)
498 if i < numLCodes {
499 codeLen = uint32(len(g.huffmans[2][i]))
Nigel Tao12347602019-06-16 23:15:05 +1000500 } else {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100501 codeLen = uint32(len(g.huffmans[3][i-numLCodes]))
Nigel Tao12347602019-06-16 23:15:05 +1000502 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100503 if codeLen == 0 {
Nigel Tao12347602019-06-16 23:15:05 +1000504 numZeroes++
505 continue
506 }
507
508 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
509 return err
510 }
511 numZeroes = 0
512
Nigel Taoff7bffc2019-10-13 23:23:48 +1100513 codeLenCode := g.huffmans[1][codeLen]
514 if codeLenCode == "" {
515 return fmt.Errorf("no code for code-length %d", codeLen)
516 }
517 deflateGlobalsWriteDynamicHuffmanBits(g.huffmans[1][codeLen])
Nigel Tao12347602019-06-16 23:15:05 +1000518 }
519 if err := deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes); err != nil {
520 return err
521 }
522 }
523
524 return nil
525}
526
527func deflateGlobalsWriteDynamicHuffmanZeroes(numZeroes uint32) error {
528 g := &deflateGlobals
529 if numZeroes == 0 {
530 return nil
531 }
532
533 if s := g.huffmans[1][18]; s != "" {
534 for numZeroes >= 11 {
535 extra := numZeroes - 11
536 if extra > 127 {
537 extra = 127
538 }
539 deflateGlobalsWriteDynamicHuffmanBits(s)
540 g.stream.writeBits(extra, 7)
541 numZeroes -= 11 + extra
542 }
543 }
544
545 if s := g.huffmans[1][17]; s != "" {
546 for numZeroes >= 3 {
547 extra := numZeroes - 3
548 if extra > 7 {
549 extra = 7
550 }
551 deflateGlobalsWriteDynamicHuffmanBits(s)
552 g.stream.writeBits(extra, 3)
553 numZeroes -= 3 + extra
554 }
555 }
556
557 if s := g.huffmans[1][0]; s != "" {
558 for ; numZeroes > 0; numZeroes-- {
559 deflateGlobalsWriteDynamicHuffmanBits(s)
560 }
561 }
562
563 if numZeroes > 0 {
564 return fmt.Errorf("could not write a run of zero-valued code lengths")
565 }
566 return nil
567}
568
569func deflateGlobalsWriteDynamicHuffmanBits(s string) {
570 g := &deflateGlobals
571 for i := 0; i < len(s); i++ {
572 g.stream.writeBits(uint32(s[i]&1), 1)
573 }
574}
575
576func parseDeflateWhichHuffman(s string) (num uint32, remaining string, ok bool) {
577 if i := strings.IndexByte(s, ' '); i >= 0 {
578 s, remaining = s[:i], s[i+1:]
579 for len(remaining) > 0 && remaining[0] == ' ' {
580 remaining = remaining[1:]
581 }
582 }
583
584 switch s {
585 case "CodeLength":
586 return 1, remaining, true
Nigel Taodc1218f2019-10-14 09:28:28 +1100587 case "Literal/Length":
Nigel Tao12347602019-06-16 23:15:05 +1000588 return 2, remaining, true
589 case "Distance":
590 return 3, remaining, true
591 }
592 return 0, "", false
Nigel Tao981bf192018-05-23 12:17:55 +1000593}
594
595func stateDeflate(line string) (stateFunc, error) {
596 g := &deflateGlobals
597 const (
Nigel Tao0cc17982019-05-19 23:43:20 +1000598 cmdB = "bytes "
Nigel Tao12347602019-06-16 23:15:05 +1000599 cmdBDH = "blockDynamicHuffman "
Nigel Tao981bf192018-05-23 12:17:55 +1000600 cmdBFH = "blockFixedHuffman "
Nigel Tao12347602019-06-16 23:15:05 +1000601 cmdBNC = "blockNoCompression "
Nigel Tao981bf192018-05-23 12:17:55 +1000602 )
603 bits := uint32(0)
604 s := ""
605
606 retState := stateFunc(nil)
607 switch {
Nigel Tao12347602019-06-16 23:15:05 +1000608 case line == "":
609 g.stream.flush()
610 return stateDeflate, nil
611
Nigel Tao0cc17982019-05-19 23:43:20 +1000612 case strings.HasPrefix(line, cmdB):
613 s := line[len(cmdB):]
614 for s != "" {
615 x, ok := uint32(0), false
Nigel Taoe1b43cd2022-06-08 14:04:09 +1000616 x, s, ok = parseHex32(s)
Nigel Tao0cc17982019-05-19 23:43:20 +1000617 if !ok {
618 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
619 }
620 out = append(out, uint8(x))
621 }
622 return stateDeflate, nil
623
Nigel Tao981bf192018-05-23 12:17:55 +1000624 case strings.HasPrefix(line, cmdBNC):
625 s = line[len(cmdBNC):]
626 retState = stateDeflateNoCompression
627 bits |= 0 << 1
628 case strings.HasPrefix(line, cmdBFH):
629 s = line[len(cmdBFH):]
630 retState = stateDeflateFixedHuffman
631 bits |= 1 << 1
Nigel Tao12347602019-06-16 23:15:05 +1000632 case strings.HasPrefix(line, cmdBDH):
633 s = line[len(cmdBDH):]
634 retState = stateDeflateDynamicHuffman
635 bits |= 2 << 1
Nigel Tao981bf192018-05-23 12:17:55 +1000636 default:
637 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
638 }
639
640 switch s {
641 case "(final) {":
642 bits |= 1
643 case "(nonFinal) {":
644 // No-op.
645 default:
646 return nil, fmt.Errorf("bad stateDeflate command: %q", line)
647 }
648
649 g.stream.writeBits(bits, 3)
650 return retState, nil
651}
652
653func stateDeflateNoCompression(line string) (stateFunc, error) {
654 g := &deflateGlobals
655 if line == "}" {
656 if len(g.bncData) > 0xFFFF {
657 return nil, fmt.Errorf("bncData is too long")
658 }
659 n := uint32(len(g.bncData))
660 g.stream.flush()
661 g.stream.writeBits(n, 16)
662 g.stream.writeBits(0xFFFF-n, 16)
663 g.stream.writeBytes(g.bncData)
664 g.bncData = g.bncData[:0]
665 return stateDeflate, nil
666 }
667
668 if lit, ok := deflateParseLiteral(line); ok {
669 g.bncData = append(g.bncData, lit...)
670 return stateDeflateNoCompression, nil
671 }
672
673 return nil, fmt.Errorf("bad blockNoCompression command: %q", line)
674}
675
676func stateDeflateFixedHuffman(line string) (stateFunc, error) {
677 g := &deflateGlobals
678 if line == "}" {
Nigel Tao981bf192018-05-23 12:17:55 +1000679 return stateDeflate, nil
680 }
681
682 if line == "endOfBlock" {
683 g.stream.writeBits(0, 7)
684 return stateDeflateFixedHuffman, nil
685 }
686
687 if lit, ok := deflateParseLiteral(line); ok {
Nigel Tao981bf192018-05-23 12:17:55 +1000688 for i := 0; i < len(lit); i++ {
Nigel Tao12347602019-06-16 23:15:05 +1000689 g.stream.writeFixedHuffmanLCode(uint32(lit[i]))
Nigel Tao981bf192018-05-23 12:17:55 +1000690 }
691 return stateDeflateFixedHuffman, nil
692 }
693
Nigel Tao11a68192019-02-02 13:04:09 +1100694 if line == "len 3 distCode 31" {
695 lCode, lExtra, lNExtra := deflateEncodeLength(3)
Nigel Tao12347602019-06-16 23:15:05 +1000696 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao11a68192019-02-02 13:04:09 +1100697 g.stream.writeBits(lExtra, lNExtra)
698 dCode, dExtra, dNExtra := uint32(31), uint32(0), uint32(0)
699 g.stream.writeBits(reverse(dCode, 5), 5)
700 g.stream.writeBits(dExtra, dNExtra)
701 return stateDeflateFixedHuffman, nil
702 }
703
Nigel Tao981bf192018-05-23 12:17:55 +1000704 if l, d, ok := deflateParseLenDist(line); ok {
705 lCode, lExtra, lNExtra := deflateEncodeLength(l)
Nigel Tao12347602019-06-16 23:15:05 +1000706 g.stream.writeFixedHuffmanLCode(lCode)
Nigel Tao981bf192018-05-23 12:17:55 +1000707 g.stream.writeBits(lExtra, lNExtra)
708 dCode, dExtra, dNExtra := deflateEncodeDistance(d)
709 g.stream.writeBits(reverse(dCode, 5), 5)
710 g.stream.writeBits(dExtra, dNExtra)
711 return stateDeflateFixedHuffman, nil
712 }
713
714 return nil, fmt.Errorf("bad stateDeflateFixedHuffman command: %q", line)
715}
716
Nigel Tao12347602019-06-16 23:15:05 +1000717func stateDeflateDynamicHuffman(line string) (stateFunc, error) {
718 g := &deflateGlobals
719 const (
Nigel Taoff7bffc2019-10-13 23:23:48 +1100720 cmdH = "huffman "
Nigel Tao12347602019-06-16 23:15:05 +1000721 )
722 switch {
723 case line == "}":
724 deflateGlobalsClearDynamicHuffmanState()
725 return stateDeflate, nil
726
727 case strings.HasPrefix(line, cmdH):
728 s := line[len(cmdH):]
729 n, s, ok := parseDeflateWhichHuffman(s)
730 if !ok {
731 break
732 }
733 g.whichHuffman = n
734 return stateDeflateDynamicHuffmanHuffman, nil
Nigel Tao12347602019-06-16 23:15:05 +1000735 }
736
737 if lit, ok := deflateParseLiteral(line); ok {
738 for i := 0; i < len(lit); i++ {
739 s := g.huffmans[2][uint32(lit[i])]
740 if s == "" {
741 return nil, fmt.Errorf("no code for literal %q (%d)", lit[i:i+1], lit[i])
742 }
743 deflateGlobalsWriteDynamicHuffmanBits(s)
744 }
745 return stateDeflateDynamicHuffman, nil
Nigel Taoff7bffc2019-10-13 23:23:48 +1100746
747 } else if l, d, ok := deflateParseLenDist(line); ok {
Nigel Taof86c80e2022-01-08 13:46:27 +1100748 if (l > 10) || (d > 4) {
749 return nil, fmt.Errorf("TODO: support len/dist ExtraBits > 0 for dynamic Huffman blocks")
Nigel Taoff7bffc2019-10-13 23:23:48 +1100750 }
Nigel Taof86c80e2022-01-08 13:46:27 +1100751 // len 3 is code 257, len 4 is code 258, etc. All with no ExtraBits.
752 if s := g.huffmans[2][l+254]; s != "" {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100753 deflateGlobalsWriteDynamicHuffmanBits(s)
754 } else {
Nigel Taof86c80e2022-01-08 13:46:27 +1100755 return nil, fmt.Errorf("no code for literal/length symbol %d", l+254)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100756 }
Nigel Taof86c80e2022-01-08 13:46:27 +1100757 // dist 1 is code 0, dist 2 is code 1, etc. All with no ExtraBits.
758 if s := g.huffmans[3][d-1]; s != "" {
Nigel Taoff7bffc2019-10-13 23:23:48 +1100759 deflateGlobalsWriteDynamicHuffmanBits(s)
760 } else {
Nigel Taof86c80e2022-01-08 13:46:27 +1100761 return nil, fmt.Errorf("no code for distance symbol %d", d-1)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100762 }
763 return stateDeflateDynamicHuffman, nil
764
Nigel Tao12347602019-06-16 23:15:05 +1000765 } else if line == "endOfBlock" {
766 s := g.huffmans[2][256]
767 if s == "" {
768 return nil, fmt.Errorf("no code for end-of-block (256)")
769 }
770 deflateGlobalsWriteDynamicHuffmanBits(s)
771 return stateDeflateDynamicHuffman, nil
772 }
773
774 return nil, fmt.Errorf("bad stateDeflateDynamicHuffman command: %q", line)
775}
776
777func stateDeflateDynamicHuffmanHuffman(line string) (stateFunc, error) {
778 g := &deflateGlobals
779outer:
780 switch {
781 case line == "}":
Nigel Tao92ea6312022-01-09 10:41:31 +1100782 if !deflateGlobalsIsHuffmanCanonical() {
783 return nil, fmt.Errorf(`"huffman %s" is non-canonical`, deflateHuffmanNames[g.whichHuffman])
784 }
Nigel Tao12347602019-06-16 23:15:05 +1000785 g.whichHuffman = 0
Nigel Taoff7bffc2019-10-13 23:23:48 +1100786 g.prevLine = ""
787 g.etcetera = false
Nigel Tao42de49a2022-01-09 10:45:47 +1100788 g.incomplete = false
Nigel Tao12347602019-06-16 23:15:05 +1000789
790 // If we have all three Huffman tables, write them.
791 for i := 1; ; i++ {
792 if i == 4 {
793 if err := deflateGlobalsWriteDynamicHuffmanTables(); err != nil {
794 return nil, err
795 }
796 break
797 }
798 if g.huffmans[i] == nil {
799 break
800 }
801 }
802
803 return stateDeflateDynamicHuffman, nil
804
Nigel Taoff7bffc2019-10-13 23:23:48 +1100805 case line == "etcetera":
806 g.etcetera = true
807 return stateDeflateDynamicHuffmanHuffman, nil
808
Nigel Tao42de49a2022-01-09 10:45:47 +1100809 case line == "incomplete":
810 g.incomplete = true
811 return stateDeflateDynamicHuffmanHuffman, nil
812
Nigel Tao12347602019-06-16 23:15:05 +1000813 default:
814 s := line
Nigel Taoe1b43cd2022-06-08 14:04:09 +1000815 n, s, ok := parseNum32(s)
Nigel Tao12347602019-06-16 23:15:05 +1000816 if !ok || s == "" {
817 break
818 }
819 for i := 0; i < len(s); i++ {
820 if c := s[i]; c != '0' && c != '1' {
821 break outer
822 }
823 }
Nigel Taoff7bffc2019-10-13 23:23:48 +1100824 if (len(s) < 1) || (15 < len(s)) {
825 return nil, fmt.Errorf("%q code length, %d, is out of range", s, len(s))
826 }
827
828 if g.etcetera {
829 g.etcetera = false
Nigel Taoe1b43cd2022-06-08 14:04:09 +1000830 n0, s0, ok := parseNum32(g.prevLine)
Nigel Taoff7bffc2019-10-13 23:23:48 +1100831 if !ok {
832 return nil, fmt.Errorf("bad etcetera command")
833 }
834 if err := stateDeflateDHHEtcetera(n0, s0, n, s); err != nil {
835 return nil, err
836 }
837 }
838
Nigel Tao12347602019-06-16 23:15:05 +1000839 if g.huffmans[g.whichHuffman] == nil {
840 g.huffmans[g.whichHuffman] = deflateHuffmanTable{}
841 }
842 g.huffmans[g.whichHuffman][n] = s
Nigel Taoff7bffc2019-10-13 23:23:48 +1100843 g.prevLine = line
Nigel Tao12347602019-06-16 23:15:05 +1000844 return stateDeflateDynamicHuffmanHuffman, nil
845 }
846
847 return nil, fmt.Errorf("bad stateDeflateDynamicHuffmanHuffman command: %q", line)
848}
849
Nigel Taoff7bffc2019-10-13 23:23:48 +1100850// stateDeflateDHHEtcetera expands the "etcetera" line in:
851//
852// 0 0000
853// 1 0001
854// etcetera
855// 7 0111
856//
857// to produce the implicit lines:
858//
859// 0 0000
860// 1 0001
861// 2 0010
862// 3 0011
863// 4 0100
864// 5 0101
865// 6 0110
866// 7 0111
867func stateDeflateDHHEtcetera(n0 uint32, s0 string, n1 uint32, s1 string) error {
868 b := []byte(s0)
869 if !incrementBitstring(b) {
870 return fmt.Errorf("etcetera: could not increment bitstring")
871 }
872 for n := n0 + 1; n < n1; n++ {
873 line := fmt.Sprintf("%d %s", n, b)
874 if _, err := stateDeflateDynamicHuffmanHuffman(line); err != nil {
875 return err
876 }
877 if !incrementBitstring(b) {
878 return fmt.Errorf("etcetera: could not increment bitstring")
879 }
880 }
881 if string(b) != s1 {
882 return fmt.Errorf("etcetera: final bitstring: got %q, want %q", b, s1)
883 }
884 return nil
885}
886
887func incrementBitstring(b []byte) (ok bool) {
888 for i := len(b) - 1; i >= 0; i-- {
889 switch b[i] {
890 case '0':
891 b[i] = '1'
892 return true
893 case '1':
894 b[i] = '0'
895 default:
896 return false
897 }
898 }
899 return false
900}
901
Nigel Tao981bf192018-05-23 12:17:55 +1000902type deflateBitStream struct {
903 bits uint32
904 nBits uint32 // Always within [0, 7].
905}
906
907// writeBits writes the low n bits of b to z.
908func (z *deflateBitStream) writeBits(b uint32, n uint32) {
909 if n > 24 {
910 panic("writeBits: n is too large")
911 }
912 z.bits |= b << z.nBits
913 z.nBits += n
914 for z.nBits >= 8 {
915 out = append(out, uint8(z.bits))
916 z.bits >>= 8
917 z.nBits -= 8
918 }
919}
920
921func (z *deflateBitStream) writeBytes(b []byte) {
922 z.flush()
923 out = append(out, b...)
924}
925
Nigel Tao12347602019-06-16 23:15:05 +1000926func (z *deflateBitStream) writeFixedHuffmanLCode(lCode uint32) {
Nigel Tao981bf192018-05-23 12:17:55 +1000927 switch {
928 case lCode < 144: // 0b._0011_0000 through 0b._1011_1111
929 lCode += 0x030
930 z.writeBits(reverse(lCode, 8), 8)
931 case lCode < 256: // 0b1_1001_0000 through 0b1_1111_1111
932 lCode += 0x190 - 144
933 z.writeBits(reverse(lCode, 9), 9)
934 case lCode < 280: // 0b._.000_0000 through 0b._.001_0111
935 lCode -= 256 - 0x000
936 z.writeBits(reverse(lCode, 7), 7)
937 default: // 0b._1100_0000 through 0b._1100_0111
938 lCode -= 280 - 0x0C0
939 z.writeBits(reverse(lCode, 8), 8)
940 }
941}
942
943func (z *deflateBitStream) flush() {
944 if z.nBits > 0 {
945 out = append(out, uint8(z.bits))
946 z.bits = 0
947 z.nBits = 0
948 }
949}
950
951func deflateEncodeLength(l uint32) (code uint32, extra uint32, nExtra uint32) {
952 switch {
953 case l < 3:
954 // No-op.
955 case l < 11:
956 l -= 3
957 return (l >> 0) + 257, l & 0x0000, 0
958 case l < 19:
959 l -= 11
960 return (l >> 1) + 265, l & 0x0001, 1
961 case l < 35:
962 l -= 19
963 return (l >> 2) + 269, l & 0x0003, 2
964 case l < 67:
965 l -= 35
966 return (l >> 3) + 273, l & 0x0007, 3
967 case l < 131:
968 l -= 67
969 return (l >> 4) + 277, l & 0x000F, 4
970 case l < 258:
971 l -= 131
972 return (l >> 5) + 281, l & 0x001F, 5
973 case l == 258:
974 return 285, 0, 0
975 }
976 panic(fmt.Sprintf("deflateEncodeLength: l=%d", l))
977}
978
979func deflateEncodeDistance(d uint32) (code uint32, extra uint32, nExtra uint32) {
980 switch {
981 case d < 1:
982 // No-op.
983 case d < 5:
984 d -= 1
985 return (d >> 0) + 0, d & 0x0000, 0
986 case d < 9:
987 d -= 5
988 return (d >> 1) + 4, d & 0x0001, 1
989 case d < 17:
990 d -= 9
991 return (d >> 2) + 6, d & 0x0003, 2
992 case d < 33:
993 d -= 17
994 return (d >> 3) + 8, d & 0x0007, 3
995 case d < 65:
996 d -= 33
997 return (d >> 4) + 10, d & 0x000F, 4
998 case d < 129:
999 d -= 65
1000 return (d >> 5) + 12, d & 0x001F, 5
1001 case d < 257:
1002 d -= 129
1003 return (d >> 6) + 14, d & 0x003F, 6
1004 case d < 513:
1005 d -= 257
1006 return (d >> 7) + 16, d & 0x007F, 7
1007 case d < 1025:
1008 d -= 513
1009 return (d >> 8) + 18, d & 0x00FF, 8
1010 case d < 2049:
1011 d -= 1025
1012 return (d >> 9) + 20, d & 0x01FF, 9
1013 case d < 4097:
1014 d -= 2049
1015 return (d >> 10) + 22, d & 0x03FF, 10
1016 case d < 8193:
1017 d -= 4097
1018 return (d >> 11) + 24, d & 0x07FF, 11
1019 case d < 16385:
1020 d -= 8193
1021 return (d >> 12) + 26, d & 0x0FFF, 12
1022 case d < 32769:
1023 d -= 16385
1024 return (d >> 13) + 28, d & 0x1FFF, 13
1025 }
1026 panic(fmt.Sprintf("deflateEncodeDistance: d=%d", d))
1027}
1028
1029func deflateParseLiteral(s string) (lit string, ok bool) {
Nigel Tao12347602019-06-16 23:15:05 +10001030 // TODO: support "\xAB" escape codes in the script?
Nigel Tao981bf192018-05-23 12:17:55 +10001031 const (
1032 prefix = `literal "`
1033 suffix = `"`
1034 )
1035 if strings.HasPrefix(s, prefix) {
1036 s = s[len(prefix):]
1037 if strings.HasSuffix(s, suffix) {
1038 return s[:len(s)-len(suffix)], true
1039 }
1040 }
1041 return "", false
1042}
1043
1044func deflateParseLenDist(line string) (l uint32, d uint32, ok bool) {
1045 const (
1046 lStr = "len "
1047 dStr = "dist "
1048 )
1049 s := line
1050 if strings.HasPrefix(s, lStr) {
1051 s = s[len(lStr):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001052 if l, s, ok := parseNum32(s); ok && 3 <= l && l <= 258 {
Nigel Tao981bf192018-05-23 12:17:55 +10001053 if strings.HasPrefix(s, dStr) {
1054 s = s[len(dStr):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001055 if d, s, ok := parseNum32(s); ok && 1 <= d && d <= 32768 && s == "" {
Nigel Tao981bf192018-05-23 12:17:55 +10001056 return l, d, true
1057 }
1058 }
1059 }
1060 }
1061 return 0, 0, false
1062}
1063
1064// ----
1065
1066func init() {
1067 formats["gif"] = stateGif
1068}
1069
1070var gifGlobals struct {
Nigel Tao31a2bc92019-05-18 17:32:24 +10001071 imageWidth uint32
1072 imageHeight uint32
1073 imageBackgroundColorIndex uint32
Nigel Tao981bf192018-05-23 12:17:55 +10001074
Nigel Taoefb18032019-10-05 11:40:24 +10001075 frameInterlaced bool
1076 frameLeft uint32
1077 frameTop uint32
1078 frameWidth uint32
1079 frameHeight uint32
Nigel Tao981bf192018-05-23 12:17:55 +10001080
1081 globalPalette [][4]uint8
Nigel Tao746681e2019-05-04 13:49:22 +10001082 localPalette [][4]uint8
Nigel Tao981bf192018-05-23 12:17:55 +10001083}
1084
1085func stateGif(line string) (stateFunc, error) {
1086 const (
Nigel Tao6700c722019-05-26 09:41:55 +10001087 cmdB = "bytes "
1088 cmdGC = "graphicControl "
1089 cmdL = "lzw "
1090 cmdLC = "loopCount "
Nigel Tao981bf192018-05-23 12:17:55 +10001091 )
1092outer:
1093 switch {
Nigel Tao12347602019-06-16 23:15:05 +10001094 case line == "":
1095 return stateGif, nil
1096
Nigel Tao981bf192018-05-23 12:17:55 +10001097 case line == "frame {":
Nigel Taoefb18032019-10-05 11:40:24 +10001098 gifGlobals.frameInterlaced = false
1099 gifGlobals.frameLeft = 0
1100 gifGlobals.frameTop = 0
1101 gifGlobals.frameWidth = 0
1102 gifGlobals.frameHeight = 0
Nigel Tao746681e2019-05-04 13:49:22 +10001103 gifGlobals.localPalette = nil
Nigel Tao981bf192018-05-23 12:17:55 +10001104 out = append(out, 0x2C)
1105 return stateGifFrame, nil
1106
1107 case line == "header":
1108 out = append(out, "GIF89a"...)
1109 return stateGif, nil
1110
1111 case line == "image {":
1112 return stateGifImage, nil
1113
1114 case line == "trailer":
1115 out = append(out, 0x3B)
1116 return stateGif, nil
1117
Nigel Tao21f28902019-04-20 16:33:11 +10001118 case strings.HasPrefix(line, cmdB):
1119 s := line[len(cmdB):]
1120 for s != "" {
1121 x, ok := uint32(0), false
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001122 x, s, ok = parseHex32(s)
Nigel Tao21f28902019-04-20 16:33:11 +10001123 if !ok {
1124 break outer
1125 }
1126 out = append(out, uint8(x))
1127 }
1128 return stateGif, nil
1129
Nigel Tao6700c722019-05-26 09:41:55 +10001130 case strings.HasPrefix(line, cmdGC):
1131 s := line[len(cmdGC):]
1132
1133 flags := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +10001134 duration := uint32(0)
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001135 transparentIndex := uint8(0)
Nigel Tao344123b2020-09-25 23:00:01 +10001136 for s != "" {
1137 term := ""
1138 if i := strings.IndexByte(s, ' '); i >= 0 {
1139 term, s = s[:i], s[i+1:]
1140 } else {
1141 term, s = s, ""
1142 }
1143
1144 const (
1145 ms = "ms"
1146 trans = "transparentIndex="
1147 )
1148 switch {
1149 case term == "animationDisposalNone":
Nigel Tao6700c722019-05-26 09:41:55 +10001150 flags |= 0x00
Nigel Tao344123b2020-09-25 23:00:01 +10001151 case term == "animationDisposalRestoreBackground":
Nigel Tao6700c722019-05-26 09:41:55 +10001152 flags |= 0x08
Nigel Tao344123b2020-09-25 23:00:01 +10001153 case term == "animationDisposalRestorePrevious":
Nigel Tao6700c722019-05-26 09:41:55 +10001154 flags |= 0x0C
Nigel Tao344123b2020-09-25 23:00:01 +10001155 case strings.HasPrefix(term, trans):
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001156 num, err := strconv.ParseUint(term[len(trans):], 0, 8)
1157 if err != nil {
Nigel Tao344123b2020-09-25 23:00:01 +10001158 break outer
1159 }
1160 flags |= 0x01
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001161 transparentIndex = uint8(num)
Nigel Tao344123b2020-09-25 23:00:01 +10001162 case strings.HasSuffix(term, ms):
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001163 num, remaining, ok := parseNum32(term[:len(term)-len(ms)])
Nigel Tao344123b2020-09-25 23:00:01 +10001164 if !ok || remaining != "" {
1165 break outer
1166 }
1167 duration = num / 10 // GIF's unit of time is 10ms.
Nigel Tao6700c722019-05-26 09:41:55 +10001168 default:
1169 break outer
1170 }
Nigel Tao6700c722019-05-26 09:41:55 +10001171 }
1172
Nigel Taob1ff27f2019-05-12 22:00:44 +10001173 out = append(out,
Nigel Tao6700c722019-05-26 09:41:55 +10001174 0x21, 0xF9, 0x04,
1175 flags,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001176 uint8(duration>>0),
1177 uint8(duration>>8),
Nigel Tao6c2fb9a2020-09-27 00:09:25 +10001178 transparentIndex,
Nigel Tao6700c722019-05-26 09:41:55 +10001179 0x00,
Nigel Taob1ff27f2019-05-12 22:00:44 +10001180 )
1181 return stateGif, nil
1182
Nigel Tao981bf192018-05-23 12:17:55 +10001183 case strings.HasPrefix(line, cmdL):
1184 s := line[len(cmdL):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001185 litWidth, s, ok := parseNum32(s)
Nigel Tao981bf192018-05-23 12:17:55 +10001186 if !ok || litWidth < 2 || 8 < litWidth {
1187 break
1188 }
1189 out = append(out, uint8(litWidth))
1190
1191 uncompressed := []byte(nil)
1192 for s != "" {
1193 x := uint32(0)
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001194 x, s, ok = parseHex32(s)
Nigel Tao981bf192018-05-23 12:17:55 +10001195 if !ok {
1196 break outer
1197 }
1198 uncompressed = append(uncompressed, uint8(x))
1199 }
1200
1201 buf := bytes.NewBuffer(nil)
1202 w := lzw.NewWriter(buf, lzw.LSB, int(litWidth))
1203 if _, err := w.Write(uncompressed); err != nil {
1204 return nil, err
1205 }
1206 if err := w.Close(); err != nil {
1207 return nil, err
1208 }
1209 compressed := buf.Bytes()
1210
1211 for len(compressed) > 0 {
1212 if len(compressed) <= 0xFF {
1213 out = append(out, uint8(len(compressed)))
1214 out = append(out, compressed...)
1215 compressed = nil
1216 } else {
1217 out = append(out, 0xFF)
1218 out = append(out, compressed[:0xFF]...)
1219 compressed = compressed[0xFF:]
1220 }
1221 }
1222 out = append(out, 0x00)
1223 return stateGif, nil
Nigel Tao7be9c392018-10-13 17:00:45 +11001224
1225 case strings.HasPrefix(line, cmdLC):
1226 s := line[len(cmdLC):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001227 loopCount, _, ok := parseNum32(s)
Nigel Tao7be9c392018-10-13 17:00:45 +11001228 if !ok || 0xFFFF < loopCount {
1229 break
1230 }
1231 out = append(out,
1232 0x21, // Extension Introducer.
1233 0xFF, // Application Extension Label.
1234 0x0B, // Block Size.
1235 )
1236 out = append(out, "NETSCAPE2.0"...)
1237 out = append(out,
1238 0x03, // Block Size.
1239 0x01, // Magic Number.
1240 byte(loopCount),
1241 byte(loopCount>>8),
1242 0x00, // Block Terminator.
1243 )
1244 return stateGif, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001245 }
1246
1247 return nil, fmt.Errorf("bad stateGif command: %q", line)
1248}
1249
1250func stateGifImage(line string) (stateFunc, error) {
1251 g := &gifGlobals
1252 if line == "}" {
1253 out = appendU16LE(out, uint16(g.imageWidth))
1254 out = appendU16LE(out, uint16(g.imageHeight))
1255 if g.globalPalette == nil {
1256 out = append(out, 0x00)
1257 } else if n := log2(uint32(len(g.globalPalette))); n < 2 || 8 < n {
1258 return nil, fmt.Errorf("bad len(g.globalPalette): %d", len(g.globalPalette))
1259 } else {
1260 out = append(out, 0x80|uint8(n-1))
1261 }
Nigel Tao31a2bc92019-05-18 17:32:24 +10001262 out = append(out, uint8(g.imageBackgroundColorIndex))
Nigel Tao981bf192018-05-23 12:17:55 +10001263 out = append(out, 0x00)
1264 for _, x := range g.globalPalette {
1265 out = append(out, x[0], x[1], x[2])
1266 }
1267 return stateGif, nil
1268 }
1269
1270 const (
Nigel Tao31a2bc92019-05-18 17:32:24 +10001271 cmdBCI = "backgroundColorIndex "
Nigel Tao981bf192018-05-23 12:17:55 +10001272 cmdIWH = "imageWidthHeight "
1273 cmdP = "palette {"
1274 )
1275 switch {
Nigel Tao31a2bc92019-05-18 17:32:24 +10001276 case strings.HasPrefix(line, cmdBCI):
1277 s := line[len(cmdBCI):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001278 if i, _, ok := parseNum32(s); ok {
Nigel Tao31a2bc92019-05-18 17:32:24 +10001279 g.imageBackgroundColorIndex = i
1280 }
1281 return stateGifImage, nil
1282
Nigel Tao981bf192018-05-23 12:17:55 +10001283 case strings.HasPrefix(line, cmdIWH):
1284 s := line[len(cmdIWH):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001285 if w, s, ok := parseNum32(s); ok {
1286 if h, _, ok := parseNum32(s); ok {
Nigel Tao981bf192018-05-23 12:17:55 +10001287 g.imageWidth = w
1288 g.imageHeight = h
1289 return stateGifImage, nil
1290 }
1291 }
1292
1293 case strings.HasPrefix(line, cmdP):
1294 return stateGifImagePalette, nil
1295 }
1296
1297 return nil, fmt.Errorf("bad stateGifImage command: %q", line)
1298}
1299
1300func stateGifFrame(line string) (stateFunc, error) {
1301 g := &gifGlobals
1302 if line == "}" {
1303 out = appendU16LE(out, uint16(g.frameLeft))
1304 out = appendU16LE(out, uint16(g.frameTop))
1305 out = appendU16LE(out, uint16(g.frameWidth))
1306 out = appendU16LE(out, uint16(g.frameHeight))
Nigel Taoefb18032019-10-05 11:40:24 +10001307 flags := byte(0x00)
1308 if g.frameInterlaced {
1309 flags |= 0x40
1310 }
Nigel Tao746681e2019-05-04 13:49:22 +10001311 if g.localPalette == nil {
Nigel Taoefb18032019-10-05 11:40:24 +10001312 out = append(out, flags)
Nigel Tao746681e2019-05-04 13:49:22 +10001313 } else if n := log2(uint32(len(g.localPalette))); n < 2 || 8 < n {
1314 return nil, fmt.Errorf("bad len(g.localPalette): %d", len(g.localPalette))
1315 } else {
Nigel Taoefb18032019-10-05 11:40:24 +10001316 out = append(out, flags|0x80|uint8(n-1))
Nigel Tao746681e2019-05-04 13:49:22 +10001317 }
1318 for _, x := range g.localPalette {
1319 out = append(out, x[0], x[1], x[2])
1320 }
Nigel Tao981bf192018-05-23 12:17:55 +10001321 return stateGif, nil
1322 }
1323
1324 const (
1325 cmdFLTWH = "frameLeftTopWidthHeight "
Nigel Tao746681e2019-05-04 13:49:22 +10001326 cmdP = "palette {"
Nigel Tao981bf192018-05-23 12:17:55 +10001327 )
1328 switch {
Nigel Taoefb18032019-10-05 11:40:24 +10001329 case line == "interlaced":
1330 g.frameInterlaced = true
1331 return stateGifFrame, nil
1332
Nigel Tao981bf192018-05-23 12:17:55 +10001333 case strings.HasPrefix(line, cmdFLTWH):
1334 s := line[len(cmdFLTWH):]
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001335 if l, s, ok := parseNum32(s); ok {
1336 if t, s, ok := parseNum32(s); ok {
1337 if w, s, ok := parseNum32(s); ok {
1338 if h, _, ok := parseNum32(s); ok {
Nigel Tao981bf192018-05-23 12:17:55 +10001339 g.frameLeft = l
1340 g.frameTop = t
1341 g.frameWidth = w
1342 g.frameHeight = h
1343 return stateGifFrame, nil
1344 }
1345 }
1346 }
1347 }
Nigel Tao746681e2019-05-04 13:49:22 +10001348
1349 case strings.HasPrefix(line, cmdP):
1350 return stateGifFramePalette, nil
Nigel Tao981bf192018-05-23 12:17:55 +10001351 }
1352
1353 return nil, fmt.Errorf("bad stateGifFrame command: %q", line)
1354}
1355
1356func stateGifImagePalette(line string) (stateFunc, error) {
1357 g := &gifGlobals
1358 if line == "}" {
1359 return stateGifImage, nil
1360 }
1361
1362 s := line
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001363 if rgb0, s, ok := parseHex32(s); ok {
1364 if rgb1, s, ok := parseHex32(s); ok {
1365 if rgb2, _, ok := parseHex32(s); ok {
Nigel Tao981bf192018-05-23 12:17:55 +10001366 g.globalPalette = append(g.globalPalette,
1367 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1368 return stateGifImagePalette, nil
1369 }
1370 }
1371 }
1372
1373 return nil, fmt.Errorf("bad stateGifImagePalette command: %q", line)
1374}
Nigel Tao746681e2019-05-04 13:49:22 +10001375
1376func stateGifFramePalette(line string) (stateFunc, error) {
1377 g := &gifGlobals
1378 if line == "}" {
1379 return stateGifFrame, nil
1380 }
1381
1382 s := line
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001383 if rgb0, s, ok := parseHex32(s); ok {
1384 if rgb1, s, ok := parseHex32(s); ok {
1385 if rgb2, _, ok := parseHex32(s); ok {
Nigel Tao746681e2019-05-04 13:49:22 +10001386 g.localPalette = append(g.localPalette,
1387 [4]uint8{uint8(rgb0), uint8(rgb1), uint8(rgb2), 0xFF})
1388 return stateGifFramePalette, nil
1389 }
1390 }
1391 }
1392
1393 return nil, fmt.Errorf("bad stateGifFramePalette command: %q", line)
1394}
Nigel Tao6c2a5912021-11-16 11:00:48 +11001395
1396// ----
1397
1398func init() {
1399 formats["png"] = statePng
1400}
1401
1402var pngGlobals struct {
1403 scratch [4]byte
1404
1405 animSeqNum uint32
1406 chunkData bytes.Buffer
1407 chunkType string
1408 zlibWriter *zlib.Writer
1409}
1410
1411func statePng(line string) (stateFunc, error) {
1412 g := &pngGlobals
1413 switch {
1414 case line == "":
1415 return statePng, nil
1416
1417 case line == "magic":
1418 out = append(out, "\x89PNG\x0D\x0A\x1A\x0A"...)
1419 return statePng, nil
1420
1421 case (len(line) == 6) && (line[4] == ' ') && (line[5] == '{'):
1422 g.chunkData.Reset()
1423 g.chunkType = line[:4]
1424 return statePngChunk, nil
1425 }
1426
1427 return nil, fmt.Errorf("bad statePng command: %q", line)
1428}
1429
1430func statePngChunk(line string) (stateFunc, error) {
1431 g := &pngGlobals
1432 if line == "}" {
1433 if n := g.chunkData.Len(); n > 0xFFFF_FFFF {
1434 return nil, fmt.Errorf("chunkData is too long")
1435 }
1436 out = appendU32BE(out, uint32(g.chunkData.Len()))
1437 n := len(out)
1438 out = append(out, g.chunkType...)
1439 out = append(out, g.chunkData.Bytes()...)
1440 out = appendU32BE(out, crc32.ChecksumIEEE(out[n:]))
1441 return statePng, nil
1442 }
1443
1444 switch {
1445 case line == "animSeqNum++":
1446 g.chunkData.Write(appendU32BE(g.scratch[:0], g.animSeqNum))
1447 g.animSeqNum++
1448 return statePngChunk, nil
1449
1450 case line == "raw {":
1451 return statePngRaw, nil
1452
1453 case line == "zlib {":
1454 g.zlibWriter = zlib.NewWriter(&g.chunkData)
1455 return statePngZlib, nil
1456 }
1457
1458 return nil, fmt.Errorf("bad statePngChunk command: %q", line)
1459}
1460
1461func statePngRaw(line string) (stateFunc, error) {
1462 g := &pngGlobals
1463 if line == "}" {
1464 return statePngChunk, nil
1465 }
1466
1467 for s := line; s != ""; {
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001468 if x, remaining, ok := parseHex32(s); ok {
Nigel Tao6c2a5912021-11-16 11:00:48 +11001469 g.chunkData.WriteByte(byte(x))
1470 s = remaining
1471 } else {
1472 return nil, fmt.Errorf("bad statePngRaw command: %q", line)
1473 }
1474 }
1475 return statePngRaw, nil
1476}
1477
1478func statePngZlib(line string) (stateFunc, error) {
1479 g := &pngGlobals
1480 if line == "}" {
1481 if err := g.zlibWriter.Close(); err != nil {
1482 return nil, fmt.Errorf("statePngZlib: %w", err)
1483 }
1484 return statePngChunk, nil
1485 }
1486
1487 for s := line; s != ""; {
Nigel Taoe1b43cd2022-06-08 14:04:09 +10001488 if x, remaining, ok := parseHex32(s); ok {
Nigel Tao6c2a5912021-11-16 11:00:48 +11001489 g.scratch[0] = byte(x)
1490 g.zlibWriter.Write(g.scratch[:1])
1491 s = remaining
1492 } else {
1493 return nil, fmt.Errorf("bad statePngZlib command: %q", line)
1494 }
1495 }
1496 return statePngZlib, nil
1497}