gdsdiff 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. gdsdiff/__init__.py +148 -0
  2. gdsdiff/__main__.py +7 -0
  3. gdsdiff/_environment.py +12 -0
  4. gdsdiff/_version.py +34 -0
  5. gdsdiff/acceptance_evidence.py +530 -0
  6. gdsdiff/api.py +2955 -0
  7. gdsdiff/backends/__init__.py +26 -0
  8. gdsdiff/backends/base.py +77 -0
  9. gdsdiff/backends/boundary_loop_ctypes.py +191 -0
  10. gdsdiff/backends/cpu.py +20 -0
  11. gdsdiff/backends/cpu_ctypes.py +255 -0
  12. gdsdiff/backends/cuda.py +40 -0
  13. gdsdiff/backends/cuda_ctypes.py +1297 -0
  14. gdsdiff/backends/exact.py +424 -0
  15. gdsdiff/backends/geometry_ctypes.py +252 -0
  16. gdsdiff/backends/identity.py +53 -0
  17. gdsdiff/backends/metal.py +198 -0
  18. gdsdiff/backends/metal_ctypes.py +866 -0
  19. gdsdiff/backends/polygon_extract_ctypes.py +233 -0
  20. gdsdiff/backends/structural_ctypes.py +130 -0
  21. gdsdiff/boundary_loops.py +616 -0
  22. gdsdiff/cache.py +544 -0
  23. gdsdiff/canonicalize.py +176 -0
  24. gdsdiff/cli.py +352 -0
  25. gdsdiff/config.py +257 -0
  26. gdsdiff/cuda_setup.py +174 -0
  27. gdsdiff/design_history.py +65 -0
  28. gdsdiff/diagnostics.py +42 -0
  29. gdsdiff/diff_gds.py +66 -0
  30. gdsdiff/exact_diff_markdown.py +979 -0
  31. gdsdiff/exact_oracle.py +649 -0
  32. gdsdiff/extract.py +376 -0
  33. gdsdiff/gds_metadata.py +43 -0
  34. gdsdiff/geometry.py +112 -0
  35. gdsdiff/grid.py +143 -0
  36. gdsdiff/hierarchy.py +215 -0
  37. gdsdiff/hierarchy_extract.py +263 -0
  38. gdsdiff/inventory.py +153 -0
  39. gdsdiff/multiprocessing_support.py +39 -0
  40. gdsdiff/native.py +135 -0
  41. gdsdiff/native_cache.py +265 -0
  42. gdsdiff/native_src/cpu_prefilter.cpp +349 -0
  43. gdsdiff/native_src/gds_boundary_loops.cpp +505 -0
  44. gdsdiff/native_src/gds_geometry_scan.cpp +601 -0
  45. gdsdiff/native_src/gds_polygon_extract.cpp +594 -0
  46. gdsdiff/native_src/gds_structural_scan.cpp +335 -0
  47. gdsdiff/native_src/gdsdiff/CMakeLists.txt +74 -0
  48. gdsdiff/native_src/gdsdiff/core.cpp +191 -0
  49. gdsdiff/native_src/gdsdiff/core.hpp +96 -0
  50. gdsdiff/native_src/gdsdiff/cuda_assignment.cu +304 -0
  51. gdsdiff/native_src/gdsdiff/cuda_assignment.cuh +33 -0
  52. gdsdiff/native_src/gdsdiff/cuda_candidates.cu +133 -0
  53. gdsdiff/native_src/gdsdiff/cuda_candidates.cuh +17 -0
  54. gdsdiff/native_src/gdsdiff/cuda_ctypes.cu +4528 -0
  55. gdsdiff/native_src/gdsdiff/cuda_memory.cu +194 -0
  56. gdsdiff/native_src/gdsdiff/cuda_memory.cuh +34 -0
  57. gdsdiff/native_src/gdsdiff/metal_ctypes.mm +2791 -0
  58. gdsdiff/native_src/gdsdiff/python_bindings.cpp +21 -0
  59. gdsdiff/native_src/gdsdiff/test_core.cpp +93 -0
  60. gdsdiff/native_src/gdsdiff/test_cuda_assignment.cu +109 -0
  61. gdsdiff/native_src/gdsdiff/test_cuda_candidates.cu +94 -0
  62. gdsdiff/native_src/gdsdiff/test_cuda_memory.cu +97 -0
  63. gdsdiff/policy.py +305 -0
  64. gdsdiff/polygon_components.py +860 -0
  65. gdsdiff/profiles.py +152 -0
  66. gdsdiff/py.typed +1 -0
  67. gdsdiff/rect_coverage.py +384 -0
  68. gdsdiff/report.py +354 -0
  69. gdsdiff/schemas/gdsdiff_report-v1.schema.json +92 -0
  70. gdsdiff/semantics.py +75 -0
  71. gdsdiff/source_edge_diff.py +1120 -0
  72. gdsdiff/spatial.py +71 -0
  73. gdsdiff/structural_guard.py +161 -0
  74. gdsdiff/surplus_coverage.py +255 -0
  75. gdsdiff/synthetic_suite.py +1643 -0
  76. gdsdiff/templates.py +709 -0
  77. gdsdiff/tiling.py +153 -0
  78. gdsdiff/windowed_exact.py +270 -0
  79. gdsdiff/windows.py +184 -0
  80. gdsdiff-0.1.0.dist-info/METADATA +135 -0
  81. gdsdiff-0.1.0.dist-info/RECORD +85 -0
  82. gdsdiff-0.1.0.dist-info/WHEEL +5 -0
  83. gdsdiff-0.1.0.dist-info/entry_points.txt +4 -0
  84. gdsdiff-0.1.0.dist-info/licenses/LICENSE +674 -0
  85. gdsdiff-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,594 @@
1
+ #include <algorithm>
2
+ #include <cstdint>
3
+ #include <cstdlib>
4
+ #include <cstring>
5
+ #include <fstream>
6
+ #include <limits>
7
+ #include <map>
8
+ #include <set>
9
+ #include <stdexcept>
10
+ #include <string>
11
+ #include <utility>
12
+ #include <vector>
13
+
14
+ namespace {
15
+
16
+ thread_local std::string g_last_error;
17
+
18
+ struct Point {
19
+ std::int64_t x = 0;
20
+ std::int64_t y = 0;
21
+ };
22
+
23
+ struct BBox {
24
+ std::int64_t x0 = 0;
25
+ std::int64_t y0 = 0;
26
+ std::int64_t x1 = 0;
27
+ std::int64_t y1 = 0;
28
+ };
29
+
30
+ struct Polygon {
31
+ std::int32_t layer = 0;
32
+ std::int32_t datatype = 0;
33
+ std::vector<Point> points;
34
+ BBox bbox;
35
+ std::uint32_t flags = 0;
36
+ };
37
+
38
+ struct Reference {
39
+ std::string child;
40
+ std::uint64_t columns = 1;
41
+ std::uint64_t rows = 1;
42
+ std::int64_t ox = 0;
43
+ std::int64_t oy = 0;
44
+ std::int64_t cx = 0;
45
+ std::int64_t cy = 0;
46
+ std::int64_t rx = 0;
47
+ std::int64_t ry = 0;
48
+ };
49
+
50
+ struct Cell {
51
+ std::vector<Polygon> polygons;
52
+ std::vector<Reference> references;
53
+ };
54
+
55
+ std::uint16_t read_u16(const unsigned char* p) {
56
+ return static_cast<std::uint16_t>((static_cast<std::uint16_t>(p[0]) << 8) | p[1]);
57
+ }
58
+
59
+ std::int16_t read_i16(const unsigned char* p) {
60
+ return static_cast<std::int16_t>(read_u16(p));
61
+ }
62
+
63
+ std::int32_t read_i32(const unsigned char* p) {
64
+ return (static_cast<std::int32_t>(p[0]) << 24) | (static_cast<std::int32_t>(p[1]) << 16)
65
+ | (static_cast<std::int32_t>(p[2]) << 8) | static_cast<std::int32_t>(p[3]);
66
+ }
67
+
68
+ std::string decode_string(const unsigned char* data, std::size_t size) {
69
+ while (size && data[size - 1] == 0) {
70
+ --size;
71
+ }
72
+ return std::string(reinterpret_cast<const char*>(data), size);
73
+ }
74
+
75
+ std::vector<unsigned char> read_file(const char* path) {
76
+ std::ifstream in(path, std::ios::binary | std::ios::ate);
77
+ if (!in) {
78
+ throw std::runtime_error("unable to open GDS file");
79
+ }
80
+ const auto size = in.tellg();
81
+ if (size < 0) {
82
+ throw std::runtime_error("unable to determine GDS file size");
83
+ }
84
+ std::vector<unsigned char> data(static_cast<std::size_t>(size));
85
+ in.seekg(0, std::ios::beg);
86
+ if (!data.empty()) {
87
+ in.read(reinterpret_cast<char*>(data.data()), static_cast<std::streamsize>(data.size()));
88
+ if (!in) {
89
+ throw std::runtime_error("unable to read GDS file");
90
+ }
91
+ }
92
+ return data;
93
+ }
94
+
95
+ std::vector<Point> parse_xy(const unsigned char* payload, std::size_t payload_size) {
96
+ if (payload_size % 8 != 0) {
97
+ throw std::runtime_error("invalid XY payload length");
98
+ }
99
+ std::vector<Point> points;
100
+ points.reserve(payload_size / 8);
101
+ for (std::size_t i = 0; i < payload_size; i += 8) {
102
+ points.push_back(Point{read_i32(payload + i), read_i32(payload + i + 4)});
103
+ }
104
+ return points;
105
+ }
106
+
107
+ std::vector<Point> normalize_points(std::vector<Point> points) {
108
+ if (points.size() > 1 && points.front().x == points.back().x && points.front().y == points.back().y) {
109
+ points.pop_back();
110
+ }
111
+ std::vector<Point> normalized;
112
+ normalized.reserve(points.size());
113
+ for (const auto& point : points) {
114
+ if (normalized.empty() || normalized.back().x != point.x || normalized.back().y != point.y) {
115
+ normalized.push_back(point);
116
+ }
117
+ }
118
+ if (normalized.size() > 1 && normalized.front().x == normalized.back().x && normalized.front().y == normalized.back().y) {
119
+ normalized.pop_back();
120
+ }
121
+ return normalized;
122
+ }
123
+
124
+ BBox bbox_from_points(const std::vector<Point>& points) {
125
+ if (points.empty()) {
126
+ throw std::runtime_error("cannot compute bbox of empty polygon");
127
+ }
128
+ BBox bbox{points[0].x, points[0].y, points[0].x, points[0].y};
129
+ for (const auto& point : points) {
130
+ bbox.x0 = std::min(bbox.x0, point.x);
131
+ bbox.y0 = std::min(bbox.y0, point.y);
132
+ bbox.x1 = std::max(bbox.x1, point.x);
133
+ bbox.y1 = std::max(bbox.y1, point.y);
134
+ }
135
+ return bbox;
136
+ }
137
+
138
+ std::uint32_t polygon_flags(const std::vector<Point>& points) {
139
+ std::uint32_t flags = 0;
140
+ std::set<std::pair<std::int64_t, std::int64_t>> unique_points;
141
+ for (const auto& point : points) {
142
+ unique_points.insert({point.x, point.y});
143
+ }
144
+ if (unique_points.size() < 3) {
145
+ flags |= 1u;
146
+ }
147
+ __int128 twice_area = 0;
148
+ if (points.size() >= 3) {
149
+ for (std::size_t index = 0; index < points.size(); ++index) {
150
+ const auto& point = points[index];
151
+ const auto& next = points[(index + 1) % points.size()];
152
+ twice_area += static_cast<__int128>(point.x) * static_cast<__int128>(next.y)
153
+ - static_cast<__int128>(next.x) * static_cast<__int128>(point.y);
154
+ }
155
+ }
156
+ if (twice_area == 0) {
157
+ flags |= 2u;
158
+ }
159
+ return flags;
160
+ }
161
+
162
+ std::uint64_t fnv1a_u64(std::uint64_t hash, std::uint64_t value) {
163
+ constexpr std::uint64_t prime = 1099511628211ull;
164
+ for (int i = 0; i < 8; ++i) {
165
+ hash ^= (value >> (i * 8)) & 0xffu;
166
+ hash *= prime;
167
+ }
168
+ return hash;
169
+ }
170
+
171
+ bool point_less(const Point& left, const Point& right) {
172
+ return left.x < right.x || (left.x == right.x && left.y < right.y);
173
+ }
174
+
175
+ std::size_t sequence_index(std::size_t count, std::size_t candidate, std::size_t step, bool forward) {
176
+ if (forward) {
177
+ return (candidate + step) % count;
178
+ }
179
+ return (candidate + count - (step % count)) % count;
180
+ }
181
+
182
+ bool sequence_less(
183
+ const std::vector<Point>& points,
184
+ std::size_t left_candidate,
185
+ bool left_forward,
186
+ std::size_t right_candidate,
187
+ bool right_forward) {
188
+ const std::size_t count = points.size();
189
+ for (std::size_t step = 0; step < count; ++step) {
190
+ const auto& left = points[sequence_index(count, left_candidate, step, left_forward)];
191
+ const auto& right = points[sequence_index(count, right_candidate, step, right_forward)];
192
+ if (point_less(left, right)) {
193
+ return true;
194
+ }
195
+ if (point_less(right, left)) {
196
+ return false;
197
+ }
198
+ }
199
+ return false;
200
+ }
201
+
202
+ bool is_axis_aligned_bbox_rectangle(const Polygon& polygon) {
203
+ if (polygon.points.size() != 4 || polygon.bbox.x0 >= polygon.bbox.x1 || polygon.bbox.y0 >= polygon.bbox.y1) {
204
+ return false;
205
+ }
206
+ bool corners[4] = {false, false, false, false};
207
+ for (const auto& point : polygon.points) {
208
+ int corner = -1;
209
+ if (point.x == polygon.bbox.x0 && point.y == polygon.bbox.y0) corner = 0;
210
+ if (point.x == polygon.bbox.x0 && point.y == polygon.bbox.y1) corner = 1;
211
+ if (point.x == polygon.bbox.x1 && point.y == polygon.bbox.y0) corner = 2;
212
+ if (point.x == polygon.bbox.x1 && point.y == polygon.bbox.y1) corner = 3;
213
+ if (corner < 0 || corners[corner]) {
214
+ return false;
215
+ }
216
+ corners[corner] = true;
217
+ }
218
+ return corners[0] && corners[1] && corners[2] && corners[3];
219
+ }
220
+
221
+ std::pair<std::uint64_t, std::uint64_t> canonical_polygon_hash(const Polygon& polygon) {
222
+ std::uint64_t h0 = 1469598103934665603ull;
223
+ std::uint64_t h1 = 1099511628211ull;
224
+ const auto& points = polygon.points;
225
+ if (polygon.flags == 0 && is_axis_aligned_bbox_rectangle(polygon)) {
226
+ h0 = fnv1a_u64(fnv1a_u64(h0, 2), 0);
227
+ h0 = fnv1a_u64(fnv1a_u64(h0, static_cast<std::uint64_t>(polygon.bbox.x0)), static_cast<std::uint64_t>(polygon.bbox.y0));
228
+ h0 = fnv1a_u64(fnv1a_u64(h0, static_cast<std::uint64_t>(polygon.bbox.x1)), static_cast<std::uint64_t>(polygon.bbox.y1));
229
+ h1 = fnv1a_u64(fnv1a_u64(h1, static_cast<std::uint64_t>(polygon.bbox.y1)), static_cast<std::uint64_t>(polygon.bbox.x1));
230
+ h1 = fnv1a_u64(fnv1a_u64(h1, static_cast<std::uint64_t>(polygon.bbox.y0)), static_cast<std::uint64_t>(polygon.bbox.x0));
231
+ return {h0, h1};
232
+ }
233
+ std::size_t best_candidate = 0;
234
+ bool best_forward = true;
235
+ if (polygon.flags == 0 && !points.empty()) {
236
+ for (std::size_t candidate = 1; candidate < points.size(); ++candidate) {
237
+ if (sequence_less(points, candidate, true, best_candidate, best_forward)) {
238
+ best_candidate = candidate;
239
+ best_forward = true;
240
+ }
241
+ }
242
+ for (std::size_t candidate = 0; candidate < points.size(); ++candidate) {
243
+ if (sequence_less(points, candidate, false, best_candidate, best_forward)) {
244
+ best_candidate = candidate;
245
+ best_forward = false;
246
+ }
247
+ }
248
+ }
249
+ for (std::size_t step = 0; step < points.size(); ++step) {
250
+ const std::size_t index = polygon.flags == 0 ? sequence_index(points.size(), best_candidate, step, best_forward) : step;
251
+ const auto& point = points[index];
252
+ const auto x = static_cast<std::uint64_t>(point.x);
253
+ const auto y = static_cast<std::uint64_t>(point.y);
254
+ h0 = fnv1a_u64(fnv1a_u64(h0, x), y);
255
+ h1 = fnv1a_u64(fnv1a_u64(h1, y), x);
256
+ }
257
+ return {h0, h1};
258
+ }
259
+
260
+ std::map<std::string, Cell> parse_gds(const char* path) {
261
+ const auto data = read_file(path);
262
+ std::map<std::string, Cell> cells;
263
+ std::size_t offset = 0;
264
+ std::string current_name;
265
+ Cell current_cell;
266
+ int element_kind = -1;
267
+ std::int32_t pending_layer = 0;
268
+ std::int32_t pending_datatype = 0;
269
+ std::string pending_sname;
270
+ Reference pending_reference;
271
+ std::vector<Point> pending_xy;
272
+
273
+ while (offset + 4 <= data.size()) {
274
+ const std::uint16_t record_len = read_u16(data.data() + offset);
275
+ const int record_type = data[offset + 2];
276
+ if (record_len < 4 || offset + record_len > data.size()) {
277
+ throw std::runtime_error("invalid GDS record length");
278
+ }
279
+ const unsigned char* payload = data.data() + offset + 4;
280
+ const std::size_t payload_size = record_len - 4;
281
+ offset += record_len;
282
+
283
+ switch (record_type) {
284
+ case 0x05: // BGNSTR
285
+ current_name.clear();
286
+ current_cell = Cell{};
287
+ break;
288
+ case 0x06: // STRNAME
289
+ current_name = decode_string(payload, payload_size);
290
+ break;
291
+ case 0x08: // BOUNDARY
292
+ element_kind = record_type;
293
+ pending_layer = 0;
294
+ pending_datatype = 0;
295
+ pending_xy.clear();
296
+ break;
297
+ case 0x09: // PATH
298
+ throw std::runtime_error("PATH records are not supported by native polygon extraction");
299
+ case 0x0A: // SREF
300
+ case 0x0B: // AREF
301
+ element_kind = record_type;
302
+ pending_reference = Reference{};
303
+ pending_sname.clear();
304
+ pending_xy.clear();
305
+ break;
306
+ case 0x0D: // LAYER
307
+ if (element_kind == 0x08 && payload_size >= 2) {
308
+ pending_layer = read_i16(payload);
309
+ }
310
+ break;
311
+ case 0x0E: // DATATYPE
312
+ if (element_kind == 0x08 && payload_size >= 2) {
313
+ pending_datatype = read_i16(payload);
314
+ }
315
+ break;
316
+ case 0x12: // SNAME
317
+ if (element_kind == 0x0A || element_kind == 0x0B) {
318
+ pending_sname = decode_string(payload, payload_size);
319
+ }
320
+ break;
321
+ case 0x13: // COLROW
322
+ if (element_kind == 0x0B) {
323
+ if (payload_size < 4) {
324
+ throw std::runtime_error("invalid COLROW record");
325
+ }
326
+ const auto columns = read_i16(payload);
327
+ const auto rows = read_i16(payload + 2);
328
+ if (columns <= 0 || rows <= 0) {
329
+ throw std::runtime_error("invalid AREF repetition");
330
+ }
331
+ pending_reference.columns = static_cast<std::uint64_t>(columns);
332
+ pending_reference.rows = static_cast<std::uint64_t>(rows);
333
+ }
334
+ break;
335
+ case 0x10: // XY
336
+ pending_xy = parse_xy(payload, payload_size);
337
+ break;
338
+ case 0x1A: // STRANS
339
+ case 0x1B: // MAG
340
+ case 0x1C: // ANGLE
341
+ if (element_kind == 0x0A || element_kind == 0x0B) {
342
+ throw std::runtime_error("transformed references are not supported by native polygon extraction");
343
+ }
344
+ break;
345
+ case 0x11: // ENDEL
346
+ if (element_kind == 0x08) {
347
+ auto normalized = normalize_points(pending_xy);
348
+ if (!normalized.empty()) {
349
+ current_cell.polygons.push_back(Polygon{
350
+ pending_layer,
351
+ pending_datatype,
352
+ normalized,
353
+ bbox_from_points(normalized),
354
+ polygon_flags(normalized),
355
+ });
356
+ }
357
+ } else if (element_kind == 0x0A || element_kind == 0x0B) {
358
+ if (pending_sname.empty()) {
359
+ throw std::runtime_error("reference missing SNAME");
360
+ }
361
+ if (pending_xy.empty()) {
362
+ throw std::runtime_error("reference missing XY");
363
+ }
364
+ pending_reference.child = pending_sname;
365
+ pending_reference.ox = pending_xy[0].x;
366
+ pending_reference.oy = pending_xy[0].y;
367
+ pending_reference.cx = pending_xy.size() > 1 ? pending_xy[1].x : pending_xy[0].x;
368
+ pending_reference.cy = pending_xy.size() > 1 ? pending_xy[1].y : pending_xy[0].y;
369
+ pending_reference.rx = pending_xy.size() > 2 ? pending_xy[2].x : pending_xy[0].x;
370
+ pending_reference.ry = pending_xy.size() > 2 ? pending_xy[2].y : pending_xy[0].y;
371
+ current_cell.references.push_back(pending_reference);
372
+ }
373
+ element_kind = -1;
374
+ pending_sname.clear();
375
+ pending_xy.clear();
376
+ break;
377
+ case 0x07: // ENDSTR
378
+ if (current_name.empty()) {
379
+ throw std::runtime_error("cell missing STRNAME");
380
+ }
381
+ cells[current_name] = current_cell;
382
+ current_name.clear();
383
+ current_cell = Cell{};
384
+ break;
385
+ default:
386
+ break;
387
+ }
388
+ }
389
+ if (offset != data.size()) {
390
+ throw std::runtime_error("trailing partial GDS record");
391
+ }
392
+ return cells;
393
+ }
394
+
395
+ std::string select_top(const std::map<std::string, Cell>& cells, const char* requested_top) {
396
+ if (requested_top && requested_top[0]) {
397
+ if (!cells.count(requested_top)) {
398
+ throw std::runtime_error("requested top cell not found");
399
+ }
400
+ return requested_top;
401
+ }
402
+ std::set<std::string> referenced;
403
+ for (const auto& item : cells) {
404
+ for (const auto& reference : item.second.references) {
405
+ referenced.insert(reference.child);
406
+ }
407
+ }
408
+ std::vector<std::string> tops;
409
+ for (const auto& item : cells) {
410
+ if (!referenced.count(item.first)) {
411
+ tops.push_back(item.first);
412
+ }
413
+ }
414
+ if (tops.size() != 1) {
415
+ throw std::runtime_error("expected exactly one top cell");
416
+ }
417
+ return tops.front();
418
+ }
419
+
420
+ std::int64_t div_floor(std::int64_t numerator, std::uint64_t denominator) {
421
+ if (denominator == 0) {
422
+ throw std::runtime_error("zero denominator");
423
+ }
424
+ if (numerator >= 0) {
425
+ return numerator / static_cast<std::int64_t>(denominator);
426
+ }
427
+ return -static_cast<std::int64_t>((static_cast<std::uint64_t>(-numerator) + denominator - 1) / denominator);
428
+ }
429
+
430
+ void append_shifted_polygon(std::vector<Polygon>& out, const Polygon& polygon, std::int64_t dx, std::int64_t dy) {
431
+ Polygon shifted;
432
+ shifted.layer = polygon.layer;
433
+ shifted.datatype = polygon.datatype;
434
+ shifted.flags = polygon.flags;
435
+ shifted.points.reserve(polygon.points.size());
436
+ for (const auto& point : polygon.points) {
437
+ shifted.points.push_back(Point{point.x + dx, point.y + dy});
438
+ }
439
+ shifted.bbox = BBox{polygon.bbox.x0 + dx, polygon.bbox.y0 + dy, polygon.bbox.x1 + dx, polygon.bbox.y1 + dy};
440
+ out.push_back(std::move(shifted));
441
+ }
442
+
443
+ void flatten_cell(
444
+ const std::string& name,
445
+ const std::map<std::string, Cell>& cells,
446
+ std::vector<Polygon>& out,
447
+ std::set<std::string>& visiting,
448
+ std::int64_t dx,
449
+ std::int64_t dy) {
450
+ if (visiting.count(name)) {
451
+ throw std::runtime_error("cell reference cycle detected");
452
+ }
453
+ const auto found = cells.find(name);
454
+ if (found == cells.end()) {
455
+ throw std::runtime_error("unresolved reference");
456
+ }
457
+ visiting.insert(name);
458
+ for (const auto& polygon : found->second.polygons) {
459
+ append_shifted_polygon(out, polygon, dx, dy);
460
+ }
461
+ for (const auto& reference : found->second.references) {
462
+ const std::uint64_t columns = std::max<std::uint64_t>(1, reference.columns);
463
+ const std::uint64_t rows = std::max<std::uint64_t>(1, reference.rows);
464
+ const std::int64_t col_dx = div_floor(reference.cx - reference.ox, columns);
465
+ const std::int64_t col_dy = div_floor(reference.cy - reference.oy, columns);
466
+ const std::int64_t row_dx = div_floor(reference.rx - reference.ox, rows);
467
+ const std::int64_t row_dy = div_floor(reference.ry - reference.oy, rows);
468
+ for (std::uint64_t column = 0; column < columns; ++column) {
469
+ for (std::uint64_t row = 0; row < rows; ++row) {
470
+ const std::int64_t child_dx = dx + reference.ox + static_cast<std::int64_t>(column) * col_dx
471
+ + static_cast<std::int64_t>(row) * row_dx;
472
+ const std::int64_t child_dy = dy + reference.oy + static_cast<std::int64_t>(column) * col_dy
473
+ + static_cast<std::int64_t>(row) * row_dy;
474
+ flatten_cell(reference.child, cells, out, visiting, child_dx, child_dy);
475
+ }
476
+ }
477
+ }
478
+ visiting.erase(name);
479
+ }
480
+
481
+ } // namespace
482
+
483
+ extern "C" {
484
+
485
+ struct GdsPolygonExtractResult {
486
+ std::uint64_t polygon_count;
487
+ std::uint64_t vertex_count;
488
+ std::int64_t* vertices_xy;
489
+ std::uint64_t* polygon_offsets;
490
+ std::int32_t* layers;
491
+ std::int32_t* datatypes;
492
+ std::int64_t* bboxes;
493
+ std::uint32_t* flags;
494
+ std::uint64_t* source_ids;
495
+ std::uint64_t* hashes;
496
+ };
497
+
498
+ const char* gds_polygon_extract_last_error() {
499
+ return g_last_error.c_str();
500
+ }
501
+
502
+ void gds_polygon_extract_free(GdsPolygonExtractResult* result) {
503
+ if (!result) {
504
+ return;
505
+ }
506
+ std::free(result->vertices_xy);
507
+ std::free(result->polygon_offsets);
508
+ std::free(result->layers);
509
+ std::free(result->datatypes);
510
+ std::free(result->bboxes);
511
+ std::free(result->flags);
512
+ std::free(result->source_ids);
513
+ std::free(result->hashes);
514
+ *result = GdsPolygonExtractResult{};
515
+ }
516
+
517
+ int gds_polygon_extract(const char* path, const char* top_name, GdsPolygonExtractResult* out) {
518
+ try {
519
+ if (!path || !out) {
520
+ throw std::runtime_error("invalid arguments");
521
+ }
522
+ *out = GdsPolygonExtractResult{};
523
+ const auto cells = parse_gds(path);
524
+ if (cells.empty()) {
525
+ throw std::runtime_error("no cells found");
526
+ }
527
+ const auto top = select_top(cells, top_name);
528
+ std::vector<Polygon> polygons;
529
+ std::set<std::string> visiting;
530
+ flatten_cell(top, cells, polygons, visiting, 0, 0);
531
+
532
+ std::uint64_t vertex_count = 0;
533
+ for (const auto& polygon : polygons) {
534
+ if (vertex_count > std::numeric_limits<std::uint64_t>::max() - polygon.points.size()) {
535
+ throw std::overflow_error("vertex count overflow");
536
+ }
537
+ vertex_count += static_cast<std::uint64_t>(polygon.points.size());
538
+ }
539
+ out->polygon_count = static_cast<std::uint64_t>(polygons.size());
540
+ out->vertex_count = vertex_count;
541
+ out->polygon_offsets = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * (polygons.size() + 1)));
542
+ out->vertices_xy = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * vertex_count * 2));
543
+ out->layers = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * polygons.size()));
544
+ out->datatypes = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * polygons.size()));
545
+ out->bboxes = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * polygons.size() * 4));
546
+ out->flags = static_cast<std::uint32_t*>(std::malloc(sizeof(std::uint32_t) * polygons.size()));
547
+ out->source_ids = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * polygons.size()));
548
+ out->hashes = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * polygons.size() * 2));
549
+ if (!out->polygon_offsets || (!out->vertices_xy && vertex_count) || (!out->layers && !polygons.empty())
550
+ || (!out->datatypes && !polygons.empty()) || (!out->bboxes && !polygons.empty())
551
+ || (!out->flags && !polygons.empty()) || (!out->source_ids && !polygons.empty())
552
+ || (!out->hashes && !polygons.empty())) {
553
+ throw std::bad_alloc();
554
+ }
555
+ std::uint64_t vertex_offset = 0;
556
+ out->polygon_offsets[0] = 0;
557
+ for (std::size_t index = 0; index < polygons.size(); ++index) {
558
+ const auto& polygon = polygons[index];
559
+ out->layers[index] = polygon.layer;
560
+ out->datatypes[index] = polygon.datatype;
561
+ out->bboxes[index * 4 + 0] = polygon.bbox.x0;
562
+ out->bboxes[index * 4 + 1] = polygon.bbox.y0;
563
+ out->bboxes[index * 4 + 2] = polygon.bbox.x1;
564
+ out->bboxes[index * 4 + 3] = polygon.bbox.y1;
565
+ out->flags[index] = polygon.flags;
566
+ out->source_ids[index] = static_cast<std::uint64_t>(index);
567
+ const auto hash = canonical_polygon_hash(polygon);
568
+ out->hashes[index * 2 + 0] = hash.first;
569
+ out->hashes[index * 2 + 1] = hash.second;
570
+ for (const auto& point : polygon.points) {
571
+ out->vertices_xy[vertex_offset * 2 + 0] = point.x;
572
+ out->vertices_xy[vertex_offset * 2 + 1] = point.y;
573
+ ++vertex_offset;
574
+ }
575
+ out->polygon_offsets[index + 1] = vertex_offset;
576
+ }
577
+ g_last_error.clear();
578
+ return 0;
579
+ } catch (const std::exception& exc) {
580
+ g_last_error = exc.what();
581
+ if (out) {
582
+ gds_polygon_extract_free(out);
583
+ }
584
+ return 1;
585
+ } catch (...) {
586
+ g_last_error = "unknown native polygon extract error";
587
+ if (out) {
588
+ gds_polygon_extract_free(out);
589
+ }
590
+ return 1;
591
+ }
592
+ }
593
+
594
+ } // extern "C"