Class: Omnizip::Formats::Rar::Rar5::MultiVolume::VolumeSplitter

Inherits:
Object
  • Object
show all
Defined in:
lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb

Overview

Volume splitter for multi-volume archives

This class handles splitting compressed data streams across multiple volumes while respecting size boundaries and file atomicity.

Examples:

Split data across volumes

splitter = VolumeSplitter.new(max_volume_size: 10_485_760)
splitter.start_volume(1)
splitter.write_to_current_volume(file1_data)
if !splitter.can_fit_in_current_volume?(file2_data.bytesize)
  splitter.finalize_volume
  splitter.start_volume(2)
end

Constant Summary collapse

HEADER_OVERHEAD =

Minimum space reserved for headers (signature + main + end headers)

1024

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_volume_size:) ⇒ VolumeSplitter

Initialize volume splitter

Parameters:

  • max_volume_size (Integer)

    Maximum size per volume in bytes



40
41
42
43
44
45
46
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 40

def initialize(max_volume_size:)
  @max_volume_size = max_volume_size
  @current_volume_number = 0
  @current_volume_bytes = 0
  @current_volume_data = []
  @volumes = []
end

Instance Attribute Details

#current_volume_bytesInteger (readonly)

Returns Bytes written to current volume.

Returns:

  • (Integer)

    Bytes written to current volume



29
30
31
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 29

def current_volume_bytes
  @current_volume_bytes
end

#current_volume_numberInteger (readonly)

Returns Current volume number (1-based).

Returns:

  • (Integer)

    Current volume number (1-based)



26
27
28
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 26

def current_volume_number
  @current_volume_number
end

#max_volume_sizeInteger (readonly)

Returns Maximum size per volume in bytes.

Returns:

  • (Integer)

    Maximum size per volume in bytes



23
24
25
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 23

def max_volume_size
  @max_volume_size
end

#volumesArray<Hash> (readonly)

Returns Volume metadata.

Returns:

  • (Array<Hash>)

    Volume metadata



32
33
34
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 32

def volumes
  @volumes
end

Class Method Details

.needs_splitting?(total_size, max_volume_size) ⇒ Boolean

Check if archive needs splitting

Parameters:

  • total_size (Integer)

    Total archive size

Returns:

  • (Boolean)

    true if splitting needed



147
148
149
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 147

def self.needs_splitting?(total_size, max_volume_size)
  total_size > max_volume_size
end

Instance Method Details

#calculate_file_distribution(files) ⇒ Array<Array<Integer>>

Calculate optimal file distribution across volumes

This method determines how to distribute files across volumes to minimize volume count while respecting atomicity.

Parameters:

  • files (Array<Hash>)

    File information with :compressed_size

Returns:

  • (Array<Array<Integer>>)

    Volume assignments (file indices per volume)



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 108

def calculate_file_distribution(files)
  distribution = []
  current_volume_files = []
  current_volume_used = HEADER_OVERHEAD

  files.each_with_index do |file, idx|
    file_size = file[:compressed_size] + file[:header_size]

    # Check if file fits in current volume
    if (current_volume_used + file_size) <= @max_volume_size
      current_volume_files << idx
      current_volume_used += file_size
    else
      # Start new volume
      distribution << current_volume_files unless current_volume_files.empty?
      current_volume_files = [idx]
      current_volume_used = HEADER_OVERHEAD + file_size

      # Check if single file exceeds volume size
      if current_volume_used > @max_volume_size
        # File must span multiple volumes (not implemented yet)
        # For now, just place it in its own volume
        distribution << [idx]
        current_volume_files = []
        current_volume_used = HEADER_OVERHEAD
      end
    end
  end

  # Add final volume if not empty
  distribution << current_volume_files unless current_volume_files.empty?

  distribution
end

#can_fit_in_current_volume?(data_size) ⇒ Boolean

Check if data can fit in current volume

Parameters:

  • data_size (Integer)

    Size of data to write

Returns:

  • (Boolean)

    true if data fits, false if new volume needed



62
63
64
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 62

def can_fit_in_current_volume?(data_size)
  (@current_volume_bytes + data_size) <= @max_volume_size
end

#finalize_volumeHash

Finalize current volume

Returns:

  • (Hash)

    Volume metadata



90
91
92
93
94
95
96
97
98
99
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 90

def finalize_volume
  volume_info = {
    number: @current_volume_number,
    size: @current_volume_bytes,
    data: @current_volume_data.join,
  }

  @volumes << volume_info
  volume_info
end

#remaining_spaceInteger

Get remaining space in current volume

Returns:

  • (Integer)

    Bytes available



69
70
71
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 69

def remaining_space
  @max_volume_size - @current_volume_bytes
end

#start_volume(volume_number) ⇒ void

This method returns an undefined value.

Start a new volume

Parameters:

  • volume_number (Integer)

    Volume number (1-based)



52
53
54
55
56
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 52

def start_volume(volume_number)
  @current_volume_number = volume_number
  @current_volume_bytes = HEADER_OVERHEAD # Reserve space for headers
  @current_volume_data = []
end

#write_to_current_volume(data) ⇒ void

This method returns an undefined value.

Write data to current volume

Parameters:

  • data (String)

    Data to write

Raises:

  • (RuntimeError)

    if no volume is active

  • (RuntimeError)

    if data doesn't fit



79
80
81
82
83
84
85
# File 'lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb', line 79

def write_to_current_volume(data)
  raise "No active volume" if @current_volume_number.zero?
  raise "Data doesn't fit in current volume" unless can_fit_in_current_volume?(data.bytesize)

  @current_volume_data << data
  @current_volume_bytes += data.bytesize
end