Skip to content

Commit 1084c60

Browse files
committed
Add a global arena and simplify serialization
1 parent 5ec7d68 commit 1084c60

File tree

9 files changed

+302
-401
lines changed

9 files changed

+302
-401
lines changed

src/ecosystem/serde.rs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ use webcore::value::{
1919

2020
use webcore::serialization::{
2121
JsSerialize,
22-
SerializedValue,
23-
PreallocatedArena
22+
SerializedValue
2423
};
2524

2625
use webcore::number::{self, Number, Storage, get_storage};
2726
use webcore::try_from::{TryInto, TryFrom};
2827
use webcore::instance_of::InstanceOf;
2928
use webcore::array::Array;
3029
use webcore::object::Object;
30+
use webcore::global_arena;
3131

3232
impl Serialize for Undefined {
3333
#[inline]
@@ -1023,18 +1023,9 @@ macro_rules! __js_serializable_serde_boilerplate {
10231023

10241024
impl< $($impl_arg),* > $crate::private::JsSerialize for $($kind_arg)* where $($bounds)* {
10251025
#[inline]
1026-
fn _into_js< 'x >( &'x self, arena: &'x $crate::private::PreallocatedArena ) -> $crate::private::SerializedValue< 'x > {
1026+
fn _into_js< 'x >( &'x self ) -> $crate::private::SerializedValue< 'x > {
10271027
let value = $crate::private::to_value( self ).unwrap();
1028-
let value = arena.save( value );
1029-
$crate::private::JsSerialize::_into_js( value, arena )
1030-
}
1031-
1032-
#[inline]
1033-
fn _memory_required( &self ) -> usize {
1034-
// TODO: This is very inefficient. The actual conversion into
1035-
// the Value should be only done once.
1036-
let value = $crate::private::to_value( self ).unwrap();
1037-
$crate::private::JsSerialize::_memory_required( &value )
1028+
$crate::private::serialize_value( value )
10381029
}
10391030
}
10401031

@@ -1213,16 +1204,9 @@ impl< T: fmt::Debug > fmt::Debug for Serde< T > {
12131204

12141205
impl< T: Serialize > JsSerialize for Serde< T > {
12151206
#[inline]
1216-
fn _into_js< 'a >( &'a self, arena: &'a PreallocatedArena ) -> SerializedValue< 'a > {
1217-
let value = to_value( &self.0 ).unwrap();
1218-
let value = arena.save( value );
1219-
value._into_js( arena )
1220-
}
1221-
1222-
#[inline]
1223-
fn _memory_required( &self ) -> usize {
1207+
fn _into_js< 'a >( &'a self ) -> SerializedValue< 'a > {
12241208
let value = to_value( &self.0 ).unwrap();
1225-
value._memory_required()
1209+
global_arena::serialize_value( value )
12261210
}
12271211
}
12281212

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,6 @@ pub mod private {
446446
pub use webcore::serialization::{
447447
JsSerialize,
448448
JsSerializeOwned,
449-
PreallocatedArena,
450449
SerializedValue
451450
};
452451

@@ -461,6 +460,9 @@ pub mod private {
461460
from_value
462461
};
463462

463+
pub use webcore::global_arena::ArenaRestorePoint;
464+
pub use webcore::global_arena::serialize_value;
465+
464466
// This is to prevent an unused_mut warnings in macros, because an `allow` doesn't work apparently?
465467
#[allow(dead_code)]
466468
#[inline(always)]

src/webcore/global_arena.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
use std::mem;
2+
use std::marker::PhantomData;
3+
4+
use webcore::value::Value;
5+
use webcore::serialization::{JsSerialize, SerializedValue};
6+
7+
struct GlobalArena {
8+
memory: *mut u8,
9+
capacity: usize,
10+
length: usize
11+
}
12+
13+
static mut VALUE_ARENA: GlobalArena = GlobalArena {
14+
memory: 0 as *mut u8,
15+
capacity: 0,
16+
length: 0
17+
};
18+
19+
static mut ARENA: GlobalArena = GlobalArena {
20+
memory: 0 as *mut u8,
21+
capacity: 0,
22+
length: 0
23+
};
24+
25+
pub struct RelativeSlice< 'a, T: 'a > {
26+
offset: usize,
27+
length: usize,
28+
tail: usize,
29+
phantom: PhantomData< &'a T >
30+
}
31+
32+
impl< 'a, T: 'a > RelativeSlice< 'a, T > {
33+
#[inline]
34+
pub fn offset( &self ) -> usize {
35+
self.offset
36+
}
37+
38+
#[inline]
39+
pub fn len( &self ) -> usize {
40+
self.length
41+
}
42+
43+
#[inline]
44+
pub unsafe fn append( &mut self, value: T ) {
45+
debug_assert!( self.tail + mem::size_of::< T >() <= self.offset + self.length * mem::size_of::< T >() );
46+
47+
let pointer = ARENA.memory.offset( self.tail as isize ) as *mut T;
48+
mem::forget( mem::replace( &mut *pointer, value ) );
49+
self.tail += mem::size_of::< T >();
50+
}
51+
}
52+
53+
#[doc(hidden)]
54+
pub fn serialize_value< 'a >( value: Value ) -> SerializedValue< 'a > {
55+
unsafe {
56+
let mut vec = Vec::from_raw_parts( VALUE_ARENA.memory as *mut Value, VALUE_ARENA.length, VALUE_ARENA.capacity );
57+
vec.push( value );
58+
let pointer = vec.last().unwrap() as *const Value;
59+
VALUE_ARENA.memory = vec.as_mut_ptr() as *mut u8;
60+
VALUE_ARENA.length = vec.len();
61+
VALUE_ARENA.capacity = vec.capacity();
62+
mem::forget( vec );
63+
64+
JsSerialize::_into_js( &*pointer )
65+
}
66+
}
67+
68+
#[inline]
69+
pub fn reserve< 'a, T >( length: usize ) -> RelativeSlice< 'a, T > {
70+
unsafe {
71+
let offset = reserve_impl( length * mem::size_of::< T >(), mem::align_of::< T >() );
72+
debug_assert_eq!( ARENA.memory.offset( offset as isize ) as usize % mem::align_of::< T >(), 0 );
73+
74+
RelativeSlice { offset, length, tail: offset, phantom: PhantomData }
75+
}
76+
}
77+
78+
unsafe fn reserve_impl( byte_length: usize, align: usize ) -> usize {
79+
let misaligned_bytes = ARENA.memory as usize % align;
80+
let alignment_bytes = if misaligned_bytes > 0 {
81+
align - misaligned_bytes
82+
} else {
83+
0
84+
};
85+
86+
let byte_length = byte_length + alignment_bytes;
87+
if ARENA.length + byte_length > ARENA.capacity {
88+
let mut vector = Vec::from_raw_parts( ARENA.memory, ARENA.length, ARENA.capacity );
89+
vector.reserve( byte_length );
90+
ARENA.capacity = vector.capacity();
91+
ARENA.memory = vector.as_mut_ptr();
92+
mem::forget( vector );
93+
94+
__js_raw_asm!( "Module.STDWEB_PRIVATE.arena = $0;", ARENA.memory );
95+
}
96+
97+
let offset = ARENA.length + alignment_bytes;
98+
ARENA.length += byte_length;
99+
offset
100+
}
101+
102+
#[doc(hidden)]
103+
#[derive(Debug)]
104+
pub struct ArenaRestorePoint {
105+
arena_length: usize,
106+
value_arena_length: usize
107+
}
108+
109+
impl ArenaRestorePoint {
110+
#[doc(hidden)]
111+
#[inline]
112+
pub fn new() -> Self {
113+
unsafe {
114+
ArenaRestorePoint {
115+
arena_length: ARENA.length,
116+
value_arena_length: VALUE_ARENA.length
117+
}
118+
}
119+
}
120+
}
121+
122+
impl Drop for ArenaRestorePoint {
123+
fn drop( &mut self ) {
124+
unsafe {
125+
debug_assert!( ARENA.length >= self.arena_length );
126+
ARENA.length = self.arena_length;
127+
128+
debug_assert!( VALUE_ARENA.length >= self.value_arena_length );
129+
let count = VALUE_ARENA.length - self.value_arena_length;
130+
if count > 0 {
131+
let mut vec = Vec::from_raw_parts( VALUE_ARENA.memory as *mut Value, VALUE_ARENA.length, VALUE_ARENA.capacity );
132+
vec.truncate( self.value_arena_length );
133+
VALUE_ARENA.memory = vec.as_mut_ptr() as *mut u8;
134+
VALUE_ARENA.length = vec.len();
135+
VALUE_ARENA.capacity = vec.capacity();
136+
mem::forget( vec );
137+
}
138+
}
139+
}
140+
}
141+
142+
#[cfg(test)]
143+
mod tests {
144+
use super::{ARENA, VALUE_ARENA, GlobalArena};
145+
146+
unsafe fn clear() {
147+
// This will leak, but in tests we don't care.
148+
ARENA = GlobalArena {
149+
memory: 0 as *mut u8,
150+
capacity: 0,
151+
length: 0
152+
};
153+
154+
VALUE_ARENA = GlobalArena {
155+
memory: 0 as *mut u8,
156+
capacity: 0,
157+
length: 0
158+
};
159+
}
160+
161+
#[test]
162+
fn empty_js_call_does_not_touch_arena() {
163+
unsafe {
164+
clear();
165+
js!();
166+
167+
assert_eq!( ARENA.memory, 0 as *mut u8 );
168+
assert_eq!( ARENA.length, 0 );
169+
assert_eq!( ARENA.capacity, 0 );
170+
}
171+
}
172+
173+
#[test]
174+
fn arena_is_properly_cleared_after_a_js_call() {
175+
unsafe {
176+
clear();
177+
js!( @{&[1, 2, 3, 4, 5, 6, 7, 8][..]}; );
178+
179+
assert_ne!( ARENA.memory, 0 as *mut u8 );
180+
assert_eq!( ARENA.length, 0 );
181+
assert_ne!( ARENA.capacity, 0 );
182+
}
183+
}
184+
185+
#[test]
186+
fn arena_is_not_reallocated_when_it_is_not_necessary() {
187+
unsafe {
188+
clear();
189+
js!( @{&[1, 2, 3, 4, 5, 6, 7, 8][..]}; );
190+
191+
let memory = ARENA.memory;
192+
let capacity = ARENA.capacity;
193+
194+
js!( @{&[1, 2, 3, 4, 5, 6, 7, 8][..]}; );
195+
196+
assert_eq!( ARENA.memory, memory );
197+
assert_eq!( ARENA.capacity, capacity );
198+
}
199+
}
200+
}

src/webcore/macros.rs

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -298,20 +298,14 @@ macro_rules! _js_impl {
298298
_js_impl!( @if $condition in [$($rest)*] {$($true_case)*} else {$($false_case)*} );
299299
};
300300

301-
(@prepare $memory_required:ident [] [$($names:tt)*]) => {};
302-
(@prepare $memory_required:ident [$arg:tt $($rest_args:tt)*] [$name:tt $($rest_names:tt)*]) => {
301+
(@serialize [] [$($names:tt)*]) => {};
302+
(@serialize [$arg:tt $($rest_args:tt)*] [$name:tt $($rest_names:tt)*]) => {
303303
let $name = $arg;
304304
let $name = $crate::private::IntoNewtype::into_newtype( $name );
305-
$memory_required += $crate::private::JsSerializeOwned::memory_required_owned( &$name );
306-
_js_impl!( @prepare $memory_required [$($rest_args)*] [$($rest_names)*] );
307-
};
308-
309-
(@serialize $arena:ident [] [$($names:tt)*]) => {};
310-
(@serialize $arena:ident [$arg:tt $($rest_args:tt)*] [$name:tt $($rest_names:tt)*]) => {
311305
let mut $name = Some( $name );
312-
let $name = $crate::private::JsSerializeOwned::into_js_owned( &mut $name, &$arena );
306+
let $name = $crate::private::JsSerializeOwned::into_js_owned( &mut $name );
313307
let $name = &$name as *const _;
314-
_js_impl!( @serialize $arena [$($rest_args)*] [$($rest_names)*] );
308+
_js_impl!( @serialize [$($rest_args)*] [$($rest_names)*] );
315309
};
316310

317311
(@call_emscripten [$code:expr] [] [$($arg_names:tt)*]) => {
@@ -390,19 +384,11 @@ macro_rules! _js_impl {
390384
$crate::initialize();
391385
}
392386

393-
let mut memory_required = 0;
394-
_js_impl!( @prepare memory_required [$($args)*] [a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15] );
395-
396-
#[allow(unused_variables)]
397-
let arena = $crate::private::PreallocatedArena::new( memory_required );
398-
399-
_js_impl!( @serialize arena [$($args)*] [a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15] );
400-
arena.assert_no_free_space_left();
401-
402-
$crate::private::noop( &mut memory_required );
387+
let restore_point = $crate::private::ArenaRestorePoint::new();
388+
_js_impl!( @serialize [$($args)*] [a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15] );
403389

404390
#[allow(unused_unsafe, unused_parens)]
405-
unsafe {
391+
let result = unsafe {
406392
_js_impl!(
407393
@if no_return in [$($flags)*] {
408394
_js_impl!(
@@ -425,7 +411,10 @@ macro_rules! _js_impl {
425411
result.deserialize()
426412
}}
427413
)
428-
}
414+
};
415+
416+
::std::mem::drop( restore_point );
417+
result
429418
}
430419
};
431420

@@ -521,25 +510,15 @@ macro_rules! __js_serializable_boilerplate {
521510
(($($impl_arg:tt)*) ($($kind_arg:tt)*) ($($bounds:tt)*)) => {
522511
impl< $($impl_arg)* > $crate::private::JsSerializeOwned for $($kind_arg)* where $($bounds)* {
523512
#[inline]
524-
fn into_js_owned< '_a >( value: &'_a mut Option< Self >, arena: &'_a $crate::private::PreallocatedArena ) -> $crate::private::SerializedValue< '_a > {
525-
$crate::private::JsSerialize::_into_js( value.as_ref().unwrap(), arena )
526-
}
527-
528-
#[inline]
529-
fn memory_required_owned( &self ) -> usize {
530-
$crate::private::JsSerialize::_memory_required( self )
513+
fn into_js_owned< '_a >( value: &'_a mut Option< Self > ) -> $crate::private::SerializedValue< '_a > {
514+
$crate::private::JsSerialize::_into_js( value.as_ref().unwrap() )
531515
}
532516
}
533517

534518
impl< '_r, $($impl_arg)* > $crate::private::JsSerializeOwned for &'_r $($kind_arg)* where $($bounds)* {
535519
#[inline]
536-
fn into_js_owned< '_a >( value: &'_a mut Option< Self >, arena: &'_a $crate::private::PreallocatedArena ) -> $crate::private::SerializedValue< '_a > {
537-
$crate::private::JsSerialize::_into_js( value.unwrap(), arena )
538-
}
539-
540-
#[inline]
541-
fn memory_required_owned( &self ) -> usize {
542-
$crate::private::JsSerialize::_memory_required( *self )
520+
fn into_js_owned< '_a >( value: &'_a mut Option< Self > ) -> $crate::private::SerializedValue< '_a > {
521+
$crate::private::JsSerialize::_into_js( value.unwrap() )
543522
}
544523
}
545524
};

src/webcore/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod instance_of;
1818
pub mod reference_type;
1919
pub mod promise;
2020
pub mod discard;
21+
pub mod global_arena;
2122

2223
#[cfg(feature = "futures-support")]
2324
pub mod promise_future;

src/webcore/runtime.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,18 @@ Module.STDWEB_PRIVATE.to_js = function to_js( address ) {
6262
} else if( kind === 6 ) {
6363
return true;
6464
} else if( kind === 7 ) {
65-
var pointer = HEAPU32[ address / 4 ];
65+
var pointer = Module.STDWEB_PRIVATE.arena + HEAPU32[ address / 4 ];
6666
var length = HEAPU32[ (address + 4) / 4 ];
6767
var output = [];
6868
for( var i = 0; i < length; ++i ) {
6969
output.push( Module.STDWEB_PRIVATE.to_js( pointer + i * 16 ) );
7070
}
7171
return output;
7272
} else if( kind === 8 ) {
73-
var value_array_pointer = HEAPU32[ address / 4 ];
73+
var arena = Module.STDWEB_PRIVATE.arena;
74+
var value_array_pointer = arena + HEAPU32[ address / 4 ];
7475
var length = HEAPU32[ (address + 4) / 4 ];
75-
var key_array_pointer = HEAPU32[ (address + 8) / 4 ];
76+
var key_array_pointer = arena + HEAPU32[ (address + 8) / 4 ];
7677
var output = {};
7778
for( var i = 0; i < length; ++i ) {
7879
var key_pointer = HEAPU32[ (key_array_pointer + i * 8) / 4 ];

0 commit comments

Comments
 (0)