Skip to content

Commit 11d20ad

Browse files
committed
Use base64 encoding for in memory maps.
Instead of using the string representation of UUIDs (37 chars) use a base64 encoded (24 chars) of the 16 bits
1 parent 15fe58c commit 11d20ad

3 files changed

Lines changed: 97 additions & 43 deletions

File tree

storage/memory/memory.go

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package memory
1818

1919
import (
2020
"fmt"
21-
"strings"
2221
"sync"
2322

2423
"golang.org/x/net/context"
@@ -29,6 +28,8 @@ import (
2928
"github.com/google/badwolf/triple/predicate"
3029
)
3130

31+
const initialAllocation = 10000
32+
3233
// DefaultStore provides a volatile in memory store.
3334
var DefaultStore storage.Store
3435

@@ -62,13 +63,13 @@ func (s *memoryStore) Version(ctx context.Context) string {
6263
func (s *memoryStore) NewGraph(ctx context.Context, id string) (storage.Graph, error) {
6364
g := &memory{
6465
id: id,
65-
idx: make(map[string]*triple.Triple),
66-
idxS: make(map[string]map[string]*triple.Triple),
67-
idxP: make(map[string]map[string]*triple.Triple),
68-
idxO: make(map[string]map[string]*triple.Triple),
69-
idxSP: make(map[string]map[string]*triple.Triple),
70-
idxPO: make(map[string]map[string]*triple.Triple),
71-
idxSO: make(map[string]map[string]*triple.Triple),
66+
idx: make(map[string]*triple.Triple, initialAllocation),
67+
idxS: make(map[string]map[string]*triple.Triple, initialAllocation),
68+
idxP: make(map[string]map[string]*triple.Triple, initialAllocation),
69+
idxO: make(map[string]map[string]*triple.Triple, initialAllocation),
70+
idxSP: make(map[string]map[string]*triple.Triple, initialAllocation),
71+
idxPO: make(map[string]map[string]*triple.Triple, initialAllocation),
72+
idxSO: make(map[string]map[string]*triple.Triple, initialAllocation),
7273
}
7374

7475
s.rwmu.Lock()
@@ -140,10 +141,10 @@ func (m *memory) AddTriples(ctx context.Context, ts []*triple.Triple) error {
140141
m.rwmu.Lock()
141142
defer m.rwmu.Unlock()
142143
for _, t := range ts {
143-
suuid := t.UUID().String()
144-
sUUID := t.Subject().UUID().String()
145-
pUUID := t.Predicate().UUID().String()
146-
oUUID := t.Object().UUID().String()
144+
suuid := UUIDToBase64(t.UUID())
145+
sUUID := UUIDToBase64(t.Subject().UUID())
146+
pUUID := UUIDToBase64(t.Predicate().UUID())
147+
oUUID := UUIDToBase64(t.Object().UUID())
147148
// Update master index
148149
m.idx[suuid] = t
149150

@@ -162,19 +163,19 @@ func (m *memory) AddTriples(ctx context.Context, ts []*triple.Triple) error {
162163
}
163164
m.idxO[oUUID][suuid] = t
164165

165-
key := strings.Join([]string{sUUID, pUUID}, ":")
166+
key := sUUID + pUUID
166167
if _, ok := m.idxSP[key]; !ok {
167168
m.idxSP[key] = make(map[string]*triple.Triple)
168169
}
169170
m.idxSP[key][suuid] = t
170171

171-
key = strings.Join([]string{pUUID, oUUID}, ":")
172+
key = pUUID + oUUID
172173
if _, ok := m.idxPO[key]; !ok {
173174
m.idxPO[key] = make(map[string]*triple.Triple)
174175
}
175176
m.idxPO[key][suuid] = t
176177

177-
key = strings.Join([]string{sUUID, oUUID}, ":")
178+
key = sUUID + oUUID
178179
if _, ok := m.idxSO[key]; !ok {
179180
m.idxSO[key] = make(map[string]*triple.Triple)
180181
}
@@ -186,30 +187,30 @@ func (m *memory) AddTriples(ctx context.Context, ts []*triple.Triple) error {
186187
// RemoveTriples removes the triples from the storage.
187188
func (m *memory) RemoveTriples(ctx context.Context, ts []*triple.Triple) error {
188189
for _, t := range ts {
189-
suuid := t.UUID().String()
190-
sUUID := t.Subject().UUID().String()
191-
pUUID := t.Predicate().UUID().String()
192-
oUUID := t.Object().UUID().String()
190+
suuid := UUIDToBase64(t.UUID())
191+
sUUID := UUIDToBase64(t.Subject().UUID())
192+
pUUID := UUIDToBase64(t.Predicate().UUID())
193+
oUUID := UUIDToBase64(t.Object().UUID())
193194
// Update master index
194195
m.rwmu.Lock()
195196
delete(m.idx, suuid)
196197
delete(m.idxS[sUUID], suuid)
197198
delete(m.idxP[pUUID], suuid)
198199
delete(m.idxO[oUUID], suuid)
199200

200-
key := strings.Join([]string{sUUID, pUUID}, ":")
201+
key := sUUID + pUUID
201202
delete(m.idxSP[key], suuid)
202203
if len(m.idxSP[key]) == 0 {
203204
delete(m.idxSP, key)
204205
}
205206

206-
key = strings.Join([]string{pUUID, oUUID}, ":")
207+
key = pUUID + oUUID
207208
delete(m.idxPO[key], suuid)
208209
if len(m.idxPO[key]) == 0 {
209210
delete(m.idxPO, key)
210211
}
211212

212-
key = strings.Join([]string{sUUID, oUUID}, ":")
213+
key = sUUID + oUUID
213214
delete(m.idxSO[key], suuid)
214215
if len(m.idxSO[key]) == 0 {
215216
delete(m.idxSO, key)
@@ -262,9 +263,9 @@ func (c *checker) CheckAndUpdate(p *predicate.Predicate) bool {
262263
// provided channel.
263264
func (m *memory) Objects(ctx context.Context, s *node.Node, p *predicate.Predicate, lo *storage.LookupOptions, objs chan<- *triple.Object) error {
264265

265-
sUUID := s.UUID().String()
266-
pUUID := p.UUID().String()
267-
spIdx := strings.Join([]string{sUUID, pUUID}, ":")
266+
sUUID := UUIDToBase64(s.UUID())
267+
pUUID := UUIDToBase64(p.UUID())
268+
spIdx := sUUID + pUUID
268269
m.rwmu.RLock()
269270
defer m.rwmu.RUnlock()
270271
defer close(objs)
@@ -284,9 +285,9 @@ func (m *memory) Subjects(ctx context.Context, p *predicate.Predicate, o *triple
284285
if subjs == nil {
285286
return fmt.Errorf("cannot provide an empty channel")
286287
}
287-
pUUID := p.UUID().String()
288-
oUUID := o.UUID().String()
289-
poIdx := strings.Join([]string{pUUID, oUUID}, ":")
288+
pUUID := UUIDToBase64(p.UUID())
289+
oUUID := UUIDToBase64(o.UUID())
290+
poIdx := pUUID + oUUID
290291
m.rwmu.RLock()
291292
defer m.rwmu.RUnlock()
292293
defer close(subjs)
@@ -306,9 +307,9 @@ func (m *memory) PredicatesForSubjectAndObject(ctx context.Context, s *node.Node
306307
if prds == nil {
307308
return fmt.Errorf("cannot provide an empty channel")
308309
}
309-
sUUID := s.UUID().String()
310-
oUUID := o.UUID().String()
311-
soIdx := strings.Join([]string{sUUID, oUUID}, ":")
310+
sUUID := UUIDToBase64(s.UUID())
311+
oUUID := UUIDToBase64(o.UUID())
312+
soIdx := sUUID + oUUID
312313
m.rwmu.RLock()
313314
defer m.rwmu.RUnlock()
314315
defer close(prds)
@@ -328,7 +329,7 @@ func (m *memory) PredicatesForSubject(ctx context.Context, s *node.Node, lo *sto
328329
if prds == nil {
329330
return fmt.Errorf("cannot provide an empty channel")
330331
}
331-
sUUID := s.UUID().String()
332+
sUUID := UUIDToBase64(s.UUID())
332333
m.rwmu.RLock()
333334
defer m.rwmu.RUnlock()
334335
defer close(prds)
@@ -347,7 +348,7 @@ func (m *memory) PredicatesForObject(ctx context.Context, o *triple.Object, lo *
347348
if prds == nil {
348349
return fmt.Errorf("cannot provide an empty channel")
349350
}
350-
oUUID := o.UUID().String()
351+
oUUID := UUIDToBase64(o.UUID())
351352
m.rwmu.RLock()
352353
defer m.rwmu.RUnlock()
353354
defer close(prds)
@@ -366,7 +367,7 @@ func (m *memory) TriplesForSubject(ctx context.Context, s *node.Node, lo *storag
366367
if trpls == nil {
367368
return fmt.Errorf("cannot provide an empty channel")
368369
}
369-
sUUID := s.UUID().String()
370+
sUUID := UUIDToBase64(s.UUID())
370371
m.rwmu.RLock()
371372
defer m.rwmu.RUnlock()
372373
defer close(trpls)
@@ -386,7 +387,7 @@ func (m *memory) TriplesForPredicate(ctx context.Context, p *predicate.Predicate
386387
if trpls == nil {
387388
return fmt.Errorf("cannot provide an empty channel")
388389
}
389-
pUUID := p.UUID().String()
390+
pUUID := UUIDToBase64(p.UUID())
390391
m.rwmu.RLock()
391392
defer m.rwmu.RUnlock()
392393
defer close(trpls)
@@ -406,7 +407,7 @@ func (m *memory) TriplesForObject(ctx context.Context, o *triple.Object, lo *sto
406407
if trpls == nil {
407408
return fmt.Errorf("cannot provide an empty channel")
408409
}
409-
oUUID := o.UUID().String()
410+
oUUID := UUIDToBase64(o.UUID())
410411
m.rwmu.RLock()
411412
defer m.rwmu.RUnlock()
412413
defer close(trpls)
@@ -426,9 +427,9 @@ func (m *memory) TriplesForSubjectAndPredicate(ctx context.Context, s *node.Node
426427
if trpls == nil {
427428
return fmt.Errorf("cannot provide an empty channel")
428429
}
429-
sUUID := s.UUID().String()
430-
pUUID := p.UUID().String()
431-
spIdx := strings.Join([]string{sUUID, pUUID}, ":")
430+
sUUID := UUIDToBase64(s.UUID())
431+
pUUID := UUIDToBase64(p.UUID())
432+
spIdx := sUUID + pUUID
432433
m.rwmu.RLock()
433434
defer m.rwmu.RUnlock()
434435
defer close(trpls)
@@ -448,9 +449,9 @@ func (m *memory) TriplesForPredicateAndObject(ctx context.Context, p *predicate.
448449
if trpls == nil {
449450
return fmt.Errorf("cannot provide an empty channel")
450451
}
451-
pUUID := p.UUID().String()
452-
oUUID := o.UUID().String()
453-
poIdx := strings.Join([]string{pUUID, oUUID}, ":")
452+
pUUID := UUIDToBase64(p.UUID())
453+
oUUID := UUIDToBase64(o.UUID())
454+
poIdx := pUUID + oUUID
454455
m.rwmu.RLock()
455456
defer m.rwmu.RUnlock()
456457
defer close(trpls)
@@ -466,7 +467,7 @@ func (m *memory) TriplesForPredicateAndObject(ctx context.Context, p *predicate.
466467

467468
// Exist checks if the provided triple exists on the store.
468469
func (m *memory) Exist(ctx context.Context, t *triple.Triple) (bool, error) {
469-
suuid := t.UUID().String()
470+
suuid := UUIDToBase64(t.UUID())
470471
m.rwmu.RLock()
471472
defer m.rwmu.RUnlock()
472473
_, ok := m.idx[suuid]

storage/memory/uuid.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package memory
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
7+
"github.com/pborman/uuid"
8+
)
9+
10+
// UUIDToBase64 recodes it into a compact string.
11+
func UUIDToBase64(uuid uuid.UUID) string {
12+
return base64.StdEncoding.EncodeToString([]byte(uuid))
13+
}
14+
15+
// UUIDToBase64 attempts to decode a base 64 encoded UUID.
16+
func Base64ToUUID(b64 string) (uuid.UUID, error) {
17+
bs, err := base64.StdEncoding.DecodeString(b64)
18+
if err != nil {
19+
return nil, err
20+
}
21+
if got, want := len(bs), 16; got != want {
22+
return nil, fmt.Errorf("invalid UUID size; got %d, want %d", got, want)
23+
}
24+
return uuid.UUID(bs), nil
25+
}

storage/memory/uuid_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package memory
2+
3+
import (
4+
"testing"
5+
6+
"github.com/pborman/uuid"
7+
)
8+
9+
func TestTwoWaySerialization(t *testing.T) {
10+
uuid1 := uuid.NewRandom()
11+
b64 := UUIDToBase64(uuid1)
12+
uuid2, err := Base64ToUUID(b64)
13+
if err != nil {
14+
t.Fatal(err)
15+
}
16+
if got, want := len(b64), 24; got != want {
17+
t.Fatalf("UUIDToBase64 returned an invalid size string; got %d, want %d", got, want)
18+
}
19+
if got, want := uuid2, uuid1; !uuid.Equal(got, want) {
20+
t.Fatalf("The marshal/unmarshal via base64 should have returned the same UUID; got %q, want %q", got, want)
21+
}
22+
}
23+
24+
func TestInvalidDeserialization(t *testing.T) {
25+
if _, err := Base64ToUUID("bogus entry"); err == nil {
26+
t.Fatal("Base64ToUUID should have never returned a corrupted UUID")
27+
}
28+
}

0 commit comments

Comments
 (0)