Skip to content

Commit ca2d530

Browse files
committed
feat(binary): 使用更安全省心的 pbuf 缓存池
1 parent bfd4a34 commit ca2d530

File tree

6 files changed

+96
-87
lines changed

6 files changed

+96
-87
lines changed

client/internal/oicq/oicq.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,8 @@ func (c *Codec) Marshal(m *Message) []byte {
8787
w.WriteU8(0x03)
8888

8989
data := w.ToBytes()
90-
buf := make([]byte, len(data))
91-
copy(buf, data)
92-
goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf)))
93-
return buf
90+
goBinary.BigEndian.PutUint16(data[1:3], uint16(len(data)))
91+
return data
9492
}
9593

9694
var (
@@ -144,7 +142,7 @@ func (t *TLV) Marshal() []byte {
144142
w.WriteBytes(elem)
145143
}
146144

147-
return append([]byte(nil), w.ToBytes()...)
145+
return w.ToBytes()
148146
}
149147

150148
func (t *TLV) Append(b ...[]byte) {

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ go 1.20
55
require (
66
github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d
77
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
8-
github.com/fumiama/gofastTEA v0.1.0
8+
github.com/fumiama/gofastTEA v0.1.3
99
github.com/fumiama/imgsz v0.0.4
10+
github.com/fumiama/orbyte v0.0.0-20250226073501-8c0982b8857b
1011
github.com/mattn/go-colorable v0.1.13
1112
github.com/pkg/errors v0.9.1
1213
github.com/sirupsen/logrus v1.9.3

go.sum

+4-6
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9o
55
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
66
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8-
github.com/fumiama/gofastTEA v0.1.0 h1:eW8yCq2BgqijIhgWcdjNuTs9RIOst8M81BKYvqHg0/g=
9-
github.com/fumiama/gofastTEA v0.1.0/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
8+
github.com/fumiama/gofastTEA v0.1.3 h1:fxOi2D66knV6QN170hb59YiqxPhjlgizvBw+o0OjxUA=
9+
github.com/fumiama/gofastTEA v0.1.3/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
1010
github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI=
1111
github.com/fumiama/imgsz v0.0.4/go.mod h1:bISOQVTlw9sRytPwe8ir7tAaEmyz9hSNj9n8mXMBG0E=
12+
github.com/fumiama/orbyte v0.0.0-20250226073501-8c0982b8857b h1:iu71yD00/ha3UZv2lYwuRL7eqHKzi29UO8sjH7regBk=
13+
github.com/fumiama/orbyte v0.0.0-20250226073501-8c0982b8857b/go.mod h1:qkUllQ1+gTx5sGrmKvIsqUgsnOO21Hiq847YHJRifbk=
1214
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
1315
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
1416
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
@@ -30,16 +32,12 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
3032
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
3133
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
3234
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
33-
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
34-
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
3535
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
3636
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
3737
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
3838
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
3939
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4040
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
41-
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
42-
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
4341
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
4442
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
4543
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

utils/binary/builder.go

+51-72
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package binary
22

33
import (
4-
"bytes"
54
"encoding/binary"
65
"io"
76
"math"
@@ -10,93 +9,63 @@ import (
109
"unsafe"
1110

1211
ftea "github.com/fumiama/gofastTEA"
12+
"github.com/fumiama/orbyte"
13+
"github.com/fumiama/orbyte/pbuf"
1314

1415
"github.com/LagrangeDev/LagrangeGo/utils"
1516
)
1617

17-
type Builder struct {
18-
buffer bytes.Buffer
18+
type teacfg struct {
1919
key ftea.TEA
2020
usetea bool
21-
hasput bool
22-
io.Writer
23-
io.ReaderFrom
2421
}
2522

26-
// NewWriterF from https://github.com/Mrs4s/MiraiGo/blob/master/binary/writer.go
27-
func NewWriterF(f func(writer *Builder)) []byte {
28-
w := NewBuilder()
29-
f(w)
30-
b := make([]byte, w.Len())
31-
copy(b, w.ToBytes())
32-
return b
33-
}
34-
35-
// ToBytes from https://github.com/Mrs4s/MiraiGo/blob/master/binary/writer.go
36-
func ToBytes(i any) []byte {
37-
return NewWriterF(func(w *Builder) {
38-
// TODO: more types
39-
switch t := i.(type) {
40-
case int16:
41-
w.WriteU16(uint16(t))
42-
case int32:
43-
w.WriteU32(uint32(t))
44-
}
45-
})
46-
}
47-
48-
func (b *Builder) init(key []byte) *Builder {
49-
b.key = ftea.NewTeaCipher(key)
50-
b.usetea = len(key) == 16
51-
b.hasput = false
52-
return b
53-
}
54-
55-
func (b *Builder) Len() int {
56-
return b.buffer.Len()
23+
func (tc *teacfg) init(key []byte) {
24+
tc.key = ftea.NewTeaCipher(key)
25+
tc.usetea = len(key) == 16
5726
}
5827

59-
// ToReader GC 不安全, 确保在 Builder 被回收前使用
60-
func (b *Builder) ToReader() io.Reader {
61-
return &b.buffer
62-
}
63-
64-
// Buffer GC 不安全, 确保在 Builder 被回收前使用
65-
func (b *Builder) Buffer() *bytes.Buffer {
66-
return &b.buffer
28+
func (b *Builder) p(f func(*pbuf.UserBuffer[teacfg])) {
29+
(*orbyte.Item[pbuf.UserBuffer[teacfg]])(b).P(f)
6730
}
6831

6932
// ToBytes return data with tea encryption if key is set
7033
//
7134
// GC 安全, 返回的数据在 Builder 被销毁之后仍能被正确读取,
7235
// 但是只能调用一次, 调用后 Builder 即失效
73-
func (b *Builder) ToBytes() []byte {
74-
if b.usetea {
75-
return b.key.Encrypt(b.buffer.Bytes())
76-
}
77-
buf := make([]byte, b.Len())
78-
copy(buf, b.buffer.Bytes())
79-
return buf
36+
func (b *Builder) ToBytes() (data []byte) {
37+
b.p(func(ub *pbuf.UserBuffer[teacfg]) {
38+
if ub.DAT.usetea {
39+
data = ub.DAT.key.Encrypt(ub.Bytes())
40+
return
41+
}
42+
data = make([]byte, ub.Len())
43+
copy(data, ub.Bytes())
44+
})
45+
return
8046
}
8147

8248
// Pack TLV with tea encryption if key is set
8349
//
8450
// GC 安全, 返回的数据在 Builder 被销毁之后仍能被正确读取,
85-
// 但是只能调用一次, 调用后 Builder 即失效
86-
func (b *Builder) Pack(typ uint16) []byte {
87-
buf := make([]byte, b.Len()+2+2+16)
88-
89-
n := 0
90-
if b.usetea {
91-
n = b.key.EncryptTo(b.buffer.Bytes(), buf[2+2:])
92-
} else {
93-
n = copy(buf[2+2:], b.buffer.Bytes())
94-
}
51+
// 调用后 Builder 仍有效
52+
func (b *Builder) Pack(typ uint16) (data []byte) {
53+
b.p(func(buf *pbuf.UserBuffer[teacfg]) {
54+
data = make([]byte, buf.Len()+2+2+16)
55+
56+
n := 0
57+
if buf.DAT.usetea {
58+
n = buf.DAT.key.EncryptTo(buf.Bytes(), data[2+2:])
59+
} else {
60+
n = copy(data[2+2:], buf.Bytes())
61+
}
9562

96-
binary.BigEndian.PutUint16(buf[0:2], typ) // type
97-
binary.BigEndian.PutUint16(buf[2:2+2], uint16(n)) // length
63+
binary.BigEndian.PutUint16(data[0:2], typ) // type
64+
binary.BigEndian.PutUint16(data[2:2+2], uint16(n)) // length
9865

99-
return buf[:n+2+2]
66+
data = data[:n+2+2]
67+
})
68+
return
10069
}
10170

10271
func (b *Builder) WriteBool(v bool) *Builder {
@@ -140,7 +109,10 @@ func (b *Builder) WritePacketString(s, prefix string, withPrefix bool) *Builder
140109

141110
// Write for impl. io.Writer
142111
func (b *Builder) Write(p []byte) (n int, err error) {
143-
return b.buffer.Write(p)
112+
b.p(func(ub *pbuf.UserBuffer[teacfg]) {
113+
n, err = ub.Write(p)
114+
})
115+
return
144116
}
145117

146118
func (b *Builder) EncryptAndWrite(key []byte, data []byte) *Builder {
@@ -150,7 +122,10 @@ func (b *Builder) EncryptAndWrite(key []byte, data []byte) *Builder {
150122

151123
// ReadFrom for impl. io.ReaderFrom
152124
func (b *Builder) ReadFrom(r io.Reader) (n int64, err error) {
153-
return io.Copy(&b.buffer, r)
125+
b.p(func(ub *pbuf.UserBuffer[teacfg]) {
126+
n, err = io.Copy(ub, r)
127+
})
128+
return
154129
}
155130

156131
func (b *Builder) WriteLenBytes(v []byte) *Builder {
@@ -169,21 +144,25 @@ func (b *Builder) WriteLenString(v string) *Builder {
169144
}
170145

171146
func (b *Builder) WriteStruct(data ...any) *Builder {
172-
for _, data := range data {
173-
_ = binary.Write(&b.buffer, binary.BigEndian, data)
174-
}
147+
b.p(func(ub *pbuf.UserBuffer[teacfg]) {
148+
for _, data := range data {
149+
_ = binary.Write(ub, binary.BigEndian, data)
150+
}
151+
})
175152
return b
176153
}
177154

178155
func (b *Builder) WriteU8(v uint8) *Builder {
179-
b.buffer.WriteByte(v)
156+
b.p(func(ub *pbuf.UserBuffer[teacfg]) {
157+
ub.WriteByte(v)
158+
})
180159
return b
181160
}
182161

183162
func writeint[T ~uint16 | ~uint32 | ~uint64](b *Builder, v T) *Builder {
184163
buf := make([]byte, 8)
185164
binary.BigEndian.PutUint64(buf, uint64(v))
186-
b.buffer.Write(buf[8-unsafe.Sizeof(v):])
165+
b.Write(buf[8-unsafe.Sizeof(v):])
187166
return b
188167
}
189168

utils/binary/builder_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"sync"
1111
"testing"
1212
"time"
13+
14+
"github.com/fumiama/orbyte/pbuf"
1315
)
1416

1517
func TestBuilder(t *testing.T) {
@@ -52,6 +54,21 @@ func TestBuilder(t *testing.T) {
5254
wg.Wait()
5355
}
5456

57+
func TestBuilderTEA(t *testing.T) {
58+
k := make([]byte, 16)
59+
_, err := rand.Read(k)
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
b := NewBuilder(k...)
64+
b.p(func(ub *pbuf.UserBuffer[teacfg]) {
65+
dat := ub.DAT.key.ToBytes()
66+
if !bytes.Equal(dat[:], k) {
67+
t.Fatal("exp", hex.EncodeToString(k), "got", hex.EncodeToString(dat[:]))
68+
}
69+
})
70+
}
71+
5572
// from https://github.com/Mrs4s/MiraiGo/blob/master/binary/writer_test.go
5673

5774
func BenchmarkNewBuilder128(b *testing.B) {

utils/binary/pool.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,29 @@ import (
77
"compress/gzip"
88
"compress/zlib"
99
"sync"
10+
"unsafe"
11+
12+
"github.com/fumiama/orbyte"
13+
"github.com/fumiama/orbyte/pbuf"
1014
)
1115

16+
var bufferPool = pbuf.NewBufferPool[teacfg]()
17+
18+
type Builder orbyte.Item[pbuf.UserBuffer[teacfg]]
19+
20+
func init() {
21+
x := *(**orbyte.Pool[pbuf.UserBuffer[teacfg]])(unsafe.Pointer(&bufferPool))
22+
x.SetManualDestroy(true)
23+
x.SetNoPutBack(true)
24+
}
25+
1226
// NewBuilder 从池中取出一个 Builder
1327
func NewBuilder(key ...byte) *Builder {
14-
b := new(Builder)
15-
b.init(key)
16-
return b
28+
b := bufferPool.NewBuffer(nil)
29+
b.P(func(ub *pbuf.UserBuffer[teacfg]) {
30+
ub.DAT.init(key)
31+
})
32+
return (*Builder)(b)
1733
}
1834

1935
var gzipPool = sync.Pool{

0 commit comments

Comments
 (0)