|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 |
| -use std::num::One; |
| 11 | +use std::num::{One, Zero, CheckedAdd}; |
12 | 12 | use std::vec::bytes::{MutableByteVector, copy_memory};
|
13 | 13 |
|
14 | 14 |
|
@@ -97,50 +97,73 @@ pub fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
|
97 | 97 | }
|
98 | 98 |
|
99 | 99 |
|
100 |
| -/// Returns true if adding the two parameters will result in integer overflow |
101 |
| -pub fn will_add_overflow<T: Int + Unsigned>(x: T, y: T) -> bool { |
102 |
| - // This doesn't handle negative values! Don't copy this code elsewhere without considering if |
103 |
| - // negative values are important to you! |
104 |
| - let max: T = Bounded::max_value(); |
105 |
| - return x > max - y; |
| 100 | +trait ToBits { |
| 101 | + /// Convert the value in bytes to the number of bits, a tuple where the 1st item is the |
| 102 | + /// high-order value and the 2nd item is the low order value. |
| 103 | + fn to_bits(self) -> (Self, Self); |
106 | 104 | }
|
107 | 105 |
|
108 |
| -/// Shifts the second parameter and then adds it to the first. fails!() if there would be unsigned |
109 |
| -/// integer overflow. |
110 |
| -pub fn shift_add_check_overflow<T: Int + Unsigned + Clone>(x: T, mut y: T, shift: T) -> T { |
111 |
| - if y.leading_zeros() < shift { |
112 |
| - fail!("Could not add values - integer overflow."); |
| 106 | +impl ToBits for u64 { |
| 107 | + fn to_bits(self) -> (u64, u64) { |
| 108 | + return (self >> 61, self << 3); |
113 | 109 | }
|
114 |
| - y = y << shift; |
| 110 | +} |
115 | 111 |
|
116 |
| - if will_add_overflow(x.clone(), y.clone()) { |
117 |
| - fail!("Could not add values - integer overflow."); |
118 |
| - } |
| 112 | +/// Adds the specified number of bytes to the bit count. fail!() if this would cause numeric |
| 113 | +/// overflow. |
| 114 | +pub fn add_bytes_to_bits<T: Int + CheckedAdd + ToBits>(bits: T, bytes: T) -> T { |
| 115 | + let (new_high_bits, new_low_bits) = bytes.to_bits(); |
119 | 116 |
|
120 |
| - return x + y; |
121 |
| -} |
| 117 | + if new_high_bits > Zero::zero() { |
| 118 | + fail!("Numeric overflow occured.") |
| 119 | + } |
122 | 120 |
|
123 |
| -/// Shifts the second parameter and then adds it to the first, which is a tuple where the first |
124 |
| -/// element is the high order value. fails!() if there would be unsigned integer overflow. |
125 |
| -pub fn shift_add_check_overflow_tuple |
126 |
| - <T: Int + Unsigned + Clone> |
127 |
| - (x: (T, T), mut y: T, shift: T) -> (T, T) { |
128 |
| - if y.leading_zeros() < shift { |
129 |
| - fail!("Could not add values - integer overflow."); |
| 121 | + match bits.checked_add(&new_low_bits) { |
| 122 | + Some(x) => return x, |
| 123 | + None => fail!("Numeric overflow occured.") |
130 | 124 | }
|
131 |
| - y = y << shift; |
| 125 | +} |
132 | 126 |
|
133 |
| - match x { |
134 |
| - (hi, low) => { |
135 |
| - let one: T = One::one(); |
136 |
| - if will_add_overflow(low.clone(), y.clone()) { |
137 |
| - if will_add_overflow(hi.clone(), one.clone()) { |
138 |
| - fail!("Could not add values - integer overflow."); |
139 |
| - } else { |
140 |
| - return (hi + one, low + y); |
141 |
| - } |
| 127 | +/// Adds the specified number of bytes to the bit count, which is a tuple where the first element is |
| 128 | +/// the high order value. fail!() if this would cause numeric overflow. |
| 129 | +pub fn add_bytes_to_bits_tuple |
| 130 | + <T: Int + Unsigned + CheckedAdd + ToBits> |
| 131 | + (bits: (T, T), bytes: T) -> (T, T) { |
| 132 | + let (new_high_bits, new_low_bits) = bytes.to_bits(); |
| 133 | + let (hi, low) = bits; |
| 134 | + |
| 135 | + // Add the low order value - if there is no overflow, then add the high order values |
| 136 | + // If the addition of the low order values causes overflow, add one to the high order values |
| 137 | + // before adding them. |
| 138 | + match low.checked_add(&new_low_bits) { |
| 139 | + Some(x) => { |
| 140 | + if new_high_bits == Zero::zero() { |
| 141 | + // This is the fast path - every other alternative will rarely occur in practice |
| 142 | + // considering how large an input would need to be for those paths to be used. |
| 143 | + return (hi, x); |
142 | 144 | } else {
|
143 |
| - return (hi, low + y); |
| 145 | + match hi.checked_add(&new_high_bits) { |
| 146 | + Some(y) => return (y, x), |
| 147 | + None => fail!("Numeric overflow occured.") |
| 148 | + } |
| 149 | + } |
| 150 | + }, |
| 151 | + None => { |
| 152 | + let one: T = One::one(); |
| 153 | + let z = match new_high_bits.checked_add(&one) { |
| 154 | + Some(w) => w, |
| 155 | + None => fail!("Numeric overflow occured.") |
| 156 | + }; |
| 157 | + match hi.checked_add(&z) { |
| 158 | + // This re-executes the addition that was already performed earlier when overflow |
| 159 | + // occured, this time allowing the overflow to happen. Technically, this could be |
| 160 | + // avoided by using the checked add intrinsic directly, but that involves using |
| 161 | + // unsafe code and is not really worthwhile considering how infrequently code will |
| 162 | + // run in practice. This is the reason that this function requires that the type T |
| 163 | + // be Unsigned - overflow is not defined for Signed types. This function could be |
| 164 | + // implemented for signed types as well if that were needed. |
| 165 | + Some(y) => return (y, low + new_low_bits), |
| 166 | + None => fail!("Numeric overflow occured.") |
144 | 167 | }
|
145 | 168 | }
|
146 | 169 | }
|
|
0 commit comments