@@ -20,13 +20,14 @@ class Log::Metadata
2020 # When the metadata is defragmented max_total_size will be updated with size
2121 protected getter max_total_size : Int32
2222 @max_total_size = uninitialized Int32
23- # How many entries are potentially overridden from parent (ie: initial entries.size)
24- @overridden_size = uninitialized Int32
2523 # How many entries are stored from @first.
26- # Initially are @overridden_size, the one explicitly overridden in entries argument.
2724 # When the metadata is defragmented @size will be increased up to
2825 # the actual number of entries resulting from merging the parent
2926 @size = uninitialized Int32
27+ # Number of parent elements we've copied on defrag. Used to iterate parent
28+ # entries first in #each.
29+ @parent_size = uninitialized Int32
30+
3031 # @first needs to be the last ivar of Metadata. The entries are allocated together with self
3132 @first = uninitialized Entry
3233
@@ -42,7 +43,8 @@ class Log::Metadata
4243 end
4344
4445 protected def setup (@parent : Metadata ?, entries : NamedTuple | Hash )
45- @size = @overridden_size = entries.size
46+ @size = entries.size
47+ @parent_size = 0
4648 @max_total_size = @size + (@parent .try(& .max_total_size) || 0 )
4749 ptr_entries = pointerof (@first )
4850
@@ -91,43 +93,44 @@ class Log::Metadata
9193 # will be recomputed, but the result should be the same.
9294 #
9395 # * @parent.nil? signals if the defrag is needed/done
94- # * The values of @overridden_size, pointerof(@first) are never changed
96+ # * The value of pointerof(@first) never changes
9597 # * @parent is set at the very end of the method
9698 protected def defrag
9799 parent = @parent
98100 return if parent.nil?
99101
100- total_size = @overridden_size
101102 ptr_entries = pointerof (@first )
102- next_free_entry = ptr_entries + @overridden_size
103+ next_free_entry = ptr_entries + @size
104+ total_size = @size
103105
106+ # Copy parent entries that ain't overwritten
107+ parent_size = 0
104108 parent.each do |(key , value )|
105- overridden = false
106- @overridden_size .times do |i |
107- if ptr_entries[i][:key ] == key
108- overridden = true
109- break
110- end
111- end
112-
113- unless overridden
114- next_free_entry.value = {key: key, value: value}
115- next_free_entry += 1
116- total_size += 1
117- end
109+ overwritten = Slice .new(ptr_entries, @size ).any? { |entry | entry[:key ] == key }
110+ next if overwritten
111+ next_free_entry.value = {key: key, value: value}
112+ parent_size += 1
113+ next_free_entry += 1
114+ total_size += 1
118115 end
119116
120117 @size = total_size
121118 @max_total_size = total_size
119+ @parent_size = parent_size
122120 @parent = nil
123121 end
124122
125123 def each (& : {Symbol , Value } - > )
126124 defrag
127125 ptr_entries = pointerof (@first )
126+ parent_size = @parent_size
127+ local_size = @size - parent_size
128128
129- @size .times do |i |
130- entry = ptr_entries[i]
129+ Slice .new(ptr_entries + local_size, parent_size).each do |entry |
130+ yield ({entry[:key ], entry[:value ]})
131+ end
132+
133+ Slice .new(ptr_entries, local_size).each do |entry |
131134 yield ({entry[:key ], entry[:value ]})
132135 end
133136 end
0 commit comments