gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/buffer/buffer_test.go (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package buffer
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"io"
    22  	"math/rand"
    23  	"slices"
    24  	"strings"
    25  	"testing"
    26  
    27  	"gvisor.dev/gvisor/pkg/state"
    28  	"gvisor.dev/gvisor/pkg/tcpip/checksum"
    29  )
    30  
    31  func BenchmarkReadAt(b *testing.B) {
    32  	b.ReportAllocs()
    33  	var buf Buffer
    34  	buf.Append(NewView(100))
    35  	defer buf.Release()
    36  
    37  	bytes := make([]byte, 10)
    38  	for i := 0; i < b.N; i++ {
    39  		buf.ReadAt(bytes, 0)
    40  	}
    41  }
    42  
    43  func BenchmarkWriteRead(b *testing.B) {
    44  	b.ReportAllocs()
    45  	var buf Buffer
    46  	defer buf.Release()
    47  	sz := 1000
    48  	rbuf := bytes.NewBuffer(make([]byte, sz))
    49  	for i := 0; i < b.N; i++ {
    50  		buf.Append(NewView(sz))
    51  		rbuf.Reset()
    52  		buf.ReadToWriter(rbuf, int64(sz))
    53  	}
    54  }
    55  
    56  func fillAppend(b *Buffer, data []byte) {
    57  	b.Append(NewViewWithData(data))
    58  }
    59  
    60  func fillAppendEnd(b *Buffer, data []byte) {
    61  	b.GrowTo(baseChunkSize-1, false)
    62  	b.Append(NewViewWithData(data))
    63  	b.TrimFront(baseChunkSize - 1)
    64  }
    65  
    66  func fillWriteFromReader(b *Buffer, data []byte) {
    67  	buf := bytes.NewBuffer(data)
    68  	b.WriteFromReader(buf, int64(len(data)))
    69  }
    70  
    71  func fillWriteFromReaderEnd(b *Buffer, data []byte) {
    72  	b.GrowTo(baseChunkSize-1, false)
    73  	buf := bytes.NewBuffer(data)
    74  	b.WriteFromReader(buf, int64(len(data)))
    75  	b.TrimFront(baseChunkSize - 1)
    76  }
    77  
    78  var fillFuncs = map[string]func(*Buffer, []byte){
    79  	"append":             fillAppend,
    80  	"appendEnd":          fillAppendEnd,
    81  	"writeFromReader":    fillWriteFromReader,
    82  	"writeFromReaderEnd": fillWriteFromReaderEnd,
    83  }
    84  
    85  func testReadAt(t *testing.T, b *Buffer, offset int64, n int, wantStr string, wantErr error) {
    86  	t.Helper()
    87  	d := make([]byte, n)
    88  	n, err := b.ReadAt(d, offset)
    89  	if n != len(wantStr) {
    90  		t.Errorf("got %d, want %d", n, len(wantStr))
    91  	}
    92  	if err != wantErr {
    93  		t.Errorf("got err %v, want %v", err, wantErr)
    94  	}
    95  	if !bytes.Equal(d[:n], []byte(wantStr)) {
    96  		t.Errorf("got %q, want %q", string(d[:n]), wantStr)
    97  	}
    98  }
    99  
   100  func TestBuffer(t *testing.T) {
   101  	testCases := []struct {
   102  		name   string
   103  		input  string
   104  		output string
   105  		op     func(*testing.T, *Buffer)
   106  	}{
   107  		// Preconditions.
   108  		{
   109  			name:   "truncate-check",
   110  			input:  "hello",
   111  			output: "hello", // Not touched.
   112  			op: func(t *testing.T, b *Buffer) {
   113  				defer func() {
   114  					if r := recover(); r == nil {
   115  						t.Errorf("Truncate(-1) did not panic")
   116  					}
   117  				}()
   118  				b.Truncate(-1)
   119  			},
   120  		},
   121  		{
   122  			name:   "growto-check",
   123  			input:  "hello",
   124  			output: "hello", // Not touched.
   125  			op: func(t *testing.T, b *Buffer) {
   126  				defer func() {
   127  					if r := recover(); r == nil {
   128  						t.Errorf("GrowTo(-1) did not panic")
   129  					}
   130  				}()
   131  				b.GrowTo(-1, false)
   132  			},
   133  		},
   134  		{
   135  			name:   "advance-check",
   136  			input:  "hello",
   137  			output: "", // Consumed.
   138  			op: func(t *testing.T, b *Buffer) {
   139  				defer func() {
   140  					if r := recover(); r == nil {
   141  						t.Errorf("advanceRead(Size()+1) did not panic")
   142  					}
   143  				}()
   144  				b.advanceRead(b.Size() + 1)
   145  			},
   146  		},
   147  
   148  		// Prepend.
   149  		{
   150  			name:   "prepend",
   151  			input:  "world",
   152  			output: "hello world",
   153  			op: func(t *testing.T, b *Buffer) {
   154  				b.Prepend(NewViewWithData([]byte("hello ")))
   155  			},
   156  		},
   157  		{
   158  			name:   "prepend-backfill-full",
   159  			input:  "hello world",
   160  			output: "jello world",
   161  			op: func(t *testing.T, b *Buffer) {
   162  				b.TrimFront(1)
   163  				b.Prepend(NewViewWithData([]byte("j")))
   164  			},
   165  		},
   166  		{
   167  			name:   "prepend-backfill-under",
   168  			input:  "hello world",
   169  			output: "hola world",
   170  			op: func(t *testing.T, b *Buffer) {
   171  				b.TrimFront(5)
   172  				b.Prepend(NewViewWithData([]byte("hola")))
   173  			},
   174  		},
   175  		{
   176  			name:   "prepend-backfill-over",
   177  			input:  "hello world",
   178  			output: "smello world",
   179  			op: func(t *testing.T, b *Buffer) {
   180  				b.TrimFront(1)
   181  				b.Prepend(NewViewWithData([]byte("sm")))
   182  			},
   183  		},
   184  		{
   185  			name:   "prepend-fill",
   186  			input:  strings.Repeat("1", baseChunkSize-1),
   187  			output: "0" + strings.Repeat("1", baseChunkSize-1),
   188  			op: func(t *testing.T, b *Buffer) {
   189  				b.Prepend(NewViewWithData([]byte("0")))
   190  			},
   191  		},
   192  		{
   193  			name:   "prepend-overflow",
   194  			input:  strings.Repeat("1", baseChunkSize),
   195  			output: "0" + strings.Repeat("1", baseChunkSize),
   196  			op: func(t *testing.T, b *Buffer) {
   197  				b.Prepend(NewViewWithData([]byte("0")))
   198  			},
   199  		},
   200  		{
   201  			name:   "prepend-multiple-buffers",
   202  			input:  strings.Repeat("1", baseChunkSize-1),
   203  			output: strings.Repeat("0", baseChunkSize*3) + strings.Repeat("1", baseChunkSize-1),
   204  			op: func(t *testing.T, b *Buffer) {
   205  				b.Prepend(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize))))
   206  				b.Prepend(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize))))
   207  				b.Prepend(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize))))
   208  			},
   209  		},
   210  
   211  		// Append and write.
   212  		{
   213  			name:   "append",
   214  			input:  "hello",
   215  			output: "hello world",
   216  			op: func(t *testing.T, b *Buffer) {
   217  				b.Append(NewViewWithData([]byte(" world")))
   218  			},
   219  		},
   220  		{
   221  			name:   "append-fill",
   222  			input:  strings.Repeat("1", baseChunkSize-1),
   223  			output: strings.Repeat("1", baseChunkSize-1) + "0",
   224  			op: func(t *testing.T, b *Buffer) {
   225  				b.Append(NewViewWithData([]byte("0")))
   226  			},
   227  		},
   228  		{
   229  			name:   "append-overflow",
   230  			input:  strings.Repeat("1", baseChunkSize),
   231  			output: strings.Repeat("1", baseChunkSize) + "0",
   232  			op: func(t *testing.T, b *Buffer) {
   233  				b.Append(NewViewWithData([]byte("0")))
   234  			},
   235  		},
   236  		{
   237  			name:   "append-multiple-views",
   238  			input:  strings.Repeat("1", baseChunkSize-1),
   239  			output: strings.Repeat("1", baseChunkSize-1) + strings.Repeat("0", baseChunkSize*3),
   240  			op: func(t *testing.T, b *Buffer) {
   241  				b.Append(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize))))
   242  				b.Append(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize))))
   243  				b.Append(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize))))
   244  			},
   245  		},
   246  
   247  		// AppendOwned.
   248  		{
   249  			name:   "append-owned",
   250  			input:  "hello",
   251  			output: "hello world",
   252  			op: func(t *testing.T, b *Buffer) {
   253  				v := NewViewWithData([]byte("Xworld"))
   254  				// Appending to a buffer that has extra references means this will
   255  				// degrade into an "appendOwned" for the chunk being added.
   256  				c := b.Clone()
   257  				defer c.Release()
   258  				b.Append(v)
   259  				v.chunk.data[0] = ' '
   260  			},
   261  		},
   262  
   263  		// Truncate.
   264  		{
   265  			name:   "truncate",
   266  			input:  "hello world",
   267  			output: "hello",
   268  			op: func(t *testing.T, b *Buffer) {
   269  				b.Truncate(5)
   270  			},
   271  		},
   272  		{
   273  			name:   "truncate-noop",
   274  			input:  "hello world",
   275  			output: "hello world",
   276  			op: func(t *testing.T, b *Buffer) {
   277  				b.Truncate(b.Size() + 1)
   278  			},
   279  		},
   280  		{
   281  			name:   "truncate-multiple-buffers",
   282  			input:  strings.Repeat("1", baseChunkSize),
   283  			output: strings.Repeat("1", baseChunkSize*2-1),
   284  			op: func(t *testing.T, b *Buffer) {
   285  				b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize))))
   286  				b.Truncate(baseChunkSize*2 - 1)
   287  			},
   288  		},
   289  		{
   290  			name:   "truncate-multiple-buffers-to-one",
   291  			input:  strings.Repeat("1", baseChunkSize),
   292  			output: "11111",
   293  			op: func(t *testing.T, b *Buffer) {
   294  				b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize))))
   295  				b.Truncate(5)
   296  			},
   297  		},
   298  
   299  		// TrimFront.
   300  		{
   301  			name:   "trim",
   302  			input:  "hello world",
   303  			output: "world",
   304  			op: func(t *testing.T, b *Buffer) {
   305  				b.TrimFront(6)
   306  			},
   307  		},
   308  		{
   309  			name:   "trim-too-large",
   310  			input:  "hello world",
   311  			output: "",
   312  			op: func(t *testing.T, b *Buffer) {
   313  				b.TrimFront(b.Size() + 1)
   314  			},
   315  		},
   316  		{
   317  			name:   "trim-multiple-buffers",
   318  			input:  strings.Repeat("1", baseChunkSize),
   319  			output: strings.Repeat("1", baseChunkSize*2-1),
   320  			op: func(t *testing.T, b *Buffer) {
   321  				b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize))))
   322  				b.TrimFront(1)
   323  			},
   324  		},
   325  		{
   326  			name:   "trim-multiple-buffers-to-one-buffer",
   327  			input:  strings.Repeat("1", baseChunkSize),
   328  			output: "1",
   329  			op: func(t *testing.T, b *Buffer) {
   330  				b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize))))
   331  				b.TrimFront(baseChunkSize*2 - 1)
   332  			},
   333  		},
   334  
   335  		// GrowTo.
   336  		{
   337  			name:   "growto",
   338  			input:  "hello world",
   339  			output: "hello world",
   340  			op: func(t *testing.T, b *Buffer) {
   341  				b.GrowTo(1, true)
   342  			},
   343  		},
   344  		{
   345  			name:   "growto-from-zero",
   346  			output: strings.Repeat("\x00", 1024),
   347  			op: func(t *testing.T, b *Buffer) {
   348  				b.GrowTo(1024, true)
   349  			},
   350  		},
   351  		{
   352  			name:   "growto-from-non-zero",
   353  			input:  strings.Repeat("1", baseChunkSize),
   354  			output: strings.Repeat("1", baseChunkSize) + strings.Repeat("\x00", baseChunkSize),
   355  			op: func(t *testing.T, b *Buffer) {
   356  				b.GrowTo(baseChunkSize*2, true)
   357  			},
   358  		},
   359  
   360  		// Clone.
   361  		{
   362  			name:   "clone",
   363  			input:  "hello",
   364  			output: "hello",
   365  			op: func(t *testing.T, b *Buffer) {
   366  				other := b.Clone()
   367  				bs := other.Flatten()
   368  				want := []byte("hello")
   369  				if !bytes.Equal(bs, want) {
   370  					t.Errorf("expected %v, got %v", want, bs)
   371  				}
   372  			},
   373  		},
   374  		{
   375  			name:   "copy-large",
   376  			input:  strings.Repeat("1", baseChunkSize+1),
   377  			output: strings.Repeat("1", baseChunkSize+1),
   378  			op: func(t *testing.T, b *Buffer) {
   379  				other := b.Clone()
   380  				bs := other.Flatten()
   381  				want := []byte(strings.Repeat("1", baseChunkSize+1))
   382  				if !bytes.Equal(bs, want) {
   383  					t.Errorf("expected %v, got %v", want, bs)
   384  				}
   385  			},
   386  		},
   387  
   388  		// Merge.
   389  		{
   390  			name:   "merge",
   391  			input:  "hello",
   392  			output: "hello world",
   393  			op: func(t *testing.T, b *Buffer) {
   394  				var other Buffer
   395  				other.Append(NewViewWithData([]byte(" world")))
   396  				b.Merge(&other)
   397  				if sz := other.Size(); sz != 0 {
   398  					t.Errorf("expected 0, got %d", sz)
   399  				}
   400  			},
   401  		},
   402  		{
   403  			name:   "merge-large",
   404  			input:  strings.Repeat("1", baseChunkSize+1),
   405  			output: strings.Repeat("1", baseChunkSize+1) + strings.Repeat("0", baseChunkSize+1),
   406  			op: func(t *testing.T, b *Buffer) {
   407  				var other Buffer
   408  				other.Append(NewViewWithData(([]byte(strings.Repeat("0", baseChunkSize+1)))))
   409  				b.Merge(&other)
   410  				if sz := other.Size(); sz != 0 {
   411  					t.Errorf("expected 0, got %d", sz)
   412  				}
   413  			},
   414  		},
   415  
   416  		// ReadAt.
   417  		{
   418  			name:   "readat",
   419  			input:  "hello",
   420  			output: "hello",
   421  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, 0, 6, "hello", io.EOF) },
   422  		},
   423  		{
   424  			name:   "readat-long",
   425  			input:  "hello",
   426  			output: "hello",
   427  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, 0, 8, "hello", io.EOF) },
   428  		},
   429  		{
   430  			name:   "readat-short",
   431  			input:  "hello",
   432  			output: "hello",
   433  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, 0, 3, "hel", nil) },
   434  		},
   435  		{
   436  			name:   "readat-offset",
   437  			input:  "hello",
   438  			output: "hello",
   439  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, 2, 3, "llo", io.EOF) },
   440  		},
   441  		{
   442  			name:   "readat-long-offset",
   443  			input:  "hello",
   444  			output: "hello",
   445  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, 2, 8, "llo", io.EOF) },
   446  		},
   447  		{
   448  			name:   "readat-short-offset",
   449  			input:  "hello",
   450  			output: "hello",
   451  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, 2, 2, "ll", nil) },
   452  		},
   453  		{
   454  			name:   "readat-skip-all",
   455  			input:  "hello",
   456  			output: "hello",
   457  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, baseChunkSize+1, 1, "", io.EOF) },
   458  		},
   459  		{
   460  			name:   "readat-second-view",
   461  			input:  strings.Repeat("0", baseChunkSize+1) + "12",
   462  			output: strings.Repeat("0", baseChunkSize+1) + "12",
   463  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, baseChunkSize+1, 1, "1", nil) },
   464  		},
   465  		{
   466  			name:   "readat-second-buffer-end",
   467  			input:  strings.Repeat("0", baseChunkSize+1) + "12",
   468  			output: strings.Repeat("0", baseChunkSize+1) + "12",
   469  			op:     func(t *testing.T, b *Buffer) { testReadAt(t, b, baseChunkSize+1, 2, "12", io.EOF) },
   470  		},
   471  	}
   472  
   473  	for _, tc := range testCases {
   474  		for fillName, fn := range fillFuncs {
   475  			t.Run(fillName+"/"+tc.name, func(t *testing.T) {
   476  				// Construct & fill the view.
   477  				var buf Buffer
   478  				fn(&buf, []byte(tc.input))
   479  
   480  				// Run the operation.
   481  				if tc.op != nil {
   482  					tc.op(t, &buf)
   483  				}
   484  
   485  				// Flatten and validate.
   486  				out := buf.Flatten()
   487  				if !bytes.Equal([]byte(tc.output), out) {
   488  					t.Errorf("expected %q, got %q", tc.output, string(out))
   489  				}
   490  
   491  				// Ensure the size is correct.
   492  				if len(out) != int(buf.Size()) {
   493  					t.Errorf("size is wrong: expected %d, got %d", len(out), buf.Size())
   494  				}
   495  
   496  				// Calculate contents via apply.
   497  				var appliedOut []byte
   498  				buf.Apply(func(v *View) {
   499  					appliedOut = append(appliedOut, v.AsSlice()...)
   500  				})
   501  				if len(appliedOut) != len(out) {
   502  					t.Errorf("expected %d, got %d", len(out), len(appliedOut))
   503  				}
   504  				if !bytes.Equal(appliedOut, out) {
   505  					t.Errorf("expected %v, got %v", out, appliedOut)
   506  				}
   507  
   508  				// Calculate contents via ReadToWriter.
   509  				var b bytes.Buffer
   510  				n, err := buf.ReadToWriter(&b, int64(len(out)))
   511  				if n != int64(len(out)) {
   512  					t.Errorf("expected %d, got %d", len(out), n)
   513  				}
   514  				if err != nil {
   515  					t.Errorf("expected nil, got %v", err)
   516  				}
   517  				if !bytes.Equal(b.Bytes(), out) {
   518  					t.Errorf("expected %v, got %v", out, b.Bytes())
   519  				}
   520  			})
   521  		}
   522  	}
   523  }
   524  
   525  func TestBufferPullUp(t *testing.T) {
   526  	for _, tc := range []struct {
   527  		desc   string
   528  		inputs []string
   529  		offset int
   530  		length int
   531  		output string
   532  		failed bool
   533  		// lengths is the lengths of each buffer node after the pull up.
   534  		lengths []int
   535  	}{
   536  		{
   537  			desc: "whole empty view",
   538  		},
   539  		{
   540  			desc:    "zero pull",
   541  			inputs:  []string{"hello", " world"},
   542  			lengths: []int{5, 6},
   543  		},
   544  		{
   545  			desc:    "whole view",
   546  			inputs:  []string{"hello", " world"},
   547  			offset:  0,
   548  			length:  11,
   549  			output:  "hello world",
   550  			lengths: []int{11},
   551  		},
   552  		{
   553  			desc:    "middle to end aligned",
   554  			inputs:  []string{"0123", "45678", "9abcd"},
   555  			offset:  4,
   556  			length:  10,
   557  			output:  "456789abcd",
   558  			lengths: []int{4, 10},
   559  		},
   560  		{
   561  			desc:    "middle to end unaligned",
   562  			inputs:  []string{"0123", "45678", "9abcd"},
   563  			offset:  6,
   564  			length:  8,
   565  			output:  "6789abcd",
   566  			lengths: []int{4, 10},
   567  		},
   568  		{
   569  			desc:    "middle aligned",
   570  			inputs:  []string{"0123", "45678", "9abcd", "efgh"},
   571  			offset:  6,
   572  			length:  5,
   573  			output:  "6789a",
   574  			lengths: []int{4, 10, 4},
   575  		},
   576  
   577  		// Failed cases.
   578  		{
   579  			desc:   "empty view - length too long",
   580  			offset: 0,
   581  			length: 1,
   582  			failed: true,
   583  		},
   584  		{
   585  			desc:   "empty view - offset too large",
   586  			offset: 1,
   587  			length: 1,
   588  			failed: true,
   589  		},
   590  		{
   591  			desc:    "length too long",
   592  			inputs:  []string{"0123", "45678", "9abcd"},
   593  			offset:  4,
   594  			length:  100,
   595  			failed:  true,
   596  			lengths: []int{4, 5, 5},
   597  		},
   598  		{
   599  			desc:    "offset too large",
   600  			inputs:  []string{"0123", "45678", "9abcd"},
   601  			offset:  100,
   602  			length:  1,
   603  			failed:  true,
   604  			lengths: []int{4, 5, 5},
   605  		},
   606  	} {
   607  		t.Run(tc.desc, func(t *testing.T) {
   608  			var b Buffer
   609  			defer b.Release()
   610  			for _, s := range tc.inputs {
   611  				v := NewViewWithData([]byte(s))
   612  				b.appendOwned(v)
   613  			}
   614  
   615  			got, gotOk := b.PullUp(tc.offset, tc.length)
   616  			want, wantOk := []byte(tc.output), !tc.failed
   617  			if gotOk == wantOk && got.Size() == 0 && len(want) == 0 {
   618  				return
   619  			}
   620  			if gotOk != wantOk || !bytes.Equal(got.AsSlice(), want) {
   621  				t.Errorf("v.PullUp(%d, %d) = %q, %t; %q, %t", tc.offset, tc.length, got.AsSlice(), gotOk, want, wantOk)
   622  			}
   623  
   624  			var gotLengths []int
   625  			for v := b.data.Front(); v != nil; v = v.Next() {
   626  				gotLengths = append(gotLengths, v.Size())
   627  			}
   628  			if !slices.Equal(gotLengths, tc.lengths) {
   629  				t.Errorf("lengths = %v; want %v", gotLengths, tc.lengths)
   630  			}
   631  		})
   632  	}
   633  }
   634  
   635  func TestReadFromLargeWriter(t *testing.T) {
   636  	writeSize := int64(1 << 20)
   637  	largeWriter := bytes.NewBuffer(make([]byte, writeSize))
   638  	b := Buffer{}
   639  	// Expect this write to be buffered into several MaxChunkSize sized views.
   640  	n, err := b.WriteFromReader(largeWriter, writeSize)
   641  	if err != nil {
   642  		t.Fatalf("b.WriteFromReader() failed: want err=nil, got %v", err)
   643  	}
   644  	if n != writeSize {
   645  		t.Errorf("got b.WriteFromReader()=%d, want %d", n, writeSize)
   646  	}
   647  	nChunks := int(writeSize / MaxChunkSize)
   648  	if b.data.Len() != nChunks {
   649  		t.Errorf("b.WriteFromReader() failed, got b.data.Len()=%d, want %d", b.data.Len(), nChunks)
   650  	}
   651  }
   652  
   653  func TestRead(t *testing.T) {
   654  	readStrings := []string{"abcdef", "123456", "ghijkl"}
   655  	totalSize := len(readStrings) * len(readStrings[0])
   656  	for readSz := 0; readSz < totalSize+1; readSz++ {
   657  		b := Buffer{}
   658  		for _, s := range readStrings {
   659  			v := NewViewWithData([]byte(s))
   660  			b.appendOwned(v)
   661  		}
   662  		orig := b.Clone()
   663  		orig.Truncate(int64(readSz))
   664  		p := make([]byte, readSz)
   665  		_, err := b.read(p)
   666  		if err != nil {
   667  			t.Fatalf("Read([]byte(%d)) failed: %v", readSz, err)
   668  		}
   669  		if !bytes.Equal(p, orig.Flatten()) {
   670  			t.Errorf("Read([]byte(%d)) failed, want p=%v, got %v", readSz, orig.Flatten(), p)
   671  		}
   672  		if int(b.Size()) != totalSize-readSz {
   673  			t.Errorf("Read([]byte(%d)) failed, want b.Size()=%v, got %v", readSz, totalSize-readSz, b.Size())
   674  		}
   675  	}
   676  }
   677  
   678  func TestReadByte(t *testing.T) {
   679  	readString := "abcdef123456ghijkl"
   680  	b := Buffer{}
   681  	nViews := 3
   682  	for i := 0; i < nViews; i++ {
   683  		vLen := len(readString) / nViews
   684  		v := NewViewWithData([]byte(readString[i*vLen : (i+1)*vLen]))
   685  		b.appendOwned(v)
   686  	}
   687  	for i := 0; i < len(readString); i++ {
   688  		orig := readString[i]
   689  		bt, err := b.readByte()
   690  		if err != nil {
   691  			t.Fatalf("readByte() failed: %v", err)
   692  		}
   693  		if bt != orig {
   694  			t.Errorf("readByte() failed, want %v, got %v", orig, bt)
   695  		}
   696  		if int(b.Size()) != len(readString[i+1:]) {
   697  			t.Errorf("readByte() failed, want b.Size()=%v, got %v", len(readString[i+1:]), b.Size())
   698  		}
   699  	}
   700  }
   701  
   702  func TestPullUpModifiedViews(t *testing.T) {
   703  	var b Buffer
   704  	defer b.Release()
   705  	for _, s := range []string{"abcdef", "123456", "ghijkl"} {
   706  		v := NewViewWithData([]byte(s))
   707  		v.TrimFront(3)
   708  		b.appendOwned(v)
   709  	}
   710  
   711  	v, ok := b.PullUp(3, 3)
   712  	if !ok {
   713  		t.Errorf("PullUp failed: want ok=true, got ok=false")
   714  	}
   715  	want := []byte("456")
   716  	if !bytes.Equal(v.AsSlice(), want) {
   717  		t.Errorf("PullUp failed: want %v, got %v", want, v.AsSlice())
   718  	}
   719  }
   720  
   721  func TestBufferClone(t *testing.T) {
   722  	const (
   723  		originalSize  = 90
   724  		bytesToDelete = 30
   725  	)
   726  	b := MakeWithData(bytes.Repeat([]byte{originalSize}, originalSize))
   727  	clonedB := b.Clone()
   728  	b.TrimFront(bytesToDelete)
   729  
   730  	if got, want := int(b.Size()), originalSize-bytesToDelete; got != want {
   731  		t.Errorf("original buffer was not changed: size expected = %d, got = %d", want, got)
   732  	}
   733  	if got := clonedB.Size(); got != originalSize {
   734  		t.Errorf("cloned buffer should not be modified: expected size = %d, got = %d", originalSize, got)
   735  	}
   736  }
   737  
   738  func TestBufferSubApply(t *testing.T) {
   739  	var b Buffer
   740  	defer b.Release()
   741  	b.appendOwned(NewViewWithData([]byte("0123")))
   742  	b.appendOwned(NewViewWithData([]byte("45678")))
   743  	b.appendOwned(NewViewWithData([]byte("9abcd")))
   744  	data := []byte("0123456789abcd")
   745  
   746  	for i := 0; i <= len(data); i++ {
   747  		for j := i; j <= len(data); j++ {
   748  			t.Run(fmt.Sprintf("SubApply(%d,%d)", i, j), func(t *testing.T) {
   749  				var got []byte
   750  				b.SubApply(i, j-i, func(v *View) {
   751  					got = append(got, v.AsSlice()...)
   752  				})
   753  				if want := data[i:j]; !bytes.Equal(got, want) {
   754  					t.Errorf("got = %q; want %q", got, want)
   755  				}
   756  			})
   757  		}
   758  	}
   759  }
   760  
   761  func doSaveAndLoad(t *testing.T, toSave, toLoad *Buffer) {
   762  	t.Helper()
   763  	var buf bytes.Buffer
   764  	ctx := context.Background()
   765  	if _, err := state.Save(ctx, &buf, toSave); err != nil {
   766  		t.Fatal("state.Save:", err)
   767  	}
   768  	if _, err := state.Load(ctx, bytes.NewReader(buf.Bytes()), toLoad); err != nil {
   769  		t.Fatal("state.Load:", err)
   770  	}
   771  }
   772  
   773  func TestSaveRestoreBufferEmpty(t *testing.T) {
   774  	var toSave Buffer
   775  	var b Buffer
   776  	doSaveAndLoad(t, &toSave, &b)
   777  
   778  	if got := b.Flatten(); len(got) != 0 {
   779  		t.Errorf("v.Flatten() = %x, want []", got)
   780  	}
   781  }
   782  
   783  func TestSaveRestoreBuffer(t *testing.T) {
   784  	// Create data that fits  slots.
   785  	data := bytes.Join([][]byte{
   786  		bytes.Repeat([]byte{1, 2}, baseChunkSize),
   787  	}, nil)
   788  
   789  	var toSave Buffer
   790  	toSave.appendOwned(NewViewWithData(data))
   791  
   792  	var b Buffer
   793  	doSaveAndLoad(t, &toSave, &b)
   794  
   795  	// Next available slot at index 3; 0-2 slot are used.
   796  	if got := b.Flatten(); !bytes.Equal(got, data) {
   797  		t.Errorf("v.Flatten() = %x, want %x", got, data)
   798  	}
   799  }
   800  
   801  func TestRangeIntersect(t *testing.T) {
   802  	for _, tc := range []struct {
   803  		desc       string
   804  		x, y, want Range
   805  	}{
   806  		{
   807  			desc: "empty intersects empty",
   808  		},
   809  		{
   810  			desc: "empty intersection",
   811  			x:    Range{end: 10},
   812  			y:    Range{begin: 10, end: 20},
   813  		},
   814  		{
   815  			desc: "some intersection",
   816  			x:    Range{begin: 5, end: 20},
   817  			y:    Range{end: 10},
   818  			want: Range{begin: 5, end: 10},
   819  		},
   820  	} {
   821  		t.Run(tc.desc, func(t *testing.T) {
   822  			if got := tc.x.Intersect(tc.y); got != tc.want {
   823  				t.Errorf("(%#v).Intersect(%#v) = %#v; want %#v", tc.x, tc.y, got, tc.want)
   824  			}
   825  			if got := tc.y.Intersect(tc.x); got != tc.want {
   826  				t.Errorf("(%#v).Intersect(%#v) = %#v; want %#v", tc.y, tc.x, got, tc.want)
   827  			}
   828  		})
   829  	}
   830  }
   831  
   832  func TestRangeOffset(t *testing.T) {
   833  	for _, tc := range []struct {
   834  		input  Range
   835  		offset int
   836  		output Range
   837  	}{
   838  		{
   839  			input:  Range{},
   840  			offset: 0,
   841  			output: Range{},
   842  		},
   843  		{
   844  			input:  Range{},
   845  			offset: -1,
   846  			output: Range{begin: -1, end: -1},
   847  		},
   848  		{
   849  			input:  Range{begin: 10, end: 20},
   850  			offset: -1,
   851  			output: Range{begin: 9, end: 19},
   852  		},
   853  		{
   854  			input:  Range{begin: 10, end: 20},
   855  			offset: 2,
   856  			output: Range{begin: 12, end: 22},
   857  		},
   858  	} {
   859  		if got := tc.input.Offset(tc.offset); got != tc.output {
   860  			t.Errorf("(%#v).Offset(%d) = %#v, want %#v", tc.input, tc.offset, got, tc.output)
   861  		}
   862  	}
   863  }
   864  
   865  func TestRangeLen(t *testing.T) {
   866  	for _, tc := range []struct {
   867  		r    Range
   868  		want int
   869  	}{
   870  		{r: Range{}, want: 0},
   871  		{r: Range{begin: 1, end: 1}, want: 0},
   872  		{r: Range{begin: -1, end: -1}, want: 0},
   873  		{r: Range{end: 10}, want: 10},
   874  		{r: Range{begin: 5, end: 10}, want: 5},
   875  	} {
   876  		if got := tc.r.Len(); got != tc.want {
   877  			t.Errorf("(%#v).Len() = %d, want %d", tc.r, got, tc.want)
   878  		}
   879  	}
   880  }
   881  
   882  func TestChecksum(t *testing.T) {
   883  	data := make([]byte, 100)
   884  	rand.Read(data)
   885  
   886  	b := MakeWithData(data[:30])
   887  	b.appendOwned(NewViewWithData(data[30:70]))
   888  	b.appendOwned(NewViewWithData(data[70:]))
   889  
   890  	for offset := 0; offset < 100; offset++ {
   891  		var cs checksum.Checksumer
   892  		cs.Add(data[offset:])
   893  		dataChecksum := cs.Checksum()
   894  		bufChecksum := b.Checksum(offset)
   895  
   896  		if dataChecksum != bufChecksum {
   897  			t.Errorf("(%#v).Checksum(%d) = %d, want %d", b, offset, bufChecksum, dataChecksum)
   898  		}
   899  	}
   900  }