Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "varinteger"
version = "1.0.7-alpha.0"
version = "2.0.0"
description = "Rust module for encoding/decoding varints that doesn't do any IO. Inspired by the Node.js varint module"
authors = ["Mathias Buus <[email protected]>"]
repository = "https://github.com/mafintosh/varinteger-rs"
authors = ["James Halliday", "Karissa McKelvey <[email protected]>", "Mathias Buus <[email protected]>"]
repository = "https://github.com/datrs/varinteger"
keywords = ["protobuf", "varint", "variable", "integer"]
readme = "README.md"
license = "MIT"
license = "MIT"
102 changes: 57 additions & 45 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,68 +1,83 @@
#![cfg_attr(feature = "nightly", deny(missing_docs))]
#![cfg_attr(feature = "nightly", feature(external_doc))]
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
#![cfg_attr(test, deny(warnings))]
use std::fmt;

/// Returns how many bytes are needed to encode a value.
#[inline]
pub fn length(value: u64) -> usize {
let zero_len = 64 - value.leading_zeros();
let offset = if zero_len == 0 { 7 } else { 6 };
((offset + zero_len) / 7) as usize
type Result<T> = std::result::Result<T, EncodeError>;

#[derive(Debug, Clone)]
pub struct EncodeError;

impl fmt::Display for EncodeError{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "buffer is too small to write varint")
}
}

/// Encode a `u64` integer to the byte slice. Returns how many bytes were
/// encoded.
#[inline]
pub fn encode(value: u64, buf: &mut [u8]) -> usize {
pub fn encode(value: u64, buf: &mut [u8]) -> Result<usize> {
encode_with_offset(value, buf, 0)
}

/// Encode a `u64` integer at a specific offset in the byte slice. Returns how
/// many bytes were encoded.
#[inline]
pub fn encode_with_offset(value: u64, buf: &mut [u8], offset: usize) -> usize {
pub fn encode_with_offset(
value: u64,
buf: &mut [u8],
offset: usize,
) -> Result<usize> {
let len = length(value);
if buf.len() < len {
return Err(EncodeError)
}
let mut v = value;
let mut off = offset;
let mut val = value;

while val > 127 {
buf[off] = (val as u8) | 128;
while v > 127 {
buf[off] = (v as u8) | 128;
off += 1;
val >>= 7;
v >>= 7;
}
buf[off] = val as u8;

off + 1 - offset
buf[off] = v as u8;
Ok(len)
}

/// Decode a byte slice into a `u64` integer. Returns how many bytes were
/// decoded.
#[inline]
pub fn decode(buf: &[u8], value: &mut u64) -> usize {
decode_with_offset(buf, 0, value)
pub fn decode(buf: &[u8]) -> Result<(usize, u64)> {
decode_with_offset(buf, 0usize)
}

/// Decode a byte slice into a `u64` integer at a specific offset. Returns how
/// many bytes were decoded.
#[inline]
pub fn decode_with_offset(buf: &[u8], offset: usize, value: &mut u64) -> usize {
let mut val = 0 as u64;
let mut fac = 1 as u64;
let mut off = offset;

loop {
let byte = buf[off];
off += 1;
val += fac * u64::from(byte & 127);
fac <<= 7;
pub fn decode_with_offset(
buf: &[u8],
_offset: usize,
) -> Result<(usize, u64)> {
let mut value = 0u64;
let mut m = 1u64;
let mut offset = _offset;
for _i in 0..8 {
if offset >= buf.len() {
return Err(EncodeError)
}
let byte = buf[offset];
offset += 1;
value += m * u64::from(byte & 127);
m *= 128;
if byte & 128 == 0 {
break;
}
}
Ok((offset, value))
}

*value = val;

off - offset
/// Returns how many bytes are needed to encode a value.
#[inline]
pub fn length(value: u64) -> usize {
let msb = (64 - value.leading_zeros()) as usize;
(msb.max(1) + 6) / 7
}

/// Returns how many bytes are needed to encode a value.
Expand All @@ -74,7 +89,7 @@ pub fn signed_length(value: i64) -> usize {
/// Encode a `i64` (signed) integer at a specific offset in the byte slice.
/// Returns how many bytes were encoded.
#[inline]
pub fn signed_encode(value: i64, buf: &mut [u8]) -> usize {
pub fn signed_encode(value: i64, buf: &mut [u8]) -> Result<usize> {
encode_with_offset(unsign(value), buf, 0)
}

Expand All @@ -85,15 +100,15 @@ pub fn signed_encode_with_offset(
value: i64,
buf: &mut [u8],
offset: usize,
) -> usize {
) -> Result<usize> {
encode_with_offset(unsign(value), buf, offset)
}

/// Decode a byte slice into a `i64` (signed) integer. Returns how many bytes
/// were decoded.
#[inline]
pub fn signed_decode(buf: &[u8], value: &mut i64) -> usize {
signed_decode_with_offset(buf, 0, value)
pub fn signed_decode(buf: &[u8]) -> Result<(usize, i64)> {
signed_decode_with_offset(buf, 0)
}

/// Decode a byte slice into a `i64` (signed) integer at a specific offset.
Expand All @@ -102,12 +117,9 @@ pub fn signed_decode(buf: &[u8], value: &mut i64) -> usize {
pub fn signed_decode_with_offset(
buf: &[u8],
offset: usize,
value: &mut i64,
) -> usize {
let mut val = 0;
let off = decode_with_offset(buf, offset, &mut val);
*value = sign(val);
off
) -> Result<(usize, i64)> {
let (off, value) = decode_with_offset(buf, offset)?;
Ok((off, sign(value)))
}

/// Convert an `i64` into a `u64`.
Expand Down
29 changes: 14 additions & 15 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ use varinteger::{
#[test]
fn test_encode() {
let mut buf = [0; 512];
assert_eq!(encode(100, &mut buf), 1);
assert_eq!(encode(100, &mut buf).unwrap(), 1);
assert_eq!(buf[0], 100);

assert_eq!(encode(1000, &mut buf), 2);
assert_eq!(encode(1000, &mut buf).unwrap(), 2);
assert_eq!(buf[0], 232);
assert_eq!(buf[1], 7);
}

#[test]
fn test_decode() {
let mut value = 0 as u64;
assert_eq!(decode(&[100], &mut value), 1);
assert_eq!(value, 100);
fn test_encode_failure() {
let mut buf = [0; 1];
assert!(encode(1000, &mut buf).is_err());
}

assert_eq!(decode(&[232, 7], &mut value), 2);
assert_eq!(value, 1000);
#[test]
fn test_decode() {
assert_eq!(decode(&[100]).unwrap(), (1, 100));
assert_eq!(decode(&[232, 7]).unwrap(), (2, 1000));
}

#[test]
Expand All @@ -42,23 +44,20 @@ fn test_length() {
#[test]
fn test_signed_encode() {
let mut buf = [0; 512];
assert_eq!(signed_encode(100, &mut buf), 2);
assert_eq!(signed_encode(100, &mut buf).unwrap(), 2);
assert_eq!(buf[0], 200);
assert_eq!(buf[1], 1);

assert_eq!(signed_encode(-100, &mut buf), 2);
assert_eq!(signed_encode(-100, &mut buf).unwrap(), 2);
assert_eq!(buf[0], 199);
assert_eq!(buf[1], 1);
}

#[test]
fn test_signed_decode() {
let mut value = 0 as i64;
assert_eq!(signed_decode(&[200, 1], &mut value), 2);
assert_eq!(value, 100);
assert_eq!(signed_decode(&[200, 1]).unwrap(), (2, 100));

assert_eq!(signed_decode(&[199, 1], &mut value), 2);
assert_eq!(value, -100);
assert_eq!(signed_decode(&[199, 1]).unwrap(), (2, -100));
}

#[test]
Expand Down