Repository: paritytech/parity-common (rust)

Issue:
[rlp] `RlpIterator::len()` does not update when the iterator is consumed.
## Issue 
The RlpIterator (and the [ExactSizeIterator](https://doc.rust-lang.org/nightly/core/iter/trait.ExactSizeIterator.html)) documentation states that the [`len`](https://doc.rust-lang.org/nightly/core/iter/trait.ExactSizeIterator.html#method.len) function

> Returns the exact remaining length of the iterator.

So it is expected that the value returned is decreased by 1 each time `next()` is invoked. However, this does not happen, and `len()` always returns the initial size of the iterator. 
### Failing test
By slightly modifying the test `rlp_iter` in `rlp/tests/tests.rs` you can trigger this misbehavior.  
```rust
#[test]
fn rlp_iter() {
	let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'];
	{
		let rlp = Rlp::new(&data);
		let mut iter = rlp.iter();

		assert_eq!(iter.len(), 2); // passes

		let cat = iter.next().unwrap();
		assert!(cat.is_data());
		assert_eq!(cat.as_raw(), &[0x83, b'c', b'a', b't']);
		
		assert_eq!(iter.len(), 1); // fails

		let dog = iter.next().unwrap();
		assert!(dog.is_data());
		assert_eq!(dog.as_raw(), &[0x83, b'd', b'o', b'g']);
		
		assert_eq!(iter.len(), 0); // unreachable

		let none = iter.next();
		assert!(none.is_none());

		let cat_again = rlp.at(0).unwrap();
		assert!(cat_again.is_data());
		assert_eq!(cat_again.as_raw(), &[0x83, b'c', b'a', b't']);
	}
}
```
## Proposed solution 

Modify the implementation of the `len()` function such that it subtracts `self.index` to `self.rlp.item_count()`.  

I can create the PR if it is ok for you.

 

Pull request:
fix(rlp): `len()` changes as `RlpIterator` is consumed
  Fixes #761  
## Problem
The issue is described in #761  
## Solution
The `RlpIterator::len` function returns `item_count - current_index`.
### Tests
No new tests were added, only changed the `rlp_iter` test in `rlp/tests/tests.rs`. You can run it with 
```sh
cargo test -p rlp --test tests -- rlp_iter
```

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 571ae99..3a57a60 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
           override: true
 
       - name: Rust Cache
-        uses: Swatinem/rust-cache@6fd3edff6979b79f87531400ad694fb7f2c84b1f # v2.2.1
+        uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1
 
       - uses: actions-rs/cargo@v1
         with:
@@ -43,7 +43,7 @@ jobs:
           override: true
 
       - name: Rust Cache
-        uses: Swatinem/rust-cache@6fd3edff6979b79f87531400ad694fb7f2c84b1f # v2.2.1
+        uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1
 
       - run: rustup target add wasm32-unknown-unknown
 
@@ -136,7 +136,7 @@ jobs:
           override: true
 
       - name: Rust Cache
-        uses: Swatinem/rust-cache@6fd3edff6979b79f87531400ad694fb7f2c84b1f # v2.2.1
+        uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1
 
       - uses: actions-rs/cargo@v1
         with:
diff --git a/ethbloom/Cargo.toml b/ethbloom/Cargo.toml
index 56c0ef7..fdc8cf8 100644
--- a/ethbloom/Cargo.toml
+++ b/ethbloom/Cargo.toml
@@ -20,7 +20,7 @@ impl-codec = { version = "0.6.0", path = "../primitive-types/impls/codec", defau
 scale-info = { version = ">=1.0, <3", features = ["derive"], default-features = false, optional = true }
 
 [dev-dependencies]
-criterion = "0.4.0"
+criterion = "0.5.1"
 rand = "0.8.0"
 hex-literal = "0.4.1"
 
diff --git a/fixed-hash/Cargo.toml b/fixed-hash/Cargo.toml
index f878d51..337dad7 100644
--- a/fixed-hash/Cargo.toml
+++ b/fixed-hash/Cargo.toml
@@ -24,7 +24,7 @@ arbitrary = { version = "1.0", optional = true }
 
 [dev-dependencies]
 rand_xorshift = "0.3.0"
-criterion = "0.4.0"
+criterion = "0.5.1"
 rand = { version = "0.8.0", default-features = false, features = ["std_rng"] }
 
 [features]
diff --git a/keccak-hash/Cargo.toml b/keccak-hash/Cargo.toml
index d5b5797..0ea2489 100644
--- a/keccak-hash/Cargo.toml
+++ b/keccak-hash/Cargo.toml
@@ -15,7 +15,7 @@ primitive-types = { path = "../primitive-types", version = "0.12", default-featu
 
 [dev-dependencies]
 tempfile = "3.1.0"
-criterion = "0.4.0"
+criterion = "0.5.1"
 
 [features]
 default = ["std"]
diff --git a/kvdb-rocksdb/Cargo.toml b/kvdb-rocksdb/Cargo.toml
index e5180f5..257f45c 100644
--- a/kvdb-rocksdb/Cargo.toml
+++ b/kvdb-rocksdb/Cargo.toml
@@ -33,7 +33,7 @@ version = "0.21"
 
 [dev-dependencies]
 alloc_counter = "0.0.4"
-criterion = "0.4"
+criterion = "0.5"
 ethereum-types = { path = "../ethereum-types" }
 kvdb-shared-tests = { path = "../kvdb-shared-tests", version = "0.11" }
 rand = "0.8.0"
diff --git a/primitive-types/src/fp_conversion.rs b/primitive-types/src/fp_conversion.rs
index cfc51d2..24f2990 100644
--- a/primitive-types/src/fp_conversion.rs
+++ b/primitive-types/src/fp_conversion.rs
@@ -33,11 +33,34 @@ impl U256 {
 
 	/// Lossy conversion of `U256` to `f64`.
 	pub fn to_f64_lossy(self) -> f64 {
-		let (res, factor) = match self {
-			U256([_, _, 0, 0]) => (self, 1.0),
-			U256([_, _, _, 0]) => (self >> 64, 2.0f64.powi(64)),
-			U256([_, _, _, _]) => (self >> 128, 2.0f64.powi(128)),
-		};
-		(res.low_u128() as f64) * factor
+		// Reference: https://blog.m-ou.se/floats/
+		// Step 1: Get leading zeroes
+		let leading_zeroes = self.leading_zeros();
+		// Step 2: Get msb to be farthest left bit
+		let left_aligned = self << leading_zeroes;
+		// Step 3: Shift msb to fit in lower 53 bits of the first u64 (64-53=11)
+		let quarter_aligned = left_aligned >> 11;
+		let mantissa = quarter_aligned.0[3];
+		// Step 4: For the dropped bits (all bits beyond the 53 most significant
+		// We want to know only 2 things. If the msb of the dropped bits is 1 or 0,
+		// and if any of the other bits are 1. (See blog for explanation)
+		// So we take care to preserve the msb bit, while jumbling the rest of the bits
+		// together so that any 1s will survive. If all 0s, then the result will also be 0.
+		let dropped_bits = quarter_aligned.0[1] | quarter_aligned.0[0] | (left_aligned.0[0] & 0xFFFF_FFFF);
+		let dropped_bits = (dropped_bits & 0x7FFF_FFFF_FFFF_FFFF) | (dropped_bits >> 63);
+		let dropped_bits = quarter_aligned.0[2] | dropped_bits;
+		// Step 5: dropped_bits contains the msb of the original bits and an OR-mixed 63 bits.
+		// If msb of dropped bits is 0, it is mantissa + 0
+		// If msb of dropped bits is 1, it is mantissa + 0 only if mantissa lowest bit is 0
+		// and other bits of the dropped bits are all 0 (which both can be tested with the below all at once)
+		let mantissa = mantissa + ((dropped_bits - (dropped_bits >> 63 & !mantissa)) >> 63);
+		// Step 6: Calculate the exponent
+		// If self is 0, exponent should be 0 (special meaning) and mantissa will end up 0 too
+		// Otherwise, (255 - n) + 1022 so it simplifies to 1277 - n
+		// 1023 and 1022 are the cutoffs for the exponent having the msb next to the decimal point
+		let exponent = if self.is_zero() { 0 } else { 1277 - leading_zeroes as u64 };
+		// Step 7: sign bit is always 0, exponent is shifted into place
+		// Use addition instead of bitwise OR to saturate the exponent if mantissa overflows
+		f64::from_bits((exponent << 52) + mantissa)
 	}
 }
diff --git a/rlp/Cargo.toml b/rlp/Cargo.toml
index a907943..55d7eaf 100644
--- a/rlp/Cargo.toml
+++ b/rlp/Cargo.toml
@@ -14,7 +14,7 @@ rustc-hex = { version = "2.0.1", default-features = false }
 rlp-derive = { version = "0.1", path = "../rlp-derive", optional = true }
 
 [dev-dependencies]
-criterion = "0.4.0"
+criterion = "0.5.1"
 hex-literal = "0.4.1"
 primitive-types = { path = "../primitive-types", version = "0.12", features = ["impl-rlp"] }
 
diff --git a/rlp/src/rlpin.rs b/rlp/src/rlpin.rs
index 53b8731..395808f 100644
--- a/rlp/src/rlpin.rs
+++ b/rlp/src/rlpin.rs
@@ -367,7 +367,7 @@ impl<'a, 'view> Iterator for RlpIterator<'a, 'view> {
 
 impl<'a, 'view> ExactSizeIterator for RlpIterator<'a, 'view> {
 	fn len(&self) -> usize {
-		self.rlp.item_count().unwrap_or(0)
+		self.rlp.item_count().unwrap_or(0).saturating_sub(self.index)
 	}
 }
 
diff --git a/uint/Cargo.toml b/uint/Cargo.toml
index b1d2e87..36f3de1 100644
--- a/uint/Cargo.toml
+++ b/uint/Cargo.toml
@@ -30,7 +30,7 @@ name = "uint_tests"
 required-features = ["std"]
 
 [dev-dependencies]
-criterion = "0.4.0"
+criterion = "0.5.1"
 num-bigint = "0.4.0"
 
 [target.'cfg(all(unix, target_arch = "x86_64"))'.dev-dependencies]
diff --git a/uint/src/uint.rs b/uint/src/uint.rs
index 2914f72..bc4b741 100644
--- a/uint/src/uint.rs
+++ b/uint/src/uint.rs
@@ -732,7 +732,7 @@ macro_rules! construct_uint {
 				r
 			}
 
-			/// Return specific byte.
+			/// Return specific byte. Byte 0 is the least significant value (ie~ little endian).
 			///
 			/// # Panics
 			///


Test patch:
diff --git a/rlp/tests/tests.rs b/rlp/tests/tests.rs
index a5eface..2033973 100644
--- a/rlp/tests/tests.rs
+++ b/rlp/tests/tests.rs
@@ -105,14 +105,20 @@ fn rlp_iter() {
 		let rlp = Rlp::new(&data);
 		let mut iter = rlp.iter();
 
+		assert_eq!(iter.len(), 2);
+
 		let cat = iter.next().unwrap();
 		assert!(cat.is_data());
 		assert_eq!(cat.as_raw(), &[0x83, b'c', b'a', b't']);
 
+		assert_eq!(iter.len(), 1);
+
 		let dog = iter.next().unwrap();
 		assert!(dog.is_data());
 		assert_eq!(dog.as_raw(), &[0x83, b'd', b'o', b'g']);
 
+		assert_eq!(iter.len(), 0);
+
 		let none = iter.next();
 		assert!(none.is_none());