43 lines
2.0 KiB
JavaScript
43 lines
2.0 KiB
JavaScript
export const MAX_PACKET_BYTES = 80 * 1024 * 1024;
|
|
const MAX_PIXELS = 16_777_216;
|
|
const crcTable = Uint32Array.from({ length: 256 }, (_, value) => {
|
|
for (let bit = 0; bit < 8; bit++) value = (value >>> 1) ^ ((value & 1) ? 0xedb88320 : 0);
|
|
return value >>> 0;
|
|
});
|
|
|
|
export function crc32(bytes) {
|
|
let crc = 0xffffffff;
|
|
for (let index = 0; index < bytes.length; index++) crc = (crc >>> 8) ^ crcTable[(crc ^ bytes[index]) & 255];
|
|
return (crc ^ 0xffffffff) >>> 0;
|
|
}
|
|
|
|
export function restorePacket(buffer) {
|
|
if (!(buffer instanceof ArrayBuffer) || buffer.byteLength < 24 || buffer.byteLength > MAX_PACKET_BYTES) {
|
|
throw new Error("预览数据长度无效");
|
|
}
|
|
const view = new DataView(buffer);
|
|
if (view.getUint32(0) !== 0x43455056 || view.getUint8(4) !== 1 || view.getUint8(7) !== 0) {
|
|
throw new Error("不支持的预览数据版本");
|
|
}
|
|
const format = view.getUint8(5);
|
|
const mask = view.getUint8(6);
|
|
const width = view.getUint32(8);
|
|
const height = view.getUint32(12);
|
|
const length = view.getUint32(16);
|
|
const checksum = view.getUint32(20);
|
|
if (!mask || ![1, 2].includes(format) || length !== buffer.byteLength - 24 || !width || !height || width * height > MAX_PIXELS) {
|
|
throw new Error("预览数据格式或尺寸无效");
|
|
}
|
|
const words = new Uint32Array(buffer, 24, Math.floor(length / 4));
|
|
const wordMask = (mask * 0x01010101) >>> 0;
|
|
for (let index = 0; index < words.length; index++) words[index] ^= wordMask;
|
|
const bytes = new Uint8Array(buffer, 24);
|
|
for (let index = words.length * 4; index < bytes.length; index++) bytes[index] ^= mask;
|
|
if (crc32(bytes) !== checksum) throw new Error("预览数据损坏,请重新运行节点");
|
|
const validSignature = format === 1
|
|
? bytes.length >= 8 && [137, 80, 78, 71, 13, 10, 26, 10].every((value, index) => bytes[index] === value)
|
|
: bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
if (!validSignature) throw new Error("预览图片格式不匹配");
|
|
return { bytes, width, height, mime: format === 1 ? "image/png" : "image/jpeg" };
|
|
}
|