Skip to content

Commit

Permalink
WASM ABI: insert -> datastore_insert_bsatn & impl new semantics (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Centril authored Sep 5, 2024
1 parent 68e3565 commit da71d0f
Show file tree
Hide file tree
Showing 31 changed files with 383 additions and 176 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions crates/bindings-csharp/Codegen/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ public override Scope.Extensions ToExtensions()
);
}

var hasAutoIncFields = Members.Any(f => f.Attrs.HasFlag(ColumnAttrs.AutoInc));
var autoIncFields = Members
.Where(f => f.Attrs.HasFlag(ColumnAttrs.AutoInc))
.Select(f => f.Name);

var iTable = $"SpacetimeDB.Internal.ITable<{ShortName}>";

Expand All @@ -186,7 +188,19 @@ public override Scope.Extensions ToExtensions()

extensions.Contents.Append(
$$"""
static bool {{iTable}}.HasAutoIncFields => {{hasAutoIncFields.ToString().ToLower()}};
public void ReadGenFields(System.IO.BinaryReader reader) {
{{string.Join(
"\n",
autoIncFields.Select(name =>
$$"""
if ({{name}} == default)
{
{{name}} = BSATN.{{name}}.Read(reader);
}
"""
)
)}}
}

static SpacetimeDB.Internal.Module.TableDesc {{iTable}}.MakeTableDesc(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new (
new (
Expand Down
5 changes: 5 additions & 0 deletions crates/bindings-csharp/Runtime/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public class UniqueAlreadyExistsException : StdbException
public override string Message => "Value with given unique identifier already exists";
}

public class ScheduleAtDelayTooLongException : StdbException
{
public override string Message => "Specified delay in scheduling row was too long";
}

public class BufferTooSmallException : StdbException
{
public override string Message => "The provided buffer is not large enough to store the data";
Expand Down
8 changes: 7 additions & 1 deletion crates/bindings-csharp/Runtime/Internal/FFI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum Errno : short
NO_SPACE = 9,
BUFFER_TOO_SMALL = 11,
UNIQUE_ALREADY_EXISTS = 12,
SCHEDULE_AT_DELAY_TOO_LONG = 13,
}

#pragma warning disable IDE1006 // Naming Styles - Not applicable to FFI stuff.
Expand Down Expand Up @@ -78,6 +79,7 @@ public static CheckedStatus ConvertToManaged(Errno status)
Errno.NO_SPACE => new NoSpaceException(),
Errno.BUFFER_TOO_SMALL => new BufferTooSmallException(),
Errno.UNIQUE_ALREADY_EXISTS => new UniqueAlreadyExistsException(),
Errno.SCHEDULE_AT_DELAY_TOO_LONG => new ScheduleAtDelayTooLongException(),
_ => new UnknownException(status),
};
}
Expand Down Expand Up @@ -145,7 +147,11 @@ out RowIter out_
);

[LibraryImport(StdbNamespace)]
public static partial CheckedStatus _insert(TableId table_id, byte[] row, uint row_len);
public static partial CheckedStatus _datastore_insert_bsatn(
TableId table_id,
Span<byte> row,
ref uint row_len
);

[LibraryImport(StdbNamespace)]
public static partial CheckedStatus _delete_by_col_eq(
Expand Down
17 changes: 9 additions & 8 deletions crates/bindings-csharp/Runtime/Internal/ITable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ public interface ITable<T> : IStructuralReadWrite
where T : ITable<T>, new()
{
// These are the methods that codegen needs to implement.
void ReadGenFields(BinaryReader reader);
static abstract Module.TableDesc MakeTableDesc(ITypeRegistrar registrar);
static abstract Filter CreateFilter();
static abstract bool HasAutoIncFields { get; }

// These are static helpers that codegen can use.

Expand Down Expand Up @@ -150,14 +150,15 @@ public static IEnumerable<T> Query(Expression<Func<T, bool>> query) =>

protected static void Insert(T row)
{
// Insert the row.
var bytes = ToBytes(row);
FFI._insert(tableId, bytes, (uint)bytes.Length);
if (T.HasAutoIncFields)
{
using var stream = new MemoryStream(bytes);
using var reader = new BinaryReader(stream);
row.ReadFields(reader);
}
var bytes_len = (uint)bytes.Length;
FFI._datastore_insert_bsatn(tableId, bytes, ref bytes_len);

// Write back any generated column values.
using var stream = new MemoryStream(bytes, 0, (int)bytes_len);
using var reader = new BinaryReader(stream);
row.ReadGenFields(reader);
}

protected readonly ref struct ColEq
Expand Down
4 changes: 2 additions & 2 deletions crates/bindings-csharp/Runtime/bindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ IMPORT(Status, _iter_by_col_eq,
(TableId table_id, ColId col_id, const uint8_t* value,
uint32_t value_len, RowIter* iter),
(table_id, col_id, value, value_len, iter));
IMPORT(Status, _insert, (TableId table_id, const uint8_t* row, uint32_t len),
(table_id, row, len));
IMPORT(Status, _datastore_insert_bsatn, (TableId table_id, const uint8_t* row_ptr, size_t* row_len_ptr),
(table_id, row_ptr, row_len_ptr));
IMPORT(Status, _delete_by_col_eq,
(TableId table_id, ColId col_id, const uint8_t* value,
uint32_t value_len, uint32_t* num_deleted),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
<UnmanagedEntryPointsAssembly Include="SpacetimeDB.Runtime" />
<WasmImport Include="$(SpacetimeNamespace)!_console_log" />
<WasmImport Include="$(SpacetimeNamespace)!_iter_by_col_eq" />
<WasmImport Include="$(SpacetimeNamespace)!_insert" />
<WasmImport Include="$(SpacetimeNamespace)!_delete_by_col_eq" />
<WasmImport Include="$(SpacetimeNamespace)!_iter_start_filtered" />
<WasmImport Include="$(SpacetimeNamespace)!_table_id_from_name" />
<WasmImport Include="$(SpacetimeNamespace)!_datastore_table_row_count" />
<WasmImport Include="$(SpacetimeNamespace)!_datastore_table_scan_bsatn" />
<WasmImport Include="$(SpacetimeNamespace)!_row_iter_bsatn_advance" />
<WasmImport Include="$(SpacetimeNamespace)!_row_iter_bsatn_close" />
<WasmImport Include="$(SpacetimeNamespace)!_datastore_insert_bsatn" />
<WasmImport Include="$(SpacetimeNamespace)!_datastore_delete_all_by_eq_bsatn" />
<WasmImport Include="$(SpacetimeNamespace)!_bytes_source_read" />
<WasmImport Include="$(SpacetimeNamespace)!_bytes_sink_write" />
Expand Down
21 changes: 21 additions & 0 deletions crates/bindings-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,26 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result<TokenStream
)
});

// Generate `integrate_generated_columns`
// which will integrate all generated auto-inc col values into `_row`.
let integrate_gen_col = columns
.iter()
.filter(|col| col.attr.has_autoinc())
.map(|col| col.field.ident.unwrap())
.map(|field| {
quote_spanned!(field.span()=>
if spacetimedb::IsSequenceTrigger::is_sequence_trigger(&_row.#field) {
_row.#field = spacetimedb::sats::bsatn::from_reader(_in).unwrap();
}
)
});
let integrate_generated_columns = quote_spanned!(item.span() =>
fn integrate_generated_columns(_row: &mut Self, mut _generated_cols: &[u8]) {
let mut _in = &mut _generated_cols;
#(#integrate_gen_col)*
}
);

let scheduled_constant = match sats_ty.scheduled {
Some(reducer_name) => quote!(Some(#reducer_name)),
None => quote!(None),
Expand All @@ -774,6 +794,7 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result<TokenStream
];
const INDEXES: &'static [spacetimedb::IndexDesc<'static>] = &[#(#indexes),*];
type InsertResult = #insert_result;
#integrate_generated_columns
#table_id_from_name_func
}
};
Expand Down
58 changes: 39 additions & 19 deletions crates/bindings-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,6 @@ pub mod raw {
out: *mut RowIter,
) -> 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: TableId, 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`.
Expand Down Expand Up @@ -229,6 +212,41 @@ pub mod raw {
/// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.
pub fn _row_iter_bsatn_close(iter: RowIter) -> u16;

/// Inserts a row into the table identified by `table_id`,
/// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory
/// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.
///
/// The byte string `row` must be a BSATN-encoded `ProductValue`
/// typed at the table's `ProductType` row-schema.
///
/// To handle auto-incrementing columns,
/// when the call is successful,
/// the `row` is written back to with the generated sequence values.
/// These values are written as a BSATN-encoded `pv: ProductValue`.
/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.
/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.
/// When the table has no sequences,
/// this implies that the `pv`, and thus `row`, will be empty.
/// The `row_len` is set to the length of `bsatn(pv)`.
///
/// # Traps
///
/// Traps if:
/// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.
/// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.
///
/// # Errors
///
/// Returns an error:
///
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
/// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`.
/// typed at the `ProductType` the table's schema specifies.
/// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.
/// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.
pub fn _datastore_insert_bsatn(table_id: TableId, row_ptr: *mut u8, row_len_ptr: *mut usize) -> u16;

/// Log at `level` a `message` message occuring in `filename:line_number`
/// with [`target`] being the module path at the `log!` invocation site.
///
Expand Down Expand Up @@ -615,8 +633,10 @@ pub fn iter_by_col_eq(table_id: TableId, col_id: ColId, val: &[u8]) -> Result<Ro
/// - `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: TableId, row: &mut [u8]) -> Result<(), Errno> {
cvt(unsafe { raw::_insert(table_id, row.as_mut_ptr(), row.len()) })
pub fn insert(table_id: TableId, row: &mut [u8]) -> Result<&[u8], Errno> {
let row_ptr = row.as_mut_ptr();
let row_len = &mut row.len();
cvt(unsafe { raw::_datastore_insert_bsatn(table_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
}

/// Deletes all rows in the table identified by `table_id`
Expand Down
30 changes: 27 additions & 3 deletions crates/bindings/src/impls.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
use crate::FilterableValue;
use crate::{FilterableValue, IsSequenceTrigger};
use spacetimedb_lib::{
sats::{i256, u256},
Address, Identity,
};

macro_rules! impl_primitives {
macro_rules! impl_filterable_value {
($($t:ty),*) => {
$(
impl FilterableValue for $t {}
)*
};
}

impl_primitives![u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, u256, i256, bool, String, Identity, Address];
impl_filterable_value![u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, u256, i256, bool, String, Identity, Address];

macro_rules! impl_is_seq_trigger {
($($t:ty),*) => {
$(
impl IsSequenceTrigger for $t {
fn is_sequence_trigger(&self) -> bool { *self == 0 }
}
)*
};
}

impl_is_seq_trigger![u8, i8, u16, i16, u32, i32, u64, i64, u128, i128];

impl IsSequenceTrigger for i256 {
fn is_sequence_trigger(&self) -> bool {
*self == Self::ZERO
}
}

impl IsSequenceTrigger for u256 {
fn is_sequence_trigger(&self) -> bool {
*self == Self::ZERO
}
}
Loading

2 comments on commit da71d0f

@github-actions
Copy link

@github-actions github-actions bot commented on da71d0f Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

@github-actions
Copy link

@github-actions github-actions bot commented on da71d0f Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

Please sign in to comment.