farce 0.5.0-arm-linux-gnu → 0.6.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/docs/modes.md +1 -1
  4. data/docs/utilities.md +1 -0
  5. data/ext/farce/atom.c +23 -7
  6. data/ext/farce/containers.h +3 -0
  7. data/ext/farce/counter.c +4 -6
  8. data/ext/farce/depend +3 -1
  9. data/ext/farce/extconf.rb +19 -14
  10. data/ext/farce/farce.c +101 -3
  11. data/ext/farce/map.c +1 -4
  12. data/ext/farce/trie.c +140 -72
  13. data/ext/farce/trie_search.h +114 -0
  14. data/ext/farce/vector.c +50 -18
  15. data/ext/farce/weak_map.c +4 -8
  16. data/ext/farce/weak_references.h +23 -0
  17. data/ext/java/org/farce/FiberScheduler.java +8 -6
  18. data/lib/farce/abstract/atom.rb +5 -1
  19. data/lib/farce/abstract/counter.rb +5 -4
  20. data/lib/farce/abstract/molecule.rb +14 -14
  21. data/lib/farce/abstract/vector.rb +1 -1
  22. data/lib/farce/atom.rb +6 -0
  23. data/lib/farce/clock.rb +8 -7
  24. data/lib/farce/counter.rb +6 -0
  25. data/lib/farce/deduper.rb +12 -4
  26. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  27. data/lib/farce/engine/jvm/counter.rb +0 -2
  28. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  29. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  30. data/lib/farce/engine/ruby/shared/weak_cache_mirror.rb +56 -0
  31. data/lib/farce/engine/ruby.rb +5 -1
  32. data/lib/farce/engine/shared/trie/ignore_case.rb +136 -0
  33. data/lib/farce/engine/shared/trie.rb +14 -1
  34. data/lib/farce/engine/shared/trie_builder.rb +19 -8
  35. data/lib/farce/engine/shared/unshared_vector.rb +3 -1
  36. data/lib/farce/engine/shared/vector.rb +2 -0
  37. data/lib/farce/engine/shared.rb +2 -0
  38. data/lib/farce/engine/truffleruby/native/counter.rb +0 -2
  39. data/lib/farce/engine/truffleruby.rb +9 -0
  40. data/lib/farce/extension.rb +5 -3
  41. data/lib/farce/integrations/sorted_set.rb +1 -0
  42. data/lib/farce/internal/fake_ractor.rb +1 -1
  43. data/lib/farce/internal/freeze.rb +1 -5
  44. data/lib/farce/internal/map_value_modes.rb +8 -7
  45. data/lib/farce/internal.rb +11 -0
  46. data/lib/farce/local/counter.rb +4 -10
  47. data/lib/farce/map.rb +8 -1
  48. data/lib/farce/memoize.rb +10 -5
  49. data/lib/farce/shareable.rb +3 -0
  50. data/lib/farce/strict/weak_cache.rb +199 -0
  51. data/lib/farce/vector.rb +8 -7
  52. data/lib/farce/version.rb +1 -1
  53. data/lib/farce/walker/definitions.rb +18 -0
  54. data/lib/farce/walker/modification.rb +28 -14
  55. data/lib/farce/walker.rb +9 -1
  56. data/lib/farce/weak_key_map.rb +1 -1
  57. data/lib/farce/weak_map.rb +1 -1
  58. data/lib/farce/weak_value_map.rb +1 -1
  59. metadata +6 -1
@@ -50,19 +50,13 @@ module Farce
50
50
 
51
51
  # Increment the counter atomically.
52
52
  # @param by [Numeric, String, #to_int] the amount, converted to an Integer
53
- # @return [self]
54
- def increment(by = 1)
55
- internal_counter.increment(by)
56
- self
57
- end
53
+ # @return [Integer] the value produced by this atomic update
54
+ def increment(by = 1) = internal_counter.increment(by)
58
55
 
59
56
  # Decrement the counter atomically.
60
57
  # @param by [Numeric, String, #to_int] the amount, converted to an Integer
61
- # @return [self]
62
- def decrement(by = 1)
63
- internal_counter.decrement(by)
64
- self
65
- end
58
+ # @return [Integer] the value produced by this atomic update
59
+ def decrement(by = 1) = internal_counter.decrement(by)
66
60
 
67
61
  alias subtract decrement
68
62
  alias remove decrement
data/lib/farce/map.rb CHANGED
@@ -48,7 +48,14 @@ module Farce
48
48
 
49
49
  private
50
50
 
51
- def new_map(**) = Internal::StrictMap.new(**)
51
+ def new_map(
52
+ compare_by_identity: false,
53
+ compare_keys_by_identity: compare_by_identity,
54
+ compare_values_by_identity: compare_by_identity
55
+ )
56
+ Internal::StrictMap.new(compare_by_identity:, compare_keys_by_identity:, compare_values_by_identity:)
57
+ end
58
+
52
59
  def freeze_backend = @map
53
60
 
54
61
  Internal.prepare_map_access(self, :modes)
data/lib/farce/memoize.rb CHANGED
@@ -77,7 +77,8 @@ module Farce
77
77
  #
78
78
  # @param mode [Symbol]
79
79
  # The mode of the memoization. Ignored for any scope other than `:global`.
80
- # Defaults to `:raise` with `weak: true`, which supports only `:raise`, `:make_shareable`, and `:dedup`.
80
+ # Use `:strict` to cache shareable results directly, including with `weak: true`.
81
+ # Defaults to `:raise` with `weak: true`, which also supports `:make_shareable` and `:dedup`.
81
82
  #
82
83
  # @param weak [Boolean]
83
84
  # Whether to use weak memoization.
@@ -159,9 +160,13 @@ module Farce
159
160
 
160
161
  case scope
161
162
  when :global
162
- modes = weak ? Internal::WeakModeManager::MODES : MODES
163
- raise ArgumentError, "unsupported mode: #{mode.inspect}" unless modes.include?(mode)
164
- init = "::Farce::#{atom_class}.new(mode: #{mode.inspect})"
163
+ if mode == :strict
164
+ init = "::Farce::Strict::#{atom_class}.new"
165
+ else
166
+ modes = weak ? Internal::WeakModeManager::MODES : MODES
167
+ raise ArgumentError, "unsupported mode: #{mode.inspect}" unless modes.include?(mode)
168
+ init = "::Farce::#{atom_class}.new(mode: #{mode.inspect})"
169
+ end
165
170
  args = methods.join(", ")
166
171
  args += ", mode: #{mode.inspect}" unless mode == :smart
167
172
  when :unshared
@@ -187,7 +192,7 @@ module Farce
187
192
  initializer << " #{ivar} = #{init}\n"
188
193
  module_eval <<~RUBY, __FILE__, __LINE__ + 1
189
194
  #{visibility} def #{method}
190
- value = #{ivar}.store_if_absent do
195
+ value = #{ivar}.value || #{ivar}.store_if_absent do
191
196
  result = super()
192
197
  nil.equal?(result) ? NIL_VALUE : result
193
198
  end
@@ -13,6 +13,9 @@ module Farce
13
13
  # object = MyClass.new
14
14
  # Ractor.shareable?(object) # => true
15
15
  module Shareable
16
+ # @api private
17
+ def self.included(base) = Internal.prepare_shareable_class(base)
18
+
16
19
  # A structurally frozen wrapper whose logical frozen state is owned by its backend.
17
20
  # The including class must implement a private `freeze_backend` method.
18
21
  #
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A weak cache that allows reusing previously generated values without holding strong references to them.
8
+ # Only shareable keys and values can be used.
9
+ #
10
+ # @example
11
+ # cache = Farce::Strict::WeakCache.new
12
+ # value = cache.store_if_absent(:key) { ExpensiveClass.new.freeze }
13
+ class WeakCache
14
+ include Shareable::Delegated
15
+ include Internal::Inspect
16
+
17
+ # Creates a new WeakCache instance if it outperforms {Strict::WeakValueMap} on the current platform.
18
+ # Otherwise it creates a standard WeakValueMap instance.
19
+ # @return [WeakCache, Strict::WeakValueMap] The created cache instance.
20
+ def self.create(...) = Internal.native_weak_maps? ? Strict::WeakValueMap.new(...) : WeakCache.new(...)
21
+
22
+ # @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil]
23
+ # The entries to store initially. Each iteration must yield a key-value pair.
24
+ # Nil creates an empty map.
25
+ # @param compare_by_identity [Boolean]
26
+ # Whether keys use object identity instead of `hash` and `eql?`.
27
+ def initialize(initial_mapping = nil, compare_by_identity: false)
28
+ @map = Strict::WeakValueMap.new(initial_mapping, compare_keys_by_identity: compare_by_identity)
29
+ initialize_cache
30
+ super()
31
+ end
32
+
33
+ # @api private
34
+ def marshal_dump = [1, @map]
35
+
36
+ # @api private
37
+ def marshal_load(data)
38
+ @map, = Internal::MarshalSupport.payload(data, 1)
39
+ initialize_cache
40
+ publish_shareable
41
+ end
42
+
43
+ # Retrieves or stores a value for the given key.
44
+ # The block is called to generate the value if the key is absent.
45
+ # Concurrent calls for the same key reuse the generated value.
46
+ # Once that value is collected, a later call generates it again.
47
+ #
48
+ # @param key [BasicObject] The key to look up.
49
+ # @yield Called without arguments when the key is absent.
50
+ # @yieldreturn [BasicObject] The value to store and return.
51
+ # @return [BasicObject] The associated value.
52
+ def store_if_absent(key, &)
53
+ raise LocalJumpError, "no block given" unless block_given?
54
+ @cache.store_if_absent(key, &)
55
+ end
56
+
57
+ alias compute_if_absent store_if_absent
58
+
59
+ # Store a value only when the key is absent.
60
+ # Existing nil and false values count as present.
61
+ # @param key [BasicObject] The key to look up or store.
62
+ # @param value [BasicObject] The value to store if the key is absent.
63
+ # @return [BasicObject, nil] The existing value, or nil when a value was inserted.
64
+ def put_if_absent(key, value)
65
+ inserted = false
66
+ previous = store_if_absent(key) do
67
+ inserted = true
68
+ value
69
+ end
70
+ inserted ? nil : previous
71
+ end
72
+
73
+ # Look up a value without generating it.
74
+ # @param key [BasicObject] The key to look up.
75
+ # @return [BasicObject, nil] The associated value, or nil if the key is absent.
76
+ def [](key) = @cache.fetch(key) { nil } # rubocop:disable Style/RedundantFetchBlock
77
+
78
+ # Return a lookup key and its associated value if present.
79
+ # @param key [BasicObject] The key to look up.
80
+ # @return [Array(BasicObject, BasicObject), nil] The pair `[key, value]`, or nil if the key is absent.
81
+ def assoc(key)
82
+ value = fetch(key) { return nil }
83
+ [key, value]
84
+ end
85
+
86
+ # Return whether a live entry exists, including entries with nil or false values.
87
+ # @param key [BasicObject] The key to look up.
88
+ # @return [Boolean]
89
+ def key?(key)
90
+ @cache.fetch(key) { return false }
91
+ true
92
+ end
93
+
94
+ alias has_key? key?
95
+ alias include? key?
96
+ alias member? key?
97
+
98
+ # Look up a value in nested maps or arrays.
99
+ # @param key [BasicObject] The key to look up.
100
+ # @param rest [Array<BasicObject>] Additional keys or indices for the nested value.
101
+ # @return [BasicObject, nil] The nested value, or nil if an intermediate value is absent or nil.
102
+ def dig(key, *rest)
103
+ value = self[key]
104
+ return value if rest.empty? || value.nil?
105
+ value.dig(*rest)
106
+ end
107
+
108
+ # Return the stored key matching a lookup key.
109
+ # @param key [BasicObject] The key to look up.
110
+ # @return [BasicObject, nil] The stored key, or nil if the key is absent.
111
+ def getkey(key) = @map.getkey(key)
112
+
113
+ # Fetch the value associated with a key, using the same missing-key behavior as Hash#fetch.
114
+ # If both a default and a block are provided, the block takes precedence.
115
+ #
116
+ # @overload fetch(key)
117
+ # @param key [BasicObject] The key to look up.
118
+ # @return [BasicObject] The associated value.
119
+ # @raise [KeyError] If the key is absent.
120
+ #
121
+ # @overload fetch(key, default)
122
+ # @param key [BasicObject] The key to look up.
123
+ # @param default [BasicObject] The value to return if the key is absent.
124
+ # @return [BasicObject] The associated value or `default`.
125
+ #
126
+ # @overload fetch(key)
127
+ # @param key [BasicObject] The key to look up.
128
+ # @yield [key] Called if the key is absent.
129
+ # @yieldparam key [BasicObject] The missing key.
130
+ # @yieldreturn [BasicObject] The value to return.
131
+ # @return [BasicObject] The associated value or the block result.
132
+ def fetch(*arguments)
133
+ unless arguments.length.between?(1, 2)
134
+ raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
135
+ end
136
+
137
+ key, default = arguments
138
+ warn "block supersedes default value argument", uplevel: 1 if block_given? && arguments.length == 2
139
+ @cache.fetch(key) do
140
+ return yield(key) if block_given?
141
+ return default if arguments.length == 2
142
+ raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
143
+ end
144
+ end
145
+
146
+ # Fetch values in the requested order, using {#fetch} for missing keys.
147
+ # @param keys [Array<BasicObject>] The keys to look up.
148
+ # @yield [key] Called for each missing key.
149
+ # @yieldparam key [BasicObject] The missing key.
150
+ # @yieldreturn [BasicObject] The fallback value, which is not stored.
151
+ # @return [Array<BasicObject>] The associated values or block results.
152
+ def fetch_values(*keys, &) = keys.map { fetch(it, &) }
153
+
154
+ # Look up values in the requested order, returning nil for missing keys.
155
+ # @param keys [Array<BasicObject>] The keys to look up.
156
+ # @return [Array<BasicObject>] The associated values, with nil for missing keys.
157
+ def values_at(*keys) = keys.map { self[it] }
158
+
159
+ # @return [Boolean] Whether keys are compared by identity instead of `hash` and `eql?`.
160
+ def compare_keys_by_identity? = @map.compare_keys_by_identity?
161
+
162
+ # (see #compare_keys_by_identity?)
163
+ def compare_by_identity? = compare_keys_by_identity?
164
+
165
+ # @return [Boolean] Whether keys must be Ractor-shareable, including copied mutable strings.
166
+ def shareable_keys? = true
167
+
168
+ # @return [Boolean] Whether values must be Ractor-shareable.
169
+ def shareable_values? = true
170
+
171
+ # @return [Boolean] Whether keys are referenced weakly.
172
+ def weak_keys? = false
173
+
174
+ # @return [Boolean] Whether values are referenced weakly.
175
+ def weak_values? = true
176
+
177
+ # @api private
178
+ def inspect_with(inspector)
179
+ super do
180
+ yield if block_given?
181
+ inspector.breakable
182
+ inspector.group("{", "}") do
183
+ inspector.breakable ""
184
+ inspector.seplist(@map, nil, :each) do |key, value|
185
+ inspector.group { inspector.hash_pair(key, value) }
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ alias to_s inspect
192
+
193
+ private
194
+
195
+ def initialize_cache = @cache = Internal.native_weak_maps? ? @map : Internal::WeakCacheMirror.new(@map)
196
+ def freeze_backend = @map
197
+ end
198
+ end
199
+ end
data/lib/farce/vector.rb CHANGED
@@ -26,14 +26,15 @@ module Farce
26
26
  # @raise [ArgumentError] If the size is negative.
27
27
  def initialize(source = nil, mode: :smart, compare_by_identity: false)
28
28
  source = normalize_vector_source(source)
29
- raise TypeError, "source must be an Array" unless source.nil? || source.is_a?(Array)
30
- unless true.equal?(compare_by_identity) || false.equal?(compare_by_identity)
31
- raise ArgumentError, "compare_by_identity must be true or false"
32
- end
29
+ raise TypeError, "source must be an Array" unless source.nil? || Integer === source || source.is_a?(Array)
33
30
 
34
- @compare_by_identity = compare_by_identity
31
+ @compare_by_identity = compare_by_identity ? true : false
35
32
  @manager = ModeManager.new(mode:)
36
- @vector = Internal::Vector.new(source&.map { @manager.wrap(it) }, compare_by_identity: true)
33
+ if source && !(Integer === source)
34
+ source = source.map { @manager.wrap(it) }
35
+ raise TypeError, "source must be an Array" unless source.nil? || source.is_a?(Array)
36
+ end
37
+ @vector = Internal::Vector.new_for_modes(source)
37
38
  super()
38
39
  end
39
40
 
@@ -53,7 +54,7 @@ module Farce
53
54
  def marshal_load(data)
54
55
  entries, @manager, identity, frozen = Internal::MarshalSupport.payload(data, 4)
55
56
  @compare_by_identity = identity
56
- @vector = Internal::Vector.new(compare_by_identity: true)
57
+ @vector = Internal::Vector.new_for_modes(nil)
57
58
  publish_shareable
58
59
  entries.each { @vector.push(Internal::MarshalSupport.restore_value(it)) }
59
60
  Internal::MarshalSupport.freeze(self, frozen)
data/lib/farce/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
 
5
5
  module Farce
6
6
  # @return [String] the current version of Farce
7
- VERSION = "0.5.0"
7
+ VERSION = "0.6.0"
8
8
  end
@@ -190,6 +190,24 @@ module Farce
190
190
  super(object, walker)
191
191
  end
192
192
 
193
+ define(Struct) do |object, walker|
194
+ result = walker.update(object, object.to_a) do |target, results|
195
+ results.each_with_index { |value, index| target[index] = value unless target[index].equal?(value) }
196
+ target
197
+ end
198
+ super(result, walker)
199
+ end
200
+
201
+ define(Range) do |object, walker|
202
+ names = object.instance_variables
203
+ values = [object.begin, object.end, *names.map { object.instance_variable_get(it) }]
204
+ walker.update(object, values) do |target, results|
205
+ names.each_with_index { |name, index| target.instance_variable_set(name, results[index + 2]) }
206
+ Internal.initialize_range(target, results[0], results[1], object.exclude_end?)
207
+ target
208
+ end
209
+ end
210
+
193
211
  define(Data) do |object, walker|
194
212
  walker.rebuild_data(object)
195
213
  end
@@ -172,14 +172,14 @@ module Farce
172
172
  node ? node.changed == true : (@seen.key?(object) && !@seen[object].equal?(object))
173
173
  end
174
174
 
175
- # Follow pending references to check that hashing cannot reach unfinished Data.
175
+ # Follow pending references to check that hashing cannot reach unfinished values.
176
176
  def hashable?(object)
177
- return true unless @unfinished_data && !@unfinished_data.empty?
177
+ return true unless @unfinished_values && !@unfinished_values.empty?
178
178
  !Walker.visit(object, constants:, class_variables:) do |value, traversal|
179
- traversal.return(true) if @unfinished_data.key?(value)
179
+ traversal.return(true) if @unfinished_values.key?(value)
180
180
  node = @nodes[value] || @destinations&.[](value)
181
181
  if node && node.plans
182
- traversal.return(true) if @unfinished_data.key?(node.destination)
182
+ traversal.return(true) if @unfinished_values.key?(node.destination)
183
183
  node.plans.each { |plan| plan.children.each { traversal.visit(resolved(it)) } }
184
184
  else
185
185
  traversal.traverse
@@ -188,10 +188,10 @@ module Farce
188
188
  end
189
189
  end
190
190
 
191
- # Reject keys and set elements whose Data dependencies are still being built.
191
+ # Reject keys and set elements whose immutable dependencies are still being built.
192
192
  def check_hash_key(object)
193
193
  return object if hashable?(object)
194
- raise ArgumentError, "hash key or set element refers to an unfinished Data value"
194
+ raise ArgumentError, "hash key or set element refers to an unfinished Data or Range value"
195
195
  end
196
196
 
197
197
  private
@@ -217,19 +217,19 @@ module Farce
217
217
  dirty ||= plan.hash_keys && (index % plan.key_stride).zero? && child && child.changed
218
218
  end
219
219
  node.changed ||= dirty
220
- plan.dirty = dirty
220
+ plan.dirty = dirty || (Range === node.destination && @unfinished_values&.key?(node.destination))
221
221
  end
222
222
 
223
- # Choose a writable destination, copying lazily or allocating unfinished Data.
223
+ # Choose a writable destination, copying lazily or allocating unfinished values.
224
224
  def prepare(node, rebuild: false)
225
225
  previous = node.destination
226
226
  return previous unless rebuild || previous.equal?(node.original)
227
227
  object = node.original
228
228
  return object if Internal::Noncopyable === object || Abstract::LeaseMap === object
229
229
  destination =
230
- if Data === object
230
+ if Data === object || Range === object
231
231
  value = object.class.allocate
232
- (@unfinished_data ||= {}.compare_by_identity)[value] = true
232
+ (@unfinished_values ||= {}.compare_by_identity)[value] = true
233
233
  value
234
234
  elsif @modify == true
235
235
  send_to(object, :frozen?) ? send_to(object, :clone, freeze: false) : object
@@ -241,6 +241,7 @@ module Farce
241
241
  (@destinations ||= {}.compare_by_identity)[destination] = node unless destination.equal?(object)
242
242
  node.destination = destination
243
243
  node.result = destination if !node.skipped && (node.result.equal?(object) || node.result.equal?(previous))
244
+ node.plans&.each { it.dirty = true } if Range === destination
244
245
  destination
245
246
  end
246
247
 
@@ -259,7 +260,7 @@ module Farce
259
260
  node.destination = result
260
261
  node.result = result if node.result.equal?(old)
261
262
  end
262
- @unfinished_data&.delete(node.destination) if Data === node.destination
263
+ @unfinished_values&.delete(node.destination) if Data === node.destination || Range === node.destination
263
264
  return unless node.cyclic
264
265
  plan.target = node.destination
265
266
  plan.applied = plan.results.dup
@@ -281,6 +282,9 @@ module Farce
281
282
  if freeze
282
283
  node.changed = true unless send_to(node.result, :frozen?)
283
284
  prepare(node) if @modify != true && node.result.equal?(node.original) && !send_to(node.result, :frozen?)
285
+ if Range === node.destination && @unfinished_values&.key?(node.destination)
286
+ node.plans&.each { apply(node, it) }
287
+ end
284
288
  send_to(node.result, :freeze)
285
289
  end
286
290
  finalize_node(node) if finalize
@@ -308,15 +312,25 @@ module Farce
308
312
  freezing = !node.skipped && (node.freeze_result || @freeze == true)
309
313
  needed = node.plans&.any?(&:dirty) || (freezing && @modify != true && !send_to(node.original, :frozen?))
310
314
  prepare(node) if needed
311
- next unless Data === node.destination && send_to(node.destination, :frozen?) && node.plans&.any? do |plan|
312
- plan.applied && plan.results.each_with_index.any? { |value, index| !value.equal?(plan.applied[index]) }
315
+ next unless Data === node.destination || Range === node.destination
316
+ # Rebuild only after this destination's own assignments become stale.
317
+ next unless node.plans&.any? do |plan|
318
+ plan.target.equal?(node.destination) && plan.applied &&
319
+ plan.results.each_with_index.any? { |value, index| !value.equal?(plan.applied[index]) }
313
320
  end
314
321
  prepare(node, rebuild: true)
315
322
  end
316
323
  break if component.each_with_index.all? { |node, index| node.destination.equal?(destinations[index]) }
317
324
  end
318
325
 
319
- component.sort_by { Data === it.destination ? 2 : (it.plans&.any?(&:hash_keys) ? 1 : 0) }.each do |node|
326
+ ordered = component.sort_by do |node|
327
+ if Data === node.destination || Range === node.destination
328
+ 2
329
+ else
330
+ node.plans&.any?(&:hash_keys) ? 1 : 0
331
+ end
332
+ end
333
+ ordered.each do |node|
320
334
  @node = node
321
335
  @current_object = node.original
322
336
  node.plans&.each do |plan|
data/lib/farce/walker.rb CHANGED
@@ -140,14 +140,18 @@ module Farce
140
140
  end
141
141
 
142
142
  # Transform a graph using the callback's return values.
143
+ #
143
144
  # Call {#traverse} to transform an object's children. Returning another value
144
145
  # replaces the object without automatically visiting that replacement.
145
146
  # Unchanged branches retain their identity, including when copy is enabled.
147
+ #
146
148
  # Changed cycles are connected before results are frozen or finalized.
147
149
  # A cyclic callback may run again as child replacements become known. Keep
148
150
  # callbacks repeatable and put publication or caching in {#finalize}.
149
151
  # Use {#freeze_result} instead of freezing a preliminary traversal result.
152
+ #
150
153
  # Data values are rebuilt only when members change, using their normal initializer.
154
+ # Ranges are rebuilt when endpoints or instance variables change.
151
155
  # Noncopyable coordination objects, such as leases, are updated in place.
152
156
  # Constants and class variables are assigned only when their value
153
157
  # changes identity. Constant replacement can emit Ruby redefinition warnings.
@@ -156,6 +160,7 @@ module Farce
156
160
  #
157
161
  # A walk is not an atomic snapshot or update. Coordinate concurrent writers
158
162
  # when transforming container structure, especially map keys.
163
+ #
159
164
  # @param object [BasicObject] the root object
160
165
  # @!macro walker_module_options
161
166
  # @param copy [Boolean, Symbol] false to edit mutable objects in place, true to
@@ -171,7 +176,7 @@ module Farce
171
176
  # @raise [ArgumentError] if copy is neither a boolean nor a Symbol
172
177
  # @raise [ArgumentError] if cyclic callbacks do not converge within 32 passes,
173
178
  # or a cyclic finalizer replaces its result
174
- # @raise [ArgumentError] if a hash key or set element refers to a Data value
179
+ # @raise [ArgumentError] if a hash key or set element refers to a Data or Range value
175
180
  # still being constructed. Identity-based containers do not hash their keys.
176
181
  def self.modify(object, copy: false, freeze: nil, constants: true, class_variables: true, &callback)
177
182
  raise LocalJumpError, "no block given" unless callback
@@ -263,9 +268,11 @@ module Farce
263
268
  def remember(object, result) = @seen[object] = result
264
269
 
265
270
  # Visit child values and describe how to assign their replacements.
271
+ #
266
272
  # The assignment block only runs during modification and may run again for cycles.
267
273
  # It receives a writable target and the resolved children. Do not mutate object
268
274
  # outside this block. Return the result of update from a traversal definition.
275
+ #
269
276
  # @param object [BasicObject] the object being traversed
270
277
  # @param values [Array] child references in assignment order
271
278
  # @param hash_keys [Boolean] rebuild when key descendants change in place
@@ -288,6 +295,7 @@ module Farce
288
295
  # and requested freezing. Registering another finalizer replaces the first.
289
296
  # Cyclic finalizers must preserve identity. Acyclic finalizers may return a
290
297
  # canonical replacement, which is propagated to the parent.
298
+ #
291
299
  # @yieldparam result [BasicObject] the settled result
292
300
  # @yieldparam cyclic [Boolean] whether the node belongs to a cycle
293
301
  # @yieldreturn [BasicObject] the final result
@@ -23,7 +23,7 @@ module Farce
23
23
 
24
24
  private
25
25
 
26
- def new_map(**) = Internal::StrictWeakKeyMap.new(**)
26
+ def new_map(...) = Internal::StrictWeakKeyMap.new(...)
27
27
  def freeze_backend = @map
28
28
  end
29
29
 
@@ -25,7 +25,7 @@ module Farce
25
25
 
26
26
  private
27
27
 
28
- def new_map(**) = Internal::StrictWeakMap.new(**)
28
+ def new_map(...) = Internal::StrictWeakMap.new(...)
29
29
  def freeze_backend = @map
30
30
  end
31
31
 
@@ -24,7 +24,7 @@ module Farce
24
24
 
25
25
  private
26
26
 
27
- def new_map(**) = Internal::StrictWeakValueMap.new(**)
27
+ def new_map(...) = Internal::StrictWeakValueMap.new(...)
28
28
  def freeze_backend = @map
29
29
  end
30
30
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: farce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: arm-linux-gnu
6
6
  authors:
7
7
  - Konstantin Haase
@@ -72,6 +72,7 @@ files:
72
72
  - ext/farce/transaction.h
73
73
  - ext/farce/tree_map.c
74
74
  - ext/farce/trie.c
75
+ - ext/farce/trie_search.h
75
76
  - ext/farce/unshareable.c
76
77
  - ext/farce/unshared_io_pool.h
77
78
  - ext/farce/unshared_signal.c
@@ -79,6 +80,7 @@ files:
79
80
  - ext/farce/unsupported.c
80
81
  - ext/farce/vector.c
81
82
  - ext/farce/weak_map.c
83
+ - ext/farce/weak_references.h
82
84
  - ext/java/org/farce/BoundedMap.java
83
85
  - ext/java/org/farce/FiberScheduler.java
84
86
  - ext/java/org/farce/PriorityKey.java
@@ -196,6 +198,7 @@ files:
196
198
  - lib/farce/engine/ruby/shared/vault.rb
197
199
  - lib/farce/engine/ruby/shared/vault_weak_map.rb
198
200
  - lib/farce/engine/ruby/shared/weak_atom.rb
201
+ - lib/farce/engine/ruby/shared/weak_cache_mirror.rb
199
202
  - lib/farce/engine/ruby/shared/weak_map.rb
200
203
  - lib/farce/engine/shared.rb
201
204
  - lib/farce/engine/shared/atom.rb
@@ -220,6 +223,7 @@ files:
220
223
  - lib/farce/engine/shared/strict_tree_map.rb
221
224
  - lib/farce/engine/shared/transaction_map_backend.rb
222
225
  - lib/farce/engine/shared/trie.rb
226
+ - lib/farce/engine/shared/trie/ignore_case.rb
223
227
  - lib/farce/engine/shared/trie_builder.rb
224
228
  - lib/farce/engine/shared/unshareable.rb
225
229
  - lib/farce/engine/shared/unshared_atom.rb
@@ -427,6 +431,7 @@ files:
427
431
  - lib/farce/strict/tree_map.rb
428
432
  - lib/farce/strict/vector.rb
429
433
  - lib/farce/strict/weak_atom.rb
434
+ - lib/farce/strict/weak_cache.rb
430
435
  - lib/farce/strict/weak_key_map.rb
431
436
  - lib/farce/strict/weak_map.rb
432
437
  - lib/farce/strict/weak_set.rb