1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package pngmeta
import (
"encoding/binary"
"errors"
"hash"
"hash/crc32"
"io"
)
var (
ErrCRC32Mismatch = errors.New("crc32 mismatch")
ErrNotPNG = errors.New("not png")
ErrBadLength = errors.New("bad length")
)
const header = "\x89PNG\r\n\x1a\n"
type PngChunk struct {
typ string
length int32
r io.Reader
realR io.Reader
checksummer hash.Hash32
}
func (c *PngChunk) Read(p []byte) (int, error) {
return io.TeeReader(c.r, c.checksummer).Read(p)
}
func (c *PngChunk) Close() error {
var crc32 uint32
if err := binary.Read(c.realR, binary.BigEndian, &crc32); err != nil {
return err
}
if crc32 != c.checksummer.Sum32() {
return ErrCRC32Mismatch
}
return nil
}
func (c *PngChunk) Type() string {
return c.typ
}
type Reader struct {
r io.Reader
}
func NewPNGStepReader(r io.Reader) (*Reader, error) {
expectedHeader := make([]byte, len(header))
if _, err := io.ReadFull(r, expectedHeader); err != nil {
return nil, err
}
if string(expectedHeader) != header {
return nil, ErrNotPNG
}
return &Reader{r}, nil
}
func (r *Reader) Next() (*PngChunk, error) {
var length int32
if err := binary.Read(r.r, binary.BigEndian, &length); err != nil {
return nil, err
}
if length < 0 {
return nil, ErrBadLength
}
var rawTyp [4]byte
if _, err := io.ReadFull(r.r, rawTyp[:]); err != nil {
return nil, err
}
typ := string(rawTyp[:])
checksummer := crc32.NewIEEE()
checksummer.Write([]byte(typ))
return &PngChunk{typ, length, io.LimitReader(r.r, int64(length)), r.r, checksummer}, nil
}
|