blob: bd251c3d9491cd0185898fddb656df2efe722d32 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package leveldb
// #cgo LDFLAGS: -lleveldb
// #include <stdlib.h>
// #include "leveldb/c.h"
// #include "syncbase_leveldb.h"
import "C"
import (
"sync"
"unsafe"
"v.io/syncbase/x/ref/services/syncbase/store"
)
// DB is a wrapper around LevelDB that implements the store.Store interface.
// TODO(rogulenko): ensure thread safety.
type DB struct {
cDb *C.leveldb_t
// Default read/write options.
readOptions *C.leveldb_readoptions_t
writeOptions *C.leveldb_writeoptions_t
// Used to prevent concurrent transactions.
// TODO(rogulenko): improve concurrency.
mu sync.Mutex
}
var _ store.Store = (*DB)(nil)
// Open opens the database located at the given path, creating it if it doesn't
// exist.
func Open(path string) (*DB, error) {
var cError *C.char
cPath := C.CString(path)
defer C.free(unsafe.Pointer(cPath))
cOpts := C.leveldb_options_create()
C.leveldb_options_set_create_if_missing(cOpts, 1)
C.leveldb_options_set_paranoid_checks(cOpts, 1)
defer C.leveldb_options_destroy(cOpts)
cDb := C.leveldb_open(cOpts, cPath, &cError)
if err := goError(cError); err != nil {
return nil, err
}
readOptions := C.leveldb_readoptions_create()
C.leveldb_readoptions_set_verify_checksums(readOptions, 1)
return &DB{
cDb: cDb,
readOptions: readOptions,
writeOptions: C.leveldb_writeoptions_create(),
}, nil
}
// Close implements the store.Store interface.
func (db *DB) Close() error {
C.leveldb_close(db.cDb)
C.leveldb_readoptions_destroy(db.readOptions)
C.leveldb_writeoptions_destroy(db.writeOptions)
return nil
}
// Destroy removes all physical data of the database located at the given path.
func Destroy(path string) error {
var cError *C.char
cPath := C.CString(path)
defer C.free(unsafe.Pointer(cPath))
cOpts := C.leveldb_options_create()
defer C.leveldb_options_destroy(cOpts)
C.leveldb_destroy_db(cOpts, cPath, &cError)
return goError(cError)
}
// NewTransaction implements the store.Store interface.
func (db *DB) NewTransaction() store.Transaction {
return newTransaction(db)
}
// NewSnapshot implements the store.Store interface.
func (db *DB) NewSnapshot() store.Snapshot {
return newSnapshot(db)
}
// Scan implements the store.StoreReader interface.
func (db *DB) Scan(start, end string) (store.Stream, error) {
return newStream(db, start, end, db.readOptions), nil
}
// Get implements the store.StoreReader interface.
func (db *DB) Get(key string) ([]byte, error) {
return db.getWithOpts(key, db.readOptions)
}
// Put implements the store.StoreWriter interface.
func (db *DB) Put(key string, v []byte) error {
// TODO(rogulenko): improve performance.
return store.RunInTransaction(db, func(st store.StoreReadWriter) error {
return st.Put(key, v)
})
}
// Delete implements the store.StoreWriter interface.
func (db *DB) Delete(key string) error {
// TODO(rogulenko): improve performance.
return store.RunInTransaction(db, func(st store.StoreReadWriter) error {
return st.Delete(key)
})
}
// getWithOpts returns the value for the given key.
// cOpts may contain a pointer to a snapshot.
func (db *DB) getWithOpts(key string, cOpts *C.leveldb_readoptions_t) ([]byte, error) {
var cError *C.char
var valLen C.size_t
cStr, cLen := cSlice(key)
val := C.leveldb_get(db.cDb, cOpts, cStr, cLen, &valLen, &cError)
if err := goError(cError); err != nil {
return nil, err
}
if val == nil {
return nil, &store.ErrUnknownKey{Key: key}
}
defer C.leveldb_free(unsafe.Pointer(val))
return C.GoBytes(unsafe.Pointer(val), C.int(valLen)), nil
}