107 lines
2.3 KiB
Ruby
107 lines
2.3 KiB
Ruby
# typed: false
|
|
require "xdiff"
|
|
require "digest"
|
|
|
|
class LogStoreSstEntry < ReduxApplicationRecord
|
|
self.primary_key = :key
|
|
|
|
# columns:
|
|
# key - bytea
|
|
# base_key - bytea (optional)
|
|
# value - bytea
|
|
# contents - bytea
|
|
|
|
belongs_to :base,
|
|
foreign_key: :base_key,
|
|
primary_key: :key,
|
|
class_name: "::LogStoreSstEntry",
|
|
optional: true
|
|
|
|
def hex_key
|
|
self.class.bin2hex(self.key)
|
|
end
|
|
|
|
def patched_value
|
|
if contents
|
|
contents_digest = Digest::SHA256.digest(contents)
|
|
if contents_digest != key
|
|
raise RuntimeError(
|
|
"digest mismatch: #{HexUtil.bin2hex(contents_digest)} != #{hex_key}",
|
|
)
|
|
end
|
|
|
|
return contents
|
|
end
|
|
|
|
@patched_value ||=
|
|
begin
|
|
if base
|
|
# format is:
|
|
# 0..4 - version
|
|
# 4..8 - flags
|
|
# 8..12 - value length after patching
|
|
# 12..44 - key of the parent
|
|
# 44..rest - patch to apply to parent
|
|
patch_value = self.value[44..]
|
|
parent_value = base.patched_value
|
|
patched_value = XDiff.patch(parent_value, patch_value)
|
|
if patched_value.length != value_length
|
|
raise RuntimeError.new(
|
|
"length mismatch: #{patched_value.length} != #{value_length}",
|
|
)
|
|
end
|
|
else
|
|
# format is:
|
|
# 0..4 - version
|
|
# 4..8 - flags
|
|
# 8..rest - complete value
|
|
patched_value = self.value[8..]
|
|
end
|
|
|
|
value_digest = Digest::SHA256.digest(patched_value)
|
|
if value_digest != key
|
|
raise RuntimeError(
|
|
"digest mismatch: #{HexUtil.bin2hex(value_digest)} != #{hex_key}",
|
|
)
|
|
end
|
|
|
|
patched_value
|
|
end
|
|
end
|
|
|
|
def parent_entry
|
|
base
|
|
end
|
|
|
|
def has_parent?
|
|
@has_parent ||= (self.value_flags & 0x01) != 0
|
|
end
|
|
|
|
def value_version
|
|
self.value[0...4].unpack("L>").first
|
|
end
|
|
|
|
def value_flags
|
|
self.value[4...8].unpack("L>").first
|
|
end
|
|
|
|
def value_length
|
|
self.value[8...12].unpack("L>").first
|
|
end
|
|
|
|
def parent_key
|
|
has_parent? ? self.value[12...44] : nil
|
|
end
|
|
|
|
def self.find_by_hex_key(hex_key)
|
|
self.find_by(key: self.hex2bin(hex_key))
|
|
end
|
|
|
|
def self.hex2bin(str)
|
|
[str].pack("H*")
|
|
end
|
|
def self.bin2hex(bin)
|
|
bin.unpack("H*").first.upcase
|
|
end
|
|
end
|