@@ -146,6 +146,33 @@ impl<const MAX_PAGES: usize> IsolatedMemory<MAX_PAGES> {
146146 Ok ( ( ) )
147147 }
148148
149+ /// Wasm `memory.fill` — fill `len` bytes starting at `dst` with `val`.
150+ ///
151+ /// Only the low 8 bits of `val` are used (Wasm spec). Traps (`OutOfBounds`)
152+ /// if the region extends beyond the current active memory.
153+ pub fn fill ( & mut self , dst : usize , val : u8 , len : usize ) -> WasmResult < ( ) > {
154+ let active = self . active_size ( ) ;
155+ fill_inner ( self . flat_mut ( ) , active, dst, val, len)
156+ }
157+
158+ /// Wasm `memory.init` — copy `len` bytes from `data[src_offset..]` into
159+ /// linear memory at `dst`.
160+ ///
161+ /// Unlike `init_data` (which copies an entire slice), this copies a
162+ /// sub-range of a passive data segment. Traps (`OutOfBounds`) if either
163+ /// the source range extends beyond `data` or the destination region extends
164+ /// beyond active memory.
165+ pub fn init_data_partial (
166+ & mut self ,
167+ dst : usize ,
168+ data : & [ u8 ] ,
169+ src_offset : usize ,
170+ len : usize ,
171+ ) -> WasmResult < ( ) > {
172+ let active = self . active_size ( ) ;
173+ init_data_partial_inner ( self . flat_mut ( ) , active, dst, data, src_offset, len)
174+ }
175+
149176 // ── Bounds-checked (safe) load/store ──────────────────────────────
150177
151178 /// Load an i32 from linear memory with bounds checking.
@@ -467,6 +494,38 @@ fn init_data_inner(
467494 Ok ( ( ) )
468495}
469496
497+ #[ inline( never) ]
498+ fn fill_inner (
499+ memory : & mut [ u8 ] ,
500+ active_bytes : usize ,
501+ dst : usize ,
502+ val : u8 ,
503+ len : usize ,
504+ ) -> WasmResult < ( ) > {
505+ let region = checked_slice_mut ( memory, active_bytes, dst, len) ?;
506+ region. fill ( val) ;
507+ Ok ( ( ) )
508+ }
509+
510+ #[ inline( never) ]
511+ fn init_data_partial_inner (
512+ memory : & mut [ u8 ] ,
513+ active_bytes : usize ,
514+ dst : usize ,
515+ data : & [ u8 ] ,
516+ src_offset : usize ,
517+ len : usize ,
518+ ) -> WasmResult < ( ) > {
519+ let src_end = src_offset. checked_add ( len) . ok_or ( WasmTrap :: OutOfBounds ) ?;
520+ if src_end > data. len ( ) {
521+ return Err ( WasmTrap :: OutOfBounds ) ;
522+ }
523+ let src = & data[ src_offset..src_end] ;
524+ let dst_region = checked_slice_mut ( memory, active_bytes, dst, len) ?;
525+ dst_region. copy_from_slice ( src) ;
526+ Ok ( ( ) )
527+ }
528+
470529// ── Unchecked inner functions ─────────────────────────────────────────
471530//
472531// SAFETY: the caller (verified backend) guarantees the offset is in-bounds,
@@ -747,6 +806,89 @@ mod tests {
747806 assert_eq ! ( mem. load_u8( 5 ) . unwrap( ) , 0xAB ) ;
748807 }
749808
809+ // ── fill ──
810+
811+ #[ test]
812+ fn fill_writes_byte_pattern ( ) {
813+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
814+ mem. fill ( 100 , 0xAB , 5 ) . unwrap ( ) ;
815+ for i in 0 ..5usize {
816+ assert_eq ! ( mem. load_u8( 100 + i) . unwrap( ) , 0xAB ) ;
817+ }
818+ }
819+
820+ #[ test]
821+ fn fill_zero_len_is_noop ( ) {
822+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
823+ assert ! ( mem. fill( 0 , 0xFF , 0 ) . is_ok( ) ) ;
824+ }
825+
826+ #[ test]
827+ fn fill_out_of_bounds ( ) {
828+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
829+ assert_eq ! ( mem. fill( PAGE_SIZE - 3 , 0 , 10 ) , Err ( WasmTrap :: OutOfBounds ) ) ;
830+ }
831+
832+ #[ test]
833+ fn fill_at_boundary ( ) {
834+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
835+ assert ! ( mem. fill( PAGE_SIZE - 4 , 0x42 , 4 ) . is_ok( ) ) ;
836+ assert_eq ! ( mem. load_u8( PAGE_SIZE - 1 ) . unwrap( ) , 0x42 ) ;
837+ }
838+
839+ // ── init_data_partial ──
840+
841+ #[ test]
842+ fn init_data_partial_copies_subrange ( ) {
843+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
844+ let data = b"Hello, World!" ;
845+ mem. init_data_partial ( 0 , data, 7 , 5 ) . unwrap ( ) ; // "World"
846+ assert_eq ! ( mem. load_u8( 0 ) . unwrap( ) , b'W' ) ;
847+ assert_eq ! ( mem. load_u8( 4 ) . unwrap( ) , b'd' ) ;
848+ }
849+
850+ #[ test]
851+ fn init_data_partial_zero_len_is_noop ( ) {
852+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
853+ assert ! ( mem. init_data_partial( 0 , b"Hello" , 0 , 0 ) . is_ok( ) ) ;
854+ }
855+
856+ #[ test]
857+ fn init_data_partial_full_segment ( ) {
858+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
859+ mem. init_data_partial ( 10 , b"Hello" , 0 , 5 ) . unwrap ( ) ;
860+ assert_eq ! ( mem. load_u8( 10 ) . unwrap( ) , b'H' ) ;
861+ assert_eq ! ( mem. load_u8( 14 ) . unwrap( ) , b'o' ) ;
862+ }
863+
864+ #[ test]
865+ fn init_data_partial_src_out_of_bounds ( ) {
866+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
867+ // src_offset=3, len=5: 3+5=8 > 5 (data.len())
868+ assert_eq ! (
869+ mem. init_data_partial( 0 , b"Hello" , 3 , 5 ) ,
870+ Err ( WasmTrap :: OutOfBounds )
871+ ) ;
872+ }
873+
874+ #[ test]
875+ fn init_data_partial_dst_out_of_bounds ( ) {
876+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
877+ assert_eq ! (
878+ mem. init_data_partial( PAGE_SIZE - 2 , b"Hello" , 0 , 5 ) ,
879+ Err ( WasmTrap :: OutOfBounds )
880+ ) ;
881+ }
882+
883+ #[ test]
884+ fn init_data_partial_src_offset_overflow ( ) {
885+ let mut mem = Mem :: try_new ( 1 ) . unwrap ( ) ;
886+ assert_eq ! (
887+ mem. init_data_partial( 0 , b"Hello" , usize :: MAX , 1 ) ,
888+ Err ( WasmTrap :: OutOfBounds )
889+ ) ;
890+ }
891+
750892 // ── little-endian encoding ──
751893
752894 #[ test]
0 commit comments