stdweb/webapi/
typed_array.rs1use std::marker::PhantomData;
2use std::mem::size_of;
3use webcore::value::Reference;
4use webcore::try_from::TryInto;
5use webcore::instance_of::InstanceOf;
6use webapi::array_buffer::ArrayBuffer;
7
8pub trait ArrayKind: Sized {
9 fn is_typed_array( reference: &Reference ) -> bool;
10 fn into_typed_array( slice: &[Self] ) -> TypedArray< Self >;
11 fn into_typed_array_from_array_buffer( buffer: &ArrayBuffer ) -> TypedArray< Self >;
12 fn from_typed_array( array: &TypedArray< Self > ) -> Vec< Self >;
13}
14
15macro_rules! arraykind {
16 ($element_type:ty, $js_array_type:ident, $heap_type:ident) => {
17 impl ArrayKind for $element_type {
18 fn is_typed_array( reference: &Reference ) -> bool {
19 instanceof!( *reference, $js_array_type )
20 }
21
22 fn into_typed_array( slice: &[Self] ) -> TypedArray< Self > {
23 let slice_ptr = (slice.as_ptr() as usize / size_of::<$element_type>()) as i32;
24 let raw = __js_raw_asm_int!(
25 concat!(
26 "return Module.STDWEB_PRIVATE.acquire_rust_reference( Module.",
27 stringify!($heap_type),
28 ".slice( $0, $1 ) );"
29 ),
30 slice_ptr,
31 slice_ptr + slice.len() as i32
32 );
33
34 let reference = unsafe {
35 Reference::from_raw_unchecked_noref( raw )
36 };
37
38 reference.downcast().unwrap()
39 }
40
41 fn into_typed_array_from_array_buffer( buffer: &ArrayBuffer ) -> TypedArray< Self > {
42 let raw = __js_raw_asm_int!(
43 concat!(
44 "return Module.STDWEB_PRIVATE.acquire_rust_reference( new ",
45 stringify!( $js_array_type ),
46 "( Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) )",
47 " );"
48 ),
49 buffer.as_ref().as_raw()
50 );
51
52 let reference = unsafe { Reference::from_raw_unchecked_noref( raw ) };
53 reference.downcast().unwrap()
54 }
55
56 fn from_typed_array( array: &TypedArray< Self > ) -> Vec< Self > {
57 let length = array.len() as usize;
58 let mut vector = Vec::with_capacity( length );
59 let vec_ptr = (vector.as_ptr() as usize / size_of::<$element_type>()) as i32;
60
61 js!( @(no_return)
62 var array = @{array};
63 var pointer = @{vec_ptr};
64 Module.$heap_type.set( array, pointer );
65 );
66
67 unsafe {
68 vector.set_len( length );
69 }
70
71 vector
72 }
73 }
74
75 impl From< TypedArray< $element_type > > for Vec< $element_type > {
76 fn from( array: TypedArray< $element_type > ) -> Self {
77 <$element_type>::from_typed_array( &array )
78 }
79 }
80
81 impl< 'a > From< &'a TypedArray< $element_type > > for Vec< $element_type > {
82 fn from( array: &'a TypedArray< $element_type > ) -> Self {
83 <$element_type>::from_typed_array( array )
84 }
85 }
86 }
87}
88
89arraykind!( i8, Int8Array, HEAP8 );
90arraykind!( u8, Uint8Array, HEAPU8 );
91arraykind!( i16, Int16Array, HEAP16 );
92arraykind!( u16, Uint16Array, HEAPU16 );
93arraykind!( i32, Int32Array, HEAP32 );
94arraykind!( u32, Uint32Array, HEAPU32 );
95arraykind!( f32, Float32Array, HEAPF32 );
96arraykind!( f64, Float64Array, HEAPF64 );
97
98impl< T: ArrayKind > InstanceOf for TypedArray< T > {
99 #[inline]
100 fn instance_of( reference: &Reference ) -> bool {
101 T::is_typed_array( reference )
102 }
103}
104
105#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
110pub struct TypedArray< T: ArrayKind >( Reference, PhantomData< T > );
111
112impl< T: ArrayKind > TypedArray< T > {
113 pub fn buffer( &self ) -> ArrayBuffer {
118 js!( return @{self}.buffer; ).try_into().unwrap()
119 }
120
121 pub fn len( &self ) -> u32 {
125 let reference = self.as_ref();
126 js!( return @{reference}.length; ).try_into().unwrap()
127 }
128
129 pub fn to_vec( &self ) -> Vec< T > {
131 T::from_typed_array( self )
132 }
133}
134
135impl< 'a, T: ArrayKind > From< &'a [T] > for TypedArray< T > {
136 fn from( slice: &'a [T] ) -> Self {
137 T::into_typed_array( slice )
138 }
139}
140
141impl< T: ArrayKind > From< ArrayBuffer > for TypedArray< T > {
142 fn from( buffer: ArrayBuffer ) -> Self {
143 T::into_typed_array_from_array_buffer( &buffer )
144 }
145}
146
147impl< 'a, T: ArrayKind > From< &'a ArrayBuffer > for TypedArray< T > {
148 fn from( buffer: &'a ArrayBuffer ) -> Self {
149 T::into_typed_array_from_array_buffer( buffer )
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::TypedArray;
156 use webcore::try_from::TryInto;
157 use webapi::array_buffer::ArrayBuffer;
158
159 macro_rules! arraykind_test {
160 ($element_type: ident, $js_array_type: ident) => {
161 mod $element_type {
162 use super::super::TypedArray;
163 use std;
164 use webcore::try_from::TryInto;
165 use webcore::value::Value;
166 use webapi::array_buffer::ArrayBuffer;
167
168 const ARRAY: &[$element_type] = &[
169 std::$element_type::MIN,
170 std::$element_type::MAX
171 ];
172
173 #[test]
174 fn into() {
175 let typed_array: TypedArray< $element_type > = ARRAY.into();
176 assert_eq!(
177 js!( return @{&typed_array} instanceof $js_array_type; ),
178 Value::Bool( true )
179 );
180 assert_eq!(
181 js!( return @{&typed_array}.length === @{ARRAY.len() as u32}; ),
182 Value::Bool( true )
183 );
184 assert_eq!(
185 js!( return @{&typed_array}[0] === @{ARRAY[0]}; ),
186 Value::Bool( true )
187 );
188 assert_eq!(
189 js!( return @{&typed_array}[1] === @{ARRAY[1]}; ),
190 Value::Bool( true )
191 );
192 }
193
194 #[test]
195 fn from() {
196 let value = js!( return new $js_array_type( [@{ARRAY[0]}, @{ARRAY[1]}] ); );
197 let typed_array: TypedArray< $element_type > = value.try_into().unwrap();
198 let vec: Vec< $element_type > = typed_array.into();
199 assert_eq!( vec.len(), ARRAY.len() );
200 assert_eq!( vec, ARRAY);
201 }
202
203 #[test]
204 fn from_array_buffer() {
205 let value = js!( return new $js_array_type( [@{ARRAY[0]}, @{ARRAY[1]}] ).buffer; );
206 let array_buffer: ArrayBuffer = value.try_into().unwrap();
207 let typed_array: TypedArray< $element_type > = array_buffer.into();
208 let vec: Vec< $element_type > = typed_array.into();
209 assert_eq!( vec.len(), ARRAY.len() );
210 assert_eq!( vec, ARRAY );
211 }
212 }
213 }
214 }
215
216 arraykind_test!(i8, Int8Array);
217 arraykind_test!(u8, Uint8Array);
218 arraykind_test!(i16, Int16Array);
219 arraykind_test!(u16, Uint16Array);
220 arraykind_test!(i32, Int32Array);
221 arraykind_test!(u32, Uint32Array);
222 arraykind_test!(f32, Float32Array);
223 arraykind_test!(f64, Float64Array);
224
225 fn get_refcount() -> i32 {
226 js!( return Object.keys( Module.STDWEB_PRIVATE.id_to_ref_map ).length; ).try_into().unwrap()
227 }
228
229 #[test]
230 fn slice_to_typed_array_does_not_leak() {
231 let initial_refcount = get_refcount();
232 {
233 let vec: Vec< i32 > = (0..10).collect();
234 let _: TypedArray< i32 > = vec[..].into();
235 }
236 assert_eq!( initial_refcount, get_refcount() );
237 }
238
239 #[test]
240 fn array_buffer_to_typed_array_does_not_leak() {
241 let initial_refcount = get_refcount();
242 {
243 let array_buffer = ArrayBuffer::new( 16 ).unwrap();
244 let _: TypedArray< i32 > = array_buffer.into();
245 }
246 assert_eq!( initial_refcount, get_refcount() );
247 }
248}