summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/pkg/genutil
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
committermo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
commit20ef0d92694465ac86b550df139e8366a0a2b4fa (patch)
tree3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/authzed/spicedb/pkg/genutil
parent44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff)
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/genutil')
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/genutil/doc.go2
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/genutil/ensure.go34
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/genutil/mapz/countingmap.go50
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/genutil/mapz/multimap.go181
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/genutil/mapz/set.go163
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/genutil/slicez/chunking.go44
6 files changed, 474 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/pkg/genutil/doc.go b/vendor/github.com/authzed/spicedb/pkg/genutil/doc.go
new file mode 100644
index 0000000..eb91f91
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/genutil/doc.go
@@ -0,0 +1,2 @@
+// Package genutil contains helper functions to deal with generic data (e.g. maps and sets).
+package genutil
diff --git a/vendor/github.com/authzed/spicedb/pkg/genutil/ensure.go b/vendor/github.com/authzed/spicedb/pkg/genutil/ensure.go
new file mode 100644
index 0000000..56467ba
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/genutil/ensure.go
@@ -0,0 +1,34 @@
+package genutil
+
+import (
+ "github.com/ccoveille/go-safecast"
+
+ "github.com/authzed/spicedb/pkg/spiceerrors"
+)
+
+// MustEnsureUInt32 is a helper function that calls EnsureUInt32 and panics on error.
+func MustEnsureUInt32(value int) uint32 {
+ ret, err := EnsureUInt32(value)
+ if err != nil {
+ panic(err)
+ }
+ return ret
+}
+
+// EnsureUInt32 ensures that the specified value can be represented as a uint32.
+func EnsureUInt32(value int) (uint32, error) {
+ uint32Value, err := safecast.ToUint32(value)
+ if err != nil {
+ return 0, spiceerrors.MustBugf("specified value could not be cast to a uint32")
+ }
+ return uint32Value, nil
+}
+
+// EnsureUInt8 ensures that the specified value can be represented as a uint8.
+func EnsureUInt8(value int) (uint8, error) {
+ uint8Value, err := safecast.ToUint8(value)
+ if err != nil {
+ return 0, spiceerrors.MustBugf("specified value could not be cast to a uint8")
+ }
+ return uint8Value, nil
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/countingmap.go b/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/countingmap.go
new file mode 100644
index 0000000..e367fce
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/countingmap.go
@@ -0,0 +1,50 @@
+package mapz
+
+import "sync"
+
+// CountingMultiMap is a multimap that counts the number of distinct values for each
+// key, removing the key from the map when the count reaches zero. Safe for concurrent
+// use.
+type CountingMultiMap[T comparable, Q comparable] struct {
+ valuesByKey map[T]*Set[Q] // GUARDED_BY(lock)
+ lock sync.Mutex
+}
+
+// NewCountingMultiMap constructs a new counting multimap.
+func NewCountingMultiMap[T comparable, Q comparable]() *CountingMultiMap[T, Q] {
+ return &CountingMultiMap[T, Q]{
+ valuesByKey: map[T]*Set[Q]{},
+ lock: sync.Mutex{},
+ }
+}
+
+// Add adds the given value to the map at the given key. Returns true if the value
+// already existed in the map for the given key.
+func (cmm *CountingMultiMap[T, Q]) Add(key T, value Q) bool {
+ cmm.lock.Lock()
+ defer cmm.lock.Unlock()
+
+ values, ok := cmm.valuesByKey[key]
+ if !ok {
+ values = NewSet[Q]()
+ cmm.valuesByKey[key] = values
+ }
+ return !values.Add(value)
+}
+
+// Remove removes the given value for the given key from the map. If, after this removal,
+// the key has no additional values, it is removed entirely from the map.
+func (cmm *CountingMultiMap[T, Q]) Remove(key T, value Q) {
+ cmm.lock.Lock()
+ defer cmm.lock.Unlock()
+
+ values, ok := cmm.valuesByKey[key]
+ if !ok {
+ return
+ }
+
+ values.Delete(value)
+ if values.IsEmpty() {
+ delete(cmm.valuesByKey, key)
+ }
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/multimap.go b/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/multimap.go
new file mode 100644
index 0000000..bc85f32
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/multimap.go
@@ -0,0 +1,181 @@
+package mapz
+
+import (
+ "golang.org/x/exp/maps"
+)
+
+// ReadOnlyMultimap is a read-only multimap.
+type ReadOnlyMultimap[T comparable, Q any] interface {
+ // Has returns true if the key is found in the map.
+ Has(key T) bool
+
+ // Get returns the values for the given key in the map and whether the key
+ // existed.
+ // If the key does not exist, an empty slice is returned.
+ Get(key T) ([]Q, bool)
+
+ // IsEmpty returns true if the map is currently empty.
+ IsEmpty() bool
+
+ // Len returns the length of the map, e.g. the number of *keys* present.
+ Len() int
+
+ // Keys returns the keys of the map.
+ Keys() []T
+
+ // Values returns all values in the map.
+ Values() []Q
+}
+
+// NewMultiMap initializes a new MultiMap.
+func NewMultiMap[T comparable, Q any]() *MultiMap[T, Q] {
+ return &MultiMap[T, Q]{items: map[T][]Q{}}
+}
+
+// NewMultiMapWithCap initializes with the provided capacity for the top-level
+// map.
+func NewMultiMapWithCap[T comparable, Q any](capacity uint32) *MultiMap[T, Q] {
+ return &MultiMap[T, Q]{items: make(map[T][]Q, capacity)}
+}
+
+// MultiMap represents a map that can contain 1 or more values for each key.
+type MultiMap[T comparable, Q any] struct {
+ items map[T][]Q
+}
+
+// Clear clears all entries in the map.
+func (mm *MultiMap[T, Q]) Clear() {
+ mm.items = map[T][]Q{}
+}
+
+// Add inserts the value into the map at the given key.
+//
+// If there exists an existing value, then this value is appended
+// *without comparison*. Put another way, a value can be added twice, if this
+// method is called twice for the same value.
+func (mm *MultiMap[T, Q]) Add(key T, item Q) {
+ if _, ok := mm.items[key]; !ok {
+ mm.items[key] = []Q{}
+ }
+
+ mm.items[key] = append(mm.items[key], item)
+}
+
+// RemoveKey removes the given key from the map.
+func (mm *MultiMap[T, Q]) RemoveKey(key T) {
+ delete(mm.items, key)
+}
+
+// Has returns true if the key is found in the map.
+func (mm *MultiMap[T, Q]) Has(key T) bool {
+ _, ok := mm.items[key]
+ return ok
+}
+
+// Get returns the values stored in the map for the provided key and whether
+// the key existed.
+//
+// If the key does not exist, an empty slice is returned.
+func (mm *MultiMap[T, Q]) Get(key T) ([]Q, bool) {
+ found, ok := mm.items[key]
+ if !ok {
+ return []Q{}, false
+ }
+
+ return found, true
+}
+
+// Sets sets the values in the multimap to those provided.
+func (mm *MultiMap[T, Q]) Set(key T, values []Q) {
+ mm.items[key] = values
+}
+
+// IsEmpty returns true if the map is currently empty.
+func (mm *MultiMap[T, Q]) IsEmpty() bool { return len(mm.items) == 0 }
+
+// Len returns the length of the map, e.g. the number of *keys* present.
+func (mm *MultiMap[T, Q]) Len() int { return len(mm.items) }
+
+// Keys returns the keys of the map.
+func (mm *MultiMap[T, Q]) Keys() []T { return maps.Keys(mm.items) }
+
+// Values returns all values in the map.
+func (mm MultiMap[T, Q]) Values() []Q {
+ values := make([]Q, 0, len(mm.items)*2)
+ for _, valueSlice := range maps.Values(mm.items) {
+ values = append(values, valueSlice...)
+ }
+ return values
+}
+
+// Clone returns a clone of the map.
+func (mm *MultiMap[T, Q]) Clone() *MultiMap[T, Q] {
+ return &MultiMap[T, Q]{maps.Clone(mm.items)}
+}
+
+// CountOf returns the number of values stored for the given key.
+func (mm *MultiMap[T, Q]) CountOf(key T) int {
+ return len(mm.items[key])
+}
+
+// IndexOfValueInMultimap returns the index of the value in the map for the given key.
+func IndexOfValueInMultimap[T comparable, Q comparable](mm *MultiMap[T, Q], key T, value Q) int {
+ values, ok := mm.items[key]
+ if !ok {
+ return -1
+ }
+
+ for i, v := range values {
+ if v == value {
+ return i
+ }
+ }
+
+ return -1
+}
+
+// AsReadOnly returns a read-only *copy* of the mulitmap.
+func (mm *MultiMap[T, Q]) AsReadOnly() ReadOnlyMultimap[T, Q] {
+ return readOnlyMultimap[T, Q]{
+ maps.Clone(mm.items),
+ }
+}
+
+type readOnlyMultimap[T comparable, Q any] struct {
+ items map[T][]Q
+}
+
+// Has returns true if the key is found in the map.
+func (mm readOnlyMultimap[T, Q]) Has(key T) bool {
+ _, ok := mm.items[key]
+ return ok
+}
+
+// Get returns the values for the given key in the map and whether the key existed. If the key
+// does not exist, an empty slice is returned.
+func (mm readOnlyMultimap[T, Q]) Get(key T) ([]Q, bool) {
+ found, ok := mm.items[key]
+ if !ok {
+ return []Q{}, false
+ }
+
+ return found, true
+}
+
+// IsEmpty returns true if the map is currently empty.
+func (mm readOnlyMultimap[T, Q]) IsEmpty() bool { return len(mm.items) == 0 }
+
+// Len returns the length of the map, e.g. the number of *keys* present.
+func (mm readOnlyMultimap[T, Q]) Len() int { return len(mm.items) }
+
+// Keys returns the keys of the map.
+func (mm readOnlyMultimap[T, Q]) Keys() []T { return maps.Keys(mm.items) }
+
+// Values returns all values in the map.
+func (mm readOnlyMultimap[T, Q]) Values() []Q {
+ values := make([]Q, 0, len(mm.items)*2)
+ for _, valueSlice := range maps.Values(mm.items) {
+ values = append(values, valueSlice...)
+ }
+ return values
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/set.go b/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/set.go
new file mode 100644
index 0000000..13fb55a
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/genutil/mapz/set.go
@@ -0,0 +1,163 @@
+package mapz
+
+import (
+ "maps"
+
+ "github.com/rs/zerolog"
+
+ expmaps "golang.org/x/exp/maps"
+)
+
+// Set implements a very basic generic set.
+type Set[T comparable] struct {
+ values map[T]struct{}
+}
+
+// NewSet returns a new set.
+func NewSet[T comparable](items ...T) *Set[T] {
+ s := &Set[T]{
+ values: map[T]struct{}{},
+ }
+ for _, item := range items {
+ s.values[item] = struct{}{}
+ }
+ return s
+}
+
+// Has returns true if the set contains the given value.
+func (s *Set[T]) Has(value T) bool {
+ _, exists := s.values[value]
+ return exists
+}
+
+// Add adds the given value to the set and returns true. If
+// the value is already present, returns false.
+func (s *Set[T]) Add(value T) bool {
+ if s.Has(value) {
+ return false
+ }
+
+ s.values[value] = struct{}{}
+ return true
+}
+
+// Insert adds the given value to the set.
+func (s *Set[T]) Insert(value T) {
+ s.values[value] = struct{}{}
+}
+
+// Delete removes the value from the set, returning nothing.
+func (s *Set[T]) Delete(value T) {
+ delete(s.values, value)
+}
+
+// Extend adds all the values to the set.
+func (s *Set[T]) Extend(values []T) {
+ for _, value := range values {
+ s.values[value] = struct{}{}
+ }
+}
+
+// Merge adds all the values from the other set to this set.
+func (s *Set[T]) Merge(other *Set[T]) {
+ for value := range other.values {
+ s.values[value] = struct{}{}
+ }
+}
+
+// Union adds all the values from the other set to this set,
+// returning a new set.
+func (s *Set[T]) Union(other *Set[T]) *Set[T] {
+ cpy := s.Copy()
+ for value := range other.values {
+ cpy.values[value] = struct{}{}
+ }
+ return cpy
+}
+
+// IntersectionDifference removes any values from this set that
+// are not shared with the other set. Returns the same set.
+func (s *Set[T]) IntersectionDifference(other *Set[T]) *Set[T] {
+ for value := range s.values {
+ if !other.Has(value) {
+ delete(s.values, value)
+ }
+ }
+ return s
+}
+
+// RemoveAll removes all values from this set found in the other set.
+func (s *Set[T]) RemoveAll(other *Set[T]) {
+ for value := range other.values {
+ delete(s.values, value)
+ }
+}
+
+// Subtract subtracts the other set from this set, returning a new set.
+func (s *Set[T]) Subtract(other *Set[T]) *Set[T] {
+ cpy := s.Copy()
+ cpy.RemoveAll(other)
+ return cpy
+}
+
+// Copy returns a copy of this set.
+func (s *Set[T]) Copy() *Set[T] {
+ return &Set[T]{
+ values: maps.Clone(s.values),
+ }
+}
+
+// Intersect removes any values from this set that
+// are not shared with the other set, returning a new set.
+func (s *Set[T]) Intersect(other *Set[T]) *Set[T] {
+ cpy := s.Copy()
+ for value := range cpy.values {
+ if !other.Has(value) {
+ delete(cpy.values, value)
+ }
+ }
+ return cpy
+}
+
+// Equal returns true if both sets have the same elements
+func (s *Set[T]) Equal(other *Set[T]) bool {
+ return maps.Equal(s.values, other.values)
+}
+
+// IsEmpty returns true if the set is empty.
+func (s *Set[T]) IsEmpty() bool {
+ return len(s.values) == 0
+}
+
+// AsSlice returns the set as a slice of values.
+func (s *Set[T]) AsSlice() []T {
+ if len(s.values) == 0 {
+ return nil
+ }
+
+ return expmaps.Keys(s.values)
+}
+
+// Len returns the length of the set.
+func (s *Set[T]) Len() int {
+ return len(s.values)
+}
+
+// ForEach executes the callback for each item in the set until an error is encountered.
+func (s *Set[T]) ForEach(callback func(value T) error) error {
+ for value := range s.values {
+ if err := callback(value); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (s *Set[T]) MarshalZerologObject(e *zerolog.Event) {
+ xs := zerolog.Arr()
+ for _, value := range s.values {
+ xs.Interface(value)
+ }
+ e.Array("values", xs)
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/genutil/slicez/chunking.go b/vendor/github.com/authzed/spicedb/pkg/genutil/slicez/chunking.go
new file mode 100644
index 0000000..bdff41c
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/genutil/slicez/chunking.go
@@ -0,0 +1,44 @@
+package slicez
+
+import (
+ "github.com/authzed/spicedb/internal/logging"
+)
+
+// ForEachChunk executes the given handler for each chunk of items in the slice.
+func ForEachChunk[T any](data []T, chunkSize uint16, handler func(items []T)) {
+ _, _ = ForEachChunkUntil(data, chunkSize, func(items []T) (bool, error) {
+ handler(items)
+ return true, nil
+ })
+}
+
+func ForEachChunkUntil[T any](data []T, chunkSize uint16, handler func(items []T) (bool, error)) (bool, error) {
+ if chunkSize == 0 {
+ logging.Warn().Int("invalid-chunk-size", int(chunkSize)).Msg("ForEachChunk got an invalid chunk size; defaulting to 100")
+ chunkSize = 100
+ }
+
+ dataLength := uint64(len(data))
+ chunkSize64 := uint64(chunkSize)
+ chunkCount := (dataLength / chunkSize64) + 1
+ for chunkIndex := uint64(0); chunkIndex < chunkCount; chunkIndex++ {
+ chunkStart := chunkIndex * chunkSize64
+ chunkEnd := (chunkIndex + 1) * chunkSize64
+ if chunkEnd > dataLength {
+ chunkEnd = dataLength
+ }
+
+ chunk := data[chunkStart:chunkEnd]
+ if len(chunk) > 0 {
+ ok, err := handler(chunk)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return ok, nil
+ }
+ }
+ }
+
+ return true, nil
+}