@@ -22,15 +22,16 @@ use crate::host::wasm_common::module_host_actor::{
2222 AnonymousViewOp , ProcedureOp , ReducerOp , ReducerResult , ViewOp , ViewReturnData ,
2323} ;
2424use crate :: host:: wasm_common:: { err_to_errno_and_log, RowIterIdx , TimingSpan , TimingSpanIdx } ;
25- use crate :: host:: AbiCall ;
25+ use crate :: host:: { AbiCall , ArgsTuple } ;
2626use anyhow:: Context ;
2727use bytes:: Bytes ;
28+ use core:: ptr:: NonNull ;
2829use spacetimedb_lib:: { bsatn, ConnectionId , Identity , RawModuleDef , Timestamp } ;
2930use spacetimedb_primitives:: { errno, ColId , IndexId , ProcedureId , ReducerId , TableId , ViewFnPtr } ;
3031use spacetimedb_sats:: u256;
3132use v8:: {
3233 callback_scope, ConstructorBehavior , Function , FunctionCallbackArguments , Isolate , Local , Module , Object ,
33- PinCallbackScope , PinScope ,
34+ PinCallbackScope , PinScope , Value ,
3435} ;
3536
3637macro_rules! create_synthetic_module {
@@ -384,10 +385,11 @@ fn register_hooks_v2_0<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionC
384385}
385386
386387/// Calls the `__call_reducer__` function `fun`.
387- pub ( super ) fn call_call_reducer (
388- scope : & mut PinScope < ' _ , ' _ > ,
389- hooks : & HookFunctions < ' _ > ,
388+ pub ( super ) fn call_call_reducer < ' scope > (
389+ scope : & mut PinScope < ' scope , ' _ > ,
390+ hooks : & HookFunctions < ' scope > ,
390391 op : ReducerOp < ' _ > ,
392+ reducer_args_data_view : Local < ' scope , v8:: DataView > ,
391393) -> ExcResult < ReducerResult > {
392394 let ReducerOp {
393395 id : ReducerId ( reducer_id) ,
@@ -402,7 +404,8 @@ pub(super) fn call_call_reducer(
402404 let sender = serialize_to_js ( scope, & sender. to_u256 ( ) ) ?;
403405 let conn_id: v8:: Local < ' _ , v8:: Value > = serialize_to_js ( scope, & conn_id. to_u128 ( ) ) ?;
404406 let timestamp = serialize_to_js ( scope, & timestamp. to_micros_since_unix_epoch ( ) ) ?;
405- let reducer_args = make_dataview ( scope, <Box < [ u8 ] > >:: from ( & * * reducer_args. get_bsatn ( ) ) ) . into ( ) ;
407+ let reducer_args = reducer_args_to_value ( scope, reducer_args, reducer_args_data_view) ;
408+
406409 let args = & [ reducer_id, sender, conn_id, timestamp, reducer_args] ;
407410
408411 // Call the function.
@@ -418,6 +421,43 @@ pub(super) fn call_call_reducer(
418421 Ok ( user_res)
419422}
420423
424+ /// Converts `args` into a `Value`.
425+ fn reducer_args_to_value < ' scope > (
426+ scope : & mut PinScope < ' scope , ' _ > ,
427+ args : & ArgsTuple ,
428+ buffer : Local < ' scope , v8:: DataView > ,
429+ ) -> Local < ' scope , Value > {
430+ let reducer_args = & * * args. get_bsatn ( ) ;
431+
432+ let len = reducer_args. len ( ) ;
433+ let dv = if len <= buffer. byte_length ( ) {
434+ // We can use the buffer.
435+ let dst_ptr = NonNull :: new ( buffer. data ( ) )
436+ . expect ( "backing store byte length should be > 0" )
437+ . cast :: < u8 > ( ) ;
438+
439+ // Copy `reducer_args` into `buffer`.
440+ let mut dst_ptr = NonNull :: slice_from_raw_parts ( dst_ptr, len) ;
441+ // SAFETY: We know `dst_ptr` to be:
442+ // - trivially properly aligned due to `u8`s alignment being 1.
443+ // - non-null as it was derived from `NonNull`.
444+ // - the range is within the allocation of `buffer`
445+ // as `len <= buffer.byte_length()`,
446+ // so `buffer` is dereferenceable.
447+ // - `dst` will point to a valid `[u8]` as it was zero-initialized.
448+ // - nothing is aliasing the pointer.
449+ let dst = unsafe { dst_ptr. as_mut ( ) } ;
450+ dst. copy_from_slice ( reducer_args) ;
451+
452+ buffer
453+ } else {
454+ // Fall back to allocating new buffers.
455+ make_dataview ( scope, <Box < [ u8 ] > >:: from ( reducer_args) )
456+ } ;
457+
458+ dv. into ( )
459+ }
460+
421461/// Calls the `__call_view__` function `fun`.
422462pub ( super ) fn call_call_view (
423463 scope : & mut PinScope < ' _ , ' _ > ,
0 commit comments