disjoint_interval_tree 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,7 @@
17
17
  /* Ruby bindings for the disjoint interval tree */
18
18
 
19
19
  #include <ruby.h>
20
+ #include <ruby/version.h>
20
21
 
21
22
  #include <inttypes.h>
22
23
 
@@ -38,6 +39,55 @@ static VALUE rb_dit_size(VALUE self);
38
39
  // TYPED DATA GLUE //
39
40
  /////////////////////
40
41
 
42
+ /* The tree stores a VALUE per interval, so it is a ruby container: every
43
+ * object it holds has to be reachable by the garbage collector.
44
+ *
45
+ * `rb_gc_mark_movable` rather than `rb_gc_mark` keeps those objects
46
+ * relocatable by a compacting GC, at the cost of having to update them in
47
+ * `rb_dit_compact` afterwards.
48
+ *
49
+ * The type is deliberately *not* declared `RUBY_TYPED_WB_PROTECTED`: honouring
50
+ * the write barrier would require `RB_OBJ_WRITE()` on the slot of the node
51
+ * `dit_insert()` creates, hence handing that node back through the C API. The
52
+ * tree is therefore marked on every minor GC instead of only when it is
53
+ * written to, which is a fine trade for now */
54
+
55
+ static void
56
+ rb_dit_mark_node(dit_node_t * node)
57
+ {
58
+ if (node == NULL)
59
+ return ;
60
+
61
+ rb_gc_mark_movable((VALUE) node->obj);
62
+
63
+ rb_dit_mark_node(node->left);
64
+ rb_dit_mark_node(node->right);
65
+ }
66
+
67
+ static void
68
+ rb_dit_mark(void * ptr)
69
+ {
70
+ rb_dit_mark_node(((dit_t *) ptr)->root);
71
+ }
72
+
73
+ static void
74
+ rb_dit_compact_node(dit_node_t * node)
75
+ {
76
+ if (node == NULL)
77
+ return ;
78
+
79
+ node->obj = (dit_object_t) rb_gc_location((VALUE) node->obj);
80
+
81
+ rb_dit_compact_node(node->left);
82
+ rb_dit_compact_node(node->right);
83
+ }
84
+
85
+ static void
86
+ rb_dit_compact(void * ptr)
87
+ {
88
+ rb_dit_compact_node(((dit_t *) ptr)->root);
89
+ }
90
+
41
91
  static void
42
92
  rb_dit_free(void * ptr)
43
93
  {
@@ -59,9 +109,10 @@ rb_dit_memsize(const void * ptr)
59
109
  static const rb_data_type_t rb_dit_type = {
60
110
  .wrap_struct_name = "DisjointIntervalTree",
61
111
  .function = {
62
- .dmark = NULL,
112
+ .dmark = rb_dit_mark,
63
113
  .dfree = rb_dit_free,
64
114
  .dsize = rb_dit_memsize,
115
+ .dcompact = rb_dit_compact,
65
116
  },
66
117
  .parent = NULL,
67
118
  .data = NULL,
@@ -123,10 +174,12 @@ rb_dit_value(VALUE v)
123
174
  rb_obj_class(v));
124
175
  }
125
176
 
177
+ /* An interval, as handed back to ruby: [a, b, obj] */
126
178
  static inline VALUE
127
- rb_dit_interval(dit_value_t a, dit_value_t b)
179
+ rb_dit_interval(dit_value_t a, dit_value_t b, dit_object_t obj)
128
180
  {
129
- return rb_assoc_new(ULL2NUM(a), ULL2NUM(b));
181
+ const VALUE triple[3] = { ULL2NUM(a), ULL2NUM(b), (VALUE) obj };
182
+ return rb_ary_new_from_values(3, triple);
130
183
  }
131
184
 
132
185
  static VALUE
@@ -143,17 +196,17 @@ rb_dit_alloc(VALUE klass)
143
196
  ////////////////
144
197
 
145
198
  static int
146
- rb_dit_cb_yield(dit_value_t a, dit_value_t b, void * user)
199
+ rb_dit_cb_yield(dit_value_t a, dit_value_t b, dit_object_t obj, void * user)
147
200
  {
148
201
  (void) user;
149
- rb_yield_values(2, ULL2NUM(a), ULL2NUM(b));
202
+ rb_yield_values(3, ULL2NUM(a), ULL2NUM(b), (VALUE) obj);
150
203
  return 0;
151
204
  }
152
205
 
153
206
  static int
154
- rb_dit_cb_push(dit_value_t a, dit_value_t b, void * user)
207
+ rb_dit_cb_push(dit_value_t a, dit_value_t b, dit_object_t obj, void * user)
155
208
  {
156
- rb_ary_push((VALUE) user, rb_dit_interval(a, b));
209
+ rb_ary_push((VALUE) user, rb_dit_interval(a, b, obj));
157
210
  return 0;
158
211
  }
159
212
 
@@ -230,15 +283,21 @@ rb_dit_initialize(int argc, VALUE * argv, VALUE self)
230
283
  {
231
284
  intervals = rb_check_array_type(intervals);
232
285
  if (NIL_P(intervals))
233
- rb_raise(rb_eTypeError, "expected an array of [a, b] intervals");
286
+ rb_raise(rb_eTypeError, "expected an array of [a, b] or [a, b, obj] intervals");
234
287
 
235
288
  for (long i = 0 ; i < RARRAY_LEN(intervals) ; ++i)
236
289
  {
237
290
  VALUE interval = rb_check_array_type(rb_ary_entry(intervals, i));
238
- if (NIL_P(interval) || RARRAY_LEN(interval) != 2)
239
- rb_raise(rb_eArgError, "expected an [a, b] interval at index %ld", i);
240
- rb_funcall(self, rb_intern("insert"), 2,
241
- rb_ary_entry(interval, 0), rb_ary_entry(interval, 1));
291
+ const long len = NIL_P(interval) ? 0 : RARRAY_LEN(interval);
292
+
293
+ if (len != 2 && len != 3)
294
+ rb_raise(rb_eArgError,
295
+ "expected an [a, b] or [a, b, obj] interval at index %ld", i);
296
+
297
+ rb_funcall(self, rb_intern("insert"), 3,
298
+ rb_ary_entry(interval, 0),
299
+ rb_ary_entry(interval, 1),
300
+ (len == 3) ? rb_ary_entry(interval, 2) : Qnil);
242
301
  }
243
302
  }
244
303
 
@@ -247,14 +306,17 @@ rb_dit_initialize(int argc, VALUE * argv, VALUE self)
247
306
 
248
307
  /* Insert `[a..b[`, returns `DIT_OK`, or raises unless `soft` is set */
249
308
  static dit_status_t
250
- rb_dit_do_insert(VALUE self, VALUE va, VALUE vb, int soft)
309
+ rb_dit_do_insert(int argc, VALUE * argv, VALUE self, int soft)
251
310
  {
311
+ VALUE va, vb, vobj;
312
+ rb_scan_args(argc, argv, "21", &va, &vb, &vobj);
313
+
252
314
  dit_t * tree = rb_dit_get_mutable(self);
253
315
 
254
316
  const dit_value_t a = rb_dit_value(va);
255
317
  const dit_value_t b = rb_dit_value(vb);
256
318
 
257
- const dit_status_t status = dit_insert(tree, a, b);
319
+ const dit_status_t status = dit_insert(tree, a, b, (dit_object_t) vobj);
258
320
 
259
321
  switch (status)
260
322
  {
@@ -287,9 +349,11 @@ rb_dit_do_insert(VALUE self, VALUE va, VALUE vb, int soft)
287
349
 
288
350
  /*
289
351
  * call-seq:
290
- * tree.insert(a, b) -> self
352
+ * tree.insert(a, b) -> self
353
+ * tree.insert(a, b, obj) -> self
291
354
  *
292
- * Insert the half-open interval `[a..b[`.
355
+ * Insert the half-open interval `[a..b[`, associated with `obj` - which
356
+ * defaults to nil, and which every query and traversal hands back.
293
357
  *
294
358
  * It is a usage contract that `[a..b[` must not overlap an already inserted
295
359
  * interval: an OverlapError is raised - and the tree is left unchanged - if
@@ -297,32 +361,33 @@ rb_dit_do_insert(VALUE self, VALUE va, VALUE vb, int soft)
297
361
  * overlap.
298
362
  */
299
363
  static VALUE
300
- rb_dit_insert(VALUE self, VALUE va, VALUE vb)
364
+ rb_dit_insert(int argc, VALUE * argv, VALUE self)
301
365
  {
302
- rb_dit_do_insert(self, va, vb, 0);
366
+ rb_dit_do_insert(argc, argv, self, 0);
303
367
  return self;
304
368
  }
305
369
 
306
370
  /*
307
371
  * call-seq:
308
- * tree.insert?(a, b) -> true or false
372
+ * tree.insert?(a, b) -> true or false
373
+ * tree.insert?(a, b, obj) -> true or false
309
374
  *
310
375
  * Same as #insert, but returns false instead of raising when `[a..b[`
311
376
  * overlaps an already inserted interval.
312
377
  */
313
378
  static VALUE
314
- rb_dit_insert_p(VALUE self, VALUE va, VALUE vb)
379
+ rb_dit_insert_p(int argc, VALUE * argv, VALUE self)
315
380
  {
316
- return (rb_dit_do_insert(self, va, vb, 1) == DIT_OK) ? Qtrue : Qfalse;
381
+ return (rb_dit_do_insert(argc, argv, self, 1) == DIT_OK) ? Qtrue : Qfalse;
317
382
  }
318
383
 
319
384
  /*
320
385
  * call-seq:
321
- * tree.intersect(a, b) { |x, y| ... } -> self
322
- * tree.intersect(a, b) -> array
386
+ * tree.intersect(a, b) { |x, y, obj| ... } -> self
387
+ * tree.intersect(a, b) -> array
323
388
  *
324
389
  * Yield every stored interval intersecting `[a..b[`, in increasing order.
325
- * Without a block, return them as an array of `[x, y]` pairs.
390
+ * Without a block, return them as an array of `[x, y, obj]` triples.
326
391
  *
327
392
  * The tree must not be modified from within the block.
328
393
  */
@@ -360,12 +425,12 @@ rb_dit_intersect_p(VALUE self, VALUE va, VALUE vb)
360
425
 
361
426
  /*
362
427
  * call-seq:
363
- * tree.remove(a, b) -> integer
364
- * tree.remove(a, b) { |x, y| ... } -> integer
428
+ * tree.remove(a, b) -> integer
429
+ * tree.remove(a, b) { |x, y, obj| ... } -> integer
365
430
  *
366
431
  * Remove every stored interval intersecting `[a..b[` and return how many were
367
- * removed. If a block is given, it is called with each removed interval once
368
- * the removal is done.
432
+ * removed. If a block is given, it is called with each removed interval - and
433
+ * its object - once the removal is done.
369
434
  *
370
435
  * Intervals are removed as a whole: an interval only partially covered by
371
436
  * `[a..b[` is removed entirely, never split.
@@ -387,7 +452,7 @@ rb_dit_remove(VALUE self, VALUE va, VALUE vb)
387
452
  rb_dit_traverse(tree, 0, a, b, rb_dit_cb_push, (void *) removed);
388
453
  }
389
454
 
390
- const size_t n = dit_remove(tree, a, b);
455
+ const size_t n = dit_remove(tree, a, b, NULL, NULL);
391
456
 
392
457
  if (!NIL_P(removed))
393
458
  {
@@ -395,7 +460,10 @@ rb_dit_remove(VALUE self, VALUE va, VALUE vb)
395
460
  for (long i = 0 ; i < RARRAY_LEN(removed) ; ++i)
396
461
  {
397
462
  VALUE interval = rb_ary_entry(removed, i);
398
- rb_yield_values(2, rb_ary_entry(interval, 0), rb_ary_entry(interval, 1));
463
+ rb_yield_values(3,
464
+ rb_ary_entry(interval, 0),
465
+ rb_ary_entry(interval, 1),
466
+ rb_ary_entry(interval, 2));
399
467
  }
400
468
  }
401
469
 
@@ -412,8 +480,8 @@ rb_dit_enum_size(VALUE self, VALUE args, VALUE eobj)
412
480
 
413
481
  /*
414
482
  * call-seq:
415
- * tree.each { |a, b| ... } -> self
416
- * tree.each -> enumerator
483
+ * tree.each { |a, b, obj| ... } -> self
484
+ * tree.each -> enumerator
417
485
  *
418
486
  * Yield every stored interval, in increasing order.
419
487
  */
@@ -433,7 +501,8 @@ rb_dit_each(VALUE self)
433
501
  * call-seq:
434
502
  * tree.to_a -> array
435
503
  *
436
- * Every stored interval, in increasing order, as an array of `[a, b]` pairs.
504
+ * Every stored interval, in increasing order, as an array of `[a, b, obj]`
505
+ * triples.
437
506
  */
438
507
  static VALUE
439
508
  rb_dit_to_a(VALUE self)
@@ -446,7 +515,7 @@ rb_dit_to_a(VALUE self)
446
515
 
447
516
  /*
448
517
  * call-seq:
449
- * tree.at(x) -> [a, b] or nil
518
+ * tree.at(x) -> [a, b, obj] or nil
450
519
  *
451
520
  * The stored interval containing the point `x`, or nil. O(log n).
452
521
  */
@@ -455,7 +524,25 @@ rb_dit_at(VALUE self, VALUE vx)
455
524
  {
456
525
  const dit_t * tree = rb_dit_get(self);
457
526
  const dit_node_t * node = dit_at(tree, rb_dit_value(vx));
458
- return node ? rb_dit_interval(node->a, node->b) : Qnil;
527
+ return node ? rb_dit_interval(node->a, node->b, node->obj) : Qnil;
528
+ }
529
+
530
+ /*
531
+ * call-seq:
532
+ * tree[x] -> obj or nil
533
+ *
534
+ * The object of the stored interval containing the point `x`, or nil when no
535
+ * interval covers it. O(log n).
536
+ *
537
+ * A nil return is ambiguous: use #at or #cover? to tell "no interval here"
538
+ * from "an interval whose object is nil".
539
+ */
540
+ static VALUE
541
+ rb_dit_aref(VALUE self, VALUE vx)
542
+ {
543
+ const dit_t * tree = rb_dit_get(self);
544
+ const dit_node_t * node = dit_at(tree, rb_dit_value(vx));
545
+ return node ? (VALUE) node->obj : Qnil;
459
546
  }
460
547
 
461
548
  /*
@@ -489,7 +576,7 @@ rb_dit_hull(VALUE self)
489
576
  if (!dit_hull(tree, &a, &b))
490
577
  return Qnil;
491
578
 
492
- return rb_dit_interval(a, b);
579
+ return rb_assoc_new(ULL2NUM(a), ULL2NUM(b));
493
580
  }
494
581
 
495
582
  /*
@@ -574,7 +661,8 @@ rb_dit_inspect(VALUE self)
574
661
  * call-seq:
575
662
  * tree.initialize_copy(other) -> self
576
663
  *
577
- * Called by #dup and #clone.
664
+ * Called by #dup and #clone. The copy is shallow: both trees end up sharing
665
+ * the same objects.
578
666
  */
579
667
  static VALUE
580
668
  rb_dit_initialize_copy(VALUE self, VALUE other)
@@ -597,16 +685,44 @@ rb_dit_initialize_copy(VALUE self, VALUE other)
597
685
  VALUE interval = rb_ary_entry(intervals, i);
598
686
  const dit_value_t a = rb_dit_value(rb_ary_entry(interval, 0));
599
687
  const dit_value_t b = rb_dit_value(rb_ary_entry(interval, 1));
600
- if (dit_insert(tree, a, b) != DIT_OK)
688
+
689
+ /* shallow copy: the two trees share the very same objects */
690
+ const dit_object_t obj = (dit_object_t) rb_ary_entry(interval, 2);
691
+
692
+ if (dit_insert(tree, a, b, obj) != DIT_OK)
601
693
  rb_raise(eError, "could not copy interval [%"PRIu64"..%"PRIu64"[", a, b);
602
694
  }
603
695
 
604
696
  return self;
605
697
  }
606
698
 
699
+ /* A C extension is only loadable by the ruby ABI it was compiled against.
700
+ * Nothing in the `require` path enforces that for a plain `.so` sitting in a
701
+ * load path - it just gets dlopen'd - and the mismatch then shows up as memory
702
+ * corruption somewhere else entirely. Fail loudly instead.
703
+ *
704
+ * `ruby_api_version` is the running interpreter's, `RUBY_API_VERSION_*` the
705
+ * headers this file was compiled with */
706
+ static void
707
+ rb_dit_check_abi(void)
708
+ {
709
+ if (ruby_api_version[0] == RUBY_API_VERSION_MAJOR &&
710
+ ruby_api_version[1] == RUBY_API_VERSION_MINOR)
711
+ return ;
712
+
713
+ rb_raise(rb_eLoadError,
714
+ "disjoint_interval_tree was compiled for ruby %d.%d but is being "
715
+ "loaded by ruby %d.%d. Rebuild the extension with that ruby: "
716
+ "`rake recompile`",
717
+ RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR,
718
+ ruby_api_version[0], ruby_api_version[1]);
719
+ }
720
+
607
721
  void
608
722
  Init_disjoint_interval_tree(void)
609
723
  {
724
+ rb_dit_check_abi();
725
+
610
726
  cTree = rb_define_class("DisjointIntervalTree", rb_cObject);
611
727
  rb_include_module(cTree, rb_mEnumerable);
612
728
 
@@ -623,14 +739,15 @@ Init_disjoint_interval_tree(void)
623
739
  rb_define_method(cTree, "initialize", rb_dit_initialize, -1);
624
740
  rb_define_method(cTree, "initialize_copy", rb_dit_initialize_copy, 1);
625
741
 
626
- rb_define_method(cTree, "insert", rb_dit_insert, 2);
627
- rb_define_method(cTree, "insert?", rb_dit_insert_p, 2);
742
+ rb_define_method(cTree, "insert", rb_dit_insert, -1);
743
+ rb_define_method(cTree, "insert?", rb_dit_insert_p, -1);
628
744
  rb_define_method(cTree, "intersect", rb_dit_intersect, 2);
629
745
  rb_define_method(cTree, "intersect?", rb_dit_intersect_p, 2);
630
746
  rb_define_method(cTree, "remove", rb_dit_remove, 2);
631
747
  rb_define_method(cTree, "each", rb_dit_each, 0);
632
748
  rb_define_method(cTree, "to_a", rb_dit_to_a, 0);
633
749
  rb_define_method(cTree, "at", rb_dit_at, 1);
750
+ rb_define_method(cTree, "[]", rb_dit_aref, 1);
634
751
  rb_define_method(cTree, "cover?", rb_dit_cover_p, 1);
635
752
  rb_define_method(cTree, "hull", rb_dit_hull, 0);
636
753
  rb_define_method(cTree, "size", rb_dit_size, 0);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DisjointIntervalTree
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -23,20 +23,25 @@ require 'disjoint_interval_tree/version'
23
23
  # `[0 .. DisjointIntervalTree::MAX[`.
24
24
  #
25
25
  # tree = DisjointIntervalTree.new
26
- # tree.insert(0, 10)
27
- # tree.insert(20, 30)
26
+ # tree.insert(0, 10, :first)
27
+ # tree.insert(20, 30, :second)
28
28
  #
29
- # tree.intersect(5, 25) { |a, b| puts "[#{a}..#{b}[" }
30
- # # => [0..10[
31
- # # => [20..30[
29
+ # tree.intersect(5, 25) { |a, b, obj| puts "[#{a}..#{b}[ #{obj}" }
30
+ # # => [0..10[ first
31
+ # # => [20..30[ second
32
32
  #
33
+ # tree[5] # => :first
33
34
  # tree.remove(5, 25) # => 2
34
35
  # tree.to_a # => []
35
36
  #
37
+ # Each interval carries an object, given at insertion - it defaults to nil -
38
+ # and handed back by every query and traversal.
39
+ #
36
40
  # Inserting an interval overlapping an already inserted one is a usage
37
41
  # contract violation and raises DisjointIntervalTree::OverlapError.
38
42
  class DisjointIntervalTree
39
- # Two trees are equal when they hold the same intervals.
43
+ # Two trees are equal when they hold the same intervals, associated with
44
+ # objects comparing equal.
40
45
  def ==(other)
41
46
  other.is_a?(DisjointIntervalTree) && to_a == other.to_a
42
47
  end