Skip to content

Commit 6dc2c41

Browse files
committed
index type
1 parent d851104 commit 6dc2c41

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

index/index.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package index
2+
3+
import "fmt"
4+
5+
type Integer interface {
6+
~int8 | ~int16 | ~int32 | ~uint8 | ~uint16 | ~uint32
7+
}
8+
9+
// Index provides a generic, ID-based lookup structure optimized for fast, contiguous access to values of type T by integer IDs.
10+
type Index[ID Integer, T any] struct {
11+
minID ID
12+
maxID ID
13+
values []T
14+
exists []bool
15+
count int
16+
}
17+
18+
// New creates a new Index with a range from minID to maxID (inclusive).
19+
func New[ID Integer, T any](minID, maxID ID) *Index[ID, T] {
20+
if minID > maxID {
21+
minID, maxID = maxID, minID
22+
}
23+
size := int(maxID - minID + 1)
24+
return &Index[ID, T]{
25+
minID: minID,
26+
maxID: maxID,
27+
values: make([]T, size),
28+
exists: make([]bool, size),
29+
}
30+
}
31+
32+
// GetMinID returns the minimum ID in the index.
33+
func (idx *Index[ID, T]) GetMinID() ID {
34+
return idx.minID
35+
}
36+
37+
// GetMaxID returns the maximum ID in the index.
38+
func (idx *Index[ID, T]) GetMaxID() ID {
39+
return idx.maxID
40+
}
41+
42+
// Set stores a value for the given ID and marks it as valid.
43+
func (idx *Index[ID, T]) Set(id ID, value T) error {
44+
if id < idx.minID || id > idx.maxID {
45+
return fmt.Errorf("ID %v out of range [%v, %v]", id, idx.minID, idx.maxID)
46+
}
47+
offset := int(id - idx.minID)
48+
if !idx.exists[offset] {
49+
idx.count++
50+
}
51+
idx.values[offset] = value
52+
idx.exists[offset] = true
53+
return nil
54+
}
55+
56+
// Get retrieves the value associated with the ID. Returns (value, true) if set, otherwise (zero, false).
57+
func (idx *Index[ID, T]) Get(id ID) (T, bool) {
58+
if id < idx.minID || id > idx.maxID {
59+
var zero T
60+
return zero, false
61+
}
62+
offset := int(id - idx.minID)
63+
return idx.values[offset], idx.exists[offset]
64+
}
65+
66+
// GetPtr retrieves pointer to the value associated with the ID. Returns *value if set, otherwise nil.
67+
func (idx *Index[ID, T]) GetPtr(id ID) *T {
68+
if id < idx.minID || id > idx.maxID {
69+
return nil
70+
}
71+
offset := int(id - idx.minID)
72+
if !idx.exists[offset] {
73+
return nil
74+
}
75+
return &idx.values[offset]
76+
}
77+
78+
// Cap returns the total number of possible values (capacity).
79+
func (idx *Index[ID, T]) Cap() int {
80+
return len(idx.values)
81+
}
82+
83+
// Len returns the number of actually set elements.
84+
func (idx *Index[ID, T]) Len() int {
85+
return idx.count
86+
}
87+
88+
// Contains returns true if value is set for a given ID.
89+
func (idx *Index[ID, T]) Contains(id ID) bool {
90+
if id < idx.minID || id > idx.maxID {
91+
return false
92+
}
93+
offset := int(id - idx.minID)
94+
return idx.exists[offset]
95+
}

tests/index_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package tests
2+
3+
import (
4+
"testing"
5+
6+
"github.com/jokruger/got/index"
7+
)
8+
9+
func TestIndex(t *testing.T) {
10+
t.Run("uint16", func(t *testing.T) {
11+
a := 1
12+
b := 10
13+
c := 20
14+
d := 30
15+
idx := index.New[uint16, int](uint16(a), uint16(d))
16+
for i := a; i <= b; i++ {
17+
idx.Set(uint16(i), i)
18+
}
19+
for i := c; i <= d; i++ {
20+
idx.Set(uint16(i), i)
21+
}
22+
23+
for i := -10; i < 40; i++ {
24+
if idx.Cap() != int(d-a+1) {
25+
t.Errorf("Expected index capacity to be %d, got %d", d-a+1, idx.Cap())
26+
}
27+
28+
if idx.Len() != (b-a+1)+(d-c+1) {
29+
t.Errorf("Expected index length to be %d, got %d", (b-a+1)+(d-c+1), idx.Len())
30+
}
31+
32+
if (i >= a && i <= b) || (i >= c && i <= d) {
33+
if !idx.Contains(uint16(i)) {
34+
t.Errorf("Expected index to contain %d, but it does not", i)
35+
}
36+
if v, ok := idx.Get(uint16(i)); !ok || v != i {
37+
t.Errorf("Expected index to return %d for %d, got %v (ok: %v)", i, i, v, ok)
38+
}
39+
} else {
40+
if idx.Contains(uint16(i)) {
41+
t.Errorf("Expected index to not contain %d, but it does", i)
42+
}
43+
if v, ok := idx.Get(uint16(i)); ok || v != 0 {
44+
t.Errorf("Expected index to return zero for %d, got %v (ok: %v)", i, v, ok)
45+
}
46+
}
47+
}
48+
})
49+
}

0 commit comments

Comments
 (0)