# chijin Implementation Notes (2026-03-02)

Key findings discovered while binding OCCT 7.9.3 to Rust via the cxx crate.

---

## 1. BinTools binary format requires a seekable stream

### Problem

Passing a non-seekable `std::istream` (e.g. a custom `std::streambuf`) to
`BinTools::Read` causes `Standard_Failure` to be thrown, crashing the process.

### Root cause

The OCCT 7.7+ binary format avoids duplicating shared sub-shapes (e.g. vertices
shared between edges in a box) by writing **backward references** — byte offsets
into already-written data. The internal `BinTools_IStream` calls `seekg()` to jump
to those positions, so a forward-only stream cannot work.

The text format (`BRepTools`) uses sequential numeric IDs and has no such constraint.

### Fix

On the read side, drain all bytes from the Rust reader into memory, then feed a
seekable `std::istringstream` to `BinTools::Read`.

```cpp
std::unique_ptr<TopoDS_Shape> read_brep_bin_stream(RustReader& reader) {
    std::string data;
    char buf[8192];
    for (;;) {
        rust::Slice<uint8_t> slice(reinterpret_cast<uint8_t*>(buf), sizeof(buf));
        size_t n = rust_reader_read(reader, slice);
        if (n == 0) break;
        data.append(buf, n);
    }
    std::istringstream iss(std::move(data));
    auto shape = std::make_unique<TopoDS_Shape>();
    BinTools::Read(*shape, iss);   // iss is seekable
    ...
}
```

The write side (`BinTools_OStream`) increments `myPosition` internally and never
calls `seekp()`, so a custom non-seekable `std::streambuf` works fine for writing.

---

## 2. OCCT exceptions must be caught before the FFI boundary

### Problem

When `Standard_Failure` (OCCT's exception base class) propagates uncaught through
the cxx FFI boundary into Rust, the process terminates immediately with
`STATUS_STACK_BUFFER_OVERRUN` (0xc0000409).

### Root cause

`Standard_Failure` does **not** inherit from `std::exception`. cxx only converts
`std::exception`-derived exceptions; anything else is undefined behaviour at the
Rust boundary.

### Fix

Wrap all I/O calls (`BinTools::Read/Write`, `BRepTools::Read/Write`) in try/catch.

```cpp
try {
    BinTools::Read(*shape, iss);
} catch (const Standard_Failure&) {
    return nullptr;
}
```

---

## 3. `TopExp_Explorer` over edges produces duplicates

### Problem

`TopExp_Explorer(shape, TopAbs_EDGE)` yields 24 edges for a box that has only 12.

### Root cause

`TopExp_Explorer` visits edges by traversing through adjacent faces. A shared edge
appears once per adjacent face (2 times for a box edge).

### Fix

Collect unique edges with `TopTools_IndexedMapOfShape`, pack them into a
`TopoDS_Compound`, then explore that compound.

```cpp
TopTools_IndexedMapOfShape edgeMap;
TopExp::MapShapes(shape, TopAbs_EDGE, edgeMap);
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
for (int i = 1; i <= edgeMap.Extent(); i++) {
    builder.Add(compound, edgeMap(i));
}
return std::make_unique<TopExp_Explorer>(compound, TopAbs_EDGE);
```

---

## 4. `-DOCCT_UWP` eliminates the advapi32 dependency

### Problem

`TKernel` contains a static initialiser in `OSD_WNT.cxx` (`Init_OSD_WNT`) that
unconditionally calls `AllocateAndInitializeSid` (advapi32) at program startup.
Rust passes `-nodefaultlibs` to GCC, bypassing the spec file that would normally
add `-ladvapi32`, causing a link error.

### Fix

Define `-DOCCT_UWP` when building OCCT with CMake. This flag gates all Windows
security-API calls in `OSD_WNT.cxx`, `OSD_File.cxx`, and `OSD_Process.cxx`
behind `#ifndef OCCT_UWP`, eliminating the advapi32 dependency entirely.

```rust
cmake::Config::new(&source_dir)
    .cflag("-DOCCT_UWP")
    .cxxflag("-DOCCT_UWP")
    // ...
    .build();
```

---

## 5. `Standard_ErrorHandler` multiple definition requires `--allow-multiple-definition`

### Problem

The MinGW linker reports a multiple definition error for
`Standard_ErrorHandler::Callback::RegisterCallback()`.

### Root cause

`Standard_ErrorHandler.hxx` defines the method inline, so it lands as a strong
symbol in both `wrapper.o` (compiled by cxx_build) and `TKernel.a`. MinGW treats
both as strong symbols and errors on the conflict.

### Fix

```rust
println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition");
```

---

## 6. OCCT 7.9 library renames

Several libraries were renamed in OCCT 7.8+.

| Old name (≤7.7) | New name (7.8+) |
|---|---|
| `TKBinTools` | `TKBin` |
| `TKShapeUpgrade` | `TKShHealing` |
| `TKSTEP`, `TKSTEP209`, `TKSTEPAttr`, `TKSTEPBase` | `TKDESTEP` |
| (new) | `TKDE`, `TKDECascade` |

---

## 7. cxx bridge namespace and includes

When using `namespace = "chijin"` in the cxx bridge, all C++ types exposed through
the bridge must live in the `chijin::` namespace. For OCCT global types, declare
`using` aliases in `wrapper.h`:

```cpp
namespace chijin {
    using TopoDS_Shape    = ::TopoDS_Shape;
    using TopoDS_Face     = ::TopoDS_Face;
    using TopoDS_Edge     = ::TopoDS_Edge;
    using TopExp_Explorer = ::TopExp_Explorer;
}
```

The `include!()` path in the cxx bridge is relative to the crate root:

```rust
include!("chijin/cpp/wrapper.h");
```
