// +build gcc
|
|
|
|
package db
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/jmhodges/levigo"
|
|
)
|
|
|
|
func init() {
|
|
dbCreator := func(name string, dir string) (DB, error) {
|
|
return NewCLevelDB(name, dir)
|
|
}
|
|
registerDBCreator(LevelDBBackendStr, dbCreator, true)
|
|
registerDBCreator(CLevelDBBackendStr, dbCreator, false)
|
|
}
|
|
|
|
var _ DB = (*CLevelDB)(nil)
|
|
|
|
type CLevelDB struct {
|
|
db *levigo.DB
|
|
ro *levigo.ReadOptions
|
|
wo *levigo.WriteOptions
|
|
woSync *levigo.WriteOptions
|
|
}
|
|
|
|
func NewCLevelDB(name string, dir string) (*CLevelDB, error) {
|
|
dbPath := filepath.Join(dir, name+".db")
|
|
|
|
opts := levigo.NewOptions()
|
|
opts.SetCache(levigo.NewLRUCache(1 << 30))
|
|
opts.SetCreateIfMissing(true)
|
|
db, err := levigo.Open(dbPath, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ro := levigo.NewReadOptions()
|
|
wo := levigo.NewWriteOptions()
|
|
woSync := levigo.NewWriteOptions()
|
|
woSync.SetSync(true)
|
|
database := &CLevelDB{
|
|
db: db,
|
|
ro: ro,
|
|
wo: wo,
|
|
woSync: woSync,
|
|
}
|
|
return database, nil
|
|
}
|
|
|
|
func (db *CLevelDB) Get(key []byte) []byte {
|
|
panicNilKey(key)
|
|
res, err := db.db.Get(db.ro, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (db *CLevelDB) Has(key []byte) bool {
|
|
panicNilKey(key)
|
|
panic("not implemented yet")
|
|
}
|
|
|
|
func (db *CLevelDB) Set(key []byte, value []byte) {
|
|
panicNilKey(key)
|
|
err := db.db.Put(db.wo, key, value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (db *CLevelDB) SetSync(key []byte, value []byte) {
|
|
panicNilKey(key)
|
|
err := db.db.Put(db.woSync, key, value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (db *CLevelDB) Delete(key []byte) {
|
|
panicNilKey(key)
|
|
err := db.db.Delete(db.wo, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (db *CLevelDB) DeleteSync(key []byte) {
|
|
panicNilKey(key)
|
|
err := db.db.Delete(db.woSync, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (db *CLevelDB) DB() *levigo.DB {
|
|
return db.db
|
|
}
|
|
|
|
func (db *CLevelDB) Close() {
|
|
db.db.Close()
|
|
db.ro.Close()
|
|
db.wo.Close()
|
|
db.woSync.Close()
|
|
}
|
|
|
|
func (db *CLevelDB) Print() {
|
|
itr := db.Iterator(BeginningKey(), EndingKey())
|
|
defer itr.Close()
|
|
for ; itr.Valid(); itr.Next() {
|
|
key := itr.Key()
|
|
value := itr.Value()
|
|
fmt.Printf("[%X]:\t[%X]\n", key, value)
|
|
}
|
|
}
|
|
|
|
func (db *CLevelDB) Stats() map[string]string {
|
|
// TODO: Find the available properties for the C LevelDB implementation
|
|
keys := []string{}
|
|
|
|
stats := make(map[string]string)
|
|
for _, key := range keys {
|
|
str := db.db.PropertyValue(key)
|
|
stats[key] = str
|
|
}
|
|
return stats
|
|
}
|
|
|
|
//----------------------------------------
|
|
// Batch
|
|
|
|
func (db *CLevelDB) NewBatch() Batch {
|
|
batch := levigo.NewWriteBatch()
|
|
return &cLevelDBBatch{db, batch}
|
|
}
|
|
|
|
type cLevelDBBatch struct {
|
|
db *CLevelDB
|
|
batch *levigo.WriteBatch
|
|
}
|
|
|
|
func (mBatch *cLevelDBBatch) Set(key, value []byte) {
|
|
mBatch.batch.Put(key, value)
|
|
}
|
|
|
|
func (mBatch *cLevelDBBatch) Delete(key []byte) {
|
|
mBatch.batch.Delete(key)
|
|
}
|
|
|
|
func (mBatch *cLevelDBBatch) Write() {
|
|
err := mBatch.db.db.Write(mBatch.db.wo, mBatch.batch)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
//----------------------------------------
|
|
// Iterator
|
|
|
|
func (db *CLevelDB) Iterator(start, end []byte) Iterator {
|
|
itr := db.db.NewIterator(db.ro)
|
|
return newCLevelDBIterator(itr, start, end)
|
|
}
|
|
|
|
func (db *CLevelDB) ReverseIterator(start, end []byte) Iterator {
|
|
// XXX
|
|
return nil
|
|
}
|
|
|
|
var _ Iterator = (*cLevelDBIterator)(nil)
|
|
|
|
type cLevelDBIterator struct {
|
|
itr *levigo.Iterator
|
|
start, end []byte
|
|
invalid bool
|
|
}
|
|
|
|
func newCLevelDBIterator(itr *levigo.Iterator, start, end []byte) *cLevelDBIterator {
|
|
|
|
if len(start) > 0 {
|
|
itr.Seek(start)
|
|
} else {
|
|
itr.SeekToFirst()
|
|
}
|
|
|
|
return &cLevelDBIterator{
|
|
itr: itr,
|
|
start: start,
|
|
end: end,
|
|
}
|
|
}
|
|
|
|
func (c *cLevelDBIterator) Domain() ([]byte, []byte) {
|
|
return c.start, c.end
|
|
}
|
|
|
|
func (c *cLevelDBIterator) Valid() bool {
|
|
c.assertNoError()
|
|
if c.invalid {
|
|
return false
|
|
}
|
|
c.invalid = !c.itr.Valid()
|
|
return !c.invalid
|
|
}
|
|
|
|
func (c *cLevelDBIterator) Key() []byte {
|
|
if !c.Valid() {
|
|
panic("cLevelDBIterator Key() called when invalid")
|
|
}
|
|
return c.itr.Key()
|
|
}
|
|
|
|
func (c *cLevelDBIterator) Value() []byte {
|
|
if !c.Valid() {
|
|
panic("cLevelDBIterator Value() called when invalid")
|
|
}
|
|
return c.itr.Value()
|
|
}
|
|
|
|
func (c *cLevelDBIterator) Next() {
|
|
if !c.Valid() {
|
|
panic("cLevelDBIterator Next() called when invalid")
|
|
}
|
|
c.itr.Next()
|
|
c.checkEndKey() // if we've exceeded the range, we're now invalid
|
|
}
|
|
|
|
// levigo has no upper bound when iterating, so need to check ourselves
|
|
func (c *cLevelDBIterator) checkEndKey() {
|
|
if !c.itr.Valid() {
|
|
c.invalid = true
|
|
return
|
|
}
|
|
|
|
key := c.itr.Key()
|
|
if c.end != nil && bytes.Compare(key, c.end) > 0 {
|
|
c.invalid = true
|
|
}
|
|
}
|
|
|
|
func (c *cLevelDBIterator) Close() {
|
|
c.itr.Close()
|
|
}
|
|
|
|
func (c *cLevelDBIterator) assertNoError() {
|
|
if err := c.itr.GetError(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|