Class: Lutaml::Model::Store
- Inherits:
-
Object
- Object
- Lutaml::Model::Store
- Defined in:
- lib/lutaml/model/store.rb
Defined Under Namespace
Classes: LiveView
Constant Summary collapse
- COMPACTION_THRESHOLD =
Compact dead index entries once a class bucket grows past this size.
1000- COMPACTION_INTERVAL =
Once the threshold is exceeded, only compact every Nth subsequent register call. Amortises the O(N) prune over N inserts so register stays O(1) per call rather than O(N) per call (O(N^2) cumulatively for the class).
1000- WeakBucket =
if Lutaml::Model.opal? # Opal has neither WeakRef nor ObjectSpace::WeakMap; documents in # the browser are small, so strong references are the fallback. ::Hash else # CRuby: WeakMap holds keys weakly with zero Ruby-level # allocation per insert and no per-object finalizer — the WeakRef # approach allocated one finalizer-registering object per # instance (lutaml-model#695) and dominated GC cycles on # instance-heavy parses. ::ObjectSpace::WeakMap end
Class Method Summary collapse
- .clear ⇒ Object
- .instance ⇒ Object
- .reference_types_in_use! ⇒ Object
-
.reference_types_in_use? ⇒ Boolean
lutaml-model#808: Ruby 3.3's GC has a WeakMap-mark hazard under heavy insert churn (freed slot dereferenced at wmap mark time).
- .register(object) ⇒ Object
- .reset! ⇒ Object
- .resolve(model_class, reference_key, reference_value) ⇒ Object
- .store ⇒ Object
Instance Method Summary collapse
- #clear ⇒ Object
- #compaction_count ⇒ Object
- #index_entry_count(model_key) ⇒ Object
-
#initialize ⇒ Store
constructor
A new instance of Store.
- #inserts_since_compaction ⇒ Object
- #live_objects(model_key) ⇒ Object
- #refs_for(model_key) ⇒ Object
- #register(object) ⇒ Object
- #resolve(model_class, reference_key, reference_value) ⇒ Object
- #store ⇒ Object
Constructor Details
#initialize ⇒ Store
Returns a new instance of Store.
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/lutaml/model/store.rb', line 76 def initialize # WeakMap-based buckets: value presence marks liveness, no # per-instance allocation (lutaml-model#695). @store = ::Hash.new { |hash, key| hash[key] = WeakBucket.new } # Nested index: { model_key => { reference_key => { value => object } } } # Grouped by model_key so register only iterates this class's own indices. @index = {} @inserts_since_compaction = ::Hash.new(0) @compaction_count = 0 end |
Class Method Details
.clear ⇒ Object
67 68 69 |
# File 'lib/lutaml/model/store.rb', line 67 def clear instance.clear end |
.instance ⇒ Object
32 33 34 |
# File 'lib/lutaml/model/store.rb', line 32 def instance @instance ||= new end |
.reference_types_in_use! ⇒ Object
49 50 51 |
# File 'lib/lutaml/model/store.rb', line 49 def reference_types_in_use! @reference_types_in_use = true end |
.reference_types_in_use? ⇒ Boolean
lutaml-model#808: Ruby 3.3's GC has a WeakMap-mark hazard under
heavy insert churn (freed slot dereferenced at wmap mark time).
Registration is only ever consumed by ref: resolution — when
no Reference-typed attribute exists anywhere, nothing can
resolve, so registration is skipped entirely and the WeakMap
stays quiet. Set the first time an attribute with a Reference
type is defined.
Not memoized ||= — the setter flips it to true for the
process lifetime and reads must see that.
45 46 47 |
# File 'lib/lutaml/model/store.rb', line 45 def reference_types_in_use? @reference_types_in_use ? true : false end |
.register(object) ⇒ Object
57 58 59 60 61 |
# File 'lib/lutaml/model/store.rb', line 57 def register(object) return unless reference_types_in_use? instance.register(object) end |
.reset! ⇒ Object
53 54 55 |
# File 'lib/lutaml/model/store.rb', line 53 def reset! @instance = new end |
.resolve(model_class, reference_key, reference_value) ⇒ Object
63 64 65 |
# File 'lib/lutaml/model/store.rb', line 63 def resolve(model_class, reference_key, reference_value) instance.resolve(model_class, reference_key, reference_value) end |
.store ⇒ Object
71 72 73 |
# File 'lib/lutaml/model/store.rb', line 71 def store instance.store end |
Instance Method Details
#clear ⇒ Object
121 122 123 124 125 126 |
# File 'lib/lutaml/model/store.rb', line 121 def clear @store = ::Hash.new { |hash, key| hash[key] = WeakBucket.new } @index = {} @inserts_since_compaction = ::Hash.new(0) @compaction_count = 0 end |
#compaction_count ⇒ Object
140 141 142 |
# File 'lib/lutaml/model/store.rb', line 140 def compaction_count @compaction_count end |
#index_entry_count(model_key) ⇒ Object
144 145 146 |
# File 'lib/lutaml/model/store.rb', line 144 def index_entry_count(model_key) @index[model_key]&.sum { |_reference_key, entries| entries.size } || 0 end |
#inserts_since_compaction ⇒ Object
136 137 138 |
# File 'lib/lutaml/model/store.rb', line 136 def inserts_since_compaction @inserts_since_compaction end |
#live_objects(model_key) ⇒ Object
114 115 116 117 118 119 |
# File 'lib/lutaml/model/store.rb', line 114 def live_objects(model_key) bucket = @store[model_key] return [] unless bucket live_objects_for(bucket) end |
#refs_for(model_key) ⇒ Object
132 133 134 |
# File 'lib/lutaml/model/store.rb', line 132 def refs_for(model_key) LiveView.new(self, model_key) end |
#register(object) ⇒ Object
87 88 89 90 91 92 93 94 95 |
# File 'lib/lutaml/model/store.rb', line 87 def register(object) model_key = object.class.to_s @store[model_key][object] = true @inserts_since_compaction[model_key] += 1 compact_if_needed(model_key) update_existing_indices(object, model_key) end |
#resolve(model_class, reference_key, reference_value) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/lutaml/model/store.rb', line 97 def resolve(model_class, reference_key, reference_value) model_key = model_class.to_s model_indices = @index[model_key] unless model_indices&.key?(reference_key) model_indices = ensure_model_index(model_key) build_index(model_indices, model_key, reference_key) end entry = model_indices[reference_key][reference_value] return nil unless entry obj = dereference(entry) model_indices[reference_key].delete(reference_value) unless obj obj end |
#store ⇒ Object
128 129 130 |
# File 'lib/lutaml/model/store.rb', line 128 def store @store.transform_values { |refs| live_objects_for(refs) } end |