Skip to content

Commit 9a1f543

Browse files
authored
Optimize network types IO (#286)
1 parent a5fbf35 commit 9a1f543

File tree

3 files changed

+79
-48
lines changed

3 files changed

+79
-48
lines changed

net/packet/types.go

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package packet
22

33
import (
4+
"encoding/binary"
45
"errors"
56
"io"
67
"math"
@@ -175,8 +176,9 @@ func (u *UnsignedByte) ReadFrom(r io.Reader) (n int64, err error) {
175176
}
176177

177178
func (s Short) WriteTo(w io.Writer) (int64, error) {
178-
n := uint16(s)
179-
nn, err := w.Write([]byte{byte(n >> 8), byte(n)})
179+
var buf [2]byte
180+
binary.BigEndian.PutUint16(buf[:], uint16(s))
181+
nn, err := w.Write(buf[:])
180182
return int64(nn), err
181183
}
182184

@@ -188,13 +190,14 @@ func (s *Short) ReadFrom(r io.Reader) (n int64, err error) {
188190
n += int64(nn)
189191
}
190192

191-
*s = Short(int16(bs[0])<<8 | int16(bs[1]))
193+
*s = Short(binary.BigEndian.Uint16(bs[:]))
192194
return
193195
}
194196

195197
func (us UnsignedShort) WriteTo(w io.Writer) (int64, error) {
196-
n := uint16(us)
197-
nn, err := w.Write([]byte{byte(n >> 8), byte(n)})
198+
var buf [2]byte
199+
binary.BigEndian.PutUint16(buf[:], uint16(us))
200+
nn, err := w.Write(buf[:])
198201
return int64(nn), err
199202
}
200203

@@ -206,13 +209,14 @@ func (us *UnsignedShort) ReadFrom(r io.Reader) (n int64, err error) {
206209
n += int64(nn)
207210
}
208211

209-
*us = UnsignedShort(int16(bs[0])<<8 | int16(bs[1]))
212+
*us = UnsignedShort(binary.BigEndian.Uint16(bs[:]))
210213
return
211214
}
212215

213216
func (i Int) WriteTo(w io.Writer) (int64, error) {
214-
n := uint32(i)
215-
nn, err := w.Write([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
217+
var buf [4]byte
218+
binary.BigEndian.PutUint32(buf[:], uint32(i))
219+
nn, err := w.Write(buf[:])
216220
return int64(nn), err
217221
}
218222

@@ -224,16 +228,14 @@ func (i *Int) ReadFrom(r io.Reader) (n int64, err error) {
224228
n += int64(nn)
225229
}
226230

227-
*i = Int(int32(bs[0])<<24 | int32(bs[1])<<16 | int32(bs[2])<<8 | int32(bs[3]))
231+
*i = Int(binary.BigEndian.Uint32(bs[:]))
228232
return
229233
}
230234

231235
func (l Long) WriteTo(w io.Writer) (int64, error) {
232-
n := uint64(l)
233-
nn, err := w.Write([]byte{
234-
byte(n >> 56), byte(n >> 48), byte(n >> 40), byte(n >> 32),
235-
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
236-
})
236+
var buf [8]byte
237+
binary.BigEndian.PutUint64(buf[:], uint64(l))
238+
nn, err := w.Write(buf[:])
237239
return int64(nn), err
238240
}
239241

@@ -245,8 +247,7 @@ func (l *Long) ReadFrom(r io.Reader) (n int64, err error) {
245247
n += int64(nn)
246248
}
247249

248-
*l = Long(int64(bs[0])<<56 | int64(bs[1])<<48 | int64(bs[2])<<40 | int64(bs[3])<<32 |
249-
int64(bs[4])<<24 | int64(bs[5])<<16 | int64(bs[6])<<8 | int64(bs[7]))
250+
*l = Long(binary.BigEndian.Uint64(bs[:]))
250251
return
251252
}
252253

@@ -260,36 +261,48 @@ func (v VarInt) WriteTo(w io.Writer) (n int64, err error) {
260261
// WriteToBytes encodes the VarInt into buf and returns the number of bytes written.
261262
// If the buffer is too small, WriteToBytes will panic.
262263
func (v VarInt) WriteToBytes(buf []byte) int {
264+
// https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
263265
num := uint32(v)
264-
i := 0
265-
for {
266-
b := num & 0x7F
267-
num >>= 7
268-
if num != 0 {
269-
b |= 0x80
270-
}
271-
buf[i] = byte(b)
272-
i++
273-
if num == 0 {
274-
break
275-
}
266+
if num&0xFFFFFF80 == 0 {
267+
buf[0] = byte(num)
268+
return 1
269+
} else if num&0xFFFFC000 == 0 {
270+
result := uint16((num&0x7F|0x80)<<8 | (num >> 7))
271+
binary.BigEndian.PutUint16(buf, result)
272+
return 2
273+
} else if num&0xFFE00000 == 0 {
274+
buf[2] = byte(num >> 14)
275+
startingBytes := uint16((num&0x7F|0x80)<<8 | ((num>>7)&0x7F | 0x80))
276+
binary.BigEndian.PutUint16(buf, startingBytes)
277+
return 3
278+
} else if num&0xF0000000 == 0 {
279+
result := (num&0x7F|0x80)<<24 | (((num>>7)&0x7F | 0x80) << 16) |
280+
((num>>14)&0x7F|0x80)<<8 | (num >> 21)
281+
binary.BigEndian.PutUint32(buf, result)
282+
return 4
283+
} else {
284+
buf[4] = byte(num >> 28)
285+
startingBytes := (num&0x7F|0x80)<<24 | ((num>>7)&0x7F|0x80)<<16 |
286+
((num>>14)&0x7F|0x80)<<8 | ((num>>21)&0x7F | 0x80)
287+
binary.BigEndian.PutUint32(buf, startingBytes)
288+
return 5
276289
}
277-
return i
278290
}
279291

280292
func (v *VarInt) ReadFrom(r io.Reader) (n int64, err error) {
281293
var V uint32
282-
var num, n2 int64
294+
var num int64
295+
byteReader := CreateByteReader(r)
283296
for sec := byte(0x80); sec&0x80 != 0; num++ {
284297
if num > MaxVarIntLen {
285298
return n, errors.New("VarInt is too big")
286299
}
287300

288-
n2, sec, err = readByte(r)
289-
n += n2
301+
sec, err = byteReader.ReadByte()
290302
if err != nil {
291303
return n, err
292304
}
305+
n += 1
293306

294307
V |= uint32(sec&0x7F) << uint32(7*num)
295308
}
@@ -326,35 +339,32 @@ func (v VarLong) WriteTo(w io.Writer) (n int64, err error) {
326339
// WriteToBytes encodes the VarLong into buf and returns the number of bytes written.
327340
// If the buffer is too small, WriteToBytes will panic.
328341
func (v VarLong) WriteToBytes(buf []byte) int {
342+
// Like VarInt, but we don't unroll the loop because it might be too long.
329343
num := uint64(v)
330-
i := 0
331-
for {
332-
b := num & 0x7F
344+
n := v.Len()
345+
continuationBytes := n - 1
346+
_ = buf[continuationBytes] // bounds check hint to compiler; see golang.org/issue/14808
347+
for i := 0; i < continuationBytes; i++ {
348+
buf[i] = byte(num&0x7F | 0x80)
333349
num >>= 7
334-
if num != 0 {
335-
b |= 0x80
336-
}
337-
buf[i] = byte(b)
338-
i++
339-
if num == 0 {
340-
break
341-
}
342350
}
343-
return i
351+
buf[continuationBytes] = byte(num)
352+
return n
344353
}
345354

346355
func (v *VarLong) ReadFrom(r io.Reader) (n int64, err error) {
347356
var V uint64
348-
var num, n2 int64
357+
var num int64
358+
byteReader := CreateByteReader(r)
349359
for sec := byte(0x80); sec&0x80 != 0; num++ {
350360
if num >= MaxVarLongLen {
351361
return n, errors.New("VarLong is too big")
352362
}
353-
n2, sec, err = readByte(r)
354-
n += n2
363+
sec, err = byteReader.ReadByte()
355364
if err != nil {
356365
return
357366
}
367+
n += 1
358368

359369
V |= uint64(sec&0x7F) << uint64(7*num)
360370
}

net/packet/types_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
pk "github.com/Tnze/go-mc/net/packet"
1111
)
1212

13-
var VarInts = []pk.VarInt{0, 1, 2, 127, 128, 255, 2147483647, -1, -2147483648}
13+
var VarInts = []pk.VarInt{0, 1, 2, 127, 128, 255, 25565, 2097151, 2147483647, -1, -2147483648}
1414

1515
var PackedVarInts = [][]byte{
1616
{0x00},
@@ -19,6 +19,8 @@ var PackedVarInts = [][]byte{
1919
{0x7f},
2020
{0x80, 0x01},
2121
{0xff, 0x01},
22+
{0xdd, 0xc7, 0x01},
23+
{0xff, 0xff, 0x7f},
2224
{0xff, 0xff, 0xff, 0xff, 0x07},
2325
{0xff, 0xff, 0xff, 0xff, 0x0f},
2426
{0x80, 0x80, 0x80, 0x80, 0x08},

net/packet/util.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,22 @@ func (t Tuple) ReadFrom(r io.Reader) (n int64, err error) {
273273
}
274274
return
275275
}
276+
277+
func CreateByteReader(reader io.Reader) io.ByteReader {
278+
if byteReader, isByteReader := reader.(io.ByteReader); isByteReader {
279+
return byteReader
280+
}
281+
return byteReaderWrapper{reader}
282+
}
283+
284+
type byteReaderWrapper struct {
285+
io.Reader
286+
}
287+
288+
var _ io.ByteReader = byteReaderWrapper{}
289+
290+
func (r byteReaderWrapper) ReadByte() (byte, error) {
291+
var buf [1]byte
292+
_, err := io.ReadFull(r.Reader, buf[:])
293+
return buf[0], err
294+
}

0 commit comments

Comments
 (0)