CardSimEngine/cardsim/sliceutil.go
Kistaro Windrider 20561c574c
sliceutil: helpers for mid-slice insert/delete.
Manipulating the hand, deck, etc. is going to use these operations a lot.
2023-04-01 18:49:06 -07:00

101 lines
2.8 KiB
Go

package cardsim
import (
"fmt"
)
// InsertInto inserts one or more items into a slice at an arbitrary index.
// Items already in the slice past the target position move to later positons.
//
// Like `append`, this may move the underlying array and it produces a new
// slice header (under the hood, it uses `append`). It returns the new slice
// (the original is in an undefined state and should no longer be used).
//
// If loc is negative or more than one past the end of T, Insert panics.
func InsertInto[T any](slice []T, loc int, elements ...T) []T {
if loc < 0 || loc > len(slice) {
panic(fmt.Sprintf("can't Insert at location %d in %d-element slice", loc, len(slice)))
}
// is this a no-op?
if len(elements) == 0 {
return slice
}
// is this just an append?
if loc == len(slice) {
return append(slice, elements...)
}
offset := len(elements)
oldLen := len(slice)
newSize := oldLen + offset
if newSize <= cap(slice) {
// We can reslice in place.
slice = slice[:newSize]
// Scoot trailing to their new positions.
copy(slice[loc+offset:], slice[loc:oldLen])
// Insert the new elements.
copy(slice[loc:], elements)
return slice
}
// Reallocate. Do the normal thing of doubling the size as a minimum
// when increasing space for a dynamic array; this amortizes the
// cost of repeatedly reallocating and moving the slice.
newCap := cap(slice) * 2
if newCap < newSize {
newCap = newSize
}
newSlice := make([]T, newSize, newCap)
if loc > 0 {
copy(newSlice, slice[0:loc])
}
copy(newSlice[loc:], elements)
copy(newSlice[loc+offset:], slice[loc:])
return newSlice
}
// DeleteFrom deletes an item from a slice at an arbitrary index. Items after it
// scoot up to close the gap. This returns the modified slice (like Append).
//
// If the provided location is not a valid location in the slice, this panics.
func DeleteFrom[T any](slice []T, loc int) []T {
return DeleteNFrom(slice, loc, 1)
}
// DeleteNFrom deletes N items from a slice at an arbitrary index. Items after
// it scoot up to close the gap. This returns the modified slice (like Append).
//
// If the range of items that would be deleted is not entirely valid within the
// slice, this panics.
func DeleteNFrom[T any](slice []T, loc, n int) []T {
if loc < 0 || loc+n > len(slice) {
panic(fmt.Sprintf("can't delete %d elements from a %d-element slice at location %d", n, len(slice), loc))
}
// Easy cases.
if n == 0 {
return slice
}
if loc == 0 {
return slice[n:]
}
if loc+n == len(slice) {
return slice[0:loc]
}
// Is it shorter to move up or move down?
if len(slice)-loc-n > loc {
// Move forward -- the end is big.
copy(slice[n:], slice[:loc])
return slice[n:]
}
// Move backward -- the beginnng is big or they're the same size
// (and moving backwards preserves more usable append capacity later).
copy(slice[loc:], slice[loc+n:])
return slice[:len(slice)-n]
}