farce 0.1.0-aarch64-linux-gnu → 0.2.0-aarch64-linux-gnu

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -1
  3. data/README.md +72 -16
  4. data/docs/gems/dry-types.md +8 -8
  5. data/docs/modes.md +67 -10
  6. data/ext/farce/atom.c +37 -22
  7. data/ext/farce/bounded_map.c +17 -13
  8. data/ext/farce/containers.h +15 -0
  9. data/ext/farce/counter.c +13 -18
  10. data/ext/farce/exchanger.c +5 -1
  11. data/ext/farce/farce.c +1 -1
  12. data/ext/farce/flag.c +39 -3
  13. data/ext/farce/io.c +3 -3
  14. data/ext/farce/lock.c +17 -23
  15. data/ext/farce/map.c +88 -66
  16. data/ext/farce/priority_queue.c +55 -43
  17. data/ext/farce/queue.c +10 -4
  18. data/ext/farce/reactor.c +52 -25
  19. data/ext/farce/reactor.h +1 -1
  20. data/ext/farce/signal.c +5 -1
  21. data/ext/farce/transaction.c +20 -24
  22. data/ext/farce/tree_map.c +33 -18
  23. data/ext/farce/trie.c +81 -43
  24. data/ext/farce/unshareable.c +168 -19
  25. data/ext/farce/unshared_io_pool.h +21 -12
  26. data/ext/farce/unshared_signal.c +13 -2
  27. data/ext/farce/unshared_wait.h +3 -0
  28. data/ext/farce/vector.c +82 -18
  29. data/ext/farce/weak_map.c +75 -63
  30. data/lib/farce/_yard/macros.rb +18 -3
  31. data/lib/farce/abstract/flag.rb +73 -0
  32. data/lib/farce/abstract/queue.rb +4 -3
  33. data/lib/farce/abstract/scheduler.rb +3 -3
  34. data/lib/farce/abstract/set.rb +17 -5
  35. data/lib/farce/abstract/sorted_set.rb +2 -2
  36. data/lib/farce/abstract/timer_queue.rb +49 -10
  37. data/lib/farce/abstract/vector.rb +510 -28
  38. data/lib/farce/atom.rb +1 -1
  39. data/lib/farce/class_mirror.rb +20 -7
  40. data/lib/farce/config.rb +7 -1
  41. data/lib/farce/engine/jvm/flag.rb +15 -3
  42. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  43. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  44. data/lib/farce/engine/ruby/shared/require_hook.rb +18 -0
  45. data/lib/farce/engine/ruby.rb +7 -0
  46. data/lib/farce/engine/shared/unshared_vector.rb +31 -4
  47. data/lib/farce/engine/shared/vector.rb +8 -0
  48. data/lib/farce/engine/shared/weak_map/index.rb +14 -1
  49. data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
  50. data/lib/farce/envelope.rb +54 -7
  51. data/lib/farce/exchanger.rb +1 -1
  52. data/lib/farce/flag.rb +2 -0
  53. data/lib/farce/integrations/active_support.rb +1 -1
  54. data/lib/farce/integrations/bson.rb +1 -1
  55. data/lib/farce/integrations/cbor.rb +1 -1
  56. data/lib/farce/integrations/concurrent.rb +3 -3
  57. data/lib/farce/integrations/date.rb +8 -0
  58. data/lib/farce/integrations/dry_types.rb +7 -4
  59. data/lib/farce/integrations/json.rb +2 -2
  60. data/lib/farce/integrations/msgpack.rb +1 -1
  61. data/lib/farce/integrations/oj.rb +3 -4
  62. data/lib/farce/integrations/psych.rb +1 -1
  63. data/lib/farce/integrations/ractor_sharing.rb +1 -1
  64. data/lib/farce/integrations/sorted_set.rb +1 -1
  65. data/lib/farce/integrations/weakref.rb +9 -1
  66. data/lib/farce/integrations/yajl.rb +3 -3
  67. data/lib/farce/integrations.rb +33 -23
  68. data/lib/farce/internal/flag_waiting.rb +30 -0
  69. data/lib/farce/internal/freeze.rb +8 -3
  70. data/lib/farce/internal/map_value_modes.rb +1 -1
  71. data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
  72. data/lib/farce/lazy.rb +4 -4
  73. data/lib/farce/lazy_ref.rb +3 -3
  74. data/lib/farce/lfu_map.rb +1 -1
  75. data/lib/farce/local/flag.rb +4 -0
  76. data/lib/farce/local/scoped.rb +1 -1
  77. data/lib/farce/lru_map.rb +1 -1
  78. data/lib/farce/map.rb +2 -2
  79. data/lib/farce/mode_manager/smart_rule.rb +138 -0
  80. data/lib/farce/mode_manager.rb +12 -3
  81. data/lib/farce/molecule.rb +2 -2
  82. data/lib/farce/pool.rb +1 -1
  83. data/lib/farce/port.rb +4 -4
  84. data/lib/farce/priority_queue.rb +1 -1
  85. data/lib/farce/proxy/register.rb +6 -0
  86. data/lib/farce/proxy/supervisor.rb +1 -1
  87. data/lib/farce/proxy.rb +7 -2
  88. data/lib/farce/queue.rb +1 -1
  89. data/lib/farce/scheduler.rb +2 -2
  90. data/lib/farce/set.rb +1 -1
  91. data/lib/farce/sorted_set.rb +1 -1
  92. data/lib/farce/timer_queue.rb +20 -6
  93. data/lib/farce/transaction/vector.rb +43 -9
  94. data/lib/farce/transaction/wrapper.rb +14 -0
  95. data/lib/farce/tree_map.rb +1 -1
  96. data/lib/farce/unsafe/lfu_map.rb +2 -0
  97. data/lib/farce/unsafe/lru_map.rb +2 -0
  98. data/lib/farce/unsafe/tree_map.rb +2 -0
  99. data/lib/farce/unshareable.rb +8 -19
  100. data/lib/farce/unshared/atom.rb +3 -0
  101. data/lib/farce/unshared/lfu_map.rb +2 -0
  102. data/lib/farce/unshared/lru_map.rb +2 -0
  103. data/lib/farce/unshared/map.rb +3 -0
  104. data/lib/farce/unshared/set.rb +1 -0
  105. data/lib/farce/unshared/sorted_set.rb +1 -0
  106. data/lib/farce/unshared/tree_map.rb +5 -1
  107. data/lib/farce/unshared/vector.rb +3 -0
  108. data/lib/farce/unshared/weak_atom.rb +5 -1
  109. data/lib/farce/unshared/weak_key_map.rb +3 -0
  110. data/lib/farce/unshared/weak_map.rb +3 -0
  111. data/lib/farce/unshared/weak_set.rb +1 -0
  112. data/lib/farce/unshared/weak_value_map.rb +3 -0
  113. data/lib/farce/vector.rb +44 -19
  114. data/lib/farce/version.rb +1 -1
  115. data/lib/farce.rb +11 -8
  116. metadata +5 -1
@@ -57,9 +57,49 @@ module Farce
57
57
  # @return [Array<BasicObject>] A new Array of logical values.
58
58
  def to_a = internal_vector.snapshot.map! { logical_value(it) }
59
59
 
60
+ # Return an Array snapshot for implicit Array conversion, such as `[].concat(vector)`.
61
+ # @return [Array<BasicObject>] A new Array of logical values.
62
+ def to_ary = to_a
63
+
60
64
  # (see #to_a)
61
65
  def deconstruct = to_a
62
66
 
67
+ # Remove all matching values using the configured comparison mode.
68
+ # Other writers wait until the comparison finishes. Reads can observe the original values.
69
+ # Comparison callbacks must not write to this vector.
70
+ # @param value [BasicObject] The value to remove.
71
+ # @yield [value] Called only when no entry matches, after releasing update access.
72
+ # @yieldparam value [BasicObject] The requested value.
73
+ # @yieldreturn [BasicObject] The fallback result.
74
+ # @return [BasicObject, nil] The last removed value, or the fallback result. Nil if absent without a block.
75
+ def delete(value)
76
+ expected = deletion_comparison(value)
77
+ removed = UNDEFINED
78
+ mutate_vector do |values|
79
+ values.delete_if do |stored|
80
+ next false unless deletion_match?(stored, expected)
81
+ removed = stored
82
+ true
83
+ end
84
+ end
85
+ return logical_value(removed) unless removed.equal?(UNDEFINED)
86
+ yield(value) if block_given?
87
+ end
88
+
89
+ # Atomically remove entries for which the block returns a truthy value.
90
+ # The block runs without a collection lock. Other writers wait for update access.
91
+ # The block must not write to this vector.
92
+ # Reads can observe the original values. If the block raises, no entries are removed.
93
+ # @yield [value] Called once for each logical value. Returns an Enumerator without a block.
94
+ # @yieldparam value [BasicObject] The current value.
95
+ # @yieldreturn [BasicObject] A truthy value to remove the entry.
96
+ # @return [self, Enumerator]
97
+ def delete_if
98
+ return enum_for(__method__) { size } unless block_given?
99
+ mutate_vector { |values| values.delete_if { yield logical_value(it) } }
100
+ self
101
+ end
102
+
63
103
  # Convert pair-like entries to a Hash. Vector entries are accepted as pairs.
64
104
  # @yield [value] Optionally transform each entry into a key-value pair.
65
105
  # @yieldparam value [BasicObject] The current entry.
@@ -162,7 +202,7 @@ module Farce
162
202
  build_derived_vector(snapshot.last(count))
163
203
  end
164
204
 
165
- # Return an element or subsequence without changing the hot path for {#[]}.
205
+ # Return an element or subsequence from a snapshot.
166
206
  # Range and start-length results are Vectors.
167
207
  # @param index [Integer, Range] An index, range, or start index when length is supplied.
168
208
  # @param length [Integer] The maximum length of a subsequence. Omit for a single index or range.
@@ -330,6 +370,222 @@ module Farce
330
370
  end
331
371
  alias collect_concat flat_map
332
372
 
373
+ # Replace each entry with the block result, publishing all replacements together.
374
+ # The block must not write to this vector. Failure leaves the stored entries unchanged.
375
+ # @yield [value] Transform each logical value. Returns an Enumerator without a block.
376
+ # @yieldparam value [BasicObject] The current value.
377
+ # @yieldreturn [BasicObject] The replacement value.
378
+ # @return [self, Enumerator]
379
+ def map!
380
+ return enum_for(__method__) { size } unless block_given?
381
+ mutate_vector do |values|
382
+ logical = values.map { logical_value(it) }
383
+ mapped = logical.map { yield it }
384
+ values.replace(storage_from_logical(mapped, values, logical))
385
+ end
386
+ self
387
+ end
388
+ alias collect! map!
389
+
390
+ # Keep entries accepted by the block, publishing the result atomically.
391
+ # The block must not write to this vector.
392
+ # @yield [value] Test each logical value. Returns an Enumerator without a block.
393
+ # @yieldparam value [BasicObject] The current value.
394
+ # @yieldreturn [BasicObject] A truthy value to retain the entry.
395
+ # @return [self, Enumerator]
396
+ def keep_if
397
+ return enum_for(__method__) { size } unless block_given?
398
+ mutate_vector { |values| values.keep_if { yield logical_value(it) } }
399
+ self
400
+ end
401
+
402
+ # Keep entries accepted by the block, returning nil if no entry was removed.
403
+ # The block must not write to this vector.
404
+ # @yield [value] Test each logical value. Returns an Enumerator without a block.
405
+ # @yieldparam value [BasicObject] The current value.
406
+ # @yieldreturn [BasicObject] A truthy value to retain the entry.
407
+ # @return [self, nil, Enumerator]
408
+ def select!
409
+ return enum_for(__method__) { size } unless block_given?
410
+ changed = mutate_vector { |values| values.select! { yield logical_value(it) } }
411
+ self if changed
412
+ end
413
+ alias filter! select!
414
+
415
+ # Remove entries accepted by the block, returning nil if no entry was removed.
416
+ # The block must not write to this vector.
417
+ # @yield [value] Test each logical value. Returns an Enumerator without a block.
418
+ # @yieldparam value [BasicObject] The current value.
419
+ # @yieldreturn [BasicObject] A truthy value to remove the entry.
420
+ # @return [self, nil, Enumerator]
421
+ def reject!
422
+ return enum_for(__method__) { size } unless block_given?
423
+ changed = mutate_vector { |values| values.reject! { yield logical_value(it) } }
424
+ self if changed
425
+ end
426
+
427
+ # Remove nil entries atomically.
428
+ # @return [self, nil] Self if entries were removed, otherwise nil.
429
+ def compact!
430
+ changed = mutate_vector(&:compact!)
431
+ self if changed
432
+ end
433
+
434
+ # Remove duplicate entries atomically, retaining the first stored entry.
435
+ # The block must not write to this vector.
436
+ # @yield [value] Optionally compute a key for each logical value.
437
+ # @yieldparam value [BasicObject] The current value.
438
+ # @yieldreturn [BasicObject] The key compared using `hash` and `eql?`.
439
+ # @return [self, nil] Self if entries were removed, otherwise nil.
440
+ def uniq!
441
+ changed = mutate_vector do |values|
442
+ values.uniq! { block_given? ? yield(logical_value(it)) : logical_value(it) }
443
+ end
444
+ self if changed
445
+ end
446
+
447
+ # Reverse all entries atomically.
448
+ # @return [self]
449
+ def reverse!
450
+ mutate_vector(&:reverse!)
451
+ self
452
+ end
453
+
454
+ # Rotate all entries atomically.
455
+ # @param count [Integer] The rotation distance. Negative values rotate right.
456
+ # @return [self]
457
+ def rotate!(count = 1)
458
+ mutate_vector { it.rotate!(count) }
459
+ self
460
+ end
461
+
462
+ # Shuffle all entries atomically without transferring retained values again.
463
+ # @param random [#rand] The random number generator.
464
+ # @return [self]
465
+ def shuffle!(random: Random)
466
+ mutate_vector { it.shuffle!(random:) }
467
+ self
468
+ end
469
+
470
+ # Sort all entries atomically using logical values.
471
+ # The comparison block must not write to this vector.
472
+ # @yield [left, right] Optionally compare two entries. Otherwise uses `<=>`.
473
+ # @yieldparam left [BasicObject] The left entry.
474
+ # @yieldparam right [BasicObject] The right entry.
475
+ # @yieldreturn [Numeric] A negative number, zero, or a positive number.
476
+ # @return [self]
477
+ def sort!(&block)
478
+ mutate_vector do |values|
479
+ logical = values.map { logical_value(it) }
480
+ sorted = block ? logical.sort(&block) : logical.sort
481
+ values.replace(storage_from_logical(sorted, values, logical))
482
+ end
483
+ self
484
+ end
485
+
486
+ # Sort all entries atomically by the block result.
487
+ # The block must not write to this vector.
488
+ # @yield [value] Compute a key. Returns an Enumerator without a block.
489
+ # @yieldparam value [BasicObject] The current logical value.
490
+ # @yieldreturn [BasicObject] The sort key.
491
+ # @return [self, Enumerator]
492
+ def sort_by!
493
+ return enum_for(__method__) { size } unless block_given?
494
+ mutate_vector { |values| values.sort_by! { yield logical_value(it) } }
495
+ self
496
+ end
497
+
498
+ # Expand nested Arrays and Vectors into a new Vector.
499
+ # @param level [Integer] The maximum depth. Negative values expand every level.
500
+ # @return [Vector] The flattened logical values.
501
+ # @raise [ArgumentError] If the expanded graph is recursive.
502
+ def flatten(level = -1)
503
+ snapshot = internal_vector.snapshot
504
+ logical = snapshot.map { logical_value(it) }
505
+ retained = retained_storage_index(snapshot, logical)
506
+ flattened = flatten_vector_values(logical, level, retained)
507
+ build_derived_vector(sequence_storage(flattened, retained))
508
+ end
509
+
510
+ # Expand nested Arrays and Vectors atomically.
511
+ # @param level [Integer] The maximum depth. Negative values expand every level.
512
+ # @return [self, nil] Self if nesting was expanded, otherwise nil.
513
+ # @raise [ArgumentError] If the expanded graph is recursive.
514
+ def flatten!(level = -1)
515
+ changed = mutate_vector do |values|
516
+ logical = values.map { logical_value(it) }
517
+ retained = retained_storage_index(values, logical)
518
+ flattened = flatten_vector_values(logical, level, retained)
519
+ next if flattened.equal?(logical)
520
+ values.replace(sequence_storage(flattened, retained))
521
+ end
522
+ self if changed
523
+ end
524
+
525
+ # Transpose rows represented by Arrays or Vectors.
526
+ # @return [Vector] A Vector containing same-kind column Vectors.
527
+ # @raise [IndexError] If rows have different lengths.
528
+ def transpose
529
+ retained = {}.compare_by_identity
530
+ rows = to_a.map { sequence_operand(it, retained) }
531
+ columns = rows.transpose.map { build_derived_vector(sequence_storage(it, retained)) }
532
+ build_derived_values(columns)
533
+ end
534
+
535
+ # Produce tuples containing one entry from each sequence.
536
+ # @param others [Array<Vector, #to_ary>] The other sequences.
537
+ # @yield [tuple] Optionally visit each tuple instead of collecting results.
538
+ # @yieldparam tuple [Vector] A same-kind Vector containing the tuple.
539
+ # @yieldreturn [void] The result is ignored.
540
+ # @return [Vector, self] Collected tuples, or self when a block is supplied.
541
+ def product(*others)
542
+ snapshot = internal_vector.snapshot
543
+ logical = snapshot.map { logical_value(it) }
544
+ retained = retained_storage_index(snapshot, logical)
545
+ operands = others.map { sequence_operand(it, retained) }
546
+ if block_given?
547
+ logical.product(*operands) { yield build_derived_vector(sequence_storage(it, retained)) }
548
+ self
549
+ else
550
+ tuples = logical.product(*operands).map { build_derived_vector(sequence_storage(it, retained)) }
551
+ build_derived_values(tuples)
552
+ end
553
+ end
554
+
555
+ # Visit combinations of entries in a snapshot.
556
+ # @param count [Integer] The number of entries per combination.
557
+ # @yield [tuple] Visit each combination. Returns a sized Enumerator without a block.
558
+ # @yieldparam tuple [Vector] A same-kind Vector containing the combination.
559
+ # @yieldreturn [void] The result is ignored.
560
+ # @return [self, Enumerator]
561
+ def combination(count, &block) = vector_combinations(__method__, [count], block)
562
+
563
+ # Visit combinations that may reuse entries in a snapshot.
564
+ # @param count [Integer] The number of entries per combination.
565
+ # @yield [tuple] Visit each combination. Returns a sized Enumerator without a block.
566
+ # @yieldparam tuple [Vector] A same-kind Vector containing the combination.
567
+ # @yieldreturn [void] The result is ignored.
568
+ # @return [self, Enumerator]
569
+ def repeated_combination(count, &block) = vector_combinations(__method__, [count], block)
570
+
571
+ # Visit permutations of entries in a snapshot.
572
+ # @param count [Integer] The number of entries per permutation. Defaults to the vector's length.
573
+ # @yield [tuple] Visit each permutation. Returns a sized Enumerator without a block.
574
+ # @yieldparam tuple [Vector] A same-kind Vector containing the permutation.
575
+ # @yieldreturn [void] The result is ignored.
576
+ # @return [self, Enumerator]
577
+ def permutation(count = UNDEFINED, &block)
578
+ vector_combinations(__method__, count.equal?(UNDEFINED) ? [] : [count], block)
579
+ end
580
+
581
+ # Visit permutations that may reuse entries in a snapshot.
582
+ # @param count [Integer] The number of entries per permutation.
583
+ # @yield [tuple] Visit each permutation. Returns a sized Enumerator without a block.
584
+ # @yieldparam tuple [Vector] A same-kind Vector containing the permutation.
585
+ # @yieldreturn [void] The result is ignored.
586
+ # @return [self, Enumerator]
587
+ def repeated_permutation(count, &block) = vector_combinations(__method__, [count], block)
588
+
333
589
  # Remove nil values.
334
590
  # @return [Vector] A new same-kind Vector containing the result.
335
591
  def compact = build_derived_vector(internal_vector.each.compact)
@@ -803,17 +1059,27 @@ module Farce
803
1059
  # @return [Vector] A new same-kind Vector containing the result.
804
1060
  def entries = build_derived_vector(internal_vector.snapshot)
805
1061
 
806
- # Read an index without waiting for atomic-update access.
807
- # @param index [Integer] The index. Negative indexes count from the end.
808
- # @return [BasicObject, nil] The value, or nil for an index outside the vector.
809
- def [](index) = internal_vector[index]
810
-
811
- # Store a value, growing the vector if necessary.
812
- # @param index [Integer] The index. Negative indexes count from the end.
813
- # @param value [BasicObject] The value to store.
814
- # @return [BasicObject] The assigned value.
815
- def []=(index, value)
816
- internal_vector[index] = value
1062
+ # Read an index without waiting for atomic-update access, or return a slice snapshot.
1063
+ # @param index [Integer, Range] The index, start index, or range. Negative indexes count from the end.
1064
+ # @param length [Integer] The slice length. Omit for a single index or range.
1065
+ # @return [BasicObject, Vector, nil] The logical value, a slice Vector, or nil if absent.
1066
+ def [](index, length = UNDEFINED)
1067
+ return internal_vector[index] if length.equal?(UNDEFINED) && !index.is_a?(Range)
1068
+ slice(index, length)
1069
+ end
1070
+
1071
+ # Store a value or replace a slice atomically, growing the vector if necessary.
1072
+ # Slice replacements accept a Vector, Array-like object, or one scalar value.
1073
+ # @param index [Integer, Range] The index, start index, or range.
1074
+ # @param value [BasicObject, Integer] The assigned value, or the slice length for three arguments.
1075
+ # @param replacement [BasicObject] The replacement for the three-argument form.
1076
+ # @return [BasicObject] The assigned value or replacement sequence.
1077
+ def []=(index, value, replacement = UNDEFINED)
1078
+ if replacement.equal?(UNDEFINED) && !index.is_a?(Range)
1079
+ internal_vector[index] = value
1080
+ else
1081
+ assign_vector_slice(index, value, replacement)
1082
+ end
817
1083
  end
818
1084
 
819
1085
  # Read an index after acquiring atomic-update access.
@@ -829,12 +1095,18 @@ module Farce
829
1095
  # @return [BasicObject, false] The value, or false on timeout.
830
1096
  def store(index, value, timeout: nil) = internal_vector.store(index, value, timeout:)
831
1097
 
832
- # Append a single value.
833
- # @param value [BasicObject] The value to append.
1098
+ # Append values atomically. Calls with one value use the scalar backend path.
1099
+ # @param value [BasicObject] The first value. Omit to leave contents unchanged.
1100
+ # @param rest [Array<BasicObject>] Additional values to append.
834
1101
  # @param timeout [Numeric, nil] The maximum wait in seconds. Nil waits indefinitely.
835
1102
  # @return [self, false] Self on success, or false on timeout.
836
- def push(value, timeout: nil)
837
- internal_vector.push(value, timeout:) ? self : false
1103
+ def push(value = UNDEFINED, *rest, timeout: nil)
1104
+ if rest.empty? && !value.equal?(UNDEFINED)
1105
+ backend = internal_vector
1106
+ completed = timeout.nil? ? backend.push(value) : backend.push(value, timeout:)
1107
+ return completed ? self : false
1108
+ end
1109
+ push_values(value.equal?(UNDEFINED) ? rest : [value, *rest], timeout:)
838
1110
  end
839
1111
 
840
1112
  # Append snapshots of one or more sequences and return this vector.
@@ -862,18 +1134,107 @@ module Farce
862
1134
  # @return [self]
863
1135
  def <<(value) = push(value)
864
1136
 
865
- # Append one value using the same options as {#push}.
866
- # @overload append(value, timeout: nil)
867
- # @param value [BasicObject] The value to append.
1137
+ # Append values using the same options as {#push}.
1138
+ # @overload append(*values, timeout: nil)
1139
+ # @param values [Array<BasicObject>] The values to append.
868
1140
  # @param timeout [Numeric, nil] The maximum wait in seconds. Nil waits indefinitely.
869
1141
  # @return [self, false] Self on success, or false on timeout.
870
1142
  def append(...) = push(...)
871
1143
 
872
- # Remove and return the last value.
1144
+ # Remove and return the last value, or a Vector of the last count values, atomically.
873
1145
  # This does not wait for an empty vector to become nonempty.
1146
+ # @param count [Integer] The maximum number of values. Omit to return one value.
874
1147
  # @param timeout [Numeric, nil] The maximum wait in seconds. Nil waits indefinitely.
875
- # @return [BasicObject, nil] The last value, or nil if empty or timed out.
876
- def pop(timeout: nil) = internal_vector.pop(timeout:)
1148
+ # @return [BasicObject, Vector, nil] Removed values, or nil if empty without a count or timed out.
1149
+ def pop(count = UNDEFINED, timeout: nil)
1150
+ if count.equal?(UNDEFINED)
1151
+ backend = internal_vector
1152
+ return timeout.nil? ? backend.pop : backend.pop(timeout:)
1153
+ end
1154
+ pop_values(count, timeout:)
1155
+ end
1156
+
1157
+ # Remove the first entry, or the first count entries, atomically.
1158
+ # @param count [Integer] The maximum number of entries. Omit to return one entry.
1159
+ # @return [BasicObject, Vector, nil] The first entry, nil if empty, or a Vector of removed entries.
1160
+ def shift(count = UNDEFINED)
1161
+ result = mutate_vector { count.equal?(UNDEFINED) ? it.shift : it.shift(count) }
1162
+ count.equal?(UNDEFINED) ? logical_value(result) : build_derived_vector(result)
1163
+ end
1164
+
1165
+ # Prepend values atomically using the vector's default transfer mode.
1166
+ # @param values [Array<BasicObject>] The values to prepend.
1167
+ # @return [self]
1168
+ def unshift(*values)
1169
+ mutate_vector { it.unshift(*values.map { derived_storage(it) }) }
1170
+ self
1171
+ end
1172
+ alias prepend unshift
1173
+
1174
+ # Insert values before an index atomically. Negative indexes count from the end.
1175
+ # @param index [Integer] The insertion index. -1 appends values.
1176
+ # @param values [Array<BasicObject>] The values to insert.
1177
+ # @return [self]
1178
+ def insert(index, *values)
1179
+ mutate_vector { it.insert(index, *values.map { derived_storage(it) }) }
1180
+ self
1181
+ end
1182
+
1183
+ # Remove the entry at an index and shift later entries left atomically.
1184
+ # @param index [Integer] The index. Negative indexes count from the end.
1185
+ # @return [BasicObject, nil] The removed logical value, or nil if absent.
1186
+ def delete_at(index) = logical_value(mutate_vector { it.delete_at(index) })
1187
+
1188
+ # Remove a range of entries atomically.
1189
+ # @param index [Integer, Range] The index, start index, or range.
1190
+ # @param length [Integer] The number of entries. Omit for a single index or range.
1191
+ # @return [BasicObject, Vector, nil] The removed entry, a Vector of removed entries, or nil if absent.
1192
+ def slice!(index, length = UNDEFINED)
1193
+ result = mutate_vector { length.equal?(UNDEFINED) ? it.slice!(index) : it.slice!(index, length) }
1194
+ return if result.nil?
1195
+ length.equal?(UNDEFINED) && !index.is_a?(Range) ? logical_value(result) : build_derived_vector(result)
1196
+ end
1197
+
1198
+ # Replace all entries atomically with the contents of another sequence.
1199
+ # @param source [Vector, #to_ary] The replacement sequence.
1200
+ # @return [self]
1201
+ def replace(source)
1202
+ mutate_vector { |values| values.replace(source.equal?(self) ? values : operand_storage(source)) }
1203
+ self
1204
+ end
1205
+
1206
+ # Fill all or part of the vector atomically, growing it when necessary.
1207
+ # The block must not write to this vector.
1208
+ # @overload fill(value, start = 0, length = size)
1209
+ # @param value [BasicObject] The fill value.
1210
+ # @param start [Integer, Range] The start index or range.
1211
+ # @param length [Integer] The number of entries to fill.
1212
+ # @overload fill(start = 0, length = size) { |index| ... }
1213
+ # @param start [Integer, Range] The start index or range.
1214
+ # @param length [Integer] The number of entries to fill.
1215
+ # @yield [index] Compute a value for each filled position.
1216
+ # @yieldparam index [Integer] The current index.
1217
+ # @yieldreturn [BasicObject] The fill value.
1218
+ # @return [self]
1219
+ def fill(*arguments)
1220
+ mutate_vector do |values|
1221
+ if block_given?
1222
+ values.fill(*arguments) { derived_storage(yield(it)) }
1223
+ else
1224
+ raise ArgumentError, "wrong number of arguments (given 0, expected 1..3)" if arguments.empty?
1225
+ values.fill(derived_storage(arguments.first), *arguments.drop(1))
1226
+ end
1227
+ end
1228
+ self
1229
+ end
1230
+
1231
+ # Compare logical entries lexicographically, as with Array.
1232
+ # @param other [Vector, #to_ary] The sequence to compare against.
1233
+ # @return [Integer, nil] A negative number, zero, a positive number, or nil if incomparable.
1234
+ def <=>(other)
1235
+ return 0 if equal?(other)
1236
+ compare_vector(other)
1237
+ end
877
1238
 
878
1239
  # Replace an index and return its previous value, growing the vector if necessary.
879
1240
  # @param index [Integer] The index. Negative indexes count from the end.
@@ -1011,6 +1372,12 @@ module Farce
1011
1372
  # Convert a stored value into the value visible through the public API.
1012
1373
  def logical_value(value) = value
1013
1374
 
1375
+ def deletion_comparison(value) = value
1376
+
1377
+ def deletion_match?(stored, expected)
1378
+ compare_by_identity? ? stored.equal?(expected) : array_value_equal?(stored, expected)
1379
+ end
1380
+
1014
1381
  # Build a new same-kind Vector from already prepared stored values.
1015
1382
  def build_derived_vector(values)
1016
1383
  self.class.new(values, compare_by_identity: compare_by_identity?)
@@ -1023,17 +1390,132 @@ module Farce
1023
1390
 
1024
1391
  private
1025
1392
 
1393
+ def mutate_vector(timeout: nil)
1394
+ result = nil
1395
+ completed = internal_vector.batch_update(timeout:) { |values| result = yield values }
1396
+ completed ? result : UNDEFINED
1397
+ end
1398
+
1399
+ def push_values(values, timeout: nil, mode: nil)
1400
+ result = mutate_vector(timeout:) { it.concat(values.map { prepare_vector_value(it, mode:) }) }
1401
+ result.equal?(UNDEFINED) ? false : self
1402
+ end
1403
+
1404
+ def prepare_vector_value(value, **) = derived_storage(value)
1405
+
1406
+ def pop_values(count, timeout: nil)
1407
+ result = mutate_vector(timeout:) { it.pop(count) }
1408
+ build_derived_vector(result) unless result.equal?(UNDEFINED)
1409
+ end
1410
+
1411
+ def operand_storage(source)
1412
+ reusable_operand_snapshot(source) || vector_operand(source).map { derived_storage(it) }
1413
+ end
1414
+
1415
+ def assign_vector_slice(index, value, replacement)
1416
+ assigned = replacement.equal?(UNDEFINED) ? value : replacement
1417
+ mutate_vector do |values|
1418
+ stored = if assigned.equal?(self)
1419
+ values.dup
1420
+ elsif assigned.is_a?(Vector)
1421
+ operand_storage(assigned)
1422
+ else
1423
+ converted = Array.try_convert(assigned)
1424
+ converted ? converted.map { derived_storage(it) } : [derived_storage(assigned)]
1425
+ end
1426
+ if replacement.equal?(UNDEFINED)
1427
+ values[index] = stored
1428
+ else
1429
+ values[index, value] = stored
1430
+ end
1431
+ end
1432
+ assigned
1433
+ end
1434
+
1435
+ def storage_from_logical(values, source_storage, source_values)
1436
+ retained = retained_storage_index(source_storage, source_values)
1437
+ values.map { |value| retained.fetch(value) { derived_storage(value) } }
1438
+ end
1439
+
1440
+ def retained_storage_index(storage, logical)
1441
+ retained = {}.compare_by_identity
1442
+ logical.each_with_index { |value, index| retained[value] ||= storage[index] }
1443
+ retained
1444
+ end
1445
+
1446
+ def sequence_storage(values, retained)
1447
+ values.map { |value| retained.fetch(value) { prepare_sequence_value(value) } }
1448
+ end
1449
+
1450
+ def prepare_sequence_value(value) = derived_storage(value)
1451
+
1452
+ def sequence_operand(value, retained)
1453
+ storage = reusable_operand_snapshot(value) if value.is_a?(Vector)
1454
+ return vector_operand(value) unless storage
1455
+ logical = storage.map { logical_value(it) }
1456
+ logical.each_with_index { |entry, index| retained[entry] ||= storage[index] }
1457
+ logical
1458
+ end
1459
+
1460
+ def flatten_vector_values(logical, level, retained)
1461
+ level = convert_vector_count(level)
1462
+ return logical if level.zero?
1463
+ seen = {}.compare_by_identity
1464
+ expanded = false
1465
+ result = []
1466
+ stack = [[logical, 0, level, nil]]
1467
+ until stack.empty?
1468
+ values, index, depth, owner = stack.last
1469
+ if index >= values.length
1470
+ seen.delete(owner) if owner
1471
+ stack.pop
1472
+ next
1473
+ end
1474
+ value = values[index]
1475
+ stack.last[1] += 1
1476
+ nested = unless depth.zero?
1477
+ value.is_a?(Vector) ? sequence_operand(value, retained) : Array.try_convert(value)
1478
+ end
1479
+ unless nested
1480
+ result << value
1481
+ next
1482
+ end
1483
+ raise ArgumentError, "tried to flatten recursive array" if seen.key?(value)
1484
+ expanded = true
1485
+ seen[value] = true
1486
+ stack << [nested, 0, depth.negative? ? -1 : depth - 1, value]
1487
+ end
1488
+ expanded ? result : logical
1489
+ end
1490
+
1491
+ def vector_combinations(method, arguments, block)
1492
+ return enum_for(method, *arguments) { to_a.public_send(method, *arguments).size } unless block
1493
+ snapshot = internal_vector.snapshot
1494
+ logical = snapshot.map { logical_value(it) }
1495
+ logical.public_send(method, *arguments) { block.call(build_derived_from_logical(it, snapshot, logical)) }
1496
+ self
1497
+ end
1498
+
1499
+ def compare_vector(other)
1500
+ guard = Internal::Storage.fiber.store_if_absent(:farce_vector_comparison) { {}.compare_by_identity }
1501
+ peers = (guard[self] ||= {}.compare_by_identity)
1502
+ return 0 if peers.key?(other)
1503
+ peers[other] = true
1504
+ entered = true
1505
+ to_a <=> (other.is_a?(Vector) ? other.to_a : other)
1506
+ ensure
1507
+ if entered
1508
+ peers.delete(other)
1509
+ guard.delete(self) if peers.empty?
1510
+ end
1511
+ end
1512
+
1026
1513
  def each_for_inspect(&) = internal_vector.each(&)
1027
1514
 
1028
1515
  def build_derived_values(values) = build_derived_vector(values.map { derived_storage(it) })
1029
1516
 
1030
1517
  def build_derived_from_logical(values, source_storage, source_values)
1031
- retained = {}.compare_by_identity
1032
- source_values.each_with_index { |value, index| retained[value] ||= source_storage[index] }
1033
- storage = values.map do |value|
1034
- retained.fetch(value) { derived_storage(value) }
1035
- end
1036
- build_derived_vector(storage)
1518
+ build_derived_vector(storage_from_logical(values, source_storage, source_values))
1037
1519
  end
1038
1520
 
1039
1521
  def derived_storage(value) = value
data/lib/farce/atom.rb CHANGED
@@ -44,7 +44,7 @@ module Farce
44
44
  # @param value [BasicObject, nil] the initial value
45
45
  # @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
46
46
  # @param mode [Symbol] the default mode used to transfer values between Ractors
47
- def initialize(value = nil, compare_by_identity: false, mode: :copy)
47
+ def initialize(value = nil, compare_by_identity: false, mode: :smart)
48
48
  unless compare_by_identity == true || compare_by_identity == false
49
49
  raise ArgumentError, "compare_by_identity must be a boolean"
50
50
  end
@@ -24,6 +24,7 @@ module Farce
24
24
  raise TypeError, "Class expected, got #{base_class.class}" unless base_class.is_a?(Class)
25
25
  @mapping = Strict::Map.new({ BasicObject => base_class })
26
26
  @cache = Strict::WeakKeyMap.new
27
+ @custom = Strict::Map.new
27
28
  @callback = block_given? ? Ractor.shareable_proc(&) : nil
28
29
  super()
29
30
  end
@@ -31,16 +32,17 @@ module Farce
31
32
  # @api private
32
33
  def marshal_dump
33
34
  raise TypeError, "class mirror callbacks cannot be marshaled" if @callback
34
- [1, @mapping.to_h, frozen?]
35
+ [1, @mapping.to_h, frozen?, @custom.keys]
35
36
  end
36
37
 
37
38
  # @api private
38
39
  def marshal_load(data)
39
- mapping, frozen = Internal::MarshalSupport.payload(data, 2)
40
- @mapping = Strict::Map.new(mapping)
41
- @cache = Strict::WeakKeyMap.new
42
- @callback = nil
43
- @farce_freeze_state = Internal::Flag.new(false)
40
+ mapping, frozen, custom = Internal::MarshalSupport.payload(data, 3)
41
+ @mapping = Strict::Map.new(mapping)
42
+ @cache = Strict::WeakKeyMap.new
43
+ @custom = Strict::Map.new(custom.to_h { |klass| [klass, true] })
44
+ @callback = nil
45
+ @farce_freeze_state = Internal::Flag.new(false)
44
46
  publish_shareable
45
47
  Internal::MarshalSupport.freeze(self, frozen)
46
48
  end
@@ -71,14 +73,25 @@ module Farce
71
73
  raise Ractor::IsolationError, "must be called from main Ractor" unless Ractor.main?
72
74
  raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
73
75
  mixin = Module.new(&) if block_given?
74
- mirror = @mapping.store_if_absent(klass) { Class.new(define(klass.superclass)) }
76
+ mirror = define_mirror(klass)
75
77
  mirror.include(mixin) if mixin
76
78
  @cache.clear
79
+ @custom[klass] = true
77
80
  mirror
78
81
  end
79
82
 
83
+ # Check whether this class was explicitly passed to {#define}.
84
+ # Automatically created ancestors and inherited definitions do not count.
85
+ # @param klass [Class] the source class
86
+ # @return [Boolean] whether the class has an explicit definition
87
+ def custom?(klass) = @custom.key?(klass)
88
+
80
89
  private
81
90
 
91
+ def define_mirror(klass)
92
+ @mapping.store_if_absent(klass) { Class.new(define_mirror(klass.superclass)) }
93
+ end
94
+
82
95
  def lookup(klass)
83
96
  raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
84
97
  @mapping.fetch(klass) { lookup(klass.superclass) }