github.com/haraldrudell/parl@v0.4.176/pslices/slice-away-append_test.go (about)

     1  /*
     2  © 2024–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pslices
     7  
     8  import (
     9  	"slices"
    10  	"testing"
    11  )
    12  
    13  // There are three outcomes for a slice-away append:
    14  //   - 1 realloc: the result is larger than the underlying array
    15  //   - 2 append: appending fits slicedAway capacity
    16  //   - 3 copy: appending to SlicedAway fits the underlying array but
    17  //     not slicedAway capacity
    18  
    19  func TestSliceAwayAppend_Realloc(t *testing.T) {
    20  	//t.Error("logging on")
    21  	var (
    22  		// unsliced slice0
    23  		slice00 = []int{1, 2, 3}
    24  		// values to append 4 5 6
    25  		values = []int{4, 5, 6}
    26  		// slice-away index for 1-element slicedAway slice
    27  		slicedAwayIndex = 1
    28  		// expected slice0 slicedAway result: 2, 4, 5, 6
    29  		expSlice0 = append([]int{slice00[slicedAwayIndex]}, values...)
    30  	)
    31  
    32  	var (
    33  		offset  int
    34  		isValid bool
    35  	)
    36  
    37  	var slice0 = slice00
    38  	var slicedAway = slice0[slicedAwayIndex : slicedAwayIndex+1]
    39  	// before: slice0: [1 2 3] slicedAway: [2] values: [4 5 6]
    40  	t.Logf("before: slice0: %v slicedAway: %v values: %v", slice0, slicedAway, values)
    41  	SliceAwayAppend(&slicedAway, &slice0, values)
    42  	// after: slice0: [2 4 5 6] slicedAway: [2 4 5 6]
    43  	t.Logf("after: slice0: %v slicedAway: %v", slice0, slicedAway)
    44  
    45  	// slicedAway value should match
    46  	if !slices.Equal(slicedAway, expSlice0) {
    47  		t.Errorf("FAIL slicedAway %v exp %v", slicedAway, expSlice0)
    48  	}
    49  
    50  	// slice0 value should match
    51  	if !slices.Equal(slice0, expSlice0) {
    52  		t.Errorf("FAIL slice0 %v exp %v", slice0, expSlice0)
    53  	}
    54  
    55  	// slice0 and slicedAway should share underlying array
    56  	offset, isValid = Offset(slice0, slicedAway)
    57  	_ = offset
    58  	if !isValid {
    59  		t.Error("FAIL slice0 slicedAway not same underlying array")
    60  	}
    61  
    62  	// slice0 should be reallocated away from slice00
    63  	offset, isValid = Offset(slice0, slice00)
    64  	_ = offset
    65  	if isValid {
    66  		t.Error("FAIL slice0 slice00 is same underlying array")
    67  	}
    68  }
    69  
    70  func TestSliceAwayAppend_Append(t *testing.T) {
    71  	//t.Error("logging on")
    72  	var (
    73  		// a slice with all values known 1 2 3 4
    74  		slice00 = []int{1, 2, 3, 4}
    75  		// values to append 5
    76  		values = []int{5}
    77  		// slice-away index for 1-element slicedAway slice
    78  		sliceAwayIndex = 1
    79  		// 1 2 5 4
    80  		expSlice0 = append(append(slices.Clone(slice00[:sliceAwayIndex+1]), values...), slice00[sliceAwayIndex+1+len(values):]...)
    81  		// 2 5
    82  		expSlicedAway = append([]int{slice00[sliceAwayIndex]}, values...)
    83  	)
    84  
    85  	var (
    86  		offset  int
    87  		isValid bool
    88  	)
    89  
    90  	var slice0 = slice00
    91  	var slicedAway = slice0[sliceAwayIndex : sliceAwayIndex+1]
    92  	// before: slice0: [1 2 3 4] slicedAway: [2] values: [5]
    93  	t.Logf("before: slice0: %v slicedAway: %v values: %v", slice0, slicedAway, values)
    94  	SliceAwayAppend(&slicedAway, &slice0, values)
    95  	// after: slice0: [1 2 5 4] slicedAway: [2 5]
    96  	t.Logf("after: slice0: %v slicedAway: %v", slice0, slicedAway)
    97  
    98  	// slicedAway value should match
    99  	if !slices.Equal(slicedAway, expSlicedAway) {
   100  		t.Errorf("FAIL slicedAway %v exp %v", slicedAway, expSlicedAway)
   101  	}
   102  
   103  	// slice0 value should match
   104  	if !slices.Equal(slice0, expSlice0) {
   105  		t.Errorf("FAIL slice0 %v exp %v", slice0, expSlice0)
   106  	}
   107  
   108  	// slice0 and slicedAway should share underlying array
   109  	offset, isValid = Offset(slice0, slicedAway)
   110  	_ = offset
   111  	if !isValid {
   112  		t.Error("FAIL slice0 slicedAway not same underlying array")
   113  	}
   114  
   115  	// slice0 and slice00 should share underlying array
   116  	offset, isValid = Offset(slice0, slice00)
   117  	_ = offset
   118  	if !isValid {
   119  		t.Error("FAIL slice0 slice00 not same underlying array")
   120  	}
   121  }
   122  
   123  func TestSliceAwayAppend_Copy(t *testing.T) {
   124  	//t.Error("logging on")
   125  	// requirements:
   126  	//	- slicedAway and values should fit slice00
   127  	//	- slicedAway should be sliced away so far that its capacity cannot
   128  	//		fit values
   129  	//	- the last element of slice00 should be untouched
   130  	//	- the second-to-last element of slice00 should be zeroed out
   131  	//	- slicedAway can be length 1
   132  	//	- therefore, values must be at least length 2
   133  	//	- values and slicedAway must be less than the second to last element of slicedAway
   134  	//	- calc: 2 + 1 < len(slice00) - 2: slice00 is length 5
   135  	var (
   136  		// unsliced slice0
   137  		slice00 = []int{1, 2, 3, 4, 5}
   138  		// values to append 5
   139  		values           = []int{6, 7}
   140  		slicedAwayIndex0 = len(slice00) - 2
   141  		slicedAwayIndex1 = len(slice00) - 1
   142  		// 4, 6, 7, 0, 5
   143  		expSlice0 = append(
   144  			append(
   145  				append(
   146  					// slicedAway
   147  					slices.Clone(slice00[slicedAwayIndex0:slicedAwayIndex1]),
   148  					// values
   149  					values...,
   150  				),
   151  				// zero-out
   152  				0,
   153  			),
   154  			// 5
   155  			slice00[slicedAwayIndex1:]...,
   156  		)
   157  		// 3 5
   158  		expSliceAway = expSlice0[:slicedAwayIndex1-slicedAwayIndex0+len(values)]
   159  	)
   160  
   161  	var (
   162  		offset  int
   163  		isValid bool
   164  	)
   165  
   166  	// re-use slice should work
   167  	var slice0 = slice00
   168  	var slicedAway = slice0[slicedAwayIndex0:slicedAwayIndex1]
   169  	// before: slice0: [1 2 3 4 5] slicedAway: [4] values: [6 7]
   170  	t.Logf("before: slice0: %v slicedAway: %v values: %v", slice0, slicedAway, values)
   171  	SliceAwayAppend(&slicedAway, &slice0, values)
   172  	// after: slice0: [4 6 7 0 5] slicedAway: [4 6 7]
   173  	t.Logf("after: slice0: %v slicedAway: %v", slice0, slicedAway)
   174  
   175  	// slicedAway should match
   176  	if !slices.Equal(slicedAway, expSliceAway) {
   177  		t.Errorf("FAIL slicedAway %v exp %v", slicedAway, expSliceAway)
   178  	}
   179  
   180  	// slice0 should match
   181  	if !slices.Equal(slice0, expSlice0) {
   182  		t.Errorf("FAIL slice0 %v exp %v", slice0, expSlice0)
   183  	}
   184  	// slice0 and slicedAway should share underlying array
   185  	offset, isValid = Offset(slice0, slicedAway)
   186  	_ = offset
   187  	if !isValid {
   188  		t.Error("FAIL slice0 slicedAway not same underlying array")
   189  	}
   190  
   191  	// slice0 and slice00 should share underlying array
   192  	offset, isValid = Offset(slice0, slice00)
   193  	_ = offset
   194  	if !isValid {
   195  		t.Error("FAIL slice0 slice00 not same underlying array")
   196  	}
   197  }
   198  
   199  func TestSliceAwayAppend1_Realloc(t *testing.T) {
   200  	//t.Error("logging on")
   201  	var (
   202  		// unsliced slice0
   203  		slice00 = []int{1}
   204  		// values to append 4 5 6
   205  		value = 4
   206  		// expected slice0 slicedAway result: 2, 4, 5, 6
   207  		expSlice0 = append(slices.Clone(slice00), value)
   208  	)
   209  
   210  	var (
   211  		offset  int
   212  		isValid bool
   213  	)
   214  
   215  	var slice0 = slice00
   216  	var slicedAway = slice0
   217  	// before: slice0: [1] slicedAway: [1] value: 4
   218  	t.Logf("before: slice0: %v slicedAway: %v value: %v", slice0, slicedAway, value)
   219  	SliceAwayAppend1(&slicedAway, &slice0, value)
   220  	// after: slice0: [1 4] slicedAway: [1 4]
   221  	t.Logf("after: slice0: %v slicedAway: %v", slice0, slicedAway)
   222  
   223  	// slicedAway value should match
   224  	if !slices.Equal(slicedAway, expSlice0) {
   225  		t.Errorf("FAIL slicedAway %v exp %v", slicedAway, expSlice0)
   226  	}
   227  
   228  	// slice0 value should match
   229  	if !slices.Equal(slice0, expSlice0) {
   230  		t.Errorf("FAIL slice0 %v exp %v", slice0, expSlice0)
   231  	}
   232  
   233  	// slice0 and slicedAway should share underlying array
   234  	offset, isValid = Offset(slice0, slicedAway)
   235  	_ = offset
   236  	if !isValid {
   237  		t.Error("FAIL slice0 slicedAway not same underlying array")
   238  	}
   239  
   240  	// slice0 should be reallocated away from slice00
   241  	offset, isValid = Offset(slice0, slice00)
   242  	_ = offset
   243  	if isValid {
   244  		t.Error("FAIL slice0 slice00 is same underlying array")
   245  	}
   246  }
   247  
   248  func TestSliceAwayAppend1_Append(t *testing.T) {
   249  	//t.Error("logging on")
   250  	var (
   251  		// a slice with all values known 1 2 3 4
   252  		slice00 = []int{1, 2, 3, 4}
   253  		// values to append 5
   254  		value = 5
   255  		// slice-away index for 1-element slicedAway slice
   256  		sliceAwayIndex = 1
   257  		// 1 2 5 4
   258  		expSlice0 = append(append(slices.Clone(slice00[:sliceAwayIndex+1]), value), slice00[sliceAwayIndex+2:]...)
   259  		// 2 5
   260  		expSlicedAway = append([]int{slice00[sliceAwayIndex]}, value)
   261  	)
   262  
   263  	var (
   264  		offset  int
   265  		isValid bool
   266  	)
   267  
   268  	var slice0 = slice00
   269  	var slicedAway = slice0[sliceAwayIndex : sliceAwayIndex+1]
   270  	// before: slice0: [1 2 3 4] slicedAway: [2] value: 5
   271  	t.Logf("before: slice0: %v slicedAway: %v value: %v", slice0, slicedAway, value)
   272  	SliceAwayAppend1(&slicedAway, &slice0, value)
   273  	// after: slice0: [1 2 5 4] slicedAway: [2 5]
   274  	t.Logf("after: slice0: %v slicedAway: %v", slice0, slicedAway)
   275  
   276  	// slicedAway value should match
   277  	if !slices.Equal(slicedAway, expSlicedAway) {
   278  		t.Errorf("FAIL slicedAway %v exp %v", slicedAway, expSlicedAway)
   279  	}
   280  
   281  	// slice0 value should match
   282  	if !slices.Equal(slice0, expSlice0) {
   283  		t.Errorf("FAIL slice0 %v exp %v", slice0, expSlice0)
   284  	}
   285  
   286  	// slice0 and slicedAway should share underlying array
   287  	offset, isValid = Offset(slice0, slicedAway)
   288  	_ = offset
   289  	if !isValid {
   290  		t.Error("FAIL slice0 slicedAway not same underlying array")
   291  	}
   292  
   293  	// slice0 and slice00 should share underlying array
   294  	offset, isValid = Offset(slice0, slice00)
   295  	_ = offset
   296  	if !isValid {
   297  		t.Error("FAIL slice0 slice00 not same underlying array")
   298  	}
   299  }
   300  
   301  func TestSliceAwayAppend1_Copy(t *testing.T) {
   302  	//t.Error("logging on")
   303  	// requirements:
   304  	//	- to force a copy while appending a single element,
   305  	//		slicedAway must be at the end of slice0
   306  	//	- slicedAway and values should fit slice00
   307  	//	- the last element of slice00 should be zeroed out
   308  	//	- slicedAway can be length 1
   309  	//	- for zero-out to happen, slice0 must be longer than
   310  	//		slicedAway and value
   311  	//	- len(slice00) = len(slicedAway) = len(value) + 1: 3
   312  	var (
   313  		// unsliced slice0
   314  		slice00 = []int{1, 2, 3}
   315  		// values to append 5
   316  		value            = 4
   317  		slicedAwayIndex0 = len(slice00) - 1
   318  		slicedAwayIndex1 = len(slice00)
   319  		// 3, 4, 0
   320  		expSlice0 = append(
   321  			append(
   322  				// slicedAway
   323  				slices.Clone(slice00[slicedAwayIndex0:slicedAwayIndex1]),
   324  				// values
   325  				value,
   326  			),
   327  			// zero-out
   328  			0,
   329  		)
   330  		// 3 4
   331  		expSliceAway = expSlice0[:slicedAwayIndex1-slicedAwayIndex0+1]
   332  	)
   333  
   334  	var (
   335  		offset  int
   336  		isValid bool
   337  	)
   338  
   339  	// re-use slice should work
   340  	var slice0 = slice00
   341  	var slicedAway = slice0[slicedAwayIndex0:slicedAwayIndex1]
   342  	// before: slice0: [1 2 3] slicedAway: [3] values: 4
   343  	t.Logf("before: slice0: %v slicedAway: %v values: %v", slice0, slicedAway, value)
   344  	SliceAwayAppend1(&slicedAway, &slice0, value)
   345  	// after: slice0: [3 4 0] slicedAway: [3 4]
   346  	t.Logf("after: slice0: %v slicedAway: %v", slice0, slicedAway)
   347  
   348  	// slicedAway should match
   349  	if !slices.Equal(slicedAway, expSliceAway) {
   350  		t.Errorf("FAIL slicedAway %v exp %v", slicedAway, expSliceAway)
   351  	}
   352  
   353  	// slice0 should match
   354  	if !slices.Equal(slice0, expSlice0) {
   355  		t.Errorf("FAIL slice0 %v exp %v", slice0, expSlice0)
   356  	}
   357  	// slice0 and slicedAway should share underlying array
   358  	offset, isValid = Offset(slice0, slicedAway)
   359  	_ = offset
   360  	if !isValid {
   361  		t.Error("FAIL slice0 slicedAway not same underlying array")
   362  	}
   363  
   364  	// slice0 and slice00 should share underlying array
   365  	offset, isValid = Offset(slice0, slice00)
   366  	_ = offset
   367  	if !isValid {
   368  		t.Error("FAIL slice0 slice00 not same underlying array")
   369  	}
   370  }
   371  
   372  func TestOffset(t *testing.T) {
   373  	//t.Error("logging on")
   374  	var size = 3
   375  	var sliceIndex = 2
   376  
   377  	var offset int
   378  	var isValid bool
   379  
   380  	var slice0 = make([]int, size)
   381  	var slicedAway = slice0[sliceIndex:]
   382  	offset, isValid = Offset(slice0, slicedAway)
   383  	// offset: 2 isValid: true
   384  	t.Logf("offset: %d isValid: %t", offset, isValid)
   385  	if !isValid {
   386  		t.Error("FAIL isValid false")
   387  	}
   388  	if offset != sliceIndex {
   389  		t.Errorf("FAIL bad offset %d exp %d", offset, sliceIndex)
   390  	}
   391  }