fix(viewer2d): sync dwg-wasm fix for large-DWG load and hatch arc artifacts

Mirrors hmwebviewer's rust/dwg-wasm change into the sample: the wasm
artifact plus the parser wrapper, which now goes through parse_dwg_json
and JSON.parse instead of the JsValue-returning parse_dwg.

Building the whole result as a serde_json::Value tree pushed the live
wasm heap near 2.9GB for a 19MB drawing, and the allocator's cost grows
with that heap, so parsing went quadratic — 354s, with the tab frozen
throughout. Streaming one entity at a time keeps it linear: 2.5s.

The same build also fixes three bugs that drew stray 4km grey shapes
where two CF-PATT SOLID hatches should be. Max hatch span 4655 -> 110.

Adds two docs recording both investigations, in the format of the
existing ones: measurements, the hypotheses that were ruled out, the
fixes, regression checks, and the MPL-2.0 position (acadrust is
unmodified, so no new source-disclosure obligation arises).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-08-03 13:55:34 +09:00
co-authored by Claude Opus 5
parent 7a6edd4347
commit b047dc44e2
7 changed files with 770 additions and 3 deletions
+15
View File
@@ -3,15 +3,30 @@
export function parse_dwg(bytes: Uint8Array): any;
/**
* The same parseResult as [`parse_dwg`], returned as a JSON string for the
* caller to hand to `JSON.parse`. Prefer this on anything but small drawings.
*
* Materializing the whole result as a `serde_json::Value` is what makes big
* files unopenable. The tree costs ~150× the DWG on the heap (a 19 MB drawing
* peaks near 2 GB), and the wasm allocator's per-allocation cost rises with the
* live heap, so conversion goes quadratic — measured 352 s in wasm for work
* that takes 2.5 s natively. Converting one entity at a time and appending its
* JSON text keeps the live heap flat and the cost linear.
*/
export function parse_dwg_json(bytes: Uint8Array): string;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly parse_dwg: (a: number, b: number) => [number, number, number];
readonly parse_dwg_json: (a: number, b: number) => [number, number, number, number];
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_externrefs: WebAssembly.Table;
readonly __externref_table_dealloc: (a: number) => void;
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
readonly __wbindgen_start: () => void;
}
+34
View File
@@ -13,6 +13,40 @@ export function parse_dwg(bytes) {
}
return takeFromExternrefTable0(ret[0]);
}
/**
* The same parseResult as [`parse_dwg`], returned as a JSON string for the
* caller to hand to `JSON.parse`. Prefer this on anything but small drawings.
*
* Materializing the whole result as a `serde_json::Value` is what makes big
* files unopenable. The tree costs ~150× the DWG on the heap (a 19 MB drawing
* peaks near 2 GB), and the wasm allocator's per-allocation cost rises with the
* live heap, so conversion goes quadratic — measured 352 s in wasm for work
* that takes 2.5 s natively. Converting one entity at a time and appending its
* JSON text keeps the live heap flat and the cost linear.
* @param {Uint8Array} bytes
* @returns {string}
*/
export function parse_dwg_json(bytes) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.parse_dwg_json(ptr0, len0);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
function __wbg_get_imports() {
const import0 = {
__proto__: null,
Binary file not shown.
+2
View File
@@ -2,8 +2,10 @@
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const parse_dwg: (a: number, b: number) => [number, number, number];
export const parse_dwg_json: (a: number, b: number) => [number, number, number, number];
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_externrefs: WebAssembly.Table;
export const __externref_table_dealloc: (a: number) => void;
export const __wbindgen_free: (a: number, b: number, c: number) => void;
export const __wbindgen_start: () => void;
+11 -3
View File
@@ -2,7 +2,7 @@
* acadrust DWG parser wrapper — loads the wasm built from rust/dwg-wasm
* (wasm-pack --target web, committed under ./acadrust-dwg).
*/
import init, { parse_dwg } from './acadrust-dwg/acadrust_dwg.js';
import init, { parse_dwg_json } from './acadrust-dwg/acadrust_dwg.js';
import wasmUrl from './acadrust-dwg/acadrust_dwg_bg.wasm?url';
import type { CadParseResult } from './dwgParser';
@@ -14,7 +14,15 @@ export function initAcadrustParser(): Promise<void> {
return ready;
}
/** Parse a DWG buffer. Requires initAcadrustParser() first. */
/**
* Parse a DWG buffer. Requires initAcadrustParser() first.
*
* Goes through `parse_dwg_json` + `JSON.parse` rather than the `parse_dwg`
* export that hands back a JsValue. The latter keeps the entire result live on
* the wasm heap as it builds, and the allocator's cost grows with that heap —
* a 19 MB drawing took ~354 s and froze the tab. Streaming the result out as
* text keeps it linear: the same file now lands in ~2.5 s.
*/
export function parseDwgAcadrust(bytes: Uint8Array): CadParseResult {
return parse_dwg(bytes) as CadParseResult;
return JSON.parse(parse_dwg_json(bytes)) as CadParseResult;
}