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,217 @@
|
|
1
|
+
// Ruby bindings for lzws library.
|
2
|
+
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#include <lzws/buffer.h>
|
5
|
+
#include <lzws/compressor/common.h>
|
6
|
+
#include <lzws/compressor/header.h>
|
7
|
+
#include <lzws/compressor/main.h>
|
8
|
+
#include <lzws/compressor/state.h>
|
9
|
+
|
10
|
+
#include "ruby.h"
|
11
|
+
|
12
|
+
#include "lzws_ext/error.h"
|
13
|
+
#include "lzws_ext/macro.h"
|
14
|
+
#include "lzws_ext/option.h"
|
15
|
+
#include "lzws_ext/stream/compressor.h"
|
16
|
+
|
17
|
+
static void free_compressor(lzws_ext_compressor_t* compressor_ptr)
|
18
|
+
{
|
19
|
+
lzws_compressor_state_t* state_ptr = compressor_ptr->state_ptr;
|
20
|
+
if (state_ptr != NULL) {
|
21
|
+
lzws_compressor_free_state(state_ptr);
|
22
|
+
}
|
23
|
+
|
24
|
+
uint8_t* destination_buffer = compressor_ptr->destination_buffer;
|
25
|
+
if (destination_buffer != NULL) {
|
26
|
+
free(destination_buffer);
|
27
|
+
}
|
28
|
+
|
29
|
+
free(compressor_ptr);
|
30
|
+
}
|
31
|
+
|
32
|
+
VALUE lzws_ext_allocate_compressor(VALUE klass)
|
33
|
+
{
|
34
|
+
lzws_ext_compressor_t* compressor_ptr;
|
35
|
+
|
36
|
+
VALUE self = Data_Make_Struct(klass, lzws_ext_compressor_t, NULL, free_compressor, compressor_ptr);
|
37
|
+
|
38
|
+
compressor_ptr->state_ptr = NULL;
|
39
|
+
compressor_ptr->destination_buffer = NULL;
|
40
|
+
compressor_ptr->destination_buffer_length = 0;
|
41
|
+
compressor_ptr->remaining_destination_buffer = NULL;
|
42
|
+
compressor_ptr->remaining_destination_buffer_length = 0;
|
43
|
+
|
44
|
+
return self;
|
45
|
+
}
|
46
|
+
|
47
|
+
#define GET_COMPRESSOR(self) \
|
48
|
+
lzws_ext_compressor_t* compressor_ptr; \
|
49
|
+
Data_Get_Struct(self, lzws_ext_compressor_t, compressor_ptr);
|
50
|
+
|
51
|
+
VALUE lzws_ext_initialize_compressor(VALUE self, VALUE options)
|
52
|
+
{
|
53
|
+
GET_COMPRESSOR(self);
|
54
|
+
LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
|
55
|
+
LZWS_EXT_UNUSED_VARIABLE(without_magic_header);
|
56
|
+
|
57
|
+
lzws_compressor_state_t* compressor_state_ptr;
|
58
|
+
|
59
|
+
lzws_result_t result = lzws_compressor_get_initial_state(
|
60
|
+
&compressor_state_ptr,
|
61
|
+
max_code_bit_length, block_mode, msb, unaligned_bit_groups, quiet);
|
62
|
+
|
63
|
+
if (result == LZWS_COMPRESSOR_ALLOCATE_FAILED) {
|
64
|
+
lzws_ext_raise_error("AllocateError", "allocate error");
|
65
|
+
}
|
66
|
+
else if (result == LZWS_COMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH) {
|
67
|
+
lzws_ext_raise_error("ValidateError", "validate error");
|
68
|
+
}
|
69
|
+
else if (result != 0) {
|
70
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
71
|
+
}
|
72
|
+
|
73
|
+
compressor_ptr->state_ptr = compressor_state_ptr;
|
74
|
+
|
75
|
+
// -----
|
76
|
+
|
77
|
+
uint8_t* destination_buffer;
|
78
|
+
size_t destination_buffer_length = buffer_length;
|
79
|
+
|
80
|
+
result = lzws_create_buffer_for_compressor(&destination_buffer, &destination_buffer_length, quiet);
|
81
|
+
if (result != 0) {
|
82
|
+
lzws_ext_raise_error("AllocateError", "allocate error");
|
83
|
+
}
|
84
|
+
|
85
|
+
compressor_ptr->destination_buffer = destination_buffer;
|
86
|
+
compressor_ptr->destination_buffer_length = destination_buffer_length;
|
87
|
+
compressor_ptr->remaining_destination_buffer = destination_buffer;
|
88
|
+
compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
|
89
|
+
|
90
|
+
return Qnil;
|
91
|
+
}
|
92
|
+
|
93
|
+
#define DO_NOT_USE_AFTER_CLOSE(compressor_ptr) \
|
94
|
+
if (compressor_ptr->state_ptr == NULL || compressor_ptr->destination_buffer == NULL) { \
|
95
|
+
lzws_ext_raise_error("UsedAfterCloseError", "compressor used after closed"); \
|
96
|
+
}
|
97
|
+
|
98
|
+
VALUE lzws_ext_compressor_write_magic_header(VALUE self)
|
99
|
+
{
|
100
|
+
GET_COMPRESSOR(self);
|
101
|
+
DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
|
102
|
+
|
103
|
+
lzws_result_t result = lzws_compressor_write_magic_header(
|
104
|
+
&compressor_ptr->remaining_destination_buffer,
|
105
|
+
&compressor_ptr->remaining_destination_buffer_length);
|
106
|
+
|
107
|
+
if (result == 0) {
|
108
|
+
return Qfalse;
|
109
|
+
}
|
110
|
+
else if (result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
|
111
|
+
return Qtrue;
|
112
|
+
}
|
113
|
+
else {
|
114
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
#define GET_STRING(source) \
|
119
|
+
Check_Type(source, T_STRING); \
|
120
|
+
\
|
121
|
+
const char* source_data = RSTRING_PTR(source); \
|
122
|
+
size_t source_length = RSTRING_LEN(source);
|
123
|
+
|
124
|
+
VALUE lzws_ext_compress(VALUE self, VALUE source)
|
125
|
+
{
|
126
|
+
GET_COMPRESSOR(self);
|
127
|
+
DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
|
128
|
+
GET_STRING(source);
|
129
|
+
|
130
|
+
uint8_t* remaining_source_data = (uint8_t*)source_data;
|
131
|
+
size_t remaining_source_length = source_length;
|
132
|
+
|
133
|
+
lzws_result_t result = lzws_compress(
|
134
|
+
compressor_ptr->state_ptr,
|
135
|
+
&remaining_source_data,
|
136
|
+
&remaining_source_length,
|
137
|
+
&compressor_ptr->remaining_destination_buffer,
|
138
|
+
&compressor_ptr->remaining_destination_buffer_length);
|
139
|
+
|
140
|
+
VALUE bytes_written = INT2NUM(source_length - remaining_source_length);
|
141
|
+
|
142
|
+
if (result == LZWS_COMPRESSOR_NEEDS_MORE_SOURCE) {
|
143
|
+
return rb_ary_new_from_args(2, bytes_written, Qfalse);
|
144
|
+
}
|
145
|
+
else if (result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
|
146
|
+
return rb_ary_new_from_args(2, bytes_written, Qtrue);
|
147
|
+
}
|
148
|
+
else {
|
149
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
VALUE lzws_ext_flush_compressor(VALUE self)
|
154
|
+
{
|
155
|
+
GET_COMPRESSOR(self);
|
156
|
+
DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
|
157
|
+
|
158
|
+
lzws_result_t result = lzws_flush_compressor(
|
159
|
+
compressor_ptr->state_ptr,
|
160
|
+
&compressor_ptr->remaining_destination_buffer,
|
161
|
+
&compressor_ptr->remaining_destination_buffer_length);
|
162
|
+
|
163
|
+
if (result == 0) {
|
164
|
+
return Qfalse;
|
165
|
+
}
|
166
|
+
else if (result == LZWS_COMPRESSOR_NEEDS_MORE_DESTINATION) {
|
167
|
+
return Qtrue;
|
168
|
+
}
|
169
|
+
else {
|
170
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
VALUE lzws_ext_compressor_read_result(VALUE self)
|
175
|
+
{
|
176
|
+
GET_COMPRESSOR(self);
|
177
|
+
DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
|
178
|
+
|
179
|
+
uint8_t* destination_buffer = compressor_ptr->destination_buffer;
|
180
|
+
size_t destination_buffer_length = compressor_ptr->destination_buffer_length;
|
181
|
+
size_t remaining_destination_buffer_length = compressor_ptr->remaining_destination_buffer_length;
|
182
|
+
|
183
|
+
const char* result_data = (const char*)destination_buffer;
|
184
|
+
size_t result_length = destination_buffer_length - remaining_destination_buffer_length;
|
185
|
+
|
186
|
+
VALUE result = rb_str_new(result_data, result_length);
|
187
|
+
|
188
|
+
compressor_ptr->remaining_destination_buffer = destination_buffer;
|
189
|
+
compressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
|
190
|
+
|
191
|
+
return result;
|
192
|
+
}
|
193
|
+
|
194
|
+
VALUE lzws_ext_compressor_close(VALUE self)
|
195
|
+
{
|
196
|
+
GET_COMPRESSOR(self);
|
197
|
+
DO_NOT_USE_AFTER_CLOSE(compressor_ptr);
|
198
|
+
|
199
|
+
lzws_compressor_state_t* state_ptr = compressor_ptr->state_ptr;
|
200
|
+
if (state_ptr != NULL) {
|
201
|
+
lzws_compressor_free_state(state_ptr);
|
202
|
+
|
203
|
+
compressor_ptr->state_ptr = NULL;
|
204
|
+
}
|
205
|
+
|
206
|
+
uint8_t* destination_buffer = compressor_ptr->destination_buffer;
|
207
|
+
if (destination_buffer != NULL) {
|
208
|
+
free(destination_buffer);
|
209
|
+
|
210
|
+
compressor_ptr->destination_buffer = NULL;
|
211
|
+
}
|
212
|
+
|
213
|
+
// It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
|
214
|
+
// and "remaining_destination_buffer_length" as is.
|
215
|
+
|
216
|
+
return Qnil;
|
217
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
// Ruby bindings for lzws library.
|
2
|
+
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#if !defined(LZWS_EXT_STREAM_COMPRESSOR_H)
|
5
|
+
#define LZWS_EXT_STREAM_COMPRESSOR_H
|
6
|
+
|
7
|
+
#include <lzws/compressor/state.h>
|
8
|
+
|
9
|
+
#include "ruby.h"
|
10
|
+
|
11
|
+
typedef struct {
|
12
|
+
lzws_compressor_state_t* state_ptr;
|
13
|
+
uint8_t* destination_buffer;
|
14
|
+
size_t destination_buffer_length;
|
15
|
+
uint8_t* remaining_destination_buffer;
|
16
|
+
size_t remaining_destination_buffer_length;
|
17
|
+
} lzws_ext_compressor_t;
|
18
|
+
|
19
|
+
VALUE lzws_ext_allocate_compressor(VALUE klass);
|
20
|
+
VALUE lzws_ext_initialize_compressor(VALUE self, VALUE options);
|
21
|
+
VALUE lzws_ext_compressor_write_magic_header(VALUE self);
|
22
|
+
VALUE lzws_ext_compress(VALUE self, VALUE source);
|
23
|
+
VALUE lzws_ext_flush_compressor(VALUE self);
|
24
|
+
VALUE lzws_ext_compressor_read_result(VALUE self);
|
25
|
+
VALUE lzws_ext_compressor_close(VALUE self);
|
26
|
+
|
27
|
+
#endif // LZWS_EXT_STREAM_COMPRESSOR_H
|
@@ -0,0 +1,206 @@
|
|
1
|
+
// Ruby bindings for lzws library.
|
2
|
+
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#include <lzws/buffer.h>
|
5
|
+
#include <lzws/decompressor/common.h>
|
6
|
+
#include <lzws/decompressor/header.h>
|
7
|
+
#include <lzws/decompressor/main.h>
|
8
|
+
#include <lzws/decompressor/state.h>
|
9
|
+
|
10
|
+
#include "ruby.h"
|
11
|
+
|
12
|
+
#include "lzws_ext/error.h"
|
13
|
+
#include "lzws_ext/macro.h"
|
14
|
+
#include "lzws_ext/option.h"
|
15
|
+
#include "lzws_ext/stream/decompressor.h"
|
16
|
+
|
17
|
+
static void free_decompressor(lzws_ext_decompressor_t* decompressor_ptr)
|
18
|
+
{
|
19
|
+
lzws_decompressor_state_t* state_ptr = decompressor_ptr->state_ptr;
|
20
|
+
if (state_ptr != NULL) {
|
21
|
+
lzws_decompressor_free_state(state_ptr);
|
22
|
+
}
|
23
|
+
|
24
|
+
uint8_t* destination_buffer = decompressor_ptr->destination_buffer;
|
25
|
+
if (destination_buffer != NULL) {
|
26
|
+
free(destination_buffer);
|
27
|
+
}
|
28
|
+
|
29
|
+
free(decompressor_ptr);
|
30
|
+
}
|
31
|
+
|
32
|
+
VALUE lzws_ext_allocate_decompressor(VALUE klass)
|
33
|
+
{
|
34
|
+
lzws_ext_decompressor_t* decompressor_ptr;
|
35
|
+
|
36
|
+
VALUE self = Data_Make_Struct(klass, lzws_ext_decompressor_t, NULL, free_decompressor, decompressor_ptr);
|
37
|
+
|
38
|
+
decompressor_ptr->state_ptr = NULL;
|
39
|
+
decompressor_ptr->destination_buffer = NULL;
|
40
|
+
decompressor_ptr->destination_buffer_length = 0;
|
41
|
+
decompressor_ptr->remaining_destination_buffer = NULL;
|
42
|
+
decompressor_ptr->remaining_destination_buffer_length = 0;
|
43
|
+
|
44
|
+
return self;
|
45
|
+
}
|
46
|
+
|
47
|
+
#define GET_DECOMPRESSOR(self) \
|
48
|
+
lzws_ext_decompressor_t* decompressor_ptr; \
|
49
|
+
Data_Get_Struct(self, lzws_ext_decompressor_t, decompressor_ptr);
|
50
|
+
|
51
|
+
VALUE lzws_ext_initialize_decompressor(VALUE self, VALUE options)
|
52
|
+
{
|
53
|
+
GET_DECOMPRESSOR(self);
|
54
|
+
LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
|
55
|
+
LZWS_EXT_UNUSED_VARIABLE(without_magic_header);
|
56
|
+
|
57
|
+
lzws_decompressor_state_t* decompressor_state_ptr;
|
58
|
+
|
59
|
+
lzws_result_t result = lzws_decompressor_get_initial_state(
|
60
|
+
&decompressor_state_ptr,
|
61
|
+
msb, unaligned_bit_groups, quiet);
|
62
|
+
|
63
|
+
if (result == LZWS_DECOMPRESSOR_ALLOCATE_FAILED) {
|
64
|
+
lzws_ext_raise_error("AllocateError", "allocate error");
|
65
|
+
}
|
66
|
+
else if (result != 0) {
|
67
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
68
|
+
}
|
69
|
+
|
70
|
+
decompressor_ptr->state_ptr = decompressor_state_ptr;
|
71
|
+
|
72
|
+
// -----
|
73
|
+
|
74
|
+
uint8_t* destination_buffer;
|
75
|
+
size_t destination_buffer_length = buffer_length;
|
76
|
+
|
77
|
+
result = lzws_create_buffer_for_decompressor(&destination_buffer, &destination_buffer_length, quiet);
|
78
|
+
if (result != 0) {
|
79
|
+
lzws_ext_raise_error("AllocateError", "allocate error");
|
80
|
+
}
|
81
|
+
|
82
|
+
decompressor_ptr->destination_buffer = destination_buffer;
|
83
|
+
decompressor_ptr->destination_buffer_length = destination_buffer_length;
|
84
|
+
decompressor_ptr->remaining_destination_buffer = destination_buffer;
|
85
|
+
decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
|
86
|
+
|
87
|
+
return Qnil;
|
88
|
+
}
|
89
|
+
|
90
|
+
#define GET_STRING(source) \
|
91
|
+
Check_Type(source, T_STRING); \
|
92
|
+
\
|
93
|
+
const char* source_data = RSTRING_PTR(source); \
|
94
|
+
size_t source_length = RSTRING_LEN(source);
|
95
|
+
|
96
|
+
#define DO_NOT_USE_AFTER_CLOSE(decompressor_ptr) \
|
97
|
+
if (decompressor_ptr->state_ptr == NULL || decompressor_ptr->destination_buffer == NULL) { \
|
98
|
+
lzws_ext_raise_error("UsedAfterCloseError", "decompressor used after close"); \
|
99
|
+
}
|
100
|
+
|
101
|
+
VALUE lzws_ext_decompressor_read_magic_header(VALUE self, VALUE source)
|
102
|
+
{
|
103
|
+
GET_DECOMPRESSOR(self);
|
104
|
+
DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
|
105
|
+
GET_STRING(source);
|
106
|
+
|
107
|
+
uint8_t* remaining_source_data = (uint8_t*)source_data;
|
108
|
+
size_t remaining_source_length = source_length;
|
109
|
+
|
110
|
+
lzws_result_t result = lzws_decompressor_read_magic_header(
|
111
|
+
decompressor_ptr->state_ptr,
|
112
|
+
&remaining_source_data,
|
113
|
+
&remaining_source_length);
|
114
|
+
|
115
|
+
VALUE bytes_read = INT2NUM(source_length - remaining_source_length);
|
116
|
+
|
117
|
+
if (result == 0 || result == LZWS_DECOMPRESSOR_NEEDS_MORE_SOURCE) {
|
118
|
+
return bytes_read;
|
119
|
+
}
|
120
|
+
else if (result == LZWS_DECOMPRESSOR_INVALID_MAGIC_HEADER) {
|
121
|
+
lzws_ext_raise_error("ValidateError", "validate error");
|
122
|
+
}
|
123
|
+
else {
|
124
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
VALUE lzws_ext_decompress(VALUE self, VALUE source)
|
129
|
+
{
|
130
|
+
GET_DECOMPRESSOR(self);
|
131
|
+
DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
|
132
|
+
GET_STRING(source);
|
133
|
+
|
134
|
+
uint8_t* remaining_source_data = (uint8_t*)source_data;
|
135
|
+
size_t remaining_source_length = source_length;
|
136
|
+
|
137
|
+
lzws_result_t result = lzws_decompress(
|
138
|
+
decompressor_ptr->state_ptr,
|
139
|
+
&remaining_source_data,
|
140
|
+
&remaining_source_length,
|
141
|
+
&decompressor_ptr->remaining_destination_buffer,
|
142
|
+
&decompressor_ptr->remaining_destination_buffer_length);
|
143
|
+
|
144
|
+
VALUE bytes_read = INT2NUM(source_length - remaining_source_length);
|
145
|
+
|
146
|
+
if (result == LZWS_DECOMPRESSOR_NEEDS_MORE_SOURCE) {
|
147
|
+
return rb_ary_new_from_args(2, bytes_read, Qfalse);
|
148
|
+
}
|
149
|
+
else if (result == LZWS_DECOMPRESSOR_NEEDS_MORE_DESTINATION) {
|
150
|
+
return rb_ary_new_from_args(2, bytes_read, Qtrue);
|
151
|
+
}
|
152
|
+
else if (result == LZWS_DECOMPRESSOR_INVALID_MAX_CODE_BIT_LENGTH) {
|
153
|
+
lzws_ext_raise_error("ValidateError", "validate error");
|
154
|
+
}
|
155
|
+
else if (result == LZWS_DECOMPRESSOR_CORRUPTED_SOURCE) {
|
156
|
+
lzws_ext_raise_error("DecompressorCorruptedSourceError", "decompressor received corrupted source");
|
157
|
+
}
|
158
|
+
else {
|
159
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
VALUE lzws_ext_decompressor_read_result(VALUE self)
|
164
|
+
{
|
165
|
+
GET_DECOMPRESSOR(self);
|
166
|
+
DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
|
167
|
+
|
168
|
+
uint8_t* destination_buffer = decompressor_ptr->destination_buffer;
|
169
|
+
size_t destination_buffer_length = decompressor_ptr->destination_buffer_length;
|
170
|
+
size_t remaining_destination_buffer_length = decompressor_ptr->remaining_destination_buffer_length;
|
171
|
+
|
172
|
+
const char* result_data = (const char*)destination_buffer;
|
173
|
+
size_t result_length = destination_buffer_length - remaining_destination_buffer_length;
|
174
|
+
|
175
|
+
VALUE result = rb_str_new(result_data, result_length);
|
176
|
+
|
177
|
+
decompressor_ptr->remaining_destination_buffer = destination_buffer;
|
178
|
+
decompressor_ptr->remaining_destination_buffer_length = destination_buffer_length;
|
179
|
+
|
180
|
+
return result;
|
181
|
+
}
|
182
|
+
|
183
|
+
VALUE lzws_ext_decompressor_close(VALUE self)
|
184
|
+
{
|
185
|
+
GET_DECOMPRESSOR(self);
|
186
|
+
DO_NOT_USE_AFTER_CLOSE(decompressor_ptr);
|
187
|
+
|
188
|
+
lzws_decompressor_state_t* state_ptr = decompressor_ptr->state_ptr;
|
189
|
+
if (state_ptr != NULL) {
|
190
|
+
lzws_decompressor_free_state(state_ptr);
|
191
|
+
|
192
|
+
decompressor_ptr->state_ptr = NULL;
|
193
|
+
}
|
194
|
+
|
195
|
+
uint8_t* destination_buffer = decompressor_ptr->destination_buffer;
|
196
|
+
if (destination_buffer != NULL) {
|
197
|
+
free(destination_buffer);
|
198
|
+
|
199
|
+
decompressor_ptr->destination_buffer = NULL;
|
200
|
+
}
|
201
|
+
|
202
|
+
// It is possible to keep "destination_buffer_length", "remaining_destination_buffer"
|
203
|
+
// and "remaining_destination_buffer_length" as is.
|
204
|
+
|
205
|
+
return Qnil;
|
206
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
// Ruby bindings for lzws library.
|
2
|
+
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#if !defined(LZWS_EXT_STREAM_DECOMPRESSOR_H)
|
5
|
+
#define LZWS_EXT_STREAM_DECOMPRESSOR_H
|
6
|
+
|
7
|
+
#include <lzws/decompressor/state.h>
|
8
|
+
|
9
|
+
#include "ruby.h"
|
10
|
+
|
11
|
+
typedef struct {
|
12
|
+
lzws_decompressor_state_t* state_ptr;
|
13
|
+
uint8_t* destination_buffer;
|
14
|
+
size_t destination_buffer_length;
|
15
|
+
uint8_t* remaining_destination_buffer;
|
16
|
+
size_t remaining_destination_buffer_length;
|
17
|
+
} lzws_ext_decompressor_t;
|
18
|
+
|
19
|
+
VALUE lzws_ext_allocate_decompressor(VALUE klass);
|
20
|
+
VALUE lzws_ext_initialize_decompressor(VALUE self, VALUE options);
|
21
|
+
VALUE lzws_ext_decompressor_read_magic_header(VALUE self, VALUE source);
|
22
|
+
VALUE lzws_ext_decompress(VALUE self, VALUE source);
|
23
|
+
VALUE lzws_ext_decompressor_read_result(VALUE self);
|
24
|
+
VALUE lzws_ext_decompressor_close(VALUE self);
|
25
|
+
|
26
|
+
#endif // LZWS_EXT_STREAM_DECOMPRESSOR_H
|
@@ -0,0 +1,78 @@
|
|
1
|
+
// Ruby bindings for lzws library.
|
2
|
+
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#include <lzws/string.h>
|
5
|
+
|
6
|
+
#include "ruby.h"
|
7
|
+
|
8
|
+
#include "lzws_ext/error.h"
|
9
|
+
#include "lzws_ext/macro.h"
|
10
|
+
#include "lzws_ext/option.h"
|
11
|
+
#include "lzws_ext/string.h"
|
12
|
+
|
13
|
+
#define GET_STRING(source) \
|
14
|
+
Check_Type(source, T_STRING); \
|
15
|
+
\
|
16
|
+
const char* source_data = RSTRING_PTR(source); \
|
17
|
+
size_t source_length = RSTRING_LEN(source);
|
18
|
+
|
19
|
+
VALUE lzws_ext_compress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE options)
|
20
|
+
{
|
21
|
+
GET_STRING(source);
|
22
|
+
LZWS_EXT_GET_COMPRESSOR_OPTIONS(options);
|
23
|
+
|
24
|
+
char* destination;
|
25
|
+
size_t destination_length;
|
26
|
+
|
27
|
+
lzws_result_t result = lzws_compress_string(
|
28
|
+
(uint8_t*)source_data, source_length,
|
29
|
+
(uint8_t**)&destination, &destination_length, buffer_length,
|
30
|
+
without_magic_header, max_code_bit_length, block_mode, msb, unaligned_bit_groups, quiet);
|
31
|
+
|
32
|
+
if (result == LZWS_STRING_ALLOCATE_FAILED) {
|
33
|
+
lzws_ext_raise_error("AllocateError", "allocate error");
|
34
|
+
}
|
35
|
+
else if (result == LZWS_STRING_VALIDATE_FAILED) {
|
36
|
+
lzws_ext_raise_error("ValidateError", "validate error");
|
37
|
+
}
|
38
|
+
else if (result != 0) {
|
39
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
40
|
+
}
|
41
|
+
|
42
|
+
// Ruby copies string on initialization.
|
43
|
+
VALUE result_string = rb_str_new(destination, destination_length);
|
44
|
+
free(destination);
|
45
|
+
return result_string;
|
46
|
+
}
|
47
|
+
|
48
|
+
VALUE lzws_ext_decompress_string(VALUE LZWS_EXT_UNUSED(self), VALUE source, VALUE options)
|
49
|
+
{
|
50
|
+
GET_STRING(source);
|
51
|
+
LZWS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
|
52
|
+
|
53
|
+
char* destination;
|
54
|
+
size_t destination_length;
|
55
|
+
|
56
|
+
lzws_result_t result = lzws_decompress_string(
|
57
|
+
(uint8_t*)source_data, source_length,
|
58
|
+
(uint8_t**)&destination, &destination_length, buffer_length,
|
59
|
+
without_magic_header, msb, unaligned_bit_groups, quiet);
|
60
|
+
|
61
|
+
if (result == LZWS_STRING_ALLOCATE_FAILED) {
|
62
|
+
lzws_ext_raise_error("AllocateError", "allocate error");
|
63
|
+
}
|
64
|
+
else if (result == LZWS_STRING_VALIDATE_FAILED) {
|
65
|
+
lzws_ext_raise_error("ValidateError", "validate error");
|
66
|
+
}
|
67
|
+
else if (result == LZWS_STRING_DECOMPRESSOR_CORRUPTED_SOURCE) {
|
68
|
+
lzws_ext_raise_error("DecompressorCorruptedSourceError", "decompressor received corrupted source");
|
69
|
+
}
|
70
|
+
else if (result != 0) {
|
71
|
+
lzws_ext_raise_error("UnexpectedError", "unexpected error");
|
72
|
+
}
|
73
|
+
|
74
|
+
// Ruby copies string on initialization.
|
75
|
+
VALUE result_string = rb_str_new(destination, destination_length);
|
76
|
+
free(destination);
|
77
|
+
return result_string;
|
78
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
// Ruby bindings for lzws library.
|
2
|
+
// Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
#if !defined(LZWS_EXT_STRING_H)
|
5
|
+
#define LZWS_EXT_STRING_H
|
6
|
+
|
7
|
+
#include "ruby.h"
|
8
|
+
|
9
|
+
VALUE lzws_ext_compress_string(VALUE self, VALUE source, VALUE options);
|
10
|
+
VALUE lzws_ext_decompress_string(VALUE self, VALUE source, VALUE options);
|
11
|
+
|
12
|
+
#endif // LZWS_EXT_STRING_H
|
data/lib/lzws/error.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
module LZWS
|
5
|
+
class BaseError < ::StandardError; end
|
6
|
+
|
7
|
+
class ValidateError < BaseError; end
|
8
|
+
class AllocateError < BaseError; end
|
9
|
+
class UnexpectedError < BaseError; end
|
10
|
+
|
11
|
+
class NotEnoughDestinationError < BaseError; end
|
12
|
+
class UsedAfterCloseError < BaseError; end
|
13
|
+
|
14
|
+
class DecompressorCorruptedSourceError < BaseError; end
|
15
|
+
|
16
|
+
class OpenFileError < BaseError; end
|
17
|
+
class AccessIOError < BaseError; end
|
18
|
+
class ReadIOError < BaseError; end
|
19
|
+
class WriteIOError < BaseError; end
|
20
|
+
end
|
data/lib/lzws/file.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Ruby bindings for lzws library.
|
2
|
+
# Copyright (c) 2019 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require "lzws_ext"
|
5
|
+
|
6
|
+
require_relative "error"
|
7
|
+
require_relative "option"
|
8
|
+
require_relative "validation"
|
9
|
+
|
10
|
+
module LZWS
|
11
|
+
module File
|
12
|
+
def self.compress(source, destination, options = {})
|
13
|
+
Validation.validate_string source
|
14
|
+
Validation.validate_string destination
|
15
|
+
|
16
|
+
options = Option.get_compressor_options options
|
17
|
+
|
18
|
+
open_files(source, destination) do |source_io, destination_io|
|
19
|
+
LZWS._native_compress_io source_io, destination_io, options
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.decompress(source, destination, options = {})
|
24
|
+
Validation.validate_string source
|
25
|
+
Validation.validate_string destination
|
26
|
+
|
27
|
+
options = Option.get_decompressor_options options
|
28
|
+
|
29
|
+
open_files(source, destination) do |source_io, destination_io|
|
30
|
+
LZWS._native_decompress_io source_io, destination_io, options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private_class_method def self.open_files(source, destination, &_block)
|
35
|
+
open_file(source, "rb") do |source_io|
|
36
|
+
open_file(destination, "wb") do |destination_io|
|
37
|
+
yield source_io, destination_io
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private_class_method def self.open_file(path, mode, &_block)
|
43
|
+
begin
|
44
|
+
io = ::File.open path, mode
|
45
|
+
rescue ::StandardError
|
46
|
+
raise OpenFileError, "open file failed"
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
yield io
|
51
|
+
ensure
|
52
|
+
io.close
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|