summaryrefslogtreecommitdiff
path: root/vendor/github.com/abema/go-mp4/internal/bitio/read.go
blob: 4da76eae6b7642fedac7aff0b1bce6d29854eac6 (plain)
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package bitio

import "io"

type Reader interface {
	io.Reader

	// alignment:
	//  |-1-byte-block-|--------------|--------------|--------------|
	//  |<-offset->|<-------------------width---------------------->|
	ReadBits(width uint) (data []byte, err error)

	ReadBit() (bit bool, err error)
}

type ReadSeeker interface {
	Reader
	io.Seeker
}

type reader struct {
	reader io.Reader
	octet  byte
	width  uint
}

func NewReader(r io.Reader) Reader {
	return &reader{reader: r}
}

func (r *reader) Read(p []byte) (n int, err error) {
	if r.width != 0 {
		return 0, ErrInvalidAlignment
	}
	return r.reader.Read(p)
}

func (r *reader) ReadBits(size uint) ([]byte, error) {
	bytes := (size + 7) / 8
	data := make([]byte, bytes)
	offset := (bytes * 8) - (size)

	for i := uint(0); i < size; i++ {
		bit, err := r.ReadBit()
		if err != nil {
			return nil, err
		}

		byteIdx := (offset + i) / 8
		bitIdx := 7 - (offset+i)%8
		if bit {
			data[byteIdx] |= 0x1 << bitIdx
		}
	}

	return data, nil
}

func (r *reader) ReadBit() (bool, error) {
	if r.width == 0 {
		buf := make([]byte, 1)
		if n, err := r.reader.Read(buf); err != nil {
			return false, err
		} else if n != 1 {
			return false, ErrDiscouragedReader
		}
		r.octet = buf[0]
		r.width = 8
	}

	r.width--
	return (r.octet>>r.width)&0x01 != 0, nil
}

type readSeeker struct {
	reader
	seeker io.Seeker
}

func NewReadSeeker(r io.ReadSeeker) ReadSeeker {
	return &readSeeker{
		reader: reader{reader: r},
		seeker: r,
	}
}

func (r *readSeeker) Seek(offset int64, whence int) (int64, error) {
	if whence == io.SeekCurrent && r.reader.width != 0 {
		return 0, ErrInvalidAlignment
	}
	n, err := r.seeker.Seek(offset, whence)
	if err != nil {
		return n, err
	}
	r.reader.width = 0
	return n, nil
}