multi_compress 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/GET_STARTED.md +1 -1
  4. data/README.md +1 -1
  5. data/THIRD_PARTY_NOTICES.md +1 -1
  6. data/ext/multi_compress/multi_compress.c +230 -77
  7. data/ext/multi_compress/vendor/.vendored +1 -1
  8. data/ext/multi_compress/vendor/brotli/c/common/version.h +2 -2
  9. data/ext/multi_compress/vendor/brotli/c/dec/decode.c +59 -30
  10. data/ext/multi_compress/vendor/brotli/c/include/brotli/decode.h +1 -3
  11. data/lib/multi_compress/active_record.rb +11 -5
  12. data/lib/multi_compress/codec.rb +164 -94
  13. data/lib/multi_compress/database/api.rb +42 -0
  14. data/lib/multi_compress/database/decoding.rb +42 -0
  15. data/lib/multi_compress/database/dictionary.rb +72 -0
  16. data/lib/multi_compress/database/encoding.rb +31 -0
  17. data/lib/multi_compress/database/header.rb +71 -0
  18. data/lib/multi_compress/database/operations.rb +73 -0
  19. data/lib/multi_compress/database/validation.rb +95 -0
  20. data/lib/multi_compress/database.rb +10 -282
  21. data/lib/multi_compress/db_deployment/bundle_archiver.rb +70 -0
  22. data/lib/multi_compress/db_deployment/bundle_metadata.rb +56 -0
  23. data/lib/multi_compress/db_deployment/bundle_stager.rb +62 -0
  24. data/lib/multi_compress/db_deployment/commands.rb +309 -0
  25. data/lib/multi_compress/db_deployment/deployment_bundle.rb +80 -0
  26. data/lib/multi_compress/db_deployment/deterministic_tar_writer.rb +76 -0
  27. data/lib/multi_compress/db_deployment/dictionary_registry.rb +244 -0
  28. data/lib/multi_compress/db_deployment/readable_view.rb +112 -0
  29. data/lib/multi_compress/db_deployment/readable_view_options.rb +51 -0
  30. data/lib/multi_compress/db_deployment/sql_identifiers.rb +45 -0
  31. data/lib/multi_compress/db_deployment.rb +10 -844
  32. data/lib/multi_compress/reader/buffer_operations.rb +57 -0
  33. data/lib/multi_compress/reader/read_operations.rb +67 -0
  34. data/lib/multi_compress/reader.rb +97 -0
  35. data/lib/multi_compress/stream_io.rb +13 -0
  36. data/lib/multi_compress/version.rb +1 -1
  37. data/lib/multi_compress/writer.rb +85 -0
  38. data/lib/multi_compress.rb +42 -333
  39. metadata +25 -6
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MultiCompress
4
+ class Reader
5
+ BUFFER_COMPACT_THRESHOLD = 64 * 1024
6
+
7
+ module BufferOperations
8
+ private
9
+
10
+ def buffer_size
11
+ @buffer.bytesize - @buffer_pos
12
+ end
13
+
14
+ def buffer_empty?
15
+ @buffer_pos >= @buffer.bytesize
16
+ end
17
+
18
+ def buffer_append(data)
19
+ compact_buffer_if_needed
20
+ @buffer << data
21
+ end
22
+
23
+ def compact_buffer_if_needed
24
+ return if @buffer_pos.zero?
25
+
26
+ total = @buffer.bytesize
27
+ return unless @buffer_pos >= BUFFER_COMPACT_THRESHOLD && @buffer_pos * 2 >= total
28
+
29
+ @buffer = @buffer.byteslice(@buffer_pos, total - @buffer_pos)
30
+ @buffer_pos = 0
31
+ end
32
+
33
+ def buffer_includes?(separator)
34
+ idx = @buffer.index(separator, @buffer_pos)
35
+ !idx.nil?
36
+ end
37
+
38
+ def extract_line(separator)
39
+ idx = @buffer.index(separator, @buffer_pos)
40
+ end_pos = idx + separator.bytesize
41
+ result = @buffer.byteslice(@buffer_pos, end_pos - @buffer_pos)
42
+ @buffer_pos = end_pos
43
+ compact_buffer_if_needed
44
+ result
45
+ end
46
+
47
+ def consume_buffer
48
+ result = @buffer.byteslice(@buffer_pos, @buffer.bytesize - @buffer_pos) || +"".b
49
+ @buffer.clear
50
+ @buffer_pos = 0
51
+ result
52
+ end
53
+ end
54
+
55
+ private_constant :BufferOperations
56
+ end
57
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MultiCompress
4
+ class Reader
5
+ module ReadOperations
6
+ private
7
+
8
+ def read_all
9
+ return nil if @eof && buffer_empty?
10
+
11
+ result = consume_buffer
12
+ result = +"".b if result.empty?
13
+ append_remaining_stream(result)
14
+ result.empty? ? nil : result
15
+ end
16
+
17
+ def append_remaining_stream(result)
18
+ until @eof
19
+ chunk = read_compressed_chunk
20
+ break unless chunk && !chunk.empty?
21
+
22
+ decompressed = @inflater.write(chunk)
23
+ result << decompressed if decompressed
24
+ end
25
+
26
+ final = @inflater.finish
27
+ result << final if final && !final.empty?
28
+ end
29
+
30
+ def read_exactly(length)
31
+ return nil if @eof && buffer_empty?
32
+
33
+ fill_buffer_until { buffer_size >= length }
34
+ return read_buffer_slice(length) if buffer_size >= length
35
+
36
+ consume_buffer unless buffer_empty?
37
+ end
38
+
39
+ def read_buffer_slice(length)
40
+ result = @buffer.byteslice(@buffer_pos, length)
41
+ @buffer_pos += length
42
+ compact_buffer_if_needed
43
+ result
44
+ end
45
+
46
+ def fill_buffer_until
47
+ until yield || @eof
48
+ chunk = read_compressed_chunk
49
+ if chunk.nil? || chunk.empty?
50
+ @eof = true
51
+ break
52
+ end
53
+ decompressed = @inflater.write(chunk)
54
+ buffer_append(decompressed) if decompressed
55
+ end
56
+ end
57
+
58
+ def read_compressed_chunk
59
+ @io.read(CHUNK_SIZE)
60
+ rescue EOFError
61
+ nil
62
+ end
63
+ end
64
+
65
+ private_constant :ReadOperations
66
+ end
67
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MultiCompress
4
+ class Reader
5
+ CHUNK_SIZE = 8192
6
+ require "multi_compress/reader/buffer_operations"
7
+ require "multi_compress/reader/read_operations"
8
+ include BufferOperations
9
+ include ReadOperations
10
+
11
+ def self.open(path_or_io, algo: nil, dictionary: nil, **opts, &block)
12
+ io, algo, owned = StreamIO.resolve_io(path_or_io, algo, mode: "rb")
13
+ reader = new(io, algo: algo, dictionary: dictionary, **opts)
14
+ reader.instance_variable_set(:@owned_io, owned)
15
+
16
+ return reader unless block
17
+
18
+ begin
19
+ yield reader
20
+ ensure
21
+ reader.close
22
+ end
23
+ end
24
+
25
+ def initialize(io, algo: nil, dictionary: nil, **opts)
26
+ @io = io
27
+ @inflater = Inflater.new(algo: algo, dictionary: dictionary, **opts)
28
+ @closed = false
29
+ @owned_io = false
30
+ @buffer = +"".b
31
+ @buffer_pos = 0
32
+ @eof = false
33
+ end
34
+
35
+ def read(length = nil)
36
+ ensure_open!
37
+ return read_all if length.nil?
38
+
39
+ read_exactly(length)
40
+ end
41
+
42
+ def gets(separator = "\n")
43
+ ensure_open!
44
+ return nil if @eof && buffer_empty?
45
+
46
+ fill_buffer_until { buffer_includes?(separator) }
47
+
48
+ return extract_line(separator) if buffer_includes?(separator)
49
+ return consume_buffer unless buffer_empty?
50
+
51
+ nil
52
+ end
53
+
54
+ def close
55
+ return if @closed
56
+
57
+ @inflater.close
58
+ @io.close if @owned_io && @io.respond_to?(:close)
59
+ @closed = true
60
+ nil
61
+ end
62
+
63
+ def closed?
64
+ @closed
65
+ end
66
+
67
+ def eof?
68
+ @eof && buffer_empty?
69
+ end
70
+
71
+ def each_line
72
+ return enum_for(:each_line) unless block_given?
73
+
74
+ while (line = gets)
75
+ yield line
76
+ end
77
+ end
78
+
79
+ def each_chunk(size)
80
+ return enum_for(:each_chunk, size) unless block_given?
81
+
82
+ while (chunk = read(size))
83
+ yield chunk
84
+ end
85
+ end
86
+
87
+ def readline
88
+ gets || raise(EOFError, "end of file reached")
89
+ end
90
+
91
+ private
92
+
93
+ def ensure_open!
94
+ raise StreamError, "reader is closed" if @closed
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MultiCompress
4
+ module StreamIO
5
+ module_function
6
+
7
+ def resolve_io(path_or_io, algo, mode:)
8
+ return [path_or_io, algo, false] unless path_or_io.is_a?(String)
9
+
10
+ [File.open(path_or_io, mode), algo || MultiCompress.algo_from_ext(path_or_io), true]
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MultiCompress
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MultiCompress
4
+ class Writer
5
+ CHUNK_BUFFER_SIZE = 8192
6
+
7
+ def self.open(path_or_io, algo: nil, level: nil, dictionary: nil, &block)
8
+ io, algo, owned = StreamIO.resolve_io(path_or_io, algo, mode: "wb")
9
+ writer = new(io, algo: algo || :zstd, level: level, dictionary: dictionary)
10
+ writer.instance_variable_set(:@owned_io, owned)
11
+
12
+ return writer unless block
13
+
14
+ begin
15
+ yield writer
16
+ ensure
17
+ writer.close
18
+ end
19
+ end
20
+
21
+ def initialize(io, algo: :zstd, level: nil, dictionary: nil)
22
+ @io = io
23
+ @deflater = Deflater.new(algo: algo, level: level, dictionary: dictionary)
24
+ @closed = false
25
+ @owned_io = false
26
+ end
27
+
28
+ def write(data)
29
+ ensure_open!
30
+ bytes = data.to_s
31
+ flush_compressed(@deflater.write(bytes))
32
+ bytes.bytesize
33
+ end
34
+
35
+ def flush
36
+ ensure_open!
37
+ flush_compressed(@deflater.flush)
38
+ @io.flush if @io.respond_to?(:flush)
39
+ self
40
+ end
41
+
42
+ def close
43
+ return if @closed
44
+
45
+ flush_compressed(@deflater.finish)
46
+ @deflater.close
47
+ @io.close if @owned_io && @io.respond_to?(:close)
48
+ @closed = true
49
+ nil
50
+ end
51
+
52
+ def closed?
53
+ @closed
54
+ end
55
+
56
+ def <<(data)
57
+ write(data)
58
+ self
59
+ end
60
+
61
+ def puts(*args)
62
+ args.flatten.each do |arg|
63
+ str = arg.to_s
64
+ write(str)
65
+ write("\n") unless str.end_with?("\n")
66
+ end
67
+ nil
68
+ end
69
+
70
+ def print(*args)
71
+ args.each { |arg| write(arg.to_s) }
72
+ nil
73
+ end
74
+
75
+ private
76
+
77
+ def ensure_open!
78
+ raise StreamError, "writer is closed" if @closed
79
+ end
80
+
81
+ def flush_compressed(compressed)
82
+ @io.write(compressed) if compressed && !compressed.empty?
83
+ end
84
+ end
85
+ end