1
Fork 0

Use StableHasher everywhere

The standard implementations of Hasher have architecture-dependent
results when hashing integers. This causes problems when the hashes are
stored within metadata - metadata written by one host architecture can't
be read by another.

To fix that, implement an architecture-independent StableHasher and use
it in all places an architecture-independent hasher is needed.

Fixes #38177.
This commit is contained in:
Ariel Ben-Yehuda 2016-12-14 01:45:03 +02:00
parent 01d53df82e
commit e1d4b8fc8c
14 changed files with 270 additions and 230 deletions

View file

@ -10,9 +10,9 @@
use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::StableHasher;
use std::fmt::Write;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use syntax::ast;
use syntax::symbol::{Symbol, InternedString};
use ty::TyCtxt;
@ -131,7 +131,8 @@ impl DefPath {
}
pub fn deterministic_hash(&self, tcx: TyCtxt) -> u64 {
let mut state = DefaultHasher::new();
debug!("deterministic_hash({:?})", self);
let mut state = StableHasher::new();
self.deterministic_hash_to(tcx, &mut state);
state.finish()
}
@ -377,4 +378,3 @@ impl DefPathData {
self.as_interned_str().to_string()
}
}

View file

@ -24,11 +24,11 @@ use util::nodemap::FxHashMap;
use middle::lang_items;
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult};
use std::cell::RefCell;
use std::cmp;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::intrinsics;
use syntax::ast::{self, Name};
use syntax::attr::{self, SignedInt, UnsignedInt};
@ -349,7 +349,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// Creates a hash of the type `Ty` which will be the same no matter what crate
/// context it's calculated within. This is used by the `type_id` intrinsic.
pub fn type_id_hash(self, ty: Ty<'tcx>) -> u64 {
let mut hasher = TypeIdHasher::new(self, DefaultHasher::default());
let mut hasher = TypeIdHasher::new(self);
hasher.visit_ty(ty);
hasher.finish()
}
@ -395,96 +395,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
}
/// When hashing a type this ends up affecting properties like symbol names. We
/// want these symbol names to be calculated independent of other factors like
/// what architecture you're compiling *from*.
///
/// The hashing just uses the standard `Hash` trait, but the implementations of
/// `Hash` for the `usize` and `isize` types are *not* architecture independent
/// (e.g. they has 4 or 8 bytes). As a result we want to avoid `usize` and
/// `isize` completely when hashing. To ensure that these don't leak in we use a
/// custom hasher implementation here which inflates the size of these to a `u64`
/// and `i64`.
///
/// The same goes for endianess: We always convert multi-byte integers to little
/// endian before hashing.
#[derive(Debug)]
pub struct ArchIndependentHasher<H> {
inner: H,
}
impl<H> ArchIndependentHasher<H> {
pub fn new(inner: H) -> ArchIndependentHasher<H> {
ArchIndependentHasher { inner: inner }
}
pub fn into_inner(self) -> H {
self.inner
}
}
impl<H: Hasher> Hasher for ArchIndependentHasher<H> {
fn write(&mut self, bytes: &[u8]) {
self.inner.write(bytes)
}
fn finish(&self) -> u64 {
self.inner.finish()
}
fn write_u8(&mut self, i: u8) {
self.inner.write_u8(i)
}
fn write_u16(&mut self, i: u16) {
self.inner.write_u16(i.to_le())
}
fn write_u32(&mut self, i: u32) {
self.inner.write_u32(i.to_le())
}
fn write_u64(&mut self, i: u64) {
self.inner.write_u64(i.to_le())
}
fn write_usize(&mut self, i: usize) {
self.inner.write_u64((i as u64).to_le())
}
fn write_i8(&mut self, i: i8) {
self.inner.write_i8(i)
}
fn write_i16(&mut self, i: i16) {
self.inner.write_i16(i.to_le())
}
fn write_i32(&mut self, i: i32) {
self.inner.write_i32(i.to_le())
}
fn write_i64(&mut self, i: i64) {
self.inner.write_i64(i.to_le())
}
fn write_isize(&mut self, i: isize) {
self.inner.write_i64((i as i64).to_le())
}
}
pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, H> {
pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
state: ArchIndependentHasher<H>,
state: StableHasher<W>,
}
impl<'a, 'gcx, 'tcx, H: Hasher> TypeIdHasher<'a, 'gcx, 'tcx, H> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, state: H) -> Self {
TypeIdHasher {
tcx: tcx,
state: ArchIndependentHasher::new(state),
}
impl<'a, 'gcx, 'tcx, W> TypeIdHasher<'a, 'gcx, 'tcx, W>
where W: StableHasherResult
{
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Self {
TypeIdHasher { tcx: tcx, state: StableHasher::new() }
}
pub fn finish(self) -> W {
self.state.finish()
}
pub fn hash<T: Hash>(&mut self, x: T) {
x.hash(&mut self.state);
}
pub fn finish(self) -> u64 {
self.state.finish()
}
fn hash_discriminant_u8<T>(&mut self, x: &T) {
let v = unsafe {
intrinsics::discriminant_value(x)
@ -504,13 +434,11 @@ impl<'a, 'gcx, 'tcx, H: Hasher> TypeIdHasher<'a, 'gcx, 'tcx, H> {
pub fn def_path(&mut self, def_path: &ast_map::DefPath) {
def_path.deterministic_hash_to(self.tcx, &mut self.state);
}
pub fn into_inner(self) -> H {
self.state.inner
}
}
impl<'a, 'gcx, 'tcx, H: Hasher> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, H> {
impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W>
where W: StableHasherResult
{
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
// Distinguish between the Ty variants uniformly.
self.hash_discriminant_u8(&ty.sty);

View file

@ -44,6 +44,8 @@ extern crate serialize as rustc_serialize; // used by deriving
#[cfg(unix)]
extern crate libc;
pub use rustc_serialize::hex::ToHex;
pub mod array_vec;
pub mod accumulate_vec;
pub mod small_vec;
@ -59,6 +61,7 @@ pub mod indexed_vec;
pub mod obligation_forest;
pub mod snapshot_map;
pub mod snapshot_vec;
pub mod stable_hasher;
pub mod transitive_relation;
pub mod unify;
pub mod fnv;

View file

@ -0,0 +1,176 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::hash::Hasher;
use std::marker::PhantomData;
use std::mem;
use blake2b::Blake2bHasher;
use rustc_serialize::leb128;
fn write_unsigned_leb128_to_buf(buf: &mut [u8; 16], value: u64) -> usize {
leb128::write_unsigned_leb128_to(value, |i, v| buf[i] = v)
}
fn write_signed_leb128_to_buf(buf: &mut [u8; 16], value: i64) -> usize {
leb128::write_signed_leb128_to(value, |i, v| buf[i] = v)
}
/// When hashing something that ends up affecting properties like symbol names. We
/// want these symbol names to be calculated independent of other factors like
/// what architecture you're compiling *from*.
///
/// The hashing just uses the standard `Hash` trait, but the implementations of
/// `Hash` for the `usize` and `isize` types are *not* architecture independent
/// (e.g. they has 4 or 8 bytes). As a result we want to avoid `usize` and
/// `isize` completely when hashing.
///
/// To do that, we encode all integers to be hashed with some
/// arch-independent encoding.
///
/// At the moment, we pass i8/u8 straight through and encode
/// all other integers using leb128.
///
/// This hasher currently always uses the stable Blake2b algorithm
/// and allows for variable output lengths through its type
/// parameter.
#[derive(Debug)]
pub struct StableHasher<W> {
state: Blake2bHasher,
bytes_hashed: u64,
width: PhantomData<W>,
}
pub trait StableHasherResult: Sized {
fn finish(hasher: StableHasher<Self>) -> Self;
}
impl<W: StableHasherResult> StableHasher<W> {
pub fn new() -> Self {
StableHasher {
state: Blake2bHasher::new(mem::size_of::<W>(), &[]),
bytes_hashed: 0,
width: PhantomData,
}
}
pub fn finish(self) -> W {
W::finish(self)
}
}
impl StableHasherResult for [u8; 20] {
fn finish(mut hasher: StableHasher<Self>) -> Self {
let mut result: [u8; 20] = [0; 20];
result.copy_from_slice(hasher.state.finalize());
result
}
}
impl StableHasherResult for u64 {
fn finish(mut hasher: StableHasher<Self>) -> Self {
hasher.state.finalize();
hasher.state.finish()
}
}
impl<W> StableHasher<W> {
#[inline]
pub fn finalize(&mut self) -> &[u8] {
self.state.finalize()
}
#[inline]
pub fn bytes_hashed(&self) -> u64 {
self.bytes_hashed
}
#[inline]
fn write_uleb128(&mut self, value: u64) {
let mut buf = [0; 16];
let len = write_unsigned_leb128_to_buf(&mut buf, value);
self.state.write(&buf[..len]);
self.bytes_hashed += len as u64;
}
#[inline]
fn write_ileb128(&mut self, value: i64) {
let mut buf = [0; 16];
let len = write_signed_leb128_to_buf(&mut buf, value);
self.state.write(&buf[..len]);
self.bytes_hashed += len as u64;
}
}
// For the non-u8 integer cases we leb128 encode them first. Because small
// integers dominate, this significantly and cheaply reduces the number of
// bytes hashed, which is good because blake2b is expensive.
impl<W> Hasher for StableHasher<W> {
fn finish(&self) -> u64 {
panic!("use StableHasher::finish instead");
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.state.write(bytes);
self.bytes_hashed += bytes.len() as u64;
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.state.write_u8(i);
self.bytes_hashed += 1;
}
#[inline]
fn write_u16(&mut self, i: u16) {
self.write_uleb128(i as u64);
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.write_uleb128(i as u64);
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.write_uleb128(i);
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.write_uleb128(i as u64);
}
#[inline]
fn write_i8(&mut self, i: i8) {
self.state.write_i8(i);
self.bytes_hashed += 1;
}
#[inline]
fn write_i16(&mut self, i: i16) {
self.write_ileb128(i as i64);
}
#[inline]
fn write_i32(&mut self, i: i32) {
self.write_ileb128(i as i64);
}
#[inline]
fn write_i64(&mut self, i: i64) {
self.write_ileb128(i);
}
#[inline]
fn write_isize(&mut self, i: isize) {
self.write_ileb128(i as i64);
}
}

View file

@ -11,9 +11,7 @@
use rustc::hir;
use rustc::hir::{map as hir_map, FreevarMap, TraitMap};
use rustc::hir::lowering::lower_crate;
use rustc_data_structures::blake2b::Blake2bHasher;
use rustc_data_structures::fmt_wrap::FmtWrap;
use rustc::ty::util::ArchIndependentHasher;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_mir as mir;
use rustc::session::{Session, CompileResult, compile_result_from_err_count};
use rustc::session::config::{self, Input, OutputFilenames, OutputType,
@ -27,6 +25,7 @@ use rustc::util::common::time;
use rustc::util::nodemap::{NodeSet, NodeMap};
use rustc_borrowck as borrowck;
use rustc_incremental::{self, IncrementalHashesMap};
use rustc_incremental::ich::Fingerprint;
use rustc_resolve::{MakeGlobMap, Resolver};
use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::CStore;
@ -1274,7 +1273,7 @@ pub fn compute_crate_disambiguator(session: &Session) -> String {
// FIXME(mw): It seems that the crate_disambiguator is used everywhere as
// a hex-string instead of raw bytes. We should really use the
// smaller representation.
let mut hasher = ArchIndependentHasher::new(Blake2bHasher::new(128 / 8, &[]));
let mut hasher = StableHasher::<Fingerprint>::new();
let mut metadata = session.opts.cg.metadata.clone();
// We don't want the crate_disambiguator to dependent on the order
@ -1292,14 +1291,11 @@ pub fn compute_crate_disambiguator(session: &Session) -> String {
hasher.write(s.as_bytes());
}
let mut hash_state = hasher.into_inner();
let hash_bytes = hash_state.finalize();
// If this is an executable, add a special suffix, so that we don't get
// symbol conflicts when linking against a library of the same name.
let is_exe = session.crate_types.borrow().contains(&config::CrateTypeExecutable);
format!("{:x}{}", FmtWrap(hash_bytes), if is_exe { "-exe" } else {""})
format!("{}{}", hasher.finish().to_hex(), if is_exe { "-exe" } else {""})
}
pub fn build_output_filenames(input: &Input,

View file

@ -1,88 +0,0 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
use std::hash::Hasher;
use rustc_data_structures::blake2b::Blake2bHasher;
use rustc::ty::util::ArchIndependentHasher;
use ich::Fingerprint;
use rustc_serialize::leb128::write_unsigned_leb128;
#[derive(Debug)]
pub struct IchHasher {
state: ArchIndependentHasher<Blake2bHasher>,
leb128_helper: Vec<u8>,
bytes_hashed: u64,
}
impl IchHasher {
pub fn new() -> IchHasher {
let hash_size = mem::size_of::<Fingerprint>();
IchHasher {
state: ArchIndependentHasher::new(Blake2bHasher::new(hash_size, &[])),
leb128_helper: vec![],
bytes_hashed: 0
}
}
pub fn bytes_hashed(&self) -> u64 {
self.bytes_hashed
}
pub fn finish(self) -> Fingerprint {
let mut fingerprint = Fingerprint::zero();
fingerprint.0.copy_from_slice(self.state.into_inner().finalize());
fingerprint
}
#[inline]
fn write_uleb128(&mut self, value: u64) {
let len = write_unsigned_leb128(&mut self.leb128_helper, 0, value);
self.state.write(&self.leb128_helper[0..len]);
self.bytes_hashed += len as u64;
}
}
// For the non-u8 integer cases we leb128 encode them first. Because small
// integers dominate, this significantly and cheaply reduces the number of
// bytes hashed, which is good because blake2b is expensive.
impl Hasher for IchHasher {
fn finish(&self) -> u64 {
bug!("Use other finish() implementation to get the full 128-bit hash.");
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.state.write(bytes);
self.bytes_hashed += bytes.len() as u64;
}
// There is no need to leb128-encode u8 values.
#[inline]
fn write_u16(&mut self, i: u16) {
self.write_uleb128(i as u64);
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.write_uleb128(i as u64);
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.write_uleb128(i);
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.write_uleb128(i as u64);
}
}

View file

@ -36,6 +36,8 @@ use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
use rustc::hir::intravisit as visit;
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use rustc::ty::TyCtxt;
use rustc_data_structures::stable_hasher::StableHasher;
use ich::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc::util::common::record_time;
use rustc::session::config::DebugInfoLevel::NoDebugInfo;
@ -43,14 +45,12 @@ use rustc::session::config::DebugInfoLevel::NoDebugInfo;
use self::def_path_hash::DefPathHashes;
use self::svh_visitor::StrictVersionHashVisitor;
use self::caching_codemap_view::CachingCodemapView;
use self::hasher::IchHasher;
use ich::Fingerprint;
mod def_path_hash;
mod svh_visitor;
mod caching_codemap_view;
pub mod hasher;
pub type IchHasher = StableHasher<Fingerprint>;
pub struct IncrementalHashesMap {
hashes: FxHashMap<DepNode<DefId>, Fingerprint>,
@ -244,4 +244,3 @@ impl<'a, 'tcx> Visitor<'tcx> for HashItemsVisitor<'a, 'tcx> {
visit::walk_foreign_item(self, item);
}
}

View file

@ -32,7 +32,7 @@ use std::hash::Hash;
use super::def_path_hash::DefPathHashes;
use super::caching_codemap_view::CachingCodemapView;
use super::hasher::IchHasher;
use super::IchHasher;
const IGNORED_ATTRIBUTES: &'static [&'static str] = &[
"cfg",

View file

@ -9,6 +9,8 @@
// except according to those terms.
use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
use rustc_data_structures::stable_hasher;
use rustc_data_structures::ToHex;
const FINGERPRINT_LENGTH: usize = 16;
@ -44,6 +46,10 @@ impl Fingerprint {
((self.0[6] as u64) << 48) |
((self.0[7] as u64) << 56)
}
pub fn to_hex(&self) -> String {
self.0.to_hex()
}
}
impl Encodable for Fingerprint {
@ -79,3 +85,12 @@ impl ::std::fmt::Display for Fingerprint {
Ok(())
}
}
impl stable_hasher::StableHasherResult for Fingerprint {
fn finish(mut hasher: stable_hasher::StableHasher<Self>) -> Self {
let mut fingerprint = Fingerprint::zero();
fingerprint.0.copy_from_slice(hasher.finalize());
fingerprint
}
}

View file

@ -48,7 +48,7 @@ pub mod ich;
pub use assert_dep_graph::assert_dep_graph;
pub use calculate_svh::compute_incremental_hashes_map;
pub use calculate_svh::IncrementalHashesMap;
pub use calculate_svh::hasher::IchHasher;
pub use calculate_svh::IchHasher;
pub use persist::load_dep_graph;
pub use persist::save_dep_graph;
pub use persist::save_trans_partition;

View file

@ -30,7 +30,7 @@ use super::preds::*;
use super::fs::*;
use super::dirty_clean;
use super::file_format;
use calculate_svh::hasher::IchHasher;
use calculate_svh::IchHasher;
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
incremental_hashes_map: &IncrementalHashesMap,

View file

@ -99,8 +99,6 @@
use common::SharedCrateContext;
use monomorphize::Instance;
use rustc_data_structures::fmt_wrap::FmtWrap;
use rustc_data_structures::blake2b::Blake2bHasher;
use rustc::middle::weak_lang_items;
use rustc::hir::def_id::LOCAL_CRATE;
@ -135,7 +133,7 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
let tcx = scx.tcx();
let mut hasher = ty::util::TypeIdHasher::new(tcx, Blake2bHasher::new(8, &[]));
let mut hasher = ty::util::TypeIdHasher::<u64>::new(tcx);
record_time(&tcx.sess.perf_stats.symbol_hash_time, || {
// the main symbol name is not necessarily unique; hash in the
@ -158,9 +156,7 @@ fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
});
// 64 bits should be enough to avoid collisions.
let mut hasher = hasher.into_inner();
let hash_bytes = hasher.finalize();
format!("h{:x}", FmtWrap(hash_bytes))
format!("h{:016x}", hasher.finish())
}
impl<'a, 'tcx> Instance<'tcx> {

View file

@ -31,7 +31,7 @@ use rustc::ty::fold::TypeVisitor;
use rustc::ty::subst::Substs;
use rustc::ty::util::TypeIdHasher;
use rustc::hir;
use rustc_data_structures::blake2b::Blake2bHasher;
use rustc_data_structures::ToHex;
use {type_of, machine, monomorphize};
use common::CrateContext;
use type_::Type;
@ -42,7 +42,6 @@ use util::common::path2cstr;
use libc::{c_uint, c_longlong};
use std::ffi::CString;
use std::fmt::Write;
use std::path::Path;
use std::ptr;
use syntax::ast;
@ -147,21 +146,11 @@ impl<'tcx> TypeMap<'tcx> {
// The hasher we are using to generate the UniqueTypeId. We want
// something that provides more than the 64 bits of the DefaultHasher.
const TYPE_ID_HASH_LENGTH: usize = 20;
let mut type_id_hasher = TypeIdHasher::new(cx.tcx(),
Blake2bHasher::new(TYPE_ID_HASH_LENGTH, &[]));
let mut type_id_hasher = TypeIdHasher::<[u8; 20]>::new(cx.tcx());
type_id_hasher.visit_ty(type_);
let mut hash_state = type_id_hasher.into_inner();
let hash: &[u8] = hash_state.finalize();
debug_assert!(hash.len() == TYPE_ID_HASH_LENGTH);
let mut unique_type_id = String::with_capacity(TYPE_ID_HASH_LENGTH * 2);
for byte in hash.into_iter() {
write!(&mut unique_type_id, "{:x}", byte).unwrap();
}
let unique_type_id = type_id_hasher.finish().to_hex();
let key = self.unique_id_interner.intern(&unique_type_id);
self.type_to_unique_id.insert(type_, UniqueTypeId(key));

View file

@ -9,18 +9,26 @@
// except according to those terms.
#[inline]
pub fn write_to_vec(vec: &mut Vec<u8>, position: &mut usize, byte: u8) {
if *position == vec.len() {
fn write_to_vec(vec: &mut Vec<u8>, position: usize, byte: u8) {
if position == vec.len() {
vec.push(byte);
} else {
vec[*position] = byte;
vec[position] = byte;
}
*position += 1;
}
pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, mut value: u64) -> usize {
let mut position = start_position;
#[inline]
/// encodes an integer using unsigned leb128 encoding and stores
/// the result using a callback function.
///
/// The callback `write` is called once for each position
/// that is to be written to with the byte to be encoded
/// at that position.
pub fn write_unsigned_leb128_to<W>(mut value: u64, mut write: W) -> usize
where W: FnMut(usize, u8)
{
let mut position = 0;
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
@ -28,14 +36,19 @@ pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, mut value
byte |= 0x80;
}
write_to_vec(out, &mut position, byte);
write(position, byte);
position += 1;
if value == 0 {
break;
}
}
return position - start_position;
position
}
pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, value: u64) -> usize {
write_unsigned_leb128_to(value, |i, v| write_to_vec(out, start_position+i, v))
}
#[inline]
@ -56,9 +69,17 @@ pub fn read_unsigned_leb128(data: &[u8], start_position: usize) -> (u64, usize)
(result, position - start_position)
}
pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, mut value: i64) -> usize {
let mut position = start_position;
#[inline]
/// encodes an integer using signed leb128 encoding and stores
/// the result using a callback function.
///
/// The callback `write` is called once for each position
/// that is to be written to with the byte to be encoded
/// at that position.
pub fn write_signed_leb128_to<W>(mut value: i64, mut write: W) -> usize
where W: FnMut(usize, u8)
{
let mut position = 0;
loop {
let mut byte = (value as u8) & 0x7f;
@ -69,14 +90,19 @@ pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, mut value:
byte |= 0x80; // Mark this byte to show that more bytes will follow.
}
write_to_vec(out, &mut position, byte);
write(position, byte);
position += 1;
if !more {
break;
}
}
return position - start_position;
position
}
pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, value: i64) -> usize {
write_signed_leb128_to(value, |i, v| write_to_vec(out, start_position+i, v))
}
#[inline]