Skip to content

Commit 1cc799b

Browse files
committed
feat: inject ifc splitter dependencies
1 parent 6804110 commit 1cc799b

File tree

4 files changed

+73
-26
lines changed

4 files changed

+73
-26
lines changed

packages/fragments/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@thatopen/fragments",
33
"description": "Simple geometric system built on top of Three.js to display 3D BIM data efficiently.",
4-
"version": "3.3.5",
4+
"version": "3.3.6",
55
"author": "That Open Company",
66
"contributors": [
77
"Antonio Gonzalez Viegas (https://github.com/agviegas)",

packages/fragments/src/Utils/ifc-splitter/index.ts

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,39 @@
33
/* eslint-disable no-cond-assign */
44
/* eslint-disable no-bitwise */
55

6-
import * as fs from "fs";
7-
import * as path from "path";
6+
// ---------------------------------------------------------------------------
7+
// Node.js dependency injection (avoids top-level fs/path imports for bundlers)
8+
// ---------------------------------------------------------------------------
9+
10+
/** Subset of Node.js `fs` used by the splitter. */
11+
export interface IfcSplitterFs {
12+
openSync(path: string, flags: string): number;
13+
readSync(
14+
fd: number,
15+
buffer: any,
16+
offset: number,
17+
length: number,
18+
position: null,
19+
): number;
20+
writeSync(fd: number, data: any, offset?: number, length?: number): number;
21+
closeSync(fd: number): void;
22+
existsSync(path: string): boolean;
23+
mkdirSync(path: string, options?: { recursive?: boolean }): void;
24+
statSync(path: string): { size: number };
25+
}
26+
27+
/** Subset of Node.js `path` used by the splitter. */
28+
export interface IfcSplitterPath {
29+
join(...paths: string[]): string;
30+
dirname(p: string): string;
31+
basename(p: string): string;
32+
}
33+
34+
/** Dependencies that must be provided by the caller (Node.js modules). */
35+
export interface IfcSplitterDeps {
36+
fs: IfcSplitterFs;
37+
path: IfcSplitterPath;
38+
}
839

940
// ---------------------------------------------------------------------------
1041
// Exported interfaces
@@ -227,13 +258,19 @@ function extractArgsString(raw: string | undefined): string | null {
227258
// ---------------------------------------------------------------------------
228259
// Synchronous chunked file reader — replaces readline (3-5x faster)
229260
// ---------------------------------------------------------------------------
230-
function forEachLine(filePath: string, callback: (line: string) => void): void {
261+
function forEachLine(
262+
fsLike: IfcSplitterFs,
263+
filePath: string,
264+
callback: (line: string) => void,
265+
): void {
231266
const CHUNK = 8 * 1024 * 1024;
232-
const fd = fs.openSync(filePath, "r");
267+
const fd = fsLike.openSync(filePath, "r");
233268
const readBuf = Buffer.allocUnsafe(CHUNK);
234269
let tail = "";
235270
let bytesRead: number;
236-
while ((bytesRead = fs.readSync(fd, readBuf as any, 0, CHUNK, null)) > 0) {
271+
while (
272+
(bytesRead = fsLike.readSync(fd, readBuf as any, 0, CHUNK, null)) > 0
273+
) {
237274
const chunk = readBuf.toString("utf-8", 0, bytesRead);
238275
let start = 0;
239276
let idx = chunk.indexOf("\n");
@@ -260,20 +297,22 @@ function forEachLine(filePath: string, callback: (line: string) => void): void {
260297
if (start < chunk.length) tail = chunk.substring(start);
261298
}
262299
if (tail) callback(tail);
263-
fs.closeSync(fd);
300+
fsLike.closeSync(fd);
264301
}
265302

266303
// ---------------------------------------------------------------------------
267304
// Buffered synchronous file writer — avoids per-line write() syscalls
268305
// ---------------------------------------------------------------------------
269306
class BufferedWriter {
307+
private fsLike: IfcSplitterFs;
270308
private fd: number;
271309
private buf: Buffer;
272310
private pos: number;
273311
private bufSize: number;
274312

275-
constructor(filePath: string, bufSize: number) {
276-
this.fd = fs.openSync(filePath, "w");
313+
constructor(fsLike: IfcSplitterFs, filePath: string, bufSize: number) {
314+
this.fsLike = fsLike;
315+
this.fd = fsLike.openSync(filePath, "w");
277316
this.buf = Buffer.allocUnsafe(bufSize);
278317
this.pos = 0;
279318
this.bufSize = bufSize;
@@ -284,7 +323,7 @@ class BufferedWriter {
284323
if (this.pos + bytes > this.bufSize) {
285324
this.flush();
286325
if (bytes > this.bufSize) {
287-
fs.writeSync(this.fd, str);
326+
this.fsLike.writeSync(this.fd, str);
288327
return;
289328
}
290329
}
@@ -293,14 +332,14 @@ class BufferedWriter {
293332

294333
flush(): void {
295334
if (this.pos > 0) {
296-
fs.writeSync(this.fd, this.buf as any, 0, this.pos);
335+
this.fsLike.writeSync(this.fd, this.buf as any, 0, this.pos);
297336
this.pos = 0;
298337
}
299338
}
300339

301340
close(): void {
302341
this.flush();
303-
fs.closeSync(this.fd);
342+
this.fsLike.closeSync(this.fd);
304343
}
305344
}
306345

@@ -389,7 +428,7 @@ class LineIndex {
389428
// ---------------------------------------------------------------------------
390429
// Streaming IFC parser
391430
// ---------------------------------------------------------------------------
392-
function parseIfc(filePath: string): ParseResult {
431+
function parseIfc(fsLike: IfcSplitterFs, filePath: string): ParseResult {
393432
const header: string[] = [];
394433
const footer: string[] = [];
395434
const index = new LineIndex();
@@ -400,7 +439,7 @@ function parseIfc(filePath: string): ParseResult {
400439

401440
console.time("parse");
402441

403-
forEachLine(filePath, (line: string) => {
442+
forEachLine(fsLike, filePath, (line: string) => {
404443
if (section === "header") {
405444
header.push(line);
406445
if (line.trim() === "DATA;") section = "data";
@@ -758,10 +797,12 @@ function resolveStyles(
758797
* @param outputDir - Directory for output files. Defaults to `output/` next to the input file.
759798
*/
760799
export function split(
800+
deps: IfcSplitterDeps,
761801
inputPath: string,
762802
numGroups: number,
763803
outputDir?: string,
764804
): void {
805+
const { fs, path } = deps;
765806
if (!fs.existsSync(inputPath)) {
766807
console.error(`File not found: ${inputPath}`);
767808
process.exit(1);
@@ -772,7 +813,7 @@ export function split(
772813
fs.mkdirSync(resolvedOutputDir, { recursive: true });
773814

774815
// 1. Parse
775-
const { header, footer, index } = parseIfc(inputPath);
816+
const { header, footer, index } = parseIfc(fs, inputPath);
776817

777818
// 2. Identify spatial structure (shared in all files)
778819
console.time("spatial");
@@ -1004,6 +1045,7 @@ export function split(
10041045
// 10. Second pass: write output files
10051046
console.time("write");
10061047
writeOutputFiles(
1048+
deps,
10071049
inputPath,
10081050
resolvedOutputDir,
10091051
header,
@@ -1031,10 +1073,12 @@ export function split(
10311073
* @param outputPath - Path for the output IFC file.
10321074
*/
10331075
export function extract(
1076+
deps: IfcSplitterDeps,
10341077
inputPath: string,
10351078
elementIds: number[],
10361079
outputPath: string,
10371080
): void {
1081+
const { fs, path } = deps;
10381082
if (!fs.existsSync(inputPath)) {
10391083
console.error(`File not found: ${inputPath}`);
10401084
process.exit(1);
@@ -1044,7 +1088,7 @@ export function extract(
10441088
fs.mkdirSync(outputDir, { recursive: true });
10451089

10461090
// 1. Parse
1047-
const { header, footer, index } = parseIfc(inputPath);
1091+
const { header, footer, index } = parseIfc(fs, inputPath);
10481092

10491093
// 2. Identify spatial structure (shared)
10501094
console.time("spatial");
@@ -1181,20 +1225,20 @@ export function extract(
11811225
console.log(` Total lines in output: ${fileIds.size}`);
11821226

11831227
// 7. Free index, write output
1184-
const maxParsedId = index.maxId;
1228+
// const maxParsedId = index.maxId;
11851229
index.free();
11861230

11871231
// Build simple inclusion set
11881232
const includeSet = new Set<number>(fileIds);
11891233

11901234
console.time("write");
1191-
const bw = new BufferedWriter(outputPath, 4 * 1024 * 1024);
1235+
const bw = new BufferedWriter(fs, outputPath, 4 * 1024 * 1024);
11921236
bw.write(`${header.join("\n")}\n`);
11931237

11941238
let section: "header" | "data" | "footer" = "header";
11951239
let accumulator = "";
11961240

1197-
forEachLine(inputPath, (line: string) => {
1241+
forEachLine(fs, inputPath, (line: string) => {
11981242
if (section === "header") {
11991243
if (line.trim() === "DATA;") section = "data";
12001244
return;
@@ -1260,13 +1304,15 @@ function emitSingleLine(
12601304
// Second-pass output writer
12611305
// ---------------------------------------------------------------------------
12621306
function writeOutputFiles(
1307+
deps: IfcSplitterDeps,
12631308
inputPath: string,
12641309
outputDir: string,
12651310
header: string[],
12661311
footer: string[],
12671312
groupsData: (GroupData | null)[],
12681313
idGroupMask: Uint32Array,
12691314
): void {
1315+
const { fs, path } = deps;
12701316
const numGroups = groupsData.length;
12711317

12721318
const writers: (BufferedWriter | null)[] = [];
@@ -1280,15 +1326,15 @@ function writeOutputFiles(
12801326
outputDir,
12811327
`split_${String(g + 1).padStart(3, "0")}.ifc`,
12821328
);
1283-
const bw = new BufferedWriter(outName, 4 * 1024 * 1024);
1329+
const bw = new BufferedWriter(fs, outName, 4 * 1024 * 1024);
12841330
bw.write(headerStr);
12851331
writers.push(bw);
12861332
}
12871333

12881334
let section: "header" | "data" | "footer" = "header";
12891335
let accumulator = "";
12901336

1291-
forEachLine(inputPath, (line: string) => {
1337+
forEachLine(fs, inputPath, (line: string) => {
12921338
if (section === "header") {
12931339
if (line.trim() === "DATA;") section = "data";
12941340
return;

packages/fragments/src/Utils/ifc-splitter/test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env node
2+
import * as fs from "fs";
23
import * as path from "path";
3-
import { split } from "./split";
4+
import { split } from "./index";
45

56
const args = process.argv.slice(2);
67
if (args.length < 2) {
@@ -14,7 +15,7 @@ const numGroups = parseInt(args[1], 10);
1415
const outputDir = args[2] ? path.resolve(args[2]) : undefined;
1516

1617
try {
17-
split(inputPath, numGroups, outputDir);
18+
split({ fs, path }, inputPath, numGroups, outputDir);
1819
} catch (err) {
1920
console.error(err);
2021
process.exit(1);

resources/worker.mjs

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)