|
16 | 16 |
|
17 | 17 | require 'stringio' |
18 | 18 | require 'zlib' |
| 19 | +require 'zstd-ruby' |
19 | 20 |
|
20 | 21 | module Fluent |
21 | 22 | module Plugin |
22 | 23 | module Compressable |
23 | | - def compress(data, **kwargs) |
| 24 | + def compress(data, type: :gzip, **kwargs) |
24 | 25 | output_io = kwargs[:output_io] |
25 | 26 | io = output_io || StringIO.new |
26 | | - Zlib::GzipWriter.wrap(io) do |gz| |
27 | | - gz.write data |
| 27 | + if type == :gzip |
| 28 | + writer = Zlib::GzipWriter.new(io) |
| 29 | + elsif type == :zstd |
| 30 | + writer = Zstd::StreamWriter.new(io) |
| 31 | + else |
| 32 | + raise ArgumentError, "Unknown compression type: #{type}" |
28 | 33 | end |
29 | | - |
| 34 | + writer.write(data) |
| 35 | + writer.finish |
30 | 36 | output_io || io.string |
31 | 37 | end |
32 | 38 |
|
33 | 39 | # compressed_data is String like `compress(data1) + compress(data2) + ... + compress(dataN)` |
34 | 40 | # https://www.ruby-forum.com/topic/971591#979503 |
35 | | - def decompress(compressed_data = nil, output_io: nil, input_io: nil) |
| 41 | + def decompress(compressed_data = nil, output_io: nil, input_io: nil, type: :gzip) |
36 | 42 | case |
37 | 43 | when input_io && output_io |
38 | | - io_decompress(input_io, output_io) |
| 44 | + io_decompress(input_io, output_io, type) |
39 | 45 | when input_io |
40 | 46 | output_io = StringIO.new |
41 | | - io = io_decompress(input_io, output_io) |
| 47 | + io = io_decompress(input_io, output_io, type) |
42 | 48 | io.string |
43 | 49 | when compressed_data.nil? || compressed_data.empty? |
44 | 50 | # check compressed_data(String) is 0 length |
45 | 51 | compressed_data |
46 | 52 | when output_io |
47 | 53 | # execute after checking compressed_data is empty or not |
48 | 54 | io = StringIO.new(compressed_data) |
49 | | - io_decompress(io, output_io) |
| 55 | + io_decompress(io, output_io, type) |
50 | 56 | else |
51 | | - string_decompress(compressed_data) |
| 57 | + string_decompress(compressed_data, type) |
52 | 58 | end |
53 | 59 | end |
54 | 60 |
|
55 | 61 | private |
56 | 62 |
|
57 | | - def string_decompress(compressed_data) |
| 63 | + def string_decompress_gzip(compressed_data) |
58 | 64 | io = StringIO.new(compressed_data) |
59 | | - |
60 | 65 | out = '' |
61 | 66 | loop do |
62 | | - gz = Zlib::GzipReader.new(io) |
63 | | - out << gz.read |
64 | | - unused = gz.unused |
65 | | - gz.finish |
66 | | - |
| 67 | + reader = Zlib::GzipReader.new(io) |
| 68 | + out << reader.read |
| 69 | + unused = reader.unused |
| 70 | + reader.finish |
67 | 71 | unless unused.nil? |
68 | 72 | adjust = unused.length |
69 | 73 | io.pos -= adjust |
70 | 74 | end |
71 | 75 | break if io.eof? |
72 | 76 | end |
| 77 | + out |
| 78 | + end |
73 | 79 |
|
| 80 | + def string_decompress_zstd(compressed_data) |
| 81 | + io = StringIO.new(compressed_data) |
| 82 | + out = '' |
| 83 | + loop do |
| 84 | + reader = Zstd::StreamReader.new(io) |
| 85 | + # Zstd::StreamReader needs to specify the size of the buffer |
| 86 | + out << reader.read(1024) |
| 87 | + # Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position |
| 88 | + break if io.eof? |
| 89 | + end |
74 | 90 | out |
75 | 91 | end |
76 | 92 |
|
77 | | - def io_decompress(input, output) |
| 93 | + def string_decompress(compressed_data, type = :gzip) |
| 94 | + if type == :gzip |
| 95 | + string_decompress_gzip(compressed_data) |
| 96 | + elsif type == :zstd |
| 97 | + string_decompress_zstd(compressed_data) |
| 98 | + else |
| 99 | + raise ArgumentError, "Unknown compression type: #{type}" |
| 100 | + end |
| 101 | + end |
| 102 | + |
| 103 | + def io_decompress_gzip(input, output) |
78 | 104 | loop do |
79 | | - gz = Zlib::GzipReader.new(input) |
80 | | - v = gz.read |
| 105 | + reader = Zlib::GzipReader.new(input) |
| 106 | + v = reader.read |
81 | 107 | output.write(v) |
82 | | - unused = gz.unused |
83 | | - gz.finish |
84 | | - |
| 108 | + unused = reader.unused |
| 109 | + reader.finish |
85 | 110 | unless unused.nil? |
86 | 111 | adjust = unused.length |
87 | 112 | input.pos -= adjust |
88 | 113 | end |
89 | 114 | break if input.eof? |
90 | 115 | end |
| 116 | + output |
| 117 | + end |
91 | 118 |
|
| 119 | + def io_decompress_zstd(input, output) |
| 120 | + loop do |
| 121 | + reader = Zstd::StreamReader.new(input) |
| 122 | + # Zstd::StreamReader needs to specify the size of the buffer |
| 123 | + v = reader.read(1024) |
| 124 | + output.write(v) |
| 125 | + # Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position |
| 126 | + break if input.eof? |
| 127 | + end |
92 | 128 | output |
93 | 129 | end |
| 130 | + |
| 131 | + def io_decompress(input, output, type = :gzip) |
| 132 | + if type == :gzip |
| 133 | + io_decompress_gzip(input, output) |
| 134 | + elsif type == :zstd |
| 135 | + io_decompress_zstd(input, output) |
| 136 | + else |
| 137 | + raise ArgumentError, "Unknown compression type: #{type}" |
| 138 | + end |
| 139 | + end |
94 | 140 | end |
95 | 141 | end |
96 | 142 | end |
0 commit comments