Free the C string MEOS hands back, in the wrappers that convert one - #22
Merged
estebanzimanyi merged 1 commit intoSep 3, 2026
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WITNESS: 125 generated wrappers end
return C.GoString(_cret), niland none ofthem frees
_cret. The generator is not free-blind -- it emitsC.free295times, 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.creadschar *trip_str = temporal_as_hexwkb(trip, WKB_EXTENDED, &length);and thenfree(trip_str).C.GoStringcopies into Go memory, so the C allocation isunreachable the moment such a wrapper returns.
MEASURED, 200000 calls of
TemporalAsHexwkbagainst resident memory: thegenerated 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 mustnever be freed.
tools/codegen.pycarries separate rows for the twotypes 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 therematters as much as the 115.
The suite reads 121 result lines, 0 skipped, and
go build ./functionsisclean.