| Crates.io | ranges-ext |
| lib.rs | ranges-ext |
| version | 0.5.0 |
| created_at | 2025-12-13 07:06:45.060114+00 |
| updated_at | 2026-01-06 15:34:43.1738+00 |
| description | A no_std range/interval set with merge, contains, and removal (with splitting) |
| homepage | |
| repository | https://github.com/ZR233/ranges-ext |
| max_upload_size | |
| id | 1982687 |
| size | 93,437 |
An efficient range/interval set data structure designed for no_std environments, with support for metadata, smart merging, and interval splitting.
RangeInfo traitno_std Compatible - Suitable for embedded and bare-metal environments[dependencies]
ranges-ext = "0.5"
[dependencies]
ranges-ext = { version = "0.5", features = ["alloc"] }
This library is #![no_std] by default and can be used directly in embedded environments. Enable the alloc feature to use dynamic capacity mode in standard environments.
use ranges_ext::{RangeInfo, RangeVecOps};
use std::ops::Range;
#[derive(Clone, Debug, PartialEq, Eq)]
struct MyRange {
range: Range<i32>,
kind: &'static str,
overwritable: bool,
}
impl Default for MyRange {
fn default() -> Self {
Self {
range: 0..0,
kind: "",
overwritable: false,
}
}
}
impl RangeInfo for MyRange {
type Kind = &'static str;
type Type = i32;
fn range(&self) -> Range<Self::Type> {
self.range.clone()
}
fn kind(&self) -> Self::Kind {
self.kind
}
fn overwritable(&self) -> bool {
self.overwritable
}
fn clone_with_range(&self, range: Range<Self::Type>) -> Self {
Self {
range,
kind: self.kind,
overwritable: self.overwritable,
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create temporary buffer (required for heapless mode)
let mut temp_buffer = [0u8; 1024];
// Use heapless::Vec as container
let mut set: heapless::Vec<MyRange, 16> = heapless::Vec::new();
// Add intervals (adjacent/overlapping intervals with same kind will auto-merge)
set.merge_add(MyRange {
range: 10..20,
kind: "A",
overwritable: true,
}, &mut temp_buffer)?;
set.merge_add(MyRange {
range: 30..40,
kind: "A",
overwritable: true,
}, &mut temp_buffer)?;
set.merge_add(MyRange {
range: 15..35,
kind: "A",
overwritable: true,
}, &mut temp_buffer)?;
// Three intervals merge into one
assert_eq!(set.len(), 1);
assert_eq!(set.as_slice()[0].range(), 10..40);
// Query
assert!(set.contains_point(10));
assert!(!set.contains_point(45));
Ok(())
}
use ranges_ext::{RangeInfo, RangeVecAllocOps};
// [Same MyRange definition as above]
#[cfg(feature = "alloc")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Use alloc::vec::Vec as container (no temporary buffer needed)
let mut set: alloc::vec::Vec<MyRange> = alloc::vec::Vec::new();
// Add intervals (no temporary buffer needed)
set.merge_add(MyRange {
range: 10..20,
kind: "A",
overwritable: true,
})?;
set.merge_add(MyRange {
range: 15..35,
kind: "A",
overwritable: true,
})?;
// Auto-merge
assert_eq!(set.len(), 1);
Ok(())
}
If you don't need metadata, you can use the unit type ():
use ranges_ext::{RangeInfo, RangeVecOps};
#[derive(Clone, Debug, PartialEq, Eq)]
struct SimpleRange {
range: core::ops::Range<i32>,
overwritable: bool,
}
impl Default for SimpleRange {
fn default() -> Self {
Self {
range: 0..0,
overwritable: false,
}
}
}
impl RangeInfo for SimpleRange {
type Kind = ();
type Type = i32;
fn range(&self) -> core::ops::Range<Self::Type> {
self.range.clone()
}
fn kind(&self) -> Self::Kind {
()
}
fn overwritable(&self) -> bool {
self.overwritable
}
fn clone_with_range(&self, range: core::ops::Range<Self::Type>) -> Self {
Self {
range,
overwritable: self.overwritable,
}
}
}
let mut temp_buffer = [0u8; 1024];
let mut set: heapless::Vec<SimpleRange, 16> = heapless::Vec::new();
set.merge_add(SimpleRange {
range: 10..20,
overwritable: true,
}, &mut temp_buffer)?;
RangeInfo is the core trait that defines the requirements for interval types:
pub trait RangeInfo: Debug + Clone + Sized + Default {
type Kind: Debug + Eq + Clone; // Metadata type
type Type: Ord + Copy; // Interval value type
fn range(&self) -> Range<Self::Type>; // Get interval
fn kind(&self) -> Self::Kind; // Get metadata (owned)
fn overwritable(&self) -> bool; // Whether it can be overwritten
fn clone_with_range(&self, range: Range<Self::Type>) -> Self; // Clone with new range
}
Important Change (0.5.0): The kind() method now returns the owned type Self::Kind instead of a reference &Self::Kind.
Provides interval operations for fixed-capacity vectors (like heapless::Vec<T, N>):
&mut [u8] for temporary storagefn merge_add(&mut self, new_info: T, temp: &mut [u8]) -> Result<(), RangeError<T>>;
fn merge_remove(&mut self, range: Range<T::Type>, temp: &mut [u8]) -> Result<(), RangeError<T>>;
fn merge_extend<I>(&mut self, ranges: I, temp: &mut [u8]) -> Result<(), RangeError<T>>
where I: IntoIterator<Item = T>;
fn contains_point(&self, value: T::Type) -> bool;
Provides interval operations for dynamic vectors (alloc::vec::Vec<T>):
fn merge_add(&mut self, new_info: T) -> Result<(), RangeError<T>>;
fn merge_remove(&mut self, range: Range<T::Type>) -> Result<(), RangeError<T>>;
fn merge_extend<I>(&mut self, ranges: I) -> Result<(), RangeError<T>>
where I: IntoIterator<Item = T>;
fn contains_point(&self, value: T::Type) -> bool;
Kind is metadata for each interval, used to:
Example:
// Same kind, will merge
set.merge_add(MyRange::new(0..10, "A", true), &mut temp)?;
set.merge_add(MyRange::new(10..20, "A", true), &mut temp)?;
// Result: [0, 20) kind="A"
// Different kinds, won't merge
set.merge_add(MyRange::new(0..10, "A", true), &mut temp)?;
set.merge_add(MyRange::new(10..20, "B", true), &mut temp)?;
// Result: [0, 10) kind="A", [10, 20) kind="B"
In heapless mode, merge_add and merge_remove operations require a temporary buffer.
Why is it needed?
How to calculate size?
// Temporary buffer size = element size × expected max number of elements
let elem_size = std::mem::size_of::<MyRange>();
let max_elements = 128; // Adjust based on actual needs
let mut temp_buffer = [0u8; elem_size * max_elements];
// More conservative calculation (considering alignment and splitting operations)
let mut temp_buffer = [0u8; elem_size * max_elements * 2];
Best practices:
let mut set: heapless::Vec<MyRange, 16> = heapless::Vec::new();
let mut temp = [0u8; 1024];
// Add a non-overwritable interval
set.merge_add(MyRange {
range: 10..30,
kind: "protected",
overwritable: false, // Not overwritable
}, &mut temp)?;
// Attempting to add a conflicting interval will fail
let result = set.merge_add(MyRange {
range: 20..40,
kind: "new",
overwritable: true,
}, &mut temp);
assert!(matches!(result, Err(RangeError::Conflict { .. })));
// Overwritable intervals can be replaced
set.merge_add(MyRange {
range: 5..15,
kind: "overwritable",
overwritable: true, // Overwritable
}, &mut temp)?;
set.merge_add(MyRange {
range: 10..25,
kind: "replacer",
overwritable: true,
}, &mut temp)?;
// [5, 15) is partially overlapped and merged by [10, 25)
let mut set: heapless::Vec<MyRange, 16> = heapless::Vec::new();
let mut temp = [0u8; 1024];
set.merge_add(MyRange::new(10..40, "A", true), &mut temp)?;
// Current: [10, 40) kind="A"
// Remove middle part
set.merge_remove(20..30, &mut temp)?;
// Result: [10, 20) kind="A", [30, 40) kind="A"
assert_eq!(set.len(), 2);
let ranges = vec![
MyRange::new(0..10, "A", true),
MyRange::new(10..20, "A", true),
MyRange::new(20..30, "B", true),
];
// heapless mode
set.merge_extend(ranges, &mut temp)?;
// alloc mode
set.merge_extend(ranges)?;
pub enum RangeError<T>
where
T: RangeInfo,
{
/// Insufficient capacity (heapless mode only)
Capacity,
/// Interval conflict: attempting to overwrite a non-overwritable interval
Conflict {
new: T, // Newly added interval
existing: T, // Existing conflicting interval
},
}
Example:
match set.merge_add(new_range, &mut temp_buffer) {
Ok(()) => println!("Add successful"),
Err(RangeError::Capacity) => println!("Insufficient capacity"),
Err(RangeError::Conflict { new, existing }) => {
println!("Conflict: new interval {:?} conflicts with {:?}", new, existing);
}
}
The project includes the following examples (located in the examples/ directory):
Demonstrates basic interval merging and iteration.
Run:
cargo run --example debug_demo
Shows how intervals with different kinds interact.
Run:
cargo run --example key_demo
Demonstrates various scenarios of interval overwriting and splitting.
Run:
cargo run --example overlap_demo
Shows how to use temporary buffers.
Run:
cargo run --example slicevec_demo
Pros:
Cons:
Use cases:
Pros:
Cons:
Use cases:
merge_add): O(n) - needs to traverse existing intervalsmerge_remove): O(n) - needs to traverse and splitcontains_point): O(log n) - uses binary searchiter): O(n) - zero-copy, just iterationCapacity limitation (heapless mode)
RangeError::CapacityType requirements
RangeInfo::Type must implement Ord + CopyRangeInfo::Kind must implement Debug + Eq + CloneInterval semantics
[start, end)start >= end are ignoredA: Buffer size depends on interval type size and expected maximum number of elements:
let elem_size = std::mem::size_of::<MyRange>();
let max_elements = 128; // Expected max number of intervals
let mut temp_buffer = [0u8; elem_size * max_elements];
// More conservative calculation (considering alignment and split operations)
let mut temp_buffer = [0u8; elem_size * max_elements * 2];
A: This design supports finer-grained interval management:
// Scenario: memory region management
// May need to distinguish "read-only", "read-write", "reserved" regions
// Even if adjacent, they should not merge
set.merge_add(MemoryRange::new(0x1000..0x2000, "read-only", true), &mut temp)?;
set.merge_add(MemoryRange::new(0x2000..0x3000, "read-write", true), &mut temp)?;
// Result keeps two separate intervals
A: Use heapless mode with stack-allocated buffer:
#![no_std]
#[cfg(test)]
mod tests {
use ranges_ext::{RangeInfo, RangeVecOps};
#[test]
fn test_heapless() {
let mut temp_buffer = [0u8; 1024];
let mut set: heapless::Vec<MyRange, 16> = heapless::Vec::new();
// ... test code
}
}
A: Major changes:
RangeInfo::kind() return type changed
// Old version
fn kind(&self) -> &Self::Kind;
// New version (0.5.0)
fn kind(&self) -> Self::Kind;
RangeSet struct no longer provided
heapless::Vec<T, N> or alloc::vec::Vec<T>RangeVecOps or RangeVecAllocOps traitsAPI renamed
add() → merge_add()remove() → merge_remove()extend() → merge_extend()contains() → contains_point()A: Use the overwritable field to control:
// Protect critical intervals
set.merge_add(MyRange::new(0..1000, "critical", false), &mut temp)?;
// Attempting to overwrite will fail
match set.merge_add(MyRange::new(500..1500, "new", true), &mut temp) {
Err(RangeError::Conflict { new, existing }) => {
eprintln!("Cannot overwrite critical interval: {:?}", existing);
}
_ => {}
}
See CHANGELOG.md for version history and changes.
Dual licensed under MIT or Apache-2.0, you may choose either.