こんにちわ。
皆さんもBase64は使われているかと思います。
バイナリを文字列で表現できるのは何かと便利ですよね。
しかしJavaScriptには、こうサクっと1発でバイナリ配列とBase64文字列を変換できる実装が用意されていないので、何かしらの手を加える必要があります。
今回は、バイナリとBase64の相互変換を行える3つの方法について、どれが1番速いかを検証してみようと思います。
方法1, 標準実装
ライブラリを使わず、これらのブラウザ標準実装を組み合わせて変換します。
TextEncoder/TextDecoderは、文字列とUint8Arrayを相互変換してくれるAPIです。
そしてatob/btoaは、お馴染みBase64のブラウザ標準実装です。
この2つを組み合わせることにより、以下の流れでバイナリとBase64を変換できます。
Uint8Array <=> BinaryString <=> Base64
BinaryStringとは0x00
~0xFF
までの文字(制御文字含)で構成される文字列です。
バイナリを型付き配列やBlobとして扱うのが難しい時によく用いられる表現です。
そしてatob/btoaは、ちょうど0x00
~0xFF
までの文字を扱えます。
逆にこれしか扱えないので、Unicodeを扱う際によく問題となりますが...
TextEncoder/TextDecoder APIはクラスとして提供されるので、Base64向けの拡張クラスを作ってみました。
// Base64 => Uint8Array
class Base64Encoder extends TextEncoder{
constructor(){
super();
}
encode(text){
return super.encode(atob(text));
}
}
// Uint8Array => Base64
class Base64Decoder extends TextDecoder{
constructor(){
super();
}
decode(buffer){
return btoa(super.decode(buffer));
}
}
Node.jsはどうするんだって...?
最近話題 のDenoさんが何とかしてくれるでしょう...知らんけど。
方法2, base64-js
週間14,700,000ダウンロードの実績があるライブラリです。
恐らくこれが1番メジャーでしょうか。
メソッドは3つのみ、シンプルで使いやすいです。
Base64とUint8Arrayを相互変換できます。
方法3, js-base64
週間6,150,000ダウンロードの実績があるライブラリです。
2番手といったところでしょうか。
base64-jsと比べてライブラリ容量は大きめですが、メンテナンスは割と最近までされているようです。
こちらも、Base64とUint8Arrayを相互変換できます。
測定
以下のPCで測定を行いました。
- CORE2Quad Q9550
- DDR2-1066 4GB
- Windows7 32bit
- Google Chrome 83
部屋に転がってたPCなので、マシンスペックについては目を瞑ってもらいましょう...
元となるデータは、小さいと一瞬で終わってしまい比較にならないと予想したので、64MBのバイト配列でエンコード/デコードを行います。
これだけ大きいと、コールスタックやヒープなどのメモリ性能もついでに見れるかと思います。
// 64MB
const test = new Uint8Array(1024 * 1024 * 64);
// 1. 標準実装
new Base64Encoder().encode(new Base64Decoder().decode(test));
// 2. base64-js
base64js.toByteArray(base64js.fromByteArray(test));
// 3. js-base64
Base64.toUint8Array(Base64.fromUint8Array(test));
結果
- 標準実装 ... 1240 ms
- base64-js ... 2870 ms
- js-base64 ... ぴえん🥺(5秒程の後にスタックオーバーフロー)
という結果になりました。
まじかよ!
ライブラリ使うより普通に標準実装を組み合わせた方が速いじゃん!
js-base64に至っては完走すら出来ませんでした。
ブラウザでBase64を扱うなら、標準実装もしくはbase64-jsを使うのが良さそうです。