2014-07-25 13:58:00 +04:00
|
|
|
// +build leveldb
|
|
|
|
|
2014-07-09 05:33:44 +04:00
|
|
|
// Package leveldb is a wrapper for c++ leveldb
|
2014-06-19 13:19:40 +04:00
|
|
|
package leveldb
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo LDFLAGS: -lleveldb
|
|
|
|
#include <leveldb/c.h>
|
2014-07-07 08:37:16 +04:00
|
|
|
#include "leveldb_ext.h"
|
2014-06-19 13:19:40 +04:00
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
2014-07-25 13:58:00 +04:00
|
|
|
"github.com/siddontang/ledisdb/store/driver"
|
2014-06-19 13:19:40 +04:00
|
|
|
"os"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
const defaultFilterBits int = 10
|
|
|
|
|
|
|
|
type Config struct {
|
2014-07-26 14:39:54 +04:00
|
|
|
Path string `json:"path"`
|
2014-06-19 13:19:40 +04:00
|
|
|
|
2014-07-26 14:39:54 +04:00
|
|
|
Compression bool `json:"compression"`
|
|
|
|
BlockSize int `json:"block_size"`
|
|
|
|
WriteBufferSize int `json:"write_buffer_size"`
|
|
|
|
CacheSize int `json:"cache_size"`
|
|
|
|
MaxOpenFiles int `json:"max_open_files"`
|
2014-06-19 13:19:40 +04:00
|
|
|
}
|
|
|
|
|
2014-07-04 11:45:23 +04:00
|
|
|
func Open(cfg *Config) (*DB, error) {
|
2014-06-19 13:19:40 +04:00
|
|
|
if err := os.MkdirAll(cfg.Path, os.ModePerm); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
db := new(DB)
|
|
|
|
db.cfg = cfg
|
|
|
|
|
|
|
|
if err := db.open(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return db, nil
|
|
|
|
}
|
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
func Repair(cfg *Config) error {
|
|
|
|
db := new(DB)
|
|
|
|
db.cfg = cfg
|
|
|
|
|
|
|
|
err := db.open()
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
//open ok, do not need repair
|
|
|
|
if err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2014-06-19 13:19:40 +04:00
|
|
|
|
2014-06-26 07:04:54 +04:00
|
|
|
var errStr *C.char
|
|
|
|
ldbname := C.CString(db.cfg.Path)
|
|
|
|
defer C.leveldb_free(unsafe.Pointer(ldbname))
|
2014-06-19 13:19:40 +04:00
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
C.leveldb_repair_db(db.opts.Opt, ldbname, &errStr)
|
2014-06-26 07:04:54 +04:00
|
|
|
if errStr != nil {
|
|
|
|
return saveError(errStr)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2014-06-19 13:19:40 +04:00
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
type DB struct {
|
|
|
|
cfg *Config
|
2014-06-26 07:04:54 +04:00
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
db *C.leveldb_t
|
2014-06-26 07:04:54 +04:00
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
opts *Options
|
|
|
|
|
|
|
|
//for default read and write options
|
|
|
|
readOpts *ReadOptions
|
|
|
|
writeOpts *WriteOptions
|
|
|
|
iteratorOpts *ReadOptions
|
|
|
|
|
|
|
|
cache *Cache
|
|
|
|
|
|
|
|
filter *FilterPolicy
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) open() error {
|
|
|
|
db.initOptions(db.cfg)
|
2014-06-19 13:19:40 +04:00
|
|
|
|
|
|
|
var errStr *C.char
|
|
|
|
ldbname := C.CString(db.cfg.Path)
|
|
|
|
defer C.leveldb_free(unsafe.Pointer(ldbname))
|
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
db.db = C.leveldb_open(db.opts.Opt, ldbname, &errStr)
|
2014-06-19 13:19:40 +04:00
|
|
|
if errStr != nil {
|
2014-07-25 13:58:00 +04:00
|
|
|
db.db = nil
|
2014-06-19 13:19:40 +04:00
|
|
|
return saveError(errStr)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-06-26 07:04:54 +04:00
|
|
|
func (db *DB) initOptions(cfg *Config) {
|
2014-06-19 13:19:40 +04:00
|
|
|
opts := NewOptions()
|
|
|
|
|
|
|
|
opts.SetCreateIfMissing(true)
|
|
|
|
|
|
|
|
if cfg.CacheSize <= 0 {
|
|
|
|
cfg.CacheSize = 4 * 1024 * 1024
|
|
|
|
}
|
|
|
|
|
|
|
|
db.cache = NewLRUCache(cfg.CacheSize)
|
|
|
|
opts.SetCache(db.cache)
|
|
|
|
|
|
|
|
//we must use bloomfilter
|
|
|
|
db.filter = NewBloomFilter(defaultFilterBits)
|
|
|
|
opts.SetFilterPolicy(db.filter)
|
|
|
|
|
|
|
|
if !cfg.Compression {
|
|
|
|
opts.SetCompression(NoCompression)
|
2014-07-25 13:58:00 +04:00
|
|
|
} else {
|
|
|
|
opts.SetCompression(SnappyCompression)
|
2014-06-19 13:19:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.BlockSize <= 0 {
|
|
|
|
cfg.BlockSize = 4 * 1024
|
|
|
|
}
|
|
|
|
|
|
|
|
opts.SetBlockSize(cfg.BlockSize)
|
|
|
|
|
|
|
|
if cfg.WriteBufferSize <= 0 {
|
|
|
|
cfg.WriteBufferSize = 4 * 1024 * 1024
|
|
|
|
}
|
|
|
|
|
|
|
|
opts.SetWriteBufferSize(cfg.WriteBufferSize)
|
|
|
|
|
|
|
|
if cfg.MaxOpenFiles < 1024 {
|
|
|
|
cfg.MaxOpenFiles = 1024
|
|
|
|
}
|
|
|
|
|
|
|
|
opts.SetMaxOpenFiles(cfg.MaxOpenFiles)
|
|
|
|
|
2014-06-26 07:04:54 +04:00
|
|
|
db.opts = opts
|
|
|
|
|
|
|
|
db.readOpts = NewReadOptions()
|
|
|
|
db.writeOpts = NewWriteOptions()
|
|
|
|
|
|
|
|
db.iteratorOpts = NewReadOptions()
|
|
|
|
db.iteratorOpts.SetFillCache(false)
|
2014-06-19 13:19:40 +04:00
|
|
|
}
|
|
|
|
|
2014-07-04 09:27:57 +04:00
|
|
|
func (db *DB) Close() error {
|
2014-06-26 07:04:54 +04:00
|
|
|
if db.db != nil {
|
|
|
|
C.leveldb_close(db.db)
|
|
|
|
db.db = nil
|
|
|
|
}
|
2014-06-19 13:19:40 +04:00
|
|
|
|
|
|
|
db.opts.Close()
|
|
|
|
|
|
|
|
if db.cache != nil {
|
|
|
|
db.cache.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if db.filter != nil {
|
|
|
|
db.filter.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
db.readOpts.Close()
|
|
|
|
db.writeOpts.Close()
|
|
|
|
db.iteratorOpts.Close()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) Put(key, value []byte) error {
|
|
|
|
return db.put(db.writeOpts, key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) Get(key []byte) ([]byte, error) {
|
2014-07-25 13:58:00 +04:00
|
|
|
return db.get(db.readOpts, key)
|
2014-06-19 13:19:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) Delete(key []byte) error {
|
|
|
|
return db.delete(db.writeOpts, key)
|
|
|
|
}
|
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
func (db *DB) NewWriteBatch() driver.IWriteBatch {
|
2014-06-19 13:19:40 +04:00
|
|
|
wb := &WriteBatch{
|
|
|
|
db: db,
|
|
|
|
wbatch: C.leveldb_writebatch_create(),
|
|
|
|
}
|
|
|
|
return wb
|
|
|
|
}
|
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
func (db *DB) NewIterator() driver.IIterator {
|
2014-06-19 13:19:40 +04:00
|
|
|
it := new(Iterator)
|
|
|
|
|
|
|
|
it.it = C.leveldb_create_iterator(db.db, db.iteratorOpts.Opt)
|
|
|
|
|
|
|
|
return it
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) put(wo *WriteOptions, key, value []byte) error {
|
|
|
|
var errStr *C.char
|
|
|
|
var k, v *C.char
|
|
|
|
if len(key) != 0 {
|
|
|
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
|
|
|
}
|
|
|
|
if len(value) != 0 {
|
|
|
|
v = (*C.char)(unsafe.Pointer(&value[0]))
|
|
|
|
}
|
|
|
|
|
|
|
|
lenk := len(key)
|
|
|
|
lenv := len(value)
|
|
|
|
C.leveldb_put(
|
|
|
|
db.db, wo.Opt, k, C.size_t(lenk), v, C.size_t(lenv), &errStr)
|
|
|
|
|
|
|
|
if errStr != nil {
|
|
|
|
return saveError(errStr)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
func (db *DB) get(ro *ReadOptions, key []byte) ([]byte, error) {
|
2014-06-19 13:19:40 +04:00
|
|
|
var errStr *C.char
|
|
|
|
var vallen C.size_t
|
|
|
|
var k *C.char
|
|
|
|
if len(key) != 0 {
|
|
|
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
|
|
|
}
|
|
|
|
|
2014-07-07 08:37:16 +04:00
|
|
|
var value *C.char
|
|
|
|
|
|
|
|
c := C.leveldb_get_ext(
|
|
|
|
db.db, ro.Opt, k, C.size_t(len(key)), &value, &vallen, &errStr)
|
2014-06-19 13:19:40 +04:00
|
|
|
|
|
|
|
if errStr != nil {
|
|
|
|
return nil, saveError(errStr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if value == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2014-07-07 08:37:16 +04:00
|
|
|
defer C.leveldb_get_free_ext(unsafe.Pointer(c))
|
|
|
|
|
2014-07-25 13:58:00 +04:00
|
|
|
return C.GoBytes(unsafe.Pointer(value), C.int(vallen)), nil
|
2014-06-19 13:19:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) delete(wo *WriteOptions, key []byte) error {
|
|
|
|
var errStr *C.char
|
|
|
|
var k *C.char
|
|
|
|
if len(key) != 0 {
|
|
|
|
k = (*C.char)(unsafe.Pointer(&key[0]))
|
|
|
|
}
|
|
|
|
|
|
|
|
C.leveldb_delete(
|
|
|
|
db.db, wo.Opt, k, C.size_t(len(key)), &errStr)
|
|
|
|
|
|
|
|
if errStr != nil {
|
|
|
|
return saveError(errStr)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|