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  }