diff --git a/internal/api/bindings.h b/internal/api/bindings.h index 5b38b7736..a2e9950a9 100644 --- a/internal/api/bindings.h +++ b/internal/api/bindings.h @@ -260,6 +260,16 @@ typedef struct Iterator_vtable { struct UnmanagedVector*, struct UnmanagedVector*, struct UnmanagedVector*); + int32_t (*next_key_db)(struct iterator_t, + struct gas_meter_t*, + uint64_t*, + struct UnmanagedVector*, + struct UnmanagedVector*); + int32_t (*next_value_db)(struct iterator_t, + struct gas_meter_t*, + uint64_t*, + struct UnmanagedVector*, + struct UnmanagedVector*); } Iterator_vtable; typedef struct GoIter { diff --git a/internal/api/callbacks.go b/internal/api/callbacks.go index 0f0cdcce9..66e96836a 100644 --- a/internal/api/callbacks.go +++ b/internal/api/callbacks.go @@ -13,6 +13,8 @@ typedef GoError (*remove_db_fn)(db_t *ptr, gas_meter_t *gas_meter, uint64_t *use typedef GoError (*scan_db_fn)(db_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, U8SliceView start, U8SliceView end, int32_t order, GoIter *out, UnmanagedVector *errOut); // iterator typedef GoError (*next_db_fn)(iterator_t idx, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *val, UnmanagedVector *errOut); +typedef GoError (*next_key_db_fn)(iterator_t idx, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *errOut); +typedef GoError (*next_value_db_fn)(iterator_t idx, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *val, UnmanagedVector *errOut); // and api typedef GoError (*humanize_address_fn)(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas); typedef GoError (*canonicalize_address_fn)(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas); @@ -25,6 +27,8 @@ GoError cDelete_cgo(db_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, U8Sli GoError cScan_cgo(db_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, U8SliceView start, U8SliceView end, int32_t order, GoIter *out, UnmanagedVector *errOut); // iterator GoError cNext_cgo(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *val, UnmanagedVector *errOut); +GoError cNextKey_cgo(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *errOut); +GoError cNextValue_cgo(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *val, UnmanagedVector *errOut); // api GoError cHumanAddress_cgo(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas); GoError cCanonicalAddress_cgo(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas); @@ -128,7 +132,9 @@ func buildDB(state *DBState, gm *types.GasMeter) C.Db { } var iterator_vtable = C.Iterator_vtable{ - next_db: (C.next_db_fn)(C.cNext_cgo), + next_db: (C.next_db_fn)(C.cNext_cgo), + next_key_db: (C.next_key_db_fn)(C.cNextKey_cgo), + next_value_db: (C.next_value_db_fn)(C.cNextValue_cgo), } // An iterator including referenced objects is 117 bytes large (calculated using https://github.com/DmitriyVTitov/size). @@ -310,6 +316,55 @@ func cNext(ref C.iterator_t, gasMeter *C.gas_meter_t, usedGas *cu64, key *C.Unma return C.GoError_None } +//export cNextKey +func cNextKey(ref C.iterator_t, gasMeter *C.gas_meter_t, usedGas *cu64, key *C.UnmanagedVector, errOut *C.UnmanagedVector) (ret C.GoError) { + return nextPart(ref, gasMeter, usedGas, key, errOut, func(iter types.Iterator) []byte { return iter.Key() }) +} + +//export cNextValue +func cNextValue(ref C.iterator_t, gasMeter *C.gas_meter_t, usedGas *cu64, value *C.UnmanagedVector, errOut *C.UnmanagedVector) (ret C.GoError) { + return nextPart(ref, gasMeter, usedGas, value, errOut, func(iter types.Iterator) []byte { return iter.Value() }) +} + +// nextPart is a helper function that contains the shared code for key- and value-only iteration. +func nextPart(ref C.iterator_t, gasMeter *C.gas_meter_t, usedGas *cu64, output *C.UnmanagedVector, errOut *C.UnmanagedVector, valFn func(types.Iterator) []byte) (ret C.GoError) { + // typical usage of iterator + // for ; itr.Valid(); itr.Next() { + // k, v := itr.Key(); itr.Value() + // ... + // } + + defer recoverPanic(&ret) + if ref.call_id == 0 || gasMeter == nil || usedGas == nil || output == nil || errOut == nil { + // we received an invalid pointer + return C.GoError_BadArgument + } + if !(*output).is_none || !(*errOut).is_none { + panic("Got a non-none UnmanagedVector we're about to override. This is a bug because someone has to drop the old one.") + } + + gm := *(*types.GasMeter)(unsafe.Pointer(gasMeter)) + iter := retrieveIterator(uint64(ref.call_id), uint64(ref.iterator_index)) + if iter == nil { + panic("Unable to retrieve iterator.") + } + if !iter.Valid() { + // end of iterator, return as no-op, nil key is considered end + return C.GoError_None + } + + gasBefore := gm.GasConsumed() + // call Next at the end, upon creation we have first data loaded + out := valFn(iter) + // check iter.Error() ???? + iter.Next() + gasAfter := gm.GasConsumed() + *usedGas = (cu64)(gasAfter - gasBefore) + + *output = newUnmanagedVector(out) + return C.GoError_None +} + var api_vtable = C.GoApi_vtable{ humanize_address: (C.humanize_address_fn)(C.cHumanAddress_cgo), canonicalize_address: (C.canonicalize_address_fn)(C.cCanonicalAddress_cgo), diff --git a/internal/api/callbacks_cgo.go b/internal/api/callbacks_cgo.go index 6cc036324..343c890a9 100644 --- a/internal/api/callbacks_cgo.go +++ b/internal/api/callbacks_cgo.go @@ -11,6 +11,8 @@ GoError cDelete(db_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, U8SliceVi GoError cScan(db_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, U8SliceView start, U8SliceView end, int32_t order, GoIter *out, UnmanagedVector *errOut); // imports (iterator) GoError cNext(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *val, UnmanagedVector *errOut); +GoError cNextKey(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *errOut); +GoError cNextValue(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *value, UnmanagedVector *errOut); // imports (api) GoError cHumanAddress(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas); GoError cCanonicalAddress(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas); @@ -35,6 +37,12 @@ GoError cScan_cgo(db_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, U8Slice GoError cNext_cgo(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *val, UnmanagedVector *errOut) { return cNext(ptr, gas_meter, used_gas, key, val, errOut); } +GoError cNextKey_cgo(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *key, UnmanagedVector *errOut) { + return cNextKey(ptr, gas_meter, used_gas, key, errOut); +} +GoError cNextValue_cgo(iterator_t *ptr, gas_meter_t *gas_meter, uint64_t *used_gas, UnmanagedVector *val, UnmanagedVector *errOut) { + return cNextValue(ptr, gas_meter, used_gas, val, errOut); +} // Gateway functions (api) GoError cCanonicalAddress_cgo(api_t *ptr, U8SliceView src, UnmanagedVector *dest, UnmanagedVector *errOut, uint64_t *used_gas) { diff --git a/internal/api/libwasmvm.dylib b/internal/api/libwasmvm.dylib index a34a04202..7ee59b39f 100755 Binary files a/internal/api/libwasmvm.dylib and b/internal/api/libwasmvm.dylib differ diff --git a/libwasmvm/bindings.h b/libwasmvm/bindings.h index 5b38b7736..a2e9950a9 100644 --- a/libwasmvm/bindings.h +++ b/libwasmvm/bindings.h @@ -260,6 +260,16 @@ typedef struct Iterator_vtable { struct UnmanagedVector*, struct UnmanagedVector*, struct UnmanagedVector*); + int32_t (*next_key_db)(struct iterator_t, + struct gas_meter_t*, + uint64_t*, + struct UnmanagedVector*, + struct UnmanagedVector*); + int32_t (*next_value_db)(struct iterator_t, + struct gas_meter_t*, + uint64_t*, + struct UnmanagedVector*, + struct UnmanagedVector*); } Iterator_vtable; typedef struct GoIter { diff --git a/libwasmvm/src/iterator.rs b/libwasmvm/src/iterator.rs index 565b9d1d6..81c9cc26f 100644 --- a/libwasmvm/src/iterator.rs +++ b/libwasmvm/src/iterator.rs @@ -29,6 +29,24 @@ pub struct Iterator_vtable { *mut UnmanagedVector, // error message output ) -> i32, >, + pub next_key_db: Option< + extern "C" fn( + iterator_t, + *mut gas_meter_t, + *mut u64, + *mut UnmanagedVector, // key output + *mut UnmanagedVector, // error message output + ) -> i32, + >, + pub next_value_db: Option< + extern "C" fn( + iterator_t, + *mut gas_meter_t, + *mut u64, + *mut UnmanagedVector, // value output + *mut UnmanagedVector, // error message output + ) -> i32, + >, } #[repr(C)] @@ -97,4 +115,60 @@ impl GoIter { }; (result, gas_info) } + + pub fn next_key(&mut self) -> BackendResult>> { + self.next_key_or_val(self.vtable.next_key_db) + } + + pub fn next_value(&mut self) -> BackendResult>> { + self.next_key_or_val(self.vtable.next_value_db) + } + + #[inline(always)] + fn next_key_or_val( + &mut self, + next_fn: Option< + extern "C" fn( + iterator_t, + *mut gas_meter_t, + *mut u64, + *mut UnmanagedVector, // output + *mut UnmanagedVector, // error message output + ) -> i32, + >, + ) -> BackendResult>> { + let next_db = match next_fn { + Some(f) => f, + None => { + let result = Err(BackendError::unknown("iterator vtable not set")); + return (result, GasInfo::free()); + } + }; + + let mut output = UnmanagedVector::default(); + let mut error_msg = UnmanagedVector::default(); + let mut used_gas = 0_u64; + let go_result: GoError = (next_db)( + self.state, + self.gas_meter, + &mut used_gas as *mut u64, + &mut output as *mut UnmanagedVector, + &mut error_msg as *mut UnmanagedVector, + ) + .into(); + // We destruct the `UnmanagedVector`s here, no matter if we need the data. + let output = output.consume(); + + let gas_info = GasInfo::with_externally_used(used_gas); + + // return complete error message (reading from buffer for GoError::Other) + let default = || "Failed to fetch next item from iterator".to_string(); + unsafe { + if let Err(err) = go_result.into_result(error_msg, default) { + return (Err(err), gas_info); + } + } + + (Ok(output), gas_info) + } } diff --git a/libwasmvm/src/storage.rs b/libwasmvm/src/storage.rs index aafa19f60..cb751c7cf 100644 --- a/libwasmvm/src/storage.rs +++ b/libwasmvm/src/storage.rs @@ -116,6 +116,34 @@ impl Storage for GoStorage { iterator.next() } + fn next_key(&mut self, iterator_id: u32) -> BackendResult>> { + let iterator = match self.iterators.get_mut(&iterator_id) { + Some(i) => i, + None => { + return ( + Err(BackendError::iterator_does_not_exist(iterator_id)), + GasInfo::free(), + ) + } + }; + + iterator.next_key() + } + + fn next_value(&mut self, iterator_id: u32) -> BackendResult>> { + let iterator = match self.iterators.get_mut(&iterator_id) { + Some(i) => i, + None => { + return ( + Err(BackendError::iterator_does_not_exist(iterator_id)), + GasInfo::free(), + ) + } + }; + + iterator.next_value() + } + fn set(&mut self, key: &[u8], value: &[u8]) -> BackendResult<()> { let mut error_msg = UnmanagedVector::default(); let mut used_gas = 0_u64;