trpc.group/trpc-go/trpc-go@v1.0.3/internal/linkbuffer/buffer_test.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package linkbuffer_test 15 16 import ( 17 stdbytes "bytes" 18 "io" 19 "math/rand" 20 "testing" 21 22 "github.com/stretchr/testify/require" 23 "trpc.group/trpc-go/trpc-go/internal/allocator" 24 . "trpc.group/trpc-go/trpc-go/internal/linkbuffer" 25 ) 26 27 func BenchmarkBuf(b *testing.B) { 28 bigBts := make([]byte, 1<<10) 29 b.Run("link_buffer_bigBytes", func(b *testing.B) { 30 b.ReportAllocs() 31 bb := NewBuf(allocator.NewClassAllocator(), 1<<9) 32 for i := 0; i < b.N; i++ { 33 bb.Append(bigBts) 34 bb.ReadNext() 35 bb.Release() 36 } 37 }) 38 b.Run("copy_each_bigBytes", func(b *testing.B) { 39 b.ReportAllocs() 40 var bb []byte 41 for i := 0; i < b.N; i++ { 42 bb = append(bb, bigBts...) 43 } 44 }) 45 b.Run("link_buffer_reuse", func(b *testing.B) { 46 b.ReportAllocs() 47 r := rand.New(rand.NewSource(1)) 48 bb := NewBuf(allocator.NewClassAllocator(), 1<<10) 49 for i := 0; i < b.N; i++ { 50 copy(bb.Alloc(16), bigBts) 51 copy(bb.Alloc(int(r.Int31()%1<<20+1)), bigBts) 52 bb.ReadNext() 53 bb.ReadNext() 54 bb.Release() 55 } 56 }) 57 b.Run("std_buffer", func(b *testing.B) { 58 b.ReportAllocs() 59 r := rand.New(rand.NewSource(1)) 60 for i := 0; i < b.N; i++ { 61 bb := stdbytes.Buffer{} 62 bb.Write(bigBts[:16]) 63 bb.Write(bigBts[:r.Int31()%1<<20+1]) 64 } 65 }) 66 b.Run("bytes_cannot_reuse", func(b *testing.B) { 67 b.ReportAllocs() 68 r := rand.New(rand.NewSource(1)) 69 for i := 0; i < b.N; i++ { 70 bts := make([]byte, r.Int31()%1<<20+16) 71 copy(bts[:16], bigBts) 72 copy(bts[16:], bigBts) 73 } 74 }) 75 } 76 77 func TestBuf_Write(t *testing.T) { 78 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 79 b := NewBuf(wa, 4) 80 81 n, err := b.Write([]byte("123")) 82 require.Nil(t, err) 83 require.Equal(t, 3, n) 84 85 n, err = b.Write([]byte("45")) 86 require.Nil(t, err) 87 require.Equal(t, 2, n) 88 89 wa.MustMallocTimes(2) 90 } 91 92 func TestBuf_Read(t *testing.T) { 93 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 94 b := NewBuf(wa, 4) 95 96 n, err := b.Write([]byte("1234567890")) 97 require.Nil(t, err) 98 require.Equal(t, 10, n) 99 100 bts := make([]byte, 3) 101 n, err = b.Read(bts) 102 require.Nil(t, err) 103 require.Equal(t, 3, n) 104 require.Equal(t, "123", string(bts)) 105 106 _, err = b.Read(bts) 107 require.Nil(t, err) 108 _, err = b.Read(bts) 109 require.Nil(t, err) 110 111 n, err = b.Read(bts) 112 require.Nil(t, err) 113 require.Equal(t, 1, n) 114 require.Equal(t, "089", string(bts)) 115 116 n, err = b.Read(bts) 117 require.ErrorIs(t, err, io.EOF) 118 require.Equal(t, 0, n) 119 120 b.Release() 121 wa.MustMallocTimes(3) 122 wa.MustAllFreed() 123 } 124 125 func TestBuf_Append(t *testing.T) { 126 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 127 b := NewBuf(wa, 4) 128 129 n, err := b.Write([]byte("12")) 130 require.Nil(t, err) 131 require.Equal(t, 2, n) 132 133 b.Append([]byte("345")) 134 135 // the remaining two bytes is available, no need to malloc new bytes. 136 n, err = b.Write([]byte("67")) 137 require.Nil(t, err) 138 require.Equal(t, 2, n) 139 140 bts := make([]byte, 7) 141 n, err = b.Read(bts) 142 require.Nil(t, err) 143 require.Equal(t, 7, n) 144 require.Equal(t, "1234567", string(bts)) 145 146 b.Release() 147 wa.MustMallocTimes(1) 148 wa.MustAllFreed() 149 } 150 151 func TestBuf_Prepend(t *testing.T) { 152 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 153 b := NewBuf(wa, 4) 154 155 n, err := b.Write([]byte("12")) 156 require.Nil(t, err) 157 require.Equal(t, 2, n) 158 159 b.Prepend([]byte("345")) 160 161 bts := make([]byte, 5) 162 n, err = b.Read(bts) 163 require.Nil(t, err) 164 require.Equal(t, 5, n) 165 require.Equal(t, "34512", string(bts)) 166 167 b.Release() 168 wa.MustMallocTimes(1) 169 wa.MustAllFreed() 170 } 171 172 func TestBuf_Alloc(t *testing.T) { 173 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 174 b := NewBuf(wa, 4) 175 176 n, err := b.Write([]byte("12")) 177 require.Nil(t, err) 178 require.Equal(t, 2, n) 179 180 bts := b.Alloc(1) 181 require.Len(t, bts, 1) 182 n, err = b.Write([]byte("456")) 183 require.Nil(t, err) 184 require.Equal(t, 3, n) 185 bts[0] = '3' 186 187 bts = b.Alloc(3) 188 require.Len(t, bts, 3) 189 copy(bts, "789") 190 191 bts = make([]byte, 9) 192 n, err = b.Read(bts) 193 require.Nil(t, err) 194 require.Equal(t, 9, n) 195 require.Equal(t, "123456789", string(bts)) 196 197 b.Release() 198 wa.MustMallocTimes(3) 199 wa.MustAllFreed() 200 } 201 202 func TestBuf_Prelloc(t *testing.T) { 203 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 204 b := NewBuf(wa, 4) 205 206 n, err := b.Write([]byte("12")) 207 require.Nil(t, err) 208 require.Equal(t, 2, n) 209 210 bts := b.Prelloc(1) 211 require.Len(t, bts, 1) 212 b.Prepend([]byte("456")) 213 bts[0] = '3' 214 215 bts = b.Prelloc(3) 216 require.Len(t, bts, 3) 217 copy(bts, "789") 218 219 bts = make([]byte, 9) 220 n, err = b.Read(bts) 221 require.Nil(t, err) 222 require.Equal(t, 9, n) 223 require.Equal(t, "789456312", string(bts)) 224 225 b.Release() 226 wa.MustMallocTimes(3) 227 wa.MustAllFreed() 228 } 229 230 func TestBuf_Merge(t *testing.T) { 231 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 232 233 b1 := NewBuf(wa, 4) 234 b1.Append([]byte("123")) 235 n, err := b1.Write([]byte("456")) 236 require.Nil(t, err) 237 require.Equal(t, 3, n) 238 239 b2 := NewBuf(wa, 2) 240 n, err = b2.Write([]byte("567")) 241 require.Nil(t, err) 242 require.Equal(t, 3, n) 243 b2.Append([]byte("89")) 244 245 bts := make([]byte, 2) 246 n, err = b2.Read(bts) 247 require.Nil(t, err) 248 require.Equal(t, 2, n) 249 require.Equal(t, "56", string(bts)) 250 b2.Release() 251 252 b1.Merge(b2) 253 bts = make([]byte, 9) 254 n, err = b1.Read(bts) 255 require.Nil(t, err) 256 require.Equal(t, 9, n) 257 require.Equal(t, "123456789", string(bts)) 258 259 b1.Release() 260 wa.MustMallocTimes(1 + 2) 261 wa.MustAllFreed() 262 } 263 264 func TestBuf_ReadN(t *testing.T) { 265 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 266 b := NewBuf(wa, 4) 267 268 n, err := b.Write([]byte("123")) 269 require.Nil(t, err) 270 require.Equal(t, 3, n) 271 b.Append([]byte("456")) 272 273 bts, n := b.ReadN(1) 274 require.Equal(t, 1, n) 275 require.Equal(t, "1", string(bts)) 276 277 bts, n = b.ReadN(3) 278 require.Equal(t, 2, n) 279 require.Equal(t, "23", string(bts)) 280 281 bts, n = b.ReadN(3) 282 require.Equal(t, 3, n) 283 require.Equal(t, "456", string(bts)) 284 285 bts, n = b.ReadN(3) 286 require.Equal(t, 0, n) 287 require.Nil(t, bts) 288 289 b.Release() 290 wa.MustMallocTimes(1) 291 wa.MustAllFreed() 292 } 293 294 func TestBuf_ReadAll(t *testing.T) { 295 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 296 b := NewBuf(wa, 4) 297 298 n, err := b.Write([]byte("123")) 299 require.Nil(t, err) 300 require.Equal(t, 3, n) 301 b.Append([]byte("45")) 302 n, err = b.Write([]byte("67890")) 303 require.Nil(t, err) 304 require.Equal(t, 5, n) 305 306 bs := b.ReadAll() 307 require.Len(t, bs, 4) 308 require.Equal(t, "123", string(bs[0])) 309 require.Equal(t, "45", string(bs[1])) 310 require.Equal(t, "6", string(bs[2])) 311 require.Equal(t, "7890", string(bs[3])) 312 313 b.Release() 314 wa.MustMallocTimes(2) 315 wa.MustAllFreed() 316 } 317 318 func TestBuf_ReadNext(t *testing.T) { 319 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 320 b := NewBuf(wa, 4) 321 322 n, err := b.Write([]byte("123")) 323 require.Nil(t, err) 324 require.Equal(t, 3, n) 325 b.Append([]byte("45")) 326 n, err = b.Write([]byte("678")) 327 require.Nil(t, err) 328 require.Equal(t, 3, n) 329 330 require.Equal(t, "123", string(b.ReadNext())) 331 require.Equal(t, "45", string(b.ReadNext())) 332 require.Equal(t, "6", string(b.ReadNext())) 333 require.Equal(t, "78", string(b.ReadNext())) 334 require.Equal(t, "", string(b.ReadNext())) 335 336 b.Release() 337 wa.MustMallocTimes(2) 338 wa.MustAllFreed() 339 } 340 341 func TestBuf_WriteBigData(t *testing.T) { 342 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 343 b := NewBuf(wa, 2) 344 345 n, err := b.Write([]byte("123")) 346 require.Nil(t, err) 347 require.Equal(t, 3, n) 348 349 copy(b.Alloc(3), "456") 350 351 n, err = b.Write([]byte("78")) 352 require.Nil(t, err) 353 require.Equal(t, 2, n) 354 355 bts := make([]byte, 8) 356 n, err = b.Read(bts) 357 require.Nil(t, err) 358 require.Equal(t, 8, n) 359 require.Equal(t, "12345678", string(bts)) 360 361 b.Release() 362 wa.MustMallocTimes(2 + 1 + 1) 363 wa.MustAllFreed() 364 } 365 366 func TestBuf_PrependAfterRead(t *testing.T) { 367 wa := newWrappedAllocator(t, allocator.NewClassAllocator()) 368 b := NewBuf(wa, 4) 369 370 b.Append([]byte("123")) 371 372 n, err := b.Write([]byte("456")) 373 require.Nil(t, err) 374 require.Equal(t, 3, n) 375 376 bts := make([]byte, 4) 377 n, err = b.Read(bts) 378 require.Nil(t, err) 379 require.Equal(t, 4, n) 380 require.Equal(t, "1234", string(bts)) 381 382 b.Prepend([]byte("34")) 383 copy(b.Prelloc(2), "12") 384 385 bts = make([]byte, 6) 386 n, err = b.Read(bts) 387 require.Nil(t, err) 388 require.Equal(t, 6, n) 389 require.Equal(t, "123456", string(bts)) 390 391 b.Release() 392 wa.MustMallocTimes(1 + 1) 393 wa.MustAllFreed() 394 } 395 396 func TestBuf_UseBytesAfterRelease(t *testing.T) { 397 bytesAllocator := newBytesAllocator() 398 399 b1 := NewBuf(bytesAllocator, 4) 400 n, err := b1.Write([]byte("123")) 401 require.Nil(t, err) 402 require.Equal(t, 3, n) 403 b1.Append([]byte("45")) 404 n, err = b1.Write([]byte("678")) 405 require.Nil(t, err) 406 require.Equal(t, 3, n) 407 408 bts := b1.ReadNext() 409 require.Equal(t, "123", string(bts)) 410 b1.Release() 411 412 b2 := NewBuf(bytesAllocator, 4) 413 n, err = b2.Write([]byte("1234")) 414 require.Nil(t, err) 415 require.Equal(t, 4, n) 416 require.Equal(t, "123", string(bts), "bts of b1 is not released now") 417 418 b1.ReadNext() // read "45" 419 b1.Release() // bts's underlying buffer is still not released 420 421 n, err = b2.Write([]byte("5678")) 422 require.Nil(t, err) 423 require.Equal(t, 4, n) 424 require.Equal(t, "123", string(bts), "bts of b1 is not released now") 425 426 b1.ReadNext() // read "6" 427 b1.Release() // bts's underlying buffer has been released 428 429 n, err = b2.Write([]byte("5678")) 430 require.Nil(t, err) 431 require.Equal(t, 4, n) 432 require.Equal(t, "567", string(bts), "bts of b1 has been changed by b2") 433 } 434 435 func newWrappedAllocator(t *testing.T, a Allocator) *wrappedAllocator { 436 return &wrappedAllocator{t: t, a: a, malloced: make(map[*byte]struct{})} 437 } 438 439 type wrappedAllocator struct { 440 t *testing.T 441 a Allocator 442 malloced map[*byte]struct{} 443 mallocTimes int 444 } 445 446 func (a *wrappedAllocator) Malloc(size int) ([]byte, interface{}) { 447 bts, free := a.a.Malloc(size) 448 a.malloced[&bts[0]] = struct{}{} 449 a.mallocTimes++ 450 return bts, free 451 } 452 453 func (a *wrappedAllocator) Free(bts interface{}) { 454 a.a.Free(bts) 455 if _, ok := a.malloced[&bts.([]byte)[0]]; !ok { 456 require.FailNow(a.t, "free unknown bytes") 457 } 458 delete(a.malloced, &bts.([]byte)[0]) 459 } 460 461 func (a *wrappedAllocator) MustMallocTimes(n int) { 462 require.Equal(a.t, n, a.mallocTimes) 463 } 464 465 func (a *wrappedAllocator) MustAllFreed() { 466 require.Empty(a.t, a.malloced) 467 } 468 469 func newBytesAllocator() *bytesAllocator { 470 return &bytesAllocator{pool: make(map[int][]interface{})} 471 } 472 473 type bytesAllocator struct { 474 pool map[int][]interface{} 475 } 476 477 func (a *bytesAllocator) Malloc(n int) ([]byte, interface{}) { 478 bs, ok := a.pool[n] 479 if ok && len(bs) != 0 { 480 bts := bs[len(bs)-1] 481 a.pool[n] = bs[:len(bs)-1] 482 return bts.([]byte), bts 483 } 484 bts := make([]byte, n) 485 return bts, bts 486 } 487 488 func (a *bytesAllocator) Free(v interface{}) { 489 bts := v.([]byte) 490 bts = bts[:cap(bts)] 491 a.pool[len(bts)] = append(a.pool[len(bts)], v) 492 }