rangemap/lib.rs
1/*!
2[`RangeMap`] and [`RangeInclusiveMap`] are map data structures whose keys
3are stored as ranges. Contiguous and overlapping ranges that map to the same
4value are coalesced into a single range.
5
6Corresponding [`RangeSet`] and [`RangeInclusiveSet`] structures are also provided.
7
8
9# Different kinds of ranges
10
11`RangeMap` and `RangeInclusiveMap` correspond to the [`Range`]
12and [`RangeInclusive`] types from the standard library respectively.
13For some applications the choice of range type may be obvious,
14or even be dictated by pre-existing design decisions. For other applications
15the choice may seem arbitrary, and be guided instead by convenience or
16aesthetic preference.
17
18If the choice is not obvious in your case, consider these differences:
19
20- If your key type `K` represents points on a continuum (e.g. `f64`),
21 and the choice of which of two adjacent ranges "owns" the value
22 where they touch is largely arbitrary, then it may be more natural
23 to work with half-open `Range`s like `0.0..1.0` and `1.0..2.0`. If you
24 were to use closed `RangeInclusive`s here instead, then to represent two such adjacent
25 ranges you would need to subtract some infinitesimal (which may depend,
26 as it does in the case of `f64`, on the specific value of `K`)
27 from the end of the earlier range. (See the last point below for more
28 on this problem.)
29- If you need to represent ranges that _include_ the maximum
30 value in the key domain (e.g. `255u8`) then you will
31 probably want to use `RangeInclusive`s like `128u8..=255u8`. Sometimes
32 it may be possible to instead work around this by using a wider key
33 type than the values you are actually trying to represent (`K=u16`
34 even though you are only trying to represent ranges covering `u8`)
35 but in these cases the key domain often represents discrete objects
36 rather than points on a continuum, and so `RangeInclusive` may
37 be a more natural way to express these ranges anyway.
38- If you are using `RangeInclusive`, then it must be possible to define
39 _successor_ and _predecessor_ functions for your key type `K`,
40 because adjacent ranges can not be detected (and thereby coalesced)
41 simply by testing their ends for equality. For key types that represent
42 points on a continuum, defining these functions may be awkward and error-prone.
43 For key types that represent discrete objects, this is usually much
44 more straightforward. (For floats specifically, the **ordered-float5**
45 feature described below provides these functions, taking the successor
46 of a float to be the next representable float.)
47
48
49# Example: use with Chrono
50
51```rust
52use chrono::offset::TimeZone;
53use chrono::{Duration, Utc};
54use rangemap::RangeMap;
55
56let people = ["Alice", "Bob", "Carol"];
57let mut roster = RangeMap::new();
58
59// Set up initial roster.
60let start_of_roster = Utc.ymd(2019, 1, 7);
61let mut week_start = start_of_roster;
62for _ in 0..3 {
63 for person in &people {
64 let next_week = week_start + Duration::weeks(1);
65 roster.insert(week_start..next_week, person);
66 week_start = next_week;
67 }
68}
69
70// Bob is covering Alice's second shift (the fourth shift overall).
71let fourth_shift_start = start_of_roster + Duration::weeks(3);
72let fourth_shift_end = fourth_shift_start + Duration::weeks(1);
73roster.insert(fourth_shift_start..fourth_shift_end, &"Bob");
74
75for (range, person) in roster.iter() {
76 println!("{} ({}): {}", range.start, range.end - range.start, person);
77}
78
79// Output:
80// 2019-01-07UTC (P7D): Alice
81// 2019-01-14UTC (P7D): Bob
82// 2019-01-21UTC (P7D): Carol
83// 2019-01-28UTC (P14D): Bob
84// 2019-02-11UTC (P7D): Carol
85// 2019-02-18UTC (P7D): Alice
86// 2019-02-25UTC (P7D): Bob
87// 2019-03-04UTC (P7D): Carol
88```
89
90
91## Crate features
92
93By default this crate has no dependencies on other crates.
94
95If you enable the **serde1** feature it will introduce a dependency on
96the _serde_ crate and provide `Serialize` and `Deserialize`
97implementations for all map and set types in this crate.
98
99You can enable the **serde1** feature in your _Cargo.toml_ file like so:
100
101```toml
102[dependencies]
103rangemap = { version = "1", features = ["serde1"] }
104```
105
106You can similarly enable support for _quickcheck_ by enabling
107the **quickcheck** feature.
108
109If you enable the **ordered-float5** feature it will introduce a dependency
110on the _ordered-float_ crate, and provide the successor and predecessor
111functions that [`RangeInclusiveMap`] and [`RangeInclusiveSet`] need in
112order to accept `NotNan<f32>` and `NotNan<f64>` keys.
113
114Note that the successor of a float is taken to be the _next representable_
115float, one ULP (unit in the last place) away. So ranges that are one ULP
116apart are adjacent, and will therefore be coalesced if they map to the
117same value: inserting `0.0..=1.0` and `1.0000000000000002..=2.0` will
118leave you with the single range `0.0..=2.0`. If that isn't what you want,
119consider using [`RangeMap`] or [`RangeSet`] with half-open ranges instead.
120
121This feature is named for the major version of _ordered-float_ that it
122targets, because enabling it makes that crate part of rangemap's public
123API: you have to be using the same major version of it that we are.
124Naming it this way leaves room to support a future major version
125alongside this one, rather than forcing a breaking change on everyone
126already using the feature.
127
128
129## Building without the Rust standard library
130
131This crate can work without the full standard library available
132(e.g. when running on bare metal without an operating system)
133but relies on the presence of a global allocator —
134i.e. it links the `core` and `alloc` crates, but not `std`.
135
136Presently there is no functionality in the crate that require
137the standard library. Such functionality will likely be
138introduced in the future, and will be gated behind a default-on
139`std` feature.
140
141See [The Rust Programming Language](https://doc.rust-lang.org/1.7.0/book/no-stdlib.html)
142book for general information about operating without the standard library.
143
144
145
146[`RangeMap`]: crate::RangeMap
147[`RangeInclusiveMap`]: crate::RangeInclusiveMap
148[`RangeSet`]: crate::RangeSet
149[`RangeInclusiveSet`]: crate::RangeInclusiveSet
150[`Range`]: core::ops::Range
151[`RangeInclusive`]: core::ops::RangeInclusive
152
153*/
154
155#![no_std]
156extern crate alloc;
157
158pub mod inclusive_map;
159pub mod inclusive_set;
160pub mod map;
161pub(crate) mod operations;
162pub mod set;
163
164#[cfg(test)]
165mod dense;
166mod range_wrapper;
167mod std_ext;
168
169pub use inclusive_map::RangeInclusiveMap;
170pub use inclusive_set::RangeInclusiveSet;
171pub use map::RangeMap;
172pub use set::RangeSet;
173pub use std_ext::{StepFns, StepLite};
174
175// Doc tests for README.
176#[cfg(feature = "nightly")]
177mod readme;