-
Notifications
You must be signed in to change notification settings - Fork 0
/
simg2img.go
171 lines (153 loc) · 4.05 KB
/
simg2img.go
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package sparse
import (
"bytes"
"errors"
"io"
)
func Simg2imgReader(input io.Reader) (io.Reader, error) {
header, err := readHeader(input)
if err != nil {
return nil, err
}
return &sparseReader{input, header, nil, io.LimitReader(input, 0)}, nil
}
func Simg2imgWriter(output io.WriteSeeker) io.Writer {
return &sparseWriter{output, 0, new(bytes.Buffer), nil, nil, 0, 28, 0}
}
type sparseReader struct {
io.Reader
header *SparseHeader
currentChunkHeader *ChunkHeader
currentChunkReader io.Reader
}
func (self *sparseReader) Read(p []byte) (n int, err error) {
bytesRead := 0
for {
n, err := self.currentChunkReader.Read(p[bytesRead:])
bytesRead = bytesRead + n
if err != nil && err != io.EOF {
return bytesRead, err
} else if err == io.EOF {
// Load next chunk (will run on the first read since initial self.currentChunkReader returns EOF on reads)
chunkHeader, err := readChunkHeader(self.Reader)
if err != nil && err != io.EOF {
return bytesRead, err
} else if err == io.EOF {
// No more chunks
return bytesRead, io.EOF
}
self.currentChunkHeader = chunkHeader
chunkReader, err := chunkReader(self.header, self.currentChunkHeader, self.Reader)
if err != nil {
return bytesRead, err
}
self.currentChunkReader = chunkReader
} else {
if bytesRead == len(p) {
// We are done
break
} else {
// Shorter read than expected. Lets try reading more to see what happens.
// (the current chunk is probably empty, must be replaced with a new one)
}
}
}
return bytesRead, nil
}
type state int
const (
state_reading_header state = iota
state_reading_chunk_header
state_reading_raw
state_failed
)
type sparseWriter struct {
output io.WriteSeeker
state state
buffer *bytes.Buffer
header *SparseHeader
currentChunkHeader *ChunkHeader
currentChunkBytesRead int64
totalSize int64
chunkIndex int
}
func (self *sparseWriter) Write(p []byte) (n int, err error) {
if self.state == state_failed {
return 0, errors.New("Writing to failed simg2img writer")
}
n, err = self.buffer.Write(p)
if err != nil {
return
}
for {
if self.buffer.Len() == 0 {
return
}
switch self.state {
case state_reading_header:
if self.buffer.Len() >= 28 {
header, er := readHeader(self.buffer)
if er != nil {
err = er
return
}
self.header = header
self.state = state_reading_chunk_header
} else {
return
}
case state_reading_chunk_header:
if self.buffer.Len() >= 12 {
chunkHeader, er := readChunkHeader(self.buffer)
if er != nil {
err = er
return
}
self.chunkIndex += 1
self.currentChunkHeader = chunkHeader
self.currentChunkBytesRead = 0
if chunkHeader.ChunkType == type_raw {
self.state = state_reading_raw
} else if chunkHeader.ChunkType == type_dont_care {
l := self.header.BlockSize * chunkHeader.ChunkSize
self.output.Seek(int64(l), io.SeekCurrent)
if self.chunkIndex == int(self.header.TotalChunks) {
self.output.Seek(-1, io.SeekCurrent)
nw, er := self.output.Write([]byte{0x00})
if er != nil {
err = er
return
} else if nw != 1 {
err = errors.New("Unable to write ending 0x00 byte")
return
}
return // Aaaand we're done!
}
self.state = state_reading_chunk_header
} else { // Unsupported TYPE_FILL
err = errors.New("Unsupported chunk type type_fill")
self.state = state_failed
return
}
} else {
return
}
case state_reading_raw:
l := int64(self.header.BlockSize*self.currentChunkHeader.ChunkSize) - self.currentChunkBytesRead
nw, er := io.Copy(self.output, io.LimitReader(self.buffer, l))
self.currentChunkBytesRead += nw
if er != nil && er != io.EOF {
err = er
self.state = state_failed
return
}
if nw == l {
// We're done with this chunk
self.state = state_reading_chunk_header
} else {
// This was as far as we got with what we have in self.buffer
return
}
}
}
}