ruby-lzws 1.0.0 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|