github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/buffer/view_test.go (about)

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