diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
| commit | 20ef0d92694465ac86b550df139e8366a0a2b4fa (patch) | |
| tree | 3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/authzed/spicedb/pkg/genutil | |
| parent | 44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff) | |
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/genutil')
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 +} |
