@unikvs/hex
バイト列を 16 進文字列として表すバイト列へ透過的に変換する Transformer プラグインの使い方を説明します。
概要
@unikvs/hex は、バイト列を 16 進文字列 (hex) として表すバイト列へ変換する Transformer プラグインです。Transformer
書き込み時は encode で hex 化し、読み取り時は decode で元のバイト列に戻すため、変換の詳細を意識せずに読み書きできます。ハッシュ値やバイナリー内容の確認、デバッグ出力、テキストしか保存できない保存先への格納に使います。
入出力はどちらも Uint8Array<ArrayBuffer> です。出力は常に小文字 (0-9・a-f) で、入力は大文字・小文字のどちらも受け付けます。変換後のサイズは元のサイズのちょうど 2 倍になります。
常にオープン状態です。open・close は不要で、isOpen は常に true、name は常に "Hex" を返します。
インストールは次のとおりです。
npm install @unikvs/hexpnpm add @unikvs/hexyarn add @unikvs/hexbun add @unikvs/hexnub add @unikvs/hexaube add @unikvs/hex使い方
通常は引数なしで生成します。UTF-8 変換に使うインスタンスを差し替えたい場合にだけ HexOptions を指定します。
import { Hex } from "@unikvs/hex";
const hex = new Hex();
import { Hex, type HexOptions } from "@unikvs/hex";
import { FastUtf8 } from "fast-utf8";
const options: HexOptions = {
encoder: new FastUtf8(),
decoder: new FastUtf8({ strict: true }),
};
const hex = new Hex(options);
HexOptions の内容は次のとおりです。通常は省略し、既定のまま使います。decoder に strict: false のインスタンスを指定した場合は、不正な UTF-8 は HexDecodeError として投げられます。詳しくはエラーを参照してください。
type HexOptions = {
readonly encoder?: FastUtf8 | undefined;
readonly decoder?: FastUtf8 | undefined;
};
主なメンバーは次のとおりです。
| メンバー | 概要 |
|---|---|
new Hex(options?) |
オプションなしで初期化します。差し替えが必要な場合だけ HexOptions を渡します。 |
name |
常に "Hex" を返します。 |
isOpen |
常に true を返します。 |
encode({ data }) |
Uint8Array<ArrayBuffer> を小文字 hex の Uint8Array<ArrayBuffer> に変換します。 |
decode({ data }) |
小文字・大文字の hex の Uint8Array<ArrayBuffer> を元のバイト列に戻します。 |
getEncodable() |
hex 化用の TransformStream を返します。 |
getDecodable() |
hex 復元用の TransformStream を返します。 |
一括変換の例です。encode の結果を UTF-8 文字列として解釈すると小文字の hex になります。
import { Hex } from "@unikvs/hex";
const hex = new Hex();
const encoded = hex.encode({ data: Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) });
console.log(new TextDecoder().decode(encoded)); // "deadbeef"
const decoded = hex.decode({ data: new TextEncoder().encode("DEADBEEF") });
console.log(decoded); // Uint8Array(4) [ 222, 173, 190, 239 ]
UniKvs には appendTransformer で登録します。以降に登録するストレージの前段になります。
import { Hex } from "@unikvs/hex";
import UniKvs from "unikvs";
const kvs = UniKvs.config()
.appendTransformer(new Hex())
.appendStorage(storage)
.create();
await kvs.open();
await kvs.set("data", input);
const output = await kvs.get("data");
set で hex 化して保存し、get で自動的に元のバイト列へ戻します。
ストリーム
単体値とストリームの両方に対応しています。
- 単体値は
encode・decodeで変換します。 - ストリーム値は
getEncodable・getDecodableのTransformStreamで変換します。
getEncodable は、入力チャンクをそれぞれ独立に hex 化して順に送出します。
getDecodable は、チャンク境界で分割された hex も正しく復元します。
- 空のストリームと長さ 0 のチャンクは値を 1 つも生成せず、エラーにもなりません。
- 終了時に半端な 1 文字が残っている場合は
HexDecodeErrorを投げます。 - hex として不正な文字が含まれている場合は、その時点で
HexDecodeErrorを投げます。 - 既定では、不正な UTF-8 や途中で切れたマルチバイト文字が含まれている場合は
TypeErrorを投げます。詳しくはエラーを参照してください。
import { Hex } from "@unikvs/hex";
const hex = new Hex();
const source = new ReadableStream({
start(controller) {
controller.enqueue(Uint8Array.from([0xde, 0xad]));
controller.enqueue(Uint8Array.from([0xbe, 0xef]));
controller.close();
},
});
const decoded = source.pipeThrough(hex.getEncodable()).pipeThrough(hex.getDecodable());
注意点
形式の扱いは次のとおりです。
0xの接頭辞は付加も除去もしません。"0x..."を渡すとxが不正文字として拒否されます。必要な場合は呼び出し側で付け外ししてください。- 空白・改行・コロンなどの区切りは読み飛ばしません。不正文字として拒否されます。
- 空入力は空出力です。
new Uint8Array(0)を渡してもエラーになりません。
サイズ(バイト長)は元のサイズのちょうど 2 倍になります。圧縮や暗号化、改ざん検知の目的には使えません。
エラー
このパッケージが投げるエラーは次のとおりです。
| エラー | 意味 | 対処 |
|---|---|---|
HexDecodeError |
hex として解釈できません。奇数長、不正な文字が原因です。 | 入力が偶数長の hex (0-9・a-f・A-F の連続) か確認してください。 |
既定では(一括・ストリームいずれも)、UTF-8 として不正なバイト列は HexDecodeError ではなくネイティブの TypeError として伝わります。HexOptions で strict: false の decoder を注入した場合は HexDecodeError として投げられます。
import { Hex, HexDecodeError } from "@unikvs/hex";
const hex = new Hex();
try {
hex.decode({ data: new TextEncoder().encode("abc") });
} catch (error) {
if (error instanceof HexDecodeError) {
console.log("hex として解釈できません。");
} else {
throw error;
}
}
使用例
Memory と組み合わせた最小例です。書き込んだ値は hex 化して保存し、読み取り時に自動的に元のバイト列に戻します。
import { Hex } from "@unikvs/hex";
import { Memory } from "@unikvs/memory";
import UniKvs, { type PlainValue } from "unikvs";
const kvs = UniKvs.config<{ data: PlainValue<Uint8Array<ArrayBuffer>> }>()
.appendTransformer(new Hex())
.appendStorage(new Memory())
.create();
await kvs.open();
const input = Uint8Array.from([0xde, 0xad, 0xbe, 0xef]);
await kvs.set("data", input);
const output = await kvs.get("data");
console.log(output.length === input.length);
await kvs.close();