Skip to content

Commit

Permalink
minor
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Jan 26, 2025
1 parent c1fd3be commit c76f541
Showing 1 changed file with 77 additions and 76 deletions.
153 changes: 77 additions & 76 deletions internal/sparse/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,71 @@ import (

// Array, a generic implementation of a sparse array
// with popcount compression and payload T.
//
// example:
// |
// v
// BitSet: [0|0|1|0|0|1|0|...] <- two bits set
// Items: [*|*] <- two slots populated
// ^
// |
//
// BitSet.Test(5): true
// BitSet.popcount(5): 2, for interval [0,5]
// BitSet.Rank0(5): 1, equal popcount - 1
type Array[T any] struct {
bitset.BitSet
Items []T
}

// Get the value at i from sparse array.
//
// example: Array.Get(5) -> Array.Items[1]
//
// ⬇
// BitSet: [0|0|1|0|0|1|0|1|...] <- 3 bits set
// Items: [*|*|*] <- len(Items) = 3
// ↑
//
// BitSet.Test(5): true
// BitSet.popcount(5): 2, for interval [0,5]
// BitSet.Rank0(5): 1, equal popcount(5)-1
func (s *Array[T]) Get(i uint) (val T, ok bool) {
if s.Test(i) {
return s.Items[s.Rank0(i)], true
}
return
}

// MustGet, use it only after a successful test
// or the behavior is undefined, maybe it panics.
func (s *Array[T]) MustGet(i uint) T {
return s.Items[s.Rank0(i)]
}

// UpdateAt or set the value at i via callback. The new value is returned
// and true if the val was already present.
func (s *Array[T]) UpdateAt(i uint, cb func(T, bool) T) (newValue T, wasPresent bool) {
var rank0 int

// if already set, get current value
var oldValue T

if wasPresent = s.Test(i); wasPresent {
rank0 = s.Rank0(i)
oldValue = s.Items[rank0]
}

// callback function to get updated or new value
newValue = cb(oldValue, wasPresent)

// already set, update and return value
if wasPresent {
s.Items[rank0] = newValue

return newValue, wasPresent
}

// new val, insert into bitset ...
s.BitSet = s.Set(i)

// bitset has changed, recalc rank
rank0 = s.Rank0(i)

// ... and insert value into slice
s.insertItem(rank0, newValue)

return newValue, wasPresent
}

// Len returns the number of items in sparse array.
func (s *Array[T]) Len() int {
return len(s.Items)
Expand Down Expand Up @@ -55,11 +103,10 @@ func (s *Array[T]) Copy() *Array[T] {

// InsertAt a value at i into the sparse array.
// If the value already exists, overwrite it with val and return true.
// The capacity is identical to the length after insertion.
func (s *Array[T]) InsertAt(i uint, val T) (exists bool) {
func (s *Array[T]) InsertAt(i uint, value T) (exists bool) {
// slot exists, overwrite val
if s.Len() != 0 && s.Test(i) {
s.Items[s.Rank0(i)] = val
s.Items[s.Rank0(i)] = value

return true
}
Expand All @@ -68,88 +115,40 @@ func (s *Array[T]) InsertAt(i uint, val T) (exists bool) {
s.BitSet = s.Set(i)

// ... and slice
s.insertItem(val, s.Rank0(i))
s.insertItem(s.Rank0(i), value)

return false
}

// DeleteAt a value at i from the sparse array, zeroes the tail.
func (s *Array[T]) DeleteAt(i uint) (val T, exists bool) {
func (s *Array[T]) DeleteAt(i uint) (value T, exists bool) {
if s.Len() == 0 || !s.Test(i) {
return
}

idx := s.Rank0(i)
val = s.Items[idx]
rank0 := s.Rank0(i)
value = s.Items[rank0]

// delete from slice
s.deleteItem(idx)
s.deleteItem(rank0)

// delete from bitset
s.BitSet = s.Clear(i)

return val, true
}

// Get the value at i from sparse array.
func (s *Array[T]) Get(i uint) (val T, ok bool) {
if s.Test(i) {
return s.Items[s.Rank0(i)], true
}
return
}

// MustGet, use it only after a successful test
// or the behavior is undefined, maybe it panics.
func (s *Array[T]) MustGet(i uint) T {
return s.Items[s.Rank0(i)]
}

// UpdateAt or set the value at i via callback. The new value is returned
// and true if the val was already present.
func (s *Array[T]) UpdateAt(i uint, cb func(T, bool) T) (newVal T, wasPresent bool) {
var idx int

// if already set, get current value
var oldVal T

if wasPresent = s.Test(i); wasPresent {
idx = s.Rank0(i)
oldVal = s.Items[idx]
}

// callback function to get updated or new value
newVal = cb(oldVal, wasPresent)

// already set, update and return value
if wasPresent {
s.Items[idx] = newVal

return newVal, wasPresent
}

// new val, insert into bitset ...
s.BitSet = s.Set(i)

// bitset has changed, recalc rank
idx = s.Rank0(i)

// ... and insert value into slice
s.insertItem(newVal, idx)

return newVal, wasPresent
return value, true
}

// insertItem inserts the item at index i, shift the rest one pos right
//
// It panics if i is out of range.
func (s *Array[T]) insertItem(item T, i int) {
func (s *Array[T]) insertItem(i int, item T) {
if len(s.Items) < cap(s.Items) {
s.Items = s.Items[:len(s.Items)+1] // fast resize, no alloc
} else {
var zero T
s.Items = append(s.Items, zero) // appends maybe more than just one item
s.Items = append(s.Items, zero) // append one item, mostly enlarge cap by more than one item
}

copy(s.Items[i+1:], s.Items[i:])
s.Items[i] = item
}
Expand All @@ -159,8 +158,10 @@ func (s *Array[T]) insertItem(item T, i int) {
// It panics if i is out of range.
func (s *Array[T]) deleteItem(i int) {
var zero T
l := len(s.Items) - 1 // new len
copy(s.Items[i:], s.Items[i+1:]) // overwrite s[i]
s.Items[l] = zero // clear the tail item
s.Items = s.Items[:l] // new len, cap is unchanged

nl := len(s.Items) - 1 // new len
copy(s.Items[i:], s.Items[i+1:]) // overwrite item at [i]

s.Items[nl] = zero // clear the tail item
s.Items = s.Items[:nl] // new len, keep cap is unchanged
}

0 comments on commit c76f541

Please sign in to comment.