This commit is contained in:
Frank Mayer 2025-02-13 04:58:27 +00:00
commit f98fc53cf0
3 changed files with 131 additions and 0 deletions

9
README.md Normal file
View File

@ -0,0 +1,9 @@
# Speicher
WORK IN PROGRESS
Pronunciation: [[ˈʃpaɪ̯çɐ]](https://upload.wikimedia.org/wikipedia/commons/2/21/De-speicher.ogg)
Speicher is a lightweight, data store.
![](https://i.imgflip.com/9f9pu3.jpg)

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module speicher
go 1.23

119
map.go Normal file
View File

@ -0,0 +1,119 @@
package speicher
import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"sync"
)
type (
MemoryMap[T any] struct {
data map[string]T
location string
mut sync.RWMutex
}
Map[T any] interface {
Get(key string) (T, bool)
Has(key string) bool
Set(key string, value T)
RangeKV() <-chan MapRangeEl[T]
RangeV() <-chan T
Save() error
Lock()
Unlock()
RLock()
RUnlock()
}
MapRangeEl[T any] struct {
Key string
Value T
}
)
func (m *MemoryMap[T]) Get(key string) (value T, found bool) {
value, found = m.data[key]
return
}
func (m *MemoryMap[T]) Has(key string) bool {
_, ok := m.data[key]
return ok
}
func (m *MemoryMap[T]) Set(key string, value T) {
m.data[key] = value
}
func (m *MemoryMap[T]) RangeKV() <-chan MapRangeEl[T] {
ch := make(chan MapRangeEl[T])
go func() {
defer close(ch)
for key, value := range m.data {
ch <- MapRangeEl[T]{Key: key, Value: value}
}
}()
return ch
}
func (m *MemoryMap[T]) RangeV() <-chan T {
ch := make(chan T)
go func() {
defer close(ch)
for _, value := range m.data {
ch <- value
}
}()
return ch
}
func (m *MemoryMap[T]) Lock() {
m.mut.Lock()
}
func (m *MemoryMap[T]) Unlock() {
m.mut.Unlock()
_ = m.Save()
}
func (m *MemoryMap[T]) RLock() {
m.mut.RLock()
}
func (m *MemoryMap[T]) RUnlock() {
m.mut.RUnlock()
}
func (m *MemoryMap[T]) Save() error {
f, err := os.OpenFile(m.location, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return errors.Join(fmt.Errorf("failed to open file %s", m.location), err)
}
encoder := json.NewEncoder(f)
if err := encoder.Encode(m.data); err != nil {
return errors.Join(fmt.Errorf("failed to encode json file %s", m.location), err)
}
return nil
}
func LoadMap[T any](location string) (Map[T], error) {
if strings.HasSuffix(location, ".json") {
return loadMapFromJsonFile[T](location)
}
return nil, fmt.Errorf("unable to find loader for %s", location)
}
func loadMapFromJsonFile[T any](location string) (Map[T], error) {
f, err := os.OpenFile(location, os.O_RDONLY, 0644)
if err != nil {
return nil, errors.Join(fmt.Errorf("failed to open file %s", location), err)
}
decoder := json.NewDecoder(f)
m := &MemoryMap[T]{}
if err := decoder.Decode(&m.data); err != nil {
return nil, errors.Join(fmt.Errorf("failed to decode json file %s", location), err)
}
return m, nil
}