forked from parsiya/golnk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbytes.go
More file actions
executable file
·236 lines (213 loc) · 6.91 KB
/
bytes.go
File metadata and controls
executable file
·236 lines (213 loc) · 6.91 KB
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package lnk
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"unicode/utf8"
)
// byteMaskuint16 returns one of the two bytes from an uint16.
func byteMaskuint16(b uint16, n int) uint16 {
// Maybe we should not panic, hmm.
if n < 0 || n > 2 {
panic(fmt.Sprintf("invalid byte mask, got %d", n))
}
mask := uint16(0x000000FF) << uint16(n*8)
return (b & mask) >> uint16(n*8)
}
// bitMaskuint32 returns one of the 32-bits from an uint32.
// Returns true for 1 and false for 0.
func bitMaskuint32(b uint32, n int) bool {
if n < 0 || n > 31 {
panic(fmt.Sprintf("invalid bit number, got %d", n))
}
return ((b >> uint(n)) & 1) == 1
}
/*
readSection reads a size from the start of the io.Reader. The size length is
decided by the parameter sSize.
sSize == 2 - read uint16
sSize == 4 - read uint32
sSize == 8 - read uint64 - Not needed for now.
Then read (size-sSize) bytes, populate the start with the original bytes
and add the rest. Finally return the []byte and a new io.Reader to it.
The size bytes are added to the start of the []byte to keep the section
[]byte intact for later offset use.
*/
func readSection(r io.Reader, sSize int, maxSize uint64) (data []byte, nr io.Reader, size int, err error) {
// We are not going to lose data by copying a smaller var into a larger one.
var sectionSize uint64
switch sSize {
case 2:
// Read uint16.
var size16 uint16
err = binary.Read(r, binary.LittleEndian, &size16)
if err != nil {
return data, nr, size, fmt.Errorf("lnk.readSection: read size %d bytes - %s", sSize, err.Error())
}
sectionSize = uint64(size16)
// Add bytes to the start of data []byte.
data = uint16Byte(size16)
case 4:
// Read uint32.
var size32 uint32
err = binary.Read(r, binary.LittleEndian, &size32)
if err != nil {
return data, nr, size, fmt.Errorf("lnk.readSection: read size %d bytes - %s", sSize, err.Error())
}
sectionSize = uint64(size32)
// Add bytes to the start of data []byte.
data = uint32Byte(size32)
case 8:
// Read uint64 or sectionSize.
err = binary.Read(r, binary.LittleEndian, §ionSize)
if err != nil {
return data, nr, size, fmt.Errorf("lnk.readSection: read size %d bytes - %s", sSize, err.Error())
}
// Add bytes to the start of data []byte.
data = uint64Byte(sectionSize)
default:
return data, nr, size, fmt.Errorf("lnk.readSection: invalid sSize - got %v", sSize)
}
// Create a []byte of sectionSize-4 and read that many bytes from io.Reader.
computedSize := sectionSize - uint64(sSize)
if computedSize > maxSize {
return data, nr, size, fmt.Errorf("lnk.readSection: invalid computed size got %d; expected a size < %d", computedSize, maxSize)
}
tempData := make([]byte, computedSize)
err = binary.Read(r, binary.LittleEndian, &tempData)
if err != nil {
return data, nr, size, fmt.Errorf("lnk.readSection: read section %d bytes - %s", sectionSize-uint64(sSize), err.Error())
}
// If this is successful, append it to data []byte.
data = append(data, tempData...)
// Create a reader from the unread bytes.
nr = bytes.NewReader(tempData)
return data, nr, int(sectionSize), nil
}
// readString returns a string of all bytes from the []byte until the first 0x00.
func readString(data []byte) string {
// Find the index of first 0x00.
i := bytes.IndexByte(data, byte(0x00))
if i == -1 {
// If 0x00 is not found, return all the slice.
i = len(data)
}
return string(data[:i])
}
// readUnicodeString returns a string of all bytes from the []byte until the
// first 0x0000.
func readUnicodeString(data []byte) string {
// Read two bytes at a time and convert to rune, stop if both are 0x0000, or
// we have reached the end of the input.
var runes []rune
for bitIndex := 0; bitIndex < len(data)/2; bitIndex++ {
if data[bitIndex*2] == 0x00 && data[(bitIndex*2)+1] == 0x00 {
return string(runes)
}
r, _ := utf8.DecodeRune(data[bitIndex*2:])
runes = append(runes, r)
}
return string(runes)
}
// readStringData reads a uint16 as size and then reads that many bytes
// (*2 for unicode) into a string. The string is not null-terminated.
// TODO: Write tests.
func readStringData(r io.Reader, isUnicode bool) (str string, err error) {
// Recover in case we attempt to read more bytes than there is in the reader.
defer func() {
if r := recover(); r != nil {
// If panic occurs, return this error message
err = fmt.Errorf("lnk.readStringData: not enough bytes in reader")
}
}()
var size uint16
err = binary.Read(r, binary.LittleEndian, &size)
if err != nil {
return str, fmt.Errorf("lnk.readStringData: read size - %s", err.Error())
}
if isUnicode {
size = size * 2
}
b := make([]byte, size)
err = binary.Read(r, binary.LittleEndian, &b)
if err != nil {
return str, fmt.Errorf("lnk.readStringData: read bytes - %s", err.Error())
}
// If Unicode, read every 2 byte and get a rune.
if isUnicode {
var runes []rune
for bitIndex := 0; bitIndex < int(size)/2; bitIndex++ {
r, _ := utf8.DecodeRune(b[bitIndex*2:])
runes = append(runes, r)
}
return string(runes), nil
}
return string(b), nil
}
// uint16Little reads an uint16 from []byte and returns the result in Little-Endian.
func uint16Little(b []byte) uint16 {
if len(b) < 2 {
panic(fmt.Sprintf("input smaller than two bytes - got %d", len(b)))
}
return binary.LittleEndian.Uint16(b)
}
// uint32Little reads an uint32 from []byte and returns the result in Little-Endian.
func uint32Little(b []byte) uint32 {
if len(b) < 4 {
panic(fmt.Sprintf("input smaller than four bytes - got %d", len(b)))
}
return binary.LittleEndian.Uint32(b)
}
// uint64Little reads an uint64 from []byte and returns the result in Little-Endian.
func uint64Little(b []byte) uint64 {
if len(b) < 8 {
panic(fmt.Sprintf("input smaller than eight bytes - got %d", len(b)))
}
return binary.LittleEndian.Uint64(b)
}
// uint32Str converts an uint32 to string using fmt.Sprint.
func uint32Str(u uint32) string {
return fmt.Sprint(u)
}
// uint32StrHex converts an uint32 to a hex encoded string using fmt.Sprintf.
func uint32StrHex(u uint32) string {
str := fmt.Sprintf("%x", u)
// Add a 0 to the start of odd-length string. This converts "0x1AB" to "0x01AB"
if (len(str) % 2) != 0 {
str = "0" + str
}
return "0x" + str
}
// uint32TableStr creates a string that has both decimal and hex values
// of uint32.
func uint32TableStr(u uint32) string {
return fmt.Sprintf("%s - %s", uint32Str(u), uint32StrHex(u))
}
// uint16Byte converts a uint16 to a []byte.
func uint16Byte(u uint16) []byte {
var buf bytes.Buffer
err := binary.Write(&buf, binary.LittleEndian, u)
if err != nil {
panic(err)
}
return buf.Bytes()
}
// uint32Byte converts a uint32 to a []byte.
func uint32Byte(u uint32) []byte {
var buf bytes.Buffer
err := binary.Write(&buf, binary.LittleEndian, u)
if err != nil {
panic(err)
}
return buf.Bytes()
}
// uint64Byte converts a uint64 to a []byte.
func uint64Byte(u uint64) []byte {
var buf bytes.Buffer
err := binary.Write(&buf, binary.LittleEndian, u)
if err != nil {
panic(err)
}
return buf.Bytes()
}