Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Change encode/decode signatures to improve error handling #6

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[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"

[dependencies]
failure = "0.1.8"
okdistribute marked this conversation as resolved.
Show resolved Hide resolved
95 changes: 49 additions & 46 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,68 +1,73 @@
#![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))]

/// 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
}
extern crate failure;
use failure::{bail, Error};

/// 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, Error> {
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, Error> {
let len = length(value);
if buf.len() < len {
bail!["buffer is too small to write varint"]
okdistribute marked this conversation as resolved.
Show resolved Hide resolved
}
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), Error> {
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,
okdistribute marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<(usize, u64), Error> {
let mut value = 0u64;
let mut m = 1u64;
let mut offset = _offset;
for _i in 0..8 {
if offset >= buf.len() {
bail!["buffer supplied to varint decoding too small"]
}
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 +79,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, Error> {
encode_with_offset(unsign(value), buf, 0)
}

Expand All @@ -85,15 +90,15 @@ pub fn signed_encode_with_offset(
value: i64,
buf: &mut [u8],
offset: usize,
) -> usize {
) -> Result<usize, Error> {
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), Error> {
signed_decode_with_offset(buf, 0)
}

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

/// Convert an `i64` into a `u64`.
Expand Down
31 changes: 16 additions & 15 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
extern crate failure;
extern crate varinteger;
use failure::Error;

use varinteger::{
decode, encode, length, signed_decode, signed_encode, signed_length,
Expand All @@ -7,22 +9,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 +46,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