From 2daf06cdce236aa4d057e1c31d86b0e15961ab45 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 3 Oct 2023 19:29:33 +0200 Subject: [PATCH] Bindings: improve docs precision (#146) * bindings: improve docs precision * Fix some 32 vs 64 bit comments --------- Co-authored-by: George Kulakowski --- crates/bindings-sys/src/lib.rs | 175 ++++++++++++++---- crates/bindings/src/lib.rs | 26 ++- crates/core/src/host/instance_env.rs | 12 +- crates/core/src/host/wasmer/mod.rs | 22 ++- .../core/src/host/wasmer/wasm_instance_env.rs | 146 +++++++++++---- 5 files changed, 294 insertions(+), 87 deletions(-) diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index c8e264be0e..59cd8ebe39 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -61,7 +61,11 @@ pub mod raw { /// /// The table id is written into the `out` pointer. /// - /// Returns an error if the table does not exist. + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - the slice `(name, name_len)` is not valid UTF-8 + /// - `name + name_len` overflows a 64-bit address. + /// - writing to `out` overflows a 32-bit integer pub fn _get_table_id(name: *const u8, name_len: usize, out: *mut u32) -> u16; /// Creates an index with the name `index_name` and type `index_type`, @@ -73,10 +77,14 @@ pub mod raw { /// /// Currently only single-column-indices are supported /// and they may only be of the btree index type. - /// In the former case, the function will panic, - /// and in latter, an error is returned. /// - /// Returns an error when a table with the provided `table_id` doesn't exist. + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - the slice `(index_name, index_name_len)` is not valid UTF-8 + /// - `index_name + index_name_len` or `col_ids + col_len` overflow a 64-bit integer + /// - `index_type > 1` + /// + /// Traps if `index_type == 1` or `col_ids.len() != 1`. pub fn _create_index( index_name: *const u8, index_name_len: usize, @@ -90,30 +98,55 @@ pub mod raw { /// where the row has a column, identified by `col_id`, /// with data matching the byte string, in WASM memory, pointed to at by `val`. /// - /// Matching is defined by decoding of `value` to an `AlgebraicValue` + /// Matching is defined BSATN-decoding `val` to an `AlgebraicValue` /// according to the column's schema and then `Ord for AlgebraicValue`. /// - /// The rows found are bsatn encoded and then concatenated. + /// The rows found are BSATN encoded and then concatenated. /// The resulting byte string from the concatenation is written /// to a fresh buffer with the buffer's identifier written to the WASM pointer `out`. - pub fn _iter_by_col_eq(table_id: u32, col_id: u32, value: *const u8, value_len: usize, out: *mut Buffer) - -> u16; - - /// Insert a row into the table identified by `table_id`, - /// where the row is read from the byte slice `row_ptr` in WASM memory, + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - `col_id` does not identify a column of the table, + /// - `(val, val_len)` cannot be decoded to an `AlgebraicValue` + /// typed at the `AlgebraicType` of the column, + /// - `val + val_len` overflows a 64-bit integer + pub fn _iter_by_col_eq(table_id: u32, col_id: u32, val: *const u8, val_len: usize, out: *mut Buffer) -> u16; + + /// Inserts a row into the table identified by `table_id`, + /// where the row is read from the byte slice `row` in WASM memory, /// lasting `row_len` bytes. + /// + /// The `(row, row_len)` slice must be a BSATN-encoded `ProductValue` + /// matching the table's `ProductType` row-schema. + /// The `row` pointer is written to with the inserted row re-encoded. + /// This is due to auto-incrementing columns. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - there were unique constraint violations + /// - `row + row_len` overflows a 64-bit integer + /// - `(row, row_len)` doesn't decode from BSATN to a `ProductValue` + /// according to the `ProductType` that the table's schema specifies. pub fn _insert(table_id: u32, row: *mut u8, row_len: usize) -> u16; /// Deletes all rows in the table identified by `table_id` /// where the column identified by `col_id` matches the byte string, /// in WASM memory, pointed to at by `value`. /// - /// Matching is defined by decoding of `value` to an `AlgebraicValue` + /// Matching is defined by BSATN-decoding `value` to an `AlgebraicValue` /// according to the column's schema and then `Ord for AlgebraicValue`. /// /// The number of rows deleted is written to the WASM pointer `out`. /// - /// Returns an error if no columns were deleted or if the column wasn't found. + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - no columns were deleted + /// - `col_id` does not identify a column of the table, + /// - `(value, value_len)` doesn't decode from BSATN to an `AlgebraicValue` + /// according to the `AlgebraicType` that the table's schema specifies for `col_id`. + /// - `value + value_len` overflows a 64-bit integer + /// - writing to `out` would overflow a 32-bit integer pub fn _delete_by_col_eq(table_id: u32, col_id: u32, value: *const u8, value_len: usize, out: *mut u32) -> u16; /* @@ -135,6 +168,9 @@ pub mod raw { /// /// The iterator is registered in the host environment /// under an assigned index which is written to the `out` pointer provided. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist pub fn _iter_start(table_id: u32, out: *mut BufferIter) -> u16; /// Like [`_iter_start`], start iteration on each row, @@ -145,6 +181,11 @@ pub mod raw { /// /// The iterator is registered in the host environment /// under an assigned index which is written to the `out` pointer provided. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - `(filter, filter_len)` doesn't decode to a filter expression + /// - `filter + filter_len` overflows a 64-bit integer pub fn _iter_start_filtered(table_id: u32, filter: *const u8, filter_len: usize, out: *mut BufferIter) -> u16; /// Advances the registered iterator with the index given by `iter_key`. @@ -153,6 +194,11 @@ pub mod raw { /// The buffer's index is returned and written to the `out` pointer. /// If there are no elements left, an invalid buffer index is written to `out`. /// On failure however, the error is returned. + /// + /// Returns an error if + /// - `iter` does not identify a registered `BufferIter` + /// - writing to `out` would overflow a 64-bit integer + /// - advancing the iterator resulted in an error pub fn _iter_next(iter: ManuallyDrop, out: *mut Buffer) -> u16; /// Drops the entire registered iterator with the index given by `iter_key`. @@ -161,11 +207,19 @@ pub mod raw { /// Returns an error if the iterator does not exist. pub fn _iter_drop(iter: ManuallyDrop) -> u16; - /// Log at `level` a `text` message occuring in `filename:line_number` + /// Log at `level` a `message` message occuring in `filename:line_number` /// with [`target`] being the module path at the `log!` invocation site. /// /// These various pointers are interpreted lossily as UTF-8 strings with a corresponding `_len`. /// + /// The `target` and `filename` pointers are ignored by passing `NULL`. + /// The line number is ignored if `line_number == u32::MAX`. + /// + /// No message is logged if + /// - `target != NULL && target + target_len > u64::MAX` + /// - `filename != NULL && filename + filename_len > u64::MAX` + /// - `message + message_len > u64::MAX` + /// /// [`target`]: https://docs.rs/log/latest/log/struct.Record.html#method.target pub fn _console_log( level: u8, @@ -174,17 +228,22 @@ pub mod raw { filename: *const u8, filename_len: usize, line_number: u32, - text: *const u8, - text_len: usize, + message: *const u8, + message_len: usize, ); - /// Schedule a reducer to be called asynchronously at `time`. + /// Schedules a reducer to be called asynchronously at `time`. /// - /// The reducer is named as the UTF-8 slice `(name, name_len)`, + /// The reducer is named as the valid UTF-8 slice `(name, name_len)`, /// and is passed the slice `(args, args_len)` as its argument. /// /// A generated schedule id is assigned to the reducer. /// This id is written to the pointer `out`. + /// + /// Traps if + /// - the `time` delay exceeds `64^6 - 1` milliseconds from now + /// - `name` does not point to valid UTF-8 + /// - `name + name_len` or `args + args_len` overflow a 64-bit integer pub fn _schedule_reducer( name: *const u8, name_len: usize, @@ -199,18 +258,31 @@ pub mod raw { /// This assumes that the reducer hasn't already been executed. pub fn _cancel_reducer(id: u64); - /// Returns the length of buffer `bufh` without consuming the buffer handle. + /// Returns the length (number of bytes) of buffer `bufh` without + /// transferring ownership of the data into the function. + /// + /// The `bufh` must have previously been allocating using `_buffer_alloc`. /// - /// Returns an error if the buffer does not exist. + /// Traps if the buffer does not exist. pub fn _buffer_len(bufh: ManuallyDrop) -> usize; - /// Consumes the buffer `bufh`, moving its contents to the slice `(into, len)`. + /// Consumes the `buffer`, + /// moving its contents to the slice `(dst, dst_len)`. /// - /// Returns an error if the buffer does not exist. - pub fn _buffer_consume(bufh: Buffer, into: *mut u8, len: usize); + /// Traps if + /// - the buffer does not exist + /// - `dst + dst_len` overflows a 64-bit integer + pub fn _buffer_consume(buffer: Buffer, dst: *mut u8, dst_len: usize); /// Creates a buffer of size `data_len` in the host environment. - /// The buffer is initialized with the contents at the `data` WASM pointer. + /// + /// The contents of the byte slice pointed to by `data` + /// and lasting `data_len` bytes + /// is written into the newly initialized buffer. + /// + /// The buffer is registered in the host environment and is indexed by the returned `u32`. + /// + /// Traps if `data + data_len` overflows a 64-bit integer. pub fn _buffer_alloc(data: *const u8, data_len: usize) -> Buffer; /// Begin a timing span. @@ -291,6 +363,7 @@ pub mod raw { /// Represents table iterators, with a similar API to [`Buffer`]. #[repr(transparent)] pub struct BufferIter { + /// The actual handle. A key into a `ResourceSlab`. raw: u32, } @@ -457,10 +530,12 @@ pub fn get_table_id(name: &str) -> Result { /// /// Currently only single-column-indices are supported /// and they may only be of the btree index type. -/// In the former case, the function will panic, -/// and in latter, an error is returned. /// -/// Returns an error when a table with the provided `table_id` doesn't exist. +/// Returns an error if +/// - a table with the provided `table_id` doesn't exist +/// - `index_type > 1` +/// +/// Traps if `index_type == 1` or `col_ids.len() != 1`. #[inline] pub fn create_index(index_name: &str, table_id: u32, index_type: u8, col_ids: &[u8]) -> Result<(), Errno> { cvt(unsafe { @@ -477,27 +552,54 @@ pub fn create_index(index_name: &str, table_id: u32, index_type: u8, col_ids: &[ /// Finds all rows in the table identified by `table_id`, /// where the row has a column, identified by `col_id`, -/// with data matching `val`. +/// with data matching the byte string `val`. /// -/// The rows found are bsatn encoded and then concatenated. +/// Matching is defined BSATN-decoding `val` to an `AlgebraicValue` +/// according to the column's schema and then `Ord for AlgebraicValue`. +/// +/// The rows found are BSATN encoded and then concatenated. /// The resulting byte string from the concatenation is written /// to a fresh buffer with a handle to it returned as a `Buffer`. +/// +/// Returns an error if +/// - a table with the provided `table_id` doesn't exist +/// - `col_id` does not identify a column of the table +/// - `val` cannot be BSATN-decoded to an `AlgebraicValue` +/// typed at the `AlgebraicType` of the column #[inline] pub fn iter_by_col_eq(table_id: u32, col_id: u32, val: &[u8]) -> Result { unsafe { call(|out| raw::_iter_by_col_eq(table_id, col_id, val.as_ptr(), val.len(), out)) } } -/// Insert `row`, provided as a byte slice, into the table identified by `table_id`. +/// Inserts a row into the table identified by `table_id`, +/// where the row is a BSATN-encoded `ProductValue` +/// matching the table's `ProductType` row-schema. +/// +/// The `row` is `&mut` due to auto-incrementing columns. +/// So `row` is written to with the inserted row re-encoded. +/// +/// Returns an error if +/// - a table with the provided `table_id` doesn't exist +/// - there were unique constraint violations +/// - `row` doesn't decode from BSATN to a `ProductValue` +/// according to the `ProductType` that the table's schema specifies. #[inline] pub fn insert(table_id: u32, row: &mut [u8]) -> Result<(), Errno> { cvt(unsafe { raw::_insert(table_id, row.as_mut_ptr(), row.len()) }) } /// Deletes all rows in the table identified by `table_id` -/// where the column identified by `col_id` equates to `value`. +/// where the column identified by `col_id` matches `value`. +/// +/// Matching is defined by BSATN-decoding `value` to an `AlgebraicValue` +/// according to the column's schema and then `Ord for AlgebraicValue`. +/// +/// Returns the number of rows deleted. /// -/// Returns the number of rows deleted -/// or an error if no columns were deleted or if the column wasn't found. +/// Returns an error if +/// - a table with the provided `table_id` doesn't exist +/// - no columns were deleted +/// - `col_id` does not identify a column of the table #[inline] pub fn delete_by_col_eq(table_id: u32, col_id: u32, value: &[u8]) -> Result { unsafe { call(|out| raw::_delete_by_col_eq(table_id, col_id, value.as_ptr(), value.len(), out)) } @@ -536,6 +638,11 @@ pub fn delete_range(table_id: u32, col_id: u32, range_start: &[u8], range_end: & /// /// The actual return value is a handle to an iterator registered with the host environment, /// but [`BufferIter`] can be used directly as an `Iterator`. +/// +/// Returns an error if +/// +/// - a table with the provided `table_id` doesn't exist +/// - `Some(filter)` doesn't decode to a filter expression #[inline] pub fn iter(table_id: u32, filter: Option<&[u8]>) -> Result { unsafe { @@ -600,6 +707,8 @@ pub fn console_log( /// /// A generated schedule id is assigned to the reducer which is returned. /// +/// Returns an error if the `time` delay exceeds `64^6 - 1` milliseconds from now. +/// /// TODO: not fully implemented yet /// TODO(Centril): Unsure what is unimplemented; perhaps it refers to a new /// implementation with a special system table rather than a special sys call. diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index d270326a1f..4daf81fdc7 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -173,14 +173,18 @@ pub fn insert(table_id: u32, row: T) -> T::InsertResult { /// Matching is defined by decoding of `value` to an `AlgebraicValue` /// according to the column's schema and then `Ord for AlgebraicValue`. /// -/// The rows found are bsatn encoded and then concatenated. +/// The rows found are BSATN encoded and then concatenated. /// The resulting byte string from the concatenation is written /// to a fresh buffer with a handle to it returned as a `Buffer`. /// -/// Panics when serialization fails. +/// Panics if +/// - BSATN serialization fails +/// - there were unique constraint violations +/// - `row` doesn't decode from BSATN to a `ProductValue` +/// according to the `ProductType` that the table's schema specifies pub fn iter_by_col_eq(table_id: u32, col_id: u8, val: &impl Serialize) -> Result { with_row_buf(|bytes| { - // Encode `val` as bsatn into `bytes` and then use that. + // Encode `val` as BSATN into `bytes` and then use that. bsatn::to_writer(bytes, val).unwrap(); sys::iter_by_col_eq(table_id, col_id as u32, bytes) }) @@ -192,14 +196,20 @@ pub fn iter_by_col_eq(table_id: u32, col_id: u8, val: &impl Serialize) -> Result /// Matching is defined by decoding of `value` to an `AlgebraicValue` /// according to the column's schema and then `Ord for AlgebraicValue`. /// -/// Returns the number of rows deleted -/// or an error if no columns were deleted or if the column wasn't found. +/// Returns the number of rows deleted. +/// +/// Returns an error if +/// - a table with the provided `table_id` doesn't exist +/// - no columns were deleted +/// - `col_id` does not identify a column of the table, +/// - `value` doesn't decode from BSATN to an `AlgebraicValue` +/// according to the `AlgebraicType` that the table's schema specifies for `col_id`. /// /// Panics when serialization fails. -pub fn delete_by_col_eq(table_id: u32, col_id: u8, eq_value: &impl Serialize) -> Result { +pub fn delete_by_col_eq(table_id: u32, col_id: u8, value: &impl Serialize) -> Result { with_row_buf(|bytes| { - // Encode `val` as bsatn into `bytes` and then use that. - bsatn::to_writer(bytes, eq_value).unwrap(); + // Encode `value` as BSATN into `bytes` and then use that. + bsatn::to_writer(bytes, value).unwrap(); sys::delete_by_col_eq(table_id, col_id.into(), bytes) }) } diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index a8367562c3..d9e5fb296c 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -246,15 +246,17 @@ impl InstanceEnv { Ok(table_id) } - /// Creates an index of type `index_type`, + /// Creates an index of type `index_type` and name `index_name`, /// on a product of the given columns in `col_ids`, /// in the table identified by `table_id`. /// - /// Currently only btree index type are supported. + /// Currently only single-column-indices are supported. + /// That is, `col_ids.len() == 1`, or the call will panic. /// - /// The `table_name` is used together with the column ids to construct the name of the index. - /// As only single-column-indices are supported right now, - /// the name will be in the format `{table_name}_{cols}`. + /// Another limitation is on the `index_type`. + /// Only `btree` indices are supported as of now, i.e., `index_type == 0`. + /// When `index_type == 1` is passed, the call will happen + /// and on `index_type > 1`, an error is returned. #[tracing::instrument(skip_all)] pub fn create_index( &self, diff --git a/crates/core/src/host/wasmer/mod.rs b/crates/core/src/host/wasmer/mod.rs index ddbbb61ac2..96e9282c1d 100644 --- a/crates/core/src/host/wasmer/mod.rs +++ b/crates/core/src/host/wasmer/mod.rs @@ -60,27 +60,41 @@ enum WasmError { Wasm(#[from] RuntimeError), } +/// Wraps access to WASM linear memory with some additional functionality. #[derive(Clone)] struct Mem { + /// The underlying WASM `memory` instance. pub memory: Memory, } impl Mem { + /// Constructs an instance of `Mem` from an exports map. fn extract(exports: &wasmer::Exports) -> anyhow::Result { - Ok(Self { - memory: exports.get_memory("memory")?.clone(), - }) + let memory = exports.get_memory("memory")?.clone(); + Ok(Self { memory }) } + + /// Creates and returns a view into the actual memory `store`. + /// This view allows for reads and writes. + fn view<'a>(&self, store: &'a impl AsStoreRef) -> wasmer::MemoryView<'a> { self.memory.view(store) } - /// Reads a slice of bytes starting from `ptr` and lasting `len` bytes into a `Vec`. + /// Reads a slice of bytes starting from `ptr` + /// and lasting `len` bytes into a `Vec`. /// /// Returns an error if the slice length overflows a 64-bit address. fn read_bytes(&self, store: &impl AsStoreRef, ptr: WasmPtr, len: u32) -> Result, MemoryAccessError> { ptr.slice(&self.view(store), len)?.read_to_vec() } + + /// Writes `data` into the slice starting from `ptr` + /// and lasting `len` bytes. + /// + /// Returns an error if + /// - the slice length overflows a 64-bit address + /// - `len != data.len()` fn set_bytes( &self, store: &impl AsStoreRef, diff --git a/crates/core/src/host/wasmer/wasm_instance_env.rs b/crates/core/src/host/wasmer/wasm_instance_env.rs index 859d3bdd93..f808a9e6ce 100644 --- a/crates/core/src/host/wasmer/wasm_instance_env.rs +++ b/crates/core/src/host/wasmer/wasm_instance_env.rs @@ -80,6 +80,8 @@ impl WasmInstanceEnv { /// /// This method should be used as opposed to a manual implementation, /// as it helps with upholding the safety invariants of [`bindings_sys::call`]. + /// + /// Returns an error if writing `T` to `out` errors. fn cvt_ret( caller: FunctionEnvMut<'_, Self>, func: &'static str, @@ -93,21 +95,27 @@ impl WasmInstanceEnv { /// Reads a string from WASM memory starting at `ptr` and lasting `len` bytes. /// - /// Returns an error if there were memory access issues - /// or if the string was not valid UTF-8. + /// Returns an error if: + /// - `ptr + len` overflows a 64-bit address. + /// - the string was not valid UTF-8 fn read_string(caller: &FunctionEnvMut<'_, Self>, mem: &Mem, ptr: WasmPtr, len: u32) -> RtResult { let bytes = mem.read_bytes(&caller, ptr, len)?; String::from_utf8(bytes).map_err(|_| RuntimeError::new("name must be utf8")) } - /// Schedule the reducer `(name, name_len)` to be executed asynchronously, - /// passing it `(args, args_len)`, at the given `time`. + /// Schedules a reducer to be called asynchronously at `time`. + /// + /// The reducer is named as the valid UTF-8 slice `(name, name_len)`, + /// and is passed the slice `(args, args_len)` as its argument. /// - /// This can be thought of as `setTimeout` in JS. - /// Note that `time = 0` can still mean that `cancel_reducer` has an effect due to threading. + /// A generated schedule id is assigned to the reducer. + /// This id is written to the pointer `out`. /// - /// The scheduled reducer is assigned a generated `id`, which is written to the pointer `out`. - /// Note that `name` must point to valid UTF-8 or a `RuntimeError` will occur. + /// Returns an error if + /// - the `time` delay exceeds `64^6 - 1` milliseconds from now + /// - `name` does not point to valid UTF-8 + /// - `name + name_len` or `args + args_len` overflow a 64-bit integer + /// - writing to `out` overflows a 64-bit integer #[tracing::instrument(skip_all)] pub fn schedule_reducer( caller: FunctionEnvMut<'_, Self>, @@ -145,7 +153,7 @@ impl WasmInstanceEnv { .map(|_| ()) } - /// Cancel a reducer that was scheduled with `id`. + /// Unschedule a reducer using the same `id` generated as when it was scheduled. /// /// This assumes that the reducer hasn't already been executed. #[tracing::instrument(skip_all)] @@ -153,9 +161,20 @@ impl WasmInstanceEnv { caller.data().instance_env.cancel_reducer(ScheduledReducerId(id)) } - /// Log at `level` a `message` occuring in `filename:line_number` with `target`. + /// Log at `level` a `message` message occuring in `filename:line_number` + /// with [`target`] being the module path at the `log!` invocation site. /// /// These various pointers are interpreted lossily as UTF-8 strings with a corresponding `_len`. + /// + /// The `target` and `filename` pointers are ignored by passing `NULL`. + /// The line number is ignored if `line_number == u32::MAX`. + /// + /// No message is logged if + /// - `target != NULL && target + target_len > u64::MAX` + /// - `filename != NULL && filename + filename_len > u64::MAX` + /// - `message + message_len > u64::MAX` + /// + /// [`target`]: https://docs.rs/log/latest/log/struct.Record.html#method.target #[tracing::instrument(skip_all)] pub fn console_log( caller: FunctionEnvMut<'_, Self>, @@ -204,22 +223,29 @@ impl WasmInstanceEnv { })(); } - /// Insert a row, into the table identified by `table_id`, - /// where the row is read from the byte slice `row_ptr` in WASM memory, + /// Inserts a row into the table identified by `table_id`, + /// where the row is read from the byte slice `row` in WASM memory, /// lasting `row_len` bytes. + /// + /// The `(row, row_len)` slice must be a BSATN-encoded `ProductValue` + /// matching the table's `ProductType` row-schema. + /// The `row` pointer is written to with the inserted row re-encoded. + /// This is due to auto-incrementing columns. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - there were unique constraint violations + /// - `row + row_len` overflows a 64-bit integer + /// - `(row, row_len)` doesn't decode from BSATN to a `ProductValue` + /// according to the `ProductType` that the table's schema specifies. #[tracing::instrument(skip_all)] - pub fn insert( - caller: FunctionEnvMut<'_, Self>, - table_id: u32, - row_ptr: WasmPtr, - row_len: u32, - ) -> RtResult { + pub fn insert(caller: FunctionEnvMut<'_, Self>, table_id: u32, row: WasmPtr, row_len: u32) -> RtResult { Self::cvt(caller, "insert", |caller, mem| { // Read the row from WASM memory into a buffer. - let mut row_buffer = mem.read_bytes(&caller, row_ptr, row_len)?; + let mut row_buffer = mem.read_bytes(&caller, row, row_len)?; // Insert the row into the DB. We get back the decoded version. - // Then re-encode and write that back into WASM memory at `row_ptr`. + // Then re-encode and write that back into WASM memory at `row`. // We're doing this because of autoinc. let new_row = caller.data().instance_env.insert(table_id, &row_buffer)?; row_buffer.clear(); @@ -229,7 +255,7 @@ impl WasmInstanceEnv { row_len as usize, "autoinc'd row is different encoded size from original row" ); - mem.set_bytes(&caller, row_ptr, row_len, &row_buffer)?; + mem.set_bytes(&caller, row, row_len, &row_buffer)?; Ok(()) }) } @@ -238,12 +264,19 @@ impl WasmInstanceEnv { /// where the column identified by `cols` matches the byte string, /// in WASM memory, pointed to at by `value`. /// - /// Matching is defined by decoding of `value` to an `AlgebraicValue` + /// Matching is defined by BSATN-decoding `value` to an `AlgebraicValue` /// according to the column's schema and then `Ord for AlgebraicValue`. /// /// The number of rows deleted is written to the WASM pointer `out`. /// - /// Returns an error if no columns were deleted or if the column wasn't found. + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - no columns were deleted + /// - `col_id` does not identify a column of the table, + /// - `(value, value_len)` doesn't decode from BSATN to an `AlgebraicValue` + /// according to the `AlgebraicType` that the table's schema specifies for `col_id`. + /// - `value + value_len` overflows a 64-bit integer + /// - writing to `out` would overflow a 32-bit integer #[tracing::instrument(skip_all)] pub fn delete_by_col_eq( caller: FunctionEnvMut<'_, Self>, @@ -342,7 +375,11 @@ impl WasmInstanceEnv { /// /// The table id is written into the `out` pointer. /// - /// Errors if the table does not exist. + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - the slice `(name, name_len)` is not valid UTF-8 + /// - `name + name_len` overflows a 64-bit address. + /// - writing to `out` overflows a 32-bit integer #[tracing::instrument(skip_all)] pub fn get_table_id( caller: FunctionEnvMut<'_, Self>, @@ -368,10 +405,14 @@ impl WasmInstanceEnv { /// /// Currently only single-column-indices are supported /// and they may only be of the btree index type. - /// In the former case, the function will panic, - /// and in latter, an error is returned. /// - /// Returns an error when a table with the provided `table_id` doesn't exist. + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - the slice `(index_name, index_name_len)` is not valid UTF-8 + /// - `index_name + index_name_len` or `col_ids + col_len` overflow a 64-bit integer + /// - `index_type > 1` + /// + /// Panics if `index_type == 1` or `col_ids.len() != 1`. #[tracing::instrument(skip_all)] pub fn create_index( caller: FunctionEnvMut<'_, Self>, @@ -402,12 +443,19 @@ impl WasmInstanceEnv { /// where the row has a column, identified by `cols`, /// with data matching the byte string, in WASM memory, pointed to at by `val`. /// - /// Matching is defined by decoding of `value` to an `AlgebraicValue` + /// Matching is defined BSATN-decoding `val` to an `AlgebraicValue` /// according to the column's schema and then `Ord for AlgebraicValue`. /// - /// The rows found are bsatn encoded and then concatenated. + /// The rows found are BSATN-encoded and then concatenated. /// The resulting byte string from the concatenation is written /// to a fresh buffer with the buffer's identifier written to the WASM pointer `out`. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - `col_id` does not identify a column of the table, + /// - `(val, val_len)` cannot be decoded to an `AlgebraicValue` + /// typed at the `AlgebraicType` of the column, + /// - `val + val_len` overflows a 64-bit integer #[tracing::instrument(skip_all)] pub fn iter_by_col_eq( caller: FunctionEnvMut<'_, Self>, @@ -433,6 +481,9 @@ impl WasmInstanceEnv { /// /// The iterator is registered in the host environment /// under an assigned index which is written to the `out` pointer provided. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist // #[tracing::instrument(skip_all)] pub fn iter_start(caller: FunctionEnvMut<'_, Self>, table_id: u32, out: WasmPtr) -> RtResult { Self::cvt_ret(caller, "iter_start", out, |mut caller, _mem| { @@ -455,6 +506,11 @@ impl WasmInstanceEnv { /// /// The iterator is registered in the host environment /// under an assigned index which is written to the `out` pointer provided. + /// + /// Returns an error if + /// - a table with the provided `table_id` doesn't exist + /// - `(filter, filter_len)` doesn't decode to a filter expression + /// - `filter + filter_len` overflows a 64-bit integer // #[tracing::instrument(skip_all)] pub fn iter_start_filtered( caller: FunctionEnvMut<'_, Self>, @@ -484,6 +540,11 @@ impl WasmInstanceEnv { /// The buffer's index is returned and written to the `out` pointer. /// If there are no elements left, an invalid buffer index is written to `out`. /// On failure however, the error is returned. + /// + /// Returns an error if + /// - `iter` does not identify a registered `BufferIter` + /// - writing to `out` would overflow a 32-bit integer + /// - advancing the iterator resulted in an error // #[tracing::instrument(skip_all)] pub fn iter_next(caller: FunctionEnvMut<'_, Self>, iter_key: u32, out: WasmPtr) -> RtResult { Self::cvt_ret(caller, "iter_next", out, |mut caller, _mem| { @@ -520,7 +581,10 @@ impl WasmInstanceEnv { }) } - /// Returns the length (number of bytes) of the `buffer`. + /// Returns the length (number of bytes) of buffer `bufh` without + /// transferring ownership of the data into the function. + /// + /// The `bufh` must have previously been allocating using `_buffer_alloc`. /// /// Returns an error if the buffer does not exist. // #[tracing::instrument(skip_all)] @@ -533,30 +597,38 @@ impl WasmInstanceEnv { .ok_or_else(|| RuntimeError::new("no such buffer")) } - /// Consumes the `buffer` and moves its contents into the slice `(ptr, len)`. + /// Consumes the `buffer`, + /// moving its contents to the slice `(dst, dst_len)`. /// - /// Returns an error if the buffer does not exist. + /// Returns an error if + /// - the buffer does not exist + /// - `dst + dst_len` overflows a 64-bit integer // #[tracing::instrument(skip_all)] pub fn buffer_consume( mut caller: FunctionEnvMut<'_, Self>, buffer: u32, - ptr: WasmPtr, - len: u32, + dst: WasmPtr, + dst_len: u32, ) -> RtResult<()> { let buf = caller .data_mut() .buffers .take(BufferIdx(buffer)) .ok_or_else(|| RuntimeError::new("no such buffer"))?; - ptr.slice(&caller.data().mem().view(&caller), len) + dst.slice(&caller.data().mem().view(&caller), dst_len) .and_then(|slice| slice.write_slice(&buf)) .map_err(mem_err) } - /// Creates a buffer of size `data_len`. - /// The buffer is initialized with the contents at the `data` WASM pointer. + /// Creates a buffer of size `data_len` in the host environment. + /// + /// The contents of the byte slice pointed to by `data` + /// and lasting `data_len` bytes + /// is written into the newly initialized buffer. /// /// The buffer is registered in the host environment and is indexed by the returned `u32`. + /// + /// Returns an error if `data + data_len` overflows a 64-bit integer. // #[tracing::instrument(skip_all)] pub fn buffer_alloc(mut caller: FunctionEnvMut<'_, Self>, data: WasmPtr, data_len: u32) -> RtResult { let buf = caller