libredwg-sys 0.16.0

Raw FFI bindings to the LibreDWG C sources (a vendored subset of upstream carrying two local patches, see NOTICE.md), built natively via cc + bindgen (no Emscripten/WASM).
Documentation
--- a/src/acds.spec
+++ b/src/acds.spec
@@ -339,89 +339,85 @@
 
 #ifdef IS_DECODER
   DECODER {
-    char *s, *e;
-    unsigned int i = 0;
-    char *acis_sab_data;
+    /* --- uncad local patch (see docs/CAVEATS.md, "Local patches to the
+       vendored LibreDWG"): each ACIS body goes to the entity whose handle
+       its _data_ record names. Upstream searched the section for the
+       "ACIS BinaryFile" signature and handed the bodies it found to the
+       drawing's solids in the order they came, which is not the order the
+       records are written in -- bodies landed on the wrong solids -- and it
+       missed the "ASM BinaryFile4" bodies R2018 writes altogether.
+       A _data_ segment is a 0x30-byte segment header, then an array of
+       20-byte record headers (RL 0x14, RL 1, RLL handle, RL offset), then
+       the records: at the array's end plus a header's offset, an RL size and
+       that many bytes. --- end uncad local patch --- */
+#define UNCAD_LE32(q)                                                         \
+  ((BITCODE_RL)(q)[0] | ((BITCODE_RL)(q)[1] << 8)                             \
+   | ((BITCODE_RL)(q)[2] << 16) | ((BITCODE_RL)(q)[3] << 24))
     const BITCODE_B acis_empty = 0;
     const BITCODE_BS version = 2;
-    BITCODE_BL num_acis_sab_data;
-    const unsigned int wanted = dwg->num_acis_sab_hdl;
-    // 414349532042696E61727946696C65 @10504/2 = 5252
-    const char start[] = "ACIS BinaryFile";
-    // 0E03456E640E026F660E0341534D0D0464617461 @13822/2 = 6911
-    const char end[] = "\016\003End\016\002of\016\003ASM\r\004data";
-    LOG_TRACE ("\nSearch for ACIS BinaryFile data:\n");
-    num_acis_sab_data = 0;
-    while (i < dat->size
-           && (s = (char *)memmem (&dat->chain[i], dat->size - i, start,
-                                   strlen (start))))
+    const size_t seg_hdr_size = 0x30;
+    const size_t rec_hdr_size = 20;
+    size_t i = 0;
+    while (i + 8 <= dat->size)
       {
-        size_t j = s - (char*)&dat->chain[0]; // absolute_offset of found range
-        if ((e = (char *)memmem (s, dat->size - j, end, strlen (end))))
+        unsigned char *seg = (unsigned char *)memmem (
+            &dat->chain[i], dat->size - i, "\254\325_data_", 8);
+        size_t start, hdrs, base, n, k;
+        if (!seg)
+          break;
+        start = (size_t)(seg - dat->chain);
+        i = start + 8;
+        hdrs = start + seg_hdr_size;
+        n = 0;
+        while (hdrs + (n + 1) * rec_hdr_size <= dat->size
+               && UNCAD_LE32 (&dat->chain[hdrs + n * rec_hdr_size])
+                      == rec_hdr_size)
+          n++;
+        base = hdrs + n * rec_hdr_size;
+        for (k = 0; k < n; k++)
           {
-            BITCODE_H hdl;
+            const unsigned char *h = &dat->chain[hdrs + k * rec_hdr_size];
+            BITCODE_RLL handle = (BITCODE_RLL)UNCAD_LE32 (h + 8)
+                                 | ((BITCODE_RLL)UNCAD_LE32 (h + 12) << 32);
+            size_t rec = base + UNCAD_LE32 (h + 16);
+            size_t size;
             Dwg_Object *o;
             Dwg_Entity_3DSOLID *sol;
-            size_t size = e - s;
-            size += strlen (end);
-            LOG_TRACE ("acis_sab_data[%d]: found %s at %" PRIuSIZE ", size %" PRIuSIZE "\n",
-                       num_acis_sab_data, start, j, size);
-            if (!dwg->num_acis_sab_hdl)
+            char *acis_sab_data;
+            if (rec < base || rec + 4 > dat->size)
+              continue;
+            size = UNCAD_LE32 (&dat->chain[rec]);
+            if (size < 15 || size > dat->size - rec - 4)
+              continue;
+            if (memcmp (&dat->chain[rec + 4], "ACIS BinaryFile", 15)
+                && memcmp (&dat->chain[rec + 4], "ASM BinaryFile4", 15))
+              continue;
+            o = dwg_resolve_handle (dwg, handle);
+            if (!o || o->supertype != DWG_SUPERTYPE_ENTITY
+                || !dwg_obj_is_3dsolid (o))
               {
-                LOG_ERROR ("Not enough %u 3DSOLIDs for the %u-th AcDs SAB data",
-                           wanted, num_acis_sab_data);
-                return DWG_ERR_INVALIDHANDLE;
-              }
-            hdl = SHIFT_HV (dwg, num_acis_sab_hdl, acis_sab_hdl);
-            o = dwg_resolve_handle (dwg, hdl->handleref.value);
-            LOG_TRACE ("%s.acis_sab_hdl[%u] = " FORMAT_REF "\n", o->name,
-                       dwg->num_acis_sab_hdl + 1, ARGS_REF (hdl));
-            if (!o || !dwg_obj_is_3dsolid (o))
-              {
-                LOG_ERROR ("Matching object %s " FORMAT_REF " not a 3DSOLID",
-                           o ? o->name : "", ARGS_REF (hdl));
-                free (hdl);
-                error |= DWG_ERR_INVALIDHANDLE;
+                LOG_WARN ("AcDs SAB data for " FORMAT_RLLx
+                          " which is not a 3DSOLID", handle);
                 continue;
               }
             sol = o->tio.entity->tio._3DSOLID;
-            // not NULL terminated
-            acis_sab_data = (char*)malloc (size);
-            memcpy (acis_sab_data, s, size);
-            num_acis_sab_data++;
-            dwg_dynapi_entity_set_value (sol, o->name, "acis_data", &acis_sab_data, 0);
+            acis_sab_data = (char *)malloc (size);
+            if (!acis_sab_data)
+              continue;
+            memcpy (acis_sab_data, &dat->chain[rec + 4], size);
+            dwg_dynapi_entity_set_value (sol, o->name, "acis_data",
+                                         &acis_sab_data, 0);
             dwg_dynapi_entity_set_value (sol, o->name, "sab_size", &size, 0);
             dwg_dynapi_entity_set_value (sol, o->name, "version", &version, 0);
-            // FIXME only until we can write acds:
-            dwg_dynapi_entity_set_value (sol, o->name, "acis_empty", &acis_empty, 0);
-            // o->tio.entity->has_ds_data = 0; // maybe there is more, like the
-            // wires and silhuettes
-            LOG_TRACE ("%s.acis_data = %" PRIuSIZE " " FORMAT_REF "\n", o->name, size,
-                       ARGS_REF (hdl));
-            free (hdl); // it is a non-global, free'able handleref. Created in
-                        // common_entity_data.spec
-            i = (j + size) & UINT_MAX; // next offset to try
-          }
-        else
-          {
-            LOG_WARN ("No End-of-ASM-data found from %" PRIuSIZE " - %" PRIuSIZE
-                      " for %d-th SAB data",
-                       j, dat->size, num_acis_sab_data);
-            i = (j + 20) & UINT_MAX;
+            dwg_dynapi_entity_set_value (sol, o->name, "acis_empty",
+                                         &acis_empty, 0);
+            LOG_TRACE ("%s.acis_data = %" PRIuSIZE " from AcDs record " FORMAT_RLLx
+                       "\n", o->name, size, handle);
           }
       }
-    if (wanted == num_acis_sab_data)
-      {
-        LOG_TRACE ("Matching number of %u 3DSOLID entities and AcDs SAB data\n",
-                   wanted);
-      }
-    else
-      {
-        LOG_WARN ("Not matching number of %u 3DSOLID entities and %u AcDs SAB "
-                  "data\n",
-                  wanted, num_acis_sab_data);
-        while (dwg->num_acis_sab_hdl > 0)
-          free (SHIFT_HV (dwg, num_acis_sab_hdl, acis_sab_hdl));
-      }
+#undef UNCAD_LE32
+    /* The handles queued for the search this replaces. */
+    while (dwg->num_acis_sab_hdl > 0)
+      free (SHIFT_HV (dwg, num_acis_sab_hdl, acis_sab_hdl));
   }
 #endif