ruby-lzws 1.0.0
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 +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +21 -0
- data/README.md +188 -0
- data/ext/extconf.rb +77 -0
- data/ext/lzws_ext/common.h +9 -0
- data/ext/lzws_ext/error.c +14 -0
- data/ext/lzws_ext/error.h +11 -0
- data/ext/lzws_ext/io.c +92 -0
- data/ext/lzws_ext/io.h +12 -0
- data/ext/lzws_ext/macro.h +15 -0
- data/ext/lzws_ext/main.c +42 -0
- data/ext/lzws_ext/option.c +22 -0
- data/ext/lzws_ext/option.h +60 -0
- data/ext/lzws_ext/stream/compressor.c +217 -0
- data/ext/lzws_ext/stream/compressor.h +27 -0
- data/ext/lzws_ext/stream/decompressor.c +206 -0
- data/ext/lzws_ext/stream/decompressor.h +26 -0
- data/ext/lzws_ext/string.c +78 -0
- data/ext/lzws_ext/string.h +12 -0
- data/lib/lzws/error.rb +20 -0
- data/lib/lzws/file.rb +56 -0
- data/lib/lzws/option.rb +66 -0
- data/lib/lzws/stream/abstract.rb +153 -0
- data/lib/lzws/stream/delegates.rb +36 -0
- data/lib/lzws/stream/raw/abstract.rb +46 -0
- data/lib/lzws/stream/raw/compressor.rb +100 -0
- data/lib/lzws/stream/raw/decompressor.rb +78 -0
- data/lib/lzws/stream/reader.rb +163 -0
- data/lib/lzws/stream/reader_helpers.rb +192 -0
- data/lib/lzws/stream/stat.rb +78 -0
- data/lib/lzws/stream/writer.rb +145 -0
- data/lib/lzws/stream/writer_helpers.rb +93 -0
- data/lib/lzws/string.rb +27 -0
- data/lib/lzws/validation.rb +36 -0
- data/lib/lzws/version.rb +6 -0
- data/lib/lzws.rb +4 -0
- metadata +163 -0
@@ -0,0 +1,192 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "../validation"
|
5
|
+
|
6
|
+
module LZWS
|
7
|
+
module Stream
|
8
|
+
module ReaderHelpers
|
9
|
+
def getbyte
|
10
|
+
read 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def each_byte(&block)
|
14
|
+
each_string method(:getbyte), &block
|
15
|
+
end
|
16
|
+
|
17
|
+
def readbyte
|
18
|
+
readstring method(:getbyte)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ungetbyte(byte)
|
22
|
+
Validation.validate_string byte
|
23
|
+
|
24
|
+
@buffer.prepend byte
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# -- char --
|
30
|
+
|
31
|
+
def getc
|
32
|
+
if @external_encoding.nil?
|
33
|
+
byte = getbyte
|
34
|
+
return nil if byte.nil?
|
35
|
+
|
36
|
+
return transcode_to_internal byte
|
37
|
+
end
|
38
|
+
|
39
|
+
char = ::String.new :encoding => ::Encoding::BINARY
|
40
|
+
|
41
|
+
# Read one byte until valid string will appear.
|
42
|
+
loop do
|
43
|
+
byte = getbyte
|
44
|
+
return nil if byte.nil?
|
45
|
+
|
46
|
+
char << byte
|
47
|
+
|
48
|
+
char.force_encoding @external_encoding
|
49
|
+
return transcode_to_internal char if char.valid_encoding?
|
50
|
+
|
51
|
+
char.force_encoding ::Encoding::BINARY
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def readchar
|
56
|
+
readstring method(:getc)
|
57
|
+
end
|
58
|
+
|
59
|
+
def each_char(&block)
|
60
|
+
each_string method(:getc), &block
|
61
|
+
end
|
62
|
+
|
63
|
+
def ungetc(char)
|
64
|
+
ungetstring char
|
65
|
+
end
|
66
|
+
|
67
|
+
# -- lines --
|
68
|
+
|
69
|
+
def gets(separator = $OUTPUT_RECORD_SEPARATOR, limit = nil)
|
70
|
+
# Limit can be a first argument.
|
71
|
+
if separator.is_a? ::Numeric
|
72
|
+
limit = separator
|
73
|
+
separator = $OUTPUT_RECORD_SEPARATOR
|
74
|
+
end
|
75
|
+
|
76
|
+
line_ending =
|
77
|
+
if separator.nil?
|
78
|
+
nil
|
79
|
+
else
|
80
|
+
Validation.validate_string separator
|
81
|
+
::String.new separator, :encoding => target_encoding
|
82
|
+
end
|
83
|
+
|
84
|
+
Validation.validate_positive_integer limit unless limit.nil?
|
85
|
+
|
86
|
+
line = ::String.new :encoding => target_encoding
|
87
|
+
|
88
|
+
loop do
|
89
|
+
char = getc
|
90
|
+
|
91
|
+
if char.nil?
|
92
|
+
return nil if line.empty?
|
93
|
+
|
94
|
+
break
|
95
|
+
end
|
96
|
+
|
97
|
+
line << char
|
98
|
+
|
99
|
+
break if
|
100
|
+
(!line_ending.nil? && line.end_with?(line_ending)) ||
|
101
|
+
(!limit.nil? && line.length >= limit)
|
102
|
+
end
|
103
|
+
|
104
|
+
@lineno += 1
|
105
|
+
|
106
|
+
line
|
107
|
+
end
|
108
|
+
|
109
|
+
def readline
|
110
|
+
readstring method(:gets)
|
111
|
+
end
|
112
|
+
|
113
|
+
def readlines
|
114
|
+
lines = []
|
115
|
+
each_line { |line| lines << line }
|
116
|
+
|
117
|
+
lines
|
118
|
+
end
|
119
|
+
|
120
|
+
def each_line(&block)
|
121
|
+
each_string method(:gets), &block
|
122
|
+
end
|
123
|
+
|
124
|
+
alias each each_line
|
125
|
+
|
126
|
+
def ungetline(line)
|
127
|
+
ungetstring line
|
128
|
+
|
129
|
+
@lineno -= 1
|
130
|
+
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# -- common --
|
135
|
+
|
136
|
+
protected def readstring(each_proc)
|
137
|
+
string = each_proc.call
|
138
|
+
raise ::EOFError if string.nil?
|
139
|
+
|
140
|
+
string
|
141
|
+
end
|
142
|
+
|
143
|
+
protected def each_string(each_proc, &block)
|
144
|
+
return enum_for __method__ unless block.is_a? ::Proc
|
145
|
+
|
146
|
+
loop do
|
147
|
+
string = each_proc.call
|
148
|
+
break if string.nil?
|
149
|
+
|
150
|
+
yield string
|
151
|
+
end
|
152
|
+
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
|
156
|
+
protected def ungetstring(string)
|
157
|
+
Validation.validate_string string
|
158
|
+
|
159
|
+
string = ::String.new string, :encoding => @internal_encoding
|
160
|
+
string = transcode_to_external string unless @external_encoding.nil?
|
161
|
+
|
162
|
+
string.force_encoding ::Encoding::BINARY
|
163
|
+
@buffer.prepend string
|
164
|
+
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
|
168
|
+
# -- etc --
|
169
|
+
|
170
|
+
module ClassMethods
|
171
|
+
def open(file_path, *args, &block)
|
172
|
+
Validation.validate_string file_path
|
173
|
+
Validation.validate_proc block
|
174
|
+
|
175
|
+
::File.open file_path, "rb" do |file|
|
176
|
+
reader = new file, *args
|
177
|
+
|
178
|
+
begin
|
179
|
+
yield reader
|
180
|
+
ensure
|
181
|
+
reader.close
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.included(klass)
|
188
|
+
klass.extend ClassMethods
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module LZWS
|
7
|
+
module Stream
|
8
|
+
class Stat
|
9
|
+
# Libraries like minitar tries to access stat to know whether stream is seekable.
|
10
|
+
# We need to mark stream as not directory, file, etc, because it is not seekable.
|
11
|
+
|
12
|
+
# User can use disabled delegates using :io reader.
|
13
|
+
|
14
|
+
extend ::Forwardable
|
15
|
+
|
16
|
+
METHODS_RETURNING_FALSE = %i[
|
17
|
+
blockdev?
|
18
|
+
chardev?
|
19
|
+
directory?
|
20
|
+
executable?
|
21
|
+
executable_real?
|
22
|
+
file?
|
23
|
+
grpowned?
|
24
|
+
owned?
|
25
|
+
pipe?
|
26
|
+
setgid?
|
27
|
+
setuid?
|
28
|
+
socket?
|
29
|
+
sticky?
|
30
|
+
symlink?
|
31
|
+
zero?
|
32
|
+
]
|
33
|
+
.freeze
|
34
|
+
|
35
|
+
DELEGATES = %i[
|
36
|
+
<=>
|
37
|
+
atime
|
38
|
+
birthtime
|
39
|
+
blksize
|
40
|
+
blocks
|
41
|
+
ctime
|
42
|
+
dev
|
43
|
+
dev_major
|
44
|
+
dev_minor
|
45
|
+
ftype
|
46
|
+
gid
|
47
|
+
ino
|
48
|
+
inspect
|
49
|
+
mode
|
50
|
+
mtime
|
51
|
+
nlink
|
52
|
+
rdev
|
53
|
+
rdev_major
|
54
|
+
rdev_minor
|
55
|
+
readable?
|
56
|
+
readable_real?
|
57
|
+
size
|
58
|
+
size?
|
59
|
+
uid
|
60
|
+
world_readable?
|
61
|
+
world_writable?
|
62
|
+
writable?
|
63
|
+
writable_real?
|
64
|
+
]
|
65
|
+
.freeze
|
66
|
+
|
67
|
+
def initialize(stat)
|
68
|
+
@stat = stat
|
69
|
+
end
|
70
|
+
|
71
|
+
METHODS_RETURNING_FALSE.each do |method_name|
|
72
|
+
define_method(method_name) { false }
|
73
|
+
end
|
74
|
+
|
75
|
+
def_delegators :@stat, *DELEGATES
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "abstract"
|
5
|
+
require_relative "raw/compressor"
|
6
|
+
require_relative "writer_helpers"
|
7
|
+
|
8
|
+
module LZWS
|
9
|
+
module Stream
|
10
|
+
class Writer < Abstract
|
11
|
+
include WriterHelpers
|
12
|
+
|
13
|
+
def initialize(destination_io, options = {}, *args)
|
14
|
+
@options = options
|
15
|
+
|
16
|
+
super destination_io, *args
|
17
|
+
end
|
18
|
+
|
19
|
+
protected def create_raw_stream
|
20
|
+
Raw::Compressor.new @options
|
21
|
+
end
|
22
|
+
|
23
|
+
# -- synchronous --
|
24
|
+
|
25
|
+
def write(*objects)
|
26
|
+
write_remaining_buffer
|
27
|
+
|
28
|
+
bytes_written = 0
|
29
|
+
|
30
|
+
objects.each do |object|
|
31
|
+
source = transcode object.to_s
|
32
|
+
bytes_written += raw_wrapper :write, source
|
33
|
+
end
|
34
|
+
|
35
|
+
@pos += bytes_written
|
36
|
+
|
37
|
+
bytes_written
|
38
|
+
end
|
39
|
+
|
40
|
+
def flush
|
41
|
+
finish :flush
|
42
|
+
|
43
|
+
@io.flush
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def rewind
|
49
|
+
finish :close
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def close
|
55
|
+
finish :close
|
56
|
+
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
protected def finish(method_name)
|
61
|
+
write_remaining_buffer
|
62
|
+
|
63
|
+
raw_wrapper method_name
|
64
|
+
end
|
65
|
+
|
66
|
+
protected def write_remaining_buffer
|
67
|
+
return nil if @buffer.bytesize == 0
|
68
|
+
|
69
|
+
@io.write @buffer
|
70
|
+
|
71
|
+
reset_buffer
|
72
|
+
end
|
73
|
+
|
74
|
+
protected def raw_wrapper(method_name, *args)
|
75
|
+
@raw_stream.send(method_name, *args) { |portion| @io.write portion }
|
76
|
+
end
|
77
|
+
|
78
|
+
# -- asynchronous --
|
79
|
+
|
80
|
+
def write_nonblock(object, *options)
|
81
|
+
return 0 unless write_remaining_buffer_nonblock(*options)
|
82
|
+
|
83
|
+
source = transcode object.to_s
|
84
|
+
bytes_written = raw_nonblock_wrapper :write, source
|
85
|
+
@pos += bytes_written
|
86
|
+
|
87
|
+
bytes_written
|
88
|
+
end
|
89
|
+
|
90
|
+
def flush_nonblock(*options)
|
91
|
+
return false unless finish_nonblock :flush, *options
|
92
|
+
|
93
|
+
@io.flush
|
94
|
+
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
def rewind_nonblock(*options)
|
99
|
+
return false unless finish_nonblock :close, *options
|
100
|
+
|
101
|
+
method(:rewind).super_method.call
|
102
|
+
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def close_nonblock(*options)
|
107
|
+
return false unless finish_nonblock :close, *options
|
108
|
+
|
109
|
+
method(:close).super_method.call
|
110
|
+
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
protected def finish_nonblock(method_name, *options)
|
115
|
+
return false unless write_remaining_buffer_nonblock(*options)
|
116
|
+
|
117
|
+
raw_nonblock_wrapper method_name
|
118
|
+
|
119
|
+
write_remaining_buffer_nonblock(*options)
|
120
|
+
end
|
121
|
+
|
122
|
+
protected def write_remaining_buffer_nonblock(*options)
|
123
|
+
return true if @buffer.bytesize == 0
|
124
|
+
|
125
|
+
bytes_written = @io.write_nonblock @buffer, *options
|
126
|
+
return false if bytes_written == 0
|
127
|
+
|
128
|
+
@buffer = @buffer.byteslice bytes_written, @buffer.bytesize - bytes_written
|
129
|
+
|
130
|
+
@buffer.bytesize == 0
|
131
|
+
end
|
132
|
+
|
133
|
+
protected def raw_nonblock_wrapper(method_name, *args)
|
134
|
+
@raw_stream.send(method_name, *args) { |portion| @buffer << portion }
|
135
|
+
end
|
136
|
+
|
137
|
+
# -- common --
|
138
|
+
|
139
|
+
protected def transcode(data)
|
140
|
+
data = data.encode @external_encoding, @transcode_options unless @external_encoding.nil?
|
141
|
+
data
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "../error"
|
5
|
+
require_relative "../validation"
|
6
|
+
|
7
|
+
module LZWS
|
8
|
+
module Stream
|
9
|
+
module WriterHelpers
|
10
|
+
def <<(object)
|
11
|
+
write object
|
12
|
+
end
|
13
|
+
|
14
|
+
def print(*objects)
|
15
|
+
if objects.empty?
|
16
|
+
write $LAST_READ_LINE unless $LAST_READ_LINE.nil?
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
|
20
|
+
objects.each do |object|
|
21
|
+
write object
|
22
|
+
write $OUTPUT_FIELD_SEPARATOR unless $OUTPUT_FIELD_SEPARATOR.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
write $OUTPUT_RECORD_SEPARATOR unless $OUTPUT_RECORD_SEPARATOR.nil?
|
26
|
+
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def printf(*args)
|
31
|
+
write sprintf(*args)
|
32
|
+
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def putc(object, encoding: ::Encoding::BINARY)
|
37
|
+
if object.is_a? ::Numeric
|
38
|
+
write object.chr(encoding)
|
39
|
+
elsif object.is_a? ::String
|
40
|
+
write object[0]
|
41
|
+
else
|
42
|
+
raise ValidateError, "invalid object: \"#{object}\" for putc"
|
43
|
+
end
|
44
|
+
|
45
|
+
object
|
46
|
+
end
|
47
|
+
|
48
|
+
def puts(*objects)
|
49
|
+
objects.each do |object|
|
50
|
+
if object.is_a? ::Array
|
51
|
+
puts(*object)
|
52
|
+
next
|
53
|
+
end
|
54
|
+
|
55
|
+
source = object.to_s
|
56
|
+
newline = "\n".encode source.encoding
|
57
|
+
|
58
|
+
# Do not add newline if source ends with newline.
|
59
|
+
if source.end_with? newline
|
60
|
+
write source
|
61
|
+
else
|
62
|
+
write source + newline
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# -- etc --
|
70
|
+
|
71
|
+
module ClassMethods
|
72
|
+
def open(file_path, *args, &block)
|
73
|
+
Validation.validate_string file_path
|
74
|
+
Validation.validate_proc block
|
75
|
+
|
76
|
+
::File.open file_path, "wb" do |file|
|
77
|
+
writer = new file, *args
|
78
|
+
|
79
|
+
begin
|
80
|
+
yield writer
|
81
|
+
ensure
|
82
|
+
writer.close
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.included(klass)
|
89
|
+
klass.extend ClassMethods
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/lzws/string.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require "lzws_ext"
|
5
|
+
|
6
|
+
require_relative "option"
|
7
|
+
require_relative "validation"
|
8
|
+
|
9
|
+
module LZWS
|
10
|
+
module String
|
11
|
+
def self.compress(source, options = {})
|
12
|
+
Validation.validate_string source
|
13
|
+
|
14
|
+
options = Option.get_compressor_options options
|
15
|
+
|
16
|
+
LZWS._native_compress_string source, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.decompress(source, options = {})
|
20
|
+
Validation.validate_string source
|
21
|
+
|
22
|
+
options = Option.get_decompressor_options options
|
23
|
+
|
24
|
+
LZWS._native_decompress_string source, options
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "error"
|
5
|
+
|
6
|
+
module LZWS
|
7
|
+
module Validation
|
8
|
+
def self.validate_bool(value)
|
9
|
+
raise ValidateError, "invalid bool" unless value.is_a?(::TrueClass) || value.is_a?(::FalseClass)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.validate_positive_integer(value)
|
13
|
+
raise ValidateError, "invalid positive integer" unless value.is_a?(::Integer) && value > 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.validate_not_negative_integer(value)
|
17
|
+
raise ValidateError, "invalid not negative integer" unless value.is_a?(::Integer) && value >= 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.validate_string(value)
|
21
|
+
raise ValidateError, "invalid string" unless value.is_a? ::String
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.validate_io(value)
|
25
|
+
raise ValidateError, "invalid io" unless value.is_a? ::IO
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.validate_hash(value)
|
29
|
+
raise ValidateError, "invalid hash" unless value.is_a? ::Hash
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.validate_proc(value)
|
33
|
+
raise ValidateError, "invalid proc" unless value.is_a?(::Proc) || value.is_a?(::Method) || value.is_a?(::UnboundMethod)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/lzws/version.rb
ADDED
data/lib/lzws.rb
ADDED