|
5 | 5 | // Package slices defines various functions useful with slices of any type.
|
6 | 6 | package slices
|
7 | 7 |
|
| 8 | +import ( |
| 9 | + "unsafe" |
| 10 | +) |
| 11 | + |
8 | 12 | // Equal reports whether two slices are equal: the same length and all
|
9 | 13 | // elements equal. If the lengths are different, Equal returns false.
|
10 | 14 | // Otherwise, the elements are compared in increasing index order, and the
|
@@ -81,22 +85,83 @@ func ContainsFunc[E any](s []E, f func(E) bool) bool {
|
81 | 85 | // Insert panics if i is out of range.
|
82 | 86 | // This function is O(len(s) + len(v)).
|
83 | 87 | func Insert[S ~[]E, E any](s S, i int, v ...E) S {
|
84 |
| - tot := len(s) + len(v) |
85 |
| - if tot <= cap(s) { |
86 |
| - s2 := s[:tot] |
87 |
| - copy(s2[i+len(v):], s[i:]) |
| 88 | + m := len(v) |
| 89 | + if m == 0 { |
| 90 | + return s |
| 91 | + } |
| 92 | + n := len(s) |
| 93 | + if i == n { |
| 94 | + return append(s, v...) |
| 95 | + } |
| 96 | + if n+m > cap(s) { |
| 97 | + // Use append rather than make so that we bump the size of |
| 98 | + // the slice up to the next storage class. |
| 99 | + // This is what Grow does but we don't call Grow because |
| 100 | + // that might copy the values twice. |
| 101 | + s2 := append(S(nil), make(S, n+m)...) |
| 102 | + copy(s2, s[:i]) |
88 | 103 | copy(s2[i:], v)
|
| 104 | + copy(s2[i+m:], s[i:]) |
89 | 105 | return s2
|
90 | 106 | }
|
91 |
| - // Use append rather than make so that we bump the size of |
92 |
| - // the slice up to the next storage class. |
93 |
| - // This is what Grow does but we don't call Grow because |
94 |
| - // that might copy the values twice. |
95 |
| - s2 := append(S(nil), make(S, tot)...) |
96 |
| - copy(s2, s[:i]) |
97 |
| - copy(s2[i:], v) |
98 |
| - copy(s2[i+len(v):], s[i:]) |
99 |
| - return s2 |
| 107 | + s = s[:n+m] |
| 108 | + |
| 109 | + // before: |
| 110 | + // s: aaaaaaaabbbbccccccccdddd |
| 111 | + // ^ ^ ^ ^ |
| 112 | + // i i+m n n+m |
| 113 | + // after: |
| 114 | + // s: aaaaaaaavvvvbbbbcccccccc |
| 115 | + // ^ ^ ^ ^ |
| 116 | + // i i+m n n+m |
| 117 | + // |
| 118 | + // a are the values that don't move in s. |
| 119 | + // v are the values copied in from v. |
| 120 | + // b and c are the values from s that are shifted up in index. |
| 121 | + // d are the values that get overwritten, never to be seen again. |
| 122 | + |
| 123 | + if !overlaps(v, s[i+m:]) { |
| 124 | + // Easy case - v does not overlap either the c or d regions. |
| 125 | + // (It might be in some of a or b, or elsewhere entirely.) |
| 126 | + // The data we copy up doesn't write to v at all, so just do it. |
| 127 | + |
| 128 | + copy(s[i+m:], s[i:]) |
| 129 | + |
| 130 | + // Now we have |
| 131 | + // s: aaaaaaaabbbbbbbbcccccccc |
| 132 | + // ^ ^ ^ ^ |
| 133 | + // i i+m n n+m |
| 134 | + // Note the b values are duplicated. |
| 135 | + |
| 136 | + copy(s[i:], v) |
| 137 | + |
| 138 | + // Now we have |
| 139 | + // s: aaaaaaaavvvvbbbbcccccccc |
| 140 | + // ^ ^ ^ ^ |
| 141 | + // i i+m n n+m |
| 142 | + // That's the result we want. |
| 143 | + return s |
| 144 | + } |
| 145 | + |
| 146 | + // The hard case - v overlaps c or d. We can't just shift up |
| 147 | + // the data because we'd move or clobber the values we're trying |
| 148 | + // to insert. |
| 149 | + // So instead, write v on top of d, then rotate. |
| 150 | + copy(s[n:], v) |
| 151 | + |
| 152 | + // Now we have |
| 153 | + // s: aaaaaaaabbbbccccccccvvvv |
| 154 | + // ^ ^ ^ ^ |
| 155 | + // i i+m n n+m |
| 156 | + |
| 157 | + rotateRight(s[i:], m) |
| 158 | + |
| 159 | + // Now we have |
| 160 | + // s: aaaaaaaavvvvbbbbcccccccc |
| 161 | + // ^ ^ ^ ^ |
| 162 | + // i i+m n n+m |
| 163 | + // That's the result we want. |
| 164 | + return s |
100 | 165 | }
|
101 | 166 |
|
102 | 167 | // Delete removes the elements s[i:j] from s, returning the modified slice.
|
@@ -143,18 +208,89 @@ func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
143 | 208 | // modified slice. Replace panics if s[i:j] is not a valid slice of s.
|
144 | 209 | func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
145 | 210 | _ = s[i:j] // verify that i:j is a valid subslice
|
| 211 | + |
| 212 | + if i == j { |
| 213 | + return Insert(s, i, v...) |
| 214 | + } |
| 215 | + if j == len(s) { |
| 216 | + return append(s[:i], v...) |
| 217 | + } |
| 218 | + |
146 | 219 | tot := len(s[:i]) + len(v) + len(s[j:])
|
147 |
| - if tot <= cap(s) { |
148 |
| - s2 := s[:tot] |
149 |
| - copy(s2[i+len(v):], s[j:]) |
| 220 | + if tot > cap(s) { |
| 221 | + // Too big to fit, allocate and copy over. |
| 222 | + s2 := append(S(nil), make(S, tot)...) // See Insert |
| 223 | + copy(s2, s[:i]) |
150 | 224 | copy(s2[i:], v)
|
| 225 | + copy(s2[i+len(v):], s[j:]) |
151 | 226 | return s2
|
152 | 227 | }
|
153 |
| - s2 := make(S, tot) |
154 |
| - copy(s2, s[:i]) |
155 |
| - copy(s2[i:], v) |
156 |
| - copy(s2[i+len(v):], s[j:]) |
157 |
| - return s2 |
| 228 | + |
| 229 | + r := s[:tot] |
| 230 | + |
| 231 | + if i+len(v) <= j { |
| 232 | + // Easy, as v fits in the deleted portion. |
| 233 | + copy(r[i:], v) |
| 234 | + if i+len(v) != j { |
| 235 | + copy(r[i+len(v):], s[j:]) |
| 236 | + } |
| 237 | + return r |
| 238 | + } |
| 239 | + |
| 240 | + // We are expanding (v is bigger than j-i). |
| 241 | + // The situation is something like this: |
| 242 | + // (example has i=4,j=8,len(s)=16,len(v)=6) |
| 243 | + // s: aaaaxxxxbbbbbbbbyy |
| 244 | + // ^ ^ ^ ^ |
| 245 | + // i j len(s) tot |
| 246 | + // a: prefix of s |
| 247 | + // x: deleted range |
| 248 | + // b: more of s |
| 249 | + // y: area to expand into |
| 250 | + |
| 251 | + if !overlaps(r[i+len(v):], v) { |
| 252 | + // Easy, as v is not clobbered by the first copy. |
| 253 | + copy(r[i+len(v):], s[j:]) |
| 254 | + copy(r[i:], v) |
| 255 | + return r |
| 256 | + } |
| 257 | + |
| 258 | + // This is a situation where we don't have a single place to which |
| 259 | + // we can copy v. Parts of it need to go to two different places. |
| 260 | + // We want to copy the prefix of v into y and the suffix into x, then |
| 261 | + // rotate |y| spots to the right. |
| 262 | + // |
| 263 | + // v[2:] v[:2] |
| 264 | + // | | |
| 265 | + // s: aaaavvvvbbbbbbbbvv |
| 266 | + // ^ ^ ^ ^ |
| 267 | + // i j len(s) tot |
| 268 | + // |
| 269 | + // If either of those two destinations don't alias v, then we're good. |
| 270 | + y := len(v) - (j - i) // length of y portion |
| 271 | + |
| 272 | + if !overlaps(r[i:j], v) { |
| 273 | + copy(r[i:j], v[y:]) |
| 274 | + copy(r[len(s):], v[:y]) |
| 275 | + rotateRight(r[i:], y) |
| 276 | + return r |
| 277 | + } |
| 278 | + if !overlaps(r[len(s):], v) { |
| 279 | + copy(r[len(s):], v[:y]) |
| 280 | + copy(r[i:j], v[y:]) |
| 281 | + rotateRight(r[i:], y) |
| 282 | + return r |
| 283 | + } |
| 284 | + |
| 285 | + // Now we know that v overlaps both x and y. |
| 286 | + // That means that the entirety of b is *inside* v. |
| 287 | + // So we don't need to preserve b at all; instead we |
| 288 | + // can copy v first, then copy the b part of v out of |
| 289 | + // v to the right destination. |
| 290 | + k := startIdx(v, s[j:]) |
| 291 | + copy(r[i:], v) |
| 292 | + copy(r[i+len(v):], r[i+k:]) |
| 293 | + return r |
158 | 294 | }
|
159 | 295 |
|
160 | 296 | // Clone returns a copy of the slice.
|
@@ -224,3 +360,90 @@ func Grow[S ~[]E, E any](s S, n int) S {
|
224 | 360 | func Clip[S ~[]E, E any](s S) S {
|
225 | 361 | return s[:len(s):len(s)]
|
226 | 362 | }
|
| 363 | + |
| 364 | +// Rotation algorithm explanation: |
| 365 | +// |
| 366 | +// rotate left by 2 |
| 367 | +// start with |
| 368 | +// 0123456789 |
| 369 | +// split up like this |
| 370 | +// 01 234567 89 |
| 371 | +// swap first 2 and last 2 |
| 372 | +// 89 234567 01 |
| 373 | +// join first parts |
| 374 | +// 89234567 01 |
| 375 | +// recursively rotate first left part by 2 |
| 376 | +// 23456789 01 |
| 377 | +// join at the end |
| 378 | +// 2345678901 |
| 379 | +// |
| 380 | +// rotate left by 8 |
| 381 | +// start with |
| 382 | +// 0123456789 |
| 383 | +// split up like this |
| 384 | +// 01 234567 89 |
| 385 | +// swap first 2 and last 2 |
| 386 | +// 89 234567 01 |
| 387 | +// join last parts |
| 388 | +// 89 23456701 |
| 389 | +// recursively rotate second part left by 6 |
| 390 | +// 89 01234567 |
| 391 | +// join at the end |
| 392 | +// 8901234567 |
| 393 | + |
| 394 | +// TODO: There are other rotate algorithms. |
| 395 | +// This algorithm has the desirable property that it moves each element exactly twice. |
| 396 | +// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes. |
| 397 | +// The follow-cycles algorithm can be 1-write but it is not very cache friendly. |
| 398 | + |
| 399 | +// rotateLeft rotates b left by n spaces. |
| 400 | +// s_final[i] = s_orig[i+r], wrapping around. |
| 401 | +func rotateLeft[S ~[]E, E any](s S, r int) { |
| 402 | + for r != 0 && r != len(s) { |
| 403 | + if r*2 <= len(s) { |
| 404 | + swap(s[:r], s[len(s)-r:]) |
| 405 | + s = s[:len(s)-r] |
| 406 | + } else { |
| 407 | + swap(s[:len(s)-r], s[r:]) |
| 408 | + s, r = s[len(s)-r:], r*2-len(s) |
| 409 | + } |
| 410 | + } |
| 411 | +} |
| 412 | +func rotateRight[S ~[]E, E any](s S, r int) { |
| 413 | + rotateLeft(s, len(s)-r) |
| 414 | +} |
| 415 | + |
| 416 | +// swap swaps the contents of x and y. x and y must be equal length and disjoint. |
| 417 | +func swap[S ~[]E, E any](x, y S) { |
| 418 | + for i := 0; i < len(x); i++ { |
| 419 | + x[i], y[i] = y[i], x[i] |
| 420 | + } |
| 421 | +} |
| 422 | + |
| 423 | +// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap. |
| 424 | +func overlaps[S ~[]E, E any](a, b S) bool { |
| 425 | + if len(a) == 0 || len(b) == 0 { |
| 426 | + return false |
| 427 | + } |
| 428 | + elemSize := unsafe.Sizeof(a[0]) |
| 429 | + if elemSize == 0 { |
| 430 | + return false |
| 431 | + } |
| 432 | + // TODO: use a runtime/unsafe facility once one becomes available. See issue 12445. |
| 433 | + // Also see crypto/internal/alias/alias.go:AnyOverlap |
| 434 | + return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) && |
| 435 | + uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1) |
| 436 | +} |
| 437 | + |
| 438 | +// startIdx returns the index in haystack where the needle starts. |
| 439 | +// prerequisite: the needle must be aliased entirely inside the haystack. |
| 440 | +func startIdx[S ~[]E, E any](haystack, needle S) int { |
| 441 | + p := &needle[0] |
| 442 | + for i := range haystack { |
| 443 | + if p == &haystack[i] { |
| 444 | + return i |
| 445 | + } |
| 446 | + } |
| 447 | + // TODO: what if the overlap is by a non-integral number of Es? |
| 448 | + panic("needle not found") |
| 449 | +} |
0 commit comments