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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf866c04fac9e6cf9d4f8c98741f17e2109e0bd5fd545d2d0482b4c28a6dd61a
4
- data.tar.gz: e6ee12aebbece01e6e1ce8181aafdfe9b32b7fbd2d1307c07d7af02d46b73f4e
3
+ metadata.gz: 110cd68288a2dd3ff785dca2a4dc2bdc0b3d47445fae88d0fef3a3a3db1b0d96
4
+ data.tar.gz: a0d837a2ba44079613b99fd862cdf156d3105b69359edbefeae32711a2be402e
5
5
  SHA512:
6
- metadata.gz: 280e35ae08985662cc05a26c5f30ef5e9897b8d7a641a83e7a0bc8bf91a1790dbf8c36f5129ff7dface1e5eeb8d286ef43387688202b26a254a6bad860faa65d
7
- data.tar.gz: f246fed35467cce900ffb7664ca62012c6d0e7bda5c2180a99814fba558331f83ab33d21a1868a0d850fa39b59eec223e39445584703f0302f339e159ce9be83
6
+ metadata.gz: 6a4fa2cabc1fad23055020ed1c49906de65e494f82fbf19966cc0a511cc8e073026dc77c4e0cb9b57d77d67e21f068fdbf79c6dfa2de978381444e1793a4793a
7
+ data.tar.gz: 432523720a899411c6a3840fc410bd4e540b7f8211dda82dff2643046959ce52ceec8bc0a8f15a6518bddb913cce69129b52ade76d8dbd33f3a7e980a690c693
@@ -0,0 +1,9 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ groups:
8
+ github-actions:
9
+ patterns: ["*"]
@@ -1,34 +1,96 @@
1
- name: Test
1
+ name: CI
2
2
 
3
3
  on: [push, pull_request]
4
4
 
5
+ permissions:
6
+ contents: read
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ defaults:
13
+ run:
14
+ shell: bash
15
+
5
16
  jobs:
17
+ lint:
18
+ runs-on: ubuntu-24.04
19
+ timeout-minutes: 10
20
+ steps:
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
+ with:
23
+ persist-credentials: false
24
+
25
+ - uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
26
+ with:
27
+ ruby-version: "3.4"
28
+ bundler-cache: true
29
+
30
+ - run: bundle exec standardrb --no-fix
31
+
6
32
  test:
7
33
  strategy:
8
34
  fail-fast: false
9
35
  matrix:
10
- ruby: ['2.5', '2.6', '2.7', '3.0']
11
- runs-on: ubuntu-latest
36
+ ruby: ["3.3", "3.4", "4.0"]
37
+ os: [ubuntu-24.04, ubuntu-24.04-arm]
38
+ cc: [gcc, clang]
39
+ runs-on: ${{ matrix.os }}
40
+ timeout-minutes: 15
12
41
  steps:
13
- - name: repository checkout
14
- uses: actions/checkout@v2
42
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
43
  with:
16
- submodules: true
44
+ persist-credentials: false
17
45
 
18
- - name: load ruby
19
- uses: ruby/setup-ruby@v1
46
+ - uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
20
47
  with:
21
48
  ruby-version: ${{ matrix.ruby }}
49
+ bundler-cache: true
22
50
 
23
- - name: RubyGems, Bundler Update
24
- run: gem update --system --no-document --conservative
25
-
26
- - name: bundle install
27
- run: bundle install --path .bundle/gems --without development
51
+ - name: Install Clang
52
+ if: matrix.cc == 'clang'
53
+ run: command -v clang || { sudo apt-get update && sudo apt-get install -y clang; }
28
54
 
29
- - name: compile
55
+ - name: Compile
56
+ env:
57
+ # Same warnings for both compilers; Ruby's defaults are GCC-only.
58
+ MAKEFLAGS: 'CC=${{ matrix.cc }} warnflags=-Wall\ -Wextra\ -Wno-unused-parameter\ -Werror'
30
59
  run: bundle exec rake compile
31
60
 
32
- - name: test
61
+ - name: Show codecs
62
+ run: bundle exec ruby -Ilib -rob64 -e 'p Ob64.codecs, Ob64.codec'
63
+
64
+ - name: Test
33
65
  run: bundle exec rake spec
34
- timeout-minutes: 10
66
+
67
+ package:
68
+ runs-on: ubuntu-24.04
69
+ timeout-minutes: 10
70
+ steps:
71
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
72
+ with:
73
+ persist-credentials: false
74
+
75
+ - uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
76
+ with:
77
+ ruby-version: "3.4"
78
+
79
+ - name: Build gem
80
+ run: gem build ob64.gemspec
81
+
82
+ - name: Install gem
83
+ run: gem install --local ./ob64-*.gem
84
+
85
+ - name: Smoke test
86
+ working-directory: ${{ runner.temp }}
87
+ run: |
88
+ ruby -e '
89
+ require "ob64"
90
+ require "ob64/core_ext"
91
+ s = Random.bytes(4096)
92
+ abort "RFC 4648 round trip failed" unless Ob64::Base64::RFC4648.decode(Ob64::Base64::RFC4648.encode(s)) == s
93
+ abort "RFC 2045 round trip failed" unless Ob64::Base64::RFC2045.decode(Ob64::Base64::RFC2045.encode(s)) == s
94
+ abort "UU round trip failed" unless Ob64::UU.decode(Ob64::UU.encode(s)) == s
95
+ abort "pack round trip failed" unless [s].pack("m0").unpack1("m0") == s
96
+ '
data/.standard.yml CHANGED
@@ -5,6 +5,5 @@ format: progress
5
5
  ignore:
6
6
  - 'benchmark.rb'
7
7
  - 'ext/**/*'
8
- - 'vendor/**/*'
9
8
  - 'spec/**/*':
10
- - Layout/AlignHash
9
+ - Layout/HashAlignment
data/CHANGELOG.md CHANGED
@@ -1,4 +1,22 @@
1
- ## [Unreleased]
1
+ ## [0.7.0] - 2026-10-26
2
+
3
+ - **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`).
4
+ - 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")`.
5
+
6
+ ## [0.6.0] - 2026-10-26
7
+
8
+ - **Breaking:** `ob64/core_ext` patches `Array#pack`, `String#unpack` and `String#unpack1` instead of `Base64`.
9
+ - **Breaking:** Require Ruby >= 3.3.
10
+ - **Breaking:** Drop CMake and libbase64. Require GCC or Clang.
11
+ - **Breaking:** Base64 methods move from `Ob64` to `Ob64::Base64` (e.g. `Ob64.encode` is now `Ob64::Base64.encode`).
12
+ - **Breaking:** `decode` and `urlsafe_decode` reject non-zero trailing bits (e.g. `"AB=="`), like `unpack1("m0")`.
13
+ - Port SIMD codecs (AVX2, AVX512, NEON64) into the extension. They also run line-wrapped `m` and `u`.
14
+ - AVX512 requires VBMI2.
15
+ - Add `Ob64.codec`, `Ob64.codec=`, `Ob64.codecs` and `Ob64.best_codec`.
16
+ - Add `Ob64::Base64.decode(string, loose: true)`.
17
+ - Add `Ob64::UU.encode`, `Ob64::UU.decode`, `Ob64::UU.encoded_length_of` and `Ob64::UU.decoded_length_of`.
18
+ - Encode and decode URL-safe in C.
19
+ - Support Linux on AArch64.
2
20
 
3
21
  # [0.5.0] - 2021-09-29
4
22
 
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
 
7
7
  gem "benchmark-ips"
8
- gem "byebug"
8
+ gem "debug"
9
9
  gem "guard"
10
10
  gem "guard-rspec"
11
11
  gem "rake", "~> 13.0"
data/README.md CHANGED
@@ -1,20 +1,18 @@
1
1
  # Ob64 gem
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/ob64.svg)](http://rubygems.org/gems/ob64)
4
- [![Build Status](https://github.com/jcmfernandes/ob64/workflows/Test/badge.svg?branch=master&event=push)](https://github.com/jcmfernandes/ob64/actions?query=workflow:Test)
5
- [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/ob64/0.5.0)
4
+ [![CI](https://github.com/jcmfernandes/ob64/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/jcmfernandes/ob64/actions/workflows/main.yml)
5
+ [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/ob64/0.7.0)
6
6
 
7
- A *fast* Base64 encoder and decoder as a Ruby gem.
7
+ A *fast* Base64 and uuencode encoder and decoder as a Ruby gem.
8
8
 
9
9
  How fast? Try it yourself, execute:
10
10
 
11
11
  $ rake benchmark
12
12
 
13
- When compared to Ruby's own `Base64` module, expect a 4x speedup in encoding, and a 2x speedup in decoding. YMMV.
14
-
15
13
  ## Acknowledgements
16
14
 
17
- Ob64 uses `libbase64` under the hood. Originally developed by [aklomp](https://github.com/aklomp), here we started from [BurningEnlightenment](https://github.com/BurningEnlightenment)'s fork. `libbase64` takes advantage of SIMD (AVX2, NEON, AArch64/NEON, SSSE3, SSE4.1, SSE4.2, AVX) acceleration available on x86, x86-64, and ARM systems.
15
+ The SIMD codecs (AVX2, AVX512, NEON64) derive from [aklomp/base64](https://github.com/aklomp/base64).
18
16
 
19
17
  ## Installation
20
18
 
@@ -32,33 +30,83 @@ Or install it yourself as:
32
30
 
33
31
  $ gem install ob64
34
32
 
35
- Requires CMake and a C99 compiler.
36
-
37
- **WARNING:** Only tested on Linux and x86-64.
33
+ Requires Ruby >= 3.3 and GCC or Clang. Linux on x86-64 and AArch64.
38
34
 
39
35
  ## Usage
40
36
 
41
37
  ```ruby
42
38
  require "ob64"
39
+ ```
40
+
41
+ ### Base64
43
42
 
43
+ One module per RFC, each with `encode`, `decode`, `encoded_length_of` and `decoded_length_of`.
44
+
45
+ RFC 4648, no line breaks, same output as `pack("m0")` / `unpack1("m0")`:
46
+
47
+ ```ruby
44
48
  bin1 = "This is data!"
45
- string = Ob64.encode(bin1)
49
+ string = Ob64::Base64::RFC4648.encode(bin1)
46
50
  # string = "VGhpcyBpcyBkYXRhIQ=="
47
51
 
48
- bin2 = Ob64.decode(string)
52
+ bin2 = Ob64::Base64::RFC4648.decode(string)
49
53
  puts "Same? #{bin1 == bin2}"
50
54
  # "Same? true"
55
+
56
+ Ob64::Base64::RFC4648.decode("VGhpcyBpcyBkYXRhIQ", loose: true) # padding optional
57
+ Ob64::Base64::RFC4648.encode_urlsafe(bin1, padding: false) # '-' and '_' instead of '+' and '/'
58
+ Ob64::Base64::RFC4648.decode_urlsafe(string)
59
+ ```
60
+
61
+ Bad padding, non-zero trailing bits and non-alphabet characters raise `ArgumentError`.
62
+
63
+ RFC 2045 (MIME), line-wrapped, same output as `pack("m")` / `unpack1("m")`:
64
+
65
+ ```ruby
66
+ Ob64::Base64::RFC2045.encode(bin) # 60-char lines, each ending in "\n"
67
+ Ob64::Base64::RFC2045.encode(bin, 76, crlf: true)
68
+ Ob64::Base64::RFC2045.decode(string) # skips line breaks and other non-alphabet characters
51
69
  ```
52
70
 
53
- If you want to replace Ruby's own `Base64` module with Ob64 **(NOT RECOMMENDED!)**:
71
+ ### uuencode
72
+
73
+ Same output as `pack("u")` / `unpack1("u")`:
74
+
75
+ ```ruby
76
+ string = Ob64::UU.encode("This is data!")
77
+ # string = "-5&AI<R!I<R!D871A(0``\n"
78
+
79
+ Ob64::UU.decode(string)
80
+ # "This is data!"
81
+ ```
82
+
83
+ ### Codecs
84
+
85
+ The best codec this CPU runs is picked at load and used for every call. `Ob64.codec=` forces one for the whole process (e.g. `:scalar`, plain C).
86
+
87
+ ```ruby
88
+ Ob64.codecs
89
+ # x86-64 with AVX512 => [:scalar, :avx2, :avx512]
90
+ # x86-64 with AVX2 => [:scalar, :avx2]
91
+ # AArch64 => [:scalar, :neon64]
92
+ # other => [:scalar]
93
+
94
+ Ob64.best_codec # => :avx512
95
+ Ob64.codec # => :avx512
96
+ Ob64.codec = :scalar
97
+ Ob64.codec = Ob64.best_codec
98
+ ```
99
+
100
+ AVX512 needs VL, VBMI and VBMI2.
101
+
102
+ ### `pack` / `unpack`
54
103
 
55
104
  ```ruby
56
105
  require "ob64/core_ext"
57
- # It does NOT replace:
58
- # - Base64.encode64
59
- # - Base64.decode64
60
106
  ```
61
107
 
108
+ Patches `Array#pack`, `String#unpack` and `String#unpack1`. Fast only for single-directive formats `m`, `mN`, `m*`, `u`, `uN`, `u*`. Everything else runs Ruby's own code. Passing `buffer:`, `offset:`, a block, or a non-String first element also runs Ruby's own code. Output is identical to Ruby's.
109
+
62
110
  See [YARD documentation](http://www.rubydoc.info/gems/ob64).
63
111
 
64
112
  ## Releases
@@ -77,11 +125,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jcmfer
77
125
 
78
126
  ## TODO
79
127
 
80
- * URL-safe encoding/decoding using separate alphabet/tables
81
128
  * Streaming interface
82
- * RFC 2045 (MIME) encoding/decoding
83
- * Make the OpenMP threshold configurable (and enable OpenMP acceleration)
84
- * Windows support
85
129
 
86
130
  ## License
87
131
 
@@ -107,9 +151,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
107
151
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
108
152
  THE SOFTWARE.
109
153
 
110
- ### libbase64
154
+ ### aklomp/base64
111
155
 
112
- Released under the BSD 2-clause License. See [LICENSE](https://github.com/jcmfernandes/base64-cmake/blob/feature/cmake/LICENSE).
156
+ Released under the BSD 2-clause License. See [LICENSE](https://github.com/aklomp/base64/blob/master/LICENSE).
113
157
 
114
158
  Copyright (c) 2005-2007, Nick Galbreath
115
159
  Copyright (c) 2013-2019, Alfred Klomp
data/benchmark.rb CHANGED
@@ -1,118 +1,85 @@
1
1
  require "benchmark/ips"
2
- require "securerandom"
2
+
3
+ # Ruby's own, captured before ob64/core_ext patches them.
4
+ NATIVE_PACK = Array.instance_method(:pack)
5
+ NATIVE_UNPACK1 = String.instance_method(:unpack1)
3
6
 
4
7
  require "ob64"
5
- require "base64"
8
+ require "ob64/core_ext"
6
9
 
7
- $run_encode_benchmark = true
8
- $run_decode_benchmark = true
9
- $run_urlsafe_encode_benchmark = true
10
- $run_urlsafe_decode_benchmark = true
10
+ $run_base64_benchmark = true
11
+ $run_uu_benchmark = true
11
12
 
12
13
  def to_size(bytes)
13
14
  bytes >= 2**20 ? "#{bytes / 2**20} MB" : "#{bytes / 2**10} kB"
14
15
  end
15
16
 
16
- def each_block
17
- [
18
- [1, :kb],
19
- [4, :kb],
20
- [64, :kb],
21
- [1, :mb],
22
- [10, :mb],
23
- [64, :mb]
24
- ].each do |(n, unit)|
25
- yield(n * 2**({kb: 10, mb: 20}.fetch(unit)))
26
- end
27
- end
28
-
29
- def benchmark(setup_block)
30
- each_block do |size|
31
- setup_block.call(size)
32
- GC.start
17
+ def each_size
18
+ [1 * 2**10, 4 * 2**10, 64 * 2**10, 1 * 2**20, 10 * 2**20, 64 * 2**20].each do |size|
33
19
  puts "\n\n#{"=" * 60}\nbenchmark with block size #{to_size(size)}"
34
- yield
20
+ yield Random.bytes(size)
35
21
  end
36
22
  end
37
23
 
38
- def encode_benchmark(&block)
39
- benchmark(
40
- lambda { |size| $unencoded = File.open("/dev/urandom") { |f| f.read(size) } },
41
- &block
42
- )
43
- end
44
-
45
- def decode_benchmark(&block)
46
- benchmark(
47
- lambda { |size| $encoded = SecureRandom.base64(size) },
48
- &block
49
- )
50
- end
51
-
52
- encode_benchmark do
53
- Benchmark.ips do |x|
54
- x.time = 5
55
- x.warmup = 2
56
-
57
- x.report("base64 .strict_encode64") do
58
- Base64.strict_encode64($unencoded)
59
- end
60
-
61
- x.report("ob64 .encode") do
62
- Ob64.encode($unencoded)
63
- end
64
-
65
- x.compare!
66
- end
67
- end if $run_encode_benchmark
68
-
69
- decode_benchmark do
70
- Benchmark.ips do |x|
71
- x.time = 5
72
- x.warmup = 2
73
-
74
- x.report("base64 .strict_decode64") do
75
- Base64.strict_decode64($encoded)
76
- end
77
-
78
- x.report("ob64 .decode") do
79
- Ob64.decode($encoded)
80
- end
81
-
82
- x.compare!
83
- end
84
- end if $run_decode_benchmark
85
-
86
- encode_benchmark do
24
+ # Compares reports, label => block
25
+ def compare(reports)
26
+ GC.start
87
27
  Benchmark.ips do |x|
88
28
  x.time = 5
89
29
  x.warmup = 2
90
30
 
91
- x.report("base64 .urlsafe_encode64") do
92
- Base64.urlsafe_encode64($unencoded)
93
- end
94
-
95
- x.report("ob64 .urlsafe_encode") do
96
- Ob64.urlsafe_encode($unencoded)
97
- end
31
+ reports.each { |label, block| x.report(label, &block) }
98
32
 
99
- x.compare!
33
+ x.compare!(order: :baseline)
100
34
  end
101
- end if $run_urlsafe_encode_benchmark
35
+ end
102
36
 
103
- decode_benchmark do
104
- Benchmark.ips do |x|
105
- x.time = 5
106
- x.warmup = 2
37
+ each_size do |bin|
38
+ m0 = NATIVE_PACK.bind_call([bin], "m0")
39
+ urlsafe = m0.tr("+/", "-_")
40
+ m = NATIVE_PACK.bind_call([bin], "m")
107
41
 
108
- x.report("base64 .urlsafe_decode64") do
109
- Base64.urlsafe_decode64($encoded)
110
- end
42
+ compare(
43
+ "Ruby pack(\"m0\")" => -> { NATIVE_PACK.bind_call([bin], "m0") },
44
+ "ob64 Base64::RFC4648.encode" => -> { Ob64::Base64::RFC4648.encode(bin) },
45
+ "ob64 pack(\"m0\")" => -> { [bin].pack("m0") }
46
+ )
47
+ compare(
48
+ "Ruby unpack1(\"m0\")" => -> { NATIVE_UNPACK1.bind_call(m0, "m0") },
49
+ "ob64 Base64::RFC4648.decode" => -> { Ob64::Base64::RFC4648.decode(m0) },
50
+ "ob64 unpack1(\"m0\")" => -> { m0.unpack1("m0") }
51
+ )
52
+ compare(
53
+ "Ruby pack(\"m0\").tr" => -> { NATIVE_PACK.bind_call([bin], "m0").tr("+/", "-_") },
54
+ "ob64 Base64::RFC4648.encode_urlsafe" => -> { Ob64::Base64::RFC4648.encode_urlsafe(bin) }
55
+ )
56
+ compare(
57
+ "Ruby tr.unpack1(\"m0\")" => -> { NATIVE_UNPACK1.bind_call(urlsafe.tr("-_", "+/"), "m0") },
58
+ "ob64 Base64::RFC4648.decode_urlsafe" => -> { Ob64::Base64::RFC4648.decode_urlsafe(urlsafe) }
59
+ )
60
+ compare(
61
+ "Ruby pack(\"m\")" => -> { NATIVE_PACK.bind_call([bin], "m") },
62
+ "ob64 Base64::RFC2045.encode" => -> { Ob64::Base64::RFC2045.encode(bin) },
63
+ "ob64 pack(\"m\")" => -> { [bin].pack("m") }
64
+ )
65
+ compare(
66
+ "Ruby unpack1(\"m\")" => -> { NATIVE_UNPACK1.bind_call(m, "m") },
67
+ "ob64 Base64::RFC2045.decode" => -> { Ob64::Base64::RFC2045.decode(m) },
68
+ "ob64 unpack1(\"m\")" => -> { m.unpack1("m") }
69
+ )
70
+ end if $run_base64_benchmark
111
71
 
112
- x.report("ob64 .urlsafe_decode") do
113
- Ob64.urlsafe_decode($encoded)
114
- end
72
+ each_size do |bin|
73
+ u = NATIVE_PACK.bind_call([bin], "u")
115
74
 
116
- x.compare!
117
- end
118
- end if $run_urlsafe_decode_benchmark
75
+ compare(
76
+ "Ruby pack(\"u\")" => -> { NATIVE_PACK.bind_call([bin], "u") },
77
+ "ob64 UU.encode" => -> { Ob64::UU.encode(bin) },
78
+ "ob64 pack(\"u\")" => -> { [bin].pack("u") }
79
+ )
80
+ compare(
81
+ "Ruby unpack1(\"u\")" => -> { NATIVE_UNPACK1.bind_call(u, "u") },
82
+ "ob64 UU.decode" => -> { Ob64::UU.decode(u) },
83
+ "ob64 unpack1(\"u\")" => -> { u.unpack1("u") }
84
+ )
85
+ end if $run_uu_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/extconf.rb CHANGED
@@ -2,46 +2,4 @@
2
2
 
3
3
  require "mkmf"
4
4
 
5
- def sys(cmd)
6
- puts " -- #{cmd}"
7
- unless ret = xsystem(cmd)
8
- raise "ERROR: '#{cmd}' failed"
9
- end
10
- ret
11
- end
12
-
13
- def run_cmake(args)
14
- pid = Process.spawn("cmake #{args}")
15
- Process.waitpid(pid)
16
- end
17
-
18
- if !find_executable("cmake")
19
- abort "ERROR: CMake is required to build ob64"
20
- end
21
-
22
- MAKE = find_executable("make") || find_executable("gmake")
23
- if !MAKE
24
- abort "ERROR: make is required to build ob64"
25
- end
26
-
27
- CWD = __dir__
28
- LIBBASE64_DIR = File.join(CWD, "..", "..", "vendor", "libbase64")
29
-
30
- Dir.chdir(LIBBASE64_DIR) do
31
- Dir.mkdir("build") unless Dir.exist?("build")
32
-
33
- Dir.chdir("build") do
34
- run_cmake(".. -DBUILD_SHARED_LIBS=true -DOPENMP=false")
35
- sys(MAKE)
36
- end
37
-
38
- $DEFLIBPATH.unshift("#{LIBBASE64_DIR}/build/bin")
39
- dir_config("base64", "#{LIBBASE64_DIR}/include", "#{LIBBASE64_DIR}/build/bin")
40
- end
41
-
42
- unless have_library("base64") && have_header("libbase64.h")
43
- abort "ERROR: Failed to build libbase64"
44
- end
45
-
46
- dir_config("ob64_ext")
47
5
  create_makefile("ob64_ext")
data/ext/ob64/ob64.h CHANGED
@@ -1,7 +1,9 @@
1
1
  #ifndef OB64_H
2
2
  #define OB64_H 1
3
3
 
4
- #include "libbase64.h"
4
+ #include "pack_base64.h"
5
5
  #include "ruby.h"
6
6
 
7
+ void Init_ob64_pack(VALUE mExt);
8
+
7
9
  #endif /* OB64_H */