package levigo /* #cgo LDFLAGS: -lleveldb #include #include "leveldb/c.h" // This function exists only to clean up lack-of-const warnings when // leveldb_approximate_sizes is called from Go-land. void levigo_leveldb_approximate_sizes( leveldb_t* db, int num_ranges, char** range_start_key, const size_t* range_start_key_len, char** range_limit_key, const size_t* range_limit_key_len, uint64_t* sizes) { leveldb_approximate_sizes(db, num_ranges, (const char* const*)range_start_key, range_start_key_len, (const char* const*)range_limit_key, range_limit_key_len, sizes); } */ import "C" import ( "unsafe" ) type DatabaseError string func (e DatabaseError) Error() string { return string(e) } // DB is a reusable handle to a LevelDB database on disk, created by Open. // // To avoid memory and file descriptor leaks, call Close when the process no // longer needs the handle. Calls to any DB method made after Close will // panic. // // The DB instance may be shared between goroutines. The usual data race // conditions will occur if the same key is written to from more than one, of // course. type DB struct { Ldb *C.leveldb_t } // Range is a range of keys in the database. GetApproximateSizes calls with it // begin at the key Start and end right before the key Limit. type Range struct { Start []byte Limit []byte } // Snapshot provides a consistent view of read operations in a DB. // // Snapshot is used in read operations by setting it on a // ReadOptions. Snapshots are created by calling DB.NewSnapshot. // // To prevent memory leaks and resource strain in the database, the snapshot // returned must be released with DB.ReleaseSnapshot method on the DB that // created it. type Snapshot struct { snap *C.leveldb_snapshot_t } // Open opens a database. // // Creating a new database is done by calling SetCreateIfMissing(true) on the // Options passed to Open. // // It is usually wise to set a Cache object on the Options with SetCache to // keep recently used data from that database in memory. func Open(dbname string, o *Options) (*DB, error) { var errStr *C.char ldbname := C.CString(dbname) defer C.free(unsafe.Pointer(ldbname)) leveldb := C.leveldb_open(o.Opt, ldbname, &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return nil, DatabaseError(gs) } return &DB{leveldb}, nil } // DestroyDatabase removes a database entirely, removing everything from the // filesystem. func DestroyDatabase(dbname string, o *Options) error { var errStr *C.char ldbname := C.CString(dbname) defer C.free(unsafe.Pointer(ldbname)) C.leveldb_destroy_db(o.Opt, ldbname, &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return DatabaseError(gs) } return nil } // RepairDatabase attempts to repair a database. // // If the database is unrepairable, an error is returned. func RepairDatabase(dbname string, o *Options) error { var errStr *C.char ldbname := C.CString(dbname) defer C.free(unsafe.Pointer(ldbname)) C.leveldb_repair_db(o.Opt, ldbname, &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return DatabaseError(gs) } return nil } // Put writes data associated with a key to the database. // // If a nil []byte is passed in as value, it will be returned by Get // as an zero-length slice. The WriteOptions passed in can be reused // by multiple calls to this and if the WriteOptions is left unchanged. // // The key and value byte slices may be reused safely. Put takes a copy of // them before returning. func (db *DB) Put(wo *WriteOptions, key, value []byte) error { var errStr *C.char // leveldb_put, _get, and _delete call memcpy() (by way of Memtable::Add) // when called, so we do not need to worry about these []byte being // reclaimed by GC. 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.Ldb, wo.Opt, k, C.size_t(lenk), v, C.size_t(lenv), &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return DatabaseError(gs) } return nil } // Get returns the data associated with the key from the database. // // If the key does not exist in the database, a nil []byte is returned. If the // key does exist, but the data is zero-length in the database, a zero-length // []byte will be returned. // // The key byte slice may be reused safely. Get takes a copy of // them before returning. func (db *DB) Get(ro *ReadOptions, key []byte) ([]byte, error) { var errStr *C.char var vallen C.size_t var k *C.char if len(key) != 0 { k = (*C.char)(unsafe.Pointer(&key[0])) } value := C.leveldb_get( db.Ldb, ro.Opt, k, C.size_t(len(key)), &vallen, &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return nil, DatabaseError(gs) } if value == nil { return nil, nil } defer C.leveldb_free(unsafe.Pointer(value)) return C.GoBytes(unsafe.Pointer(value), C.int(vallen)), nil } // Delete removes the data associated with the key from the database. // // The key byte slice may be reused safely. Delete takes a copy of // them before returning. The WriteOptions passed in can be reused by // multiple calls to this and if the WriteOptions is left unchanged. 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.Ldb, wo.Opt, k, C.size_t(len(key)), &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return DatabaseError(gs) } return nil } // Write atomically writes a WriteBatch to disk. The WriteOptions // passed in can be reused by multiple calls to this and other methods. func (db *DB) Write(wo *WriteOptions, w *WriteBatch) error { var errStr *C.char C.leveldb_write(db.Ldb, wo.Opt, w.wbatch, &errStr) if errStr != nil { gs := C.GoString(errStr) C.leveldb_free(unsafe.Pointer(errStr)) return DatabaseError(gs) } return nil } // NewIterator returns an Iterator over the the database that uses the // ReadOptions given. // // Often, this is used for large, offline bulk reads while serving live // traffic. In that case, it may be wise to disable caching so that the data // processed by the returned Iterator does not displace the already cached // data. This can be done by calling SetFillCache(false) on the ReadOptions // before passing it here. // // Similarly, ReadOptions.SetSnapshot is also useful. // // The ReadOptions passed in can be reused by multiple calls to this // and other methods if the ReadOptions is left unchanged. func (db *DB) NewIterator(ro *ReadOptions) *Iterator { it := C.leveldb_create_iterator(db.Ldb, ro.Opt) return &Iterator{Iter: it} } // GetApproximateSizes returns the approximate number of bytes of file system // space used by one or more key ranges. // // The keys counted will begin at Range.Start and end on the key before // Range.Limit. func (db *DB) GetApproximateSizes(ranges []Range) []uint64 { starts := make([]*C.char, len(ranges)) limits := make([]*C.char, len(ranges)) startLens := make([]C.size_t, len(ranges)) limitLens := make([]C.size_t, len(ranges)) for i, r := range ranges { starts[i] = C.CString(string(r.Start)) startLens[i] = C.size_t(len(r.Start)) limits[i] = C.CString(string(r.Limit)) limitLens[i] = C.size_t(len(r.Limit)) } sizes := make([]uint64, len(ranges)) numranges := C.int(len(ranges)) startsPtr := &starts[0] limitsPtr := &limits[0] startLensPtr := &startLens[0] limitLensPtr := &limitLens[0] sizesPtr := (*C.uint64_t)(&sizes[0]) C.levigo_leveldb_approximate_sizes( db.Ldb, numranges, startsPtr, startLensPtr, limitsPtr, limitLensPtr, sizesPtr) for i := range ranges { C.free(unsafe.Pointer(starts[i])) C.free(unsafe.Pointer(limits[i])) } return sizes } // PropertyValue returns the value of a database property. // // Examples of properties include "leveldb.stats", "leveldb.sstables", // and "leveldb.num-files-at-level0". func (db *DB) PropertyValue(propName string) string { cname := C.CString(propName) value := C.GoString(C.leveldb_property_value(db.Ldb, cname)) C.free(unsafe.Pointer(cname)) return value } // NewSnapshot creates a new snapshot of the database. // // The Snapshot, when used in a ReadOptions, provides a consistent // view of state of the database at the the snapshot was created. // // To prevent memory leaks and resource strain in the database, the snapshot // returned must be released with DB.ReleaseSnapshot method on the DB that // created it. // // See the LevelDB documentation for details. func (db *DB) NewSnapshot() *Snapshot { return &Snapshot{C.leveldb_create_snapshot(db.Ldb)} } // ReleaseSnapshot removes the snapshot from the database's list of snapshots, // and deallocates it. func (db *DB) ReleaseSnapshot(snap *Snapshot) { C.leveldb_release_snapshot(db.Ldb, snap.snap) } // CompactRange runs a manual compaction on the Range of keys given. This is // not likely to be needed for typical usage. func (db *DB) CompactRange(r Range) { var start, limit *C.char if len(r.Start) != 0 { start = (*C.char)(unsafe.Pointer(&r.Start[0])) } if len(r.Limit) != 0 { limit = (*C.char)(unsafe.Pointer(&r.Limit[0])) } C.leveldb_compact_range( db.Ldb, start, C.size_t(len(r.Start)), limit, C.size_t(len(r.Limit))) } // Close closes the database, rendering it unusable for I/O, by deallocating // the underlying handle. // // Any attempts to use the DB after Close is called will panic. func (db *DB) Close() { C.leveldb_close(db.Ldb) }