all repos — paprika @ profile

go rewrite of taigabot

database/db.go (view raw)

  1package database
  2
  3import (
  4	"bytes"
  5	"errors"
  6	"fmt"
  7	"math"
  8	"path"
  9	"strconv"
 10	"time"
 11
 12	"git.icyphox.sh/paprika/config"
 13	"github.com/dgraph-io/badger/v3"
 14)
 15
 16// Use this as a global DB handle.
 17var DB database
 18
 19type database struct {
 20	*badger.DB
 21}
 22
 23// see: https://dgraph.io/docs/badger/get-started/#garbage-collection
 24func gc() {
 25	ticker := time.NewTicker(5 * time.Minute)
 26	defer ticker.Stop()
 27	for range ticker.C {
 28	again:
 29		err := DB.DB.RunValueLogGC(0.7)
 30		if err == nil {
 31			goto again
 32		}
 33	}
 34	panic("Unreachable!")
 35}
 36
 37func Open() (*badger.DB, error) {
 38	db, err := badger.Open(
 39		badger.DefaultOptions(path.Join(config.C.DbPath, "badger")),
 40	)
 41	if err != nil {
 42		return nil, err
 43	}
 44	go gc()
 45	return db, nil
 46}
 47
 48// Wrapper function to simplify setting a key/val
 49// in badger.
 50func (d *database) Set(key, val []byte) error {
 51	err := d.Update(func(txn *badger.Txn) error {
 52		err := txn.Set(key, val)
 53		return err
 54	})
 55
 56	return err
 57}
 58
 59// Wrapper function to simplify getting a key from badger.
 60func (d *database) Get(key []byte) ([]byte, error) {
 61	var val []byte
 62	err := d.View(func(txn *badger.Txn) error {
 63		item, err := txn.Get(key)
 64		if err != nil {
 65			return err
 66		}
 67
 68		err = item.Value(func(v []byte) error {
 69			val, err = item.ValueCopy(nil)
 70			return err
 71		})
 72		return err
 73	})
 74	if err != nil {
 75		return nil, err
 76	}
 77	return val, nil
 78}
 79
 80var NumTooBig = errors.New("Number Too Big")
 81var InvalidNumber = errors.New("Invalid Number")
 82
 83// encode number so it sorts lexicographically, while being semi readable.
 84func EncodeNumber(n int) ([]byte, error) {
 85	neg := false
 86	num := n
 87	if n < 0 {
 88		neg = true
 89		num = -num
 90	}
 91
 92	digits := int(math.Trunc(math.Log10(float64(num))))+1
 93	if digits > 93 {
 94		return []byte{}, NumTooBig
 95	}
 96
 97	if !neg {
 98		lenCode := 33 + digits
 99		return []byte(fmt.Sprintf("%c %d", lenCode, n)), nil
100	} else {
101		lenCode := 127 - digits
102		return []byte(fmt.Sprintf("!%c %d", lenCode, n)), nil
103	}
104}
105
106// encode number so it sorts lexicographically, while being semi readable.
107func DecodeNumber(n []byte) (int, error) {
108	if len(n) < 3 {
109		return 0, InvalidNumber
110	}
111
112	// No digit padding
113	if n[0] < 33 || n[0] > 126 {
114		return 0, InvalidNumber
115	}
116
117	num := bytes.SplitN(n, []byte{' '}, 2)
118	if len(num) != 2 {
119		return 0, InvalidNumber
120	}
121
122	number, err := strconv.Atoi(string(num[1]))
123	if err != nil {
124		return number, err
125	}
126
127	if n[0] == '!' {
128		return -number, nil
129	} else {
130		return number, nil
131	}
132}