Skip to content

Commit

Permalink
Implement GoStorage::{next_key, next_value}
Browse files Browse the repository at this point in the history
  • Loading branch information
chipshort committed Aug 25, 2023
1 parent d3f767b commit 444ba2f
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 1 deletion.
10 changes: 10 additions & 0 deletions internal/api/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
57 changes: 56 additions & 1 deletion internal/api/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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),
Expand Down
8 changes: 8 additions & 0 deletions internal/api/callbacks_cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand Down
Binary file modified internal/api/libwasmvm.dylib
Binary file not shown.
10 changes: 10 additions & 0 deletions libwasmvm/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
74 changes: 74 additions & 0 deletions libwasmvm/src/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -97,4 +115,60 @@ impl GoIter {
};
(result, gas_info)
}

pub fn next_key(&mut self) -> BackendResult<Option<Vec<u8>>> {
self.next_key_or_val(self.vtable.next_key_db)
}

pub fn next_value(&mut self) -> BackendResult<Option<Vec<u8>>> {
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<Option<Vec<u8>>> {
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)
}
}
28 changes: 28 additions & 0 deletions libwasmvm/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,34 @@ impl Storage for GoStorage {
iterator.next()
}

fn next_key(&mut self, iterator_id: u32) -> BackendResult<Option<Vec<u8>>> {
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<Option<Vec<u8>>> {
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;
Expand Down

0 comments on commit 444ba2f

Please sign in to comment.