ob64 0.5.0 → 0.7.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 +4 -4
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/main.yml +78 -16
- data/.standard.yml +1 -2
- data/CHANGELOG.md +19 -1
- data/Gemfile +1 -1
- data/README.md +65 -21
- data/benchmark.rb +62 -95
- data/bin/rake +27 -0
- data/ext/ob64/extconf.rb +0 -42
- data/ext/ob64/ob64.h +3 -1
- data/ext/ob64/ob64_ext.c +80 -23
- data/ext/ob64/ob64_pack.c +289 -0
- data/ext/ob64/pack_base64.c +5209 -0
- data/ext/ob64/pack_base64.h +36 -0
- data/ext/ob64/pack_generic.h +151 -0
- data/ext/ob64/pack_simd.h +22 -0
- data/ext/ob64/pack_uuencode.c +1741 -0
- data/ext/ob64/pack_uuencode.h +18 -0
- data/lib/ob64/core_ext.rb +2 -25
- data/lib/ob64/version.rb +1 -1
- data/lib/ob64.rb +218 -70
- data/ob64.gemspec +3 -5
- metadata +18 -73
- data/.gitmodules +0 -3
- data/ext/ob64/libbase64.h +0 -1
- data/vendor/libbase64/.gitignore +0 -12
- data/vendor/libbase64/.travis.yml +0 -71
- data/vendor/libbase64/CMakeLists.txt +0 -264
- data/vendor/libbase64/LICENSE +0 -28
- data/vendor/libbase64/Makefile +0 -93
- data/vendor/libbase64/README.md +0 -474
- data/vendor/libbase64/base64-benchmarks.png +0 -0
- data/vendor/libbase64/bin/base64.c +0 -132
- data/vendor/libbase64/cmake/Modules/TargetArch.cmake +0 -29
- data/vendor/libbase64/cmake/Modules/TargetSIMDInstructionSet.cmake +0 -34
- data/vendor/libbase64/cmake/base64-config.cmake.in +0 -5
- data/vendor/libbase64/cmake/config.h.in +0 -25
- data/vendor/libbase64/cmake/test-arch.c +0 -35
- data/vendor/libbase64/include/libbase64.h +0 -145
- data/vendor/libbase64/lib/arch/avx/codec.c +0 -42
- data/vendor/libbase64/lib/arch/avx2/codec.c +0 -42
- data/vendor/libbase64/lib/arch/avx2/dec_loop.c +0 -110
- data/vendor/libbase64/lib/arch/avx2/dec_reshuffle.c +0 -34
- data/vendor/libbase64/lib/arch/avx2/enc_loop.c +0 -89
- data/vendor/libbase64/lib/arch/avx2/enc_reshuffle.c +0 -83
- data/vendor/libbase64/lib/arch/avx2/enc_translate.c +0 -30
- data/vendor/libbase64/lib/arch/generic/32/dec_loop.c +0 -86
- data/vendor/libbase64/lib/arch/generic/32/enc_loop.c +0 -73
- data/vendor/libbase64/lib/arch/generic/64/enc_loop.c +0 -77
- data/vendor/libbase64/lib/arch/generic/codec.c +0 -39
- data/vendor/libbase64/lib/arch/generic/dec_head.c +0 -37
- data/vendor/libbase64/lib/arch/generic/dec_tail.c +0 -91
- data/vendor/libbase64/lib/arch/generic/enc_head.c +0 -24
- data/vendor/libbase64/lib/arch/generic/enc_tail.c +0 -34
- data/vendor/libbase64/lib/arch/neon32/codec.c +0 -72
- data/vendor/libbase64/lib/arch/neon32/dec_loop.c +0 -106
- data/vendor/libbase64/lib/arch/neon32/enc_loop.c +0 -58
- data/vendor/libbase64/lib/arch/neon32/enc_reshuffle.c +0 -54
- data/vendor/libbase64/lib/arch/neon32/enc_translate.c +0 -57
- data/vendor/libbase64/lib/arch/neon64/codec.c +0 -70
- data/vendor/libbase64/lib/arch/neon64/dec_loop.c +0 -129
- data/vendor/libbase64/lib/arch/neon64/enc_loop.c +0 -66
- data/vendor/libbase64/lib/arch/neon64/enc_reshuffle.c +0 -54
- data/vendor/libbase64/lib/arch/sse41/codec.c +0 -42
- data/vendor/libbase64/lib/arch/sse42/codec.c +0 -42
- data/vendor/libbase64/lib/arch/ssse3/codec.c +0 -42
- data/vendor/libbase64/lib/arch/ssse3/dec_loop.c +0 -173
- data/vendor/libbase64/lib/arch/ssse3/dec_reshuffle.c +0 -33
- data/vendor/libbase64/lib/arch/ssse3/enc_loop.c +0 -67
- data/vendor/libbase64/lib/arch/ssse3/enc_reshuffle.c +0 -48
- data/vendor/libbase64/lib/arch/ssse3/enc_translate.c +0 -33
- data/vendor/libbase64/lib/codec_choose.c +0 -281
- data/vendor/libbase64/lib/codecs.h +0 -65
- data/vendor/libbase64/lib/env.h +0 -67
- data/vendor/libbase64/lib/exports.txt +0 -7
- data/vendor/libbase64/lib/lib.c +0 -164
- data/vendor/libbase64/lib/lib_openmp.c +0 -149
- data/vendor/libbase64/lib/tables/.gitignore +0 -1
- data/vendor/libbase64/lib/tables/Makefile +0 -17
- data/vendor/libbase64/lib/tables/table_dec_32bit.h +0 -393
- data/vendor/libbase64/lib/tables/table_enc_12bit.h +0 -1031
- data/vendor/libbase64/lib/tables/table_enc_12bit.py +0 -45
- data/vendor/libbase64/lib/tables/table_generator.c +0 -184
- data/vendor/libbase64/lib/tables/tables.c +0 -40
- data/vendor/libbase64/lib/tables/tables.h +0 -23
data/ext/ob64/ob64_ext.c
CHANGED
|
@@ -2,55 +2,82 @@
|
|
|
2
2
|
#include "ob64.h"
|
|
3
3
|
|
|
4
4
|
VALUE mOb64 = Qnil;
|
|
5
|
-
VALUE
|
|
5
|
+
VALUE mOb64_Ext = Qnil;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/* Indexed by enum pack_codec */
|
|
8
|
+
static const char *const codec_names[PACK_CODECS] = { "scalar", "avx2", "avx512", "neon64" };
|
|
9
|
+
static ID codec_ids[PACK_CODECS];
|
|
10
|
+
|
|
11
|
+
VALUE Ob64_encode_string(VALUE self, VALUE bin, VALUE urlsafe, VALUE padding);
|
|
12
|
+
VALUE Ob64_decode_string(VALUE self, VALUE string, VALUE urlsafe, VALUE loose);
|
|
9
13
|
VALUE Ob64_encoded_length_of(VALUE self, VALUE bin, VALUE withPadding);
|
|
10
14
|
VALUE Ob64_decoded_length_of(VALUE self, VALUE string);
|
|
15
|
+
VALUE Ob64_codec(VALUE self);
|
|
16
|
+
VALUE Ob64_set_codec(VALUE self, VALUE codec);
|
|
17
|
+
VALUE Ob64_codecs(VALUE self);
|
|
11
18
|
|
|
12
|
-
size_t __encoded_length_of(
|
|
19
|
+
size_t __encoded_length_of(size_t bin_len, bool withPadding);
|
|
13
20
|
size_t __decoded_length_of(VALUE string);
|
|
14
21
|
|
|
15
22
|
void
|
|
16
23
|
Init_ob64_ext(void)
|
|
17
24
|
{
|
|
25
|
+
rb_ext_ractor_safe(true);
|
|
26
|
+
pack_base64_init();
|
|
27
|
+
for (int i = 0; i < PACK_CODECS; i++) {
|
|
28
|
+
codec_ids[i] = rb_intern(codec_names[i]);
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
mOb64 = rb_define_module("Ob64");
|
|
19
|
-
|
|
32
|
+
mOb64_Ext = rb_define_module_under(mOb64, "Ext");
|
|
20
33
|
|
|
21
|
-
rb_define_private_method(
|
|
22
|
-
rb_define_private_method(
|
|
23
|
-
rb_define_private_method(
|
|
24
|
-
rb_define_private_method(
|
|
34
|
+
rb_define_private_method(mOb64_Ext, "__encode", Ob64_encode_string, 3);
|
|
35
|
+
rb_define_private_method(mOb64_Ext, "__decode", Ob64_decode_string, 3);
|
|
36
|
+
rb_define_private_method(mOb64_Ext, "__encoded_length_of", Ob64_encoded_length_of, 2);
|
|
37
|
+
rb_define_private_method(mOb64_Ext, "__decoded_length_of", Ob64_decoded_length_of, 1);
|
|
38
|
+
rb_define_private_method(mOb64_Ext, "__codec", Ob64_codec, 0);
|
|
39
|
+
rb_define_private_method(mOb64_Ext, "__set_codec", Ob64_set_codec, 1);
|
|
40
|
+
rb_define_private_method(mOb64_Ext, "__codecs", Ob64_codecs, 0);
|
|
41
|
+
|
|
42
|
+
Init_ob64_pack(mOb64_Ext);
|
|
25
43
|
}
|
|
26
44
|
|
|
27
|
-
VALUE Ob64_encode_string(VALUE self, VALUE bin)
|
|
45
|
+
VALUE Ob64_encode_string(VALUE self, VALUE bin, VALUE urlsafe, VALUE padding)
|
|
28
46
|
{
|
|
29
|
-
|
|
30
|
-
size_t
|
|
47
|
+
StringValue(bin);
|
|
48
|
+
size_t bin_len = RSTRING_LEN(bin);
|
|
49
|
+
VALUE result = rb_str_buf_new(__encoded_length_of(bin_len, true));
|
|
50
|
+
int flags = RTEST(urlsafe) ? PACK_BASE64_URL_SAFE : 0;
|
|
31
51
|
|
|
32
|
-
|
|
33
|
-
rb_str_set_len(result,
|
|
52
|
+
pack_base64_encode(RSTRING_PTR(bin), bin_len, RSTRING_PTR(result), flags);
|
|
53
|
+
rb_str_set_len(result, __encoded_length_of(bin_len, RTEST(padding)));
|
|
54
|
+
RB_GC_GUARD(bin);
|
|
34
55
|
|
|
35
56
|
return result;
|
|
36
57
|
}
|
|
37
58
|
|
|
38
|
-
VALUE Ob64_decode_string(VALUE self, VALUE string)
|
|
59
|
+
VALUE Ob64_decode_string(VALUE self, VALUE string, VALUE urlsafe, VALUE loose)
|
|
39
60
|
{
|
|
40
|
-
|
|
61
|
+
StringValue(string);
|
|
62
|
+
size_t string_len = RSTRING_LEN(string);
|
|
63
|
+
/* Room for unpadded input */
|
|
64
|
+
VALUE result = rb_str_buf_new((string_len + 3) / 4 * 3);
|
|
65
|
+
int flags = (RTEST(urlsafe) ? PACK_BASE64_URL_SAFE : 0) | (RTEST(loose) ? PACK_BASE64_LOOSE : 0);
|
|
41
66
|
size_t nout;
|
|
42
67
|
|
|
43
|
-
if (!
|
|
68
|
+
if (!pack_base64_decode(RSTRING_PTR(string), string_len, RSTRING_PTR(result), &nout, flags)) {
|
|
44
69
|
rb_raise(rb_eArgError, "invalid base64");
|
|
45
70
|
}
|
|
46
71
|
rb_str_set_len(result, nout);
|
|
72
|
+
RB_GC_GUARD(string);
|
|
47
73
|
|
|
48
74
|
return result;
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
VALUE Ob64_encoded_length_of(VALUE self, VALUE bin, VALUE withPadding)
|
|
52
78
|
{
|
|
53
|
-
|
|
79
|
+
StringValue(bin);
|
|
80
|
+
return ULONG2NUM((unsigned long)__encoded_length_of(RSTRING_LEN(bin), RTEST(withPadding)));
|
|
54
81
|
}
|
|
55
82
|
|
|
56
83
|
VALUE Ob64_decoded_length_of(VALUE self, VALUE string)
|
|
@@ -58,12 +85,12 @@ VALUE Ob64_decoded_length_of(VALUE self, VALUE string)
|
|
|
58
85
|
return ULONG2NUM((unsigned long)__decoded_length_of(string));
|
|
59
86
|
}
|
|
60
87
|
|
|
61
|
-
size_t __encoded_length_of(
|
|
88
|
+
size_t __encoded_length_of(size_t bin_len, bool withPadding)
|
|
62
89
|
{
|
|
63
90
|
if (withPadding) {
|
|
64
|
-
return (
|
|
91
|
+
return (bin_len + 2) / 3 * 4;
|
|
65
92
|
} else {
|
|
66
|
-
return ((
|
|
93
|
+
return ((bin_len * 8) + 5) / 6;
|
|
67
94
|
}
|
|
68
95
|
}
|
|
69
96
|
|
|
@@ -72,8 +99,8 @@ size_t __decoded_length_of(VALUE string)
|
|
|
72
99
|
char *string_ptr = StringValuePtr(string);
|
|
73
100
|
size_t string_len = RSTRING_LEN(string);
|
|
74
101
|
size_t padding = 0;
|
|
75
|
-
if (string_ptr[string_len - 1] == '=') padding++;
|
|
76
|
-
if (string_ptr[string_len - 2] == '=') padding++;
|
|
102
|
+
if (string_len >= 1 && string_ptr[string_len - 1] == '=') padding++;
|
|
103
|
+
if (string_len >= 2 && string_ptr[string_len - 2] == '=') padding++;
|
|
77
104
|
|
|
78
105
|
if ((string_len - padding) % 4 == 1) {
|
|
79
106
|
rb_raise(rb_eArgError, "invalid base64");
|
|
@@ -81,3 +108,33 @@ size_t __decoded_length_of(VALUE string)
|
|
|
81
108
|
|
|
82
109
|
return (3 * (string_len - padding)) / 4;
|
|
83
110
|
}
|
|
111
|
+
|
|
112
|
+
VALUE Ob64_codec(VALUE self)
|
|
113
|
+
{
|
|
114
|
+
return ID2SYM(codec_ids[pack_codec_current()]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
VALUE Ob64_set_codec(VALUE self, VALUE codec)
|
|
118
|
+
{
|
|
119
|
+
for (int i = 0; i < PACK_CODECS; i++) {
|
|
120
|
+
if (codec == ID2SYM(codec_ids[i])) {
|
|
121
|
+
if (!pack_codec_runnable(i)) {
|
|
122
|
+
rb_raise(rb_eArgError, "%s is not supported by this CPU", codec_names[i]);
|
|
123
|
+
}
|
|
124
|
+
pack_codec_set(i);
|
|
125
|
+
return codec;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
rb_raise(rb_eArgError, "unknown codec: %+"PRIsVALUE, codec);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
VALUE Ob64_codecs(VALUE self)
|
|
132
|
+
{
|
|
133
|
+
VALUE codecs = rb_ary_new();
|
|
134
|
+
|
|
135
|
+
for (int i = 0; i < PACK_CODECS; i++) {
|
|
136
|
+
if (pack_codec_runnable(i)) rb_ary_push(codecs, ID2SYM(codec_ids[i]));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return codecs;
|
|
140
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#include <limits.h>
|
|
2
|
+
#include "ob64.h"
|
|
3
|
+
#include "pack_uuencode.h"
|
|
4
|
+
#include "ruby/encoding.h"
|
|
5
|
+
|
|
6
|
+
#define COUNT_STAR (-1)
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Returns 'm' or 'u' if fmt is a String matching \A[mu](\d{1,9}|\*)?\z in an
|
|
10
|
+
* ASCII-compatible encoding, 0 otherwise. *count: 1 if absent, COUNT_STAR for '*'.
|
|
11
|
+
*/
|
|
12
|
+
static int
|
|
13
|
+
parse_format(VALUE fmt, long *count)
|
|
14
|
+
{
|
|
15
|
+
if (!RB_TYPE_P(fmt, T_STRING) || !rb_enc_str_asciicompat_p(fmt)) return 0;
|
|
16
|
+
|
|
17
|
+
const char *p = RSTRING_PTR(fmt);
|
|
18
|
+
long len = RSTRING_LEN(fmt);
|
|
19
|
+
|
|
20
|
+
if (len < 1 || len > 10 || (p[0] != 'm' && p[0] != 'u')) return 0;
|
|
21
|
+
if (len == 1) {
|
|
22
|
+
*count = 1;
|
|
23
|
+
}
|
|
24
|
+
else if (len == 2 && p[1] == '*') {
|
|
25
|
+
*count = COUNT_STAR;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
*count = 0;
|
|
29
|
+
for (long i = 1; i < len; i++) {
|
|
30
|
+
if (p[i] < '0' || p[i] > '9') return 0;
|
|
31
|
+
*count = *count * 10 + (p[i] - '0');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return p[0];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Sets res's length to n and makes it a US-ASCII String, as native pack does */
|
|
39
|
+
static VALUE
|
|
40
|
+
ascii_result(VALUE res, size_t n)
|
|
41
|
+
{
|
|
42
|
+
rb_str_set_len(res, (long)n);
|
|
43
|
+
ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
|
|
44
|
+
|
|
45
|
+
return res;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static VALUE
|
|
49
|
+
uuencode(VALUE from, long line_len)
|
|
50
|
+
{
|
|
51
|
+
long plen = RSTRING_LEN(from);
|
|
52
|
+
/* A length byte and a newline per line */
|
|
53
|
+
long lines = (plen + line_len - 1) / line_len;
|
|
54
|
+
VALUE res = rb_str_buf_new((plen + 2) / 3 * 4 + lines * 2);
|
|
55
|
+
const char *src = RSTRING_PTR(from);
|
|
56
|
+
char *out = RSTRING_PTR(res);
|
|
57
|
+
size_t n = pack_uuencode_lines(src, (size_t)plen, out, (size_t)line_len);
|
|
58
|
+
RB_GC_GUARD(from);
|
|
59
|
+
|
|
60
|
+
return ascii_result(res, n);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* line_len: bytes per line, a multiple of 3; crlf: lines end in "\r\n" */
|
|
64
|
+
static VALUE
|
|
65
|
+
base64_encode_rfc2045(VALUE from, long line_len, int crlf)
|
|
66
|
+
{
|
|
67
|
+
long plen = RSTRING_LEN(from);
|
|
68
|
+
/* A line break per line */
|
|
69
|
+
long lines = (plen + line_len - 1) / line_len;
|
|
70
|
+
VALUE res = rb_str_buf_new((plen + 2) / 3 * 4 + lines * (crlf ? 2 : 1));
|
|
71
|
+
size_t n = pack_base64_encode_rfc2045(RSTRING_PTR(from), (size_t)plen, RSTRING_PTR(res), (int)line_len, crlf);
|
|
72
|
+
RB_GC_GUARD(from);
|
|
73
|
+
|
|
74
|
+
return ascii_result(res, n);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static VALUE
|
|
78
|
+
Ob64_pack(int argc, VALUE *argv, VALUE ary)
|
|
79
|
+
{
|
|
80
|
+
long count, plen;
|
|
81
|
+
int type;
|
|
82
|
+
VALUE from;
|
|
83
|
+
|
|
84
|
+
if (argc != 1 || rb_keyword_given_p() || rb_block_given_p() ||
|
|
85
|
+
!(type = parse_format(argv[0], &count)) ||
|
|
86
|
+
RARRAY_LEN(ary) < 1 || !RB_TYPE_P(from = RARRAY_AREF(ary, 0), T_STRING) ||
|
|
87
|
+
/* Bound plen so the output size below cannot overflow */
|
|
88
|
+
(plen = RSTRING_LEN(from)) > LONG_MAX / 4) {
|
|
89
|
+
return rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (type == 'm' && count == 0) {
|
|
93
|
+
VALUE res = rb_str_buf_new((plen + 2) / 3 * 4);
|
|
94
|
+
size_t n = pack_base64_encode(RSTRING_PTR(from), (size_t)plen, RSTRING_PTR(res), 0);
|
|
95
|
+
RB_GC_GUARD(from);
|
|
96
|
+
|
|
97
|
+
return ascii_result(res, n);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
long len = count;
|
|
101
|
+
|
|
102
|
+
/* '*' gets 45, as natively */
|
|
103
|
+
if (len <= 2) len = 45;
|
|
104
|
+
else if (len > 63 && type == 'u') len = 63;
|
|
105
|
+
else len = len / 3 * 3;
|
|
106
|
+
|
|
107
|
+
return type == 'u' ? uuencode(from, len) : base64_encode_rfc2045(from, len, 0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static VALUE
|
|
111
|
+
uudecode(VALUE str)
|
|
112
|
+
{
|
|
113
|
+
long len = RSTRING_LEN(str);
|
|
114
|
+
/* +3: the decoder stores up to 3 bytes past a line's decoded length */
|
|
115
|
+
VALUE buf = rb_str_buf_new((long)pack_uudecode_cap((size_t)len) + 3);
|
|
116
|
+
size_t total = pack_uudecode_lines(RSTRING_PTR(str), (size_t)len, RSTRING_PTR(buf));
|
|
117
|
+
RB_GC_GUARD(str);
|
|
118
|
+
|
|
119
|
+
rb_str_set_len(buf, (long)total);
|
|
120
|
+
|
|
121
|
+
return buf;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static VALUE
|
|
125
|
+
base64_decode(VALUE str, long count)
|
|
126
|
+
{
|
|
127
|
+
long len = RSTRING_LEN(str);
|
|
128
|
+
/* Room for unpadded input and decoder overruns */
|
|
129
|
+
VALUE buf = rb_str_buf_new(len / 4 * 3 + 3);
|
|
130
|
+
size_t n;
|
|
131
|
+
|
|
132
|
+
if (count == 0) {
|
|
133
|
+
if (!pack_base64_decode(RSTRING_PTR(str), (size_t)len, RSTRING_PTR(buf), &n, 0)) {
|
|
134
|
+
rb_raise(rb_eArgError, "invalid base64");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
n = pack_base64_decode_rfc2045(RSTRING_PTR(str), (size_t)len, RSTRING_PTR(buf));
|
|
139
|
+
}
|
|
140
|
+
RB_GC_GUARD(str);
|
|
141
|
+
|
|
142
|
+
rb_str_set_len(buf, (long)n);
|
|
143
|
+
|
|
144
|
+
return buf;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Returns the decoded String, or Qundef when native Ruby must handle the call */
|
|
148
|
+
static VALUE
|
|
149
|
+
unpack_fast(int argc, VALUE *argv, VALUE str)
|
|
150
|
+
{
|
|
151
|
+
long count;
|
|
152
|
+
int type;
|
|
153
|
+
|
|
154
|
+
if (argc != 1 || rb_keyword_given_p() || rb_block_given_p() ||
|
|
155
|
+
!(type = parse_format(argv[0], &count))) {
|
|
156
|
+
return Qundef;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return type == 'm' ? base64_decode(str, count) : uudecode(str);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static VALUE
|
|
163
|
+
Ob64_unpack(int argc, VALUE *argv, VALUE str)
|
|
164
|
+
{
|
|
165
|
+
VALUE res = unpack_fast(argc, argv, str);
|
|
166
|
+
|
|
167
|
+
if (RB_UNDEF_P(res)) return rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
168
|
+
|
|
169
|
+
return rb_ary_new_from_values(1, &res);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static VALUE
|
|
173
|
+
Ob64_unpack1(int argc, VALUE *argv, VALUE str)
|
|
174
|
+
{
|
|
175
|
+
VALUE res = unpack_fast(argc, argv, str);
|
|
176
|
+
|
|
177
|
+
if (RB_UNDEF_P(res)) return rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
178
|
+
|
|
179
|
+
return res;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Bytes per line for a line_length in chars; validates both arguments */
|
|
183
|
+
static long
|
|
184
|
+
rfc2045_line_bytes(VALUE bin, VALUE line_length)
|
|
185
|
+
{
|
|
186
|
+
long chars = NUM2LONG(line_length);
|
|
187
|
+
|
|
188
|
+
StringValue(bin);
|
|
189
|
+
if (chars < 4 || chars % 4 || chars / 4 * 3 > INT_MAX) {
|
|
190
|
+
rb_raise(rb_eArgError, "line length must be a positive multiple of 4: %ld", chars);
|
|
191
|
+
}
|
|
192
|
+
/* Bound the output size */
|
|
193
|
+
if (RSTRING_LEN(bin) > LONG_MAX / 4) rb_raise(rb_eArgError, "argument too big");
|
|
194
|
+
|
|
195
|
+
return chars / 4 * 3;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static VALUE
|
|
199
|
+
Ob64_encode_rfc_2045(VALUE self, VALUE bin, VALUE line_length, VALUE crlf)
|
|
200
|
+
{
|
|
201
|
+
long line_bytes = rfc2045_line_bytes(bin, line_length);
|
|
202
|
+
|
|
203
|
+
return base64_encode_rfc2045(bin, line_bytes, RTEST(crlf));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static VALUE
|
|
207
|
+
Ob64_decode_rfc_2045(VALUE self, VALUE string)
|
|
208
|
+
{
|
|
209
|
+
StringValue(string);
|
|
210
|
+
|
|
211
|
+
return base64_decode(string, 1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
static VALUE
|
|
215
|
+
Ob64_encoded_length_of_rfc_2045(VALUE self, VALUE bin, VALUE line_length, VALUE crlf)
|
|
216
|
+
{
|
|
217
|
+
long line_bytes = rfc2045_line_bytes(bin, line_length);
|
|
218
|
+
long plen = RSTRING_LEN(bin);
|
|
219
|
+
|
|
220
|
+
/* 4 chars per 3 bytes, a line break per line */
|
|
221
|
+
return LONG2NUM((plen + 2) / 3 * 4 + (plen + line_bytes - 1) / line_bytes * (RTEST(crlf) ? 2 : 1));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
static VALUE
|
|
225
|
+
Ob64_decoded_length_of_rfc_2045(VALUE self, VALUE string)
|
|
226
|
+
{
|
|
227
|
+
StringValue(string);
|
|
228
|
+
size_t n = pack_base64_decoded_length_rfc2045(RSTRING_PTR(string), (size_t)RSTRING_LEN(string));
|
|
229
|
+
RB_GC_GUARD(string);
|
|
230
|
+
|
|
231
|
+
return SIZET2NUM(n);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static VALUE
|
|
235
|
+
Ob64_uuencode(VALUE self, VALUE bin)
|
|
236
|
+
{
|
|
237
|
+
StringValue(bin);
|
|
238
|
+
|
|
239
|
+
return uuencode(bin, 45);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static VALUE
|
|
243
|
+
Ob64_uudecode(VALUE self, VALUE string)
|
|
244
|
+
{
|
|
245
|
+
StringValue(string);
|
|
246
|
+
|
|
247
|
+
return uudecode(string);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static VALUE
|
|
251
|
+
Ob64_uuencoded_length_of(VALUE self, VALUE bin)
|
|
252
|
+
{
|
|
253
|
+
StringValue(bin);
|
|
254
|
+
long plen = RSTRING_LEN(bin);
|
|
255
|
+
/* Per line: a length byte, 4 chars per 3 bytes and a newline */
|
|
256
|
+
long full = plen / 45, rest = plen % 45;
|
|
257
|
+
|
|
258
|
+
return LONG2NUM(full * 62 + (rest ? (rest + 2) / 3 * 4 + 2 : 0));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
static VALUE
|
|
262
|
+
Ob64_uudecoded_length_of(VALUE self, VALUE string)
|
|
263
|
+
{
|
|
264
|
+
StringValue(string);
|
|
265
|
+
size_t total = pack_uudecode_lines(RSTRING_PTR(string), (size_t)RSTRING_LEN(string), NULL);
|
|
266
|
+
RB_GC_GUARD(string);
|
|
267
|
+
|
|
268
|
+
return LONG2NUM((long)total);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
void
|
|
272
|
+
Init_ob64_pack(VALUE mExt)
|
|
273
|
+
{
|
|
274
|
+
VALUE mPack = rb_define_module_under(mExt, "Pack");
|
|
275
|
+
VALUE mUnpack = rb_define_module_under(mExt, "Unpack");
|
|
276
|
+
|
|
277
|
+
rb_define_method(mPack, "pack", Ob64_pack, -1);
|
|
278
|
+
rb_define_method(mUnpack, "unpack", Ob64_unpack, -1);
|
|
279
|
+
rb_define_method(mUnpack, "unpack1", Ob64_unpack1, -1);
|
|
280
|
+
|
|
281
|
+
rb_define_private_method(mExt, "__encode_rfc_2045", Ob64_encode_rfc_2045, 3);
|
|
282
|
+
rb_define_private_method(mExt, "__decode_rfc_2045", Ob64_decode_rfc_2045, 1);
|
|
283
|
+
rb_define_private_method(mExt, "__encoded_length_of_rfc_2045", Ob64_encoded_length_of_rfc_2045, 3);
|
|
284
|
+
rb_define_private_method(mExt, "__decoded_length_of_rfc_2045", Ob64_decoded_length_of_rfc_2045, 1);
|
|
285
|
+
rb_define_private_method(mExt, "__uuencode", Ob64_uuencode, 1);
|
|
286
|
+
rb_define_private_method(mExt, "__uudecode", Ob64_uudecode, 1);
|
|
287
|
+
rb_define_private_method(mExt, "__uuencoded_length_of", Ob64_uuencoded_length_of, 1);
|
|
288
|
+
rb_define_private_method(mExt, "__uudecoded_length_of", Ob64_uudecoded_length_of, 1);
|
|
289
|
+
}
|