konpeito 0.1.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/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- metadata +267 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Konpeito Compression stdlib - zlib wrapper
|
|
3
|
+
*
|
|
4
|
+
* Provides compression/decompression using zlib.
|
|
5
|
+
* Supports gzip, deflate, and raw compression.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include <ruby.h>
|
|
9
|
+
#include <zlib.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
#include <string.h>
|
|
12
|
+
|
|
13
|
+
/* Default chunk size for compression */
|
|
14
|
+
#define CHUNK_SIZE 16384
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Compress data using gzip format
|
|
18
|
+
*
|
|
19
|
+
* @param data [String] Data to compress
|
|
20
|
+
* @return [String] Gzip-compressed data
|
|
21
|
+
* @raise [RuntimeError] if compression fails
|
|
22
|
+
*/
|
|
23
|
+
VALUE konpeito_compression_gzip(VALUE self, VALUE data) {
|
|
24
|
+
Check_Type(data, T_STRING);
|
|
25
|
+
|
|
26
|
+
const unsigned char *input = (const unsigned char *)RSTRING_PTR(data);
|
|
27
|
+
size_t input_len = RSTRING_LEN(data);
|
|
28
|
+
|
|
29
|
+
/* Allocate output buffer (worst case: input size + overhead) */
|
|
30
|
+
uLongf output_len = compressBound(input_len) + 18; /* gzip header/footer */
|
|
31
|
+
unsigned char *output = malloc(output_len);
|
|
32
|
+
if (!output) {
|
|
33
|
+
rb_raise(rb_eNoMemError, "Failed to allocate compression buffer");
|
|
34
|
+
return Qnil;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Initialize zlib stream for gzip */
|
|
38
|
+
z_stream stream;
|
|
39
|
+
memset(&stream, 0, sizeof(stream));
|
|
40
|
+
|
|
41
|
+
/* windowBits 15 + 16 = gzip format */
|
|
42
|
+
int ret = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
|
43
|
+
15 + 16, 8, Z_DEFAULT_STRATEGY);
|
|
44
|
+
if (ret != Z_OK) {
|
|
45
|
+
free(output);
|
|
46
|
+
rb_raise(rb_eRuntimeError, "Failed to initialize compression: %s",
|
|
47
|
+
stream.msg ? stream.msg : "unknown error");
|
|
48
|
+
return Qnil;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
stream.next_in = (Bytef *)input;
|
|
52
|
+
stream.avail_in = (uInt)input_len;
|
|
53
|
+
stream.next_out = output;
|
|
54
|
+
stream.avail_out = (uInt)output_len;
|
|
55
|
+
|
|
56
|
+
ret = deflate(&stream, Z_FINISH);
|
|
57
|
+
if (ret != Z_STREAM_END) {
|
|
58
|
+
deflateEnd(&stream);
|
|
59
|
+
free(output);
|
|
60
|
+
rb_raise(rb_eRuntimeError, "Compression failed: %s",
|
|
61
|
+
stream.msg ? stream.msg : "unknown error");
|
|
62
|
+
return Qnil;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
size_t compressed_len = stream.total_out;
|
|
66
|
+
deflateEnd(&stream);
|
|
67
|
+
|
|
68
|
+
VALUE result = rb_str_new((const char *)output, compressed_len);
|
|
69
|
+
free(output);
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* Decompress gzip data
|
|
76
|
+
*
|
|
77
|
+
* @param data [String] Gzip-compressed data
|
|
78
|
+
* @return [String] Decompressed data
|
|
79
|
+
* @raise [RuntimeError] if decompression fails
|
|
80
|
+
*/
|
|
81
|
+
VALUE konpeito_compression_gunzip(VALUE self, VALUE data) {
|
|
82
|
+
Check_Type(data, T_STRING);
|
|
83
|
+
|
|
84
|
+
const unsigned char *input = (const unsigned char *)RSTRING_PTR(data);
|
|
85
|
+
size_t input_len = RSTRING_LEN(data);
|
|
86
|
+
|
|
87
|
+
/* Start with a reasonable output buffer size */
|
|
88
|
+
size_t output_size = input_len * 4;
|
|
89
|
+
if (output_size < CHUNK_SIZE) output_size = CHUNK_SIZE;
|
|
90
|
+
unsigned char *output = malloc(output_size);
|
|
91
|
+
if (!output) {
|
|
92
|
+
rb_raise(rb_eNoMemError, "Failed to allocate decompression buffer");
|
|
93
|
+
return Qnil;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Initialize zlib stream for gzip */
|
|
97
|
+
z_stream stream;
|
|
98
|
+
memset(&stream, 0, sizeof(stream));
|
|
99
|
+
|
|
100
|
+
/* windowBits 15 + 32 = auto-detect gzip/zlib */
|
|
101
|
+
int ret = inflateInit2(&stream, 15 + 32);
|
|
102
|
+
if (ret != Z_OK) {
|
|
103
|
+
free(output);
|
|
104
|
+
rb_raise(rb_eRuntimeError, "Failed to initialize decompression: %s",
|
|
105
|
+
stream.msg ? stream.msg : "unknown error");
|
|
106
|
+
return Qnil;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
stream.next_in = (Bytef *)input;
|
|
110
|
+
stream.avail_in = (uInt)input_len;
|
|
111
|
+
|
|
112
|
+
size_t total_out = 0;
|
|
113
|
+
|
|
114
|
+
do {
|
|
115
|
+
/* Expand buffer if needed */
|
|
116
|
+
if (total_out >= output_size) {
|
|
117
|
+
output_size *= 2;
|
|
118
|
+
unsigned char *new_output = realloc(output, output_size);
|
|
119
|
+
if (!new_output) {
|
|
120
|
+
inflateEnd(&stream);
|
|
121
|
+
free(output);
|
|
122
|
+
rb_raise(rb_eNoMemError, "Failed to expand decompression buffer");
|
|
123
|
+
return Qnil;
|
|
124
|
+
}
|
|
125
|
+
output = new_output;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
stream.next_out = output + total_out;
|
|
129
|
+
stream.avail_out = (uInt)(output_size - total_out);
|
|
130
|
+
|
|
131
|
+
ret = inflate(&stream, Z_NO_FLUSH);
|
|
132
|
+
|
|
133
|
+
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
|
|
134
|
+
inflateEnd(&stream);
|
|
135
|
+
free(output);
|
|
136
|
+
rb_raise(rb_eRuntimeError, "Decompression failed: %s",
|
|
137
|
+
stream.msg ? stream.msg : "data error");
|
|
138
|
+
return Qnil;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
total_out = stream.total_out;
|
|
142
|
+
|
|
143
|
+
} while (ret != Z_STREAM_END);
|
|
144
|
+
|
|
145
|
+
inflateEnd(&stream);
|
|
146
|
+
|
|
147
|
+
VALUE result = rb_str_new((const char *)output, total_out);
|
|
148
|
+
free(output);
|
|
149
|
+
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* Compress data using deflate (raw zlib format, no header)
|
|
155
|
+
*
|
|
156
|
+
* @param data [String] Data to compress
|
|
157
|
+
* @param level [Integer] Compression level (0-9, default 6)
|
|
158
|
+
* @return [String] Deflate-compressed data
|
|
159
|
+
* @raise [RuntimeError] if compression fails
|
|
160
|
+
*/
|
|
161
|
+
VALUE konpeito_compression_deflate(VALUE self, VALUE data, VALUE level) {
|
|
162
|
+
Check_Type(data, T_STRING);
|
|
163
|
+
|
|
164
|
+
const unsigned char *input = (const unsigned char *)RSTRING_PTR(data);
|
|
165
|
+
size_t input_len = RSTRING_LEN(data);
|
|
166
|
+
int compression_level = NIL_P(level) ? Z_DEFAULT_COMPRESSION : NUM2INT(level);
|
|
167
|
+
|
|
168
|
+
if (compression_level < 0 || compression_level > 9) {
|
|
169
|
+
if (compression_level != Z_DEFAULT_COMPRESSION) {
|
|
170
|
+
rb_raise(rb_eArgError, "Compression level must be 0-9");
|
|
171
|
+
return Qnil;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Allocate output buffer */
|
|
176
|
+
uLongf output_len = compressBound(input_len);
|
|
177
|
+
unsigned char *output = malloc(output_len);
|
|
178
|
+
if (!output) {
|
|
179
|
+
rb_raise(rb_eNoMemError, "Failed to allocate compression buffer");
|
|
180
|
+
return Qnil;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Initialize zlib stream for raw deflate */
|
|
184
|
+
z_stream stream;
|
|
185
|
+
memset(&stream, 0, sizeof(stream));
|
|
186
|
+
|
|
187
|
+
/* windowBits -15 = raw deflate (no zlib header) */
|
|
188
|
+
int ret = deflateInit2(&stream, compression_level, Z_DEFLATED,
|
|
189
|
+
-15, 8, Z_DEFAULT_STRATEGY);
|
|
190
|
+
if (ret != Z_OK) {
|
|
191
|
+
free(output);
|
|
192
|
+
rb_raise(rb_eRuntimeError, "Failed to initialize compression: %s",
|
|
193
|
+
stream.msg ? stream.msg : "unknown error");
|
|
194
|
+
return Qnil;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
stream.next_in = (Bytef *)input;
|
|
198
|
+
stream.avail_in = (uInt)input_len;
|
|
199
|
+
stream.next_out = output;
|
|
200
|
+
stream.avail_out = (uInt)output_len;
|
|
201
|
+
|
|
202
|
+
ret = deflate(&stream, Z_FINISH);
|
|
203
|
+
if (ret != Z_STREAM_END) {
|
|
204
|
+
deflateEnd(&stream);
|
|
205
|
+
free(output);
|
|
206
|
+
rb_raise(rb_eRuntimeError, "Compression failed: %s",
|
|
207
|
+
stream.msg ? stream.msg : "unknown error");
|
|
208
|
+
return Qnil;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
size_t compressed_len = stream.total_out;
|
|
212
|
+
deflateEnd(&stream);
|
|
213
|
+
|
|
214
|
+
VALUE result = rb_str_new((const char *)output, compressed_len);
|
|
215
|
+
free(output);
|
|
216
|
+
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* Decompress deflate data (raw zlib format, no header)
|
|
222
|
+
*
|
|
223
|
+
* @param data [String] Deflate-compressed data
|
|
224
|
+
* @return [String] Decompressed data
|
|
225
|
+
* @raise [RuntimeError] if decompression fails
|
|
226
|
+
*/
|
|
227
|
+
VALUE konpeito_compression_inflate(VALUE self, VALUE data) {
|
|
228
|
+
Check_Type(data, T_STRING);
|
|
229
|
+
|
|
230
|
+
const unsigned char *input = (const unsigned char *)RSTRING_PTR(data);
|
|
231
|
+
size_t input_len = RSTRING_LEN(data);
|
|
232
|
+
|
|
233
|
+
/* Start with a reasonable output buffer size */
|
|
234
|
+
size_t output_size = input_len * 4;
|
|
235
|
+
if (output_size < CHUNK_SIZE) output_size = CHUNK_SIZE;
|
|
236
|
+
unsigned char *output = malloc(output_size);
|
|
237
|
+
if (!output) {
|
|
238
|
+
rb_raise(rb_eNoMemError, "Failed to allocate decompression buffer");
|
|
239
|
+
return Qnil;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Initialize zlib stream for raw inflate */
|
|
243
|
+
z_stream stream;
|
|
244
|
+
memset(&stream, 0, sizeof(stream));
|
|
245
|
+
|
|
246
|
+
/* windowBits -15 = raw inflate (no zlib header) */
|
|
247
|
+
int ret = inflateInit2(&stream, -15);
|
|
248
|
+
if (ret != Z_OK) {
|
|
249
|
+
free(output);
|
|
250
|
+
rb_raise(rb_eRuntimeError, "Failed to initialize decompression: %s",
|
|
251
|
+
stream.msg ? stream.msg : "unknown error");
|
|
252
|
+
return Qnil;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
stream.next_in = (Bytef *)input;
|
|
256
|
+
stream.avail_in = (uInt)input_len;
|
|
257
|
+
|
|
258
|
+
size_t total_out = 0;
|
|
259
|
+
|
|
260
|
+
do {
|
|
261
|
+
/* Expand buffer if needed */
|
|
262
|
+
if (total_out >= output_size) {
|
|
263
|
+
output_size *= 2;
|
|
264
|
+
unsigned char *new_output = realloc(output, output_size);
|
|
265
|
+
if (!new_output) {
|
|
266
|
+
inflateEnd(&stream);
|
|
267
|
+
free(output);
|
|
268
|
+
rb_raise(rb_eNoMemError, "Failed to expand decompression buffer");
|
|
269
|
+
return Qnil;
|
|
270
|
+
}
|
|
271
|
+
output = new_output;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
stream.next_out = output + total_out;
|
|
275
|
+
stream.avail_out = (uInt)(output_size - total_out);
|
|
276
|
+
|
|
277
|
+
ret = inflate(&stream, Z_NO_FLUSH);
|
|
278
|
+
|
|
279
|
+
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
|
|
280
|
+
inflateEnd(&stream);
|
|
281
|
+
free(output);
|
|
282
|
+
rb_raise(rb_eRuntimeError, "Decompression failed: %s",
|
|
283
|
+
stream.msg ? stream.msg : "data error");
|
|
284
|
+
return Qnil;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
total_out = stream.total_out;
|
|
288
|
+
|
|
289
|
+
} while (ret != Z_STREAM_END);
|
|
290
|
+
|
|
291
|
+
inflateEnd(&stream);
|
|
292
|
+
|
|
293
|
+
VALUE result = rb_str_new((const char *)output, total_out);
|
|
294
|
+
free(output);
|
|
295
|
+
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/*
|
|
300
|
+
* Compress data using zlib format (with header)
|
|
301
|
+
*
|
|
302
|
+
* @param data [String] Data to compress
|
|
303
|
+
* @return [String] Zlib-compressed data
|
|
304
|
+
* @raise [RuntimeError] if compression fails
|
|
305
|
+
*/
|
|
306
|
+
VALUE konpeito_compression_zlib_compress(VALUE self, VALUE data) {
|
|
307
|
+
Check_Type(data, T_STRING);
|
|
308
|
+
|
|
309
|
+
const unsigned char *input = (const unsigned char *)RSTRING_PTR(data);
|
|
310
|
+
size_t input_len = RSTRING_LEN(data);
|
|
311
|
+
|
|
312
|
+
uLongf output_len = compressBound(input_len);
|
|
313
|
+
unsigned char *output = malloc(output_len);
|
|
314
|
+
if (!output) {
|
|
315
|
+
rb_raise(rb_eNoMemError, "Failed to allocate compression buffer");
|
|
316
|
+
return Qnil;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
int ret = compress(output, &output_len, input, input_len);
|
|
320
|
+
if (ret != Z_OK) {
|
|
321
|
+
free(output);
|
|
322
|
+
rb_raise(rb_eRuntimeError, "Compression failed with code %d", ret);
|
|
323
|
+
return Qnil;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
VALUE result = rb_str_new((const char *)output, output_len);
|
|
327
|
+
free(output);
|
|
328
|
+
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/*
|
|
333
|
+
* Decompress zlib data (with header)
|
|
334
|
+
*
|
|
335
|
+
* @param data [String] Zlib-compressed data
|
|
336
|
+
* @param max_size [Integer] Maximum decompressed size (for safety)
|
|
337
|
+
* @return [String] Decompressed data
|
|
338
|
+
* @raise [RuntimeError] if decompression fails
|
|
339
|
+
*/
|
|
340
|
+
VALUE konpeito_compression_zlib_decompress(VALUE self, VALUE data, VALUE max_size) {
|
|
341
|
+
Check_Type(data, T_STRING);
|
|
342
|
+
|
|
343
|
+
const unsigned char *input = (const unsigned char *)RSTRING_PTR(data);
|
|
344
|
+
size_t input_len = RSTRING_LEN(data);
|
|
345
|
+
size_t max_output = NIL_P(max_size) ? 100 * 1024 * 1024 : (size_t)NUM2LONG(max_size); /* 100MB default */
|
|
346
|
+
|
|
347
|
+
/* Start with estimated output size */
|
|
348
|
+
size_t output_size = input_len * 4;
|
|
349
|
+
if (output_size < CHUNK_SIZE) output_size = CHUNK_SIZE;
|
|
350
|
+
if (output_size > max_output) output_size = max_output;
|
|
351
|
+
|
|
352
|
+
unsigned char *output = malloc(output_size);
|
|
353
|
+
if (!output) {
|
|
354
|
+
rb_raise(rb_eNoMemError, "Failed to allocate decompression buffer");
|
|
355
|
+
return Qnil;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
uLongf destLen = (uLongf)output_size;
|
|
359
|
+
int ret;
|
|
360
|
+
|
|
361
|
+
while (1) {
|
|
362
|
+
ret = uncompress(output, &destLen, input, input_len);
|
|
363
|
+
|
|
364
|
+
if (ret == Z_OK) {
|
|
365
|
+
break;
|
|
366
|
+
} else if (ret == Z_BUF_ERROR) {
|
|
367
|
+
/* Need bigger buffer */
|
|
368
|
+
output_size *= 2;
|
|
369
|
+
if (output_size > max_output) {
|
|
370
|
+
free(output);
|
|
371
|
+
rb_raise(rb_eRuntimeError, "Decompressed data exceeds maximum size");
|
|
372
|
+
return Qnil;
|
|
373
|
+
}
|
|
374
|
+
unsigned char *new_output = realloc(output, output_size);
|
|
375
|
+
if (!new_output) {
|
|
376
|
+
free(output);
|
|
377
|
+
rb_raise(rb_eNoMemError, "Failed to expand decompression buffer");
|
|
378
|
+
return Qnil;
|
|
379
|
+
}
|
|
380
|
+
output = new_output;
|
|
381
|
+
destLen = (uLongf)output_size;
|
|
382
|
+
} else {
|
|
383
|
+
free(output);
|
|
384
|
+
rb_raise(rb_eRuntimeError, "Decompression failed with code %d", ret);
|
|
385
|
+
return Qnil;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
VALUE result = rb_str_new((const char *)output, destLen);
|
|
390
|
+
free(output);
|
|
391
|
+
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Module initialization */
|
|
396
|
+
void Init_konpeito_compression(void) {
|
|
397
|
+
VALUE mKonpeitoCompression = rb_define_module("KonpeitoCompression");
|
|
398
|
+
|
|
399
|
+
/* Gzip format (RFC 1952) */
|
|
400
|
+
rb_define_module_function(mKonpeitoCompression, "gzip", konpeito_compression_gzip, 1);
|
|
401
|
+
rb_define_module_function(mKonpeitoCompression, "gunzip", konpeito_compression_gunzip, 1);
|
|
402
|
+
|
|
403
|
+
/* Raw deflate (RFC 1951) - no header */
|
|
404
|
+
rb_define_module_function(mKonpeitoCompression, "deflate", konpeito_compression_deflate, 2);
|
|
405
|
+
rb_define_module_function(mKonpeitoCompression, "inflate", konpeito_compression_inflate, 1);
|
|
406
|
+
|
|
407
|
+
/* Zlib format (RFC 1950) - with header */
|
|
408
|
+
rb_define_module_function(mKonpeitoCompression, "zlib_compress", konpeito_compression_zlib_compress, 1);
|
|
409
|
+
rb_define_module_function(mKonpeitoCompression, "zlib_decompress", konpeito_compression_zlib_decompress, 2);
|
|
410
|
+
|
|
411
|
+
/* Constants */
|
|
412
|
+
rb_define_const(mKonpeitoCompression, "BEST_SPEED", INT2FIX(Z_BEST_SPEED));
|
|
413
|
+
rb_define_const(mKonpeitoCompression, "BEST_COMPRESSION", INT2FIX(Z_BEST_COMPRESSION));
|
|
414
|
+
rb_define_const(mKonpeitoCompression, "DEFAULT_COMPRESSION", INT2FIX(Z_DEFAULT_COMPRESSION));
|
|
415
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# extconf.rb for konpeito_compression
|
|
2
|
+
require 'mkmf'
|
|
3
|
+
|
|
4
|
+
# Check for zlib
|
|
5
|
+
unless have_library('z') && have_header('zlib.h')
|
|
6
|
+
abort <<~MSG
|
|
7
|
+
zlib is required for KonpeitoCompression.
|
|
8
|
+
|
|
9
|
+
Installation:
|
|
10
|
+
macOS: Usually pre-installed. If not: brew install zlib
|
|
11
|
+
Ubuntu: sudo apt-get install zlib1g-dev
|
|
12
|
+
Fedora: sudo dnf install zlib-devel
|
|
13
|
+
MSG
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Optimization flags
|
|
17
|
+
$CFLAGS << ' -O3'
|
|
18
|
+
|
|
19
|
+
create_makefile('konpeito_compression')
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# KonpeitoCrypto - Cryptographic operations using OpenSSL
|
|
2
|
+
#
|
|
3
|
+
# This module provides cryptographic functionality using the OpenSSL library.
|
|
4
|
+
# It is implemented as a C extension for maximum performance.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# require 'konpeito/stdlib/crypto'
|
|
8
|
+
#
|
|
9
|
+
# # Hash functions (hex output)
|
|
10
|
+
# hash = KonpeitoCrypto.sha256("hello world")
|
|
11
|
+
# # => "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
|
12
|
+
#
|
|
13
|
+
# hash = KonpeitoCrypto.sha512("hello world")
|
|
14
|
+
# # => "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f..."
|
|
15
|
+
#
|
|
16
|
+
# # HMAC for message authentication
|
|
17
|
+
# mac = KonpeitoCrypto.hmac_sha256("secret-key", "message to authenticate")
|
|
18
|
+
#
|
|
19
|
+
# # Secure random bytes
|
|
20
|
+
# bytes = KonpeitoCrypto.random_bytes(32) # 32 random bytes
|
|
21
|
+
# hex = KonpeitoCrypto.random_hex(16) # 32 character hex string
|
|
22
|
+
#
|
|
23
|
+
# # Timing-safe comparison (for password/token verification)
|
|
24
|
+
# if KonpeitoCrypto.secure_compare(user_token, stored_token)
|
|
25
|
+
# puts "Valid token"
|
|
26
|
+
# end
|
|
27
|
+
|
|
28
|
+
# Try to load the native extension
|
|
29
|
+
begin
|
|
30
|
+
require_relative 'konpeito_crypto'
|
|
31
|
+
rescue LoadError => e
|
|
32
|
+
# Fallback to OpenSSL gem if native extension is not available
|
|
33
|
+
require 'openssl'
|
|
34
|
+
require 'securerandom'
|
|
35
|
+
|
|
36
|
+
module KonpeitoCrypto
|
|
37
|
+
class << self
|
|
38
|
+
def sha256(data)
|
|
39
|
+
OpenSSL::Digest::SHA256.hexdigest(data)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def sha256_binary(data)
|
|
43
|
+
OpenSSL::Digest::SHA256.digest(data)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def sha512(data)
|
|
47
|
+
OpenSSL::Digest::SHA512.hexdigest(data)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def sha512_binary(data)
|
|
51
|
+
OpenSSL::Digest::SHA512.digest(data)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def hmac_sha256(key, data)
|
|
55
|
+
OpenSSL::HMAC.hexdigest('SHA256', key, data)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def hmac_sha256_binary(key, data)
|
|
59
|
+
OpenSSL::HMAC.digest('SHA256', key, data)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def hmac_sha512(key, data)
|
|
63
|
+
OpenSSL::HMAC.hexdigest('SHA512', key, data)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def random_bytes(count)
|
|
67
|
+
SecureRandom.random_bytes(count)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def random_hex(count)
|
|
71
|
+
SecureRandom.hex(count)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def secure_compare(a, b)
|
|
75
|
+
return false if a.bytesize != b.bytesize
|
|
76
|
+
|
|
77
|
+
result = 0
|
|
78
|
+
a.bytes.zip(b.bytes) { |x, y| result |= x ^ y }
|
|
79
|
+
result == 0
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
warn "KonpeitoCrypto: Native extension not available, using OpenSSL gem fallback (#{e.message})"
|
|
85
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# KonpeitoCrypto - Cryptographic operations using OpenSSL
|
|
2
|
+
#
|
|
3
|
+
# Usage:
|
|
4
|
+
# # Hash functions
|
|
5
|
+
# hash = KonpeitoCrypto.sha256("hello") # => "2cf24dba5fb0a30e..."
|
|
6
|
+
# hash = KonpeitoCrypto.sha512("hello") # => "9b71d224bd62f378..."
|
|
7
|
+
#
|
|
8
|
+
# # HMAC
|
|
9
|
+
# mac = KonpeitoCrypto.hmac_sha256("secret", "message")
|
|
10
|
+
#
|
|
11
|
+
# # Random bytes
|
|
12
|
+
# bytes = KonpeitoCrypto.random_bytes(32) # => 32 random bytes (binary)
|
|
13
|
+
# hex = KonpeitoCrypto.random_hex(16) # => 32 char hex string
|
|
14
|
+
#
|
|
15
|
+
# # Secure comparison (timing-attack safe)
|
|
16
|
+
# KonpeitoCrypto.secure_compare(hash1, hash2)
|
|
17
|
+
|
|
18
|
+
module KonpeitoCrypto
|
|
19
|
+
# Compute SHA256 hash (hex output)
|
|
20
|
+
# @param data [String] Data to hash
|
|
21
|
+
# @return [String] Hex-encoded SHA256 hash (64 characters)
|
|
22
|
+
def self.sha256: (String data) -> String
|
|
23
|
+
|
|
24
|
+
# Compute SHA256 hash (binary output)
|
|
25
|
+
# @param data [String] Data to hash
|
|
26
|
+
# @return [String] Binary SHA256 hash (32 bytes)
|
|
27
|
+
def self.sha256_binary: (String data) -> String
|
|
28
|
+
|
|
29
|
+
# Compute SHA512 hash (hex output)
|
|
30
|
+
# @param data [String] Data to hash
|
|
31
|
+
# @return [String] Hex-encoded SHA512 hash (128 characters)
|
|
32
|
+
def self.sha512: (String data) -> String
|
|
33
|
+
|
|
34
|
+
# Compute SHA512 hash (binary output)
|
|
35
|
+
# @param data [String] Data to hash
|
|
36
|
+
# @return [String] Binary SHA512 hash (64 bytes)
|
|
37
|
+
def self.sha512_binary: (String data) -> String
|
|
38
|
+
|
|
39
|
+
# Compute HMAC-SHA256 (hex output)
|
|
40
|
+
# @param key [String] Secret key
|
|
41
|
+
# @param data [String] Data to authenticate
|
|
42
|
+
# @return [String] Hex-encoded HMAC-SHA256 (64 characters)
|
|
43
|
+
def self.hmac_sha256: (String key, String data) -> String
|
|
44
|
+
|
|
45
|
+
# Compute HMAC-SHA256 (binary output)
|
|
46
|
+
# @param key [String] Secret key
|
|
47
|
+
# @param data [String] Data to authenticate
|
|
48
|
+
# @return [String] Binary HMAC-SHA256 (32 bytes)
|
|
49
|
+
def self.hmac_sha256_binary: (String key, String data) -> String
|
|
50
|
+
|
|
51
|
+
# Compute HMAC-SHA512 (hex output)
|
|
52
|
+
# @param key [String] Secret key
|
|
53
|
+
# @param data [String] Data to authenticate
|
|
54
|
+
# @return [String] Hex-encoded HMAC-SHA512 (128 characters)
|
|
55
|
+
def self.hmac_sha512: (String key, String data) -> String
|
|
56
|
+
|
|
57
|
+
# Generate cryptographically secure random bytes
|
|
58
|
+
# @param count [Integer] Number of bytes to generate
|
|
59
|
+
# @return [String] Binary string of random bytes
|
|
60
|
+
# @raise [RuntimeError] if random generation fails
|
|
61
|
+
def self.random_bytes: (Integer count) -> String
|
|
62
|
+
|
|
63
|
+
# Generate cryptographically secure random bytes as hex string
|
|
64
|
+
# @param count [Integer] Number of bytes to generate (output will be 2x this length)
|
|
65
|
+
# @return [String] Hex-encoded random bytes
|
|
66
|
+
# @raise [RuntimeError] if random generation fails
|
|
67
|
+
def self.random_hex: (Integer count) -> String
|
|
68
|
+
|
|
69
|
+
# Constant-time comparison of two strings (prevents timing attacks)
|
|
70
|
+
# @param a [String] First string
|
|
71
|
+
# @param b [String] Second string
|
|
72
|
+
# @return [bool] true if strings are equal, false otherwise
|
|
73
|
+
def self.secure_compare: (String a, String b) -> bool
|
|
74
|
+
end
|