94 lines
3.0 KiB
TypeScript
94 lines
3.0 KiB
TypeScript
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { DesktopFileNodeParser } from '../src/onenote/onestore/file-node.js';
|
|
import { OneNoteParserError } from '../src/onenote/parser/error.js';
|
|
import { resolveParserLimits } from '../src/onenote/parser/limits.js';
|
|
|
|
const MAGIC = 0xa4567ab1f5f7f4c4n;
|
|
const FOOTER = 0x8bc215c38233ba4bn;
|
|
|
|
describe('DesktopFileNodeParser', () => {
|
|
it('rejects a multi-fragment cycle immediately', () => {
|
|
const bytes = new Uint8Array(72);
|
|
writeFragment(bytes, 0, 0x10, 0, 36);
|
|
writeFragment(bytes, 36, 0x10, 1, 0);
|
|
|
|
const parser = new DesktopFileNodeParser(bytes, resolveParserLimits());
|
|
const parse = () =>
|
|
parser.parseList({ offset: 0, size: 36, nil: false, zero: false });
|
|
|
|
expect(parse).toThrowError(OneNoteParserError);
|
|
expect(parse).toThrow(/cycle/i);
|
|
});
|
|
|
|
it('stops decoding at ChunkTerminatorFND and permits an opaque tail', () => {
|
|
const bytes = new Uint8Array(52);
|
|
const view = new DataView(bytes.buffer);
|
|
view.setBigUint64(0, MAGIC, true);
|
|
view.setUint32(8, 0x10, true);
|
|
view.setUint32(12, 0, true);
|
|
view.setUint32(16, 0x10ff, true); // id 0xff, declared size 4
|
|
bytes.fill(0xa5, 20, 32); // deliberately not valid FileNode words
|
|
view.setBigUint64(32, 0xffffffffffffffffn, true);
|
|
view.setUint32(40, 0, true);
|
|
view.setBigUint64(44, FOOTER, true);
|
|
|
|
const parser = new DesktopFileNodeParser(bytes, resolveParserLimits());
|
|
const parsed = parser.parseList({
|
|
offset: 0,
|
|
size: bytes.length,
|
|
nil: false,
|
|
zero: false,
|
|
});
|
|
|
|
expect(parsed.nodes).toEqual([]);
|
|
});
|
|
|
|
it('uses the transaction-log count to delimit nonzero fragment padding', () => {
|
|
const bytes = new Uint8Array(52);
|
|
const view = new DataView(bytes.buffer);
|
|
view.setBigUint64(0, MAGIC, true);
|
|
view.setUint32(8, 0x10, true);
|
|
view.setUint32(12, 0, true);
|
|
view.setUint32(16, 0x1004, true); // id 4, declared size 4
|
|
bytes.fill(0xa5, 20, 32); // opaque tail without a terminator
|
|
view.setBigUint64(32, 0xffffffffffffffffn, true);
|
|
view.setUint32(40, 0, true);
|
|
view.setBigUint64(44, FOOTER, true);
|
|
|
|
const parser = new DesktopFileNodeParser(
|
|
bytes,
|
|
resolveParserLimits(),
|
|
new Map([[0x10, 1]])
|
|
);
|
|
const parsed = parser.parseList({
|
|
offset: 0,
|
|
size: bytes.length,
|
|
nil: false,
|
|
zero: false,
|
|
});
|
|
|
|
expect(parsed.nodes.map(({ id }) => id)).toEqual([0x004]);
|
|
});
|
|
});
|
|
|
|
function writeFragment(
|
|
bytes: Uint8Array,
|
|
offset: number,
|
|
listId: number,
|
|
sequence: number,
|
|
nextOffset: number
|
|
): void {
|
|
const view = new DataView(bytes.buffer, offset, 36);
|
|
view.setBigUint64(0, MAGIC, true);
|
|
view.setUint32(8, listId, true);
|
|
view.setUint32(12, sequence, true);
|
|
view.setBigUint64(16, BigInt(nextOffset), true);
|
|
view.setUint32(24, 36, true);
|
|
view.setBigUint64(28, FOOTER, true);
|
|
}
|