blob: b15374dc2751d815d87527356f6c68ad2eda552a [file] [log] [blame]
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -07005// Package leveldb provides a LevelDB-based implementation of store.Store.
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -07006package leveldb
7
8// #cgo LDFLAGS: -lleveldb
9// #include <stdlib.h>
10// #include "leveldb/c.h"
11// #include "syncbase_leveldb.h"
12import "C"
13import (
14 "sync"
15 "unsafe"
16
17 "v.io/syncbase/x/ref/services/syncbase/store"
18)
19
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070020// db is a wrapper around LevelDB that implements the store.Store interface.
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070021// TODO(rogulenko): ensure thread safety.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070022type db struct {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070023 cDb *C.leveldb_t
24 // Default read/write options.
25 readOptions *C.leveldb_readoptions_t
26 writeOptions *C.leveldb_writeoptions_t
27 // Used to prevent concurrent transactions.
28 // TODO(rogulenko): improve concurrency.
29 mu sync.Mutex
30}
31
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070032var _ store.Store = (*db)(nil)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070033
34// Open opens the database located at the given path, creating it if it doesn't
35// exist.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070036func Open(path string) (store.Store, error) {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070037 var cError *C.char
38 cPath := C.CString(path)
39 defer C.free(unsafe.Pointer(cPath))
40
41 cOpts := C.leveldb_options_create()
42 C.leveldb_options_set_create_if_missing(cOpts, 1)
43 C.leveldb_options_set_paranoid_checks(cOpts, 1)
44 defer C.leveldb_options_destroy(cOpts)
45
46 cDb := C.leveldb_open(cOpts, cPath, &cError)
47 if err := goError(cError); err != nil {
48 return nil, err
49 }
50 readOptions := C.leveldb_readoptions_create()
51 C.leveldb_readoptions_set_verify_checksums(readOptions, 1)
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070052 return &db{
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070053 cDb: cDb,
54 readOptions: readOptions,
55 writeOptions: C.leveldb_writeoptions_create(),
56 }, nil
57}
58
59// Close implements the store.Store interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070060func (d *db) Close() error {
61 C.leveldb_close(d.cDb)
62 C.leveldb_readoptions_destroy(d.readOptions)
63 C.leveldb_writeoptions_destroy(d.writeOptions)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070064 return nil
65}
66
67// Destroy removes all physical data of the database located at the given path.
68func Destroy(path string) error {
69 var cError *C.char
70 cPath := C.CString(path)
71 defer C.free(unsafe.Pointer(cPath))
72 cOpts := C.leveldb_options_create()
73 defer C.leveldb_options_destroy(cOpts)
74 C.leveldb_destroy_db(cOpts, cPath, &cError)
75 return goError(cError)
76}
77
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070078// Get implements the store.StoreReader interface.
79func (d *db) Get(key, valbuf []byte) ([]byte, error) {
80 return d.getWithOpts(key, valbuf, d.readOptions)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070081}
82
83// Scan implements the store.StoreReader interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070084func (d *db) Scan(start, end []byte) (store.Stream, error) {
85 return newStream(d, start, end, d.readOptions), nil
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070086}
87
88// Put implements the store.StoreWriter interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070089func (d *db) Put(key, value []byte) error {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070090 // TODO(rogulenko): improve performance.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070091 return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
Sergey Rogulenko802fe1e2015-05-08 12:51:22 -070092 return st.Put(key, value)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070093 })
94}
95
96// Delete implements the store.StoreWriter interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070097func (d *db) Delete(key []byte) error {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070098 // TODO(rogulenko): improve performance.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070099 return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700100 return st.Delete(key)
101 })
102}
103
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700104// NewTransaction implements the store.Store interface.
105func (d *db) NewTransaction() store.Transaction {
106 return newTransaction(d)
107}
108
109// NewSnapshot implements the store.Store interface.
110func (d *db) NewSnapshot() store.Snapshot {
111 return newSnapshot(d)
112}
113
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700114// getWithOpts returns the value for the given key.
115// cOpts may contain a pointer to a snapshot.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700116func (d *db) getWithOpts(key, valbuf []byte, cOpts *C.leveldb_readoptions_t) ([]byte, error) {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700117 var cError *C.char
118 var valLen C.size_t
119 cStr, cLen := cSlice(key)
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700120 val := C.leveldb_get(d.cDb, cOpts, cStr, cLen, &valLen, &cError)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700121 if err := goError(cError); err != nil {
122 return nil, err
123 }
124 if val == nil {
Sergey Rogulenko802fe1e2015-05-08 12:51:22 -0700125 return nil, &store.ErrUnknownKey{Key: string(key)}
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700126 }
127 defer C.leveldb_free(unsafe.Pointer(val))
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700128 return store.CopyBytes(valbuf, goBytes(val, valLen)), nil
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700129}