mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
feat(serde_v8): deserialize ArrayBuffers (#13436)
Previously we would only deserialize `ArrayBufferView`s as zero-copy bufs This avoids rewrapping `ArrayBuffers` in `ArrayBufferView`s when implementing APIs that take [BufferSource](https://webidl.spec.whatwg.org/#BufferSource) args passed through the op-layer
This commit is contained in:
parent
443d8fc41c
commit
1cc38f5155
6 changed files with 112 additions and 42 deletions
|
@ -134,9 +134,20 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
|
|||
ValueType::ArrayBufferView => {
|
||||
v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
||||
.and_then(|view| {
|
||||
magic::zero_copy_buf::ZeroCopyBuf::try_new(self.scope, view)
|
||||
magic::zero_copy_buf::ZeroCopyBuf::try_from((
|
||||
&mut *self.scope,
|
||||
view,
|
||||
))
|
||||
})
|
||||
.map_err(|_| Error::ExpectedInteger)
|
||||
.map_err(|_| Error::ExpectedBuffer)
|
||||
.and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb)))
|
||||
}
|
||||
ValueType::ArrayBuffer => {
|
||||
v8::Local::<v8::ArrayBuffer>::try_from(self.input)
|
||||
.and_then(|buffer| {
|
||||
magic::zero_copy_buf::ZeroCopyBuf::try_from(buffer)
|
||||
})
|
||||
.map_err(|_| Error::ExpectedBuffer)
|
||||
.and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb)))
|
||||
}
|
||||
}
|
||||
|
@ -339,12 +350,20 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
|
|||
|
||||
// Magic Buffer
|
||||
if name == magic::buffer::BUF_NAME {
|
||||
let zero_copy_buf =
|
||||
v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
||||
let zero_copy_buf = match self.input.is_array_buffer() {
|
||||
// ArrayBuffer
|
||||
true => v8::Local::<v8::ArrayBuffer>::try_from(self.input)
|
||||
.and_then(magic::zero_copy_buf::ZeroCopyBuf::try_from),
|
||||
// maybe ArrayBufferView
|
||||
false => v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
||||
.and_then(|view| {
|
||||
magic::zero_copy_buf::ZeroCopyBuf::try_new(self.scope, view)
|
||||
})
|
||||
.map_err(|_| Error::ExpectedArray)?;
|
||||
magic::zero_copy_buf::ZeroCopyBuf::try_from((
|
||||
&mut *self.scope,
|
||||
view,
|
||||
))
|
||||
}),
|
||||
}
|
||||
.map_err(|_| Error::ExpectedBuffer)?;
|
||||
let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) };
|
||||
return visitor.visit_bytes(&data);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ pub enum Error {
|
|||
ExpectedMap,
|
||||
ExpectedEnum,
|
||||
ExpectedObject,
|
||||
ExpectedBuffer,
|
||||
|
||||
ExpectedUtf8,
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
|
@ -15,25 +16,32 @@ pub enum MagicBuffer {
|
|||
}
|
||||
|
||||
impl MagicBuffer {
|
||||
pub fn new<'s>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
view: v8::Local<v8::ArrayBufferView>,
|
||||
) -> Self {
|
||||
Self::try_new(scope, view).unwrap()
|
||||
}
|
||||
|
||||
pub fn try_new<'s>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
view: v8::Local<v8::ArrayBufferView>,
|
||||
) -> Result<Self, v8::DataError> {
|
||||
Ok(Self::FromV8(ZeroCopyBuf::try_new(scope, view)?))
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice())))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<v8::Local<'s, v8::ArrayBuffer>> for MagicBuffer {
|
||||
type Error = v8::DataError;
|
||||
fn try_from(buffer: v8::Local<v8::ArrayBuffer>) -> Result<Self, Self::Error> {
|
||||
Ok(Self::FromV8(ZeroCopyBuf::try_from(buffer)?))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@AaronO): consider streamlining this as "ScopedValue" ?
|
||||
type ScopedView<'a, 'b, 's> = (
|
||||
&'s mut v8::HandleScope<'a>,
|
||||
v8::Local<'b, v8::ArrayBufferView>,
|
||||
);
|
||||
impl<'a, 'b, 's> TryFrom<ScopedView<'a, 'b, 's>> for MagicBuffer {
|
||||
type Error = v8::DataError;
|
||||
fn try_from(
|
||||
scoped_view: ScopedView<'a, 'b, 's>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self::FromV8(ZeroCopyBuf::try_from(scoped_view)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for MagicBuffer {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
|
|
|
@ -25,31 +25,46 @@ pub struct ZeroCopyBuf {
|
|||
unsafe impl Send for ZeroCopyBuf {}
|
||||
|
||||
impl ZeroCopyBuf {
|
||||
pub fn new<'s>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
view: v8::Local<v8::ArrayBufferView>,
|
||||
) -> Self {
|
||||
Self::try_new(scope, view).unwrap()
|
||||
}
|
||||
|
||||
pub fn try_new<'s>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
view: v8::Local<v8::ArrayBufferView>,
|
||||
pub fn from_buffer(
|
||||
buffer: v8::Local<v8::ArrayBuffer>,
|
||||
byte_offset: usize,
|
||||
byte_length: usize,
|
||||
) -> Result<Self, v8::DataError> {
|
||||
let backing_store = view.buffer(scope).unwrap().get_backing_store();
|
||||
if backing_store.is_shared() {
|
||||
return Err(v8::DataError::BadType {
|
||||
let backing_store = buffer.get_backing_store();
|
||||
match backing_store.is_shared() {
|
||||
true => Err(v8::DataError::BadType {
|
||||
actual: "shared ArrayBufferView",
|
||||
expected: "non-shared ArrayBufferView",
|
||||
});
|
||||
}),
|
||||
false => Ok(Self {
|
||||
backing_store,
|
||||
byte_offset,
|
||||
byte_length,
|
||||
}),
|
||||
}
|
||||
let byte_offset = view.byte_offset();
|
||||
let byte_length = view.byte_length();
|
||||
Ok(Self {
|
||||
backing_store,
|
||||
byte_offset,
|
||||
byte_length,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<v8::Local<'s, v8::ArrayBuffer>> for ZeroCopyBuf {
|
||||
type Error = v8::DataError;
|
||||
fn try_from(buffer: v8::Local<v8::ArrayBuffer>) -> Result<Self, Self::Error> {
|
||||
Self::from_buffer(buffer, 0, buffer.byte_length())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@AaronO): consider streamlining this as "ScopedValue" ?
|
||||
type ScopedView<'a, 'b, 's> = (
|
||||
&'s mut v8::HandleScope<'a>,
|
||||
v8::Local<'b, v8::ArrayBufferView>,
|
||||
);
|
||||
impl<'a, 'b, 's> TryFrom<ScopedView<'a, 'b, 's>> for ZeroCopyBuf {
|
||||
type Error = v8::DataError;
|
||||
fn try_from(
|
||||
scoped_view: ScopedView<'a, 'b, 's>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let (scope, view) = scoped_view;
|
||||
let buffer = view.buffer(scope).unwrap();
|
||||
Self::from_buffer(buffer, view.byte_offset(), view.byte_length())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ pub enum ValueType {
|
|||
Number,
|
||||
String,
|
||||
Array,
|
||||
ArrayBuffer,
|
||||
ArrayBufferView,
|
||||
Object,
|
||||
}
|
||||
|
@ -25,6 +26,8 @@ impl ValueType {
|
|||
return Self::String;
|
||||
} else if v.is_array() {
|
||||
return Self::Array;
|
||||
} else if v.is_array_buffer() {
|
||||
return Self::ArrayBuffer;
|
||||
} else if v.is_array_buffer_view() {
|
||||
return Self::ArrayBufferView;
|
||||
} else if v.is_object() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
use serde_v8::utils::{js_exec, v8_do};
|
||||
use serde_v8::Buffer;
|
||||
use serde_v8::Error;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
|
@ -192,6 +193,29 @@ fn de_string_or_buffer() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn de_buffers() {
|
||||
// ArrayBufferView
|
||||
dedo("new Uint8Array([97])", |scope, v| {
|
||||
let buf: Buffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(&*buf, &[97]);
|
||||
});
|
||||
|
||||
// ArrayBuffer
|
||||
dedo("(new Uint8Array([97])).buffer", |scope, v| {
|
||||
let buf: Buffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(&*buf, &[97]);
|
||||
});
|
||||
|
||||
dedo(
|
||||
"(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))",
|
||||
|scope, v| {
|
||||
let buf: Buffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(&*buf, &[0x68, 0x65, 0x6C, 0x6C, 0x6F]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
////
|
||||
// JSON tests: serde_json::Value compatibility
|
||||
////
|
||||
|
|
Loading…
Reference in a new issue