first commit
This commit is contained in:
369
rocksdb/rocksdb.odin
Normal file
369
rocksdb/rocksdb.odin
Normal file
@@ -0,0 +1,369 @@
|
||||
package rocksdb
|
||||
|
||||
import "core:c"
|
||||
import "core:fmt"
|
||||
|
||||
foreign import rocksdb "system:rocksdb"
|
||||
|
||||
// RocksDB C API types
|
||||
RocksDB_T :: distinct rawptr
|
||||
RocksDB_Options :: distinct rawptr
|
||||
RocksDB_WriteOptions :: distinct rawptr
|
||||
RocksDB_ReadOptions :: distinct rawptr
|
||||
RocksDB_WriteBatch :: distinct rawptr
|
||||
RocksDB_Iterator :: distinct rawptr
|
||||
RocksDB_FlushOptions :: distinct rawptr
|
||||
|
||||
// Error type
|
||||
Error :: enum {
|
||||
None,
|
||||
OpenFailed,
|
||||
WriteFailed,
|
||||
ReadFailed,
|
||||
DeleteFailed,
|
||||
InvalidArgument,
|
||||
Corruption,
|
||||
NotFound,
|
||||
IOError,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
// Database handle with options
|
||||
DB :: struct {
|
||||
handle: RocksDB_T,
|
||||
options: RocksDB_Options,
|
||||
write_options: RocksDB_WriteOptions,
|
||||
read_options: RocksDB_ReadOptions,
|
||||
}
|
||||
|
||||
// Foreign C functions
|
||||
@(default_calling_convention = "c")
|
||||
foreign rocksdb {
|
||||
// Database operations
|
||||
rocksdb_open :: proc(options: RocksDB_Options, path: cstring, errptr: ^cstring) -> RocksDB_T ---
|
||||
rocksdb_close :: proc(db: RocksDB_T) ---
|
||||
|
||||
// Options
|
||||
rocksdb_options_create :: proc() -> RocksDB_Options ---
|
||||
rocksdb_options_destroy :: proc(options: RocksDB_Options) ---
|
||||
rocksdb_options_set_create_if_missing :: proc(options: RocksDB_Options, val: c.uchar) ---
|
||||
rocksdb_options_increase_parallelism :: proc(options: RocksDB_Options, total_threads: c.int) ---
|
||||
rocksdb_options_optimize_level_style_compaction :: proc(options: RocksDB_Options, memtable_memory_budget: c.uint64_t) ---
|
||||
rocksdb_options_set_compression :: proc(options: RocksDB_Options, compression: c.int) ---
|
||||
|
||||
// Write options
|
||||
rocksdb_writeoptions_create :: proc() -> RocksDB_WriteOptions ---
|
||||
rocksdb_writeoptions_destroy :: proc(options: RocksDB_WriteOptions) ---
|
||||
|
||||
// Read options
|
||||
rocksdb_readoptions_create :: proc() -> RocksDB_ReadOptions ---
|
||||
rocksdb_readoptions_destroy :: proc(options: RocksDB_ReadOptions) ---
|
||||
|
||||
// Put/Get/Delete
|
||||
rocksdb_put :: proc(db: RocksDB_T, options: RocksDB_WriteOptions, key: [^]byte, keylen: c.size_t, val: [^]byte, vallen: c.size_t, errptr: ^cstring) ---
|
||||
rocksdb_get :: proc(db: RocksDB_T, options: RocksDB_ReadOptions, key: [^]byte, keylen: c.size_t, vallen: ^c.size_t, errptr: ^cstring) -> [^]byte ---
|
||||
rocksdb_delete :: proc(db: RocksDB_T, options: RocksDB_WriteOptions, key: [^]byte, keylen: c.size_t, errptr: ^cstring) ---
|
||||
|
||||
// Flush
|
||||
rocksdb_flushoptions_create :: proc() -> RocksDB_FlushOptions ---
|
||||
rocksdb_flushoptions_destroy :: proc(options: RocksDB_FlushOptions) ---
|
||||
rocksdb_flush :: proc(db: RocksDB_T, options: RocksDB_FlushOptions, errptr: ^cstring) ---
|
||||
|
||||
// Write batch
|
||||
rocksdb_writebatch_create :: proc() -> RocksDB_WriteBatch ---
|
||||
rocksdb_writebatch_destroy :: proc(batch: RocksDB_WriteBatch) ---
|
||||
rocksdb_writebatch_put :: proc(batch: RocksDB_WriteBatch, key: [^]byte, keylen: c.size_t, val: [^]byte, vallen: c.size_t) ---
|
||||
rocksdb_writebatch_delete :: proc(batch: RocksDB_WriteBatch, key: [^]byte, keylen: c.size_t) ---
|
||||
rocksdb_writebatch_clear :: proc(batch: RocksDB_WriteBatch) ---
|
||||
rocksdb_write :: proc(db: RocksDB_T, options: RocksDB_WriteOptions, batch: RocksDB_WriteBatch, errptr: ^cstring) ---
|
||||
|
||||
// Iterator
|
||||
rocksdb_create_iterator :: proc(db: RocksDB_T, options: RocksDB_ReadOptions) -> RocksDB_Iterator ---
|
||||
rocksdb_iter_destroy :: proc(iter: RocksDB_Iterator) ---
|
||||
rocksdb_iter_seek_to_first :: proc(iter: RocksDB_Iterator) ---
|
||||
rocksdb_iter_seek_to_last :: proc(iter: RocksDB_Iterator) ---
|
||||
rocksdb_iter_seek :: proc(iter: RocksDB_Iterator, key: [^]byte, keylen: c.size_t) ---
|
||||
rocksdb_iter_seek_for_prev :: proc(iter: RocksDB_Iterator, key: [^]byte, keylen: c.size_t) ---
|
||||
rocksdb_iter_valid :: proc(iter: RocksDB_Iterator) -> c.uchar ---
|
||||
rocksdb_iter_next :: proc(iter: RocksDB_Iterator) ---
|
||||
rocksdb_iter_prev :: proc(iter: RocksDB_Iterator) ---
|
||||
rocksdb_iter_key :: proc(iter: RocksDB_Iterator, klen: ^c.size_t) -> [^]byte ---
|
||||
rocksdb_iter_value :: proc(iter: RocksDB_Iterator, vlen: ^c.size_t) -> [^]byte ---
|
||||
|
||||
// Memory management
|
||||
rocksdb_free :: proc(ptr: rawptr) ---
|
||||
}
|
||||
|
||||
// Compression types
|
||||
ROCKSDB_NO_COMPRESSION :: 0
|
||||
ROCKSDB_SNAPPY_COMPRESSION :: 1
|
||||
ROCKSDB_ZLIB_COMPRESSION :: 2
|
||||
ROCKSDB_BZIP2_COMPRESSION :: 3
|
||||
ROCKSDB_LZ4_COMPRESSION :: 4
|
||||
ROCKSDB_LZ4HC_COMPRESSION :: 5
|
||||
ROCKSDB_ZSTD_COMPRESSION :: 7
|
||||
|
||||
// Open a database
|
||||
db_open :: proc(path: string, create_if_missing := true) -> (DB, Error) {
|
||||
options := rocksdb_options_create()
|
||||
if options == nil {
|
||||
return {}, .Unknown
|
||||
}
|
||||
|
||||
// Set create if missing
|
||||
rocksdb_options_set_create_if_missing(options, create_if_missing ? 1 : 0)
|
||||
|
||||
// Performance optimizations
|
||||
rocksdb_options_increase_parallelism(options, 4)
|
||||
rocksdb_options_optimize_level_style_compaction(options, 512 * 1024 * 1024)
|
||||
rocksdb_options_set_compression(options, ROCKSDB_LZ4_COMPRESSION)
|
||||
|
||||
// Create write and read options
|
||||
write_options := rocksdb_writeoptions_create()
|
||||
if write_options == nil {
|
||||
rocksdb_options_destroy(options)
|
||||
return {}, .Unknown
|
||||
}
|
||||
|
||||
read_options := rocksdb_readoptions_create()
|
||||
if read_options == nil {
|
||||
rocksdb_writeoptions_destroy(write_options)
|
||||
rocksdb_options_destroy(options)
|
||||
return {}, .Unknown
|
||||
}
|
||||
|
||||
// Open database
|
||||
err: cstring
|
||||
path_cstr := fmt.ctprintf("%s", path)
|
||||
handle := rocksdb_open(options, path_cstr, &err)
|
||||
if err != nil {
|
||||
defer rocksdb_free(rawptr(err)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
rocksdb_readoptions_destroy(read_options)
|
||||
rocksdb_writeoptions_destroy(write_options)
|
||||
rocksdb_options_destroy(options)
|
||||
return {}, .OpenFailed
|
||||
}
|
||||
|
||||
return DB{
|
||||
handle = handle,
|
||||
options = options,
|
||||
write_options = write_options,
|
||||
read_options = read_options,
|
||||
}, .None
|
||||
}
|
||||
|
||||
// Close database
|
||||
db_close :: proc(db: ^DB) {
|
||||
rocksdb_readoptions_destroy(db.read_options)
|
||||
rocksdb_writeoptions_destroy(db.write_options)
|
||||
rocksdb_close(db.handle)
|
||||
rocksdb_options_destroy(db.options)
|
||||
}
|
||||
|
||||
// Put key-value pair
|
||||
db_put :: proc(db: ^DB, key: []byte, value: []byte) -> Error {
|
||||
err: cstring
|
||||
rocksdb_put(
|
||||
db.handle,
|
||||
db.write_options,
|
||||
raw_data(key),
|
||||
c.size_t(len(key)),
|
||||
raw_data(value),
|
||||
c.size_t(len(value)),
|
||||
&err,
|
||||
)
|
||||
if err != nil {
|
||||
defer rocksdb_free(rawptr(err)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
return .WriteFailed
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
// Get value by key (returns owned slice - caller must free)
|
||||
db_get :: proc(db: ^DB, key: []byte) -> (value: []byte, err: Error) {
|
||||
errptr: cstring
|
||||
value_len: c.size_t
|
||||
|
||||
value_ptr := rocksdb_get(
|
||||
db.handle,
|
||||
db.read_options,
|
||||
raw_data(key),
|
||||
c.size_t(len(key)),
|
||||
&value_len,
|
||||
&errptr,
|
||||
)
|
||||
|
||||
if errptr != nil {
|
||||
defer rocksdb_free(rawptr(errptr)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
return nil, .ReadFailed
|
||||
}
|
||||
|
||||
if value_ptr == nil {
|
||||
return nil, .NotFound
|
||||
}
|
||||
|
||||
// Copy the data and free RocksDB's buffer
|
||||
result := make([]byte, value_len, context.allocator)
|
||||
copy(result, value_ptr[:value_len])
|
||||
rocksdb_free(rawptr(value_ptr)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
|
||||
return result, .None
|
||||
}
|
||||
|
||||
// Delete key
|
||||
db_delete :: proc(db: ^DB, key: []byte) -> Error {
|
||||
err: cstring
|
||||
rocksdb_delete(
|
||||
db.handle,
|
||||
db.write_options,
|
||||
raw_data(key),
|
||||
c.size_t(len(key)),
|
||||
&err,
|
||||
)
|
||||
if err != nil {
|
||||
defer rocksdb_free(rawptr(err)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
return .DeleteFailed
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
// Flush database
|
||||
db_flush :: proc(db: ^DB) -> Error {
|
||||
flush_opts := rocksdb_flushoptions_create()
|
||||
if flush_opts == nil {
|
||||
return .Unknown
|
||||
}
|
||||
defer rocksdb_flushoptions_destroy(flush_opts)
|
||||
|
||||
err: cstring
|
||||
rocksdb_flush(db.handle, flush_opts, &err)
|
||||
if err != nil {
|
||||
defer rocksdb_free(rawptr(err)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
return .IOError
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
// Write batch
|
||||
WriteBatch :: struct {
|
||||
handle: RocksDB_WriteBatch,
|
||||
}
|
||||
|
||||
// Create write batch
|
||||
batch_create :: proc() -> (WriteBatch, Error) {
|
||||
handle := rocksdb_writebatch_create()
|
||||
if handle == nil {
|
||||
return {}, .Unknown
|
||||
}
|
||||
return WriteBatch{handle = handle}, .None
|
||||
}
|
||||
|
||||
// Destroy write batch
|
||||
batch_destroy :: proc(batch: ^WriteBatch) {
|
||||
rocksdb_writebatch_destroy(batch.handle)
|
||||
}
|
||||
|
||||
// Add put operation to batch
|
||||
batch_put :: proc(batch: ^WriteBatch, key: []byte, value: []byte) {
|
||||
rocksdb_writebatch_put(
|
||||
batch.handle,
|
||||
raw_data(key),
|
||||
c.size_t(len(key)),
|
||||
raw_data(value),
|
||||
c.size_t(len(value)),
|
||||
)
|
||||
}
|
||||
|
||||
// Add delete operation to batch
|
||||
batch_delete :: proc(batch: ^WriteBatch, key: []byte) {
|
||||
rocksdb_writebatch_delete(
|
||||
batch.handle,
|
||||
raw_data(key),
|
||||
c.size_t(len(key)),
|
||||
)
|
||||
}
|
||||
|
||||
// Clear batch
|
||||
batch_clear :: proc(batch: ^WriteBatch) {
|
||||
rocksdb_writebatch_clear(batch.handle)
|
||||
}
|
||||
|
||||
// Write batch to database
|
||||
batch_write :: proc(db: ^DB, batch: ^WriteBatch) -> Error {
|
||||
err: cstring
|
||||
rocksdb_write(db.handle, db.write_options, batch.handle, &err)
|
||||
if err != nil {
|
||||
defer rocksdb_free(rawptr(err)) // Cast it here and now so we don't deal with issues from FFI down the line
|
||||
return .WriteFailed
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
// Iterator
|
||||
Iterator :: struct {
|
||||
handle: RocksDB_Iterator,
|
||||
}
|
||||
|
||||
// Create iterator
|
||||
iter_create :: proc(db: ^DB) -> (Iterator, Error) {
|
||||
handle := rocksdb_create_iterator(db.handle, db.read_options)
|
||||
if handle == nil {
|
||||
return {}, .Unknown
|
||||
}
|
||||
return Iterator{handle = handle}, .None
|
||||
}
|
||||
|
||||
// Destroy iterator
|
||||
iter_destroy :: proc(iter: ^Iterator) {
|
||||
rocksdb_iter_destroy(iter.handle)
|
||||
}
|
||||
|
||||
// Seek to first
|
||||
iter_seek_to_first :: proc(iter: ^Iterator) {
|
||||
rocksdb_iter_seek_to_first(iter.handle)
|
||||
}
|
||||
|
||||
// Seek to last
|
||||
iter_seek_to_last :: proc(iter: ^Iterator) {
|
||||
rocksdb_iter_seek_to_last(iter.handle)
|
||||
}
|
||||
|
||||
// Seek to key
|
||||
iter_seek :: proc(iter: ^Iterator, target: []byte) {
|
||||
rocksdb_iter_seek(iter.handle, raw_data(target), c.size_t(len(target)))
|
||||
}
|
||||
|
||||
// Check if iterator is valid
|
||||
iter_valid :: proc(iter: ^Iterator) -> bool {
|
||||
return rocksdb_iter_valid(iter.handle) != 0
|
||||
}
|
||||
|
||||
// Move to next
|
||||
iter_next :: proc(iter: ^Iterator) {
|
||||
rocksdb_iter_next(iter.handle)
|
||||
}
|
||||
|
||||
// Move to previous
|
||||
iter_prev :: proc(iter: ^Iterator) {
|
||||
rocksdb_iter_prev(iter.handle)
|
||||
}
|
||||
|
||||
// Get current key (returns borrowed slice)
|
||||
iter_key :: proc(iter: ^Iterator) -> []byte {
|
||||
klen: c.size_t
|
||||
key_ptr := rocksdb_iter_key(iter.handle, &klen)
|
||||
if key_ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return key_ptr[:klen]
|
||||
}
|
||||
|
||||
// Get current value (returns borrowed slice)
|
||||
iter_value :: proc(iter: ^Iterator) -> []byte {
|
||||
vlen: c.size_t
|
||||
value_ptr := rocksdb_iter_value(iter.handle, &vlen)
|
||||
if value_ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return value_ptr[:vlen]
|
||||
}
|
||||
Reference in New Issue
Block a user