UNPKG

qrloop

Version:

Envelop big blob of data into frames that can be displayed in series of QR Codes

116 lines 4.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.dataToFrames = exports.wrapData = exports.makeDataFrame = exports.makeFountainFrame = void 0; const md5_1 = __importDefault(require("md5")); const buffer_1 = require("buffer"); const Buffer_1 = require("./Buffer"); const constants_1 = require("./constants"); function makeFountainFrame(dataChunks, selectedFrameIndexes) { const k = selectedFrameIndexes.length; const head = buffer_1.Buffer.alloc(3 + 2 * k); head.writeUInt8(constants_1.FOUNTAIN_V1, 0); head.writeUInt16BE(k, 1); const selectedFramesData = []; for (let j = 0; j < k; j++) { const frameIndex = selectedFrameIndexes[j]; selectedFramesData.push(dataChunks[frameIndex]); head.writeUInt16BE(frameIndex, 3 + 2 * j); } const data = (0, Buffer_1.xor)(selectedFramesData); return buffer_1.Buffer.concat([head, data]).toString("base64"); } exports.makeFountainFrame = makeFountainFrame; function makeDataFrame({ data, nonce, totalFrames, frameIndex, }) { const head = buffer_1.Buffer.alloc(5); head.writeUInt8(nonce, 0); head.writeUInt16BE(totalFrames, 1); head.writeUInt16BE(frameIndex, 3); return buffer_1.Buffer.concat([head, data]).toString("base64"); } exports.makeDataFrame = makeDataFrame; function wrapData(data) { const lengthBuffer = buffer_1.Buffer.alloc(4); lengthBuffer.writeUInt32BE(data.length, 0); const md5Buffer = buffer_1.Buffer.from((0, md5_1.default)(data), "hex"); return buffer_1.Buffer.concat([lengthBuffer, md5Buffer, data]); } exports.wrapData = wrapData; /** * in one loop: * the data is prepend in the frames with this head: * 4 bytes: uint, data length * 16 bytes: md5 of data * * each frame is a base64 of: * 1 byte: nonce * 2 bytes: uint, total number of frames * 2 bytes: uint, index of frame * variable data * * each "fountain" frame is base64 of: * 1 byte: fountain version * 2 bytes: number of K frames associated * K times 2 bytes: the index of each frame * variable data: the XOR of the frames data * * It inspires idea from https://en.wikipedia.org/wiki/Luby_transform_code */ function makeLoop(wrappedData, dataSize, index, random) { const nonce = index % constants_1.MAX_NONCE; const dataChunks = (0, Buffer_1.cutAndPad)(wrappedData, dataSize); const fountains = []; if (dataChunks.length > 2) { // TODO optimal number fcount and k still need to be determined const fcount = Math.floor(dataChunks.length / 6); const k = Math.ceil(dataChunks.length / 2); for (let i = 0; i < fcount; i++) { const distribution = Array(dataChunks.length) .fill(null) .map((_, i) => ({ i, n: random() })) .sort((a, b) => a.n - b.n) .slice(0, k) .map((o) => o.i); fountains.push(makeFountainFrame(dataChunks, distribution)); } } const result = []; let j = 0; const fountainEach = Math.floor(dataChunks.length / fountains.length); for (let i = 0; i < dataChunks.length; i++) { result.push(makeDataFrame({ data: dataChunks[i], nonce, totalFrames: dataChunks.length, frameIndex: i, })); if (i % fountainEach === 0 && fountains[j]) { result.push(fountains[j++]); } } return result; } /** * Export data into one series of chunk of string that you can generate a QR with * @param dataOrStr the complete data to encode in a series of QR code frames * @param dataSize the number of bytes to use from data for each frame * @param loops number of loops to generate. more loops increase chance for readers to read frames */ function dataToFrames(dataOrStr, dataSize = 120, loops = 1) { // Simple deterministic RNG let seed = 1; function random() { let x = Math.sin(seed++) * 10000; return x - Math.floor(x); } const wrappedData = wrapData(buffer_1.Buffer.from(dataOrStr)); let r = []; for (let i = 0; i < loops; i++) { r = r.concat(makeLoop(wrappedData, dataSize, i, random)); } return r; } exports.dataToFrames = dataToFrames; //# sourceMappingURL=exporter.js.map