Skip to content

Commit 9c659de

Browse files
committed
Move remaining mp4_fallible dependent code into this repo
1 parent 761cdd8 commit 9c659de

File tree

3 files changed

+129
-16
lines changed

3 files changed

+129
-16
lines changed

mp4parse/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@ byteorder = "1.2.1"
2828
bitreader = { version = "0.3.2" }
2929
hashbrown = "0.7.1"
3030
num-traits = "0.2.0"
31-
mp4parse_fallible = { version = "0.0.3", optional = true }
3231
log = "0.4"
3332
static_assertions = "1.1.0"
3433

3534
[dev-dependencies]
3635
test-assembler = "0.1.2"
3736
env_logger = "0.7.1"
37+
38+
[features]
39+
# Enable mp4parse_fallible to use fallible memory allocation rather than
40+
# panicking on OOM. Note that this is only safe within Gecko where the system
41+
# allocator has been globally overridden (see BMO 1457359).
42+
mp4parse_fallible = []

mp4parse/src/lib.rs

Lines changed: 122 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ mod fallible {
163163
#[cfg(feature = "mp4parse_fallible")]
164164
extern "C" {
165165
fn malloc(bytes: usize) -> *mut u8;
166+
fn realloc(ptr: *mut u8, bytes: usize) -> *mut u8;
166167
}
167168

168169
#[cfg(feature = "mp4parse_fallible")]
169-
use mp4parse_fallible::FallibleVec;
170170
use std::cmp::PartialEq;
171171
use std::convert::TryInto as _;
172172
use std::hash::Hash;
@@ -236,20 +236,21 @@ mod fallible {
236236
#[cfg(feature = "mp4parse_fallible")]
237237
{
238238
let size = std::mem::size_of::<T>();
239-
unsafe {
240-
let new_ptr = malloc(size) as *mut T;
241-
if new_ptr.is_null() {
242-
return Err(super::Error::OutOfMemory);
243-
}
239+
let new_ptr = unsafe { malloc(size) as *mut T };
244240

245-
// If we did a simple assignment: *new_ptr = x, then the
246-
// value pointed to by new_ptr would immediately be
247-
// dropped, but that would cause an invalid memory access.
248-
// Instead, we use replace() to avoid the immediate drop
249-
// and forget() to ensure that drop never happens.
250-
std::mem::forget(std::mem::replace(&mut *new_ptr, x));
251-
inner = Box::from_raw(new_ptr);
241+
if new_ptr.is_null() {
242+
return Err(super::Error::OutOfMemory);
252243
}
244+
245+
// If we did a simple assignment: *new_ptr = x, then the value
246+
// pointed to by new_ptr would immediately be dropped, but
247+
// that would cause an invalid memory access. Instead, we use
248+
// replace() to avoid the immediate drop and forget() to
249+
// ensure that drop never happens.
250+
inner = unsafe {
251+
std::mem::forget(std::mem::replace(&mut *new_ptr, x));
252+
Box::from_raw(new_ptr)
253+
};
253254
}
254255
#[cfg(not(feature = "mp4parse_fallible"))]
255256
{
@@ -393,8 +394,24 @@ mod fallible {
393394
fn reserve(&mut self, additional: usize) -> Result<()> {
394395
#[cfg(feature = "mp4parse_fallible")]
395396
{
396-
FallibleVec::try_reserve(&mut self.inner, additional)
397-
.map_err(|_| super::Error::OutOfMemory)
397+
let available = self
398+
.inner
399+
.capacity()
400+
.checked_sub(self.inner.len())
401+
.expect("capacity >= len");
402+
if additional > available {
403+
let increase = additional
404+
.checked_sub(available)
405+
.expect("additional > available");
406+
let new_cap = self
407+
.inner
408+
.capacity()
409+
.checked_add(increase)
410+
.ok_or(super::Error::OutOfMemory)?;
411+
self.try_extend(new_cap)?;
412+
debug_assert!(self.inner.capacity() == new_cap);
413+
}
414+
Ok(())
398415
}
399416
#[cfg(not(feature = "mp4parse_fallible"))]
400417
{
@@ -411,6 +428,39 @@ mod fallible {
411428
self.inner.resize_with(new_len, f);
412429
Ok(())
413430
}
431+
432+
#[cfg(feature = "mp4parse_fallible")]
433+
fn try_extend(&mut self, new_cap: usize) -> Result<()> {
434+
let old_ptr = self.as_mut_ptr();
435+
let old_len = self.inner.len();
436+
437+
let old_cap: usize = self.inner.capacity();
438+
439+
if old_cap >= new_cap {
440+
return Ok(());
441+
}
442+
443+
let new_size_bytes = new_cap
444+
.checked_mul(std::mem::size_of::<T>())
445+
.ok_or(super::Error::OutOfMemory)?;
446+
447+
let new_ptr = unsafe {
448+
if old_cap == 0 {
449+
malloc(new_size_bytes)
450+
} else {
451+
realloc(old_ptr as *mut u8, new_size_bytes)
452+
}
453+
};
454+
455+
if new_ptr.is_null() {
456+
return Err(super::Error::OutOfMemory);
457+
}
458+
459+
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap) };
460+
461+
std::mem::forget(std::mem::replace(&mut self.inner, new_vec));
462+
Ok(())
463+
}
414464
}
415465

416466
impl<T: Clone> TryVec<TryVec<T>> {
@@ -432,6 +482,53 @@ mod fallible {
432482
}
433483
}
434484

485+
#[test]
486+
#[cfg(feature = "mp4parse_fallible")]
487+
fn oom() {
488+
let mut vec: TryVec<char> = TryVec::new();
489+
match vec.reserve(std::usize::MAX) {
490+
Ok(_) => panic!("it should be OOM"),
491+
_ => (),
492+
}
493+
}
494+
495+
#[test]
496+
fn try_reserve() {
497+
let mut vec: TryVec<_> = vec![1].into();
498+
let old_cap = vec.inner.capacity();
499+
let new_cap = old_cap + 1;
500+
vec.reserve(new_cap).unwrap();
501+
assert!(vec.inner.capacity() >= new_cap);
502+
}
503+
504+
#[test]
505+
fn try_reserve_idempotent() {
506+
let mut vec: TryVec<_> = vec![1].into();
507+
let old_cap = vec.inner.capacity();
508+
let new_cap = old_cap + 1;
509+
vec.reserve(new_cap).unwrap();
510+
let cap_after_reserve = vec.inner.capacity();
511+
vec.reserve(new_cap).unwrap();
512+
assert_eq!(cap_after_reserve, vec.inner.capacity());
513+
}
514+
515+
#[test]
516+
#[cfg(feature = "mp4parse_fallible")]
517+
fn capacity_overflow() {
518+
let mut vec: TryVec<_> = vec![1].into();
519+
match vec.reserve(std::usize::MAX) {
520+
Ok(_) => panic!("capacity calculation should overflow"),
521+
_ => (),
522+
}
523+
}
524+
525+
#[test]
526+
fn extend_from_slice() {
527+
let mut vec: TryVec<u8> = b"foo".as_ref().try_into().unwrap();
528+
vec.extend_from_slice(b"bar").unwrap();
529+
assert_eq!(vec, b"foobar".as_ref());
530+
}
531+
435532
impl<T> IntoIterator for TryVec<T> {
436533
type Item = T;
437534
type IntoIter = std::vec::IntoIter<T>;
@@ -491,6 +588,16 @@ mod fallible {
491588
}
492589
}
493590

591+
impl<T: Clone> std::convert::TryFrom<&[T]> for TryVec<T> {
592+
type Error = super::Error;
593+
594+
fn try_from(value: &[T]) -> Result<Self> {
595+
let mut v = Self::new();
596+
v.extend_from_slice(value)?;
597+
Ok(v)
598+
}
599+
}
600+
494601
impl std::convert::TryFrom<&str> for TryVec<u8> {
495602
type Error = super::Error;
496603

mp4parse_capi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ authors = [
55
"Ralph Giles <[email protected]>",
66
"Matthew Gregan <[email protected]>",
77
"Alfredo Yang <[email protected]>",
8+
"Jon Bauman <[email protected]>",
89
]
910

1011
description = "Parser for ISO base media file format (mp4)"

0 commit comments

Comments
 (0)