Compare commits

5 Commits

Author SHA1 Message Date
Zachary Levy
ea19b83ba4 Cleanup 2026-04-21 16:59:11 -07:00
Zachary Levy
7650b90d91 Comment cleanup 2026-04-21 16:09:40 -07:00
Zachary Levy
ba522fa051 QR code improvements 2026-04-21 15:51:17 -07:00
Zachary Levy
a4623a13b5 Basic texture support 2026-04-21 13:46:41 -07:00
Zachary Levy
f85187eff3 Clean up memory management 2026-04-20 22:39:21 -07:00
12 changed files with 1123 additions and 1149 deletions

View File

@@ -1,18 +1,19 @@
package examples
import "core:fmt"
import "core:log"
import "core:mem"
import "core:os"
main :: proc() {
//----- General setup ----------------------------------
//----- Tracking allocator ----------------------------------
{
tracking_temp_allocator := false
// Temp
track_temp: mem.Tracking_Allocator
mem.tracking_allocator_init(&track_temp, context.temp_allocator)
context.temp_allocator = mem.tracking_allocator(&track_temp)
if tracking_temp_allocator {
mem.tracking_allocator_init(&track_temp, context.temp_allocator)
context.temp_allocator = mem.tracking_allocator(&track_temp)
}
// Default
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
@@ -21,10 +22,18 @@ main :: proc() {
// This could be fine for some global state or it could be a memory leak.
defer {
// Temp allocator
if len(track_temp.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - temp allocator: ===\n", len(track_temp.bad_free_array))
for entry in track_temp.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
if tracking_temp_allocator {
if len(track_temp.allocation_map) > 0 {
fmt.eprintf("=== %v allocations not freed - temp allocator: ===\n", len(track_temp.allocation_map))
for _, entry in track_temp.allocation_map {
fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location)
}
}
if len(track_temp.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - temp allocator: ===\n", len(track_temp.bad_free_array))
for entry in track_temp.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
}
}
mem.tracking_allocator_destroy(&track_temp)
}
@@ -43,9 +52,6 @@ main :: proc() {
}
mem.tracking_allocator_destroy(&track)
}
// Logger
context.logger = log.create_console_logger()
defer log.destroy_console_logger(context.logger)
}
args := os.args

View File

@@ -1,8 +1,6 @@
package meta
import "core:fmt"
import "core:log"
import "core:mem"
import "core:os"
Command :: struct {
@@ -22,48 +20,6 @@ COMMANDS :: []Command {
}
main :: proc() {
//----- General setup ----------------------------------
when ODIN_DEBUG {
// Temp
track_temp: mem.Tracking_Allocator
mem.tracking_allocator_init(&track_temp, context.temp_allocator)
context.temp_allocator = mem.tracking_allocator(&track_temp)
// Default
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
// Log a warning about any memory that was not freed by the end of the program.
// This could be fine for some global state or it could be a memory leak.
defer {
// Temp allocator
if len(track_temp.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - temp allocator: ===\n", len(track_temp.bad_free_array))
for entry in track_temp.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
}
mem.tracking_allocator_destroy(&track_temp)
}
// Default allocator
if len(track.allocation_map) > 0 {
fmt.eprintf("=== %v allocations not freed - main allocator: ===\n", len(track.allocation_map))
for _, entry in track.allocation_map {
fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location)
}
}
if len(track.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - main allocator: ===\n", len(track.bad_free_array))
for entry in track.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
}
}
mem.tracking_allocator_destroy(&track)
}
// Logger
context.logger = log.create_console_logger()
defer log.destroy_console_logger(context.logger)
}
args := os.args[1:]
if len(args) == 0 {

View File

@@ -1,32 +1,39 @@
package examples
import "core:fmt"
import "core:log"
import "core:mem"
import "core:os"
import qr ".."
main :: proc() {
//----- General setup ----------------------------------
//----- Tracking allocator ----------------------------------
{
tracking_temp_allocator := false
// Temp
track_temp: mem.Tracking_Allocator
mem.tracking_allocator_init(&track_temp, context.temp_allocator)
context.temp_allocator = mem.tracking_allocator(&track_temp)
if tracking_temp_allocator {
mem.tracking_allocator_init(&track_temp, context.temp_allocator)
context.temp_allocator = mem.tracking_allocator(&track_temp)
}
// Default
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
// Log a warning about any memory that was not freed by the end of the program.
// This could be fine for some global state or it could be a memory leak.
defer {
// Temp allocator
if len(track_temp.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - temp allocator: ===\n", len(track_temp.bad_free_array))
for entry in track_temp.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
if tracking_temp_allocator {
if len(track_temp.allocation_map) > 0 {
fmt.eprintf("=== %v allocations not freed - temp allocator: ===\n", len(track_temp.allocation_map))
for _, entry in track_temp.allocation_map {
fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location)
}
}
if len(track_temp.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - temp allocator: ===\n", len(track_temp.bad_free_array))
for entry in track_temp.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
}
}
mem.tracking_allocator_destroy(&track_temp)
}
@@ -45,9 +52,6 @@ main :: proc() {
}
mem.tracking_allocator_destroy(&track)
}
// Logger
context.logger = log.create_console_logger()
defer log.destroy_console_logger(context.logger)
}
args := os.args

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,11 +1,8 @@
package examples
import "core:fmt"
import "core:log"
import "core:mem"
import "core:os"
import "core:sys/posix"
import mdb "../../lmdb"
// 0o660
@@ -13,75 +10,34 @@ DB_MODE :: posix.mode_t{.IWGRP, .IRGRP, .IWUSR, .IRUSR}
DB_PATH :: "out/debug/lmdb_example_db"
main :: proc() {
//----- General setup ----------------------------------
{
// Temp
track_temp: mem.Tracking_Allocator
mem.tracking_allocator_init(&track_temp, context.temp_allocator)
context.temp_allocator = mem.tracking_allocator(&track_temp)
environment: ^mdb.Env
// Default
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
// Log a warning about any memory that was not freed by the end of the program.
// This could be fine for some global state or it could be a memory leak.
defer {
// Temp allocator
if len(track_temp.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - temp allocator: ===\n", len(track_temp.bad_free_array))
for entry in track_temp.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
}
mem.tracking_allocator_destroy(&track_temp)
}
// Default allocator
if len(track.allocation_map) > 0 {
fmt.eprintf("=== %v allocations not freed - main allocator: ===\n", len(track.allocation_map))
for _, entry in track.allocation_map {
fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location)
}
}
if len(track.bad_free_array) > 0 {
fmt.eprintf("=== %v incorrect frees - main allocator: ===\n", len(track.bad_free_array))
for entry in track.bad_free_array {
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
}
}
mem.tracking_allocator_destroy(&track)
}
// Logger
context.logger = log.create_console_logger()
defer log.destroy_console_logger(context.logger)
}
// Create environment for lmdb
mdb.panic_on_err(mdb.env_create(&environment))
// Create directory for databases. Won't do anything if it already exists.
// 0o774 gives all permissions for owner and group, read for everyone else.
os.make_directory(DB_PATH, 0o774)
// Open the database files (creates them if they don't already exist)
mdb.panic_on_err(mdb.env_open(environment, DB_PATH, 0, DB_MODE))
environment: ^mdb.Env
// Transactions
txn_handle: ^mdb.Txn
db_handle: mdb.Dbi
// Put transaction
key := 7
key_val := mdb.autoval(&key)
put_data := 12
put_data_val := mdb.autoval(&put_data)
mdb.panic_on_err(mdb.txn_begin(environment, nil, 0, &txn_handle))
mdb.panic_on_err(mdb.dbi_open(txn_handle, nil, 0, &db_handle))
mdb.panic_on_err(mdb.put(txn_handle, db_handle, &key_val.raw, &put_data_val.raw, 0))
mdb.panic_on_err(mdb.txn_commit(txn_handle))
// Create environment for lmdb
mdb.panic_on_err(mdb.env_create(&environment))
// Create directory for databases. Won't do anything if it already exists.
os.make_directory(DB_PATH)
// Open the database files (creates them if they don't already exist)
mdb.panic_on_err(mdb.env_open(environment, DB_PATH, {}, DB_MODE))
// Transactions
txn_handle: ^mdb.Txn
db_handle: mdb.Dbi
// Put transaction
key := 7
key_val := mdb.blittable_val(&key)
put_data := 12
put_data_val := mdb.blittable_val(&put_data)
mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle))
mdb.panic_on_err(mdb.dbi_open(txn_handle, nil, {}, &db_handle))
mdb.panic_on_err(mdb.put(txn_handle, db_handle, &key_val, &put_data_val, {}))
mdb.panic_on_err(mdb.txn_commit(txn_handle))
// Get transaction
data_val: mdb.Val
mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle))
mdb.panic_on_err(mdb.get(txn_handle, db_handle, &key_val, &data_val))
data_cpy := mdb.blittable_copy(&data_val, int)
mdb.panic_on_err(mdb.txn_commit(txn_handle))
fmt.println("Get result:", data_cpy)
// Get transaction
get_data_val := mdb.nil_autoval(int)
mdb.panic_on_err(mdb.txn_begin(environment, nil, 0, &txn_handle))
mdb.panic_on_err(mdb.get(txn_handle, db_handle, &key_val.raw, &get_data_val.raw))
mdb.panic_on_err(mdb.txn_commit(txn_handle))
data_cpy := mdb.autoval_get_data(&get_data_val)^
fmt.println("Get result:", data_cpy)
}

350
vendor/lmdb/lmdb.odin vendored
View File

@@ -164,123 +164,24 @@
*/
package lmdb
foreign import lib "system:lmdb"
import "core:c"
import "core:fmt"
import "core:reflect"
import "core:sys/posix"
// ---------------------------------------------------------------------------------------------------------------------
// ----- Added Odin Helpers ------------------------
// ---------------------------------------------------------------------------------------------------------------------
// Wrap a blittable value's bytes as an LMDB Val.
// T must be a contiguous type with no indirection (no pointers, slices, strings, maps, etc.).
blittable_val :: #force_inline proc(val_ptr: ^$T) -> Val {
fmt.assertf(
reflect.has_no_indirections(type_info_of(T)),
"blitval: type '%v' contains indirection and cannot be stored directly in LMDB",
typeid_of(T),
)
return Val{size_of(T), val_ptr}
}
// Reads a blittable T out of the LMDB memory map by copying it into caller
// storage. The returned T has no lifetime tie to the transaction.
blittable_copy :: #force_inline proc(val: ^Val, $T: typeid) -> T {
fmt.assertf(
reflect.has_no_indirections(type_info_of(T)),
"blitval_copy: type '%v' contains indirection and cannot be read directly from LMDB",
typeid_of(T),
)
return (cast(^T)val.data)^
}
// Zero-copy pointer view into the LMDB memory map as a ^T.
// Useful for large blittable types where you want to read individual fields
// without copying the entire value (e.g. ptr.timestamp, ptr.flags).
// MUST NOT be written through — writes either segfault (default env mode)
// or silently corrupt the database (ENV_WRITEMAP).
// MUST NOT be retained past txn_commit, txn_abort, or any subsequent write
// operation on the same env — the pointer is invalidated.
blittable_view :: #force_inline proc(val: ^Val, $T: typeid) -> ^T {
fmt.assertf(
reflect.has_no_indirections(type_info_of(T)),
"blitval_view: type '%v' contains indirection and cannot be viewed directly from LMDB",
typeid_of(T),
)
return cast(^T)val.data
}
// Wrap a slice of blittable elements as an LMDB Val for use with put/get.
// T must be a contiguous type with no indirection.
// The caller's slice must remain valid (not freed, not resized) for the
// duration of the put call that consumes this Val.
slice_val :: #force_inline proc(s: []$T) -> Val {
fmt.assertf(
reflect.has_no_indirections(type_info_of(T)),
"slice_val: element type '%v' contains indirection and cannot be stored directly in LMDB",
typeid_of(T),
)
return Val{uint(len(s) * size_of(T)), raw_data(s)}
}
// Zero-copy slice view into the LMDB memory map.
// T must match the element type that was originally stored.
// MUST NOT be modified — writes through this slice either segfault (default
// env mode) or silently corrupt the database (ENV_WRITEMAP).
// MUST be copied (e.g. slice.clone) if it needs to outlive the current
// transaction; the view is invalidated by txn_commit, txn_abort, or any
// subsequent write operation on the same env.
slice_view :: #force_inline proc(val: ^Val, $T: typeid) -> []T {
fmt.assertf(
reflect.has_no_indirections(type_info_of(T)),
"slice_view: element type '%v' contains indirection and cannot be read directly from LMDB",
typeid_of(T),
)
return (cast([^]T)val.data)[:val.size / size_of(T)]
}
// Wrap a string's bytes as an LMDB Val for use with put/get.
// The caller's string must remain valid (backing memory not freed) for the
// duration of the put call that consumes this Val.
string_val :: #force_inline proc(s: string) -> Val {
return Val{uint(len(s)), raw_data(s)}
}
// Zero-copy string view into the LMDB memory map.
// MUST NOT be modified — writes through the underlying bytes either segfault
// (default env mode) or silently corrupt the database (ENV_WRITEMAP).
// MUST be copied (e.g. strings.clone) if it needs to outlive the current
// transaction; the view is invalidated by txn_commit, txn_abort, or any
// subsequent write operation on the same env.
string_view :: #force_inline proc(val: ^Val) -> string {
return string((cast([^]u8)val.data)[:val.size])
}
// Panic if there is an error
panic_on_err :: #force_inline proc(error: Error, loc := #caller_location) {
if error != .NONE {
fmt.panicf("LMDB error %v: %s", error, strerror(i32(error)), loc = loc)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Bindings ------------------------
// ---------------------------------------------------------------------------------------------------------------------
_ :: c
when ODIN_OS == .Windows {
#panic("TODO: Compile windows .lib for lmdb")
mode_t :: c.int
filehandle_t :: rawptr
} else when ODIN_OS ==
.Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
foreign import lib "system:lmdb"
mode_t :: posix.mode_t
filehandle_t :: c.int
} else {
#panic("levlib/vendor/lmdb: unsupported OS target")
mode_t :: posix.mode_t
}
when ODIN_OS == .Windows {
filehandle_t :: rawptr
} else {
filehandle_t :: c.int
}
Env :: struct {}
@@ -288,7 +189,7 @@ Env :: struct {}
Txn :: struct {}
/** @brief A handle for an individual database in the DB environment. */
Dbi :: c.uint
Dbi :: u32
Cursor :: struct {}
@@ -304,8 +205,33 @@ Cursor :: struct {}
* Other data items can in theory be from 0 to 0xffffffff bytes long.
*/
Val :: struct {
size: uint, /**< size of the data item */
data: rawptr, /**< address of the data item */
mv_size: uint, /**< size of the data item */
mv_data: rawptr, /**< address of the data item */
}
// Automatic `Val` handling for a given type 'T'.
// Will not traverse pointers. If `T` stores pointers, you probably don't want to use this.
Auto_Val :: struct($T: typeid) {
raw: Val,
}
autoval :: #force_inline proc "contextless" (val_ptr: ^$T) -> Auto_Val(T) {
return Auto_Val(T){Val{size_of(T), val_ptr}}
}
nil_autoval :: #force_inline proc "contextless" ($T: typeid) -> Auto_Val(T) {
return Auto_Val(T){Val{size_of(T), nil}}
}
autoval_get_data :: #force_inline proc "contextless" (val: ^Auto_Val($T)) -> ^T {
return cast(^T)val.raw.mv_data
}
// Panic if there is an error
panic_on_err :: #force_inline proc(error: Error) {
if error != .NONE {
fmt.panicf("Irrecoverable LMDB error", strerror(i32(error)))
}
}
/** @brief A callback function used to compare two keys in a database */
@@ -327,65 +253,85 @@ Cmp_Func :: #type proc "c" (_: ^Val, _: ^Val) -> i32
*/
Rel_Func :: #type proc "c" (item: ^Val, oldptr, newptr, relctx: rawptr)
/** @defgroup mdb_env Environment Flags
/** @defgroup mdb_env Environment Flags
* @{
*/
Env_Flag :: enum u32 {
FIXEDMAP = 0, /**< mmap at a fixed address (experimental) */
NOSUBDIR = 14, /**< no environment directory */
NOSYNC = 16, /**< don't fsync after commit */
RDONLY = 17, /**< read only */
NOMETASYNC = 18, /**< don't fsync metapage after commit */
WRITEMAP = 19, /**< use writable mmap */
MAPASYNC = 20, /**< use asynchronous msync when WRITEMAP is used */
NOTLS = 21, /**< tie reader locktable slots to Txn objects instead of to threads */
NOLOCK = 22, /**< don't do any locking, caller must manage their own locks */
NORDAHEAD = 23, /**< don't do readahead (no effect on Windows) */
NOMEMINIT = 24, /**< don't initialize malloc'd memory before writing to datafile */
PREVSNAPSHOT = 25, /**< use the previous snapshot rather than the latest one */
}
Env_Flags :: distinct bit_set[Env_Flag;c.uint]
/** mmap at a fixed address (experimental) */
ENV_FIXEDMAP :: 0x01
/** no environment directory */
ENV_NOSUBDIR :: 0x4000
/** don't fsync after commit */
ENV_NOSYNC :: 0x10000
/** read only */
ENV_RDONLY :: 0x20000
/** don't fsync metapage after commit */
ENV_NOMETASYNC :: 0x40000
/** use writable mmap */
ENV_WRITEMAP :: 0x80000
/** use asynchronous msync when #MDB_WRITEMAP is used */
ENV_MAPASYNC :: 0x100000
/** tie reader locktable slots to #MDB_txn objects instead of to threads */
ENV_NOTLS :: 0x200000
/** don't do any locking, caller must manage their own locks */
ENV_NOLOCK :: 0x400000
/** don't do readahead (no effect on Windows) */
ENV_NORDAHEAD :: 0x800000
/** don't initialize malloc'd memory before writing to datafile */
ENV_NOMEMINIT :: 0x1000000
/** @} */
/** @defgroup mdb_dbi_open Database Flags
/** @defgroup mdb_dbi_open Database Flags
* @{
*/
Db_Flag :: enum u32 {
REVERSEKEY = 1, /**< use reverse string keys */
DUPSORT = 2, /**< use sorted duplicates */
INTEGERKEY = 3, /**< numeric keys in native byte order */
DUPFIXED = 4, /**< with DUPSORT, sorted dup items have fixed size */
INTEGERDUP = 5, /**< with DUPSORT, dups are INTEGERKEY-style integers */
REVERSEDUP = 6, /**< with DUPSORT, use reverse string dups */
CREATE = 18, /**< create DB if not already existing */
}
Db_Flags :: distinct bit_set[Db_Flag;c.uint]
/** use reverse string keys */
DB_REVERSEKEY :: 0x02
/** use sorted duplicates */
DB_DUPSORT :: 0x04
/** numeric keys in native byte order: either unsigned int or size_t.
* The keys must all be of the same size. */
DB_INTEGERKEY :: 0x08
/** with #MDB_DUPSORT, sorted dup items have fixed size */
DB_DUPFIXED :: 0x10
/** with #MDB_DUPSORT, dups are #MDB_INTEGERKEY-style integers */
DB_INTEGERDUP :: 0x20
/** with #MDB_DUPSORT, use reverse string dups */
DB_REVERSEDUP :: 0x40
/** create DB if not already existing */
DB_CREATE :: 0x40000
/** @} */
/** @defgroup mdb_put Write Flags
/** @defgroup mdb_put Write Flags
* @{
*/
Write_Flag :: enum u32 {
NOOVERWRITE = 4, /**< For put: Don't write if the key already exists */
NODUPDATA = 5, /**< For DUPSORT: don't write if the key and data pair already exist.
For mdb_cursor_del: remove all duplicate data items. */
CURRENT = 6, /**< For mdb_cursor_put: overwrite the current key/data pair */
RESERVE = 16, /**< For put: Just reserve space for data, don't copy it */
APPEND = 17, /**< Data is being appended, don't split full pages */
APPENDDUP = 18, /**< Duplicate data is being appended, don't split full pages */
MULTIPLE = 19, /**< Store multiple data items in one call. Only for DUPFIXED. */
}
Write_Flags :: distinct bit_set[Write_Flag;c.uint]
/** @} */
/** For put: Don't write if the key already exists. */
WRITE_NOOVERWRITE :: 0x10
/** Only for #MDB_DUPSORT<br>
* For put: don't write if the key and data pair already exist.<br>
* For mdb_cursor_del: remove all duplicate data items.
*/
WRITE_NODUPDATA :: 0x20
/** For mdb_cursor_put: overwrite the current key/data pair */
WRITE_CURRENT :: 0x40
/** For put: Just reserve space for data, don't copy it. Return a
* pointer to the reserved space.
*/
WRITE_RESERVE :: 0x10000
/** Data is being appended, don't split full pages. */
WRITE_APPEND :: 0x20000
/** Duplicate data is being appended, don't split full pages. */
WRITE_APPENDDUP :: 0x40000
/** Store multiple data items in one call. Only for #MDB_DUPFIXED. */
WRITE_MULTIPLE :: 0x80000
/* @} */
/** @defgroup mdb_copy Copy Flags
/** @defgroup mdb_copy Copy Flags
* @{
*/
Copy_Flag :: enum u32 {
COMPACT = 0, /**< Compacting copy: Omit free space from copy, and renumber all pages sequentially. */
}
Copy_Flags :: distinct bit_set[Copy_Flag;c.uint]
/** @} */
/** Compacting copy: Omit free space from copy, and renumber all
* pages sequentially.
*/
CP_COMPACT :: 0x01
/* @} */
/** @brief Cursor Get operations.
*
@@ -394,24 +340,33 @@ Copy_Flags :: distinct bit_set[Copy_Flag;c.uint]
*/
Cursor_Op :: enum c.int {
FIRST, /**< Position at first key/data item */
FIRST_DUP, /**< Position at first data item of current key. Only for DUPSORT */
GET_BOTH, /**< Position at key/data pair. Only for DUPSORT */
GET_BOTH_RANGE, /**< Position at key, nearest data. Only for DUPSORT */
FIRST_DUP, /**< Position at first data item of current key.
Only for #MDB_DUPSORT */
GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */
GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */
GET_CURRENT, /**< Return key/data at current cursor position */
GET_MULTIPLE, /**< Return up to a page of duplicate data items from current cursor position. Only for DUPFIXED */
GET_MULTIPLE, /**< Return up to a page of duplicate data items
from current cursor position. Move cursor to prepare
for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
LAST, /**< Position at last key/data item */
LAST_DUP, /**< Position at last data item of current key. Only for DUPSORT */
LAST_DUP, /**< Position at last data item of current key.
Only for #MDB_DUPSORT */
NEXT, /**< Position at next data item */
NEXT_DUP, /**< Position at next data item of current key. Only for DUPSORT */
NEXT_MULTIPLE, /**< Return up to a page of duplicate data items from next cursor position. Only for DUPFIXED */
NEXT_DUP, /**< Position at next data item of current key.
Only for #MDB_DUPSORT */
NEXT_MULTIPLE, /**< Return up to a page of duplicate data items
from next cursor position. Move cursor to prepare
for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
NEXT_NODUP, /**< Position at first data item of next key */
PREV, /**< Position at previous data item */
PREV_DUP, /**< Position at previous data item of current key. Only for DUPSORT */
PREV_DUP, /**< Position at previous data item of current key.
Only for #MDB_DUPSORT */
PREV_NODUP, /**< Position at last data item of previous key */
SET, /**< Position at specified key */
SET_KEY, /**< Position at specified key, return key + data */
SET_RANGE, /**< Position at first key greater than or equal to specified key */
PREV_MULTIPLE, /**< Position at previous page and return up to a page of duplicate data items. Only for DUPFIXED */
SET_RANGE, /**< Position at first key greater than or equal to specified key. */
PREV_MULTIPLE, /**< Position at previous page and return up to
a page of duplicate data items. Only for #MDB_DUPFIXED */
}
Error :: enum c.int {
@@ -464,28 +419,33 @@ Error :: enum c.int {
BAD_VALSIZE = -30781,
/** The specified DBI was changed unexpectedly */
BAD_DBI = -30780,
/** Unexpected problem - txn should abort */
PROBLEM = -30779,
}
/** @brief Statistics for a database in the environment */
Stat :: struct {
psize: u32, /**< Size of a database page. This is currently the same for all databases. */
depth: u32, /**< Depth (height) of the B-tree */
branch_pages: uint, /**< Number of internal (non-leaf) pages */
leaf_pages: uint, /**< Number of leaf pages */
overflow_pages: uint, /**< Number of overflow pages */
entries: uint, /**< Number of data items */
ms_psize: u32,
/**< Size of a database page.
This is currently the same for all databases. */
ms_depth: u32,
/**< Depth (height) of the B-tree */
ms_branch_pages: uint,
/**< Number of internal (non-leaf) pages */
ms_leaf_pages: uint,
/**< Number of leaf pages */
ms_overflow_pages: uint,
/**< Number of overflow pages */
ms_entries: uint,
/**< Number of data items */
}
/** @brief Information about the environment */
Env_Info :: struct {
mapaddr: rawptr, /**< Address of map, if fixed */
mapsize: uint, /**< Size of the data memory map */
last_pgno: uint, /**< ID of the last used page */
last_txnid: uint, /**< ID of the last committed transaction */
maxreaders: u32, /**< max reader slots in the environment */
numreaders: u32, /**< max reader slots used in the environment */
me_mapaddr: rawptr, /**< Address of map, if fixed */
me_mapsize: uint, /**< Size of the data memory map */
me_last_pgno: uint, /**< ID of the last used page */
me_last_txnid: uint, /**< ID of the last committed transaction */
me_maxreaders: u32, /**< max reader slots in the environment */
me_numreaders: u32, /**< max reader slots used in the environment */
}
/** @brief A callback function for most LMDB assert() failures,
@@ -494,7 +454,7 @@ Env_Info :: struct {
* @param[in] env An environment handle returned by #mdb_env_create().
* @param[in] msg The assertion message, not including newline.
*/
Assert_Func :: #type proc "c" (_: ^Env, _: cstring)
Assert_Func :: proc "c" (_: ^Env, _: cstring)
/** @brief A callback function used to print a message from the library.
*
@@ -502,7 +462,7 @@ Assert_Func :: #type proc "c" (_: ^Env, _: cstring)
* @param[in] ctx An arbitrary context pointer for the callback.
* @return < 0 on failure, >= 0 on success.
*/
Msg_Func :: #type proc "c" (_: cstring, _: rawptr) -> i32
Msg_Func :: proc "c" (_: cstring, _: rawptr) -> i32
@(default_calling_convention = "c", link_prefix = "mdb_")
foreign lib {
@@ -663,7 +623,7 @@ foreign lib {
* </ul>
*/
@(require_results)
env_open :: proc(env: ^Env, path: cstring, flags: Env_Flags, mode: mode_t) -> Error ---
env_open :: proc(env: ^Env, path: cstring, flags: u32, mode: mode_t) -> Error ---
/** @brief Copy an LMDB environment to the specified path.
*
@@ -722,7 +682,7 @@ foreign lib {
* @return A non-zero error value on failure and 0 on success.
*/
@(require_results)
env_copy2 :: proc(env: ^Env, path: cstring, flags: Copy_Flags) -> Error ---
env_copy2 :: proc(env: ^Env, path: cstring, flags: u32) -> Error ---
/** @brief Copy an LMDB environment to the specified file descriptor,
* with options.
@@ -742,7 +702,7 @@ foreign lib {
* @return A non-zero error value on failure and 0 on success.
*/
@(require_results)
env_copyfd2 :: proc(env: ^Env, fd: filehandle_t, flags: Copy_Flags) -> Error ---
env_copyfd2 :: proc(env: ^Env, fd: filehandle_t, flags: u32) -> Error ---
/** @brief Return statistics about the LMDB environment.
*
@@ -807,7 +767,7 @@ foreign lib {
* </ul>
*/
@(require_results)
env_set_flags :: proc(env: ^Env, flags: Env_Flags, onoff: i32) -> Error ---
env_set_flags :: proc(env: ^Env, flags: u32, onoff: i32) -> Error ---
/** @brief Get environment flags.
*
@@ -820,7 +780,7 @@ foreign lib {
* </ul>
*/
@(require_results)
env_get_flags :: proc(env: ^Env, flags: ^Env_Flags) -> Error ---
env_get_flags :: proc(env: ^Env, flags: ^u32) -> Error ---
/** @brief Return the path that was used in #mdb_env_open().
*
@@ -1013,7 +973,7 @@ foreign lib {
* </ul>
*/
@(require_results)
txn_begin :: proc(env: ^Env, parent: ^Txn, flags: Env_Flags, txn: ^^Txn) -> Error ---
txn_begin :: proc(env: ^Env, parent: ^Txn, flags: u32, txn: ^^Txn) -> Error ---
/** @brief Returns the transaction's #MDB_env
*
@@ -1166,7 +1126,7 @@ foreign lib {
* </ul>
*/
@(require_results)
dbi_open :: proc(txn: ^Txn, name: cstring, flags: Db_Flags, dbi: ^Dbi) -> Error ---
dbi_open :: proc(txn: ^Txn, name: cstring, flags: u32, dbi: ^Dbi) -> Error ---
/** @brief Retrieve statistics for a database.
*
@@ -1191,7 +1151,7 @@ foreign lib {
* @return A non-zero error value on failure and 0 on success.
*/
@(require_results)
dbi_flags :: proc(txn: ^Txn, dbi: Dbi, flags: ^Db_Flags) -> Error ---
dbi_flags :: proc(txn: ^Txn, dbi: Dbi, flags: ^u32) -> Error ---
/** @brief Close a database handle. Normally unnecessary. Use with care:
*
@@ -1269,7 +1229,6 @@ foreign lib {
@(require_results)
set_dupsort :: proc(txn: ^Txn, dbi: Dbi, cmp: Cmp_Func) -> Error ---
// NOTE: Unimplemented in current LMDB — this function has no effect.
/** @brief Set a relocation function for a #MDB_FIXEDMAP database.
*
* @todo The relocation function is called whenever it is necessary to move the data
@@ -1291,7 +1250,6 @@ foreign lib {
@(require_results)
set_relfunc :: proc(txn: ^Txn, dbi: Dbi, rel: Rel_Func) -> Error ---
// NOTE: Unimplemented in current LMDB — this function has no effect.
/** @brief Set a context pointer for a #MDB_FIXEDMAP database's relocation function.
*
* See #mdb_set_relfunc and #MDB_rel_func for more details.
@@ -1386,7 +1344,7 @@ foreign lib {
* </ul>
*/
@(require_results)
put :: proc(txn: ^Txn, dbi: Dbi, key: ^Val, data: ^Val, flags: Write_Flags) -> Error ---
put :: proc(txn: ^Txn, dbi: Dbi, key: ^Val, data: ^Val, flags: u32) -> Error ---
/** @brief Delete items from a database.
*
@@ -1559,7 +1517,7 @@ foreign lib {
* </ul>
*/
@(require_results)
cursor_put :: proc(cursor: ^Cursor, key: ^Val, data: ^Val, flags: Write_Flags) -> Error ---
cursor_put :: proc(cursor: ^Cursor, key: ^Val, data: ^Val, flags: u32) -> Error ---
/** @brief Delete current key/data pair
*
@@ -1583,7 +1541,7 @@ foreign lib {
* </ul>
*/
@(require_results)
cursor_del :: proc(cursor: ^Cursor, flags: Write_Flags) -> Error ---
cursor_del :: proc(cursor: ^Cursor, flags: u32) -> Error ---
/** @brief Return count of duplicates for current key.
*