Skip to content

Commit 986b04c

Browse files
committed
encoding/binary: add AppendByteOrder
AppendByteOrder specifies new methods for LittleEndian and BigEndian for appending an unsigned integer to a byte slice. The performance of AppendXXX methods are slower than PutXXX methods since the former needs to do a few slice operations, while the latter is essentially a single integer store. In practice, existing usages of PutXXX performed slicing operations around the call such that this cost was present, regardless. name time/op PutUint16-24 0.48ns ± 2% AppendUint16-24 1.54ns ± 1% PutUint32-24 0.46ns ± 2% AppendUint32-24 0.89ns ± 1% PutUint64-24 0.46ns ± 2% AppendUint64-24 0.89ns ± 1% LittleEndianPutUint16-24 0.47ns ± 2% LittleEndianAppendUint16-24 1.54ns ± 1% LittleEndianPutUint32-24 0.45ns ± 3% LittleEndianAppendUint32-24 0.92ns ± 2% LittleEndianPutUint64-24 0.46ns ± 3% LittleEndianAppendUint64-24 0.95ns ± 4% Fixes #50601 Change-Id: I33d2bbc93a3ce01a9269feac33a2432bc1166ead Reviewed-on: https://go-review.googlesource.com/c/go/+/386017 Trust: Joseph Tsai <[email protected]> Trust: Josh Bleecher Snyder <[email protected]> Reviewed-by: Josh Bleecher Snyder <[email protected]> Run-TryBot: Joseph Tsai <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent a5b8b56 commit 986b04c

File tree

2 files changed

+178
-9
lines changed

2 files changed

+178
-9
lines changed

src/encoding/binary/binary.go

+70-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
"sync"
3030
)
3131

32-
// A ByteOrder specifies how to convert byte sequences into
32+
// A ByteOrder specifies how to convert byte slices into
3333
// 16-, 32-, or 64-bit unsigned integers.
3434
type ByteOrder interface {
3535
Uint16([]byte) uint16
@@ -41,10 +41,19 @@ type ByteOrder interface {
4141
String() string
4242
}
4343

44-
// LittleEndian is the little-endian implementation of ByteOrder.
44+
// AppendByteOrder specifies how to append 16-, 32-, or 64-bit unsigned integers
45+
// into a byte slice.
46+
type AppendByteOrder interface {
47+
AppendUint16([]byte, uint16) []byte
48+
AppendUint32([]byte, uint32) []byte
49+
AppendUint64([]byte, uint64) []byte
50+
String() string
51+
}
52+
53+
// LittleEndian is the little-endian implementation of ByteOrder and AppendByteOrder.
4554
var LittleEndian littleEndian
4655

47-
// BigEndian is the big-endian implementation of ByteOrder.
56+
// BigEndian is the big-endian implementation of ByteOrder and AppendByteOrder.
4857
var BigEndian bigEndian
4958

5059
type littleEndian struct{}
@@ -60,6 +69,13 @@ func (littleEndian) PutUint16(b []byte, v uint16) {
6069
b[1] = byte(v >> 8)
6170
}
6271

72+
func (littleEndian) AppendUint16(b []byte, v uint16) []byte {
73+
return append(b,
74+
byte(v),
75+
byte(v>>8),
76+
)
77+
}
78+
6379
func (littleEndian) Uint32(b []byte) uint32 {
6480
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
6581
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
@@ -73,6 +89,15 @@ func (littleEndian) PutUint32(b []byte, v uint32) {
7389
b[3] = byte(v >> 24)
7490
}
7591

92+
func (littleEndian) AppendUint32(b []byte, v uint32) []byte {
93+
return append(b,
94+
byte(v),
95+
byte(v>>8),
96+
byte(v>>16),
97+
byte(v>>24),
98+
)
99+
}
100+
76101
func (littleEndian) Uint64(b []byte) uint64 {
77102
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
78103
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
@@ -91,6 +116,19 @@ func (littleEndian) PutUint64(b []byte, v uint64) {
91116
b[7] = byte(v >> 56)
92117
}
93118

119+
func (littleEndian) AppendUint64(b []byte, v uint64) []byte {
120+
return append(b,
121+
byte(v),
122+
byte(v>>8),
123+
byte(v>>16),
124+
byte(v>>24),
125+
byte(v>>32),
126+
byte(v>>40),
127+
byte(v>>48),
128+
byte(v>>56),
129+
)
130+
}
131+
94132
func (littleEndian) String() string { return "LittleEndian" }
95133

96134
func (littleEndian) GoString() string { return "binary.LittleEndian" }
@@ -108,6 +146,13 @@ func (bigEndian) PutUint16(b []byte, v uint16) {
108146
b[1] = byte(v)
109147
}
110148

149+
func (bigEndian) AppendUint16(b []byte, v uint16) []byte {
150+
return append(b,
151+
byte(v>>8),
152+
byte(v),
153+
)
154+
}
155+
111156
func (bigEndian) Uint32(b []byte) uint32 {
112157
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
113158
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
@@ -121,6 +166,15 @@ func (bigEndian) PutUint32(b []byte, v uint32) {
121166
b[3] = byte(v)
122167
}
123168

169+
func (bigEndian) AppendUint32(b []byte, v uint32) []byte {
170+
return append(b,
171+
byte(v>>24),
172+
byte(v>>16),
173+
byte(v>>8),
174+
byte(v),
175+
)
176+
}
177+
124178
func (bigEndian) Uint64(b []byte) uint64 {
125179
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
126180
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
@@ -139,6 +193,19 @@ func (bigEndian) PutUint64(b []byte, v uint64) {
139193
b[7] = byte(v)
140194
}
141195

196+
func (bigEndian) AppendUint64(b []byte, v uint64) []byte {
197+
return append(b,
198+
byte(v>>56),
199+
byte(v>>48),
200+
byte(v>>40),
201+
byte(v>>32),
202+
byte(v>>24),
203+
byte(v>>16),
204+
byte(v>>8),
205+
byte(v),
206+
)
207+
}
208+
142209
func (bigEndian) String() string { return "BigEndian" }
143210

144211
func (bigEndian) GoString() string { return "binary.BigEndian" }

src/encoding/binary/binary_test.go

+108-6
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,65 @@ func testPutUint64SmallSliceLengthPanics() (panicked bool) {
442442
return false
443443
}
444444

445+
func TestByteOrder(t *testing.T) {
446+
type byteOrder interface {
447+
ByteOrder
448+
AppendByteOrder
449+
}
450+
buf := make([]byte, 8)
451+
for _, order := range []byteOrder{LittleEndian, BigEndian} {
452+
const offset = 3
453+
for _, value := range []uint64{
454+
0x0000000000000000,
455+
0x0123456789abcdef,
456+
0xfedcba9876543210,
457+
0xffffffffffffffff,
458+
0xaaaaaaaaaaaaaaaa,
459+
math.Float64bits(math.Pi),
460+
math.Float64bits(math.E),
461+
} {
462+
want16 := uint16(value)
463+
order.PutUint16(buf[:2], want16)
464+
if got := order.Uint16(buf[:2]); got != want16 {
465+
t.Errorf("PutUint16: Uint16 = %v, want %v", got, want16)
466+
}
467+
buf = order.AppendUint16(buf[:offset], want16)
468+
if got := order.Uint16(buf[offset:]); got != want16 {
469+
t.Errorf("AppendUint16: Uint16 = %v, want %v", got, want16)
470+
}
471+
if len(buf) != offset+2 {
472+
t.Errorf("AppendUint16: len(buf) = %d, want %d", len(buf), offset+2)
473+
}
474+
475+
want32 := uint32(value)
476+
order.PutUint32(buf[:4], want32)
477+
if got := order.Uint32(buf[:4]); got != want32 {
478+
t.Errorf("PutUint32: Uint32 = %v, want %v", got, want32)
479+
}
480+
buf = order.AppendUint32(buf[:offset], want32)
481+
if got := order.Uint32(buf[offset:]); got != want32 {
482+
t.Errorf("AppendUint32: Uint32 = %v, want %v", got, want32)
483+
}
484+
if len(buf) != offset+4 {
485+
t.Errorf("AppendUint32: len(buf) = %d, want %d", len(buf), offset+4)
486+
}
487+
488+
want64 := uint64(value)
489+
order.PutUint64(buf[:8], want64)
490+
if got := order.Uint64(buf[:8]); got != want64 {
491+
t.Errorf("PutUint64: Uint64 = %v, want %v", got, want64)
492+
}
493+
buf = order.AppendUint64(buf[:offset], want64)
494+
if got := order.Uint64(buf[offset:]); got != want64 {
495+
t.Errorf("AppendUint64: Uint64 = %v, want %v", got, want64)
496+
}
497+
if len(buf) != offset+8 {
498+
t.Errorf("AppendUint64: len(buf) = %d, want %d", len(buf), offset+8)
499+
}
500+
}
501+
}
502+
}
503+
445504
func TestEarlyBoundsChecks(t *testing.T) {
446505
if testUint64SmallSliceLengthPanics() != true {
447506
t.Errorf("binary.LittleEndian.Uint64 expected to panic for small slices, but didn't")
@@ -596,41 +655,84 @@ func BenchmarkWriteSlice1000Int32s(b *testing.B) {
596655
func BenchmarkPutUint16(b *testing.B) {
597656
b.SetBytes(2)
598657
for i := 0; i < b.N; i++ {
599-
BigEndian.PutUint16(putbuf[:], uint16(i))
658+
BigEndian.PutUint16(putbuf[:2], uint16(i))
659+
}
660+
}
661+
662+
func BenchmarkAppendUint16(b *testing.B) {
663+
b.SetBytes(2)
664+
for i := 0; i < b.N; i++ {
665+
putbuf = BigEndian.AppendUint16(putbuf[:0], uint16(i))
600666
}
601667
}
602668

603669
func BenchmarkPutUint32(b *testing.B) {
604670
b.SetBytes(4)
605671
for i := 0; i < b.N; i++ {
606-
BigEndian.PutUint32(putbuf[:], uint32(i))
672+
BigEndian.PutUint32(putbuf[:4], uint32(i))
673+
}
674+
}
675+
676+
func BenchmarkAppendUint32(b *testing.B) {
677+
b.SetBytes(4)
678+
for i := 0; i < b.N; i++ {
679+
putbuf = BigEndian.AppendUint32(putbuf[:0], uint32(i))
607680
}
608681
}
609682

610683
func BenchmarkPutUint64(b *testing.B) {
611684
b.SetBytes(8)
612685
for i := 0; i < b.N; i++ {
613-
BigEndian.PutUint64(putbuf[:], uint64(i))
686+
BigEndian.PutUint64(putbuf[:8], uint64(i))
687+
}
688+
}
689+
690+
func BenchmarkAppendUint64(b *testing.B) {
691+
b.SetBytes(8)
692+
for i := 0; i < b.N; i++ {
693+
putbuf = BigEndian.AppendUint64(putbuf[:0], uint64(i))
614694
}
615695
}
696+
616697
func BenchmarkLittleEndianPutUint16(b *testing.B) {
617698
b.SetBytes(2)
618699
for i := 0; i < b.N; i++ {
619-
LittleEndian.PutUint16(putbuf[:], uint16(i))
700+
LittleEndian.PutUint16(putbuf[:2], uint16(i))
701+
}
702+
}
703+
704+
func BenchmarkLittleEndianAppendUint16(b *testing.B) {
705+
b.SetBytes(2)
706+
for i := 0; i < b.N; i++ {
707+
putbuf = LittleEndian.AppendUint16(putbuf[:0], uint16(i))
620708
}
621709
}
622710

623711
func BenchmarkLittleEndianPutUint32(b *testing.B) {
624712
b.SetBytes(4)
625713
for i := 0; i < b.N; i++ {
626-
LittleEndian.PutUint32(putbuf[:], uint32(i))
714+
LittleEndian.PutUint32(putbuf[:4], uint32(i))
715+
}
716+
}
717+
718+
func BenchmarkLittleEndianAppendUint32(b *testing.B) {
719+
b.SetBytes(4)
720+
for i := 0; i < b.N; i++ {
721+
putbuf = LittleEndian.AppendUint32(putbuf[:0], uint32(i))
627722
}
628723
}
629724

630725
func BenchmarkLittleEndianPutUint64(b *testing.B) {
631726
b.SetBytes(8)
632727
for i := 0; i < b.N; i++ {
633-
LittleEndian.PutUint64(putbuf[:], uint64(i))
728+
LittleEndian.PutUint64(putbuf[:8], uint64(i))
729+
}
730+
}
731+
732+
func BenchmarkLittleEndianAppendUint64(b *testing.B) {
733+
b.SetBytes(8)
734+
for i := 0; i < b.N; i++ {
735+
putbuf = LittleEndian.AppendUint64(putbuf[:0], uint64(i))
634736
}
635737
}
636738

0 commit comments

Comments
 (0)