Skip to content

Commit 6257531

Browse files
authored
Merge pull request #22 from marshallpierce/error-enum
Error enums
2 parents 1976604 + d9688d7 commit 6257531

File tree

3 files changed

+84
-37
lines changed

3 files changed

+84
-37
lines changed

src/lib.rs

+80-33
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,52 @@ pub struct Histogram<T: Counter> {
240240
counts: Vec<T>,
241241
}
242242

243+
/// Errors that can occur when creating a histogram.
244+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
245+
pub enum CreationError {
246+
/// Lowest discernible value must be >= 1.
247+
LowIsZero,
248+
/// Lowest discernible value must be <= `u64::max_value() / 2` because the highest value is
249+
/// a `u64` and the lowest value must be no bigger than half the highest.
250+
LowExceedsMax,
251+
/// Highest trackable value must be >= 2 * lowest discernible value for some internal
252+
/// calculations to work out. In practice, high is typically much higher than 2 * low.
253+
HighLessThanTwiceLow,
254+
/// Number of significant digits must be in the range `[0, 5]`. It is capped at 5 because 5
255+
/// significant digits is already more than almost anyone needs, and memory usage scales
256+
/// exponentially as this increases.
257+
SigFigExceedsMax,
258+
/// Cannot represent sigfig worth of values beyond the lowest discernible value. Decrease the
259+
/// significant figures, lowest discernible value, or both.
260+
///
261+
/// This could happen if low is very large (like 2^60) and sigfigs is 5, which requires 18
262+
/// additional bits, which would then require more bits than will fit in a u64. Specifically,
263+
/// the exponent of the largest power of two that is smaller than the lowest value and the bits
264+
/// needed to represent the requested significant figures must sum to 63 or less.
265+
CannotRepresentSigFigBeyondLow
266+
}
267+
268+
/// Errors that can occur when adding another histogram.
269+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
270+
pub enum AdditionError {
271+
/// The other histogram includes values that do not fit in this histogram's range.
272+
/// Only possible when auto resize is disabled.
273+
OtherAddendValuesExceedRange
274+
}
275+
276+
/// Errors that can occur when subtracting another histogram.
277+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
278+
pub enum SubtractionError {
279+
/// The other histogram includes values that do not fit in this histogram's range.
280+
/// Only possible when auto resize is disabled.
281+
SubtrahendValuesExceedMinuendRange,
282+
/// The other histogram includes counts that are higher than the current count for a value, and
283+
/// counts cannot go negative. The subtraction may have been partially applied to some counts as
284+
/// this error is returned when the first impossible subtraction is detected.
285+
SubtrahendCountExceedsMinuendCount
286+
}
287+
288+
243289
/// Module containing the implementations of all `Histogram` iterators.
244290
pub mod iterators;
245291

@@ -356,7 +402,7 @@ impl<T: Counter> Histogram<T> {
356402

357403
/// Overwrite this histogram with the given histogram. All data and statistics in this
358404
/// histogram will be overwritten.
359-
pub fn set_to<B: Borrow<Histogram<T>>>(&mut self, source: B) -> Result<(), &'static str> {
405+
pub fn set_to<B: Borrow<Histogram<T>>>(&mut self, source: B) -> Result<(), AdditionError> {
360406
self.reset();
361407
self.add(source.borrow())
362408
}
@@ -380,15 +426,14 @@ impl<T: Counter> Histogram<T> {
380426
///
381427
/// May fail if values in the other histogram are higher than `.high()`, and auto-resize is
382428
/// disabled.
383-
pub fn add<B: Borrow<Histogram<T>>>(&mut self, source: B) -> Result<(), &'static str> {
429+
pub fn add<B: Borrow<Histogram<T>>>(&mut self, source: B) -> Result<(), AdditionError> {
384430
let source = source.borrow();
385431

386432
// make sure we can take the values in source
387433
let top = self.highest_equivalent(self.value_for(self.last()));
388434
if top < source.max() {
389435
if !self.auto_resize {
390-
return Err("The other histogram includes values that do not fit in this \
391-
histogram's range.");
436+
return Err(AdditionError::OtherAddendValuesExceedRange);
392437
}
393438
self.resize(source.max());
394439
}
@@ -480,15 +525,14 @@ impl<T: Counter> Histogram<T> {
480525
/// disabled. Or, if the count for a given value in the other histogram is higher than that of
481526
/// this histogram. In the latter case, some of the counts may still have been updated, which
482527
/// may cause data corruption.
483-
pub fn subtract<B: Borrow<Histogram<T>>>(&mut self, other: B) -> Result<(), &'static str> {
528+
pub fn subtract<B: Borrow<Histogram<T>>>(&mut self, other: B) -> Result<(), SubtractionError> {
484529
let other = other.borrow();
485530

486531
// make sure we can take the values in source
487532
let top = self.highest_equivalent(self.value_for(self.last()));
488533
if top < other.max() {
489534
if !self.auto_resize {
490-
return Err("The other histogram includes values that do not fit in this \
491-
histogram's range.");
535+
return Err(SubtractionError::SubtrahendValuesExceedMinuendRange);
492536
}
493537
self.resize(other.max());
494538
}
@@ -498,8 +542,7 @@ impl<T: Counter> Histogram<T> {
498542
if other_count != T::zero() {
499543
let other_value = other.value_for(i);
500544
if self.count_at(other_value).unwrap() < other_count {
501-
return Err("The other histogram includes counts that are higher than the \
502-
current count for that value.");
545+
return Err(SubtractionError::SubtrahendCountExceedsMinuendCount);
503546
}
504547
self.alter_n(other_value, other_count, false).expect("value should fit by now");
505548
}
@@ -553,10 +596,10 @@ impl<T: Counter> Histogram<T> {
553596
/// auto-adjusting highest trackable value. Can auto-resize up to track values up to
554597
/// `(i64::max_value() / 2)`.
555598
///
556-
/// `sigfig` specifies the number of significant value digits to preserve in the recorded data.
557-
/// This is the number of significant decimal digits to which the histogram will maintain value
558-
/// resolution and separation. Must be a non-negative integer between 0 and 5.
559-
pub fn new(sigfig: u8) -> Result<Histogram<T>, &'static str> {
599+
/// See [`new_with_bounds`] for info on `sigfig`.
600+
///
601+
/// [`new_with_bounds`]: #method.new_with_bounds
602+
pub fn new(sigfig: u8) -> Result<Histogram<T>, CreationError> {
560603
let mut h = Self::new_with_bounds(1, 2, sigfig);
561604
if let Ok(ref mut h) = h {
562605
h.auto_resize = true;
@@ -568,41 +611,45 @@ impl<T: Counter> Histogram<T> {
568611
/// significant decimal digits. The histogram will be constructed to implicitly track
569612
/// (distinguish from 0) values as low as 1. Auto-resizing will be disabled.
570613
///
571-
/// `high` is the highest value to be tracked by the histogram, and must be a positive integer
572-
/// that is >= 2. `sigfig` specifies the number of significant figures to maintain. This is the
573-
/// number of significant decimal digits to which the histogram will maintain value resolution
574-
/// and separation. Must be a non-negative integer between 0 and 5.
575-
pub fn new_with_max(high: u64, sigfig: u8) -> Result<Histogram<T>, &'static str> {
614+
/// See [`new_with_bounds`] for info on `high` and `sigfig`.
615+
///
616+
/// [`new_with_bounds`]: #method.new_with_bounds
617+
pub fn new_with_max(high: u64, sigfig: u8) -> Result<Histogram<T>, CreationError> {
576618
Self::new_with_bounds(1, high, sigfig)
577619
}
578620

579621
/// Construct a `Histogram` with known upper and lower bounds for recorded sample values.
580-
/// Providing a lowest discernible value (`low`) is useful is situations where the units used
581-
/// for the histogram's values are much smaller that the minimal accuracy required. E.g. when
582-
/// tracking time values stated in nanosecond units, where the minimal accuracy required is a
583-
/// microsecond, the proper value for `low` would be 1000.
584622
///
585623
/// `low` is the lowest value that can be discerned (distinguished from 0) by the histogram,
586624
/// and must be a positive integer that is >= 1. It may be internally rounded down to nearest
587-
/// power of 2. `high` is the highest value to be tracked by the histogram, and must be a
588-
/// positive integer that is `>= (2 * low)`. `sigfig` Specifies the number of significant
589-
/// figures to maintain. This is the number of significant decimal digits to which the
590-
/// histogram will maintain value resolution and separation. Must be a non-negative integer
591-
/// between 0 and 5.
592-
pub fn new_with_bounds(low: u64, high: u64, sigfig: u8) -> Result<Histogram<T>, &'static str> {
625+
/// power of 2. Providing a lowest discernible value (`low`) is useful is situations where the
626+
/// units used for the histogram's values are much smaller that the minimal accuracy required.
627+
/// E.g. when tracking time values stated in nanosecond units, where the minimal accuracy
628+
/// required is a microsecond, the proper value for `low` would be 1000. If you're not sure,
629+
/// use 1.
630+
///
631+
/// `high` is the highest value to be tracked by the histogram, and must be a
632+
/// positive integer that is `>= (2 * low)`. If you're not sure, use `u64::max_value()`.
633+
///
634+
/// `sigfig` Specifies the number of significant figures to maintain. This is the number of
635+
/// significant decimal digits to which the histogram will maintain value resolution and
636+
/// separation. Must be in the range [0, 5]. If you're not sure, use 3. As `sigfig` increases,
637+
/// memory usage grows exponentially, so choose carefully if there will be many histograms in
638+
/// memory at once or if storage is otherwise a concern.
639+
pub fn new_with_bounds(low: u64, high: u64, sigfig: u8) -> Result<Histogram<T>, CreationError> {
593640
// Verify argument validity
594641
if low < 1 {
595-
return Err("lowest discernible value must be >= 1");
642+
return Err(CreationError::LowIsZero);
596643
}
597644
if low > u64::max_value() / 2 {
598645
// avoid overflow in 2 * low
599-
return Err("lowest discernible value must be <= u64::max_value() / 2")
646+
return Err(CreationError::LowExceedsMax)
600647
}
601648
if high < 2 * low {
602-
return Err("highest trackable value must be >= 2 * lowest discernible value");
649+
return Err(CreationError::HighLessThanTwiceLow);
603650
}
604651
if sigfig > 5 {
605-
return Err("number of significant digits must be between 0 and 5");
652+
return Err(CreationError::SigFigExceedsMax);
606653
}
607654

608655
// Given a 3 decimal point accuracy, the expectation is obviously for "+/- 1 unit at 1000".
@@ -634,7 +681,7 @@ impl<T: Counter> Histogram<T> {
634681
// histogram vs ones whose magnitude here fits in 63 bits is debatable, and it makes
635682
// it harder to work through the logic. Sums larger than 64 are totally broken as
636683
// leading_zero_count_base would go negative.
637-
return Err("Cannot represent sigfig worth of values beyond low");
684+
return Err(CreationError::CannotRepresentSigFigBeyondLow);
638685
};
639686

640687
let sub_bucket_half_count = sub_bucket_count / 2;

src/tests/index_calculation.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Histogram;
1+
use super::super::{CreationError, Histogram};
22
use tests::helpers::histo64;
33

44
#[test]
@@ -114,7 +114,7 @@ fn unit_magnitude_52_sub_bucket_magnitude_11_index_calculations() {
114114

115115
#[test]
116116
fn unit_magnitude_53_sub_bucket_magnitude_11_throws() {
117-
assert_eq!("Cannot represent sigfig worth of values beyond low",
117+
assert_eq!(CreationError::CannotRepresentSigFigBeyondLow,
118118
Histogram::<u64>::new_with_bounds(1_u64 << 53, 1_u64 << 63, 3).unwrap_err());
119119
}
120120

src/tests/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Histogram;
1+
use super::{CreationError, Histogram};
22

33
#[path = "helpers.rs"]
44
mod helpers;
@@ -10,5 +10,5 @@ mod index_calculation;
1010
#[test]
1111
fn new_err_high_not_double_low() {
1212
let res = Histogram::<u64>::new_with_bounds(10, 15, 0);
13-
assert_eq!("highest trackable value must be >= 2 * lowest discernible value", res.unwrap_err());
13+
assert_eq!(CreationError::HighLessThanTwiceLow, res.unwrap_err());
1414
}

0 commit comments

Comments
 (0)