ruby-lzws 1.0.0 → 1.1.4
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.
- checksums.yaml +4 -4
- data/README.md +188 -30
- data/ext/extconf.rb +36 -34
- data/ext/lzws_ext/buffer.c +30 -0
- data/ext/lzws_ext/buffer.h +23 -0
- data/ext/lzws_ext/common.h +7 -0
- data/ext/lzws_ext/error.c +33 -4
- data/ext/lzws_ext/error.h +20 -1
- data/ext/lzws_ext/io.c +58 -47
- data/ext/lzws_ext/io.h +2 -0
- data/ext/lzws_ext/macro.h +0 -2
- data/ext/lzws_ext/main.c +8 -30
- data/ext/lzws_ext/option.c +30 -10
- data/ext/lzws_ext/option.h +33 -50
- data/ext/lzws_ext/stream/compressor.c +77 -90
- data/ext/lzws_ext/stream/compressor.h +7 -4
- data/ext/lzws_ext/stream/decompressor.c +73 -90
- data/ext/lzws_ext/stream/decompressor.h +6 -3
- data/ext/lzws_ext/string.c +246 -44
- data/ext/lzws_ext/string.h +2 -0
- data/lib/lzws.rb +5 -1
- data/lib/lzws/error.rb +8 -7
- data/lib/lzws/file.rb +6 -18
- data/lib/lzws/option.rb +13 -9
- data/lib/lzws/stream/abstract.rb +10 -8
- data/lib/lzws/stream/raw/abstract.rb +24 -9
- data/lib/lzws/stream/raw/compressor.rb +9 -31
- data/lib/lzws/stream/raw/decompressor.rb +6 -24
- data/lib/lzws/stream/reader.rb +22 -19
- data/lib/lzws/stream/reader_helpers.rb +5 -3
- data/lib/lzws/stream/writer.rb +1 -1
- data/lib/lzws/stream/writer_helpers.rb +7 -10
- data/lib/lzws/string.rb +4 -2
- data/lib/lzws/validation.rb +14 -1
- data/lib/lzws/version.rb +1 -1
- metadata +74 -16
data/lib/lzws/option.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Ruby bindings for lzws library.
|
2
2
|
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
3
|
|
4
|
+
require "lzws_ext"
|
5
|
+
|
4
6
|
require_relative "error"
|
5
7
|
require_relative "validation"
|
6
8
|
|
@@ -8,11 +10,11 @@ module LZWS
|
|
8
10
|
module Option
|
9
11
|
# Default options will be compatible with UNIX compress.
|
10
12
|
|
13
|
+
DEFAULT_BUFFER_LENGTH = 0
|
11
14
|
LOWEST_MAX_CODE_BIT_LENGTH = 9
|
12
15
|
BIGGEST_MAX_CODE_BIT_LENGTH = 16
|
13
16
|
|
14
17
|
DECOMPRESSOR_DEFAULTS = {
|
15
|
-
:buffer_length => 0,
|
16
18
|
:without_magic_header => false,
|
17
19
|
:msb => false,
|
18
20
|
:unaligned_bit_groups => false,
|
@@ -26,19 +28,19 @@ module LZWS
|
|
26
28
|
)
|
27
29
|
.freeze
|
28
30
|
|
29
|
-
def self.get_compressor_options(options)
|
31
|
+
def self.get_compressor_options(options, buffer_length_names)
|
30
32
|
Validation.validate_hash options
|
31
33
|
|
32
|
-
|
34
|
+
buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
|
35
|
+
options = COMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
|
33
36
|
|
34
|
-
Validation.validate_not_negative_integer options[
|
37
|
+
buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
|
35
38
|
|
36
39
|
max_code_bit_length = options[:max_code_bit_length]
|
37
40
|
Validation.validate_positive_integer max_code_bit_length
|
38
41
|
|
39
42
|
raise ValidateError, "invalid max code bit length" if
|
40
|
-
max_code_bit_length < LOWEST_MAX_CODE_BIT_LENGTH ||
|
41
|
-
max_code_bit_length > BIGGEST_MAX_CODE_BIT_LENGTH
|
43
|
+
max_code_bit_length < LOWEST_MAX_CODE_BIT_LENGTH || max_code_bit_length > BIGGEST_MAX_CODE_BIT_LENGTH
|
42
44
|
|
43
45
|
Validation.validate_bool options[:without_magic_header]
|
44
46
|
Validation.validate_bool options[:block_mode]
|
@@ -49,12 +51,14 @@ module LZWS
|
|
49
51
|
options
|
50
52
|
end
|
51
53
|
|
52
|
-
def self.get_decompressor_options(options)
|
54
|
+
def self.get_decompressor_options(options, buffer_length_names)
|
53
55
|
Validation.validate_hash options
|
54
56
|
|
55
|
-
|
57
|
+
buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
|
58
|
+
options = DECOMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
|
59
|
+
|
60
|
+
buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
|
56
61
|
|
57
|
-
Validation.validate_not_negative_integer options[:buffer_length]
|
58
62
|
Validation.validate_bool options[:without_magic_header]
|
59
63
|
Validation.validate_bool options[:msb]
|
60
64
|
Validation.validate_bool options[:unaligned_bit_groups]
|
data/lib/lzws/stream/abstract.rb
CHANGED
@@ -9,7 +9,7 @@ require_relative "../validation"
|
|
9
9
|
module LZWS
|
10
10
|
module Stream
|
11
11
|
class Abstract
|
12
|
-
#
|
12
|
+
# Native stream is not seekable by design.
|
13
13
|
# Related methods like "seek" and "pos=" can't be implemented.
|
14
14
|
|
15
15
|
# It is not possible to maintain correspondance between bytes consumed from source and bytes written to destination by design.
|
@@ -21,18 +21,20 @@ module LZWS
|
|
21
21
|
attr_reader :stat
|
22
22
|
attr_reader :external_encoding
|
23
23
|
attr_reader :internal_encoding
|
24
|
+
attr_reader :transcode_options
|
24
25
|
attr_reader :pos
|
26
|
+
|
25
27
|
alias tell pos
|
26
28
|
|
27
|
-
def initialize(io,
|
29
|
+
def initialize(io, options = {})
|
28
30
|
@raw_stream = create_raw_stream
|
29
31
|
|
30
32
|
Validation.validate_io io
|
31
33
|
@io = io
|
32
34
|
|
33
|
-
@stat = Stat.new @io.stat
|
35
|
+
@stat = Stat.new @io.stat if @io.respond_to? :stat
|
34
36
|
|
35
|
-
set_encoding external_encoding, internal_encoding, transcode_options
|
37
|
+
set_encoding options[:external_encoding], options[:internal_encoding], options[:transcode_options]
|
36
38
|
reset_buffer
|
37
39
|
reset_io_advise
|
38
40
|
|
@@ -49,8 +51,8 @@ module LZWS
|
|
49
51
|
|
50
52
|
protected def reset_io_advise
|
51
53
|
# Both compressor and decompressor need sequential io access.
|
52
|
-
@io.advise :sequential
|
53
|
-
rescue ::Errno::ESPIPE
|
54
|
+
@io.advise :sequential if @io.respond_to? :advise
|
55
|
+
rescue ::Errno::ESPIPE
|
54
56
|
# ok
|
55
57
|
end
|
56
58
|
|
@@ -115,7 +117,6 @@ module LZWS
|
|
115
117
|
|
116
118
|
protected def target_encoding
|
117
119
|
return @internal_encoding unless @internal_encoding.nil?
|
118
|
-
|
119
120
|
return @external_encoding unless @external_encoding.nil?
|
120
121
|
|
121
122
|
::Encoding::BINARY
|
@@ -126,7 +127,8 @@ module LZWS
|
|
126
127
|
def rewind
|
127
128
|
@raw_stream = create_raw_stream
|
128
129
|
|
129
|
-
@io.rewind
|
130
|
+
@io.rewind if @io.respond_to? :rewind
|
131
|
+
|
130
132
|
reset_buffer
|
131
133
|
reset_io_advise
|
132
134
|
|
@@ -4,6 +4,7 @@
|
|
4
4
|
require "lzws_ext"
|
5
5
|
|
6
6
|
require_relative "../../error"
|
7
|
+
require_relative "../../validation"
|
7
8
|
|
8
9
|
module LZWS
|
9
10
|
module Stream
|
@@ -14,19 +15,16 @@ module LZWS
|
|
14
15
|
@is_closed = false
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
return nil if @is_closed
|
18
|
+
# -- write --
|
19
19
|
|
20
|
-
|
20
|
+
def flush(&writer)
|
21
|
+
do_not_use_after_close
|
21
22
|
|
22
|
-
|
23
|
-
@is_closed = true
|
23
|
+
Validation.validate_proc writer
|
24
24
|
|
25
|
-
|
26
|
-
end
|
25
|
+
write_result(&writer)
|
27
26
|
|
28
|
-
|
29
|
-
@is_closed
|
27
|
+
nil
|
30
28
|
end
|
31
29
|
|
32
30
|
protected def flush_destination_buffer(&writer)
|
@@ -40,6 +38,23 @@ module LZWS
|
|
40
38
|
|
41
39
|
result.bytesize
|
42
40
|
end
|
41
|
+
|
42
|
+
# -- close --
|
43
|
+
|
44
|
+
protected def do_not_use_after_close
|
45
|
+
raise UsedAfterCloseError, "used after close" if closed?
|
46
|
+
end
|
47
|
+
|
48
|
+
def close(&writer)
|
49
|
+
write_result(&writer)
|
50
|
+
|
51
|
+
@native_stream.close
|
52
|
+
@is_closed = true
|
53
|
+
end
|
54
|
+
|
55
|
+
def closed?
|
56
|
+
@is_closed
|
57
|
+
end
|
43
58
|
end
|
44
59
|
end
|
45
60
|
end
|
@@ -12,13 +12,13 @@ module LZWS
|
|
12
12
|
module Stream
|
13
13
|
module Raw
|
14
14
|
class Compressor < Abstract
|
15
|
+
BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
|
16
|
+
|
15
17
|
def initialize(options = {})
|
16
|
-
options = Option.get_compressor_options options
|
18
|
+
options = Option.get_compressor_options options, BUFFER_LENGTH_NAMES
|
17
19
|
native_stream = NativeCompressor.new options
|
18
20
|
|
19
21
|
super native_stream
|
20
|
-
|
21
|
-
@need_to_write_magic_header = !options[:without_magic_header]
|
22
22
|
end
|
23
23
|
|
24
24
|
def write(source, &writer)
|
@@ -27,11 +27,6 @@ module LZWS
|
|
27
27
|
Validation.validate_string source
|
28
28
|
Validation.validate_proc writer
|
29
29
|
|
30
|
-
if @need_to_write_magic_header
|
31
|
-
write_magic_header(&writer)
|
32
|
-
@need_to_write_magic_header = false
|
33
|
-
end
|
34
|
-
|
35
30
|
total_bytes_written = 0
|
36
31
|
|
37
32
|
loop do
|
@@ -45,8 +40,10 @@ module LZWS
|
|
45
40
|
end
|
46
41
|
|
47
42
|
unless bytes_written == source.bytesize
|
43
|
+
# :nocov:
|
48
44
|
# Compressor write should eat all provided "source" without remainder.
|
49
45
|
raise UnexpectedError, "unexpected error"
|
46
|
+
# :nocov:
|
50
47
|
end
|
51
48
|
|
52
49
|
break
|
@@ -55,28 +52,13 @@ module LZWS
|
|
55
52
|
total_bytes_written
|
56
53
|
end
|
57
54
|
|
58
|
-
|
59
|
-
|
60
|
-
need_more_destination = @native_stream.write_magic_header
|
61
|
-
|
62
|
-
if need_more_destination
|
63
|
-
flush_destination_buffer(&writer)
|
64
|
-
next
|
65
|
-
end
|
66
|
-
|
67
|
-
break
|
68
|
-
end
|
69
|
-
|
70
|
-
nil
|
71
|
-
end
|
72
|
-
|
73
|
-
def flush(&writer)
|
74
|
-
do_not_use_after_close
|
55
|
+
def close(&writer)
|
56
|
+
return nil if closed?
|
75
57
|
|
76
58
|
Validation.validate_proc writer
|
77
59
|
|
78
60
|
loop do
|
79
|
-
need_more_destination = @native_stream.
|
61
|
+
need_more_destination = @native_stream.finish
|
80
62
|
|
81
63
|
if need_more_destination
|
82
64
|
flush_destination_buffer(&writer)
|
@@ -86,14 +68,10 @@ module LZWS
|
|
86
68
|
break
|
87
69
|
end
|
88
70
|
|
89
|
-
|
71
|
+
super
|
90
72
|
|
91
73
|
nil
|
92
74
|
end
|
93
|
-
|
94
|
-
protected def do_not_use_after_close
|
95
|
-
raise UsedAfterCloseError, "compressor used after close" if @is_closed
|
96
|
-
end
|
97
75
|
end
|
98
76
|
end
|
99
77
|
end
|
@@ -4,7 +4,6 @@
|
|
4
4
|
require "lzws_ext"
|
5
5
|
|
6
6
|
require_relative "abstract"
|
7
|
-
require_relative "../../error"
|
8
7
|
require_relative "../../option"
|
9
8
|
require_relative "../../validation"
|
10
9
|
|
@@ -12,13 +11,13 @@ module LZWS
|
|
12
11
|
module Stream
|
13
12
|
module Raw
|
14
13
|
class Decompressor < Abstract
|
14
|
+
BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
|
15
|
+
|
15
16
|
def initialize(options = {})
|
16
|
-
options = Option.get_decompressor_options options
|
17
|
+
options = Option.get_decompressor_options options, BUFFER_LENGTH_NAMES
|
17
18
|
native_stream = NativeDecompressor.new options
|
18
19
|
|
19
20
|
super native_stream
|
20
|
-
|
21
|
-
@need_to_read_magic_header = !options[:without_magic_header]
|
22
21
|
end
|
23
22
|
|
24
23
|
def read(source, &writer)
|
@@ -29,19 +28,6 @@ module LZWS
|
|
29
28
|
|
30
29
|
total_bytes_read = 0
|
31
30
|
|
32
|
-
if @need_to_read_magic_header
|
33
|
-
bytes_read = @native_stream.read_magic_header source
|
34
|
-
if bytes_read == 0
|
35
|
-
# Decompressor is not able to read full magic header.
|
36
|
-
return 0
|
37
|
-
end
|
38
|
-
|
39
|
-
total_bytes_read += bytes_read
|
40
|
-
source = source.byteslice bytes_read, source.bytesize - bytes_read
|
41
|
-
|
42
|
-
@need_to_read_magic_header = false
|
43
|
-
end
|
44
|
-
|
45
31
|
loop do
|
46
32
|
bytes_read, need_more_destination = @native_stream.read source
|
47
33
|
total_bytes_read += bytes_read
|
@@ -59,19 +45,15 @@ module LZWS
|
|
59
45
|
total_bytes_read
|
60
46
|
end
|
61
47
|
|
62
|
-
def
|
63
|
-
|
48
|
+
def close(&writer)
|
49
|
+
return nil if closed?
|
64
50
|
|
65
51
|
Validation.validate_proc writer
|
66
52
|
|
67
|
-
|
53
|
+
super
|
68
54
|
|
69
55
|
nil
|
70
56
|
end
|
71
|
-
|
72
|
-
protected def do_not_use_after_close
|
73
|
-
raise UsedAfterCloseError, "decompressor used after close" if @is_closed
|
74
|
-
end
|
75
57
|
end
|
76
58
|
end
|
77
59
|
end
|
data/lib/lzws/stream/reader.rb
CHANGED
@@ -11,8 +11,6 @@ module LZWS
|
|
11
11
|
class Reader < Abstract
|
12
12
|
include ReaderHelpers
|
13
13
|
|
14
|
-
DEFAULT_IO_PORTION_BYTESIZE = 1 << 15 # 32 KB
|
15
|
-
|
16
14
|
attr_accessor :lineno
|
17
15
|
|
18
16
|
def initialize(source_io, options = {}, *args)
|
@@ -20,17 +18,22 @@ module LZWS
|
|
20
18
|
|
21
19
|
super source_io, *args
|
22
20
|
|
23
|
-
|
24
|
-
@options.delete :io_portion_bytesize
|
25
|
-
|
26
|
-
Validation.validate_positive_integer io_portion_bytesize unless io_portion_bytesize.nil?
|
27
|
-
@io_portion_bytesize = io_portion_bytesize || DEFAULT_IO_PORTION_BYTESIZE
|
28
|
-
|
21
|
+
initialize_source_buffer_length
|
29
22
|
reset_io_remainder
|
30
23
|
|
31
24
|
@lineno = 0
|
32
25
|
end
|
33
26
|
|
27
|
+
protected def initialize_source_buffer_length
|
28
|
+
source_buffer_length = @options[:source_buffer_length]
|
29
|
+
Validation.validate_not_negative_integer source_buffer_length unless source_buffer_length.nil?
|
30
|
+
|
31
|
+
source_buffer_length = Buffer::DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR \
|
32
|
+
if source_buffer_length == 0 || source_buffer_length.nil?
|
33
|
+
|
34
|
+
@source_buffer_length = source_buffer_length
|
35
|
+
end
|
36
|
+
|
34
37
|
protected def create_raw_stream
|
35
38
|
Raw::Decompressor.new @options
|
36
39
|
end
|
@@ -50,12 +53,12 @@ module LZWS
|
|
50
53
|
unless bytes_to_read.nil?
|
51
54
|
return nil if eof?
|
52
55
|
|
53
|
-
|
56
|
+
read_more_to_buffer until @buffer.bytesize >= bytes_to_read || @io.eof?
|
54
57
|
|
55
58
|
return read_bytes_from_buffer bytes_to_read, out_buffer
|
56
59
|
end
|
57
60
|
|
58
|
-
|
61
|
+
read_more_to_buffer until @io.eof?
|
59
62
|
|
60
63
|
result = @buffer
|
61
64
|
reset_buffer
|
@@ -68,21 +71,21 @@ module LZWS
|
|
68
71
|
result
|
69
72
|
end
|
70
73
|
|
71
|
-
protected def
|
72
|
-
io_data = @io.read @
|
74
|
+
protected def read_more_to_buffer
|
75
|
+
io_data = @io.read @source_buffer_length
|
73
76
|
append_io_data_to_buffer io_data
|
74
77
|
end
|
75
78
|
|
76
79
|
def readpartial(bytes_to_read = nil, out_buffer = nil)
|
77
80
|
raise ::EOFError if eof?
|
78
81
|
|
79
|
-
|
82
|
+
readpartial_to_buffer until @buffer.bytesize >= bytes_to_read || @io.eof?
|
80
83
|
|
81
84
|
read_bytes_from_buffer bytes_to_read, out_buffer
|
82
85
|
end
|
83
86
|
|
84
|
-
protected def
|
85
|
-
io_data = @io.readpartial @
|
87
|
+
protected def readpartial_to_buffer
|
88
|
+
io_data = @io.readpartial @source_buffer_length
|
86
89
|
append_io_data_to_buffer io_data
|
87
90
|
end
|
88
91
|
|
@@ -105,13 +108,13 @@ module LZWS
|
|
105
108
|
def read_nonblock(bytes_to_read, out_buffer = nil, *options)
|
106
109
|
raise ::EOFError if eof?
|
107
110
|
|
108
|
-
|
111
|
+
read_more_to_buffer_nonblock(*options) until @buffer.bytesize >= bytes_to_read || @io.eof?
|
109
112
|
|
110
113
|
read_bytes_from_buffer bytes_to_read, out_buffer
|
111
114
|
end
|
112
115
|
|
113
|
-
protected def
|
114
|
-
io_data = @io.read_nonblock @
|
116
|
+
protected def read_more_to_buffer_nonblock(*options)
|
117
|
+
io_data = @io.read_nonblock @source_buffer_length, *options
|
115
118
|
append_io_data_to_buffer io_data
|
116
119
|
end
|
117
120
|
|
@@ -144,7 +147,7 @@ module LZWS
|
|
144
147
|
end
|
145
148
|
|
146
149
|
protected def transcode_to_internal(data)
|
147
|
-
data = data.encode @internal_encoding,
|
150
|
+
data = data.encode @internal_encoding, **@transcode_options unless @internal_encoding.nil?
|
148
151
|
data
|
149
152
|
end
|
150
153
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# Ruby bindings for lzws library.
|
2
2
|
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
3
|
|
4
|
+
require "English"
|
5
|
+
|
4
6
|
require_relative "../validation"
|
5
7
|
|
6
8
|
module LZWS
|
@@ -156,7 +158,7 @@ module LZWS
|
|
156
158
|
protected def ungetstring(string)
|
157
159
|
Validation.validate_string string
|
158
160
|
|
159
|
-
string = ::String.new string, :encoding => @internal_encoding
|
161
|
+
string = ::String.new string, :encoding => @internal_encoding unless @internal_encoding.nil?
|
160
162
|
string = transcode_to_external string unless @external_encoding.nil?
|
161
163
|
|
162
164
|
string.force_encoding ::Encoding::BINARY
|
@@ -172,8 +174,8 @@ module LZWS
|
|
172
174
|
Validation.validate_string file_path
|
173
175
|
Validation.validate_proc block
|
174
176
|
|
175
|
-
::File.open file_path, "rb" do |
|
176
|
-
reader = new
|
177
|
+
::File.open file_path, "rb" do |io|
|
178
|
+
reader = new io, *args
|
177
179
|
|
178
180
|
begin
|
179
181
|
yield reader
|