Changed blob to use DataSlice with Arc in order to limit wasteful copying of byte vector

This commit is contained in:
David Raifaizen 2016-01-06 19:41:52 -05:00
parent 238a8786de
commit 76f689cd06
7 changed files with 190 additions and 132 deletions

View file

@ -8,104 +8,30 @@ use dom::bindings::error::Fallible;
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use num::ToPrimitive;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Cell;
use std::cmp::{max, min};
use std::sync::mpsc::Sender;
use std::sync::Arc;
use util::str::DOMString;
// https://w3c.github.io/FileAPI/#blob
#[dom_struct]
pub struct Blob {
reflector_: Reflector,
bytes: Option<Vec<u8>>,
typeString: String,
global: GlobalField,
isClosed_: Cell<bool>,
#[derive(Clone, JSTraceable)]
pub struct DataSlice {
bytes: Arc<Vec<u8>>,
bytes_start: usize,
bytes_end: usize
}
fn is_ascii_printable(string: &str) -> bool {
// Step 5.1 in Sec 5.1 of File API spec
// https://w3c.github.io/FileAPI/#constructorBlob
string.chars().all(|c| c >= '\x20' && c <= '\x7E')
}
impl Blob {
pub fn new_inherited(global: GlobalRef, bytes: Option<Vec<u8>>, typeString: &str) -> Blob {
Blob {
reflector_: Reflector::new(),
bytes: bytes,
typeString: typeString.to_owned(),
global: GlobalField::from_rooted(&global),
isClosed_: Cell::new(false),
}
}
pub fn new(global: GlobalRef, bytes: Option<Vec<u8>>, typeString: &str) -> Root<Blob> {
reflect_dom_object(box Blob::new_inherited(global, bytes, typeString),
global,
BlobBinding::Wrap)
}
// https://w3c.github.io/FileAPI/#constructorBlob
pub fn Constructor(global: GlobalRef) -> Fallible<Root<Blob>> {
Ok(Blob::new(global, None, ""))
}
// https://w3c.github.io/FileAPI/#constructorBlob
pub fn Constructor_(global: GlobalRef,
blobParts: DOMString,
blobPropertyBag: &BlobBinding::BlobPropertyBag)
-> Fallible<Root<Blob>> {
// TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView or Blob
// FIXME(ajeffrey): convert directly from a DOMString to a Vec<u8>
let bytes: Option<Vec<u8>> = Some(String::from(blobParts).into_bytes());
let typeString = if is_ascii_printable(&blobPropertyBag.type_) {
&*blobPropertyBag.type_
} else {
""
};
Ok(Blob::new(global, bytes, &typeString.to_ascii_lowercase()))
}
pub fn read_out_buffer(&self, send: Sender<Vec<u8>>) {
send.send(self.bytes.clone().unwrap_or(vec![])).unwrap();
}
// simpler to use version of read_out_buffer
pub fn clone_bytes(&self) -> Vec<u8> {
self.bytes.clone().unwrap_or(vec![])
}
}
impl BlobMethods for Blob {
// https://w3c.github.io/FileAPI/#dfn-size
fn Size(&self) -> u64 {
match self.bytes {
None => 0,
Some(ref bytes) => bytes.len() as u64,
}
}
// https://w3c.github.io/FileAPI/#dfn-type
fn Type(&self) -> DOMString {
DOMString::from(self.typeString.clone())
}
// https://w3c.github.io/FileAPI/#slice-method-algo
fn Slice(&self,
start: Option<i64>,
end: Option<i64>,
contentType: Option<DOMString>)
-> Root<Blob> {
let size: i64 = self.Size().to_i64().unwrap();
impl DataSlice {
pub fn new(bytes: Arc<Vec<u8>>, start: Option<i64>, end: Option<i64>) -> DataSlice {
let size = bytes.len() as i64;
let relativeStart: i64 = match start {
None => 0,
Some(start) => {
if start < 0 {
max(size.to_i64().unwrap() + start, 0)
max(size + start, 0)
} else {
min(start, size)
}
@ -121,6 +47,119 @@ impl BlobMethods for Blob {
}
}
};
let span: i64 = max(relativeEnd - relativeStart, 0);
let start = relativeStart.to_usize().unwrap();
let end = (relativeStart + span).to_usize().unwrap();
DataSlice {
bytes: bytes,
bytes_start: start,
bytes_end: end
}
}
pub fn get_bytes(&self) -> &[u8] {
&self.bytes[self.bytes_start..self.bytes_end]
}
pub fn size(&self) -> u64 {
(self.bytes_end as u64) - (self.bytes_start as u64)
}
}
// https://w3c.github.io/FileAPI/#blob
#[dom_struct]
pub struct Blob {
reflector_: Reflector,
#[ignore_heap_size_of = "No clear owner"]
data: DataSlice,
typeString: String,
global: GlobalField,
isClosed_: Cell<bool>,
}
fn is_ascii_printable(string: &str) -> bool {
// Step 5.1 in Sec 5.1 of File API spec
// https://w3c.github.io/FileAPI/#constructorBlob
string.chars().all(|c| c >= '\x20' && c <= '\x7E')
}
impl Blob {
pub fn new_inherited(global: GlobalRef,
bytes: Arc<Vec<u8>>,
bytes_start: Option<i64>,
bytes_end: Option<i64>,
typeString: &str) -> Blob {
Blob {
reflector_: Reflector::new(),
data: DataSlice::new(bytes, bytes_start, bytes_end),
typeString: typeString.to_owned(),
global: GlobalField::from_rooted(&global),
isClosed_: Cell::new(false),
}
}
pub fn new(global: GlobalRef, bytes: Vec<u8>, typeString: &str) -> Root<Blob> {
let boxed_blob = box Blob::new_inherited(global, Arc::new(bytes), None, None, typeString);
reflect_dom_object(boxed_blob, global, BlobBinding::Wrap)
}
fn new_sliced(global: GlobalRef,
bytes: Arc<Vec<u8>>,
bytes_start: Option<i64>,
bytes_end: Option<i64>,
typeString: &str) -> Root<Blob> {
let boxed_blob = box Blob::new_inherited(global, bytes, bytes_start, bytes_end, typeString);
reflect_dom_object(boxed_blob, global, BlobBinding::Wrap)
}
// https://w3c.github.io/FileAPI/#constructorBlob
pub fn Constructor(global: GlobalRef) -> Fallible<Root<Blob>> {
Ok(Blob::new(global, Vec::new(), ""))
}
// https://w3c.github.io/FileAPI/#constructorBlob
pub fn Constructor_(global: GlobalRef,
blobParts: DOMString,
blobPropertyBag: &BlobBinding::BlobPropertyBag)
-> Fallible<Root<Blob>> {
// TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView or Blob
// FIXME(ajeffrey): convert directly from a DOMString to a Vec<u8>
let bytes: Vec<u8> = String::from(blobParts).into_bytes();
let typeString = if is_ascii_printable(&blobPropertyBag.type_) {
&*blobPropertyBag.type_
} else {
""
};
Ok(Blob::new(global, bytes, &typeString.to_ascii_lowercase()))
}
pub fn get_data(&self) -> &DataSlice {
&self.data
}
}
impl BlobMethods for Blob {
// https://w3c.github.io/FileAPI/#dfn-size
fn Size(&self) -> u64 {
self.data.size()
}
// https://w3c.github.io/FileAPI/#dfn-type
fn Type(&self) -> DOMString {
DOMString::from(self.typeString.clone())
}
// https://w3c.github.io/FileAPI/#slice-method-algo
fn Slice(&self,
start: Option<i64>,
end: Option<i64>,
contentType: Option<DOMString>)
-> Root<Blob> {
let relativeContentType = match contentType {
None => DOMString::new(),
Some(mut str) => {
@ -132,18 +171,9 @@ impl BlobMethods for Blob {
}
}
};
let span: i64 = max(relativeEnd - relativeStart, 0);
let global = self.global.root();
match self.bytes {
None => Blob::new(global.r(), None, &relativeContentType),
Some(ref vec) => {
let start = relativeStart.to_usize().unwrap();
let end = (relativeStart + span).to_usize().unwrap();
let mut bytes: Vec<u8> = Vec::new();
bytes.extend_from_slice(&vec[start..end]);
Blob::new(global.r(), Some(bytes), &relativeContentType)
}
}
let bytes = self.data.bytes.clone();
Blob::new_sliced(global.r(), bytes, start, end, &relativeContentType)
}
// https://w3c.github.io/FileAPI/#dfn-isClosed