idheap/
lib.rs

1//! A heap of integer identifiers.
2
3pub mod err;
4
5use std::alloc::{alloc, dealloc, Layout};
6
7use err::Error;
8
9struct IdBlock {
10  next: *mut IdBlock,
11  prev: *mut IdBlock,
12  low: usize,
13  high: usize
14}
15
16impl IdBlock {
17  fn count(&self) -> usize {
18    self.high - self.low + 1
19  }
20}
21
22/// A heap of integer identifiers that can be allocated off the heap and then
23/// returned.
24pub struct IdHeap {
25  /// Lower id bound (inclusive).
26  low: usize,
27
28  /// Upper id bound (inclusive).
29  high: usize,
30
31  /// Doubly linked list containing all free/unallocated id's.
32  ///
33  /// Each block contains a lower and upper bounds of available id's, and the
34  /// blocks are ordered (lowest first).
35  blocks: *mut IdBlock
36}
37
38unsafe impl Send for IdHeap {}
39
40/// Create a block spanning ids from `low` to `high`.
41fn mkblock(low: usize, high: usize) -> *mut IdBlock {
42  let layout = Layout::new::<IdBlock>();
43  let blk = unsafe { alloc(layout) };
44  let blk = blk as *mut IdBlock;
45
46  unsafe {
47    (*blk).next = std::ptr::null_mut();
48    (*blk).prev = std::ptr::null_mut();
49    (*blk).low = low;
50    (*blk).high = high;
51  }
52
53  blk
54}
55
56impl IdHeap {
57  /// Create a new intger identifier heap with the id bound `low` .. `high`
58  /// (inclusive-inclusive).
59  ///
60  /// The entire range will be unallocated on creation.
61  pub fn new(low: usize, high: usize) -> Result<Self, Error> {
62    if high < low {
63      #[cfg(feature = "hardfail")]
64      panic!("minimum id bound much be lower than the maximum id bound");
65
66      #[cfg(not(feature = "hardfail"))]
67      return Err(Error::invalid_param(
68        "Lower id bound must be lower than the higher id bound"
69      ));
70    }
71
72    let idblk = mkblock(low, high);
73    Ok(Self {
74      low,
75      high,
76      blocks: idblk
77    })
78  }
79
80  /// Generate an IdHeap from a id blocks specification string.
81  ///
82  /// A spec string can be generated using [`IdHeap::make_strlist()`].
83  ///
84  /// - `0-99` means there's one block of ids from 0 to 99 available.
85  /// - `0-9,11,20-29` means there are three blocks of ids available.
86  pub fn from_strlist(
87    spec: &str,
88    low: usize,
89    high: usize
90  ) -> Result<Self, Error> {
91    let mut head: *mut IdBlock = std::ptr::null_mut();
92    let mut tail: *mut IdBlock = std::ptr::null_mut();
93
94    if !spec.is_empty() {
95      for blkspec in spec.split(',') {
96        let range: Vec<&str> = blkspec.split('-').collect();
97
98        let blk = if range.len() == 1 {
99          let id = match range[0].parse::<usize>() {
100            Ok(id) => id,
101            Err(_e) => {
102              release_chain(head);
103              return Err(Error::bad_format("Not a number in block spec"));
104            }
105          };
106
107          mkblock(id, id)
108        } else if range.len() == 2 {
109          let low = match range[0].parse::<usize>() {
110            Ok(id) => id,
111            Err(_e) => {
112              release_chain(head);
113              return Err(Error::bad_format("Not a number in block spec"));
114            }
115          };
116          let high = match range[1].parse::<usize>() {
117            Ok(id) => id,
118            Err(_e) => {
119              release_chain(head);
120              return Err(Error::bad_format("Not a number in block spec"));
121            }
122          };
123
124          mkblock(low, high)
125        } else {
126          release_chain(head);
127          return Err(Error::bad_format("invalid block spec"));
128        };
129
130        //
131        // Make sure bounds are valid
132        //
133        unsafe {
134          if (*blk).low > (*blk).high {
135            release_chain(head);
136            release_node(blk);
137            return Err(Error::bad_format("Low bound higher than high bound"));
138          }
139
140          if (*blk).low < low || (*blk).high > high {
141            release_chain(head);
142            release_node(blk);
143            return Err(Error::bad_format("Out of bounds"));
144          }
145        }
146
147        if head == std::ptr::null_mut() {
148          head = blk;
149          tail = blk;
150        } else {
151          unsafe {
152            // Make sure this current block's lower bound is higher than the
153            // previous tail's upper bound, and that there's a gap between the
154            // two.
155            if (*tail).high >= (*blk).low {
156              return Err(Error::bad_format("Overlapping spans"));
157            }
158            let d = (*blk).low - (*tail).high;
159            if d < 2 {
160              return Err(Error::bad_format("Contiguous spans"));
161            }
162
163            (*tail).next = blk;
164            (*blk).prev = tail;
165
166            tail = blk;
167          }
168        }
169      }
170    }
171
172    Ok(Self {
173      low,
174      high,
175      blocks: head
176    })
177  }
178
179
180  pub fn from_slice(
181    slice: &[(usize, usize)],
182    low: usize,
183    high: usize
184  ) -> Result<Self, Error> {
185    let mut head: *mut IdBlock = std::ptr::null_mut();
186    let mut tail: *mut IdBlock = std::ptr::null_mut();
187
188    if !slice.is_empty() {
189      for (l, h) in slice {
190        let blk = if l == h {
191          mkblock(*l, *l)
192        } else if *l > *h {
193          release_chain(head);
194          return Err(Error::bad_format("Low bound higher than high bound"));
195        } else {
196          mkblock(*l, *h)
197        };
198
199        unsafe {
200          if (*blk).low < low || (*blk).high > high {
201            release_chain(head);
202            release_node(blk);
203            return Err(Error::bad_format("Out of bounds"));
204          }
205        }
206
207        if head == std::ptr::null_mut() {
208          head = blk;
209          tail = blk;
210        } else {
211          unsafe {
212            // Make sure this current block's lower bound is higher than the
213            // previous tail's upper bound, and that there's a gap between the
214            // two.
215            if (*tail).high >= (*blk).low {
216              return Err(Error::bad_format("Overlapping spans"));
217            }
218            let d = (*blk).low - (*tail).high;
219            if d < 2 {
220              return Err(Error::bad_format("Contiguous spans"));
221            }
222
223            (*tail).next = blk;
224            (*blk).prev = tail;
225
226            tail = blk;
227          }
228        }
229      }
230    }
231
232    Ok(Self {
233      low,
234      high,
235      blocks: head
236    })
237  }
238
239
240  /// Return total number of id's in heap.
241  pub fn capacity(&self) -> usize {
242    self.high - self.low + 1
243  }
244
245  /// Number of ids currently in heap.
246  pub fn count(&self) -> usize {
247    let mut ret = 0;
248
249    let mut idblk = self.blocks;
250    unsafe {
251      while idblk != std::ptr::null_mut() {
252        ret += (*idblk).count();
253        idblk = (*idblk).next;
254      }
255    }
256
257    ret
258  }
259
260  /// Number of ids allocated.
261  pub fn allocated(&self) -> usize {
262    self.capacity() - self.count()
263  }
264
265  pub fn is_empty(&self) -> bool {
266    self.blocks == std::ptr::null_mut()
267  }
268
269
270  /// Generate a string from current id heap state.  The ouput format is
271  /// compatible with [`IdHeap::from_strlist()`].
272  pub fn make_strlist(&self) -> String {
273    let mut ret = Vec::new();
274
275    let mut n = self.blocks;
276    while n != std::ptr::null_mut() {
277      unsafe {
278        if (*n).low == (*n).high {
279          ret.push(format!("{}", (*n).low));
280        } else {
281          ret.push(format!("{}-{}", (*n).low, (*n).high));
282        }
283
284        n = (*n).next;
285      }
286    }
287
288    ret.join(",")
289  }
290
291
292  /// Allocate an identifier.
293  ///
294  /// The id will always be the lowest available.
295  ///
296  /// Once the application is done with the id, it must be returned to the heap
297  /// by calling [`IdHeap::release()`].
298  ///
299  /// Returns `Some(id)` on success.  Returns `None` if the id heap is empty.
300  pub fn alloc(&mut self) -> Option<usize> {
301    if self.blocks == std::ptr::null_mut() {
302      return None;
303    }
304
305    let idblk = self.blocks;
306
307    let id = unsafe { (*idblk).low };
308
309    unsafe {
310      if (*idblk).low != (*idblk).high {
311        // keep block, but remove the allocated id
312        (*idblk).low += 1;
313      } else {
314        // block is empty -- remove it
315        self.blocks = (*self.blocks).next;
316
317        // deallocate the empty block
318        let layout = Layout::new::<IdBlock>();
319        dealloc(idblk as *mut u8, layout);
320      }
321    }
322
323    Some(id)
324  }
325
326
327  /// Attempt to allocate a specific id.
328  ///
329  /// Generally speaking use of this function is discouraged (applications
330  /// should use [`IdHeap::alloc()`] unless the heap's state is being restored
331  /// to a known state.
332  ///
333  /// Returns:
334  /// - `Ok(id)` on success.
335  /// - `Err(Error::Empty)` if heap is empty.
336  /// - `Err(Error::Unavailable)` if the id has already been allocated.
337  pub fn alloc_id(&mut self, id: usize) -> Result<usize, Error> {
338    //
339    // Make sure the id is without the id heap's bounds.
340    //
341    if id < self.low || id > self.high {
342      #[cfg(feature = "hardfail")]
343      panic!("id out of bounds");
344
345      #[cfg(not(feature = "hardfail"))]
346      return Err(Error::OutOfBounds);
347    }
348
349    //
350    // Search for block containing id
351    //
352    let mut idblk = self.blocks;
353    unsafe {
354      //
355      // Try to find the position where the id belongs.
356      //
357      while idblk != std::ptr::null_mut() {
358        // If the requested id is higher than the upper bound, then the id can
359        // not exist in this block, but may be in later blocks in the chain.
360        // Go to next block.  (May end up being null, terminating the loop).
361        if id > (*idblk).high {
362          idblk = (*idblk).next;
363          continue;
364        }
365
366        // If the requested id lower than the block's lowest value, then the id
367        // does not exist within this id heap.
368        if id < (*idblk).low {
369          return Err(Error::Unavailable);
370        }
371
372        //
373        // If this point is reached the id exists within this block.  There are
374        // few different situations:
375        // - The id is the only id in the block
376        // - The id is at one of the block's boundaries
377        // - The id is inside the bounds of the block.  The block needs to be
378        //   split.
379        //
380
381        if (*idblk).low == (*idblk).high {
382          // Id exists within the block
383          self.free_node(idblk);
384          return Ok(id);
385        }
386
387        if (*idblk).low == id {
388          (*idblk).low += 1;
389          return Ok(id);
390        }
391
392        if (*idblk).high == id {
393          (*idblk).high -= 1;
394          return Ok(id);
395        }
396
397        //
398        // If this point is reached the current block needs to be split
399        //
400        let newblock = mkblock((*idblk).low, id - 1);
401
402        (*idblk).low = id + 1;
403
404        self.insert_node(idblk, newblock);
405
406        return Ok(id);
407      }
408    }
409
410    Err(Error::Unavailable)
411  }
412
413
414  /// Return an id back to the heap.
415  pub fn release(&mut self, id: usize) -> Result<(), Error> {
416    //
417    // Make sure the id is without the id heap's bounds.
418    //
419    if id < self.low || id > self.high {
420      #[cfg(feature = "hardfail")]
421      panic!("id out of bounds");
422
423      #[cfg(not(feature = "hardfail"))]
424      return Err(Error::OutOfBounds);
425    }
426
427    let mut idblk = self.blocks;
428    let mut last = std::ptr::null_mut();
429    unsafe {
430      //
431      // Try to find the position where the id belongs.
432      //
433      while idblk != std::ptr::null_mut() {
434        if id + 1 == (*idblk).low {
435          // Id belongs to the beginning of this block
436          (*idblk).low -= 1;
437
438          // Check if this block should be merged with the previous one
439          if (*idblk).prev != std::ptr::null_mut() {
440            // Have a previous node
441            if (*(*idblk).prev).high + 1 == (*idblk).low {
442              // Yes, nodes should be merged.
443              // Delete the current node, so merge backwards.
444              (*(*idblk).prev).high = (*idblk).high;
445
446              // Unlink and release node
447              self.free_node(idblk);
448            }
449          }
450
451          return Ok(());
452        }
453
454        if id == (*idblk).high + 1 {
455          // Id belongs to the end of this block
456          (*idblk).high += 1;
457
458          // Check if this block should be merged with the next one
459
460          if (*idblk).next != std::ptr::null_mut() {
461            // Have a next node
462            if (*(*idblk).next).low == (*idblk).high + 1 {
463              // Yes, nodes should be merged.
464              // Delete the current node, so merge forwards.
465              (*(*idblk).next).low = (*idblk).low;
466
467              // Unlink and release node
468              self.free_node(idblk);
469            }
470          }
471
472          return Ok(());
473        }
474
475        if id < (*idblk).low {
476          // Found an insertion point for a new block.
477          break;
478        }
479
480        //
481        // If id is this block then application attempted to return an
482        // unallocated id to the heap.
483        //
484        if id >= (*idblk).low && id <= (*idblk).high {
485          #[cfg(feature = "hardfail")]
486          panic!("Attempted to release an unallocated id");
487
488          #[cfg(not(feature = "hardfail"))]
489          return Err(Error::invalid_param(""));
490        }
491
492        // remember the last node
493        last = idblk;
494
495        // Follow the forward link.  May end up at null.
496        idblk = (*idblk).next;
497      }
498
499      //
500      // If this point is reached it means id was not returned to a block.
501      // This can happen for three different reasons:
502      // - Reached an insertion point (idblk will be non-null)
503      // - Reached the end (idblk will be null and last will be non-null)
504      // - There are no blocks in the id heap (last till be null)
505      //
506
507      //
508      // Regardless of which situation this is, create a new block for the id.
509      //
510      let mut newblock = mkblock(id, id);
511      if self.blocks == std::ptr::null_mut() {
512        // id heap was empty -- make new block the entire chain
513        self.blocks = newblock;
514      } else if idblk != std::ptr::null_mut() {
515        self.insert_node(idblk, newblock);
516      } else if last != std::ptr::null_mut() {
517        // Append new node to the end of the block list.
518        //  'last' will be set to the last node.  Link 'newnode' after 'last'.
519        (*last).next = newblock;
520        (*newblock).prev = last;
521      }
522    }
523
524    Ok(())
525  }
526}
527
528
529/// Internals
530impl IdHeap {
531  /// Insert an id block specified by `newblock`, at the point of `idblk`.
532  unsafe fn insert_node(
533    &mut self,
534    idblk: *mut IdBlock,
535    newblock: *mut IdBlock
536  ) {
537    // idblk is the insertion point
538    // It may be the first block, which would mean that the new block would
539    // become the new head.
540
541    (*newblock).next = idblk;
542    (*newblock).prev = (*idblk).prev;
543
544    // Relink next node's previous link to the new node.
545    // There's always a next block since this is an insertion
546    (*(*newblock).next).prev = newblock;
547
548    // If there's a previous node, then make it link forward to the new
549    // node
550    if (*newblock).prev != std::ptr::null_mut() {
551      (*(*newblock).prev).next = newblock;
552    } else {
553      // The node being inserted will be the new head
554      self.blocks = newblock;
555    }
556  }
557
558
559  /// Release a id block node, updating chain pointer as needed.
560  /// It is the responsibility of the caller to ensure that `idblk` points to a
561  /// valid block.
562  unsafe fn free_node(&mut self, idblk: *mut IdBlock) {
563    if idblk == self.blocks {
564      // Make next node the new head.  (This can be null)
565      self.blocks = (*idblk).next;
566    }
567
568    //
569    // unlink node
570    //
571    if (*idblk).prev != std::ptr::null_mut() {
572      // Have a previous node -- make its next link point to our next (may be
573      // null).
574      //
575      //                                    +-------+
576      // +---+   +---+   +---+      +---+   | +---+ |   +---+
577      // |   |-->|###|-->|   |  =>  |   |---+ |###|-+-->|   |
578      // |   |<--|###|<--|   |      |   |<----|###|<----|   |
579      // +---+   +---+   +---+      +---+     +---+     +---+
580      (*(*idblk).prev).next = (*idblk).next;
581    } else if (*idblk).next != std::ptr::null_mut() {
582      // No previous node, but have a next one -- so make it not have a
583      // previous link.
584      //
585      //
586      // +---+   +---+      +---+    +---+
587      // |###|-->|   |  =>  |###|--->|   |
588      // |###|<--|   |      |###|    |   |
589      // +---+   +---+      +---+    +---+
590      (*(*idblk).next).prev = std::ptr::null_mut();
591    }
592
593    if (*idblk).next != std::ptr::null_mut() {
594      // Have a next node -- make its previous link point to our previous
595      // (may be null)
596      //
597      //           +-------+                      +-------+
598      //   +---+   | +---+ |   +---+      +---+   | +---+ |   +---+
599      //   |   |---+ |###|-+-->|   |  =>  |   |---+ |###|-+-->|   |
600      //   |   |<----|###|<----|   |      |   |<--+-|###| +---|   |
601      //   +---+     +---+     +---+      +---+   | +---+ |   +---+
602      //                                          +-------+
603      (*(*idblk).next).prev = (*idblk).prev;
604    } else if (*idblk).prev != std::ptr::null_mut() {
605      // No next node, but have a previous one -- so make it not have a
606      // next link.
607      (*(*idblk).prev).next = std::ptr::null_mut();
608    }
609
610    let layout = Layout::new::<IdBlock>();
611    dealloc(idblk as *mut u8, layout);
612  }
613
614
615  /// Get a list of all the available id blocks.
616  #[allow(dead_code)]
617  fn get_blocks_list(&self) -> Vec<(usize, usize)> {
618    let mut ret = Vec::new();
619    let mut idblk = self.blocks;
620    unsafe {
621      while idblk != std::ptr::null_mut() {
622        ret.push(((*idblk).low, (*idblk).high));
623        idblk = (*idblk).next;
624      }
625    }
626    ret
627  }
628}
629
630impl Drop for IdHeap {
631  fn drop(&mut self) {
632    let layout = Layout::new::<IdBlock>();
633    let mut idblk = self.blocks;
634    while idblk != std::ptr::null_mut() {
635      unsafe {
636        let next = (*idblk).next;
637        dealloc(idblk as *mut u8, layout);
638        idblk = next;
639      }
640    }
641  }
642}
643
644
645fn release_chain(head: *mut IdBlock) {
646  let mut n = head;
647
648  while n != std::ptr::null_mut() {
649    let next = unsafe { (*n).next };
650
651    release_node(n);
652
653    n = next;
654  }
655}
656
657fn release_node(n: *mut IdBlock) {
658  let layout = Layout::new::<IdBlock>();
659  unsafe {
660    dealloc(n as *mut u8, layout);
661  }
662}
663
664
665#[cfg(test)]
666mod tests {
667  use super::*;
668
669  #[test]
670  fn single_id() {
671    let mut idh = IdHeap::new(1, 1).unwrap();
672    assert_eq!(idh.capacity(), 1);
673    assert_eq!(idh.count(), 1);
674    assert_eq!(idh.allocated(), 0);
675    let fb = idh.get_blocks_list();
676    assert_eq!(fb.as_slice(), [(1, 1)]);
677    assert_eq!(fb.is_empty(), false);
678
679    assert_eq!(idh.alloc(), Some(1));
680
681    assert_eq!(idh.capacity(), 1);
682    assert_eq!(idh.count(), 0);
683    assert_eq!(idh.allocated(), 1);
684    let fb = idh.get_blocks_list();
685    assert_eq!(fb.as_slice(), []);
686    assert_eq!(fb.is_empty(), true);
687
688    idh.release(1).unwrap();
689
690    assert_eq!(idh.capacity(), 1);
691    assert_eq!(idh.count(), 1);
692    assert_eq!(idh.allocated(), 0);
693    let fb = idh.get_blocks_list();
694    assert_eq!(fb.as_slice(), [(1, 1)]);
695    assert_eq!(fb.is_empty(), false);
696  }
697
698
699  #[test]
700  fn just_init() {
701    let mut idh = IdHeap::new(0, 9).unwrap();
702    assert_eq!(idh.capacity(), 10);
703    assert_eq!(idh.count(), 10);
704    assert_eq!(idh.allocated(), 0);
705    let fb = idh.get_blocks_list();
706    assert_eq!(fb.as_slice(), [(0, 9)]);
707
708    assert_eq!(idh.alloc(), Some(0));
709  }
710
711
712  #[test]
713  fn alloc_one() {
714    let mut idh = IdHeap::new(0, 9).unwrap();
715
716    assert_eq!(idh.alloc(), Some(0));
717
718    assert_eq!(idh.capacity(), 10);
719    assert_eq!(idh.count(), 9);
720    assert_eq!(idh.allocated(), 1);
721    let fb = idh.get_blocks_list();
722    assert_eq!(fb.as_slice(), [(1, 9)]);
723  }
724
725
726  #[test]
727  fn return_new_first_block() {
728    let mut idh = IdHeap::new(0, 9).unwrap();
729
730    // Remove first and second ids
731    assert_eq!(idh.alloc(), Some(0));
732    assert_eq!(idh.alloc(), Some(1));
733
734    // Return the first id, which should force the creation of a new first
735    // block.
736    idh.release(0).unwrap();
737
738    assert_eq!(idh.capacity(), 10);
739    assert_eq!(idh.count(), 9);
740    assert_eq!(idh.allocated(), 1);
741    let fb = idh.get_blocks_list();
742    assert_eq!(fb.as_slice(), [(0, 0), (2, 9)]);
743  }
744
745
746  #[test]
747  fn merge_front_and_back_add_tail() {
748    let mut idh = IdHeap::new(0, 2).unwrap();
749
750    assert_eq!(idh.alloc(), Some(0));
751    assert_eq!(idh.alloc(), Some(1));
752    assert_eq!(idh.alloc(), Some(2));
753
754    let fb = idh.get_blocks_list();
755    assert_eq!(fb.as_slice(), []);
756
757    idh.release(0).unwrap();
758
759    let fb = idh.get_blocks_list();
760    assert_eq!(fb.as_slice(), [(0, 0)]);
761
762    // Add new tail
763    idh.release(2).unwrap();
764
765    let fb = idh.get_blocks_list();
766    assert_eq!(fb.as_slice(), [(0, 0), (2, 2)]);
767
768    idh.release(1).unwrap();
769
770    let fb = idh.get_blocks_list();
771    assert_eq!(fb.as_slice(), [(0, 2)]);
772  }
773
774
775  #[test]
776  fn merge_front_and_back_add_head() {
777    let mut idh = IdHeap::new(0, 2).unwrap();
778
779    assert_eq!(idh.alloc(), Some(0));
780    assert_eq!(idh.alloc(), Some(1));
781    assert_eq!(idh.alloc(), Some(2));
782
783    let fb = idh.get_blocks_list();
784    assert_eq!(fb.as_slice(), []);
785
786    idh.release(2).unwrap();
787
788    let fb = idh.get_blocks_list();
789    assert_eq!(fb.as_slice(), [(2, 2)]);
790
791    // Add new head
792    idh.release(0).unwrap();
793
794    let fb = idh.get_blocks_list();
795    assert_eq!(fb.as_slice(), [(0, 0), (2, 2)]);
796
797    idh.release(1).unwrap();
798
799    let fb = idh.get_blocks_list();
800    assert_eq!(fb.as_slice(), [(0, 2)]);
801  }
802
803
804  #[test]
805  fn alloc_id_head() {
806    let mut idh = IdHeap::new(1, 3).unwrap();
807
808    assert_eq!(idh.alloc_id(1).unwrap(), 1);
809
810    let fb = idh.get_blocks_list();
811    assert_eq!(fb.as_slice(), [(2, 3)]);
812
813    // Return the id
814    idh.release(1).unwrap();
815
816    // Make sure the blocks merged correctly
817    let fb = idh.get_blocks_list();
818    assert_eq!(fb.as_slice(), [(1, 3)]);
819  }
820
821
822  #[test]
823  fn alloc_id_split() {
824    // Allocate an id heap with three elements in it
825    let mut idh = IdHeap::new(1, 3).unwrap();
826
827    // Request the meddle id, to force the id block to split in two
828    assert_eq!(idh.alloc_id(2).unwrap(), 2);
829
830    // Make sure the split ocurred
831    let fb = idh.get_blocks_list();
832    assert_eq!(fb.as_slice(), [(1, 1), (3, 3)]);
833
834    // Return the id
835    idh.release(2).unwrap();
836
837    // Make sure the blocks merged correctly
838    let fb = idh.get_blocks_list();
839    assert_eq!(fb.as_slice(), [(1, 3)]);
840  }
841
842
843  #[test]
844  fn alloc_id_tail() {
845    let mut idh = IdHeap::new(1, 3).unwrap();
846
847    assert_eq!(idh.alloc_id(3).unwrap(), 3);
848
849    let fb = idh.get_blocks_list();
850    assert_eq!(fb.as_slice(), [(1, 2)]);
851
852    // Return the id
853    idh.release(3).unwrap();
854
855    // Make sure the blocks merged correctly
856    let fb = idh.get_blocks_list();
857    assert_eq!(fb.as_slice(), [(1, 3)]);
858  }
859
860
861  #[test]
862  fn from_strlist() {
863    let idh = IdHeap::from_strlist("", 0, 7).unwrap();
864    let fb = idh.get_blocks_list();
865    assert_eq!(fb.as_slice(), []);
866
867    let idh = IdHeap::from_strlist("0", 0, 7).unwrap();
868    let fb = idh.get_blocks_list();
869    assert_eq!(fb.as_slice(), [(0, 0)]);
870
871    let idh = IdHeap::from_strlist("0-2", 0, 7).unwrap();
872    let fb = idh.get_blocks_list();
873    assert_eq!(fb.as_slice(), [(0, 2)]);
874
875    let idh = IdHeap::from_strlist("0-2,6", 0, 7).unwrap();
876    let fb = idh.get_blocks_list();
877    assert_eq!(fb.as_slice(), [(0, 2), (6, 6)]);
878
879    let idh = IdHeap::from_strlist("0-1,3,5-7", 0, 7).unwrap();
880    let fb = idh.get_blocks_list();
881    assert_eq!(fb.as_slice(), [(0, 1), (3, 3), (5, 7)]);
882  }
883
884
885  #[test]
886  fn from_slice() {
887    let idh = IdHeap::from_slice(&[], 0, 7).unwrap();
888    let fb = idh.get_blocks_list();
889    assert_eq!(fb.as_slice(), []);
890
891    let idh = IdHeap::from_slice(&[(0, 0)], 0, 7).unwrap();
892    let fb = idh.get_blocks_list();
893    assert_eq!(fb.as_slice(), [(0, 0)]);
894
895    let idh = IdHeap::from_slice(&[(0, 2)], 0, 7).unwrap();
896    let fb = idh.get_blocks_list();
897    assert_eq!(fb.as_slice(), [(0, 2)]);
898
899    let idh = IdHeap::from_slice(&[(0, 2), (6, 6)], 0, 7).unwrap();
900    let fb = idh.get_blocks_list();
901    assert_eq!(fb.as_slice(), [(0, 2), (6, 6)]);
902
903    let idh = IdHeap::from_slice(&[(0, 1), (3, 3), (5, 7)], 0, 7).unwrap();
904    let fb = idh.get_blocks_list();
905    assert_eq!(fb.as_slice(), [(0, 1), (3, 3), (5, 7)]);
906  }
907
908
909  #[test]
910  fn from_strlist_fail() {
911    match IdHeap::from_strlist("1-0", 0, 7) {
912      Err(Error::BadFormat(s)) => {
913        assert_eq!(&s, "Low bound higher than high bound");
914      }
915      _ => panic!("test failed")
916    }
917
918    match IdHeap::from_strlist("a", 0, 7) {
919      Err(Error::BadFormat(s)) => {
920        assert_eq!(&s, "Not a number in block spec");
921      }
922      _ => panic!("test failed")
923    }
924
925    match IdHeap::from_strlist("a-4", 0, 7) {
926      Err(Error::BadFormat(s)) => {
927        assert_eq!(&s, "Not a number in block spec");
928      }
929      _ => panic!("test failed")
930    }
931
932    match IdHeap::from_strlist("0-a", 0, 7) {
933      Err(Error::BadFormat(s)) => {
934        assert_eq!(&s, "Not a number in block spec");
935      }
936      _ => panic!("test failed")
937    }
938
939    match IdHeap::from_strlist("0-2", 1, 7) {
940      Err(Error::BadFormat(s)) => {
941        assert_eq!(&s, "Out of bounds");
942      }
943      _ => panic!("test failed")
944    }
945
946    match IdHeap::from_strlist("4-8", 1, 7) {
947      Err(Error::BadFormat(s)) => {
948        assert_eq!(&s, "Out of bounds");
949      }
950      _ => panic!("test failed")
951    }
952
953    //  |--------|
954    //        |-------|
955    match IdHeap::from_strlist("0-5,4-7", 0, 7) {
956      Err(Error::BadFormat(s)) => {
957        assert_eq!(&s, "Overlapping spans");
958      }
959      _ => panic!("test failed")
960    }
961
962    //  |--------|
963    //           |-------|
964    match IdHeap::from_strlist("0-4,4-7", 0, 7) {
965      Err(Error::BadFormat(s)) => {
966        assert_eq!(&s, "Overlapping spans");
967      }
968      _ => panic!("test failed")
969    }
970
971    // A gap is needed, so this is not allowed:
972    //  |--------||-------|
973    match IdHeap::from_strlist("0-4,5-7", 0, 7) {
974      Err(Error::BadFormat(s)) => {
975        assert_eq!(&s, "Contiguous spans");
976      }
977      _ => panic!("test failed")
978    }
979  }
980
981
982  #[test]
983  fn from_slice_fail() {
984    match IdHeap::from_slice(&[(1, 0)], 0, 7) {
985      Err(Error::BadFormat(s)) => {
986        assert_eq!(&s, "Low bound higher than high bound");
987      }
988      _ => panic!("test failed")
989    }
990
991    match IdHeap::from_slice(&[(0, 2)], 1, 7) {
992      Err(Error::BadFormat(s)) => {
993        assert_eq!(&s, "Out of bounds");
994      }
995      _ => panic!("test failed")
996    }
997
998    match IdHeap::from_slice(&[(4, 8)], 1, 7) {
999      Err(Error::BadFormat(s)) => {
1000        assert_eq!(&s, "Out of bounds");
1001      }
1002      _ => panic!("test failed")
1003    }
1004
1005    //  |--------|
1006    //        |-------|
1007    match IdHeap::from_slice(&[(0, 5), (4, 7)], 0, 7) {
1008      Err(Error::BadFormat(s)) => {
1009        assert_eq!(&s, "Overlapping spans");
1010      }
1011      _ => panic!("test failed")
1012    }
1013
1014    //  |--------|
1015    //           |-------|
1016    match IdHeap::from_slice(&[(0, 4), (4, 7)], 0, 7) {
1017      Err(Error::BadFormat(s)) => {
1018        assert_eq!(&s, "Overlapping spans");
1019      }
1020      _ => panic!("test failed")
1021    }
1022
1023    // A gap is needed, so this is not allowed:
1024    //  |--------||-------|
1025    match IdHeap::from_slice(&[(0, 4), (5, 7)], 0, 7) {
1026      Err(Error::BadFormat(s)) => {
1027        assert_eq!(&s, "Contiguous spans");
1028      }
1029      _ => panic!("test failed")
1030    }
1031  }
1032
1033
1034  #[test]
1035  fn deserialize_serialize() {
1036    let input = "";
1037    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1038    assert_eq!(input, idh.make_strlist());
1039
1040    let input = "0";
1041    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1042    assert_eq!(input, idh.make_strlist());
1043
1044    let input = "0-2";
1045    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1046    assert_eq!(input, idh.make_strlist());
1047
1048    let input = "0-2,4";
1049    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1050    assert_eq!(input, idh.make_strlist());
1051
1052    let input = "0-2,4-6";
1053    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1054    assert_eq!(input, idh.make_strlist());
1055
1056    let input = "0-1,3,5-7";
1057    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1058    assert_eq!(input, idh.make_strlist());
1059
1060    let input = "0,2,4,6";
1061    let idh = IdHeap::from_strlist(input, 0, 7).unwrap();
1062    assert_eq!(input, idh.make_strlist());
1063  }
1064
1065
1066  #[test]
1067  fn send_to_thread() {
1068    let mut idh = IdHeap::new(0, 7).unwrap();
1069
1070    let handle = std::thread::spawn(|| {
1071      let id = idh.alloc().unwrap();
1072      (idh, id)
1073    });
1074
1075    let (mut idh, id) = handle.join().unwrap();
1076    idh.release(id).unwrap();
1077  }
1078}
1079
1080// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :