Skip to content

Commit

Permalink
PERF: Remove need of vector for handling optional flags #11
Browse files Browse the repository at this point in the history
  • Loading branch information
kellerkindt committed May 4, 2020
1 parent ffc5b52 commit 68d0a05
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 97 deletions.
21 changes: 17 additions & 4 deletions src/io/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,32 @@ impl BitBuffer {
}

/// Changes the write-position to the given position for the closure call.
/// Restores the original write position after the call.
/// Restores the original write-position after the call.
///
/// # Panics
/// Positions beyond the current buffer length will result in panics.
#[inline]
pub fn with_write_position_at<T, F: Fn(&mut Self) -> T>(&mut self, position: usize, f: F) -> T {
assert!(position <= self.buffer.len() * 8);
let before = self.write_position;
self.write_position = position;
debug_assert!(position <= self.buffer.len() * 8);
let before = core::mem::replace(&mut self.write_position, position);
let result = f(self);
self.write_position = before;
result
}

/// Changes the read-position to the given position for the closure call.
/// Restores the original read-position after the call.
///
/// # Panics
/// Positions beyond the current write-position will result in panics.
#[inline]
pub fn with_read_position_at<T, F: Fn(&mut Self) -> T>(&mut self, position: usize, f: F) -> T {
debug_assert!(position < self.write_position);
let before = core::mem::replace(&mut self.read_position, position);
let result = f(self);
self.read_position = before;
result
}
}

fn bit_string_copy(
Expand Down
2 changes: 2 additions & 0 deletions src/io/uper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum Error {
InvalidChoiceIndex(usize, usize),
ValueNotInRange(i64, i64, i64),
SizeNotInRange(usize, usize, usize),
OptFlagsExhausted,
EndOfStream,
}

Expand Down Expand Up @@ -51,6 +52,7 @@ impl std::fmt::Display for Error {
"The size {} is not within the inclusive range of {} and {}",
size, min, max
),
Error::OptFlagsExhausted => write!(f, "All optional flags have already been exhausted"),
Error::EndOfStream => write!(
f,
"Can no longer read or write any bytes from the underlying dataset"
Expand Down
157 changes: 64 additions & 93 deletions src/syn/io/uper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,12 @@ use crate::io::uper::Error as UperError;
use crate::io::uper::Reader as _UperReader;
use crate::io::uper::Writer as _UperWriter;
use crate::prelude::*;

pub struct ScopeStack<T> {
scopes: Vec<Vec<T>>,
scope: Vec<T>,
}

impl<T> Default for ScopeStack<T> {
fn default() -> Self {
ScopeStack {
scopes: Vec::with_capacity(16),
scope: Vec::default(),
}
}
}

impl<T> ScopeStack<T> {
#[inline]
pub fn current_mut(&mut self) -> &mut Vec<T> {
&mut self.scope
}

#[inline]
pub fn stash(&mut self) {
self.push(Vec::default())
}

#[inline]
pub fn push(&mut self, mut scope: Vec<T>) {
std::mem::swap(&mut scope, &mut self.scope);
self.scopes.push(scope);
}

#[inline]
pub fn pop(&mut self) -> Vec<T> {
let mut scope = self.scopes.pop().unwrap_or_default();
std::mem::swap(&mut scope, &mut self.scope);
scope
}
}
use std::ops::Range;

#[derive(Default)]
pub struct UperWriter {
buffer: BitBuffer,
scope: ScopeStack<usize>,
scope: Option<Range<usize>>,
}

impl UperWriter {
Expand All @@ -69,21 +31,20 @@ impl UperWriter {
}

#[inline]
pub fn scope_pushed<R, F: Fn(&mut Self) -> R>(
&mut self,
scope: Vec<usize>,
f: F,
) -> (R, Vec<usize>) {
self.scope.push(scope);
pub fn scope_pushed<R, F: Fn(&mut Self) -> R>(&mut self, scope: Range<usize>, f: F) -> R {
let original = core::mem::replace(&mut self.scope, Some(scope));
let result = f(self);
(result, self.scope.pop())
let scope = core::mem::replace(&mut self.scope, original);
let scope = scope.unwrap(); // save because this is the original from above
debug_assert_eq!(scope.start, scope.end);
result
}

#[inline]
pub fn scope_stashed<R, F: Fn(&mut Self) -> R>(&mut self, f: F) -> R {
self.scope.stash();
let scope = self.scope.take();
let result = f(self);
self.scope.pop();
self.scope = scope;
result
}
}
Expand All @@ -96,24 +57,21 @@ impl Writer for UperWriter {
&mut self,
f: F,
) -> Result<(), Self::Error> {
// In UPER the optional flag for all OPTIONAL values are written before any field
// value is written. This reserves the bits, so that on a later call of `write_opt`
// the value can be set to the actual state.
let mut list = Vec::default();
// In UPER the values for all OPTIONAL flags are written before any field
// value is written. This remembers their position, so a later call of `write_opt`
// can write them to the buffer
let write_pos = self.buffer.write_position;
for i in (0..C::OPTIONAL_FIELDS).rev() {
let range = write_pos..write_pos + C::OPTIONAL_FIELDS; // TODO
for _ in 0..C::OPTIONAL_FIELDS {
// insert in reverse order so that a simple pop() in `write_opt` retrieves
// the relevant position
list.push(write_pos + i);
if let Err(e) = self.buffer.write_bit(false) {
self.buffer.write_position = write_pos; // undo write_bits
return Err(e);
}
}
let (result, scope) = self.scope_pushed(list, f);
result?; // first error on this before throwing non-informative assert errors
debug_assert!(scope.is_empty());
Ok(())

self.scope_pushed(range, f)
}

#[inline]
Expand Down Expand Up @@ -176,9 +134,16 @@ impl Writer for UperWriter {
&mut self,
value: Option<&<T as WritableType>::Type>,
) -> Result<(), Self::Error> {
if let Some(position) = self.scope.current_mut().pop() {
self.buffer
.with_write_position_at(position, |buffer| buffer.write_bit(value.is_some()))?;
if let Some(range) = &mut self.scope {
if range.start < range.end {
let result = self
.buffer
.with_write_position_at(range.start, |b| b.write_bit(value.is_some()));
range.start += 1;
result?;
} else {
return Err(UperError::OptFlagsExhausted);
}
} else {
self.buffer.write_bit(value.is_some())?;
}
Expand Down Expand Up @@ -224,7 +189,7 @@ impl Writer for UperWriter {

pub struct UperReader {
buffer: BitBuffer,
scope: ScopeStack<bool>,
scope: Option<Range<usize>>,
}

impl UperReader {
Expand All @@ -235,26 +200,25 @@ impl UperReader {
}
}

pub fn bits_remaining(&self) -> usize {
#[inline]
pub const fn bits_remaining(&self) -> usize {
self.buffer.write_position - self.buffer.read_position
}

#[inline]
pub fn scope_pushed<R, F: Fn(&mut Self) -> R>(
&mut self,
scope: Vec<bool>,
f: F,
) -> (R, Vec<bool>) {
self.scope.push(scope);
pub fn scope_pushed<R, F: Fn(&mut Self) -> R>(&mut self, scope: Range<usize>, f: F) -> R {
let original = core::mem::replace(&mut self.scope, Some(scope));
let result = f(self);
(result, self.scope.pop())
let scope = core::mem::replace(&mut self.scope, original);
debug_assert_eq!(scope.clone().unwrap().start, scope.unwrap().end); // save because this is the original from above
result
}

#[inline]
pub fn scope_stashed<R, F: Fn(&mut Self) -> R>(&mut self, f: F) -> R {
self.scope.stash();
let scope = self.scope.take();
let result = f(self);
self.scope.pop();
self.scope = scope;
result
}
}
Expand All @@ -271,17 +235,15 @@ impl Reader for UperReader {
&mut self,
f: F,
) -> Result<S, Self::Error> {
// In UPER the optional flag for all OPTIONAL values are written before any field
// value is written. This loads those bits, so that on a later call of `read_opt` can
// retrieve them by a simple call of `pop` on the optionals buffer
let mut optionals = vec![false; C::OPTIONAL_FIELDS];
for i in (0..C::OPTIONAL_FIELDS).rev() {
optionals[i] = self.buffer.read_bit()?;
// In UPER the values for all OPTIONAL flags are written before any field
// value is written. This remembers their position, so a later call of `read_opt`
// can retrieve them from the buffer
let range = self.buffer.read_position..self.buffer.read_position + C::OPTIONAL_FIELDS;
if self.buffer.bit_len() < range.end {
return Err(UperError::EndOfStream);
}
let (result, scope) = self.scope_pushed(optionals, f);
let result = result?; // first error on this before throwing non-informative assert errors
debug_assert!(scope.is_empty());
Ok(result)
self.buffer.read_position = range.end; // skip optional
self.scope_pushed(range, f)
}

#[inline]
Expand All @@ -292,15 +254,16 @@ impl Reader for UperReader {
let max = C::MAX.unwrap_or(std::usize::MAX);
let len = self.buffer.read_length_determinant()? + min; // TODO untested for MIN != 0
if len > max {
return Err(UperError::SizeNotInRange(len, min, max));
Err(UperError::SizeNotInRange(len, min, max))
} else {
self.scope_stashed(|w| {
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(T::read_value(w)?);
}
Ok(vec)
})
}
self.scope_stashed(|w| {
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(T::read_value(w)?);
}
Ok(vec)
})
}

#[inline]
Expand Down Expand Up @@ -343,8 +306,16 @@ impl Reader for UperReader {
fn read_opt<T: ReadableType>(
&mut self,
) -> Result<Option<<T as ReadableType>::Type>, Self::Error> {
let value = if let Some(pre_fetched) = self.scope.current_mut().pop() {
pre_fetched
let value = if let Some(range) = &mut self.scope {
if range.start < range.end {
let result = self
.buffer
.with_read_position_at(range.start, |b| b.read_bit());
range.start += 1;
result?
} else {
return Err(UperError::OptFlagsExhausted);
}
} else {
self.buffer.read_bit()?
};
Expand Down
2 changes: 2 additions & 0 deletions src/syn/numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ read_write!(u8, u16, u32);
impl<C: Constraint<u64>> WritableType for Integer<u64, C> {
type Type = u64;

#[inline]
fn write_value<W: Writer>(
writer: &mut W,
value: &Self::Type,
Expand All @@ -98,6 +99,7 @@ impl<C: Constraint<u64>> WritableType for Integer<u64, C> {
impl<C: Constraint<u64>> ReadableType for Integer<u64, C> {
type Type = u64;

#[inline]
fn read_value<R: Reader>(reader: &mut R) -> Result<Self::Type, <R as Reader>::Error> {
if C::MIN.is_none() && C::MAX.is_none() {
Ok(reader.read_int_max()?)
Expand Down

0 comments on commit 68d0a05

Please sign in to comment.