refactored geos package

master
Oliver Tonnhofer 2013-05-28 11:10:19 +02:00
parent 5eed0502ea
commit 2a699a09c8
8 changed files with 395 additions and 284 deletions

View File

@ -4,10 +4,13 @@ import (
"errors"
"goposm/geom/geos"
"goposm/geom/ogr"
"goposm/logging"
"math"
"strings"
)
var log = logging.NewLogger("Clipper")
// Tile bbox into multiple sub-boxes, each of `width` size.
// >>> list(tile_bbox((-1, 1, 0.49, 1.51), 0.5)) #doctest: +NORMALIZE_WHITESPACE
@ -140,13 +143,16 @@ func filterGeometryByType(g *geos.Geos, geom *geos.Geom, targetType string) []*g
for _, part := range g.Geoms(geom) {
// only parts with same type
if g.Type(part) == targetType {
geoms = append(geoms, part)
geoms = append(geoms, g.Clone(part))
}
}
g.Destroy(geom)
if len(geoms) != 0 {
return geoms
}
return []*geos.Geom{}
}
g.Destroy(geom)
return []*geos.Geom{}
}
@ -164,21 +170,21 @@ func (clipper *Clipper) Clip(geom *geos.Geom) ([]*geos.Geom, error) {
var intersections []*geos.Geom
for _, hit := range hits {
hit.Lock.Lock()
hit.Lock()
if g.PreparedContains(hit.Prepared, geom) {
hit.Lock.Unlock()
hit.Unlock()
return []*geos.Geom{geom}, nil
}
if g.PreparedIntersects(hit.Prepared, geom) {
hit.Lock.Unlock()
hit.Unlock()
newPart := g.Intersection(hit.Geom, geom)
newParts := filterGeometryByType(g, newPart, geomType)
for _, p := range newParts {
intersections = append(intersections, p)
}
} else {
hit.Lock.Unlock()
hit.Unlock()
}
}
return mergeGeometries(g, intersections, geomType), nil
@ -188,9 +194,15 @@ func flattenPolygons(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom {
var result []*geos.Geom
for _, geom := range geoms {
if g.Type(geom) == "MultiPolygon" {
result = append(result, g.Geoms(geom)...)
} else {
for _, part := range g.Geoms(geom) {
result = append(result, g.Clone(part))
}
g.Destroy(geom)
} else if g.Type(geom) == "Polygon" {
result = append(result, geom)
} else {
log.Printf("unexpected geometry type in flattenPolygons")
g.Destroy(geom)
}
}
return result
@ -200,9 +212,15 @@ func flattenLineStrings(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom {
var result []*geos.Geom
for _, geom := range geoms {
if g.Type(geom) == "MultiLineString" {
result = append(result, g.Geoms(geom)...)
} else {
for _, part := range g.Geoms(geom) {
result = append(result, g.Clone(part))
}
g.Destroy(geom)
} else if g.Type(geom) == "LineString" {
result = append(result, geom)
} else {
log.Printf("unexpected geometry type in flattenPolygons")
g.Destroy(geom)
}
}
return result
@ -213,6 +231,8 @@ func filterInvalidLineStrings(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom {
for _, geom := range geoms {
if geom.Length() > 1e-9 {
result = append(result, geom)
} else {
g.Destroy(geom)
}
}
return result

View File

@ -65,12 +65,15 @@ func LineStringWkb(g *geos.Geos, nodes []element.Node) (*element.Geometry, error
coordSeq.SetXY(g, uint32(i), nd.Long, nd.Lat)
}
geom, err := coordSeq.AsLineString(g)
wkb := g.AsWkb(geom)
if wkb == nil {
g.Destroy(geom)
return nil, errors.New("could not create wkb")
if err != nil {
// coordSeq gets Destroy by GEOS
return nil, err
}
g.DestroyLater(geom)
wkb := g.AsWkb(geom)
if wkb == nil {
return nil, errors.New("could not create wkb")
}
return &element.Geometry{
Wkb: wkb,
Geom: geom,
@ -97,7 +100,8 @@ func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
if err != nil {
return nil, err
}
// coordSeq inherited by LineString, no destroy
// coordSeq inherited by LinearRing, no destroy
for i, nd := range nodes {
err := coordSeq.SetXY(g, uint32(i), nd.Long, nd.Lat)
if err != nil {
@ -106,7 +110,7 @@ func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
}
ring, err := coordSeq.AsLinearRing(g)
if err != nil {
g.DestroyCoordSeq(coordSeq)
// coordSeq gets Destroy by GEOS
return nil, err
}
// ring inherited by Polygon, no destroy

63
geom/geos/coords.go Normal file
View File

@ -0,0 +1,63 @@
package geos
/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
*/
import "C"
type CoordSeq struct {
v *C.GEOSCoordSequence
}
func (this *Geos) CreateCoordSeq(size, dim uint32) (*CoordSeq, error) {
result := C.GEOSCoordSeq_create_r(this.v, C.uint(size), C.uint(dim))
if result == nil {
return nil, CreateError("could not create CoordSeq")
}
return &CoordSeq{result}, nil
}
func (this *CoordSeq) SetXY(handle *Geos, i uint32, x, y float64) error {
if C.GEOSCoordSeq_setX_r(handle.v, this.v, C.uint(i), C.double(x)) == 0 {
return Error("unable to SetY")
}
if C.GEOSCoordSeq_setY_r(handle.v, this.v, C.uint(i), C.double(y)) == 0 {
return Error("unable to SetX")
}
return nil
}
func (this *CoordSeq) AsPoint(handle *Geos) (*Geom, error) {
geom := C.GEOSGeom_createPoint_r(handle.v, this.v)
if geom == nil {
return nil, CreateError("unable to create Point")
}
return &Geom{geom}, nil
}
func (this *CoordSeq) AsLineString(handle *Geos) (*Geom, error) {
geom := C.GEOSGeom_createLineString_r(handle.v, this.v)
if geom == nil {
return nil, CreateError("unable to create LineString")
}
return &Geom{geom}, nil
}
func (this *CoordSeq) AsLinearRing(handle *Geos) (*Geom, error) {
ring := C.GEOSGeom_createLinearRing_r(handle.v, this.v)
if ring == nil {
return nil, CreateError("unable to create LinearRing")
}
return &Geom{ring}, nil
}
func (this *Geos) DestroyCoordSeq(coordSeq *CoordSeq) {
if coordSeq.v != nil {
C.GEOSCoordSeq_destroy_r(this.v, coordSeq.v)
coordSeq.v = nil
} else {
panic("double free?")
}
}

View File

@ -9,18 +9,12 @@ extern void goLogString(char *msg);
extern void debug_wrap(const char *fmt, ...);
extern GEOSContextHandle_t initGEOS_r_debug();
extern void initGEOS_debug();
extern void IndexQuerySendCallback(void *, void *);
extern void goIndexSendQueryResult(size_t, void *);
extern void IndexQuery(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, void *);
extern void IndexAdd(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, size_t);
*/
import "C"
import (
"goposm/logging"
"runtime"
"sync"
"unsafe"
)
@ -39,10 +33,6 @@ type Geom struct {
v *C.GEOSGeometry
}
type PreparedGeom struct {
v *C.GEOSPreparedGeometry
}
type CreateError string
type Error string
@ -79,50 +69,34 @@ func init() {
C.initGEOS_debug()
}
type CoordSeq struct {
v *C.GEOSCoordSequence
func (this *Geos) Destroy(geom *Geom) {
runtime.SetFinalizer(geom, nil)
if geom.v != nil {
C.GEOSGeom_destroy_r(this.v, geom.v)
geom.v = nil
} else {
log.Printf("double free?")
}
}
func (this *Geos) CreateCoordSeq(size, dim uint32) (*CoordSeq, error) {
result := C.GEOSCoordSeq_create_r(this.v, C.uint(size), C.uint(dim))
func destroyGeom(geom *Geom) {
C.GEOSGeom_destroy(geom.v)
}
func (this *Geos) DestroyLater(geom *Geom) {
runtime.SetFinalizer(geom, destroyGeom)
}
func (this *Geos) Clone(geom *Geom) *Geom {
if geom == nil || geom.v == nil {
return nil
}
result := C.GEOSGeom_clone_r(this.v, geom.v)
if result == nil {
return nil, CreateError("could not create CoordSeq")
return nil
}
return &CoordSeq{result}, nil
}
func (this *CoordSeq) SetXY(handle *Geos, i uint32, x, y float64) error {
if C.GEOSCoordSeq_setX_r(handle.v, this.v, C.uint(i), C.double(x)) == 0 {
return Error("unable to SetY")
}
if C.GEOSCoordSeq_setY_r(handle.v, this.v, C.uint(i), C.double(y)) == 0 {
return Error("unable to SetX")
}
return nil
}
func (this *CoordSeq) AsPoint(handle *Geos) (*Geom, error) {
geom := C.GEOSGeom_createPoint_r(handle.v, this.v)
if geom == nil {
return nil, CreateError("unable to create Point")
}
return &Geom{geom}, nil
}
func (this *CoordSeq) AsLineString(handle *Geos) (*Geom, error) {
geom := C.GEOSGeom_createLineString_r(handle.v, this.v)
if geom == nil {
return nil, CreateError("unable to create LineString")
}
return &Geom{geom}, nil
}
func (this *CoordSeq) AsLinearRing(handle *Geos) (*Geom, error) {
ring := C.GEOSGeom_createLinearRing_r(handle.v, this.v)
if ring == nil {
return nil, CreateError("unable to create LinearRing")
}
return &Geom{ring}, nil
return &Geom{result}
}
func (this *Geos) CreatePolygon(shell *Geom, holes []*Geom) *Geom {
@ -136,16 +110,6 @@ func (this *Geos) CreatePolygon(shell *Geom, holes []*Geom) *Geom {
return &Geom{polygon}
}
func (this *Geos) FromWkt(wkt string) (geom *Geom) {
wktC := C.CString(wkt)
defer C.free(unsafe.Pointer(wktC))
return &Geom{C.GEOSGeomFromWKT_r(this.v, wktC)}
}
func (this *Geos) Buffer(geom *Geom, size float64) *Geom {
return &Geom{C.GEOSBuffer_r(this.v, geom.v, C.double(size), 50)}
}
func (this *Geos) NumGeoms(geom *Geom) int32 {
count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
return count
@ -164,89 +128,6 @@ func (this *Geos) Geoms(geom *Geom) []*Geom {
return result
}
func (this *Geos) Contains(a, b *Geom) bool {
result := C.GEOSContains_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) Intersects(a, b *Geom) bool {
result := C.GEOSIntersects_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) Prepare(geom *Geom) *PreparedGeom {
prep := C.GEOSPrepare_r(this.v, geom.v)
if prep == nil {
return nil
}
return &PreparedGeom{prep}
}
func (this *Geos) PreparedContains(a *PreparedGeom, b *Geom) bool {
result := C.GEOSPreparedContains_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) PreparedIntersects(a *PreparedGeom, b *Geom) bool {
// fmt.Println(this.Type(b))
result := C.GEOSPreparedIntersects_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) Intersection(a, b *Geom) *Geom {
result := C.GEOSIntersection_r(this.v, a.v, b.v)
if result == nil {
return nil
}
geom := &Geom{result}
this.DestroyLater(geom)
return geom
}
func (this *Geos) UnionPolygons(polygons []*Geom) *Geom {
multiPolygon := this.MultiPolygon(polygons)
if multiPolygon == nil {
return nil
}
result := C.GEOSUnaryUnion_r(this.v, multiPolygon.v)
if result == nil {
return nil
}
return &Geom{result}
}
func (this *Geos) LineMerge(lines []*Geom) []*Geom {
multiLineString := this.MultiLineString(lines)
if multiLineString == nil {
return nil
}
result := C.GEOSLineMerge_r(this.v, multiLineString.v)
if result == nil {
return nil
}
geom := &Geom{result}
if this.Type(geom) == "LineString" {
return []*Geom{geom}
}
return this.Geoms(geom)
}
func (this *Geos) ExteriorRing(geom *Geom) *Geom {
ring := C.GEOSGetExteriorRing_r(this.v, geom.v)
if ring == nil {
@ -349,43 +230,6 @@ func (this *Geos) MultiLineString(lines []*Geom) *Geom {
return &Geom{geom}
}
func (this *Geos) AsWkt(geom *Geom) string {
str := C.GEOSGeomToWKT_r(this.v, geom.v)
result := C.GoString(str)
C.free(unsafe.Pointer(str))
return result
}
func (this *Geos) AsWkb(geom *Geom) []byte {
var size C.size_t
buf := C.GEOSGeomToWKB_buf_r(this.v, geom.v, &size)
if buf == nil {
return nil
}
result := C.GoBytes(unsafe.Pointer(buf), C.int(size))
C.free(unsafe.Pointer(buf))
return result
}
func (this *Geos) FromWkb(wkb []byte) *Geom {
geom := C.GEOSGeomFromWKB_buf_r(this.v, (*C.uchar)(&wkb[0]), C.size_t(len(wkb)))
if geom == nil {
return nil
}
return &Geom{geom}
}
func (this *Geos) Clone(geom *Geom) *Geom {
if geom == nil || geom.v == nil {
return nil
}
result := C.GEOSGeom_clone_r(this.v, geom.v)
if result == nil {
return nil
}
return &Geom{result}
}
func (this *Geos) IsValid(geom *Geom) bool {
if C.GEOSisValid_r(this.v, geom.v) == 1 {
return true
@ -409,6 +253,14 @@ func (this *Geos) Type(geom *Geom) string {
return C.GoString(geomType)
}
func (this *Geos) Equals(a, b *Geom) bool {
result := C.GEOSEquals_r(this.v, a.v, b.v)
if result == 1 {
return true
}
return false
}
func (this *Geom) Area() float64 {
var area C.double
if ret := C.GEOSArea(this.v, &area); ret == 1 {
@ -427,12 +279,11 @@ func (this *Geom) Length() float64 {
}
}
func (this *Geos) Equals(a, b *Geom) bool {
result := C.GEOSEquals_r(this.v, a.v, b.v)
if result == 1 {
return true
}
return false
type Bounds struct {
MinX float64
MinY float64
MaxX float64
MaxY float64
}
var NilBounds = Bounds{1e20, 1e20, -1e20, -1e20}
@ -475,86 +326,3 @@ func (this *Geom) Bounds() Bounds {
return Bounds{minx, miny, maxx, maxy}
}
type Bounds struct {
MinX float64
MinY float64
MaxX float64
MaxY float64
}
func (this *Geos) Destroy(geom *Geom) {
if geom.v != nil {
C.GEOSGeom_destroy_r(this.v, geom.v)
geom.v = nil
} else {
panic("double free?")
}
}
func destroyGeom(geom *Geom) {
C.GEOSGeom_destroy(geom.v)
}
func (this *Geos) DestroyLater(geom *Geom) {
runtime.SetFinalizer(geom, destroyGeom)
}
func (this *Geos) DestroyCoordSeq(coordSeq *CoordSeq) {
if coordSeq.v != nil {
C.GEOSCoordSeq_destroy_r(this.v, coordSeq.v)
coordSeq.v = nil
} else {
panic("double free?")
}
}
type indexGeom struct {
Geom *Geom
Lock *sync.Mutex
Prepared *PreparedGeom
}
type Index struct {
v *C.GEOSSTRtree
geoms []indexGeom
}
func (this *Geos) CreateIndex() *Index {
tree := C.GEOSSTRtree_create_r(this.v, 10)
if tree == nil {
panic("unable to create tree")
}
return &Index{tree, []indexGeom{}}
}
// IndexQuery adds a geom to the index with the id.
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
id := len(index.geoms)
C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
prep := this.Prepare(geom)
index.geoms = append(index.geoms, indexGeom{geom, &sync.Mutex{}, prep})
}
// IndexQuery queries the index for intersections with geom.
func (this *Geos) IndexQuery(index *Index, geom *Geom) []indexGeom {
hits := make(chan int)
go func() {
//
// using a pointer to our hits chan to pass it through
// C.IndexQuerySendCallback (in C.IndexQuery) back
// to goIndexSendQueryResult
C.IndexQuery(this.v, index.v, geom.v, unsafe.Pointer(&hits))
close(hits)
}()
var geoms []indexGeom
for idx := range hits {
geoms = append(geoms, index.geoms[idx])
}
return geoms
}
//export goIndexSendQueryResult
func goIndexSendQueryResult(id C.size_t, ptr unsafe.Pointer) {
results := *(*chan int)(ptr)
results <- int(id)
}

69
geom/geos/index.go Normal file
View File

@ -0,0 +1,69 @@
package geos
/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
extern void IndexQuerySendCallback(void *, void *);
extern void goIndexSendQueryResult(size_t, void *);
extern void IndexQuery(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, void *);
extern void IndexAdd(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, size_t);
*/
import "C"
import (
"sync"
"unsafe"
)
// IndexGeom is a struct for indexed geometries used by Index
// and returned by IndexQuery. Access to Prepared requires acquiring .Lock()
type IndexGeom struct {
*sync.Mutex // Mutex for Prepared
Geom *Geom
Prepared *PreparedGeom
}
type Index struct {
v *C.GEOSSTRtree
geoms []IndexGeom
}
func (this *Geos) CreateIndex() *Index {
tree := C.GEOSSTRtree_create_r(this.v, 10)
if tree == nil {
panic("unable to create tree")
}
return &Index{tree, []IndexGeom{}}
}
// IndexQuery adds a geom to the index with the id.
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
id := len(index.geoms)
C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
prep := this.Prepare(geom)
index.geoms = append(index.geoms, IndexGeom{&sync.Mutex{}, geom, prep})
}
// IndexQuery queries the index for intersections with geom.
func (this *Geos) IndexQuery(index *Index, geom *Geom) []IndexGeom {
hits := make(chan int)
go func() {
// using a pointer to our hits chan to pass it through
// C.IndexQuerySendCallback (in C.IndexQuery) back
// to goIndexSendQueryResult
C.IndexQuery(this.v, index.v, geom.v, unsafe.Pointer(&hits))
close(hits)
}()
var geoms []IndexGeom
for idx := range hits {
geoms = append(geoms, index.geoms[idx])
}
return geoms
}
//export goIndexSendQueryResult
func goIndexSendQueryResult(id C.size_t, ptr unsafe.Pointer) {
results := *(*chan int)(ptr)
results <- int(id)
}

95
geom/geos/ops.go Normal file
View File

@ -0,0 +1,95 @@
package geos
/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
*/
import "C"
func (this *Geos) Contains(a, b *Geom) bool {
result := C.GEOSContains_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) Intersects(a, b *Geom) bool {
result := C.GEOSIntersects_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) Intersection(a, b *Geom) *Geom {
result := C.GEOSIntersection_r(this.v, a.v, b.v)
if result == nil {
return nil
}
geom := &Geom{result}
return geom
}
func (this *Geos) Buffer(geom *Geom, size float64) *Geom {
buffered := C.GEOSBuffer_r(this.v, geom.v, C.double(size), 50)
if buffered == nil {
return nil
}
return &Geom{buffered}
}
// UnionPolygons tries to merge polygons.
// Returns a single (Multi)Polygon.
// Destroys polygons and returns new allocated (Multi)Polygon as necessary.
func (this *Geos) UnionPolygons(polygons []*Geom) *Geom {
if len(polygons) == 0 {
return nil
}
if len(polygons) == 1 {
return polygons[0]
}
multiPolygon := this.MultiPolygon(polygons)
if multiPolygon == nil {
return nil
}
defer this.Destroy(multiPolygon)
result := C.GEOSUnaryUnion_r(this.v, multiPolygon.v)
if result == nil {
return nil
}
return &Geom{result}
}
// LineMerge tries to merge lines. Returns slice of LineStrings.
// Destroys lines and returns new allocated LineString Geoms.
func (this *Geos) LineMerge(lines []*Geom) []*Geom {
if len(lines) <= 1 {
return lines
}
multiLineString := this.MultiLineString(lines)
if multiLineString == nil {
return nil
}
defer this.Destroy(multiLineString)
merged := C.GEOSLineMerge_r(this.v, multiLineString.v)
if merged == nil {
return nil
}
geom := &Geom{merged}
if this.Type(geom) == "LineString" {
return []*Geom{geom}
}
// extract MultiLineString
lines = make([]*Geom, 0, this.NumGeoms(geom))
for _, line := range this.Geoms(geom) {
lines = append(lines, this.Clone(line))
}
this.Destroy(geom)
return lines
}

38
geom/geos/prepared.go Normal file
View File

@ -0,0 +1,38 @@
package geos
/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
*/
import "C"
type PreparedGeom struct {
v *C.GEOSPreparedGeometry
}
func (this *Geos) Prepare(geom *Geom) *PreparedGeom {
prep := C.GEOSPrepare_r(this.v, geom.v)
if prep == nil {
return nil
}
return &PreparedGeom{prep}
}
func (this *Geos) PreparedContains(a *PreparedGeom, b *Geom) bool {
result := C.GEOSPreparedContains_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
func (this *Geos) PreparedIntersects(a *PreparedGeom, b *Geom) bool {
result := C.GEOSPreparedIntersects_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}

54
geom/geos/serialize.go Normal file
View File

@ -0,0 +1,54 @@
package geos
/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
func (this *Geos) FromWkt(wkt string) *Geom {
wktC := C.CString(wkt)
defer C.free(unsafe.Pointer(wktC))
geom := C.GEOSGeomFromWKT_r(this.v, wktC)
if geom == nil {
return nil
}
return &Geom{geom}
}
func (this *Geos) FromWkb(wkb []byte) *Geom {
if len(wkb) == 0 {
return nil
}
geom := C.GEOSGeomFromWKB_buf_r(this.v, (*C.uchar)(&wkb[0]), C.size_t(len(wkb)))
if geom == nil {
return nil
}
return &Geom{geom}
}
func (this *Geos) AsWkt(geom *Geom) string {
str := C.GEOSGeomToWKT_r(this.v, geom.v)
if str == nil {
return ""
}
result := C.GoString(str)
C.free(unsafe.Pointer(str))
return result
}
func (this *Geos) AsWkb(geom *Geom) []byte {
var size C.size_t
buf := C.GEOSGeomToWKB_buf_r(this.v, geom.v, &size)
if buf == nil {
return nil
}
result := C.GoBytes(unsafe.Pointer(buf), C.int(size))
C.free(unsafe.Pointer(buf))
return result
}