Skip to content

Commit 35efa77

Browse files
randall77eric
authored andcommitted
slices: handle aliasing cases in Insert/Replace
Handle cases where the inserted slice is actually part of the slice that is being inserted into. Requires a bit more work, but no more allocations. (Compare to #494536.) Not entirely sure this is worth the complication. Fixes golang#60138 Change-Id: Ia72c872b04309b99025e6ca5a4a326ebed2abb69 Reviewed-on: https://go-review.googlesource.com/c/go/+/494817 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Reviewed-by: Keith Randall <[email protected]> Run-TryBot: Keith Randall <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
1 parent ec887ca commit 35efa77

File tree

3 files changed

+320
-22
lines changed

3 files changed

+320
-22
lines changed

src/go/build/deps_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@ var depsRules = `
4646
internal/goexperiment, internal/goos,
4747
internal/goversion, internal/nettrace, internal/platform,
4848
log/internal,
49-
maps, slices, unicode/utf8, unicode/utf16, unicode,
49+
maps, unicode/utf8, unicode/utf16, unicode,
5050
unsafe;
5151
52+
# slices depends on unsafe for overlapping check.
53+
unsafe
54+
< slices;
55+
5256
# These packages depend only on internal/goarch and unsafe.
5357
internal/goarch, unsafe
5458
< internal/abi;

src/slices/slices.go

Lines changed: 244 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
// Package slices defines various functions useful with slices of any type.
66
package slices
77

8+
import (
9+
"unsafe"
10+
)
11+
812
// Equal reports whether two slices are equal: the same length and all
913
// elements equal. If the lengths are different, Equal returns false.
1014
// 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 {
8185
// Insert panics if i is out of range.
8286
// This function is O(len(s) + len(v)).
8387
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])
88103
copy(s2[i:], v)
104+
copy(s2[i+m:], s[i:])
89105
return s2
90106
}
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
100165
}
101166

102167
// 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 {
143208
// modified slice. Replace panics if s[i:j] is not a valid slice of s.
144209
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
145210
_ = 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+
146219
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])
150224
copy(s2[i:], v)
225+
copy(s2[i+len(v):], s[j:])
151226
return s2
152227
}
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
158294
}
159295

160296
// Clone returns a copy of the slice.
@@ -224,3 +360,90 @@ func Grow[S ~[]E, E any](s S, n int) S {
224360
func Clip[S ~[]E, E any](s S) S {
225361
return s[:len(s):len(s)]
226362
}
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

Comments
 (0)