From e64caac9f0d360619ebfdf882d8197bb9ff7cbce Mon Sep 17 00:00:00 2001 From: Zachary Levy Date: Fri, 5 Jun 2026 13:34:59 -0700 Subject: [PATCH] Update LMDB blittable safety checks --- vendor/lmdb/examples/examples.odin | 8 +-- vendor/lmdb/lmdb.odin | 105 +++++++++++++++++++---------- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/vendor/lmdb/examples/examples.odin b/vendor/lmdb/examples/examples.odin index eff83b2..6a1f734 100644 --- a/vendor/lmdb/examples/examples.odin +++ b/vendor/lmdb/examples/examples.odin @@ -68,9 +68,9 @@ main :: proc() { db_handle: mdb.Dbi // Put transaction key := 7 - key_val := mdb.blittable_val(&key) + key_val := mdb.pod_val(&key) put_data := 12 - put_data_val := mdb.blittable_val(&put_data) + put_data_val := mdb.pod_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, {})) @@ -80,7 +80,7 @@ main :: proc() { 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)) + data_cpy := mdb.pod_copy(data_val, int) + mdb.txn_abort(txn_handle) fmt.println("Get result:", data_cpy) } diff --git a/vendor/lmdb/lmdb.odin b/vendor/lmdb/lmdb.odin index 6c072b0..fc376f1 100644 --- a/vendor/lmdb/lmdb.odin +++ b/vendor/lmdb/lmdb.odin @@ -169,58 +169,86 @@ import "core:fmt" import "core:reflect" import "core:sys/posix" +import b "../../basic" + // --------------------------------------------------------------------------------------------------------------------- // ----- Added Odin Helpers ------------------------ // --------------------------------------------------------------------------------------------------------------------- -// Wrap a blittable value's bytes as an LMDB Val. +// Wrap a POD 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), - ) +pod_val :: #force_inline proc(val_ptr: ^$T) -> Val { + when ODIN_DEBUG { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "pod_val: 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 +// Reads a POD 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), - ) +pod_copy :: #force_inline proc(val: Val, $T: typeid) -> T { + when ODIN_DEBUG { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "pod_copy: type '%v' contains indirection and cannot be read directly from LMDB", + typeid_of(T), + ) + } + when b.ODIN_BOUNDS_CHECK { + fmt.assertf( + val.size == size_of(T), + "size_of(%v) (%v) != val.size (%v)", + typeid_of(T), + size_of(T), + val.size, + ) + } 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 +// Useful for large POD 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), - ) +pod_view :: #force_inline proc(val: Val, $T: typeid) -> ^T { + when ODIN_DEBUG { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "pod_view: type '%v' contains indirection and cannot be viewed directly from LMDB", + typeid_of(T), + ) + } + when b.ODIN_BOUNDS_CHECK { + fmt.assertf( + val.size == size_of(T), + "size_of(%v) (%v) != val.size (%v)", + typeid_of(T), + size_of(T), + val.size, + ) + } return cast(^T)val.data } -// Wrap a slice of blittable elements as an LMDB Val for use with put/get. +// Wrap a slice of POD 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), - ) +pod_slice_val :: #force_inline proc(s: []$T) -> Val { + when ODIN_DEBUG { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "pod_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)} } @@ -231,12 +259,21 @@ slice_val :: #force_inline proc(s: []$T) -> Val { // 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), - ) +pod_slice_view :: #force_inline proc(val: Val, $T: typeid) -> []T { + when ODIN_DEBUG { + fmt.assertf( + reflect.has_no_indirections(type_info_of(T)), + "pod_slice_view: element type '%v' contains indirection and cannot be read directly from LMDB", + typeid_of(T), + ) + fmt.assertf( + val.size % size_of(T) == 0, + "pod_slice_view: val.size (%v) is not a multiple of size_of(%v) (%v)", + val.size, + typeid_of(T), + size_of(T), + ) + } return (cast([^]T)val.data)[:val.size / size_of(T)] } -- 2.43.0