Class: Homebrew::API::PackagesIndex Private
Overview
This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.
Byte-offset index into a signature-verified internal packages JWS payload, so commands can parse only the entries they need instead of the whole multi-megabyte document.
The index is derived, unverified cache data guarded in layers: the
payload bytes it points into are signature-verified on every run,
loading requires the recorded top-level spans to tile that payload
exactly (so the formulae and casks section spans are provably the
real top-level values) and every lookup revalidates that its offsets
sit at the expected "<name>": key inside the requested section's
span and that the slice parses. A forged or stale index therefore
cannot inject unverified content or remap a name to another entry,
even a matching key in the other section; it fails validation and
callers fall back to a full parse when Invalid is raised.
Defined Under Namespace
Classes: Invalid
Constant Summary collapse
- FORMAT_VERSION =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
1- SECTION_KEYS =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
%w[formulae casks].freeze
- MAX_FALSE_MATCH_RETRIES =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
Bounds index building when payload bytes stop round-tripping through
JSON.generate; giving up just means no index is written. 100
Instance Attribute Summary collapse
- #payload ⇒ String readonly private
- #source_stat ⇒ File::Stat readonly private
Class Method Summary collapse
-
.build(payload:, parsed:) ⇒ Hash{String => Hash{String => Array<(Integer, Integer)>}}?
private
Locates every top-level value and every formula and cask entry in the payload bytes.
- .load(target, payload:, source_stat:) ⇒ PackagesIndex? private
- .path_for(target) ⇒ Pathname private
- .source_fingerprint(stat) ⇒ Hash{String => Integer} private
-
.write!(target, payload:, parsed:, source_stat:) ⇒ void
private
Builds and persists an index for a freshly verified and parsed payload.
Instance Method Summary collapse
- #cask_hash(name) ⇒ Hash{String => T.untyped}? private
- #cask_name?(name) ⇒ Boolean private
- #cask_names ⇒ Array<String> private
- #formula_hash(name) ⇒ Hash{String => T.untyped}? private
- #formula_name?(name) ⇒ Boolean private
- #formula_names ⇒ Array<String> private
- #initialize(payload:, source_stat:, top_level:, sections:) ⇒ void constructor private
- #top_level_value(key) ⇒ T.untyped private
Constructor Details
#initialize(payload:, source_stat:, top_level:, sections:) ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
203 204 205 206 207 208 |
# File 'api/packages_index.rb', line 203 def initialize(payload:, source_stat:, top_level:, sections:) @payload = payload @source_stat = source_stat @top_level = top_level @sections = sections end |
Instance Attribute Details
#payload ⇒ String (readonly)
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
194 195 196 |
# File 'api/packages_index.rb', line 194 def payload @payload end |
#source_stat ⇒ File::Stat (readonly)
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
197 198 199 |
# File 'api/packages_index.rb', line 197 def source_stat @source_stat end |
Class Method Details
.build(payload:, parsed:) ⇒ Hash{String => Hash{String => Array<(Integer, Integer)>}}?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Locates every top-level value and every formula and cask entry in the
payload bytes. Offsets are found by searching for each JSON key in
document order and validating that the following bytes byte-match the
entry's JSON.generate round trip, so every recorded offset provably
reproduces the canonical parse.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'api/packages_index.rb', line 132 def self.build(payload:, parsed:) data = T.let({ "top_level" => {} }, T::Hash[String, T::Hash[String, [Integer, Integer]]]) SECTION_KEYS.each { |section| data[section] = {} } retries = 0 position = 0 parsed.each do |key, value| location = locate(payload, key, value, position) return nil if location.nil? value_start, value_bytesize = location T.must(data["top_level"])[key] = [value_start, value_bytesize] if SECTION_KEYS.include?(key) && value.is_a?(Hash) entry_position = value_start value.each do |name, entry| entry_location = T.let(nil, T.nilable([Integer, Integer])) loop do entry_location = locate(payload, name, entry, entry_position) break unless entry_location.nil? retries += 1 return nil if retries > MAX_FALSE_MATCH_RETRIES next_position = payload.byteindex("#{name.to_json}:", entry_position) return nil if next_position.nil? entry_position = next_position + 1 end entry_start, entry_bytesize = entry_location T.must(data[key])[name] = [entry_start, entry_bytesize] entry_position = entry_start + entry_bytesize end end position = value_start + value_bytesize end data end |
.load(target, payload:, source_stat:) ⇒ PackagesIndex?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'api/packages_index.rb', line 44 def self.load(target, payload:, source_stat:) data = JSON.parse(path_for(target).read(encoding: Encoding::UTF_8)) return unless data.is_a?(Hash) return if data["version"] != FORMAT_VERSION return if source_fingerprint(source_stat).any? { |key, value| data[key] != value } return if data["payload_bytesize"] != payload.bytesize top_level = data["top_level"] sections = data.slice(*SECTION_KEYS) return unless top_level.is_a?(Hash) return unless sections.values.all?(Hash) return unless top_level_spans_tile_payload?(payload, top_level) new(payload:, source_stat:, top_level:, sections:) rescue SystemCallError, JSON::ParserError nil end |
.path_for(target) ⇒ Pathname
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
31 32 33 |
# File 'api/packages_index.rb', line 31 def self.path_for(target) Pathname("#{target}.payload.index") end |
.source_fingerprint(stat) ⇒ Hash{String => Integer}
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
36 37 38 39 40 41 |
# File 'api/packages_index.rb', line 36 def self.source_fingerprint(stat) { "source_size" => stat.size, "source_mtime_ns" => (stat.mtime.to_r * 1_000_000_000).to_i, } end |
.write!(target, payload:, parsed:, source_stat:) ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
This method returns an undefined value.
Builds and persists an index for a freshly verified and parsed payload. Failing to build or write one only costs the fast path.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'api/packages_index.rb', line 100 def self.write!(target, payload:, parsed:, source_stat:) # Never write to a user-owned cache as root, matching `skip_download?`. return if Homebrew.running_as_root_but_not_owned_by_root? return if (data = build(payload:, parsed:)).nil? data = { "version" => FORMAT_VERSION, **source_fingerprint(source_stat), "payload_bytesize" => payload.bytesize, **data, } index_path = path_for(target) temporary_path = Pathname("#{index_path}.tmp") begin temporary_path.write(JSON.generate(data)) File.rename(temporary_path, index_path) ensure temporary_path.unlink if temporary_path.exist? end rescue SystemCallError nil end |
Instance Method Details
#cask_hash(name) ⇒ Hash{String => T.untyped}?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
216 217 218 |
# File 'api/packages_index.rb', line 216 def cask_hash(name) entry_value("casks", name) end |
#cask_name?(name) ⇒ Boolean
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
236 237 238 |
# File 'api/packages_index.rb', line 236 def cask_name?(name) @sections.fetch("casks", {}).key?(name) end |
#cask_names ⇒ Array<String>
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
226 227 228 |
# File 'api/packages_index.rb', line 226 def cask_names @sections.fetch("casks", {}).keys end |
#formula_hash(name) ⇒ Hash{String => T.untyped}?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
211 212 213 |
# File 'api/packages_index.rb', line 211 def formula_hash(name) entry_value("formulae", name) end |
#formula_name?(name) ⇒ Boolean
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
231 232 233 |
# File 'api/packages_index.rb', line 231 def formula_name?(name) @sections.fetch("formulae", {}).key?(name) end |
#formula_names ⇒ Array<String>
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
221 222 223 |
# File 'api/packages_index.rb', line 221 def formula_names @sections.fetch("formulae", {}).keys end |
#top_level_value(key) ⇒ T.untyped
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
241 242 243 244 245 246 247 248 |
# File 'api/packages_index.rb', line 241 def top_level_value(key) return if SECTION_KEYS.include?(key) location = @top_level[key] return if location.nil? slice_value(key, location) end |