mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 23:34:47 -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 => {
|
ValueType::ArrayBufferView => {
|
||||||
v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
||||||
.and_then(|view| {
|
.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)))
|
.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
|
// Magic Buffer
|
||||||
if name == magic::buffer::BUF_NAME {
|
if name == magic::buffer::BUF_NAME {
|
||||||
let zero_copy_buf =
|
let zero_copy_buf = match self.input.is_array_buffer() {
|
||||||
v8::Local::<v8::ArrayBufferView>::try_from(self.input)
|
// 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| {
|
.and_then(|view| {
|
||||||
magic::zero_copy_buf::ZeroCopyBuf::try_new(self.scope, view)
|
magic::zero_copy_buf::ZeroCopyBuf::try_from((
|
||||||
})
|
&mut *self.scope,
|
||||||
.map_err(|_| Error::ExpectedArray)?;
|
view,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
.map_err(|_| Error::ExpectedBuffer)?;
|
||||||
let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) };
|
let data: [u8; 32] = unsafe { std::mem::transmute(zero_copy_buf) };
|
||||||
return visitor.visit_bytes(&data);
|
return visitor.visit_bytes(&data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub enum Error {
|
||||||
ExpectedMap,
|
ExpectedMap,
|
||||||
ExpectedEnum,
|
ExpectedEnum,
|
||||||
ExpectedObject,
|
ExpectedObject,
|
||||||
|
ExpectedBuffer,
|
||||||
|
|
||||||
ExpectedUtf8,
|
ExpectedUtf8,
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
@ -15,25 +16,32 @@ pub enum MagicBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn empty() -> Self {
|
||||||
MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice())))
|
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 {
|
impl Clone for MagicBuffer {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -25,31 +25,46 @@ pub struct ZeroCopyBuf {
|
||||||
unsafe impl Send for ZeroCopyBuf {}
|
unsafe impl Send for ZeroCopyBuf {}
|
||||||
|
|
||||||
impl ZeroCopyBuf {
|
impl ZeroCopyBuf {
|
||||||
pub fn new<'s>(
|
pub fn from_buffer(
|
||||||
scope: &mut v8::HandleScope<'s>,
|
buffer: v8::Local<v8::ArrayBuffer>,
|
||||||
view: v8::Local<v8::ArrayBufferView>,
|
byte_offset: usize,
|
||||||
) -> Self {
|
byte_length: usize,
|
||||||
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> {
|
) -> Result<Self, v8::DataError> {
|
||||||
let backing_store = view.buffer(scope).unwrap().get_backing_store();
|
let backing_store = buffer.get_backing_store();
|
||||||
if backing_store.is_shared() {
|
match backing_store.is_shared() {
|
||||||
return Err(v8::DataError::BadType {
|
true => Err(v8::DataError::BadType {
|
||||||
actual: "shared ArrayBufferView",
|
actual: "shared ArrayBufferView",
|
||||||
expected: "non-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,
|
impl<'s> TryFrom<v8::Local<'s, v8::ArrayBuffer>> for ZeroCopyBuf {
|
||||||
byte_offset,
|
type Error = v8::DataError;
|
||||||
byte_length,
|
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,
|
Number,
|
||||||
String,
|
String,
|
||||||
Array,
|
Array,
|
||||||
|
ArrayBuffer,
|
||||||
ArrayBufferView,
|
ArrayBufferView,
|
||||||
Object,
|
Object,
|
||||||
}
|
}
|
||||||
|
@ -25,6 +26,8 @@ impl ValueType {
|
||||||
return Self::String;
|
return Self::String;
|
||||||
} else if v.is_array() {
|
} else if v.is_array() {
|
||||||
return Self::Array;
|
return Self::Array;
|
||||||
|
} else if v.is_array_buffer() {
|
||||||
|
return Self::ArrayBuffer;
|
||||||
} else if v.is_array_buffer_view() {
|
} else if v.is_array_buffer_view() {
|
||||||
return Self::ArrayBufferView;
|
return Self::ArrayBufferView;
|
||||||
} else if v.is_object() {
|
} else if v.is_object() {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use serde_v8::utils::{js_exec, v8_do};
|
use serde_v8::utils::{js_exec, v8_do};
|
||||||
|
use serde_v8::Buffer;
|
||||||
use serde_v8::Error;
|
use serde_v8::Error;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[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
|
// JSON tests: serde_json::Value compatibility
|
||||||
////
|
////
|
||||||
|
|
Loading…
Reference in a new issue