Skip to content

Commit 779ff50

Browse files
committed
Store parent element last in @FIRST
This will reduce elements that needs to be copied to only non-overwritten parent elements. One result of this is that an overwritten parent entry will be moved from the "parent" part of the Metadata to the "own" part. E.g. `{a: 1, b: 2} extend {a: 3}` results in `{b: 2, a:3}`
1 parent 2b38019 commit 779ff50

File tree

2 files changed

+28
-36
lines changed

2 files changed

+28
-36
lines changed

spec/std/log/metadata_spec.cr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,12 @@ describe Log::Metadata do
7272

7373
md.@size.should eq(1)
7474
md.@max_total_size.should eq(4)
75-
md.@overridden_size.should eq(1)
7675
md.@parent.should be(parent)
7776

7877
md.should eq(m({a: 3, b: 2}))
7978

8079
md.@size.should eq(2)
8180
md.@max_total_size.should eq(2)
82-
md.@overridden_size.should eq(1)
8381
md.@parent.should be_nil
8482
end
8583

src/log/metadata.cr

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ 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+
# Numer of parent elements we've copied on defrag
28+
@parent_size = uninitialized Int32
29+
3030
# @first needs to be the last ivar of Metadata. The entries are allocated together with self
3131
@first = uninitialized Entry
3232

@@ -42,18 +42,18 @@ class Log::Metadata
4242
end
4343

4444
protected def setup(@parent : Metadata?, entries : NamedTuple | Hash)
45-
@size = @overridden_size = entries.size
45+
@size = entries.size
46+
@parent_size = 0
4647
parent_size = (@parent.try(&.max_total_size) || 0)
4748
@max_total_size = @size + parent_size
4849
ptr_entries = pointerof(@first)
4950

50-
# Use parent_size as offset to make room for parent entries when defragging
5151
if entries.is_a?(NamedTuple)
52-
entries.each_with_index(parent_size) do |key, value, i|
52+
entries.each_with_index do |key, value, i|
5353
ptr_entries[i] = {key: key, value: Value.to_metadata_value(value)}
5454
end
5555
else
56-
entries.each_with_index(parent_size) do |(key, value), i|
56+
entries.each_with_index do |(key, value), i|
5757
ptr_entries[i] = {key: key, value: Value.to_metadata_value(value)}
5858
end
5959
end
@@ -93,57 +93,51 @@ class Log::Metadata
9393
# will be recomputed, but the result should be the same.
9494
#
9595
# * @parent.nil? signals if the defrag is needed/done
96-
# * The values of @overridden_size, pointerof(@first) are never changed
96+
# * The value of pointerof(@first) are never changed
9797
# * @parent is set at the very end of the method
9898
protected def defrag
9999
parent = @parent
100100
return if parent.nil?
101101

102102
ptr_entries = pointerof(@first)
103-
next_free_entry = ptr_entries
104-
# Math to get @size from end
105-
my_entries = ptr_entries + (@max_total_size - @size)
103+
next_free_entry = ptr_entries + @size
104+
total_size = @size
106105

107-
# Copy all parent entries to the beginning of "our" entries
106+
# Copy parent entries that ain't overwritten
108107
parent_size = 0
109-
parent.each_with_index do |(key, value), i|
110-
next_free_entry.value = {key: key, value: value}
111-
next_free_entry += 1
112-
parent_size += 1
113-
end
114-
115-
total_size = parent_size
116-
117-
# Move all of "our" entries, but overwrite parent if exists
118-
@size.times do |i|
119-
entry = my_entries[i]
120-
108+
parent.each do |(key, value)|
121109
overwritten = false
122-
parent_size.times do |j|
123-
if ptr_entries[j][:key] == entry[:key]
124-
# Overwrite parent entry with our entry
125-
ptr_entries[j] = {key: entry[:key], value: entry[:value]}
110+
@size.times do |i|
111+
if ptr_entries[i][:key] == key
126112
overwritten = true
127113
break
128114
end
129115
end
130116
next if overwritten
131-
132-
next_free_entry.value = {key: entry[:key], value: entry[:value]}
117+
next_free_entry.value = {key: key, value: value}
118+
parent_size += 1
133119
next_free_entry += 1
134120
total_size += 1
135121
end
136122

137123
@size = total_size
138124
@max_total_size = total_size
125+
@parent_size = parent_size
139126
@parent = nil
140127
end
141128

142129
def each(& : {Symbol, Value} ->)
143130
defrag
144131
ptr_entries = pointerof(@first)
132+
parent_size = @parent_size
133+
local_size = @size - parent_size
134+
135+
parent_size.times do |i|
136+
entry = ptr_entries[i + local_size]
137+
yield({entry[:key], entry[:value]})
138+
end
145139

146-
@size.times do |i|
140+
local_size.times do |i|
147141
entry = ptr_entries[i]
148142
yield({entry[:key], entry[:value]})
149143
end
@@ -171,9 +165,9 @@ class Log::Metadata
171165

172166
ptr_entries = pointerof(@first)
173167
# Math to get @size from end
174-
my_entries = ptr_entries + (@max_total_size - @size)
175-
@size.times do |i|
176-
return my_entries[i] if my_entries[i][:key] == key
168+
# my_entries = ptr_entries + (@max_total_size - @size)
169+
(@size - @parent_size).times do |i|
170+
return ptr_entries[i] if ptr_entries[i][:key] == key
177171
end
178172

179173
return parent.find_entry(key) if parent

0 commit comments

Comments
 (0)