farce 0.1.0-arm-linux-gnu → 0.2.0-arm-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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -1
- data/README.md +72 -16
- data/docs/gems/dry-types.md +8 -8
- data/docs/modes.md +67 -10
- data/ext/farce/atom.c +37 -22
- data/ext/farce/bounded_map.c +17 -13
- data/ext/farce/containers.h +15 -0
- data/ext/farce/counter.c +13 -18
- data/ext/farce/exchanger.c +5 -1
- data/ext/farce/farce.c +1 -1
- data/ext/farce/flag.c +39 -3
- data/ext/farce/io.c +3 -3
- data/ext/farce/lock.c +17 -23
- data/ext/farce/map.c +88 -66
- data/ext/farce/priority_queue.c +55 -43
- data/ext/farce/queue.c +10 -4
- data/ext/farce/reactor.c +52 -25
- data/ext/farce/reactor.h +1 -1
- data/ext/farce/signal.c +5 -1
- data/ext/farce/transaction.c +20 -24
- data/ext/farce/tree_map.c +33 -18
- data/ext/farce/trie.c +81 -43
- data/ext/farce/unshareable.c +168 -19
- data/ext/farce/unshared_io_pool.h +21 -12
- data/ext/farce/unshared_signal.c +13 -2
- data/ext/farce/unshared_wait.h +3 -0
- data/ext/farce/vector.c +82 -18
- data/ext/farce/weak_map.c +75 -63
- data/lib/farce/_yard/macros.rb +18 -3
- data/lib/farce/abstract/flag.rb +73 -0
- data/lib/farce/abstract/queue.rb +4 -3
- data/lib/farce/abstract/scheduler.rb +3 -3
- data/lib/farce/abstract/set.rb +17 -5
- data/lib/farce/abstract/sorted_set.rb +2 -2
- data/lib/farce/abstract/timer_queue.rb +49 -10
- data/lib/farce/abstract/vector.rb +510 -28
- data/lib/farce/atom.rb +1 -1
- data/lib/farce/class_mirror.rb +20 -7
- data/lib/farce/config.rb +7 -1
- data/lib/farce/engine/jvm/flag.rb +15 -3
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/shared/require_hook.rb +18 -0
- data/lib/farce/engine/ruby.rb +7 -0
- data/lib/farce/engine/shared/unshared_vector.rb +31 -4
- data/lib/farce/engine/shared/vector.rb +8 -0
- data/lib/farce/engine/shared/weak_map/index.rb +14 -1
- data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
- data/lib/farce/envelope.rb +54 -7
- data/lib/farce/exchanger.rb +1 -1
- data/lib/farce/flag.rb +2 -0
- data/lib/farce/integrations/active_support.rb +1 -1
- data/lib/farce/integrations/bson.rb +1 -1
- data/lib/farce/integrations/cbor.rb +1 -1
- data/lib/farce/integrations/concurrent.rb +3 -3
- data/lib/farce/integrations/date.rb +8 -0
- data/lib/farce/integrations/dry_types.rb +7 -4
- data/lib/farce/integrations/json.rb +2 -2
- data/lib/farce/integrations/msgpack.rb +1 -1
- data/lib/farce/integrations/oj.rb +3 -4
- data/lib/farce/integrations/psych.rb +1 -1
- data/lib/farce/integrations/ractor_sharing.rb +1 -1
- data/lib/farce/integrations/sorted_set.rb +1 -1
- data/lib/farce/integrations/weakref.rb +9 -1
- data/lib/farce/integrations/yajl.rb +3 -3
- data/lib/farce/integrations.rb +33 -23
- data/lib/farce/internal/flag_waiting.rb +30 -0
- data/lib/farce/internal/freeze.rb +8 -3
- data/lib/farce/internal/map_value_modes.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
- data/lib/farce/lazy.rb +4 -4
- data/lib/farce/lazy_ref.rb +3 -3
- data/lib/farce/lfu_map.rb +1 -1
- data/lib/farce/local/flag.rb +4 -0
- data/lib/farce/local/scoped.rb +1 -1
- data/lib/farce/lru_map.rb +1 -1
- data/lib/farce/map.rb +2 -2
- data/lib/farce/mode_manager/smart_rule.rb +138 -0
- data/lib/farce/mode_manager.rb +12 -3
- data/lib/farce/molecule.rb +2 -2
- data/lib/farce/pool.rb +1 -1
- data/lib/farce/port.rb +4 -4
- data/lib/farce/priority_queue.rb +1 -1
- data/lib/farce/proxy/register.rb +6 -0
- data/lib/farce/proxy/supervisor.rb +1 -1
- data/lib/farce/proxy.rb +7 -2
- data/lib/farce/queue.rb +1 -1
- data/lib/farce/scheduler.rb +2 -2
- data/lib/farce/set.rb +1 -1
- data/lib/farce/sorted_set.rb +1 -1
- data/lib/farce/timer_queue.rb +20 -6
- data/lib/farce/transaction/vector.rb +43 -9
- data/lib/farce/transaction/wrapper.rb +14 -0
- data/lib/farce/tree_map.rb +1 -1
- data/lib/farce/unsafe/lfu_map.rb +2 -0
- data/lib/farce/unsafe/lru_map.rb +2 -0
- data/lib/farce/unsafe/tree_map.rb +2 -0
- data/lib/farce/unshareable.rb +8 -19
- data/lib/farce/unshared/atom.rb +3 -0
- data/lib/farce/unshared/lfu_map.rb +2 -0
- data/lib/farce/unshared/lru_map.rb +2 -0
- data/lib/farce/unshared/map.rb +3 -0
- data/lib/farce/unshared/set.rb +1 -0
- data/lib/farce/unshared/sorted_set.rb +1 -0
- data/lib/farce/unshared/tree_map.rb +5 -1
- data/lib/farce/unshared/vector.rb +3 -0
- data/lib/farce/unshared/weak_atom.rb +5 -1
- data/lib/farce/unshared/weak_key_map.rb +3 -0
- data/lib/farce/unshared/weak_map.rb +3 -0
- data/lib/farce/unshared/weak_set.rb +1 -0
- data/lib/farce/unshared/weak_value_map.rb +3 -0
- data/lib/farce/vector.rb +44 -19
- data/lib/farce/version.rb +1 -1
- data/lib/farce.rb +11 -8
- 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
|
|
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
|
-
# @
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
|
833
|
-
# @param value [BasicObject] The value to
|
|
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
|
-
|
|
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
|
|
866
|
-
# @overload append(
|
|
867
|
-
# @param
|
|
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]
|
|
876
|
-
def pop(
|
|
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
|
-
|
|
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: :
|
|
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
|
data/lib/farce/class_mirror.rb
CHANGED
|
@@ -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
|
|
40
|
-
@mapping
|
|
41
|
-
@cache
|
|
42
|
-
@
|
|
43
|
-
@
|
|
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 =
|
|
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) }
|