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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +9 -0
  3. data/.github/workflows/main.yml +78 -16
  4. data/.standard.yml +1 -2
  5. data/CHANGELOG.md +19 -1
  6. data/Gemfile +1 -1
  7. data/README.md +65 -21
  8. data/benchmark.rb +62 -95
  9. data/bin/rake +27 -0
  10. data/ext/ob64/extconf.rb +0 -42
  11. data/ext/ob64/ob64.h +3 -1
  12. data/ext/ob64/ob64_ext.c +80 -23
  13. data/ext/ob64/ob64_pack.c +289 -0
  14. data/ext/ob64/pack_base64.c +5209 -0
  15. data/ext/ob64/pack_base64.h +36 -0
  16. data/ext/ob64/pack_generic.h +151 -0
  17. data/ext/ob64/pack_simd.h +22 -0
  18. data/ext/ob64/pack_uuencode.c +1741 -0
  19. data/ext/ob64/pack_uuencode.h +18 -0
  20. data/lib/ob64/core_ext.rb +2 -25
  21. data/lib/ob64/version.rb +1 -1
  22. data/lib/ob64.rb +218 -70
  23. data/ob64.gemspec +3 -5
  24. metadata +18 -73
  25. data/.gitmodules +0 -3
  26. data/ext/ob64/libbase64.h +0 -1
  27. data/vendor/libbase64/.gitignore +0 -12
  28. data/vendor/libbase64/.travis.yml +0 -71
  29. data/vendor/libbase64/CMakeLists.txt +0 -264
  30. data/vendor/libbase64/LICENSE +0 -28
  31. data/vendor/libbase64/Makefile +0 -93
  32. data/vendor/libbase64/README.md +0 -474
  33. data/vendor/libbase64/base64-benchmarks.png +0 -0
  34. data/vendor/libbase64/bin/base64.c +0 -132
  35. data/vendor/libbase64/cmake/Modules/TargetArch.cmake +0 -29
  36. data/vendor/libbase64/cmake/Modules/TargetSIMDInstructionSet.cmake +0 -34
  37. data/vendor/libbase64/cmake/base64-config.cmake.in +0 -5
  38. data/vendor/libbase64/cmake/config.h.in +0 -25
  39. data/vendor/libbase64/cmake/test-arch.c +0 -35
  40. data/vendor/libbase64/include/libbase64.h +0 -145
  41. data/vendor/libbase64/lib/arch/avx/codec.c +0 -42
  42. data/vendor/libbase64/lib/arch/avx2/codec.c +0 -42
  43. data/vendor/libbase64/lib/arch/avx2/dec_loop.c +0 -110
  44. data/vendor/libbase64/lib/arch/avx2/dec_reshuffle.c +0 -34
  45. data/vendor/libbase64/lib/arch/avx2/enc_loop.c +0 -89
  46. data/vendor/libbase64/lib/arch/avx2/enc_reshuffle.c +0 -83
  47. data/vendor/libbase64/lib/arch/avx2/enc_translate.c +0 -30
  48. data/vendor/libbase64/lib/arch/generic/32/dec_loop.c +0 -86
  49. data/vendor/libbase64/lib/arch/generic/32/enc_loop.c +0 -73
  50. data/vendor/libbase64/lib/arch/generic/64/enc_loop.c +0 -77
  51. data/vendor/libbase64/lib/arch/generic/codec.c +0 -39
  52. data/vendor/libbase64/lib/arch/generic/dec_head.c +0 -37
  53. data/vendor/libbase64/lib/arch/generic/dec_tail.c +0 -91
  54. data/vendor/libbase64/lib/arch/generic/enc_head.c +0 -24
  55. data/vendor/libbase64/lib/arch/generic/enc_tail.c +0 -34
  56. data/vendor/libbase64/lib/arch/neon32/codec.c +0 -72
  57. data/vendor/libbase64/lib/arch/neon32/dec_loop.c +0 -106
  58. data/vendor/libbase64/lib/arch/neon32/enc_loop.c +0 -58
  59. data/vendor/libbase64/lib/arch/neon32/enc_reshuffle.c +0 -54
  60. data/vendor/libbase64/lib/arch/neon32/enc_translate.c +0 -57
  61. data/vendor/libbase64/lib/arch/neon64/codec.c +0 -70
  62. data/vendor/libbase64/lib/arch/neon64/dec_loop.c +0 -129
  63. data/vendor/libbase64/lib/arch/neon64/enc_loop.c +0 -66
  64. data/vendor/libbase64/lib/arch/neon64/enc_reshuffle.c +0 -54
  65. data/vendor/libbase64/lib/arch/sse41/codec.c +0 -42
  66. data/vendor/libbase64/lib/arch/sse42/codec.c +0 -42
  67. data/vendor/libbase64/lib/arch/ssse3/codec.c +0 -42
  68. data/vendor/libbase64/lib/arch/ssse3/dec_loop.c +0 -173
  69. data/vendor/libbase64/lib/arch/ssse3/dec_reshuffle.c +0 -33
  70. data/vendor/libbase64/lib/arch/ssse3/enc_loop.c +0 -67
  71. data/vendor/libbase64/lib/arch/ssse3/enc_reshuffle.c +0 -48
  72. data/vendor/libbase64/lib/arch/ssse3/enc_translate.c +0 -33
  73. data/vendor/libbase64/lib/codec_choose.c +0 -281
  74. data/vendor/libbase64/lib/codecs.h +0 -65
  75. data/vendor/libbase64/lib/env.h +0 -67
  76. data/vendor/libbase64/lib/exports.txt +0 -7
  77. data/vendor/libbase64/lib/lib.c +0 -164
  78. data/vendor/libbase64/lib/lib_openmp.c +0 -149
  79. data/vendor/libbase64/lib/tables/.gitignore +0 -1
  80. data/vendor/libbase64/lib/tables/Makefile +0 -17
  81. data/vendor/libbase64/lib/tables/table_dec_32bit.h +0 -393
  82. data/vendor/libbase64/lib/tables/table_enc_12bit.h +0 -1031
  83. data/vendor/libbase64/lib/tables/table_enc_12bit.py +0 -45
  84. data/vendor/libbase64/lib/tables/table_generator.c +0 -184
  85. data/vendor/libbase64/lib/tables/tables.c +0 -40
  86. 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 mOb64_LibBase64 = Qnil;
5
+ VALUE mOb64_Ext = Qnil;
6
6
 
7
- VALUE Ob64_encode_string(VALUE self, VALUE bin);
8
- VALUE Ob64_decode_string(VALUE self, VALUE string);
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(VALUE string, bool withPadding);
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
- mOb64_LibBase64 = rb_define_module_under(mOb64, "LibBase64");
32
+ mOb64_Ext = rb_define_module_under(mOb64, "Ext");
20
33
 
21
- rb_define_private_method(mOb64_LibBase64, "__encode", Ob64_encode_string, 1);
22
- rb_define_private_method(mOb64_LibBase64, "__decode", Ob64_decode_string, 1);
23
- rb_define_private_method(mOb64_LibBase64, "__encoded_length_of", Ob64_encoded_length_of, 2);
24
- rb_define_private_method(mOb64_LibBase64, "__decoded_length_of", Ob64_decoded_length_of, 1);
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
- VALUE result = rb_str_buf_new(__encoded_length_of(bin, true));
30
- size_t nout;
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
- base64_encode(StringValuePtr(bin), RSTRING_LEN(bin), StringValuePtr(result), &nout, 0);
33
- rb_str_set_len(result, nout);
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
- VALUE result = rb_str_buf_new(__decoded_length_of(string));
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 (!base64_decode(StringValuePtr(string), RSTRING_LEN(string), StringValuePtr(result), &nout, 0)) {
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
- return ULONG2NUM((unsigned long)__encoded_length_of(bin, withPadding != Qnil && withPadding != Qfalse ? true : false));
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(VALUE bin, bool withPadding)
88
+ size_t __encoded_length_of(size_t bin_len, bool withPadding)
62
89
  {
63
90
  if (withPadding) {
64
- return (RSTRING_LEN(bin) + 2) / 3 * 4;
91
+ return (bin_len + 2) / 3 * 4;
65
92
  } else {
66
- return ((RSTRING_LEN(bin) * 8) + 5) / 6;
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
+ }