Files
redux-scraper/app/models/log_store_sst_entry.rb
2025-01-01 03:29:53 +00:00

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