aboutsummaryrefslogtreecommitdiff
path: root/lib/db/namespaced.go
blob: b7d00bffc759ce0ca1d4449a6d0c2b8aeae870f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package db

import (
	"encoding/binary"
	"time"

	"github.com/syncthing/syncthing/lib/db/backend"
)

// NamespacedKV is a simple key-value store using a specific namespace within
// a leveldb.
type NamespacedKV struct {
	db     backend.Backend
	prefix string
}

// NewNamespacedKV returns a new NamespacedKV that lives in the namespace
// specified by the prefix.
func NewNamespacedKV(db backend.Backend, prefix string) *NamespacedKV {
	return &NamespacedKV{
		db:     db,
		prefix: prefix,
	}
}

// PutInt64 stores a new int64. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutInt64(key string, val int64) error {
	var valBs [8]byte
	binary.BigEndian.PutUint64(valBs[:], uint64(val))
	return n.db.Put(n.prefixedKey(key), valBs[:])
}

// Int64 returns the stored value interpreted as an int64 and a boolean that
// is false if no value was stored at the key.
func (n *NamespacedKV) Int64(key string) (int64, bool, error) {
	valBs, err := n.db.Get(n.prefixedKey(key))
	if err != nil {
		return 0, false, filterNotFound(err)
	}
	val := binary.BigEndian.Uint64(valBs)
	return int64(val), true, nil
}

// PutTime stores a new time.Time. Any existing value (even if of another
// type) is overwritten.
func (n *NamespacedKV) PutTime(key string, val time.Time) error {
	valBs, _ := val.MarshalBinary() // never returns an error
	return n.db.Put(n.prefixedKey(key), valBs)
}

// Time returns the stored value interpreted as a time.Time and a boolean
// that is false if no value was stored at the key.
func (n NamespacedKV) Time(key string) (time.Time, bool, error) {
	var t time.Time
	valBs, err := n.db.Get(n.prefixedKey(key))
	if err != nil {
		return t, false, filterNotFound(err)
	}
	err = t.UnmarshalBinary(valBs)
	return t, err == nil, err
}

// PutString stores a new string. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutString(key, val string) error {
	return n.db.Put(n.prefixedKey(key), []byte(val))
}

// String returns the stored value interpreted as a string and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) String(key string) (string, bool, error) {
	valBs, err := n.db.Get(n.prefixedKey(key))
	if err != nil {
		return "", false, filterNotFound(err)
	}
	return string(valBs), true, nil
}

// PutBytes stores a new byte slice. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutBytes(key string, val []byte) error {
	return n.db.Put(n.prefixedKey(key), val)
}

// Bytes returns the stored value as a raw byte slice and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) Bytes(key string) ([]byte, bool, error) {
	valBs, err := n.db.Get(n.prefixedKey(key))
	if err != nil {
		return nil, false, filterNotFound(err)
	}
	return valBs, true, nil
}

// PutBool stores a new boolean. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutBool(key string, val bool) error {
	if val {
		return n.db.Put(n.prefixedKey(key), []byte{0x0})
	}
	return n.db.Put(n.prefixedKey(key), []byte{0x1})
}

// Bool returns the stored value as a boolean and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) Bool(key string) (bool, bool, error) {
	valBs, err := n.db.Get(n.prefixedKey(key))
	if err != nil {
		return false, false, filterNotFound(err)
	}
	return valBs[0] == 0x0, true, nil
}

// Delete deletes the specified key. It is allowed to delete a nonexistent
// key.
func (n NamespacedKV) Delete(key string) error {
	return n.db.Delete(n.prefixedKey(key))
}

func (n NamespacedKV) prefixedKey(key string) []byte {
	return []byte(n.prefix + key)
}

// Well known namespaces that can be instantiated without knowing the key
// details.

// NewDeviceStatisticsNamespace creates a KV namespace for device statistics
// for the given device.
func NewDeviceStatisticsNamespace(db backend.Backend, device string) *NamespacedKV {
	return NewNamespacedKV(db, string(KeyTypeDeviceStatistic)+device)
}

// NewFolderStatisticsNamespace creates a KV namespace for folder statistics
// for the given folder.
func NewFolderStatisticsNamespace(db backend.Backend, folder string) *NamespacedKV {
	return NewNamespacedKV(db, string(KeyTypeFolderStatistic)+folder)
}

// NewMiscDateNamespace creates a KV namespace for miscellaneous metadata.
func NewMiscDataNamespace(db backend.Backend) *NamespacedKV {
	return NewNamespacedKV(db, string(KeyTypeMiscData))
}

func filterNotFound(err error) error {
	if backend.IsNotFound(err) {
		return nil
	}
	return err
}