tinydb

package module
v0.0.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 22, 2025 License: MIT Imports: 1 Imported by: 0

README

TinyDB

Project Badges

TinyDB is a TinyGo-compatible, minimal key–value store for string keys and values. It intentionally provides a very small API and a pluggable storage backend so it can be embedded into small or resource-constrained projects.

Summary

  • Purpose: store and retrieve string key/value pairs persistently with a tiny, dependency-free runtime.
  • Target: TinyGo and small Go programs where avoiding heavy runtime features (JSON, reflection, fmt) is desirable.
  • Persistence: delegated to a user-provided Store implementation.

Key features

  • Minimal public API: only Get and Set operations.
  • Keys and values are plain string types.
  • Pluggable storage via the Store interface (file, memory, remote object store, etc.).
  • Simple on-disk/text format: one key=value entry per line.
  • TinyGo friendly: no use of reflection or encoding/json in the core.

Quick start

  1. Implement the Store interface for your persistence layer (file, in-memory, S3, etc.).
  2. Create a database instance with tinydb.New(name, logger, store).
  3. Use Get(key) and Set(key, value) to read and write values.

Example minimal flow:

  • Implement Store
  • New DB: db, err := tinydb.New("mydb.tdb", logger, store)
  • Set: db.Set("foo", "bar")
  • Get: val, err := db.Get("foo")

API contract (concise)

  • Inputs: key and value are string.
  • Outputs: Get returns (string, error). Set returns error.
  • Persistence: handled entirely by the Store implementation.
  • Failure modes: I/O errors or backend errors are returned as error from the public methods.

Edge cases to consider:

  • Missing key: Get returns "" and a non-nil error.
  • Backend I/O failure: propagated as an error.
  • Values containing newlines or = characters: the canonical on-disk format is key=value per line; avoid storing raw newlines in values.

Interfaces (example)

The public interfaces look like this:

type KVStore interface {
    Get(key string) (string, error)
    Set(key, value string) error
}

// Store abstracts the persistence backend used by tinydb.
type Store interface {
    // GetFile returns the full contents of a named file or an error.
    GetFile(filePath string) ([]byte, error)

    // SetFile replaces the file contents with the provided data.
    SetFile(filePath string, data []byte) error

    // AddToFile appends the provided data to the named file.
    // Used by tinydb when adding a new key/value pair to avoid
    // rewriting the entire backing store for each insert.
    AddToFile(filePath string, data []byte) error
}

Constructor example:

db, err := tinydb.New("mydb.tdb", logger, store)
  • name (string): logical database name; commonly a file path used by the Store implementation.
  • logger (io.Writer): optional logging target; may be os.Stdout or nil.
  • store (Store): required backend implementation.
  • name (string): logical database name; commonly a file path used by the Store implementation.
  • logger (tinydb.LoggerFunc, signature func(...any)): optional logger function; may be a wrapper that writes to os.Stdout or nil.
  • store (Store): required backend implementation.

Minimal example (file-backed store)

package main

import (
    "os"
    "github.com/cdvelop/tinydb"
)

type FileStore struct{}

func (fs FileStore) GetFile(path string) ([]byte, error) {
    return os.ReadFile(path)
}

func (fs FileStore) SetFile(path string, data []byte) error {
    return os.WriteFile(path, data, 0644)
}

// AddToFile appends bytes to the end of the named file. This is used by
// tinydb when inserting new key/value pairs to avoid rewriting the whole
// store on every insert.
func (fs FileStore) AddToFile(path string, data []byte) error {
    f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer f.Close()
    _, err = f.Write(data)
    return err
}

func main() {
    // Simple LoggerFunc using only the built-in print/println helpers
    logger := func(args ...any) {
        for i, a := range args {
            if i > 0 {
                print(" ")
            }
            print(a)
        }
        println()
    }
    db, err := tinydb.New("settings.tdb", logger, FileStore{})
    if err != nil {
        panic(err)
    }

    if err := db.Set("username", "cesar"); err != nil {
        panic(err)
    }

    val, err := db.Get("theme")
    if err != nil {
        // key missing or other error
        println("error getting key:", err.Error())
        return
    }
    println("theme:", val)
}

Storage format

By default tinydb uses a simple text representation: one key=value per line.

Example file contents:

username=cesar
theme=dark
window=1024x768

Notes:

  • Avoid embedding newlines in values; the implementation expects single-line entries.
  • If your values may contain = or newline characters, use an encoding strategy in your Store or pre-encode values before calling Set.

Testing suggestions

  • Unit test the Store implementations (file, memory, mocks) independently.
  • Add tests for basic Set/Get behaviour and for recovery after failures (simulate I/O errors).

Benchmarks ⚡️

Run on: Linux (11th Gen Intel i7-11800H @ 2.30GHz)

The following micro-benchmark measures allocations and time for repeated Set operations.

Test ns/op B/op allocs/op
BenchmarkSetAlloc-16 96,493 364 5

Quick notes:

  • 🟢 ns/op — lower is better (time per operation).
  • 🟢 B/op and allocs/op — lower is better (memory allocations per operation).
  • 🟡 Result: reasonable for a small in-memory store; low allocations but non-negligible per-op cost.
  • 💡 Suggestion: consider reusing buffers (buffer pools) to reduce allocations if you need maximum optimization.
  • ⚠️ This benchmark uses an in-memory memStore (no disk I/O); file-backed implementations will be slower.

Limitations

  • Not a full database: tinydb is intended for small key/value needs with simple persistence.
  • No indexing, transactions, concurrency locking, or binary values support out of the box.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type KVStore

type KVStore interface {
	Get(key string) (string, error)
	Set(key, value string) error
}

KVStore defines the minimum API

type LoggerFunc

type LoggerFunc func(...any)

LoggerFunc is a simple logger that accepts any values (like fmt.Println). Use a nil LoggerFunc when you want no-op logging; New will set a safe default.

type Store

type Store interface {
	GetFile(filePath string) ([]byte, error)
	SetFile(filePath string, data []byte) error
	AddToFile(filePath string, data []byte) error
}

Store defines the persistence interface

type TinyDB

type TinyDB struct {
	// contains filtered or unexported fields
}

func New

func New(name string, log LoggerFunc, store Store) (*TinyDB, error)

New creates or loads a database

func (*TinyDB) Get

func (t *TinyDB) Get(key string) (string, error)

func (*TinyDB) Set

func (t *TinyDB) Set(key, value string) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL