Uint8Array, Uint16Array, Uint32Array をバイトの並び順を保持して相互変換
普通にUint○○Array (TypedArray) 同士をキャストすると、入りきらないデータは切り落とされます。
const uint16Array = Uint16Array.from([0xff01, 0xfe02, 0xfd03, 0xfc04]);
const uint8Array = new Uint8Array(uint16Array);
console.log(uint8Array);
// -> Uint8Array(4) [1, 2, 3, 4] = 0x01, 0x02, 0x03, 0x04
これを [255, 1, 254, 2, 253, 3, 252, 4]
= 0xff, 0x01, 0xfe, 0x02, 0xfd, 0x03, 0xfc, 0x04
に変換できないかと思って、やってみました。
もくじ
[2019-11-23 追記] 以下の記事を参照してください。この記事より役に立つと思います。
JavaScript Blob, TypedArray, 文字列, Canvas, Imageとか周りの変換まとめ
どれ同士でも相互変換できる関数
Uint8Array
Uint16Array
Uint32Array
どれ同士でも変換できる関数です。なんとなく Uint8ClampedArray
も受け付けてみました。
いったん Uint8Array
に変換した後、他のビット長の Uint○○Array
に変換してます。
でもこの関数、これだけ見ると、自分で書いたのに意味が分かりません。なので以降の項には、バラバラで読める状態のものを載せておきます。
/**
* UintArrayを別のビット長のUintArrayに変換
* @param {Uint8Array|Uint8ClampedArray|Uint16Array|Uint32Array} uintArray - 変換元のUintArray
* @param {Number} toBitLength - 変換先のビット長 (8, 16, 32)
* @return {Uint8Array|Uint16Array|Uint32Array} 変換後のUintArray
*/
const convertUintArray = (uintArray, toBitLength) => {
// 型から変換元のビット長を判定
let fromBitLength;
if (uintArray instanceof Uint8Array || uintArray instanceof Uint8ClampedArray) {
fromBitLength = 8;
} else if (uintArray instanceof Uint16Array) {
fromBitLength = 16;
} else if (uintArray instanceof Uint32Array) {
fromBitLength = 32;
} else {
throw new TypeError('引数がUintArrayではありません。');
}
// 変換元と変換先のビット長が同じならそのまま返却
if (fromBitLength == toBitLength) {
return uintArray;
}
// 変換元のビット長が8でなければ8に変換
let uint8Array;
if (fromBitLength == 8) {
uint8Array = uintArray;
} else {
const divisor = fromBitLength / 8;
const len = uintArray.length;
const shiftMax = fromBitLength - 8;
uint8Array = new Uint8Array(len * divisor);
for (let i = 0; i < len; ++i) {
for (let j = 0; j < divisor; ++j) {
uint8Array[i * divisor + j] = (uintArray[i] & (0xff << (8 * (divisor - 1 - j)))) >> (shiftMax - j * 8);
}
}
}
// 変換先のビット長が8ならここで返却
if (toBitLength == 8) {
return uint8Array;
}
// 8から変換先のビット長に変換
let type;
if (toBitLength == 16) {
type = Uint16Array;
} else if (toBitLength == 32) {
type = Uint32Array;
} else {
throw new RangeError('変換元がUint8Arrayの場合、変換先のバイト長は16か32を指定してください。');
}
const len = uint8Array.length;
const divisor = toBitLength / 8;
if (len % divisor > 0) {
throw new RangeError('Uint8Arrayの長さが'+ divisor +'の倍数ではありません。');
}
const newUintArray = new type(len / divisor);
const shiftMax = toBitLength - 8;
for (let i = 0, j = 0; i < len; ++i) {
const mod = i % divisor;
newUintArray[j] += uint8Array[i] << (shiftMax - mod * 8);
if (mod == divisor - 1) {
++j;
}
}
return newUintArray;
};
使ってみるとこんな感じです。
// TypedArrayを16進表記に変換する関数
const toHexStr = (typedArray) => typedArray.reduce((a, c) => a +(a === '' ? '' : ' ')+ c.toString(16), '');
// Uint○○Array作成
const uint8Array = Uint8Array.from([0xff, 0x01, 0xfe, 0x02, 0xfd, 0x03, 0xfc, 0x04]);
const uint16Array = Uint16Array.from([0xff01, 0xfe02, 0xfd03, 0xfc04])
const uint32Array = Uint32Array.from([0xff01fe02, 0xfd03fc04]);
// 確認
console.log('16 -> 8', toHexStr(convertUintArray(uint16Array, 8))); // -> ff 1 fe 2 fd 3 fc 4
console.log('32 -> 8', toHexStr(convertUintArray(uint32Array, 8))); // -> ff 1 fe 2 fd 3 fc 4
console.log(' 8 -> 16', toHexStr(convertUintArray(uint8Array, 16))); // -> ff01 fe02 fd03 fc04
console.log('32 -> 16', toHexStr(convertUintArray(uint32Array, 16))); // -> ff01 fe02 fd03 fc04
console.log(' 8 -> 32', toHexStr(convertUintArray(uint8Array, 32))); // -> ff01fe02 fd03fc04
console.log('16 -> 32', toHexStr(convertUintArray(uint16Array, 32))); // -> ff01fe02 fd03fc04
Uint8Array → Uint16Array
const uint8ArrayToUint16Array = (uint8Array) => {
const len = uint8Array.length;
if (len % 2 > 0) {
throw new RangeError('Uint8Arrayの長さが2の倍数ではありません。');
}
const uint16Array = new Uint16Array(len / 2);
for (let i = 0, j = 0; i < len; ++i) {
const mod = i % 2;
if (mod == 0) {
uint16Array[j] += uint8Array[i] << 8;
} else {
uint16Array[j] += uint8Array[i];
++j;
}
}
return uint16Array;
};
Uint8Array → Uint32Array
見るからに冗長なコードですが、これじゃないと私が読めません。すみません。
const uint8ArrayToUint32Array = (uint8Array) => {
const len = uint8Array.length;
if (len % 4 > 0) {
throw new RangeError('Uint8Arrayの長さが4の倍数ではありません。');
}
const uint32Array = new Uint32Array(len / 4);
for (let i = 0, j = 0; i < len; ++i) {
const mod = i % 4;
if (mod == 0) {
uint32Array[j] += uint8Array[i] << 24;
} else if (mod == 1) {
uint32Array[j] += uint8Array[i] << 16;
} else if (mod == 2) {
uint32Array[j] += uint8Array[i] << 8;
} else {
uint32Array[j] += uint8Array[i];
++j;
}
}
return uint32Array;
};
Uint16Array → Uint8Array
const uint16ArrayToUint8Array = (uint16Array) => {
const len = uint16Array.length;
const uint8Array = new Uint8Array(len * 2);
for (let i = 0, j = 0; i < len; ++i, j += 2) {
uint8Array[j] = (uint16Array[i] & 0xff00) >> 8;
uint8Array[j + 1] = uint16Array[i] & 0x00ff;
}
return uint8Array;
};
Uint32Array → Uint8Array
見るからに冗長なコードですg
const uint32ArrayToUint8Array = (uint32Array) => {
const len = uint32Array.length;
const uint8Array = new Uint8Array(len * 4);
for (let i = 0, j = 0; i < len; ++i, j += 4) {
uint8Array[j] = (uint32Array[i] & 0xff000000) >> 24;
uint8Array[j + 1] = (uint32Array[i] & 0x00ff0000) >> 16;
uint8Array[j + 2] = (uint32Array[i] & 0x0000ff00) >> 8;
uint8Array[j + 3] = uint32Array[i] & 0x000000ff;
}
return uint8Array;
};