Skip to content

Commit

Permalink
Improve documentation and specs for SummaryManager
Browse files Browse the repository at this point in the history
  • Loading branch information
rhannequin committed Jan 7, 2025
1 parent 087b6e2 commit 0b987f0
Show file tree
Hide file tree
Showing 2 changed files with 389 additions and 21 deletions.
129 changes: 108 additions & 21 deletions lib/ephem/io/summary_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,98 @@

module Ephem
module IO
# The SummaryManager class handles the parsing and iteration of summary
# records in a Double Precision Array File (DAF). It manages the extraction
# of summary records and their associated names, handling both little and
# big endian formats.
#
# Each summary record consists of:
# * Control information (24 bytes)
# * Multiple summary entries (variable length)
# * Associated name entries in the following record
#
# The class provides iteration capabilities over all summaries in the file,
# handling record chains and proper binary unpacking based on the file's
# endianness.
#
# @example Basic usage
# binary_reader = Ephem::IO::BinaryReader.new(file)
# record_data = Ephem::IO::RecordParser
# .new
# .parse_file_record(first_record)
# manager = Ephem::IO::SummaryManager.new(
# record_data: record_data,
# binary_reader: binary_reader,
# endianness: :little
# )
#
# manager.each_summary do |name, values|
# puts "Summary #{name}: #{values.inspect}"
# end
#
# @example Using as an enumerator
# summaries = manager.each_summary.map do |name, values|
# { name: name, doubles: values[0..1], integer: values[2] }
# end
#
# @api public
class SummaryManager
SUMMARY_RECORD_SIZE = 1024
VALID_ENDIANNESS = [:little, :big].freeze

# Initializes a new SummaryManager instance
#
# @param record_data [RecordData] Data extracted from the file record,
# containing information about doubles count, integers count, and record
# chain navigation
# @param binary_reader [BinaryReader] Reader instance for accessing binary
# data from the DAF file
# @param endianness [Symbol] Endianness of the binary data, either :little
# or :big
#
# @raise [ArgumentError] If endianness is neither :little nor :big
def initialize(record_data:, binary_reader:, endianness:)
validate_endianness!(endianness)
@record_data = record_data
@binary_reader = binary_reader
@endianness = endianness
@parser = RecordParser.new(endianness: endianness)
setup_summary_format
end

# Iterates through each summary in the DAF file
#
# This method traverses the chain of summary records, extracting both the
# summary data and associated names. It handles proper binary unpacking
# based on the file's endianness and the record format specification.
#
# If no block is given, returns an Enumerator for the summaries.
#
# @yield [name, values] Yields each summary's name and values
# @yieldparam name [String] The name associated with the summary
# @yieldparam values [Array<Float, Integer>] Array containing the summary
# values, with doubles followed by integers according to the record
# format
#
# @return [Enumerator] If no block given, returns an Enumerator that will
# iterate over all summaries
# @return [nil] If block given, returns nil after iteration completes
def each_summary
return enum_for(:each_summary) unless block_given?
return to_enum(__method__) unless block_given?

record_number = @record_data.forward_record
while record_number != 0
process_summary_record(record_number) do |name, values|
yield name, values
end
record_number = get_next_record(record_number)
iterate_summary_chain(@record_data.forward_record) do |name, values|
yield name, values
end
end

private

def validate_endianness!(endianness)
return if VALID_ENDIANNESS.include?(endianness)

raise EndiannessError,
"Invalid endianness: #{endianness}. Must be one of: #{VALID_ENDIANNESS.join(", ")}"
end

def setup_summary_format
@summary_format = build_summary_format
@summary_length = calculate_summary_length
Expand All @@ -33,9 +102,9 @@ def setup_summary_format
end

def build_summary_format
doubles = "#{endian_double}#{@record_data.double_count}"
integers = "#{endian_uint32}#{@record_data.integer_count}"
doubles + integers
doubles_format = "#{endian_double}#{@record_data.double_count}"
integers_format = "#{endian_uint32}#{@record_data.integer_count}"
doubles_format + integers_format
end

def calculate_summary_length
Expand All @@ -49,23 +118,42 @@ def calculate_summary_step
end

def calculate_summaries_per_record
(1024 - RecordParser::SUMMARY_CONTROL_SIZE) / @summary_step
(
SUMMARY_RECORD_SIZE - RecordParser::SUMMARY_CONTROL_SIZE
) / @summary_step
end

def iterate_summary_chain(record_number)
while record_number != 0
process_summary_record(record_number) do |name, values|
yield name, values
end
record_number = get_next_record(record_number)
end
end

def process_summary_record(record_number)
data = @binary_reader.read_record(record_number)
control = @parser.parse_summary_control(
data[0, RecordParser::SUMMARY_CONTROL_SIZE]
)

control = parse_control_data(data)
summary_data = extract_summary_data(data)
name_data = @binary_reader.read_record(record_number + 1)

process_summaries(control[:n_summaries], summary_data, name_data) do |name, values|
process_summaries(
control[:n_summaries],
summary_data,
name_data
) do |name, values|
yield name, values
end
end

def parse_control_data(data)
control_data = data[0, RecordParser::SUMMARY_CONTROL_SIZE]
RecordParser
.new(endianness: @endianness)
.parse_summary_control(control_data)
end

def extract_summary_data(data)
data[RecordParser::SUMMARY_CONTROL_SIZE..]
end
Expand All @@ -80,19 +168,18 @@ def process_summaries(count, summary_data, name_data)
end

def extract_values(data)
return [] if data.nil? || data.empty?
data.unpack(@summary_format)
end

def extract_name(data)
return "" if data.nil? || data.empty?
data.strip
end

def get_next_record(record_number)
data = @binary_reader.read_record(record_number)
control = @parser.parse_summary_control(
data[0, RecordParser::SUMMARY_CONTROL_SIZE]
)
control[:next_record]
parse_control_data(data)[:next_record]
end

def endian_double
Expand Down
Loading

0 comments on commit 0b987f0

Please sign in to comment.