235 lines
6.7 KiB
Rust
235 lines
6.7 KiB
Rust
/*
|
|
Taken from the `leb128` crate, which is Apache2 / MIT dual licensed
|
|
|
|
Copyright (c) 2015 The Rust Project Developers
|
|
|
|
Permission is hereby granted, free of charge, to any
|
|
person obtaining a copy of this software and associated
|
|
documentation files (the "Software"), to deal in the
|
|
Software without restriction, including without
|
|
limitation the rights to use, copy, modify, merge,
|
|
publish, distribute, sublicense, and/or sell copies of
|
|
the Software, and to permit persons to whom the Software
|
|
is furnished to do so, subject to the following
|
|
conditions:
|
|
|
|
The above copyright notice and this permission notice
|
|
shall be included in all copies or substantial portions
|
|
of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
We have our own version because it's important to know how many bytes were read;
|
|
|
|
TODO: If somehow this is hot, theirs a branchless SIMD version, I read about on
|
|
wikipedia
|
|
*/
|
|
|
|
pub const CONTINUATION_BIT: u8 = 1 << 7;
|
|
pub const SIGN_BIT: u8 = 1 << 6;
|
|
|
|
pub fn low_bits_of_byte(byte: u8) -> u8 {
|
|
byte & !CONTINUATION_BIT
|
|
}
|
|
|
|
pub fn low_bits_of_u64(val: u64) -> u8 {
|
|
let byte = val & (std::u8::MAX as u64);
|
|
low_bits_of_byte(byte as u8)
|
|
}
|
|
|
|
/// A module for reading LEB128-encoded signed and unsigned integers.
|
|
pub mod read {
|
|
use super::{low_bits_of_byte, CONTINUATION_BIT, SIGN_BIT};
|
|
use std::fmt;
|
|
use std::io;
|
|
|
|
/// An error type for reading LEB128-encoded values.
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
/// There was an underlying IO error.
|
|
IoError(io::Error),
|
|
/// The number being read is larger than can be represented.
|
|
Overflow,
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(e: io::Error) -> Self {
|
|
Error::IoError(e)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
match *self {
|
|
Error::IoError(ref e) => e.fmt(f),
|
|
Error::Overflow => {
|
|
write!(f, "The number being read is larger than can be represented")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
match *self {
|
|
Error::IoError(ref e) => Some(e),
|
|
Error::Overflow => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Read an unsigned LEB128-encoded number from the `std::io::Read` stream
|
|
/// `r`.
|
|
///
|
|
/// On success, return the number, and the number of bytes read.
|
|
pub fn unsigned<R>(r: &mut R) -> Result<(u64, usize), Error>
|
|
where
|
|
R: ?Sized + io::Read,
|
|
{
|
|
let mut result = 0;
|
|
let mut shift = 0;
|
|
let mut read = 0;
|
|
|
|
loop {
|
|
let mut buf = [0];
|
|
r.read_exact(&mut buf)?;
|
|
read += 1;
|
|
|
|
if shift == 63 && buf[0] != 0x00 && buf[0] != 0x01 {
|
|
while buf[0] & CONTINUATION_BIT != 0 {
|
|
r.read_exact(&mut buf)?;
|
|
read += 1;
|
|
}
|
|
return Err(Error::Overflow);
|
|
}
|
|
|
|
let low_bits = low_bits_of_byte(buf[0]) as u64;
|
|
result |= low_bits << shift;
|
|
|
|
if buf[0] & CONTINUATION_BIT == 0 {
|
|
return Ok((result, read));
|
|
}
|
|
|
|
shift += 7;
|
|
}
|
|
}
|
|
|
|
/// Read a signed LEB128-encoded number from the `std::io::Read` stream `r`.
|
|
///
|
|
/// On success, return the number and the number of bytes read.
|
|
pub fn signed<R>(r: &mut R) -> Result<(i64, usize), Error>
|
|
where
|
|
R: ?Sized + io::Read,
|
|
{
|
|
let mut result = 0;
|
|
let mut shift = 0;
|
|
let size = 64;
|
|
let mut byte;
|
|
let mut read = 0;
|
|
|
|
loop {
|
|
let mut buf = [0];
|
|
r.read_exact(&mut buf)?;
|
|
read += 1;
|
|
|
|
byte = buf[0];
|
|
if shift == 63 && byte != 0x00 && byte != 0x7f {
|
|
while buf[0] & CONTINUATION_BIT != 0 {
|
|
r.read_exact(&mut buf)?;
|
|
read += 1;
|
|
}
|
|
return Err(Error::Overflow);
|
|
}
|
|
|
|
let low_bits = low_bits_of_byte(byte) as i64;
|
|
result |= low_bits << shift;
|
|
shift += 7;
|
|
|
|
if byte & CONTINUATION_BIT == 0 {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if shift < size && (SIGN_BIT & byte) == SIGN_BIT {
|
|
// Sign extend the result.
|
|
result |= !0 << shift;
|
|
}
|
|
|
|
Ok((result, read))
|
|
}
|
|
}
|
|
|
|
/// A module for writing LEB128-encoded signed and unsigned integers.
|
|
pub mod write {
|
|
use super::{low_bits_of_u64, CONTINUATION_BIT};
|
|
use std::io;
|
|
|
|
/// Write `val` to the `std::io::Write` stream `w` as an unsigned LEB128 value.
|
|
///
|
|
/// On success, return the number of bytes written to `w`.
|
|
pub fn unsigned<W>(w: &mut W, mut val: u64) -> Result<usize, io::Error>
|
|
where
|
|
W: ?Sized + io::Write,
|
|
{
|
|
let mut bytes_written = 0;
|
|
loop {
|
|
let mut byte = low_bits_of_u64(val);
|
|
val >>= 7;
|
|
if val != 0 {
|
|
// More bytes to come, so set the continuation bit.
|
|
byte |= CONTINUATION_BIT;
|
|
}
|
|
|
|
let buf = [byte];
|
|
w.write_all(&buf)?;
|
|
bytes_written += 1;
|
|
|
|
if val == 0 {
|
|
return Ok(bytes_written);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Write `val` to the `std::io::Write` stream `w` as a signed LEB128 value.
|
|
///
|
|
/// On success, return the number of bytes written to `w`.
|
|
pub fn signed<W>(w: &mut W, mut val: i64) -> Result<usize, io::Error>
|
|
where
|
|
W: ?Sized + io::Write,
|
|
{
|
|
let mut bytes_written = 0;
|
|
loop {
|
|
let mut byte = val as u8;
|
|
// Keep the sign bit for testing
|
|
val >>= 6;
|
|
let done = val == 0 || val == -1;
|
|
if done {
|
|
byte &= !CONTINUATION_BIT;
|
|
} else {
|
|
// Remove the sign bit
|
|
val >>= 1;
|
|
// More bytes to come, so set the continuation bit.
|
|
byte |= CONTINUATION_BIT;
|
|
}
|
|
|
|
let buf = [byte];
|
|
w.write_all(&buf)?;
|
|
bytes_written += 1;
|
|
|
|
if done {
|
|
return Ok(bytes_written);
|
|
}
|
|
}
|
|
}
|
|
}
|