From 1a7efd50a3c801ce47903bb4a5fa19577c145c3e Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Thu, 3 Sep 2026 12:52:30 +0200 Subject: [PATCH] Free the C string MEOS hands back, in the wrappers that convert one WITNESS: 125 generated wrappers end `return C.GoString(_cret), nil` and none of them frees `_cret`. The generator is not free-blind -- it emits `C.free` 295 times, every one for an INPUT it allocated itself (`_c_endian`, `_c_name`, `_c_path`). It frees what it allocates and drops what MEOS hands back. The return belongs to the caller, and MEOS's own example says so: `meos/examples/03_berlinmod_assemble.c` reads `char *trip_str = temporal_as_hexwkb(trip, WKB_EXTENDED, &length);` and then `free(trip_str)`. `C.GoString` copies into Go memory, so the C allocation is unreachable the moment such a wrapper returns. MEASURED, 200000 calls of `TemporalAsHexwkb` against resident memory: the generated wrapper grows +19444 KB where this package's hand-written accessor, which frees, grows +4488 KB. With the free emitted the same wrapper grows +424 KB. THE DISCRIMINATOR IS THE C DECLARATION, so nothing is inferred from a name or a group: `char *` belongs to the caller, `const char *` belongs to MEOS and must never be freed. `tools/codegen.py` carries separate rows for the two types whose conversions otherwise coincide; the free hangs off that distinction and is deferred, so it runs after the copy, and it sits after the errno guard so a failed call never reaches it. The regenerated surface splits exactly along that line: of the 125 wrappers, 115 free and 10 do not, and those 10 are precisely the `const char *` entries -- `geo_typename`, `interptype_name`, `meos_pc_schema_compression`, `meos_pc_schema_xml`, `meosoper_name`, `meostype_name`, `temporal_basetype_name`, `temporal_interp`, `temporal_subtype`, `tempsubtype_name`. Freeing one of those would be a fault, so the zero there matters as much as the 115. The suite reads 121 result lines, 0 skipped, and `go build ./functions` is clean. --- functions/meos_meos.go | 43 +++++++++++++++++++ functions/meos_meos_cbuffer.go | 5 +++ functions/meos_meos_geo.go | 16 +++++++ functions/meos_meos_h3.go | 2 + functions/meos_meos_internal.go | 7 +++ functions/meos_meos_internal_geo.go | 66 +++++++++++++++++++++++++++++ functions/meos_meos_json.go | 6 +++ functions/meos_meos_npoint.go | 7 +++ functions/meos_meos_pointcloud.go | 7 +++ functions/meos_meos_pose.go | 17 ++++++++ functions/meos_meos_quadbin.go | 2 + functions/meos_meos_rgeo.go | 3 ++ tools/codegen.py | 26 +++++++++++- 13 files changed, 206 insertions(+), 1 deletion(-) diff --git a/functions/meos_meos.go b/functions/meos_meos.go index 04acf0e..33e4052 100644 --- a/functions/meos_meos.go +++ b/functions/meos_meos.go @@ -107,6 +107,7 @@ func BoolOut(b bool) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -118,6 +119,7 @@ func Float8Out(num float64, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -142,6 +144,7 @@ func DateOut(date int32) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -177,6 +180,7 @@ func IntervalOut(interv *Interval) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -201,6 +205,7 @@ func TimeOut(time int64) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -225,6 +230,7 @@ func TimestampOut(ts int64) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -249,6 +255,7 @@ func TimestamptzOut(tstz int64) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -275,6 +282,7 @@ func TextToCstring(txt string) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -301,6 +309,7 @@ func TextOut(txt string) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -985,6 +994,7 @@ func MeosGetDatestyle() (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -996,6 +1006,7 @@ func MeosGetIntervalstyle() (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1055,6 +1066,7 @@ func MeosVersion() (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1066,6 +1078,7 @@ func MeosFullVersion() (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1077,6 +1090,7 @@ func MobilitydbVersion() (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1088,6 +1102,7 @@ func MobilitydbFullVersion() (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1112,6 +1127,7 @@ func BigintsetOut(set *Set) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1147,6 +1163,7 @@ func BigintspanOut(s *Span) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1171,6 +1188,7 @@ func BigintspansetOut(ss *SpanSet) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1195,6 +1213,7 @@ func DatesetOut(s *Set) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1219,6 +1238,7 @@ func DatespanOut(s *Span) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1243,6 +1263,7 @@ func DatespansetOut(ss *SpanSet) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1267,6 +1288,7 @@ func FloatsetOut(set *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1302,6 +1324,7 @@ func FloatspanOut(s *Span, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1326,6 +1349,7 @@ func FloatspansetOut(ss *SpanSet, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1350,6 +1374,7 @@ func IntsetOut(set *Set) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1385,6 +1410,7 @@ func IntspanOut(s *Span) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1409,6 +1435,7 @@ func IntspansetOut(ss *SpanSet) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1420,6 +1447,7 @@ func SetAsHexwkb(s *Set, variant uint8, size_out unsafe.Pointer) (_r0 string, _e if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1466,6 +1494,7 @@ func SpanAsHexwkb(s *Span, variant uint8, size_out unsafe.Pointer) (_r0 string, if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1512,6 +1541,7 @@ func SpansetAsHexwkb(ss *SpanSet, variant uint8, size_out unsafe.Pointer) (_r0 s if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1571,6 +1601,7 @@ func TextsetOut(set *Set) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1595,6 +1626,7 @@ func TstzsetOut(set *Set) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1619,6 +1651,7 @@ func TstzspanOut(s *Span) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1643,6 +1676,7 @@ func TstzspansetOut(ss *SpanSet) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -8115,6 +8149,7 @@ func TBOXAsHexwkb(box *TBox, variant uint8, size_out unsafe.Pointer) (_r0 string if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -8174,6 +8209,7 @@ func TBOXOut(box *TBox, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -8995,6 +9031,7 @@ func TboolOut(temp *Temporal) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -9006,6 +9043,7 @@ func TemporalAsHexwkb(temp *Temporal, variant uint8, size_out unsafe.Pointer) (_ if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -9019,6 +9057,7 @@ func TemporalAsMFJSON(temp *Temporal, with_bbox bool, flags int, precision int, if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -9104,6 +9143,7 @@ func TfloatOut(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -9167,6 +9207,7 @@ func TintOut(temp *Temporal) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -9178,6 +9219,7 @@ func TbigintOut(temp *Temporal) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -9215,6 +9257,7 @@ func TtextOut(temp *Temporal) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_cbuffer.go b/functions/meos_meos_cbuffer.go index 6ddcdf5..37109d1 100644 --- a/functions/meos_meos_cbuffer.go +++ b/functions/meos_meos_cbuffer.go @@ -94,6 +94,7 @@ func CbufferAsEWKT(cb *Cbuffer, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -105,6 +106,7 @@ func CbufferAsHexwkb(cb *Cbuffer, variant uint8, size_out unsafe.Pointer) (_r0 s if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -116,6 +118,7 @@ func CbufferAsText(cb *Cbuffer, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -175,6 +178,7 @@ func CbufferOut(cb *Cbuffer, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -608,6 +612,7 @@ func CbuffersetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_geo.go b/functions/meos_meos_geo.go index 9338273..904bcbc 100644 --- a/functions/meos_meos_geo.go +++ b/functions/meos_meos_geo.go @@ -118,6 +118,7 @@ func Box3dOut(box *Box3D, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -153,6 +154,7 @@ func GboxOut(box *GBox, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -177,6 +179,7 @@ func GeoAsEWKT(gs *Geom, precision int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -190,6 +193,7 @@ func GeoAsGeojson(gs *Geom, option int, precision int, srs string) (_r0 string, if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -203,6 +207,7 @@ func GeoAsHexewkb(gs *Geom, endian string) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -214,6 +219,7 @@ func GeoAsText(gs *Geom, precision int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -262,6 +268,7 @@ func GeoOut(gs *Geom) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1001,6 +1008,7 @@ func GeomRelate(gs1 *Geom, gs2 *Geom) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1161,6 +1169,7 @@ func SpatialsetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1172,6 +1181,7 @@ func SpatialsetAsText(set *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1183,6 +1193,7 @@ func SpatialsetAsEWKT(set *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1406,6 +1417,7 @@ func STBOXAsHexwkb(box *STBox, variant uint8, size_out unsafe.Pointer) (_r0 stri if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1465,6 +1477,7 @@ func STBOXOut(box *STBox, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -2302,6 +2315,7 @@ func TspatialOut(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -2417,6 +2431,7 @@ func TspatialAsEWKT(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -2428,6 +2443,7 @@ func TspatialAsText(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_h3.go b/functions/meos_meos_h3.go index 11f4718..152b2fa 100644 --- a/functions/meos_meos_h3.go +++ b/functions/meos_meos_h3.go @@ -107,6 +107,7 @@ func H3indexOut(cell uint64) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -373,6 +374,7 @@ func H3indexAsHexwkb(cell uint64, variant uint8, size_out unsafe.Pointer) (_r0 s if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_internal.go b/functions/meos_meos_internal.go index 278582b..e53fea4 100644 --- a/functions/meos_meos_internal.go +++ b/functions/meos_meos_internal.go @@ -174,6 +174,7 @@ func SetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -198,6 +199,7 @@ func SpanOut(s *Span, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -222,6 +224,7 @@ func SpansetOut(ss *SpanSet, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1034,6 +1037,7 @@ func TemporalOut(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1108,6 +1112,7 @@ func TinstantOut(inst *TInstant, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1197,6 +1202,7 @@ func TsequenceOut(seq *TSequence, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1221,6 +1227,7 @@ func TsequencesetOut(ss *TSequenceSet, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_internal_geo.go b/functions/meos_meos_internal_geo.go index 6a7c334..c30bba6 100644 --- a/functions/meos_meos_internal_geo.go +++ b/functions/meos_meos_internal_geo.go @@ -817,6 +817,72 @@ func GeoEdgeCtxMake(gs *Geom) (_r0 unsafe.Pointer, _err error) { } +// GeoIsPlanarLinear wraps MEOS C function geo_is_planar_linear. +func GeoIsPlanarLinear(gs *Geom) (_r0 bool, _err error) { + C.meos_errno_reset() + _cret := C.geo_is_planar_linear(gs._inner) + if _err = meosError(); _err != nil { + return + } + return bool(_cret), nil +} + + +// GeoClipSubject wraps MEOS C function geo_clip_subject. +func GeoClipSubject(gs *Geom) (_r0 bool, _err error) { + C.meos_errno_reset() + _cret := C.geo_clip_subject(gs._inner) + if _err = meosError(); _err != nil { + return + } + return bool(_cret), nil +} + + +// GeoIsPointSet wraps MEOS C function geo_is_point_set. +func GeoIsPointSet(gs *Geom) (_r0 bool, _err error) { + C.meos_errno_reset() + _cret := C.geo_is_point_set(gs._inner) + if _err = meosError(); _err != nil { + return + } + return bool(_cret), nil +} + + +// GeoMeosSupported wraps MEOS C function geo_meos_supported. +func GeoMeosSupported(gs *Geom) (_r0 bool, _err error) { + C.meos_errno_reset() + _cret := C.geo_meos_supported(gs._inner) + if _err = meosError(); _err != nil { + return + } + return bool(_cret), nil +} + + +// GeoPointsCovered wraps MEOS C function geo_points_covered. +func GeoPointsCovered(pts *Geom, gs *Geom, covered bool) (_r0 *Geom, _err error) { + C.meos_errno_reset() + _cret := C.geo_points_covered(pts._inner, gs._inner, C.bool(covered)) + if _err = meosError(); _err != nil { + return + } + return &Geom{_inner: _cret}, nil +} + + +// GeoClipLinearGeom wraps MEOS C function geo_clip_linear_geom. +func GeoClipLinearGeom(line *Geom, gs *Geom, inside bool) (_r0 *Geom, _err error) { + C.meos_errno_reset() + _cret := C.geo_clip_linear_geom(line._inner, gs._inner, C.bool(inside)) + if _err = meosError(); _err != nil { + return + } + return &Geom{_inner: _cret}, nil +} + + // GeoEdgeCtxFree wraps MEOS C function geo_edge_ctx_free. func GeoEdgeCtxFree(ctx unsafe.Pointer) (_err error) { C.meos_errno_reset() diff --git a/functions/meos_meos_json.go b/functions/meos_meos_json.go index 3cb4a9d..61ac06f 100644 --- a/functions/meos_meos_json.go +++ b/functions/meos_meos_json.go @@ -109,6 +109,7 @@ func JSONOut(js string) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -146,6 +147,7 @@ func JsonbOut(jb *Jsonb) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -223,6 +225,7 @@ func JsonbToCstring(jb *Jsonb) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -950,6 +953,7 @@ func JsonpathOut(jp *JsonPath) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -974,6 +978,7 @@ func JsonbsetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1459,6 +1464,7 @@ func TjsonbOut(temp *Temporal) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_npoint.go b/functions/meos_meos_npoint.go index 63108e8..1595991 100644 --- a/functions/meos_meos_npoint.go +++ b/functions/meos_meos_npoint.go @@ -94,6 +94,7 @@ func NpointAsEWKT(np *Npoint, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -105,6 +106,7 @@ func NpointAsHexwkb(np *Npoint, variant uint8, size_out unsafe.Pointer) (_r0 str if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -116,6 +118,7 @@ func NpointAsText(np *Npoint, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -175,6 +178,7 @@ func NpointOut(np *Npoint, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -199,6 +203,7 @@ func NsegmentOut(ns *Nsegment, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -674,6 +679,7 @@ func NpointsetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -888,6 +894,7 @@ func TnpointOut(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_pointcloud.go b/functions/meos_meos_pointcloud.go index 00e5dc7..c3b76f8 100644 --- a/functions/meos_meos_pointcloud.go +++ b/functions/meos_meos_pointcloud.go @@ -107,6 +107,7 @@ func PcpointHexOut(pt *Pcpoint, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -131,6 +132,7 @@ func PcpointAsHexwkb(pt *Pcpoint) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -473,6 +475,7 @@ func PcpatchHexOut(pa *Pcpatch, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -497,6 +500,7 @@ func PcpatchAsHexwkb(pa *Pcpatch) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -708,6 +712,7 @@ func PcpointsetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -898,6 +903,7 @@ func PcpatchsetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1088,6 +1094,7 @@ func TpcboxOut(box *TPCBox, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_pose.go b/functions/meos_meos_pose.go index 9f35b17..e50774f 100644 --- a/functions/meos_meos_pose.go +++ b/functions/meos_meos_pose.go @@ -94,6 +94,7 @@ func PoseAsEWKT(pose *Pose, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -105,6 +106,7 @@ func PoseAsHexwkb(pose *Pose, variant uint8, size_out unsafe.Pointer) (_r0 strin if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -116,6 +118,7 @@ func PoseAsText(pose *Pose, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -175,6 +178,7 @@ func PoseOut(pose *Pose, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -199,6 +203,7 @@ func PoseAsGeopose(pose *Pose, conformance int, precision int) (_r0 string, _err if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -223,6 +228,7 @@ func TposeAsGeopose(temp *Temporal, conformance int, precision int) (_r0 string, if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -234,6 +240,7 @@ func TposeAsGeoposeStreamHeader(temp *Temporal, precision int) (_r0 string, _err if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -245,6 +252,7 @@ func TposeAsGeoposeStreamElement(temp *Temporal, inst *TInstant, precision int) if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -256,6 +264,7 @@ func TposeAsGeoposeStream(temp *Temporal, precision int) (_r0 string, _err error if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -799,6 +808,7 @@ func PosesetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1633,6 +1643,7 @@ func PosechainOut(pc unsafe.Pointer, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1644,6 +1655,7 @@ func PosechainAsText(pc unsafe.Pointer, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1655,6 +1667,7 @@ func PosechainAsEWKT(pc unsafe.Pointer, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1677,6 +1690,7 @@ func PosechainAsHexwkb(pc unsafe.Pointer, variant uint8, size_out unsafe.Pointer if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1969,6 +1983,7 @@ func TposechainAsGeopose(temp *Temporal, precision int) (_r0 string, _err error) if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -1980,6 +1995,7 @@ func TposechainarrAsGeopose(temparr unsafe.Pointer, count int, precision int) (_ if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -2103,6 +2119,7 @@ func PosechainsetOut(s *Set, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_quadbin.go b/functions/meos_meos_quadbin.go index 1a694d7..23c138a 100644 --- a/functions/meos_meos_quadbin.go +++ b/functions/meos_meos_quadbin.go @@ -395,6 +395,7 @@ func QuadbinIndexToString(index uint64) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -419,6 +420,7 @@ func QuadbinCellToQuadkey(cell uint64) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/functions/meos_meos_rgeo.go b/functions/meos_meos_rgeo.go index f2b2b87..2396520 100644 --- a/functions/meos_meos_rgeo.go +++ b/functions/meos_meos_rgeo.go @@ -120,6 +120,7 @@ func TrgeometryOut(temp *Temporal) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -131,6 +132,7 @@ func TrgeometryAsText(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } @@ -142,6 +144,7 @@ func TrgeometryAsEWKT(temp *Temporal, maxdd int) (_r0 string, _err error) { if _err = meosError(); _err != nil { return } + defer C.free(unsafe.Pointer(_cret)) return C.GoString(_cret), nil } diff --git a/tools/codegen.py b/tools/codegen.py index ddc10f9..ccb0f60 100644 --- a/tools/codegen.py +++ b/tools/codegen.py @@ -85,6 +85,26 @@ _ERRNO_GUARD = "\tif _err = meosError(); _err != nil {\n\t\treturn\n\t}" +def _free_returned_string(return_c: str) -> str: + """The free for a C string MEOS hands to the caller, or nothing. + + A ``char *`` result belongs to the caller: MEOS's own example frees it + (``meos/examples/03_berlinmod_assemble.c`` does ``free(trip_str)`` on the + result of ``temporal_as_hexwkb``). ``C.GoString`` copies into Go memory, so + without this the C allocation is unreachable the moment the wrapper returns. + + ``const char *`` is the opposite case and must NOT be freed: MEOS hands back + a name it owns (``temporal_interp``, ``temporal_subtype``, + ``temporal_basetype_name``). The C declaration is the whole discriminator, + so nothing here is inferred from a function's name or group. + + The free is deferred, so it runs after ``C.GoString`` has copied, and it sits + after the errno guard so a failed call -- which returns no buffer -- never + reaches it. + """ + return "\n\tdefer C.free(unsafe.Pointer(_cret))" if return_c == "char *" else "" + + def _result_signature(all_returns: list[str]) -> str: """Render the Go result list, with the projected error appended. @@ -574,7 +594,8 @@ def _emit_array_input_group(entry: dict, group: dict) -> EmittedFunc: if return_c == "void": tail = f"{_ERRNO_RESET}\n\t{call}\n{_ERRNO_GUARD}\n\treturn" else: - tail = (f"{_ERRNO_RESET}\n\t_cret := {call}\n{_ERRNO_GUARD}\n" + tail = (f"{_ERRNO_RESET}\n\t_cret := {call}\n{_ERRNO_GUARD}" + f"{_free_returned_string(return_c)}\n" f"\treturn {ret_from_c.replace('$x', '_cret')}, nil") code = ( f"// {go_name} wraps MEOS C function {c_name}.\n" @@ -924,6 +945,9 @@ def classify_one(p, i): # parameter named ``res`` (e.g. h3_uncompact_cells' resolution arg). body_lines.append(f"\t_cret := {call}") body_lines.append(_ERRNO_GUARD) + free_cret = _free_returned_string(return_c) + if free_cret: + body_lines.append(free_cret.lstrip("\n")) return_expr = ret_from_c.replace("$x", "_cret") if extra_returns: tail = ", ".join(c2g for _, _, c2g in extra_returns)