ruby-zstds 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +498 -0
  5. data/ext/extconf.rb +82 -0
  6. data/ext/zstds_ext/buffer.c +30 -0
  7. data/ext/zstds_ext/buffer.h +23 -0
  8. data/ext/zstds_ext/common.h +16 -0
  9. data/ext/zstds_ext/dictionary.c +106 -0
  10. data/ext/zstds_ext/dictionary.h +16 -0
  11. data/ext/zstds_ext/error.c +81 -0
  12. data/ext/zstds_ext/error.h +35 -0
  13. data/ext/zstds_ext/io.c +512 -0
  14. data/ext/zstds_ext/io.h +14 -0
  15. data/ext/zstds_ext/macro.h +13 -0
  16. data/ext/zstds_ext/main.c +25 -0
  17. data/ext/zstds_ext/option.c +287 -0
  18. data/ext/zstds_ext/option.h +122 -0
  19. data/ext/zstds_ext/stream/compressor.c +241 -0
  20. data/ext/zstds_ext/stream/compressor.h +31 -0
  21. data/ext/zstds_ext/stream/decompressor.c +183 -0
  22. data/ext/zstds_ext/stream/decompressor.h +29 -0
  23. data/ext/zstds_ext/string.c +254 -0
  24. data/ext/zstds_ext/string.h +14 -0
  25. data/lib/zstds.rb +9 -0
  26. data/lib/zstds/dictionary.rb +47 -0
  27. data/lib/zstds/error.rb +22 -0
  28. data/lib/zstds/file.rb +46 -0
  29. data/lib/zstds/option.rb +194 -0
  30. data/lib/zstds/stream/abstract.rb +153 -0
  31. data/lib/zstds/stream/delegates.rb +36 -0
  32. data/lib/zstds/stream/raw/abstract.rb +55 -0
  33. data/lib/zstds/stream/raw/compressor.rb +101 -0
  34. data/lib/zstds/stream/raw/decompressor.rb +70 -0
  35. data/lib/zstds/stream/reader.rb +166 -0
  36. data/lib/zstds/stream/reader_helpers.rb +192 -0
  37. data/lib/zstds/stream/stat.rb +78 -0
  38. data/lib/zstds/stream/writer.rb +145 -0
  39. data/lib/zstds/stream/writer_helpers.rb +93 -0
  40. data/lib/zstds/string.rb +31 -0
  41. data/lib/zstds/validation.rb +48 -0
  42. data/lib/zstds/version.rb +6 -0
  43. metadata +182 -0
data/ext/extconf.rb ADDED
@@ -0,0 +1,82 @@
1
+ # Ruby bindings for zstd library.
2
+ # Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ require "mkmf"
5
+
6
+ $LDFLAGS << " -pthread" # rubocop:disable Style/GlobalVars
7
+
8
+ def require_header(name, types = [])
9
+ abort "Can't find #{name} header" unless find_header name
10
+
11
+ types.each do |type|
12
+ abort "Can't find #{type} type in #{name} header" unless find_type type, nil, name
13
+ end
14
+ end
15
+
16
+ require_header "zstd_errors.h", %w[
17
+ ZSTD_ErrorCode
18
+ ]
19
+ require_header "zstd.h", [
20
+ "ZSTD_CCtx *",
21
+ "ZSTD_DCtx *",
22
+ "ZSTD_strategy",
23
+ "ZSTD_bounds",
24
+ "ZSTD_inBuffer",
25
+ "ZSTD_outBuffer"
26
+ ]
27
+ require_header "zdict.h"
28
+
29
+ def require_library(name, functions)
30
+ functions.each do |function|
31
+ abort "Can't find #{function} function in #{name} library" unless find_library name, function
32
+ end
33
+ end
34
+
35
+ require_library(
36
+ "zstd",
37
+ %w[
38
+ ZSTD_isError
39
+ ZSTD_getErrorCode
40
+ ZSTD_createCCtx
41
+ ZSTD_createDCtx
42
+ ZSTD_freeCCtx
43
+ ZSTD_freeDCtx
44
+ ZSTD_CCtx_setParameter
45
+ ZSTD_DCtx_setParameter
46
+ ZSTD_CCtx_setPledgedSrcSize
47
+ ZSTD_cParam_getBounds
48
+ ZSTD_dParam_getBounds
49
+ ZSTD_CStreamInSize
50
+ ZSTD_CStreamOutSize
51
+ ZSTD_DStreamInSize
52
+ ZSTD_DStreamOutSize
53
+ ZSTD_compressStream2
54
+ ZSTD_decompressStream
55
+ ZDICT_getDictID
56
+ ZDICT_trainFromBuffer
57
+ ]
58
+ )
59
+
60
+ extension_name = "zstds_ext".freeze
61
+ dir_config extension_name
62
+
63
+ # rubocop:disable Style/GlobalVars
64
+ $srcs = %w[
65
+ stream/compressor
66
+ stream/decompressor
67
+ buffer
68
+ dictionary
69
+ error
70
+ io
71
+ main
72
+ option
73
+ string
74
+ ]
75
+ .map { |name| "src/#{extension_name}/#{name}.c" }
76
+ .freeze
77
+
78
+ $CFLAGS << " -Wno-declaration-after-statement"
79
+ $VPATH << "$(srcdir)/#{extension_name}:$(srcdir)/#{extension_name}/stream"
80
+ # rubocop:enable Style/GlobalVars
81
+
82
+ create_makefile extension_name
@@ -0,0 +1,30 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #include "zstds_ext/buffer.h"
5
+
6
+ #include <zstd.h>
7
+
8
+ #include "ruby.h"
9
+
10
+ VALUE zstds_ext_create_string_buffer(VALUE length)
11
+ {
12
+ return rb_str_new(NULL, NUM2SIZET(length));
13
+ }
14
+
15
+ VALUE zstds_ext_resize_string_buffer(VALUE args)
16
+ {
17
+ VALUE buffer = rb_ary_entry(args, 0);
18
+ VALUE length = rb_ary_entry(args, 1);
19
+ return rb_str_resize(buffer, NUM2SIZET(length));
20
+ }
21
+
22
+ void zstds_ext_buffer_exports(VALUE root_module)
23
+ {
24
+ VALUE module = rb_define_module_under(root_module, "Buffer");
25
+
26
+ rb_define_const(module, "DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR", SIZET2NUM(ZSTD_CStreamInSize()));
27
+ rb_define_const(module, "DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR", SIZET2NUM(ZSTD_CStreamOutSize()));
28
+ rb_define_const(module, "DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR", SIZET2NUM(ZSTD_DStreamInSize()));
29
+ rb_define_const(module, "DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR", SIZET2NUM(ZSTD_DStreamOutSize()));
30
+ }
@@ -0,0 +1,23 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(ZSTDS_EXT_BUFFER_H)
5
+ #define ZSTDS_EXT_BUFFER_H
6
+
7
+ #include "ruby.h"
8
+
9
+ VALUE zstds_ext_create_string_buffer(VALUE length);
10
+
11
+ #define ZSTDS_EXT_CREATE_STRING_BUFFER(buffer, length, exception) \
12
+ VALUE buffer = rb_protect(zstds_ext_create_string_buffer, SIZET2NUM(length), &exception);
13
+
14
+ VALUE zstds_ext_resize_string_buffer(VALUE args);
15
+
16
+ #define ZSTDS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
17
+ VALUE args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
18
+ buffer = rb_protect(zstds_ext_resize_string_buffer, args, &exception); \
19
+ RB_GC_GUARD(args);
20
+
21
+ void zstds_ext_buffer_exports(VALUE root_module);
22
+
23
+ #endif // ZSTDS_EXT_BUFFER_H
@@ -0,0 +1,16 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(ZSTDS_EXT_COMMON_H)
5
+ #define ZSTDS_EXT_COMMON_H
6
+
7
+ #include <stdint.h>
8
+ #include <stdlib.h>
9
+
10
+ #define ZSTDS_EXT_MODULE_NAME "ZSTDS"
11
+
12
+ // WARNING: zstd library are mixing size and error codes inside size_t, dangerous.
13
+ typedef size_t zstds_result_t;
14
+ typedef uint_fast8_t zstds_ext_result_t;
15
+
16
+ #endif // ZSTDS_EXT_COMMON_H
@@ -0,0 +1,106 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #include "zstds_ext/dictionary.h"
5
+
6
+ #include <stdint.h>
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+ #include <zdict.h>
10
+
11
+ #include "ruby.h"
12
+ #include "zstds_ext/buffer.h"
13
+ #include "zstds_ext/common.h"
14
+ #include "zstds_ext/error.h"
15
+ #include "zstds_ext/macro.h"
16
+ #include "zstds_ext/option.h"
17
+
18
+ VALUE zstds_ext_get_dictionary_buffer_id(VALUE ZSTDS_EXT_UNUSED(self), VALUE buffer)
19
+ {
20
+ unsigned int id = ZDICT_getDictID(RSTRING_PTR(buffer), RSTRING_LEN(buffer));
21
+ if (id == 0) {
22
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_VALIDATE_FAILED);
23
+ }
24
+
25
+ return UINT2NUM(id);
26
+ }
27
+
28
+ VALUE zstds_ext_train_dictionary_buffer(VALUE ZSTDS_EXT_UNUSED(self), VALUE samples, VALUE options)
29
+ {
30
+ Check_Type(samples, T_ARRAY);
31
+
32
+ size_t sample_index;
33
+ unsigned int samples_length = (unsigned int)RARRAY_LEN(samples);
34
+ size_t samples_size = 0;
35
+
36
+ for (sample_index = 0; sample_index < samples_length; sample_index++) {
37
+ VALUE sample = rb_ary_entry(samples, sample_index);
38
+ Check_Type(sample, T_STRING);
39
+
40
+ samples_size += RSTRING_LEN(sample);
41
+ }
42
+
43
+ Check_Type(options, T_HASH);
44
+ ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, capacity);
45
+
46
+ if (capacity == 0) {
47
+ capacity = ZSTDS_EXT_DEFAULT_DICTIONARY_CAPACITY;
48
+ }
49
+
50
+ int exception;
51
+
52
+ ZSTDS_EXT_CREATE_STRING_BUFFER(buffer, capacity, exception);
53
+ if (exception != 0) {
54
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
55
+ }
56
+
57
+ uint8_t* samples_buffer = malloc(samples_size);
58
+ if (samples_buffer == NULL) {
59
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
60
+ }
61
+
62
+ size_t* samples_sizes = malloc(samples_length * sizeof(size_t));
63
+ if (samples_sizes == NULL) {
64
+ free(samples_buffer);
65
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
66
+ }
67
+
68
+ size_t sample_offset = 0;
69
+
70
+ for (sample_index = 0; sample_index < samples_length; sample_index++) {
71
+ VALUE sample = rb_ary_entry(samples, sample_index);
72
+ const char* sample_data = RSTRING_PTR(sample);
73
+ size_t sample_size = RSTRING_LEN(sample);
74
+
75
+ memmove(samples_buffer + sample_offset, sample_data, sample_size);
76
+ sample_offset += sample_size;
77
+
78
+ samples_sizes[sample_index] = sample_size;
79
+ }
80
+
81
+ zstds_result_t result = ZDICT_trainFromBuffer(
82
+ RSTRING_PTR(buffer), capacity,
83
+ samples_buffer, samples_sizes, samples_length);
84
+
85
+ free(samples_buffer);
86
+ free(samples_sizes);
87
+
88
+ if (ZSTD_isError(result)) {
89
+ zstds_ext_raise_error(zstds_ext_get_error(ZSTD_getErrorCode(result)));
90
+ }
91
+
92
+ ZSTDS_EXT_RESIZE_STRING_BUFFER(buffer, result, exception);
93
+ if (exception != 0) {
94
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
95
+ }
96
+
97
+ return buffer;
98
+ }
99
+
100
+ void zstds_ext_dictionary_exports(VALUE root_module)
101
+ {
102
+ VALUE dictionary = rb_define_class_under(root_module, "Dictionary", rb_cObject);
103
+
104
+ rb_define_singleton_method(dictionary, "get_buffer_id", zstds_ext_get_dictionary_buffer_id, 1);
105
+ rb_define_singleton_method(dictionary, "train_buffer", zstds_ext_train_dictionary_buffer, 2);
106
+ }
@@ -0,0 +1,16 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(ZSTDS_EXT_DICTIONARY_H)
5
+ #define ZSTDS_EXT_DICTIONARY_H
6
+
7
+ #include "ruby.h"
8
+
9
+ #define ZSTDS_EXT_DEFAULT_DICTIONARY_CAPACITY (1 << 17); // 128 KB
10
+
11
+ VALUE zstds_ext_get_dictionary_buffer_id(VALUE self, VALUE buffer);
12
+ VALUE zstds_ext_train_dictionary_buffer(VALUE self, VALUE samples, VALUE options);
13
+
14
+ void zstds_ext_dictionary_exports(VALUE root_module);
15
+
16
+ #endif // ZSTDS_EXT_DICTIONARY_H
@@ -0,0 +1,81 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #include "zstds_ext/error.h"
5
+
6
+ #include <zstd_errors.h>
7
+
8
+ #include "ruby.h"
9
+ #include "zstds_ext/common.h"
10
+
11
+ zstds_ext_result_t zstds_ext_get_error(ZSTD_ErrorCode error_code)
12
+ {
13
+ switch (error_code) {
14
+ case ZSTD_error_memory_allocation:
15
+ return ZSTDS_EXT_ERROR_ALLOCATE_FAILED;
16
+ case ZSTD_error_parameter_unsupported:
17
+ case ZSTD_error_parameter_outOfBound:
18
+ case ZSTD_error_tableLog_tooLarge:
19
+ case ZSTD_error_maxSymbolValue_tooLarge:
20
+ case ZSTD_error_maxSymbolValue_tooSmall:
21
+ case ZSTD_error_stage_wrong:
22
+ case ZSTD_error_init_missing:
23
+ case ZSTD_error_workSpace_tooSmall:
24
+ case ZSTD_error_srcSize_wrong:
25
+ case ZSTD_error_dstSize_tooSmall:
26
+ case ZSTD_error_dstBuffer_null:
27
+ return ZSTDS_EXT_ERROR_VALIDATE_FAILED;
28
+ case ZSTD_error_prefix_unknown:
29
+ case ZSTD_error_version_unsupported:
30
+ case ZSTD_error_frameParameter_unsupported:
31
+ case ZSTD_error_frameParameter_windowTooLarge:
32
+ case ZSTD_error_corruption_detected:
33
+ case ZSTD_error_checksum_wrong:
34
+ return ZSTDS_EXT_ERROR_DECOMPRESSOR_CORRUPTED_SOURCE;
35
+ case ZSTD_error_dictionary_corrupted:
36
+ case ZSTD_error_dictionary_wrong:
37
+ case ZSTD_error_dictionaryCreation_failed:
38
+ return ZSTDS_EXT_ERROR_CORRUPTED_DICTIONARY;
39
+ default:
40
+ return ZSTDS_EXT_ERROR_UNEXPECTED;
41
+ }
42
+ }
43
+
44
+ static inline NORETURN(void raise(const char *name, const char *description))
45
+ {
46
+ VALUE module = rb_define_module(ZSTDS_EXT_MODULE_NAME);
47
+ VALUE error = rb_const_get(module, rb_intern(name));
48
+ rb_raise(error, "%s", description);
49
+ }
50
+
51
+ void zstds_ext_raise_error(zstds_ext_result_t ext_result)
52
+ {
53
+ switch (ext_result) {
54
+ case ZSTDS_EXT_ERROR_ALLOCATE_FAILED:
55
+ raise("AllocateError", "allocate error");
56
+ case ZSTDS_EXT_ERROR_VALIDATE_FAILED:
57
+ raise("ValidateError", "validate error");
58
+
59
+ case ZSTDS_EXT_ERROR_USED_AFTER_CLOSE:
60
+ raise("UsedAfterCloseError", "used after closed");
61
+ case ZSTDS_EXT_ERROR_NOT_ENOUGH_SOURCE_BUFFER:
62
+ raise("NotEnoughSourceBufferError", "not enough source buffer");
63
+ case ZSTDS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER:
64
+ raise("NotEnoughDestinationBufferError", "not enough destination buffer");
65
+ case ZSTDS_EXT_ERROR_DECOMPRESSOR_CORRUPTED_SOURCE:
66
+ raise("DecompressorCorruptedSourceError", "decompressor received corrupted source");
67
+ case ZSTDS_EXT_ERROR_CORRUPTED_DICTIONARY:
68
+ raise("CorruptedDictionaryError", "corrupted dictionary");
69
+
70
+ case ZSTDS_EXT_ERROR_ACCESS_IO:
71
+ raise("AccessIOError", "failed to access IO");
72
+ case ZSTDS_EXT_ERROR_READ_IO:
73
+ raise("ReadIOError", "failed to read IO");
74
+ case ZSTDS_EXT_ERROR_WRITE_IO:
75
+ raise("WriteIOError", "failed to write IO");
76
+
77
+ default:
78
+ // ZSTDS_EXT_ERROR_UNEXPECTED
79
+ raise("UnexpectedError", "unexpected error");
80
+ }
81
+ }
@@ -0,0 +1,35 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(ZSTDS_EXT_ERROR_H)
5
+ #define ZSTDS_EXT_ERROR_H
6
+
7
+ #include <zstd_errors.h>
8
+
9
+ #include "ruby.h"
10
+ #include "zstds_ext/common.h"
11
+
12
+ // Results for errors listed in "lib/zstds/error" used in c extension.
13
+
14
+ enum {
15
+ ZSTDS_EXT_ERROR_ALLOCATE_FAILED = 1,
16
+ ZSTDS_EXT_ERROR_VALIDATE_FAILED,
17
+
18
+ ZSTDS_EXT_ERROR_USED_AFTER_CLOSE,
19
+ ZSTDS_EXT_ERROR_NOT_ENOUGH_SOURCE_BUFFER,
20
+ ZSTDS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER,
21
+ ZSTDS_EXT_ERROR_DECOMPRESSOR_CORRUPTED_SOURCE,
22
+ ZSTDS_EXT_ERROR_CORRUPTED_DICTIONARY,
23
+
24
+ ZSTDS_EXT_ERROR_ACCESS_IO,
25
+ ZSTDS_EXT_ERROR_READ_IO,
26
+ ZSTDS_EXT_ERROR_WRITE_IO,
27
+
28
+ ZSTDS_EXT_ERROR_UNEXPECTED
29
+ };
30
+
31
+ zstds_ext_result_t zstds_ext_get_error(ZSTD_ErrorCode error_code);
32
+
33
+ NORETURN(void zstds_ext_raise_error(zstds_ext_result_t ext_result));
34
+
35
+ #endif // ZSTDS_EXT_ERROR_H
@@ -0,0 +1,512 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #include "ruby/io.h"
5
+
6
+ #include <stdint.h>
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+ #include <zstd.h>
11
+
12
+ #include "ruby.h"
13
+ #include "zstds_ext/common.h"
14
+ #include "zstds_ext/error.h"
15
+ #include "zstds_ext/io.h"
16
+ #include "zstds_ext/macro.h"
17
+ #include "zstds_ext/option.h"
18
+
19
+ // Additional possible results:
20
+ enum {
21
+ ZSTDS_EXT_FILE_READ_FINISHED = 128
22
+ };
23
+
24
+ // -- file --
25
+
26
+ static inline zstds_ext_result_t read_file(FILE* source_file, uint8_t* source_buffer, size_t* source_length_ptr, size_t source_buffer_length)
27
+ {
28
+ size_t read_length = fread(source_buffer, 1, source_buffer_length, source_file);
29
+ if (read_length == 0 && feof(source_file)) {
30
+ return ZSTDS_EXT_FILE_READ_FINISHED;
31
+ }
32
+
33
+ if (read_length != source_buffer_length && ferror(source_file)) {
34
+ return ZSTDS_EXT_ERROR_READ_IO;
35
+ }
36
+
37
+ *source_length_ptr = read_length;
38
+
39
+ return 0;
40
+ }
41
+
42
+ static inline zstds_ext_result_t write_file(FILE* destination_file, uint8_t* destination_buffer, size_t destination_length)
43
+ {
44
+ size_t written_length = fwrite(destination_buffer, 1, destination_length, destination_file);
45
+ if (written_length != destination_length) {
46
+ return ZSTDS_EXT_ERROR_WRITE_IO;
47
+ }
48
+
49
+ return 0;
50
+ }
51
+
52
+ // -- buffer --
53
+
54
+ static inline zstds_ext_result_t create_buffers(
55
+ uint8_t** source_buffer_ptr, size_t source_buffer_length,
56
+ uint8_t** destination_buffer_ptr, size_t destination_buffer_length)
57
+ {
58
+ uint8_t* source_buffer = malloc(source_buffer_length);
59
+ if (source_buffer == NULL) {
60
+ return ZSTDS_EXT_ERROR_ALLOCATE_FAILED;
61
+ }
62
+
63
+ uint8_t* destination_buffer = malloc(destination_buffer_length);
64
+ if (destination_buffer == NULL) {
65
+ free(source_buffer);
66
+ return ZSTDS_EXT_ERROR_ALLOCATE_FAILED;
67
+ }
68
+
69
+ *source_buffer_ptr = source_buffer;
70
+ *destination_buffer_ptr = destination_buffer;
71
+
72
+ return 0;
73
+ }
74
+
75
+ // We have read some source from file into source buffer.
76
+ // Than algorithm has read part of this source.
77
+ // We need to move remaining source to the top of source buffer.
78
+ // Than we can read more source from file.
79
+ // Algorithm can use same buffer again.
80
+
81
+ static inline zstds_ext_result_t read_more_source(
82
+ FILE* source_file,
83
+ const uint8_t** source_ptr, size_t* source_length_ptr,
84
+ uint8_t* source_buffer, size_t source_buffer_length)
85
+ {
86
+ const uint8_t* source = *source_ptr;
87
+ size_t source_length = *source_length_ptr;
88
+
89
+ if (source != source_buffer) {
90
+ if (source_length != 0) {
91
+ memmove(source_buffer, source, source_length);
92
+ }
93
+
94
+ // Source can be accessed even if next code will fail.
95
+ *source_ptr = source_buffer;
96
+ }
97
+
98
+ size_t remaining_source_buffer_length = source_buffer_length - source_length;
99
+ if (remaining_source_buffer_length == 0) {
100
+ // We want to read more data at once, than buffer has.
101
+ return ZSTDS_EXT_ERROR_NOT_ENOUGH_SOURCE_BUFFER;
102
+ }
103
+
104
+ uint8_t* remaining_source_buffer = source_buffer + source_length;
105
+ size_t new_source_length;
106
+
107
+ zstds_ext_result_t ext_result = read_file(source_file, remaining_source_buffer, &new_source_length, remaining_source_buffer_length);
108
+ if (ext_result != 0) {
109
+ return ext_result;
110
+ }
111
+
112
+ *source_length_ptr = source_length + new_source_length;
113
+
114
+ return 0;
115
+ }
116
+
117
+ #define BUFFERED_READ_SOURCE(function, ...) \
118
+ do { \
119
+ bool is_function_called = false; \
120
+ \
121
+ while (true) { \
122
+ ext_result = read_more_source( \
123
+ source_file, \
124
+ &source, &source_length, \
125
+ source_buffer, source_buffer_length); \
126
+ \
127
+ if (ext_result == ZSTDS_EXT_FILE_READ_FINISHED) { \
128
+ if (source_length != 0) { \
129
+ /* ZSTD won't provide any remainder by design. */ \
130
+ return ZSTDS_EXT_ERROR_READ_IO; \
131
+ } \
132
+ break; \
133
+ } \
134
+ else if (ext_result != 0) { \
135
+ return ext_result; \
136
+ } \
137
+ \
138
+ ext_result = function(__VA_ARGS__); \
139
+ if (ext_result != 0) { \
140
+ return ext_result; \
141
+ } \
142
+ \
143
+ is_function_called = true; \
144
+ } \
145
+ \
146
+ if (!is_function_called) { \
147
+ /* Function should be called at least once. */ \
148
+ ext_result = function(__VA_ARGS__); \
149
+ if (ext_result != 0) { \
150
+ return ext_result; \
151
+ } \
152
+ } \
153
+ } while (false);
154
+
155
+ // Algorithm has written data into destination buffer.
156
+ // We need to write this data into file.
157
+ // Than algorithm can use same buffer again.
158
+
159
+ static inline zstds_ext_result_t flush_destination_buffer(
160
+ FILE* destination_file,
161
+ uint8_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
162
+ {
163
+ if (*destination_length_ptr == 0) {
164
+ // We want to write more data at once, than buffer has.
165
+ return ZSTDS_EXT_ERROR_NOT_ENOUGH_DESTINATION_BUFFER;
166
+ }
167
+
168
+ zstds_ext_result_t ext_result = write_file(destination_file, destination_buffer, *destination_length_ptr);
169
+ if (ext_result != 0) {
170
+ return ext_result;
171
+ }
172
+
173
+ *destination_length_ptr = 0;
174
+
175
+ return 0;
176
+ }
177
+
178
+ static inline zstds_ext_result_t write_remaining_destination(FILE* destination_file, uint8_t* destination_buffer, size_t destination_length)
179
+ {
180
+ if (destination_length == 0) {
181
+ return 0;
182
+ }
183
+
184
+ return write_file(destination_file, destination_buffer, destination_length);
185
+ }
186
+
187
+ // -- utils --
188
+
189
+ #define GET_FILE(target) \
190
+ Check_Type(target, T_FILE); \
191
+ \
192
+ rb_io_t* target##_io; \
193
+ GetOpenFile(target, target##_io); \
194
+ \
195
+ FILE* target##_file = rb_io_stdio_file(target##_io); \
196
+ if (target##_file == NULL) { \
197
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ACCESS_IO); \
198
+ }
199
+
200
+ // -- compress --
201
+
202
+ static inline zstds_ext_result_t buffered_compress(
203
+ ZSTD_CCtx* ctx,
204
+ const uint8_t** source_ptr, size_t* source_length_ptr,
205
+ FILE* destination_file, uint8_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
206
+ {
207
+ zstds_result_t result;
208
+ zstds_ext_result_t ext_result;
209
+
210
+ ZSTD_inBuffer in_buffer;
211
+ in_buffer.src = *source_ptr;
212
+ in_buffer.size = *source_length_ptr;
213
+ in_buffer.pos = 0;
214
+
215
+ ZSTD_outBuffer out_buffer;
216
+
217
+ while (true) {
218
+ out_buffer.dst = destination_buffer + *destination_length_ptr;
219
+ out_buffer.size = destination_buffer_length - *destination_length_ptr;
220
+ out_buffer.pos = 0;
221
+
222
+ result = ZSTD_compressStream2(ctx, &out_buffer, &in_buffer, ZSTD_e_continue);
223
+ if (ZSTD_isError(result)) {
224
+ return zstds_ext_get_error(ZSTD_getErrorCode(result));
225
+ }
226
+
227
+ *destination_length_ptr += out_buffer.pos;
228
+
229
+ if (*destination_length_ptr == destination_buffer_length) {
230
+ ext_result = flush_destination_buffer(
231
+ destination_file,
232
+ destination_buffer, destination_length_ptr, destination_buffer_length);
233
+
234
+ if (ext_result != 0) {
235
+ return ext_result;
236
+ }
237
+
238
+ continue;
239
+ }
240
+
241
+ break;
242
+ }
243
+
244
+ *source_ptr += in_buffer.pos;
245
+ *source_length_ptr -= in_buffer.pos;
246
+
247
+ return 0;
248
+ }
249
+
250
+ static inline zstds_ext_result_t buffered_compressor_finish(
251
+ ZSTD_CCtx* ctx,
252
+ FILE* destination_file, uint8_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
253
+ {
254
+ zstds_result_t result;
255
+ zstds_ext_result_t ext_result;
256
+
257
+ ZSTD_inBuffer in_buffer;
258
+ in_buffer.src = NULL;
259
+ in_buffer.size = 0;
260
+ in_buffer.pos = 0;
261
+
262
+ ZSTD_outBuffer out_buffer;
263
+
264
+ while (true) {
265
+ out_buffer.dst = destination_buffer + *destination_length_ptr;
266
+ out_buffer.size = destination_buffer_length - *destination_length_ptr;
267
+ out_buffer.pos = 0;
268
+
269
+ result = ZSTD_compressStream2(ctx, &out_buffer, &in_buffer, ZSTD_e_end);
270
+ if (ZSTD_isError(result)) {
271
+ return zstds_ext_get_error(ZSTD_getErrorCode(result));
272
+ }
273
+
274
+ *destination_length_ptr += out_buffer.pos;
275
+
276
+ if (result != 0) {
277
+ ext_result = flush_destination_buffer(
278
+ destination_file,
279
+ destination_buffer, destination_length_ptr, destination_buffer_length);
280
+
281
+ if (ext_result != 0) {
282
+ return ext_result;
283
+ }
284
+
285
+ continue;
286
+ }
287
+
288
+ break;
289
+ }
290
+
291
+ return 0;
292
+ }
293
+
294
+ static inline zstds_ext_result_t compress(
295
+ ZSTD_CCtx* ctx,
296
+ FILE* source_file, uint8_t* source_buffer, size_t source_buffer_length,
297
+ FILE* destination_file, uint8_t* destination_buffer, size_t destination_buffer_length)
298
+ {
299
+ zstds_ext_result_t ext_result;
300
+
301
+ const uint8_t* source = source_buffer;
302
+ size_t source_length = 0;
303
+ size_t destination_length = 0;
304
+
305
+ BUFFERED_READ_SOURCE(
306
+ buffered_compress,
307
+ ctx,
308
+ &source, &source_length,
309
+ destination_file, destination_buffer, &destination_length, destination_buffer_length);
310
+
311
+ ext_result = buffered_compressor_finish(
312
+ ctx,
313
+ destination_file, destination_buffer, &destination_length, destination_buffer_length);
314
+
315
+ if (ext_result != 0) {
316
+ return ext_result;
317
+ }
318
+
319
+ return write_remaining_destination(destination_file, destination_buffer, destination_length);
320
+ }
321
+
322
+ VALUE zstds_ext_compress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
323
+ {
324
+ GET_FILE(source);
325
+ GET_FILE(destination);
326
+ Check_Type(options, T_HASH);
327
+ ZSTDS_EXT_GET_COMPRESSOR_OPTIONS(options);
328
+ ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
329
+ ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
330
+
331
+ ZSTD_CCtx* ctx = ZSTD_createCCtx();
332
+ if (ctx == NULL) {
333
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
334
+ }
335
+
336
+ zstds_ext_result_t ext_result = zstds_ext_set_compressor_options(ctx, &compressor_options);
337
+ if (ext_result != 0) {
338
+ ZSTD_freeCCtx(ctx);
339
+ zstds_ext_raise_error(ext_result);
340
+ }
341
+
342
+ if (source_buffer_length == 0) {
343
+ source_buffer_length = ZSTD_CStreamInSize();
344
+ }
345
+ if (destination_buffer_length == 0) {
346
+ destination_buffer_length = ZSTD_CStreamOutSize();
347
+ }
348
+
349
+ uint8_t* source_buffer;
350
+ uint8_t* destination_buffer;
351
+
352
+ ext_result = create_buffers(
353
+ &source_buffer, source_buffer_length,
354
+ &destination_buffer, destination_buffer_length);
355
+
356
+ if (ext_result != 0) {
357
+ ZSTD_freeCCtx(ctx);
358
+ zstds_ext_raise_error(ext_result);
359
+ }
360
+
361
+ ext_result = compress(
362
+ ctx,
363
+ source_file, source_buffer, source_buffer_length,
364
+ destination_file, destination_buffer, destination_buffer_length);
365
+
366
+ free(source_buffer);
367
+ free(destination_buffer);
368
+ ZSTD_freeCCtx(ctx);
369
+
370
+ if (ext_result != 0) {
371
+ zstds_ext_raise_error(ext_result);
372
+ }
373
+
374
+ // Ruby itself won't flush stdio file before closing fd, flush is required.
375
+ fflush(destination_file);
376
+
377
+ return Qnil;
378
+ }
379
+
380
+ // -- decompress --
381
+
382
+ static inline zstds_ext_result_t buffered_decompress(
383
+ ZSTD_DCtx* ctx,
384
+ const uint8_t** source_ptr, size_t* source_length_ptr,
385
+ FILE* destination_file, uint8_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
386
+ {
387
+ zstds_result_t result;
388
+ zstds_ext_result_t ext_result;
389
+
390
+ ZSTD_inBuffer in_buffer;
391
+ in_buffer.src = *source_ptr;
392
+ in_buffer.size = *source_length_ptr;
393
+ in_buffer.pos = 0;
394
+
395
+ ZSTD_outBuffer out_buffer;
396
+
397
+ while (true) {
398
+ out_buffer.dst = destination_buffer + *destination_length_ptr;
399
+ out_buffer.size = destination_buffer_length - *destination_length_ptr;
400
+ out_buffer.pos = 0;
401
+
402
+ result = ZSTD_decompressStream(ctx, &out_buffer, &in_buffer);
403
+ if (ZSTD_isError(result)) {
404
+ return zstds_ext_get_error(ZSTD_getErrorCode(result));
405
+ }
406
+
407
+ *destination_length_ptr += out_buffer.pos;
408
+
409
+ if (*destination_length_ptr == destination_buffer_length) {
410
+ ext_result = flush_destination_buffer(
411
+ destination_file,
412
+ destination_buffer, destination_length_ptr, destination_buffer_length);
413
+
414
+ if (ext_result != 0) {
415
+ return ext_result;
416
+ }
417
+
418
+ continue;
419
+ }
420
+
421
+ break;
422
+ }
423
+
424
+ *source_ptr += in_buffer.pos;
425
+ *source_length_ptr -= in_buffer.pos;
426
+
427
+ return 0;
428
+ }
429
+
430
+ static inline zstds_ext_result_t decompress(
431
+ ZSTD_DCtx* ctx,
432
+ FILE* source_file, uint8_t* source_buffer, size_t source_buffer_length,
433
+ FILE* destination_file, uint8_t* destination_buffer, size_t destination_buffer_length)
434
+ {
435
+ zstds_ext_result_t ext_result;
436
+
437
+ const uint8_t* source = source_buffer;
438
+ size_t source_length = 0;
439
+ size_t destination_length = 0;
440
+
441
+ BUFFERED_READ_SOURCE(
442
+ buffered_decompress,
443
+ ctx,
444
+ &source, &source_length,
445
+ destination_file, destination_buffer, &destination_length, destination_buffer_length);
446
+
447
+ return write_remaining_destination(destination_file, destination_buffer, destination_length);
448
+ }
449
+
450
+ VALUE zstds_ext_decompress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE destination, VALUE options)
451
+ {
452
+ GET_FILE(source);
453
+ GET_FILE(destination);
454
+ Check_Type(options, T_HASH);
455
+ ZSTDS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
456
+ ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
457
+ ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
458
+
459
+ ZSTD_DCtx* ctx = ZSTD_createDCtx();
460
+ if (ctx == NULL) {
461
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
462
+ }
463
+
464
+ zstds_ext_result_t ext_result = zstds_ext_set_decompressor_options(ctx, &decompressor_options);
465
+ if (ext_result != 0) {
466
+ ZSTD_freeDCtx(ctx);
467
+ zstds_ext_raise_error(ext_result);
468
+ }
469
+
470
+ if (source_buffer_length == 0) {
471
+ source_buffer_length = ZSTD_DStreamInSize();
472
+ }
473
+ if (destination_buffer_length == 0) {
474
+ destination_buffer_length = ZSTD_DStreamOutSize();
475
+ }
476
+
477
+ uint8_t* source_buffer;
478
+ uint8_t* destination_buffer;
479
+
480
+ ext_result = create_buffers(
481
+ &source_buffer, source_buffer_length,
482
+ &destination_buffer, destination_buffer_length);
483
+
484
+ if (ext_result != 0) {
485
+ ZSTD_freeDCtx(ctx);
486
+ zstds_ext_raise_error(ext_result);
487
+ }
488
+
489
+ ext_result = decompress(
490
+ ctx,
491
+ source_file, source_buffer, source_buffer_length,
492
+ destination_file, destination_buffer, destination_buffer_length);
493
+
494
+ free(source_buffer);
495
+ free(destination_buffer);
496
+ ZSTD_freeDCtx(ctx);
497
+
498
+ if (ext_result != 0) {
499
+ zstds_ext_raise_error(ext_result);
500
+ }
501
+
502
+ // Ruby itself won't flush stdio file before closing fd, flush is required.
503
+ fflush(destination_file);
504
+
505
+ return Qnil;
506
+ }
507
+
508
+ void zstds_ext_io_exports(VALUE root_module)
509
+ {
510
+ rb_define_module_function(root_module, "_native_compress_io", RUBY_METHOD_FUNC(zstds_ext_compress_io), 3);
511
+ rb_define_module_function(root_module, "_native_decompress_io", RUBY_METHOD_FUNC(zstds_ext_decompress_io), 3);
512
+ }