github.com/primecitizens/pcz/std@v0.2.1/builtin/slice/grow.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  //
     4  // Copyright 2021 The Go Authors. All rights reserved.
     5  // Use of this source code is governed by a BSD-style
     6  // license that can be found in the LICENSE file.
     7  
     8  package stdslice
     9  
    10  import (
    11  	"unsafe"
    12  
    13  	"github.com/primecitizens/pcz/std/core/alloc"
    14  	"github.com/primecitizens/pcz/std/core/assert"
    15  )
    16  
    17  func Append[E any, Alloc alloc.M](alloc Alloc, dst []E, elems ...E) []E {
    18  	assert.TODO()
    19  	return dst
    20  }
    21  
    22  // Grow increases the slice's capacity, if necessary, to guarantee space for
    23  // another n elements. After Grow(n), at least n elements can be appended
    24  // to the slice without another allocation. If n is negative or too large to
    25  // allocate the memory, Grow panics.
    26  func Grow[E any, Alloc alloc.M](alloc Alloc, s []E, n int) []E {
    27  	if n < 0 {
    28  		assert.Throw("cannot", "be", "negative")
    29  	}
    30  	if n -= cap(s) - len(s); n > 0 {
    31  		s = Append(alloc, s[:cap(s)], make([]E, n)...)[:len(s)]
    32  	}
    33  	return s
    34  }
    35  
    36  // Insert inserts the values v... into s at index i,
    37  // returning the modified slice.
    38  // The elements at s[i:] are shifted up to make room.
    39  // In the returned slice r, r[i] == v[0],
    40  // and r[i+len(v)] == value originally at r[i].
    41  // Insert panics if i is out of range.
    42  // This function is O(len(s) + len(v)).
    43  func Insert[E any, Alloc alloc.M](alloc Alloc, s []E, i int, v ...E) []E {
    44  	m := len(v)
    45  	if m == 0 {
    46  		return s
    47  	}
    48  	n := len(s)
    49  	if i == n {
    50  		return Append(alloc, s, v...)
    51  	}
    52  	if n+m > cap(s) {
    53  		// Use append rather than make so that we bump the size of
    54  		// the slice up to the next storage class.
    55  		// This is what Grow does but we don't call Grow because
    56  		// that might copy the values twice.
    57  		s2 := Append(alloc, s[:i], make([]E, n+m-i)...)
    58  		copy(s2[i:], v)
    59  		copy(s2[i+m:], s[i:])
    60  		return s2
    61  	}
    62  	s = s[:n+m]
    63  
    64  	// before:
    65  	// s: aaaaaaaabbbbccccccccdddd
    66  	//            ^   ^       ^   ^
    67  	//            i  i+m      n  n+m
    68  	// after:
    69  	// s: aaaaaaaavvvvbbbbcccccccc
    70  	//            ^   ^       ^   ^
    71  	//            i  i+m      n  n+m
    72  	//
    73  	// a are the values that don't move in s.
    74  	// v are the values copied in from v.
    75  	// b and c are the values from s that are shifted up in index.
    76  	// d are the values that get overwritten, never to be seen again.
    77  
    78  	if !overlaps(v, s[i+m:]) {
    79  		// Easy case - v does not overlap either the c or d regions.
    80  		// (It might be in some of a or b, or elsewhere entirely.)
    81  		// The data we copy up doesn't write to v at all, so just do it.
    82  
    83  		copy(s[i+m:], s[i:])
    84  
    85  		// Now we have
    86  		// s: aaaaaaaabbbbbbbbcccccccc
    87  		//            ^   ^       ^   ^
    88  		//            i  i+m      n  n+m
    89  		// Note the b values are duplicated.
    90  
    91  		copy(s[i:], v)
    92  
    93  		// Now we have
    94  		// s: aaaaaaaavvvvbbbbcccccccc
    95  		//            ^   ^       ^   ^
    96  		//            i  i+m      n  n+m
    97  		// That's the result we want.
    98  		return s
    99  	}
   100  
   101  	// The hard case - v overlaps c or d. We can't just shift up
   102  	// the data because we'd move or clobber the values we're trying
   103  	// to insert.
   104  	// So instead, write v on top of d, then rotate.
   105  	copy(s[n:], v)
   106  
   107  	// Now we have
   108  	// s: aaaaaaaabbbbccccccccvvvv
   109  	//            ^   ^       ^   ^
   110  	//            i  i+m      n  n+m
   111  
   112  	rotateRight(s[i:], m)
   113  
   114  	// Now we have
   115  	// s: aaaaaaaavvvvbbbbcccccccc
   116  	//            ^   ^       ^   ^
   117  	//            i  i+m      n  n+m
   118  	// That's the result we want.
   119  	return s
   120  }
   121  
   122  // Replace replaces the elements s[i:j] by the given v, and returns the
   123  // modified slice. Replace panics if s[i:j] is not a valid slice of s.
   124  func Replace[E any, Alloc alloc.M](alloc Alloc, s []E, i, j int, v ...E) []E {
   125  	_ = s[i:j] // verify that i:j is a valid subslice
   126  
   127  	if i == j {
   128  		return Insert(alloc, s, i, v...)
   129  	}
   130  	if j == len(s) {
   131  		return Append(alloc, s[:i], v...)
   132  	}
   133  
   134  	tot := len(s[:i]) + len(v) + len(s[j:])
   135  	if tot > cap(s) {
   136  		// Too big to fit, allocate and copy over.
   137  		s2 := Append(alloc, s[:i], make([]E, tot-i)...) // See Insert
   138  		copy(s2[i:], v)
   139  		copy(s2[i+len(v):], s[j:])
   140  		return s2
   141  	}
   142  
   143  	r := s[:tot]
   144  
   145  	if i+len(v) <= j {
   146  		// Easy, as v fits in the deleted portion.
   147  		copy(r[i:], v)
   148  		if i+len(v) != j {
   149  			copy(r[i+len(v):], s[j:])
   150  		}
   151  		return r
   152  	}
   153  
   154  	// We are expanding (v is bigger than j-i).
   155  	// The situation is something like this:
   156  	// (example has i=4,j=8,len(s)=16,len(v)=6)
   157  	// s: aaaaxxxxbbbbbbbbyy
   158  	//        ^   ^       ^ ^
   159  	//        i   j  len(s) tot
   160  	// a: prefix of s
   161  	// x: deleted range
   162  	// b: more of s
   163  	// y: area to expand into
   164  
   165  	if !overlaps(r[i+len(v):], v) {
   166  		// Easy, as v is not clobbered by the first copy.
   167  		copy(r[i+len(v):], s[j:])
   168  		copy(r[i:], v)
   169  		return r
   170  	}
   171  
   172  	// This is a situation where we don't have a single place to which
   173  	// we can copy v. Parts of it need to go to two different places.
   174  	// We want to copy the prefix of v into y and the suffix into x, then
   175  	// rotate |y| spots to the right.
   176  	//
   177  	//        v[2:]      v[:2]
   178  	//         |           |
   179  	// s: aaaavvvvbbbbbbbbvv
   180  	//        ^   ^       ^ ^
   181  	//        i   j  len(s) tot
   182  	//
   183  	// If either of those two destinations don't alias v, then we're good.
   184  	y := len(v) - (j - i) // length of y portion
   185  
   186  	if !overlaps(r[i:j], v) {
   187  		copy(r[i:j], v[y:])
   188  		copy(r[len(s):], v[:y])
   189  		rotateRight(r[i:], y)
   190  		return r
   191  	}
   192  	if !overlaps(r[len(s):], v) {
   193  		copy(r[len(s):], v[:y])
   194  		copy(r[i:j], v[y:])
   195  		rotateRight(r[i:], y)
   196  		return r
   197  	}
   198  
   199  	// Now we know that v overlaps both x and y.
   200  	// That means that the entirety of b is *inside* v.
   201  	// So we don't need to preserve b at all; instead we
   202  	// can copy v first, then copy the b part of v out of
   203  	// v to the right destination.
   204  	k := startIdx(v, s[j:])
   205  	copy(r[i:], v)
   206  	copy(r[i+len(v):], r[i+k:])
   207  	return r
   208  }
   209  
   210  // Rotation algorithm explanation:
   211  //
   212  // rotate left by 2
   213  // start with
   214  //   0123456789
   215  // split up like this
   216  //   01 234567 89
   217  // swap first 2 and last 2
   218  //   89 234567 01
   219  // join first parts
   220  //   89234567 01
   221  // recursively rotate first left part by 2
   222  //   23456789 01
   223  // join at the end
   224  //   2345678901
   225  //
   226  // rotate left by 8
   227  // start with
   228  //   0123456789
   229  // split up like this
   230  //   01 234567 89
   231  // swap first 2 and last 2
   232  //   89 234567 01
   233  // join last parts
   234  //   89 23456701
   235  // recursively rotate second part left by 6
   236  //   89 01234567
   237  // join at the end
   238  //   8901234567
   239  
   240  // TODO: There are other rotate algorithms.
   241  // This algorithm has the desirable property that it moves each element exactly twice.
   242  // The triple-reverse algorithm is simpler and more cache friendly, but takes more writes.
   243  // The follow-cycles algorithm can be 1-write but it is not very cache friendly.
   244  
   245  // rotateLeft rotates b left by n spaces.
   246  // s_final[i] = s_orig[i+r], wrapping around.
   247  func rotateLeft[E any](s []E, r int) {
   248  	for r != 0 && r != len(s) {
   249  		if r*2 <= len(s) {
   250  			swap(s[:r], s[len(s)-r:])
   251  			s = s[:len(s)-r]
   252  		} else {
   253  			swap(s[:len(s)-r], s[r:])
   254  			s, r = s[len(s)-r:], r*2-len(s)
   255  		}
   256  	}
   257  }
   258  
   259  func rotateRight[E any](s []E, r int) {
   260  	rotateLeft(s, len(s)-r)
   261  }
   262  
   263  // swap swaps the contents of x and y. x and y must be equal length and disjoint.
   264  func swap[E any](x, y []E) {
   265  	for i := 0; i < len(x); i++ {
   266  		x[i], y[i] = y[i], x[i]
   267  	}
   268  }
   269  
   270  // overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap.
   271  func overlaps[E any](a, b []E) bool {
   272  	if len(a) == 0 || len(b) == 0 {
   273  		return false
   274  	}
   275  	elemSize := unsafe.Sizeof(a[0])
   276  	if elemSize == 0 {
   277  		return false
   278  	}
   279  	// TODO: use a runtime/unsafe facility once one becomes available. See issue 12445.
   280  	// Also see crypto/internal/alias/alias.go:AnyOverlap
   281  	return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) &&
   282  		uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1)
   283  }
   284  
   285  // startIdx returns the index in haystack where the needle starts.
   286  // prerequisite: the needle must be aliased entirely inside the haystack.
   287  func startIdx[E any](haystack, needle []E) int {
   288  	p := &needle[0]
   289  	for i := range haystack {
   290  		if p == &haystack[i] {
   291  			return i
   292  		}
   293  	}
   294  	// TODO: what if the overlap is by a non-integral number of Es?
   295  	assert.Throw("needle", "not", "found")
   296  	return -1
   297  }