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.
@@ -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