tg_geometry 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -79
- data/README.md +82 -191
- data/Rakefile +3 -3
- data/benchmark/falcon_concurrency.rb +1 -1
- data/benchmark/feature_source.rb +92 -0
- data/docs/ARCHITECTURE.md +29 -107
- data/docs/BENCHMARKING.md +20 -1
- data/docs/CASUAL_EXAMPLE.md +71 -458
- data/docs/CONCURRENCY.md +13 -7
- data/docs/ERROR_HANDLING.md +30 -0
- data/docs/FEATURE_SOURCE.md +166 -0
- data/docs/LIMITATIONS.md +11 -50
- data/docs/MEMORY_OWNERSHIP.md +20 -2
- data/ext/tg_geometry/extconf.rb +46 -4
- data/ext/tg_geometry/tg_geometry_ext.c +2453 -150
- data/ext/tg_geometry/tg_geometry_vendor_json.c +17 -0
- data/ext/tg_geometry/tg_geometry_vendor_tg.c +3 -0
- data/ext/tg_geometry/vendor/.vendored +8 -2
- data/ext/tg_geometry/vendor/json/LICENSE +20 -0
- data/ext/tg_geometry/vendor/json/VERSION +3 -0
- data/ext/tg_geometry/vendor/json/json.c +1024 -0
- data/ext/tg_geometry/vendor/json/json.h +207 -0
- data/lib/tg/geometry/registry.rb +3 -3
- data/lib/tg/geometry/version.rb +1 -1
- data/script/vendor_libs.rb +22 -6
- data/spec/{expansion_a_auto_strategy_spec.rb → auto_strategy_spec.rb} +1 -1
- data/spec/{block_12_batch_packed_spec.rb → batch_packed_spec.rb} +1 -1
- data/spec/{block_20_concurrency_spec.rb → concurrency_spec.rb} +1 -1
- data/spec/{block_13_error_hardening_spec.rb → error_hardening_spec.rb} +1 -1
- data/spec/feature_source_nogvl_spec.rb +51 -0
- data/spec/feature_source_spec.rb +268 -0
- data/spec/{expansion_d_format_coverage_spec.rb → format_coverage_spec.rb} +1 -1
- data/spec/{block_20_fuzz_spec.rb → fuzz_spec.rb} +1 -1
- data/spec/{block_4_geom_api_spec.rb → geom_api_spec.rb} +1 -1
- data/spec/{block_3_geom_parse_spec.rb → geom_parse_spec.rb} +1 -1
- data/spec/{block_8_index_borrowed_geometry_spec.rb → index_borrowed_geometry_spec.rb} +1 -1
- data/spec/{block_6_index_build_spec.rb → index_build_spec.rb} +2 -2
- data/spec/{block_9_flat_query_spec.rb → index_flat_query_spec.rb} +1 -1
- data/spec/{block_7_index_owned_geometry_spec.rb → index_owned_geometry_spec.rb} +1 -1
- data/spec/{block_10_rtree_strategy_spec.rb → index_rtree_accounting_spec.rb} +1 -1
- data/spec/{block_11_rtree_order_spec.rb → index_rtree_order_spec.rb} +1 -1
- data/spec/{block_1_skeleton_spec.rb → load_and_errors_spec.rb} +1 -1
- data/spec/{expansion_e_low_level_geometry_spec.rb → low_level_geometry_spec.rb} +1 -1
- data/spec/{block_14_memory_gc_hardening_spec.rb → memory_gc_spec.rb} +1 -1
- data/spec/{expansion_i_ractor_spec.rb → ractor_spec.rb} +1 -1
- data/spec/{block_5_rect_api_spec.rb → rect_api_spec.rb} +1 -1
- data/spec/{expansion_b_registry_spec.rb → registry_spec.rb} +1 -1
- data/spec/{expansion_j_full_tg_api_coverage_spec.rb → tg_api_coverage_spec.rb} +1 -1
- data/spec/{block_2_vendor_spec.rb → vendor_sources_spec.rb} +4 -4
- metadata +39 -38
- data/docs/ACTIVE_RECORD.md +0 -26
- data/docs/AUTO_STRATEGY.md +0 -15
- data/docs/EXPANSION_E_TO_H_STATUS.md +0 -51
- data/docs/FORMAT_COVERAGE.md +0 -23
- data/docs/FULL_TG_API_COVERAGE.md +0 -109
- data/docs/LOW_LEVEL_GEOMETRY.md +0 -121
- data/docs/RACTOR.md +0 -40
- data/docs/REGISTRY.md +0 -37
- data/docs/RELEASE_CHECKLIST.md +0 -39
- /data/spec/{expansion_c_active_record_source_spec.rb → active_record_source_spec.rb} +0 -0
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,130 +1,52 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Architecture
|
|
2
2
|
|
|
3
|
-
`tg_geometry` is a Ruby C extension
|
|
3
|
+
`tg_geometry` is a Ruby C extension around vendored `tidwall/tg`, `tidwall/rtree.c`, and `tidwall/json.c`.
|
|
4
4
|
|
|
5
|
-
The
|
|
5
|
+
The public Ruby API is under `TG::Geometry`. The native extension owns the hot paths for parsing, predicates, indexing, batch point lookup, and GeoJSON FeatureCollection extraction.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- exact planar geometry predicates;
|
|
9
|
-
- immutable rectangles for bounding boxes and query windows;
|
|
10
|
-
- immutable geofencing-oriented indexes that return user ids;
|
|
11
|
-
- flat and rtree collection-level search strategies;
|
|
12
|
-
- native-endian packed point batches for high-throughput same-process calls;
|
|
13
|
-
- read-only borrowed low-level Line/Ring/Polygon wrappers;
|
|
14
|
-
- grouped TG API coverage for predicates, accessors, point/empty constructors, Segment values, and GeometryCollection children.
|
|
7
|
+
## Immutable geometry wrappers
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
`TG::Geometry::Geom` wraps one TG geometry pointer. It is frozen before being returned to Ruby and does not expose manual free, close, detach, or replacement APIs.
|
|
17
10
|
|
|
18
|
-
|
|
11
|
+
Owned wrappers call `tg_geom_free` from their typed-data free function and adjust Ruby GC memory pressure using the cached TG memory size.
|
|
19
12
|
|
|
20
|
-
|
|
13
|
+
Borrowed child wrappers, where exposed, keep the parent `Geom` alive through a Ruby owner reference and do not free the borrowed TG pointer directly.
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
require "tg/geometry"
|
|
24
|
-
```
|
|
15
|
+
## Rect
|
|
25
16
|
|
|
26
|
-
|
|
17
|
+
`TG::Geometry::Rect` is a small immutable Ruby object for bounding boxes and rectangle queries. It rejects non-finite coordinates and invalid coordinate order.
|
|
27
18
|
|
|
28
|
-
##
|
|
19
|
+
## Immutable Index
|
|
29
20
|
|
|
30
|
-
|
|
21
|
+
`TG::Geometry::Index` is built once and then read-only. Reloading is done by building a new index and swapping the Ruby reference.
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
- `ext/tg_geometry/tg_geometry_vendor_rtree.c` includes `vendor/rtree/rtree.c`.
|
|
23
|
+
Index entries store:
|
|
34
24
|
|
|
35
|
-
|
|
25
|
+
- Ruby id;
|
|
26
|
+
- optional Ruby geometry owner for borrowed `via: :geom` entries;
|
|
27
|
+
- TG geometry pointer;
|
|
28
|
+
- cached bbox;
|
|
29
|
+
- insertion ordinal;
|
|
30
|
+
- ownership flag and native byte count.
|
|
36
31
|
|
|
37
|
-
|
|
32
|
+
For owned inputs (`via: :geojson` / `via: :wkb`), the index owns the parsed TG geometry. For borrowed inputs (`via: :geom`), the index marks the source `Geom` wrapper so the native pointer remains valid.
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
## Flat and rtree strategies
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
`:flat` scans entries in insertion order.
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
- objects are created only by parse APIs, safe constructors, or internal borrowed child wrappers;
|
|
45
|
-
- the native pointer is never replaced;
|
|
46
|
-
- there is no `close!`, `free!`, `detach!`, or mutation API;
|
|
47
|
-
- parsed objects are frozen before being returned.
|
|
38
|
+
`:rtree` uses `tidwall/rtree.c` as a bbox prefilter. Query results are still returned in insertion order, not rtree traversal order. Every bbox/rtree candidate is filtered with exact TG geometry predicates before returning ids.
|
|
48
39
|
|
|
49
|
-
|
|
40
|
+
Rtree memory is accounted exactly through a custom malloc/free allocator with per-allocation headers.
|
|
50
41
|
|
|
51
|
-
##
|
|
42
|
+
## FeatureSource
|
|
52
43
|
|
|
53
|
-
`TG::Geometry::
|
|
44
|
+
`TG::Geometry::FeatureSource` reads a full GeoJSON FeatureCollection into an owned C buffer, validates it with `json_validn`, traverses it with `json.c`, and extracts raw geometry/properties ranges while the buffer is alive.
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
- `center`;
|
|
57
|
-
- `intersects?`;
|
|
58
|
-
- `contains_point?`;
|
|
59
|
-
- expansion methods returning new Rect objects.
|
|
46
|
+
`read_entries_*` and `read_features_*` copy returned geometry/properties strings into Ruby strings before the source buffer is freed.
|
|
60
47
|
|
|
61
|
-
`
|
|
48
|
+
`build_index_*` parses geometry ranges directly into native index entries and does not allocate Ruby geometry strings.
|
|
62
49
|
|
|
63
|
-
##
|
|
50
|
+
## Concurrency
|
|
64
51
|
|
|
65
|
-
`
|
|
66
|
-
|
|
67
|
-
```ruby
|
|
68
|
-
index = TG::Geometry::Index.build(
|
|
69
|
-
[[id1, object1], [id2, object2]],
|
|
70
|
-
via: :geojson,
|
|
71
|
-
strategy: :rtree,
|
|
72
|
-
predicate: :covers,
|
|
73
|
-
geometry_index: :ystripes
|
|
74
|
-
)
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Accepted `via:` modes:
|
|
78
|
-
|
|
79
|
-
- `:geom` borrows native geometry from existing `TG::Geometry::Geom` wrappers and marks the owner Ruby objects;
|
|
80
|
-
- `:geojson` parses entry strings into Index-owned TG geometries;
|
|
81
|
-
- `:wkb` parses entry strings as raw WKB bytes into Index-owned TG geometries.
|
|
82
|
-
|
|
83
|
-
Accepted strategies:
|
|
84
|
-
|
|
85
|
-
- `:flat` scans entries in insertion order with bbox prefiltering and exact TG geometry filtering;
|
|
86
|
-
- `:rtree` uses vendored `rtree.c` over entry bboxes, then applies exact TG geometry filtering.
|
|
87
|
-
|
|
88
|
-
`strategy: :auto` is not implemented in the first public release. Use explicit `:flat` or `:rtree` and validate with repository benchmarks.
|
|
89
|
-
|
|
90
|
-
## Result order
|
|
91
|
-
|
|
92
|
-
Insertion order is public behavior.
|
|
93
|
-
|
|
94
|
-
Each entry stores a unique `ordinal`. Flat strategy naturally scans entries in ordinal order. Rtree strategy uses rtree only as a candidate prefilter; candidate marks are local to the query and results are emitted by scanning entries in ordinal order. Rtree traversal order never leaks into Ruby results.
|
|
95
|
-
|
|
96
|
-
## Point predicate implementation
|
|
97
|
-
|
|
98
|
-
Point queries allocate a temporary TG point geometry and use exact TG predicates:
|
|
99
|
-
|
|
100
|
-
- `:covers` calls `tg_geom_covers(entry_geom, point_geom)`;
|
|
101
|
-
- `:contains` calls `tg_geom_contains(entry_geom, point_geom)`.
|
|
102
|
-
|
|
103
|
-
This is intentionally not the fastest possible point path. The first release chooses exact `covers` / `contains` semantics over a no-allocation shortcut. A faster path such as `tg_geom_intersects_xy` can only replace it after boundary and hole-boundary equivalence tests plus benchmarks are added.
|
|
104
|
-
|
|
105
|
-
## Reload pattern
|
|
106
|
-
|
|
107
|
-
The intended application reload pattern is atomic reference replacement:
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
new_index = TG::Geometry::Index.build(entries, via: :geojson, strategy: :rtree)
|
|
111
|
-
@index = new_index
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Old readers keep using the old immutable object until they release it. New readers see the new object after the Ruby reference swap. There is no in-place reload, mutation, add, delete, or builder API in the first release.
|
|
115
|
-
|
|
116
|
-
## Expansion Blocks A-E and I-J
|
|
117
|
-
|
|
118
|
-
Expansion Block A (`strategy: :auto`) is not enabled in the first public release. The native Index stores only explicit concrete strategies (`:flat` or `:rtree`).
|
|
119
|
-
|
|
120
|
-
Expansion Block B adds `TG::Geometry::Registry` in Ruby. Registry wraps an immutable Index reference and reloads by building a new Index before swapping the reference.
|
|
121
|
-
|
|
122
|
-
Expansion Block C adds `TG::Geometry::ActiveRecordSource` as optional Ruby-only source sugar. The native extension does not depend on Rails or ActiveRecord.
|
|
123
|
-
|
|
124
|
-
Expansion Block D adds Hex/GeoBIN parse/write helpers and raw `extra_json` copying. It does not parse properties into Ruby Hashes.
|
|
125
|
-
|
|
126
|
-
Expansion Block E adds read-only borrowed wrappers for `TG::Geometry::Line`, `TG::Geometry::Ring`, and `TG::Geometry::Polygon`. These wrappers keep the parent `TG::Geometry::Geom` alive through `geom_owner`, mark it for GC, update it during compaction, and never free borrowed TG child pointers directly.
|
|
127
|
-
|
|
128
|
-
Expansion Block I documents and tests the current Ractor boundary: native wrappers are not advertised as Ractor-shareable objects. Normal thread read-only access remains the supported concurrency model.
|
|
129
|
-
|
|
130
|
-
Expansion Block J adds grouped TG API coverage without exposing global mutable environment settings or callback-based APIs. Implemented groups are additional predicates, geometry metadata/collection accessors, point and empty geometry constructors, value `TG::Geometry::Segment`, and borrowed GeometryCollection child `Geom` wrappers. See `docs/FULL_TG_API_COVERAGE.md`.
|
|
52
|
+
`Geom` and `Index` are immutable after construction. Concurrent read-only use from normal Ruby threads is supported. The implementation keeps the GVL for parse, write, query, batch, FeatureSource, and rtree build/free paths.
|
data/docs/BENCHMARKING.md
CHANGED
|
@@ -70,6 +70,25 @@ The first release does not expose `strategy: :auto`. Choosing a threshold requir
|
|
|
70
70
|
|
|
71
71
|
The first release does not claim Falcon or Async behavior. A dedicated Falcon/Async benchmark remains an open setup item until the dependency and scenario are approved.
|
|
72
72
|
|
|
73
|
-
##
|
|
73
|
+
## Planned API areas: auto strategy threshold
|
|
74
74
|
|
|
75
75
|
`strategy: :auto` remains postponed for the first public release. A future implementation must use a complete project-owned benchmark matrix and document the selected threshold before exposing the public option.
|
|
76
|
+
|
|
77
|
+
## FeatureSource benchmark
|
|
78
|
+
|
|
79
|
+
`benchmark/feature_source.rb` compares:
|
|
80
|
+
|
|
81
|
+
- `JSON.parse + Ruby extraction` baseline;
|
|
82
|
+
- `FeatureSource.read_entries_file`;
|
|
83
|
+
- `FeatureSource.read_features_file`;
|
|
84
|
+
- direct `FeatureSource.build_index_file`;
|
|
85
|
+
- `Index.build(read_entries, via: :geojson)`.
|
|
86
|
+
|
|
87
|
+
Run:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bundle exec ruby benchmark/feature_source.rb
|
|
91
|
+
TGEOMETRY_BENCH_FULL=1 bundle exec ruby benchmark/feature_source.rb
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Do not publish FeatureSource performance claims unless they come from this benchmark on the target dataset and environment.
|