ob64 0.6.0 → 0.8.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/scripts/install-sde +21 -0
- data/.github/workflows/main.yml +56 -5
- data/CHANGELOG.md +10 -0
- data/Gemfile +1 -1
- data/README.md +63 -14
- data/Rakefile +11 -0
- data/benchmark.rb +6 -4
- data/bin/rake +27 -0
- data/ext/ob64/ob64_ext.c +3 -0
- data/ext/ob64/ob64_pack.c +81 -20
- data/ext/ob64/pack_base64.c +32 -5
- data/ext/ob64/pack_base64.h +4 -1
- data/lib/ob64/version.rb +1 -1
- data/lib/ob64.rb +151 -82
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93d2eaeaa970d18c2ab92ff9f5bb4621c22135c16faa0f1ef09ac71babe94b95
|
|
4
|
+
data.tar.gz: 7bad60e17ca1b3189634cb2043d58bf1ffa85c701deff0b99886dde4a34082e1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc8c44a79e33c420a13c1d4460a2773ed6d60ff0e4eab47ea5ad0a1db4365d85c4e642e442dfa4a2753ef0153f8bde876dca4867005f28c124f4b7251b06e55d
|
|
7
|
+
data.tar.gz: 61b2550bc2035667a86d6bfd87149fcba0bd3faa33d9e29c6f42f89cef8be2b5e5df6a36a35682e2cb9d193ea1252ee7d37bcb1d787d0c62cfa78d1618218144
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Installs Intel SDE if this CPU can't run the avx512 codec, and exports SDE,
|
|
3
|
+
# a command prefix that emulates a CPU that can.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
missing=
|
|
7
|
+
for flag in avx512vl avx512vbmi avx512_vbmi2; do
|
|
8
|
+
grep -qw "$flag" /proc/cpuinfo || missing=1
|
|
9
|
+
done
|
|
10
|
+
[ -n "$missing" ] || exit 0
|
|
11
|
+
|
|
12
|
+
url=https://downloadmirror.intel.com/924984/sde-external-10.13.1-2026-07-28-lin.tar.xz
|
|
13
|
+
sha256=94e97d623fec54385686e1e7ba65ebc9941748c05ee451423948334892bf2b50
|
|
14
|
+
dir="$RUNNER_TEMP/sde"
|
|
15
|
+
|
|
16
|
+
curl -fsSL -o "$dir.tar.xz" "$url"
|
|
17
|
+
echo "$sha256 $dir.tar.xz" | sha256sum -c --quiet
|
|
18
|
+
mkdir -p "$dir"
|
|
19
|
+
tar xJf "$dir.tar.xz" -C "$dir" --strip-components=1
|
|
20
|
+
# Ice Lake: AVX512 with VBMI and VBMI2
|
|
21
|
+
echo "SDE=$dir/sde64 -icl --" >>"$GITHUB_ENV"
|
data/.github/workflows/main.yml
CHANGED
|
@@ -36,8 +36,15 @@ jobs:
|
|
|
36
36
|
ruby: ["3.3", "3.4", "4.0"]
|
|
37
37
|
os: [ubuntu-24.04, ubuntu-24.04-arm]
|
|
38
38
|
cc: [gcc, clang]
|
|
39
|
+
include:
|
|
40
|
+
# Ruby built with AddressSanitizer, x86-64 only: memory errors in the
|
|
41
|
+
# C code fail the specs. ubsan adds UndefinedBehaviorSanitizer.
|
|
42
|
+
- ruby: asan-release
|
|
43
|
+
os: ubuntu-24.04
|
|
44
|
+
cc: clang
|
|
45
|
+
ubsan: true
|
|
39
46
|
runs-on: ${{ matrix.os }}
|
|
40
|
-
timeout-minutes:
|
|
47
|
+
timeout-minutes: 30
|
|
41
48
|
steps:
|
|
42
49
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
43
50
|
with:
|
|
@@ -55,14 +62,57 @@ jobs:
|
|
|
55
62
|
- name: Compile
|
|
56
63
|
env:
|
|
57
64
|
# Same warnings for both compilers; Ruby's defaults are GCC-only.
|
|
58
|
-
MAKEFLAGS:
|
|
65
|
+
MAKEFLAGS: >-
|
|
66
|
+
CC=${{ matrix.cc }}${{ matrix.ubsan && '\ -fsanitize=undefined\ -fno-sanitize-recover=all' || '' }}
|
|
67
|
+
warnflags=-Wall\ -Wextra\ -Wno-unused-parameter\ -Werror
|
|
59
68
|
run: bundle exec rake compile
|
|
60
69
|
|
|
70
|
+
- name: Install Intel SDE if the CPU lacks AVX512
|
|
71
|
+
if: runner.arch == 'X64'
|
|
72
|
+
run: .github/scripts/install-sde
|
|
73
|
+
|
|
61
74
|
- name: Show codecs
|
|
62
|
-
run: bundle exec ruby -Ilib -rob64 -e 'p Ob64.codecs, Ob64.codec'
|
|
75
|
+
run: $SDE bundle exec ruby -Ilib -rob64 -e 'p Ob64.codecs, Ob64.codec'
|
|
76
|
+
|
|
77
|
+
- name: Test
|
|
78
|
+
env:
|
|
79
|
+
# Fails the codec spec if one is missing
|
|
80
|
+
OB64_CODECS: ${{ runner.arch == 'X64' && 'scalar,avx2,avx512' || 'scalar,neon64' }}
|
|
81
|
+
run: $SDE bundle exec rake spec
|
|
82
|
+
|
|
83
|
+
codecs:
|
|
84
|
+
# The C codecs alone under ASan and UBSan, on exact-size buffers.
|
|
85
|
+
strategy:
|
|
86
|
+
fail-fast: false
|
|
87
|
+
matrix:
|
|
88
|
+
os: [ubuntu-24.04, ubuntu-24.04-arm]
|
|
89
|
+
cc: [gcc, clang]
|
|
90
|
+
runs-on: ${{ matrix.os }}
|
|
91
|
+
# Long under SDE, which emulates AVX512 slowly
|
|
92
|
+
timeout-minutes: 90
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
95
|
+
with:
|
|
96
|
+
persist-credentials: false
|
|
97
|
+
|
|
98
|
+
- uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
|
|
99
|
+
with:
|
|
100
|
+
ruby-version: "3.4"
|
|
101
|
+
bundler-cache: true
|
|
102
|
+
|
|
103
|
+
- name: Install Clang
|
|
104
|
+
if: matrix.cc == 'clang'
|
|
105
|
+
run: command -v clang || { sudo apt-get update && sudo apt-get install -y clang; }
|
|
106
|
+
|
|
107
|
+
- name: Install Intel SDE if the CPU lacks AVX512
|
|
108
|
+
if: runner.arch == 'X64'
|
|
109
|
+
run: .github/scripts/install-sde
|
|
63
110
|
|
|
64
111
|
- name: Test
|
|
65
|
-
|
|
112
|
+
env:
|
|
113
|
+
CC: ${{ matrix.cc }}
|
|
114
|
+
OB64_CODECS: ${{ runner.arch == 'X64' && 'scalar,avx2,avx512' || 'scalar,neon64' }}
|
|
115
|
+
run: bundle exec rake spec:c
|
|
66
116
|
|
|
67
117
|
package:
|
|
68
118
|
runs-on: ubuntu-24.04
|
|
@@ -89,7 +139,8 @@ jobs:
|
|
|
89
139
|
require "ob64"
|
|
90
140
|
require "ob64/core_ext"
|
|
91
141
|
s = Random.bytes(4096)
|
|
92
|
-
abort "
|
|
142
|
+
abort "RFC 4648 round trip failed" unless Ob64::Base64::RFC4648.decode(Ob64::Base64::RFC4648.encode(s)) == s
|
|
143
|
+
abort "RFC 2045 round trip failed" unless Ob64::Base64::RFC2045.decode(Ob64::Base64::RFC2045.encode(s)) == s
|
|
93
144
|
abort "UU round trip failed" unless Ob64::UU.decode(Ob64::UU.encode(s)) == s
|
|
94
145
|
abort "pack round trip failed" unless [s].pack("m0").unpack1("m0") == s
|
|
95
146
|
'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [0.8.0] - 2026-10-27
|
|
2
|
+
|
|
3
|
+
- Fix undefined behavior in AVX512 detection.
|
|
4
|
+
- `Ob64::Base64::RFC4648.encode` and `encode_urlsafe` return US-ASCII Strings, like `pack("m0")` (were BINARY).
|
|
5
|
+
|
|
6
|
+
## [0.7.0] - 2026-10-26
|
|
7
|
+
|
|
8
|
+
- **Breaking:** Split `Ob64::Base64` into one module per RFC. `Ob64::Base64::RFC4648` has `encode`, `decode`, `encode_urlsafe`, `decode_urlsafe`, `encoded_length_of` and `decoded_length_of` (were `Ob64::Base64.encode`, `decode`, `urlsafe_encode`, `urlsafe_decode`, `encoded_length_of` and `decoded_length_of`).
|
|
9
|
+
- Add `Ob64::Base64::RFC2045` with `encode` (optionally with CRLF line breaks), `decode`, `encoded_length_of` and `decoded_length_of`, like `pack("m")` and `unpack1("m")`.
|
|
10
|
+
|
|
1
11
|
## [0.6.0] - 2026-10-26
|
|
2
12
|
|
|
3
13
|
- **Breaking:** `ob64/core_ext` patches `Array#pack`, `String#unpack` and `String#unpack1` instead of `Base64`.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -2,13 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
[](http://rubygems.org/gems/ob64)
|
|
4
4
|
[](https://github.com/jcmfernandes/ob64/actions/workflows/main.yml)
|
|
5
|
-
[](http://www.rubydoc.info/gems/ob64/0.
|
|
5
|
+
[](http://www.rubydoc.info/gems/ob64/0.8.0)
|
|
6
6
|
|
|
7
7
|
A *fast* Base64 and uuencode encoder and decoder as a Ruby gem.
|
|
8
8
|
|
|
9
|
-
How fast?
|
|
9
|
+
How fast? Times faster than Ruby's own `pack` / `unpack1`, per call, measured with `benchmark.rb` on an AMD Ryzen 9 9900X (AVX512) with Ruby 3.4:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```mermaid
|
|
12
|
+
%%{init: {"themeVariables": {"xyChart": {"plotColorPalette": "#2a78d6"}}}}%%
|
|
13
|
+
xychart-beta
|
|
14
|
+
title "1 kB input: times faster than Ruby"
|
|
15
|
+
x-axis ["m0 enc", "m0 dec", "url enc", "url dec", "m enc", "m dec", "u enc", "u dec"]
|
|
16
|
+
y-axis "times faster" 0 --> 45
|
|
17
|
+
bar [3.1, 4.8, 11.1, 12.6, 5.2, 3.9, 5.9, 4.2]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
%%{init: {"themeVariables": {"xyChart": {"plotColorPalette": "#2a78d6"}}}}%%
|
|
22
|
+
xychart-beta
|
|
23
|
+
title "4 kB input: times faster than Ruby"
|
|
24
|
+
x-axis ["m0 enc", "m0 dec", "url enc", "url dec", "m enc", "m dec", "u enc", "u dec"]
|
|
25
|
+
y-axis "times faster" 0 --> 45
|
|
26
|
+
bar [6.3, 8.4, 17.6, 22.9, 8.4, 6.1, 7.5, 5.2]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
%%{init: {"themeVariables": {"xyChart": {"plotColorPalette": "#2a78d6"}}}}%%
|
|
31
|
+
xychart-beta
|
|
32
|
+
title "16 kB input: times faster than Ruby"
|
|
33
|
+
x-axis ["m0 enc", "m0 dec", "url enc", "url dec", "m enc", "m dec", "u enc", "u dec"]
|
|
34
|
+
y-axis "times faster" 0 --> 45
|
|
35
|
+
bar [11.1, 12.6, 24.3, 32.8, 6.0, 7.0, 7.2, 5.3]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```mermaid
|
|
39
|
+
%%{init: {"themeVariables": {"xyChart": {"plotColorPalette": "#2a78d6"}}}}%%
|
|
40
|
+
xychart-beta
|
|
41
|
+
title "512 kB input: times faster than Ruby"
|
|
42
|
+
x-axis ["m0 enc", "m0 dec", "url enc", "url dec", "m enc", "m dec", "u enc", "u dec"]
|
|
43
|
+
y-axis "times faster" 0 --> 45
|
|
44
|
+
bar [14.0, 13.8, 39.2, 42.0, 7.7, 7.6, 7.3, 5.3]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- `m0`: RFC 4648 (`Ob64::Base64::RFC4648.encode` / `decode`)
|
|
48
|
+
- `url`: RFC 4648 URL-safe (`encode_urlsafe` / `decode_urlsafe`), against Ruby's `pack("m0")` plus `tr`
|
|
49
|
+
- `m`: RFC 2045 (`Ob64::Base64::RFC2045.encode` / `decode`)
|
|
50
|
+
- `u`: uuencode (`Ob64::UU.encode` / `decode`)
|
|
51
|
+
|
|
52
|
+
Try it yourself:
|
|
53
|
+
|
|
54
|
+
$ bin/rake compile benchmark
|
|
12
55
|
|
|
13
56
|
## Acknowledgements
|
|
14
57
|
|
|
@@ -40,26 +83,34 @@ require "ob64"
|
|
|
40
83
|
|
|
41
84
|
### Base64
|
|
42
85
|
|
|
86
|
+
One module per RFC, each with `encode`, `decode`, `encoded_length_of` and `decoded_length_of`.
|
|
87
|
+
|
|
88
|
+
RFC 4648, no line breaks, same output as `pack("m0")` / `unpack1("m0")`:
|
|
89
|
+
|
|
43
90
|
```ruby
|
|
44
91
|
bin1 = "This is data!"
|
|
45
|
-
string = Ob64::Base64.encode(bin1)
|
|
92
|
+
string = Ob64::Base64::RFC4648.encode(bin1)
|
|
46
93
|
# string = "VGhpcyBpcyBkYXRhIQ=="
|
|
47
94
|
|
|
48
|
-
bin2 = Ob64::Base64.decode(string)
|
|
95
|
+
bin2 = Ob64::Base64::RFC4648.decode(string)
|
|
49
96
|
puts "Same? #{bin1 == bin2}"
|
|
50
97
|
# "Same? true"
|
|
98
|
+
|
|
99
|
+
Ob64::Base64::RFC4648.decode("VGhpcyBpcyBkYXRhIQ", loose: true) # padding optional
|
|
100
|
+
Ob64::Base64::RFC4648.encode_urlsafe(bin1, padding: false) # '-' and '_' instead of '+' and '/'
|
|
101
|
+
Ob64::Base64::RFC4648.decode_urlsafe(string)
|
|
51
102
|
```
|
|
52
103
|
|
|
53
|
-
|
|
104
|
+
Bad padding, non-zero trailing bits and non-alphabet characters raise `ArgumentError`.
|
|
105
|
+
|
|
106
|
+
RFC 2045 (MIME), line-wrapped, same output as `pack("m")` / `unpack1("m")`:
|
|
54
107
|
|
|
55
108
|
```ruby
|
|
56
|
-
Ob64::Base64.
|
|
109
|
+
Ob64::Base64::RFC2045.encode(bin) # 60-char lines, each ending in "\n"
|
|
110
|
+
Ob64::Base64::RFC2045.encode(bin, 76, crlf: true)
|
|
111
|
+
Ob64::Base64::RFC2045.decode(string) # skips line breaks and other non-alphabet characters
|
|
57
112
|
```
|
|
58
113
|
|
|
59
|
-
Bad padding, non-zero trailing bits and non-alphabet characters still raise `ArgumentError`.
|
|
60
|
-
|
|
61
|
-
URL-safe alphabet: `Ob64::Base64.urlsafe_encode(bin, padding: false)`, `Ob64::Base64.urlsafe_decode(string)`.
|
|
62
|
-
|
|
63
114
|
### uuencode
|
|
64
115
|
|
|
65
116
|
Same output as `pack("u")` / `unpack1("u")`:
|
|
@@ -107,7 +158,7 @@ See [{file:CHANGELOG.md}](CHANGELOG.md).
|
|
|
107
158
|
|
|
108
159
|
## Development
|
|
109
160
|
|
|
110
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
161
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests, and `rake spec:c` to check the C codecs for memory errors. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
111
162
|
|
|
112
163
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
113
164
|
|
|
@@ -118,8 +169,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jcmfer
|
|
|
118
169
|
## TODO
|
|
119
170
|
|
|
120
171
|
* Streaming interface
|
|
121
|
-
* RFC 2045 (MIME) encoding/decoding
|
|
122
|
-
* Windows support
|
|
123
172
|
|
|
124
173
|
## License
|
|
125
174
|
|
data/Rakefile
CHANGED
|
@@ -14,6 +14,17 @@ Rake::ExtensionTask.new("ob64_ext") do |ext|
|
|
|
14
14
|
ext.ext_dir = "ext/ob64"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
desc "Check the SIMD codecs against scalar in C, under ASan and UBSan; SEED=n repeats a run"
|
|
18
|
+
task "spec:c" do
|
|
19
|
+
mkdir_p "tmp"
|
|
20
|
+
sh "#{ENV.fetch("CC", "cc")} -std=gnu99 -O3 -g -fsanitize=address,undefined -fno-sanitize-recover=all " \
|
|
21
|
+
"-fno-omit-frame-pointer -Wall -Wextra -Wno-unused-parameter -Werror -Iext/ob64 -o tmp/codecs_test " \
|
|
22
|
+
"spec/c/codecs_test.c ext/ob64/pack_base64.c ext/ob64/pack_uuencode.c"
|
|
23
|
+
# SDE: an emulator prefix, see .github/scripts/install-sde. It must run the test
|
|
24
|
+
# itself: under SDE, ASan programs started by another one exit 0 unrun.
|
|
25
|
+
sh "#{ENV["SDE"]} tmp/codecs_test #{ENV["SEED"]}"
|
|
26
|
+
end
|
|
27
|
+
|
|
17
28
|
desc "Run benchmark"
|
|
18
29
|
task :benchmark do
|
|
19
30
|
load "#{__dir__}/benchmark.rb"
|
data/benchmark.rb
CHANGED
|
@@ -41,28 +41,30 @@ each_size do |bin|
|
|
|
41
41
|
|
|
42
42
|
compare(
|
|
43
43
|
"Ruby pack(\"m0\")" => -> { NATIVE_PACK.bind_call([bin], "m0") },
|
|
44
|
-
"ob64 Base64.encode" => -> { Ob64::Base64.encode(bin) },
|
|
44
|
+
"ob64 Base64::RFC4648.encode" => -> { Ob64::Base64::RFC4648.encode(bin) },
|
|
45
45
|
"ob64 pack(\"m0\")" => -> { [bin].pack("m0") }
|
|
46
46
|
)
|
|
47
47
|
compare(
|
|
48
48
|
"Ruby unpack1(\"m0\")" => -> { NATIVE_UNPACK1.bind_call(m0, "m0") },
|
|
49
|
-
"ob64 Base64.decode" => -> { Ob64::Base64.decode(m0) },
|
|
49
|
+
"ob64 Base64::RFC4648.decode" => -> { Ob64::Base64::RFC4648.decode(m0) },
|
|
50
50
|
"ob64 unpack1(\"m0\")" => -> { m0.unpack1("m0") }
|
|
51
51
|
)
|
|
52
52
|
compare(
|
|
53
53
|
"Ruby pack(\"m0\").tr" => -> { NATIVE_PACK.bind_call([bin], "m0").tr("+/", "-_") },
|
|
54
|
-
"ob64 Base64.
|
|
54
|
+
"ob64 Base64::RFC4648.encode_urlsafe" => -> { Ob64::Base64::RFC4648.encode_urlsafe(bin) }
|
|
55
55
|
)
|
|
56
56
|
compare(
|
|
57
57
|
"Ruby tr.unpack1(\"m0\")" => -> { NATIVE_UNPACK1.bind_call(urlsafe.tr("-_", "+/"), "m0") },
|
|
58
|
-
"ob64 Base64.
|
|
58
|
+
"ob64 Base64::RFC4648.decode_urlsafe" => -> { Ob64::Base64::RFC4648.decode_urlsafe(urlsafe) }
|
|
59
59
|
)
|
|
60
60
|
compare(
|
|
61
61
|
"Ruby pack(\"m\")" => -> { NATIVE_PACK.bind_call([bin], "m") },
|
|
62
|
+
"ob64 Base64::RFC2045.encode" => -> { Ob64::Base64::RFC2045.encode(bin) },
|
|
62
63
|
"ob64 pack(\"m\")" => -> { [bin].pack("m") }
|
|
63
64
|
)
|
|
64
65
|
compare(
|
|
65
66
|
"Ruby unpack1(\"m\")" => -> { NATIVE_UNPACK1.bind_call(m, "m") },
|
|
67
|
+
"ob64 Base64::RFC2045.decode" => -> { Ob64::Base64::RFC2045.decode(m) },
|
|
66
68
|
"ob64 unpack1(\"m\")" => -> { m.unpack1("m") }
|
|
67
69
|
)
|
|
68
70
|
end if $run_base64_benchmark
|
data/bin/rake
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|
12
|
+
|
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
|
14
|
+
|
|
15
|
+
if File.file?(bundle_binstub)
|
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
|
17
|
+
load(bundle_binstub)
|
|
18
|
+
else
|
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require "rubygems"
|
|
25
|
+
require "bundler/setup"
|
|
26
|
+
|
|
27
|
+
load Gem.bin_path("rake", "rake")
|
data/ext/ob64/ob64_ext.c
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include <stdbool.h>
|
|
2
2
|
#include "ob64.h"
|
|
3
|
+
#include "ruby/encoding.h"
|
|
3
4
|
|
|
4
5
|
VALUE mOb64 = Qnil;
|
|
5
6
|
VALUE mOb64_Ext = Qnil;
|
|
@@ -51,6 +52,8 @@ VALUE Ob64_encode_string(VALUE self, VALUE bin, VALUE urlsafe, VALUE padding)
|
|
|
51
52
|
|
|
52
53
|
pack_base64_encode(RSTRING_PTR(bin), bin_len, RSTRING_PTR(result), flags);
|
|
53
54
|
rb_str_set_len(result, __encoded_length_of(bin_len, RTEST(padding)));
|
|
55
|
+
/* US-ASCII, as pack("m0") */
|
|
56
|
+
ENCODING_CODERANGE_SET(result, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
|
|
54
57
|
RB_GC_GUARD(bin);
|
|
55
58
|
|
|
56
59
|
return result;
|
data/ext/ob64/ob64_pack.c
CHANGED
|
@@ -60,6 +60,20 @@ uuencode(VALUE from, long line_len)
|
|
|
60
60
|
return ascii_result(res, n);
|
|
61
61
|
}
|
|
62
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
|
+
|
|
63
77
|
static VALUE
|
|
64
78
|
Ob64_pack(int argc, VALUE *argv, VALUE ary)
|
|
65
79
|
{
|
|
@@ -75,31 +89,22 @@ Ob64_pack(int argc, VALUE *argv, VALUE ary)
|
|
|
75
89
|
return rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
VALUE res;
|
|
79
|
-
size_t n;
|
|
80
|
-
|
|
81
92
|
if (type == 'm' && count == 0) {
|
|
82
|
-
res = rb_str_buf_new((plen + 2) / 3 * 4);
|
|
83
|
-
n = pack_base64_encode(RSTRING_PTR(from), (size_t)plen, RSTRING_PTR(res), 0);
|
|
84
|
-
|
|
85
|
-
else {
|
|
86
|
-
long len = count;
|
|
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);
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else if (len > 63 && type == 'u') len = 63;
|
|
91
|
-
else len = len / 3 * 3;
|
|
97
|
+
return ascii_result(res, n);
|
|
98
|
+
}
|
|
92
99
|
|
|
93
|
-
|
|
100
|
+
long len = count;
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
RB_GC_GUARD(from);
|
|
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;
|
|
101
106
|
|
|
102
|
-
return
|
|
107
|
+
return type == 'u' ? uuencode(from, len) : base64_encode_rfc2045(from, len, 0);
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
static VALUE
|
|
@@ -174,6 +179,58 @@ Ob64_unpack1(int argc, VALUE *argv, VALUE str)
|
|
|
174
179
|
return res;
|
|
175
180
|
}
|
|
176
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
|
+
|
|
177
234
|
static VALUE
|
|
178
235
|
Ob64_uuencode(VALUE self, VALUE bin)
|
|
179
236
|
{
|
|
@@ -221,6 +278,10 @@ Init_ob64_pack(VALUE mExt)
|
|
|
221
278
|
rb_define_method(mUnpack, "unpack", Ob64_unpack, -1);
|
|
222
279
|
rb_define_method(mUnpack, "unpack1", Ob64_unpack1, -1);
|
|
223
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);
|
|
224
285
|
rb_define_private_method(mExt, "__uuencode", Ob64_uuencode, 1);
|
|
225
286
|
rb_define_private_method(mExt, "__uudecode", Ob64_uudecode, 1);
|
|
226
287
|
rb_define_private_method(mExt, "__uuencoded_length_of", Ob64_uuencoded_length_of, 1);
|
data/ext/ob64/pack_base64.c
CHANGED
|
@@ -4359,7 +4359,7 @@ pack_base64_xgetbv (uint32_t index)
|
|
|
4359
4359
|
}
|
|
4360
4360
|
|
|
4361
4361
|
#ifndef bit_AVX512vl
|
|
4362
|
-
#define bit_AVX512vl (
|
|
4362
|
+
#define bit_AVX512vl (1u << 31)
|
|
4363
4363
|
#endif
|
|
4364
4364
|
#ifndef bit_AVX512vbmi
|
|
4365
4365
|
#define bit_AVX512vbmi (1 << 1)
|
|
@@ -4993,7 +4993,7 @@ pack_base64_decode (const char *src, size_t srclen, char *out, size_t *outlen, i
|
|
|
4993
4993
|
* ====================================================================== */
|
|
4994
4994
|
|
|
4995
4995
|
static size_t
|
|
4996
|
-
encode_rfc2045_plain(const char *src, size_t srclen, char *out, int bytes_per_line)
|
|
4996
|
+
encode_rfc2045_plain(const char *src, size_t srclen, char *out, int bytes_per_line, int crlf)
|
|
4997
4997
|
{
|
|
4998
4998
|
const uint8_t *s = (const uint8_t *)src;
|
|
4999
4999
|
char *o = out;
|
|
@@ -5012,6 +5012,7 @@ encode_rfc2045_plain(const char *src, size_t srclen, char *out, int bytes_per_li
|
|
|
5012
5012
|
srclen -= 3;
|
|
5013
5013
|
line_bytes += 3;
|
|
5014
5014
|
if (line_bytes >= bytes_per_line) {
|
|
5015
|
+
if (crlf) *o++ = '\r';
|
|
5015
5016
|
*o++ = '\n';
|
|
5016
5017
|
line_bytes = 0;
|
|
5017
5018
|
}
|
|
@@ -5022,6 +5023,7 @@ encode_rfc2045_plain(const char *src, size_t srclen, char *out, int bytes_per_li
|
|
|
5022
5023
|
*o++ = base64_table_enc_6bit[((s[0] & 0x03) << 4) | (s[1] >> 4)];
|
|
5023
5024
|
*o++ = base64_table_enc_6bit[(s[1] & 0x0F) << 2];
|
|
5024
5025
|
*o++ = '=';
|
|
5026
|
+
if (crlf) *o++ = '\r';
|
|
5025
5027
|
*o++ = '\n';
|
|
5026
5028
|
}
|
|
5027
5029
|
else if (srclen == 1) {
|
|
@@ -5029,9 +5031,11 @@ encode_rfc2045_plain(const char *src, size_t srclen, char *out, int bytes_per_li
|
|
|
5029
5031
|
*o++ = base64_table_enc_6bit[(s[0] & 0x03) << 4];
|
|
5030
5032
|
*o++ = '=';
|
|
5031
5033
|
*o++ = '=';
|
|
5034
|
+
if (crlf) *o++ = '\r';
|
|
5032
5035
|
*o++ = '\n';
|
|
5033
5036
|
}
|
|
5034
5037
|
else if (line_bytes > 0) {
|
|
5038
|
+
if (crlf) *o++ = '\r';
|
|
5035
5039
|
*o++ = '\n';
|
|
5036
5040
|
}
|
|
5037
5041
|
|
|
@@ -5052,13 +5056,13 @@ encode_whole (const struct codec *codec, const char *src, size_t srclen, char *o
|
|
|
5052
5056
|
}
|
|
5053
5057
|
|
|
5054
5058
|
size_t
|
|
5055
|
-
pack_base64_encode_rfc2045(const char *src, size_t srclen, char *out, int bytes_per_line)
|
|
5059
|
+
pack_base64_encode_rfc2045(const char *src, size_t srclen, char *out, int bytes_per_line, int crlf)
|
|
5056
5060
|
{
|
|
5057
5061
|
const struct codec *codec = codec_get();
|
|
5058
5062
|
|
|
5059
5063
|
bytes_per_line = bytes_per_line / 3 * 3;
|
|
5060
5064
|
if (bytes_per_line == 0) bytes_per_line = 45;
|
|
5061
|
-
if (!codec->enc_loop) return encode_rfc2045_plain(src, srclen, out, bytes_per_line);
|
|
5065
|
+
if (!codec->enc_loop) return encode_rfc2045_plain(src, srclen, out, bytes_per_line, crlf);
|
|
5062
5066
|
|
|
5063
5067
|
size_t line = (size_t)bytes_per_line;
|
|
5064
5068
|
size_t chars = line / 3 * 4;
|
|
@@ -5072,6 +5076,7 @@ pack_base64_encode_rfc2045(const char *src, size_t srclen, char *out, int bytes_
|
|
|
5072
5076
|
if (lines_per_batch == 0) {
|
|
5073
5077
|
/* Lines longer than the buffer: encode in place */
|
|
5074
5078
|
o += encode_whole(codec, src, line, o);
|
|
5079
|
+
if (crlf) *o++ = '\r';
|
|
5075
5080
|
*o++ = '\n';
|
|
5076
5081
|
lines = 1;
|
|
5077
5082
|
}
|
|
@@ -5081,6 +5086,7 @@ pack_base64_encode_rfc2045(const char *src, size_t srclen, char *out, int bytes_
|
|
|
5081
5086
|
for (size_t i = 0; i < lines; i++) {
|
|
5082
5087
|
memcpy(o, buf + i * chars, chars);
|
|
5083
5088
|
o += chars;
|
|
5089
|
+
if (crlf) *o++ = '\r';
|
|
5084
5090
|
*o++ = '\n';
|
|
5085
5091
|
}
|
|
5086
5092
|
}
|
|
@@ -5088,7 +5094,7 @@ pack_base64_encode_rfc2045(const char *src, size_t srclen, char *out, int bytes_
|
|
|
5088
5094
|
srclen -= lines * line;
|
|
5089
5095
|
}
|
|
5090
5096
|
|
|
5091
|
-
return (size_t)(o - out) + encode_rfc2045_plain(src, srclen, o, bytes_per_line);
|
|
5097
|
+
return (size_t)(o - out) + encode_rfc2045_plain(src, srclen, o, bytes_per_line, crlf);
|
|
5092
5098
|
}
|
|
5093
5099
|
|
|
5094
5100
|
/*
|
|
@@ -5180,3 +5186,24 @@ pack_base64_decode_rfc2045(const char *src, size_t srclen, char *out)
|
|
|
5180
5186
|
if (n == 0) return (size_t)(o - (uint8_t *)out) + decode_rfc2045_from(s, end, o, NULL, 0);
|
|
5181
5187
|
return (size_t)(o - (uint8_t *)out) + decode_rfc2045_from(s, end, o, buf, n);
|
|
5182
5188
|
}
|
|
5189
|
+
|
|
5190
|
+
size_t
|
|
5191
|
+
pack_base64_decoded_length_rfc2045(const char *src, size_t srclen)
|
|
5192
|
+
{
|
|
5193
|
+
const uint8_t *s = (const uint8_t *)src;
|
|
5194
|
+
const uint8_t *end = s + srclen;
|
|
5195
|
+
size_t n = 0;
|
|
5196
|
+
|
|
5197
|
+
/* Counts what pack_base64_decode_rfc2045 decodes: alphabet chars up to a '=' in group position 3-4 */
|
|
5198
|
+
for (; s < end; s++) {
|
|
5199
|
+
const uint8_t v = base64_table_dec_8bit[*s];
|
|
5200
|
+
|
|
5201
|
+
if (v == 255) continue;
|
|
5202
|
+
if (v == 254) {
|
|
5203
|
+
if (n % 4 >= 2) break;
|
|
5204
|
+
continue;
|
|
5205
|
+
}
|
|
5206
|
+
n++;
|
|
5207
|
+
}
|
|
5208
|
+
return n / 4 * 3 + (n % 4 == 3 ? 2 : n % 4 == 2 ? 1 : 0);
|
|
5209
|
+
}
|
data/ext/ob64/pack_base64.h
CHANGED
|
@@ -27,7 +27,10 @@ size_t pack_base64_encode(const char *src, size_t srclen, char *out, int flags);
|
|
|
27
27
|
int pack_base64_decode(const char *src, size_t srclen, char *out, size_t *outlen, int flags);
|
|
28
28
|
|
|
29
29
|
/* RFC 2045 codec (m with line wrapping, lenient decode) */
|
|
30
|
-
|
|
30
|
+
/* Lines end in "\r\n" if crlf, "\n" otherwise */
|
|
31
|
+
size_t pack_base64_encode_rfc2045(const char *src, size_t srclen, char *out, int bytes_per_line, int crlf);
|
|
31
32
|
size_t pack_base64_decode_rfc2045(const char *src, size_t srclen, char *out);
|
|
33
|
+
/* Length pack_base64_decode_rfc2045 would return; reads all of src */
|
|
34
|
+
size_t pack_base64_decoded_length_rfc2045(const char *src, size_t srclen);
|
|
32
35
|
|
|
33
36
|
#endif /* PACK_BASE64_H */
|
data/lib/ob64/version.rb
CHANGED
data/lib/ob64.rb
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
require_relative "ob64/version"
|
|
4
4
|
require_relative "ob64_ext"
|
|
5
5
|
|
|
6
|
-
# Fast base64 and uuencode encoding and decoding: see {Base64}
|
|
6
|
+
# Fast base64 and uuencode encoding and decoding: see {Base64::RFC4648},
|
|
7
|
+
# {Base64::RFC2045} and {UU}.
|
|
7
8
|
module Ob64
|
|
8
9
|
# The glue between Ruby and the C codecs. See +../ext/ob64/ob64_ext.c+.
|
|
9
10
|
module Ext; end
|
|
@@ -44,88 +45,150 @@ module Ob64
|
|
|
44
45
|
codecs.last
|
|
45
46
|
end
|
|
46
47
|
|
|
47
|
-
#
|
|
48
|
+
# Base64 encoding and decoding, one module per RFC: {RFC4648} and {RFC2045}.
|
|
48
49
|
module Base64
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
50
|
+
# Base64 as in RFC 4648: no line breaks, strict decoding.
|
|
51
|
+
module RFC4648
|
|
52
|
+
include Ext
|
|
53
|
+
extend Ext
|
|
54
|
+
|
|
55
|
+
module_function
|
|
56
|
+
|
|
57
|
+
# Returns the Base64-encoded version of +bin+, like
|
|
58
|
+
# <tt>[bin].pack("m0")</tt>. No line feeds are added.
|
|
59
|
+
#
|
|
60
|
+
# @param bin [String]
|
|
61
|
+
# @return [String]
|
|
62
|
+
def encode(bin)
|
|
63
|
+
__encode(bin, false, true)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns the Base64-decoded version of +string+, like
|
|
67
|
+
# <tt>string.unpack1("m0")</tt>.
|
|
68
|
+
# ArgumentError is raised if +string+ is incorrectly padded or contains
|
|
69
|
+
# non-alphabet characters. Note that CR or LF are also rejected.
|
|
70
|
+
#
|
|
71
|
+
# With +loose+, the padding character is optional: both correctly-padded
|
|
72
|
+
# and unpadded input are accepted. Incorrectly-padded input is still
|
|
73
|
+
# rejected.
|
|
74
|
+
#
|
|
75
|
+
# @param string [String]
|
|
76
|
+
# @param loose [Boolean] - if unpadded input is accepted
|
|
77
|
+
# @return [String]
|
|
78
|
+
# @raise [ArgumentError] if +string+ cannot be decoded
|
|
79
|
+
def decode(string, loose: false)
|
|
80
|
+
__decode(string, false, loose)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns the Base64-encoded version of +bin+ with the ``URL and Filename
|
|
84
|
+
# Safe'' alphabet: '-' instead of '+' and '_' instead of '/'.
|
|
85
|
+
# Note that the result can still contain '='.
|
|
86
|
+
# You can remove the padding by setting +padding+ as false.
|
|
87
|
+
#
|
|
88
|
+
# @param bin [String]
|
|
89
|
+
# @param padding [Boolean] - if the output must be padded
|
|
90
|
+
# @return [String]
|
|
91
|
+
def encode_urlsafe(bin, padding: true)
|
|
92
|
+
__encode(bin, true, padding)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns the Base64-decoded version of +string+ with the ``URL and
|
|
96
|
+
# Filename Safe'' alphabet: '-' instead of '+' and '_' instead of '/'.
|
|
97
|
+
#
|
|
98
|
+
# The padding character is optional.
|
|
99
|
+
# This method accepts both correctly-padded and unpadded input.
|
|
100
|
+
# Note that it still rejects incorrectly-padded input.
|
|
101
|
+
#
|
|
102
|
+
# @param string [String]
|
|
103
|
+
# @return [String]
|
|
104
|
+
# @raise [ArgumentError] if +string+ cannot be decoded
|
|
105
|
+
def decode_urlsafe(string)
|
|
106
|
+
__decode(string, true, true)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns the length of {encode} (or, with the same +padding+,
|
|
110
|
+
# {encode_urlsafe}) of +bin+.
|
|
111
|
+
#
|
|
112
|
+
# Fast: computed from +bin+'s length.
|
|
113
|
+
#
|
|
114
|
+
# @param bin [String]
|
|
115
|
+
# @param padding [Boolean] - if the Base64-encoded version of +bin+ will be padded
|
|
116
|
+
# @return [Integer]
|
|
117
|
+
def encoded_length_of(bin, padding: true)
|
|
118
|
+
__encoded_length_of(bin, padding)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns the length of {decode} (or {decode_urlsafe}) of +string+,
|
|
122
|
+
# padded or not.
|
|
123
|
+
#
|
|
124
|
+
# Fast: computed from +string+'s length and its last two characters. It
|
|
125
|
+
# doesn't validate the rest, so decoding may still raise.
|
|
126
|
+
#
|
|
127
|
+
# @param string [String]
|
|
128
|
+
# @return [Integer]
|
|
129
|
+
# @raise [ArgumentError] if +string+ has an invalid length
|
|
130
|
+
def decoded_length_of(string)
|
|
131
|
+
__decoded_length_of(string)
|
|
132
|
+
end
|
|
109
133
|
end
|
|
110
134
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
# Base64 as in RFC 2045 (MIME): line breaks, lenient decoding.
|
|
136
|
+
module RFC2045
|
|
137
|
+
include Ext
|
|
138
|
+
extend Ext
|
|
139
|
+
|
|
140
|
+
module_function
|
|
141
|
+
|
|
142
|
+
# Returns the Base64-encoded version of +bin+ in lines of +line_length+
|
|
143
|
+
# characters, each ending in a line break. Like <tt>[bin].pack("m")</tt>
|
|
144
|
+
# by default; with +crlf+, lines end in "\r\n", as MIME requires.
|
|
145
|
+
# RFC 2045 allows lines of up to 76 characters; longer ones are allowed
|
|
146
|
+
# here, as in +pack+, but break the RFC.
|
|
147
|
+
#
|
|
148
|
+
# @param bin [String]
|
|
149
|
+
# @param line_length [Integer] - characters per line, a positive multiple of 4
|
|
150
|
+
# @param crlf [Boolean] - if lines end in "\r\n" instead of "\n"
|
|
151
|
+
# @return [String]
|
|
152
|
+
# @raise [ArgumentError] if +line_length+ is not a positive multiple of 4
|
|
153
|
+
def encode(bin, line_length = 60, crlf: false)
|
|
154
|
+
__encode_rfc_2045(bin, line_length, crlf)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns the Base64-decoded version of +string+, like
|
|
158
|
+
# <tt>string.unpack1("m")</tt>: characters outside the alphabet, such
|
|
159
|
+
# as line breaks, are skipped. Decoding is lenient, as in Ruby, and
|
|
160
|
+
# never raises for malformed input.
|
|
161
|
+
#
|
|
162
|
+
# @param string [String]
|
|
163
|
+
# @return [String]
|
|
164
|
+
def decode(string)
|
|
165
|
+
__decode_rfc_2045(string)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns the length of {encode} of +bin+.
|
|
169
|
+
#
|
|
170
|
+
# Fast: computed from +bin+'s length.
|
|
171
|
+
#
|
|
172
|
+
# @param bin [String]
|
|
173
|
+
# @param line_length [Integer] - characters per line, a positive multiple of 4
|
|
174
|
+
# @param crlf [Boolean] - if lines end in "\r\n" instead of "\n"
|
|
175
|
+
# @return [Integer]
|
|
176
|
+
# @raise [ArgumentError] if +line_length+ is not a positive multiple of 4
|
|
177
|
+
def encoded_length_of(bin, line_length = 60, crlf: false)
|
|
178
|
+
__encoded_length_of_rfc_2045(bin, line_length, crlf)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Returns the length of {decode} of +string+.
|
|
182
|
+
#
|
|
183
|
+
# Costly: reads all of +string+, since skipped characters and +=+ decide
|
|
184
|
+
# the length. To size a buffer, <tt>string.bytesize * 3 / 4</tt> is
|
|
185
|
+
# always enough and fast.
|
|
186
|
+
#
|
|
187
|
+
# @param string [String]
|
|
188
|
+
# @return [Integer]
|
|
189
|
+
def decoded_length_of(string)
|
|
190
|
+
__decoded_length_of_rfc_2045(string)
|
|
191
|
+
end
|
|
129
192
|
end
|
|
130
193
|
end
|
|
131
194
|
|
|
@@ -155,7 +218,9 @@ module Ob64
|
|
|
155
218
|
__uudecode(string)
|
|
156
219
|
end
|
|
157
220
|
|
|
158
|
-
# Returns the length of
|
|
221
|
+
# Returns the length of {encode} of +bin+.
|
|
222
|
+
#
|
|
223
|
+
# Fast: computed from +bin+'s length.
|
|
159
224
|
#
|
|
160
225
|
# @param bin [String]
|
|
161
226
|
# @return [Integer]
|
|
@@ -163,9 +228,13 @@ module Ob64
|
|
|
163
228
|
__uuencoded_length_of(bin)
|
|
164
229
|
end
|
|
165
230
|
|
|
166
|
-
# Returns the length of
|
|
231
|
+
# Returns the length of {decode} of +string+.
|
|
167
232
|
# Like {decode}, never raises for malformed input.
|
|
168
233
|
#
|
|
234
|
+
# Costly: reads all of +string+, since each line's length character
|
|
235
|
+
# decides the length. To size a buffer, <tt>string.bytesize * 3 / 4</tt>
|
|
236
|
+
# is always enough and fast.
|
|
237
|
+
#
|
|
169
238
|
# @param string [String]
|
|
170
239
|
# @return [Integer]
|
|
171
240
|
def decoded_length_of(string)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ob64
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- João Fernandes
|
|
@@ -19,6 +19,7 @@ extensions:
|
|
|
19
19
|
extra_rdoc_files: []
|
|
20
20
|
files:
|
|
21
21
|
- ".github/dependabot.yml"
|
|
22
|
+
- ".github/scripts/install-sde"
|
|
22
23
|
- ".github/workflows/main.yml"
|
|
23
24
|
- ".gitignore"
|
|
24
25
|
- ".rspec"
|
|
@@ -35,6 +36,7 @@ files:
|
|
|
35
36
|
- bin/_guard-core
|
|
36
37
|
- bin/console
|
|
37
38
|
- bin/guard
|
|
39
|
+
- bin/rake
|
|
38
40
|
- bin/rspec
|
|
39
41
|
- bin/setup
|
|
40
42
|
- bin/standardrb
|
|
@@ -58,8 +60,8 @@ licenses:
|
|
|
58
60
|
metadata:
|
|
59
61
|
homepage_uri: https://github.com/jcmfernandes/ob64
|
|
60
62
|
changelog_uri: https://github.com/jcmfernandes/ob64/blob/master/CHANGELOG.md
|
|
61
|
-
documentation_uri: https://www.rubydoc.info/gems/ob64/0.
|
|
62
|
-
source_code_uri: https://github.com/jcmfernandes/ob64/tree/v0.
|
|
63
|
+
documentation_uri: https://www.rubydoc.info/gems/ob64/0.8.0
|
|
64
|
+
source_code_uri: https://github.com/jcmfernandes/ob64/tree/v0.8.0
|
|
63
65
|
rdoc_options: []
|
|
64
66
|
require_paths:
|
|
65
67
|
- lib
|
|
@@ -74,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
76
|
- !ruby/object:Gem::Version
|
|
75
77
|
version: '0'
|
|
76
78
|
requirements: []
|
|
77
|
-
rubygems_version:
|
|
79
|
+
rubygems_version: 4.0.23
|
|
78
80
|
specification_version: 4
|
|
79
81
|
summary: A fast Base64 and uuencode encoder and decoder.
|
|
80
82
|
test_files: []
|