]> granicus.if.org Git - postgis/commitdiff
Add first cut to lwgeom_to_wkb function.
authorPaul Ramsey <pramsey@cleverelephant.ca>
Tue, 2 Mar 2010 23:18:39 +0000 (23:18 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Tue, 2 Mar 2010 23:18:39 +0000 (23:18 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@5365 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/Makefile.in
liblwgeom/cunit/cu_libgeom.c
liblwgeom/cunit/cu_wkt.c
liblwgeom/libgeom.h
liblwgeom/liblwgeom.h
liblwgeom/lwout_wkb.c [new file with mode: 0644]
liblwgeom/lwout_wkt.c

index 7973df8293fe6a1439c23b030abd54f043a8ee62..a40cca4dcc8c8f1bff19994313dda9b0a80c1a3d 100644 (file)
@@ -38,6 +38,7 @@ SA_OBJS = \
        lwmcurve.o \
        lwmsurface.o \
        lwout_wkt.o \
+       lwout_wkb.o \
        lwutil.o \
        lwhomogenize.o \
        lwalgorithm.o \
index 256a02e583fb2087b2f27c5d17256864d86c1799..e4ca6fd737769552771361b261f4c8f6210bf311 100644 (file)
@@ -334,7 +334,7 @@ static void do_lwgeom_flip_coordinates(char *in, char *out)
         g = lwgeom_from_ewkt(in, PARSER_CHECK_NONE);
         h = lwgeom_flip_coordinates(g);
 
-       t = lwgeom_to_wkt(g, 8, WKT_EXTENDED); 
+       t = lwgeom_to_wkt(g, WKT_EXTENDED, 8, NULL); 
        if (t == NULL) fprintf(stderr, "In:%s", in);
         if (strcmp(t, out))
                 fprintf(stderr, "\nIn:   %s\nOut:  %s\nTheo: %s\n", in, t, out);
index 8b16f6a415324d97880543b6745a2f8db4442c9d..106efb11e220b4c36521c463807d0759b42ae99f 100644 (file)
@@ -48,7 +48,7 @@ static char* cu_wkt(char *wkt, uchar variant)
 {
        LWGEOM *g = lwgeom_from_ewkt(wkt, PARSER_CHECK_NONE);
        if ( s ) free(s);
-       s = lwgeom_to_wkt(g, 8, variant);       
+       s = lwgeom_to_wkt(g, variant, 8, NULL); 
        lwgeom_free(g);
        return s;
 }
index f78f029359850f2d839562b786c1d0e5c60f143e..ccd4582751d76745c8d4a420db6d3038868b1002 100644 (file)
@@ -36,7 +36,7 @@
 #define LW_FALSE 0
 
 /**
-* WKT Output Variant Types
+* Well-Known Text (WKT) Output Variant Types
 */
 #define WKT_ISO 0x01
 #define WKT_SFSQL 0x02
 #define WKT_NOTYPE 0x08
 #define WKT_NOPARENS 0x10
 
+/**
+* Well-Known Binary (WKB) Output Variant Types
+*/
+#define WKB_ISO 0x01
+#define WKB_SFSQL 0x02
+#define WKB_EXTENDED 0x04
+#define WKB_NDR 0x08
+
 /**
 * Maximum allowed SRID value. 
 * Currently we are using 20 bits (1048575) of storage for SRID.
index b1262bc74bdcd3f33243aaee7fa706a1d4a6223b..e8475380da6c6c91c909317f6e629c99cf3ad439 100644 (file)
@@ -1437,7 +1437,8 @@ extern void deparse_hex(uchar str, char *result);
 /*
 ** New parsing and unparsing functions.
 */
-extern char *lwgeom_to_wkt(const LWGEOM *geom, int precision, uchar variant);
+extern char *lwgeom_to_wkt(const LWGEOM *geom, uchar variant, int precision, size_t *size_out);
+extern char* lwgeom_to_wkb(const LWGEOM *geom, uchar variant, size_t *size_out);
 
 
 /* Parser check flags */
diff --git a/liblwgeom/lwout_wkb.c b/liblwgeom/lwout_wkb.c
new file mode 100644 (file)
index 0000000..8bd22cc
--- /dev/null
@@ -0,0 +1,550 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************/
+
+#include "libgeom.h"
+
+static char* lwgeom_to_wkb_buf(const LWGEOM *geom, char *buf, uchar variant);
+static size_t lwgeom_to_wkb_size(const LWGEOM *geom, uchar variant);
+
+#define WKB_DOUBLE_SIZE 8
+#define WKB_INT_SIZE 4
+#define WKB_BYTE_SIZE 1
+
+/**
+* Optional SRID
+*/
+static int lwgeom_wkb_needs_srid(const LWGEOM *geom, uchar variant)
+{
+       if ( (variant & WKB_EXTENDED) && lwgeom_has_srid(geom) )
+               return LW_TRUE;
+       return LW_FALSE;
+}
+
+/**
+* GeometryType
+*/
+static unsigned int lwgeom_wkb_type(const LWGEOM *geom, uchar variant)
+{
+       unsigned int wkb_type = 0;
+
+       uchar type = geom->type;;
+       
+       switch( TYPE_GETTYPE(type) )
+       {
+               case POINTTYPE:
+                       wkb_type = 1;
+                       break;
+               case LINETYPE:
+                       wkb_type = 2;
+                       break;
+               case POLYGONTYPE:
+                       wkb_type = 3;
+                       break;
+               case MULTIPOINTTYPE:
+                       wkb_type = 4;
+                       break;
+               case MULTILINETYPE:
+                       wkb_type = 5;
+                       break;
+               case MULTIPOLYGONTYPE:
+                       wkb_type = 6;
+                       break;
+               case COLLECTIONTYPE:
+                       wkb_type = 7;
+                       break;
+               case CIRCSTRINGTYPE:
+                       wkb_type = 8;
+                       break;
+               case COMPOUNDTYPE:
+                       wkb_type = 9;
+                       break;
+               case CURVEPOLYTYPE:
+                       wkb_type = 10;
+                       break;
+               case MULTICURVETYPE:
+                       wkb_type = 11;
+                       break;
+               case MULTISURFACETYPE:
+                       wkb_type = 12;
+                       break;
+               default:
+                       lwerror("Unsupported geometry type: %s [%d]", lwgeom_typename(type), type);
+       }
+       
+       if( variant & WKB_EXTENDED )
+       {
+               if ( TYPE_HASZ(type) )
+                       wkb_type |= WKBZOFFSET;
+               if ( TYPE_HASM(type) )
+                       wkb_type |= WKBMOFFSET;
+               if ( lwgeom_has_srid(geom) )
+                       wkb_type |= WKBSRIDFLAG;
+       }
+       else if( variant & WKB_ISO )
+       {
+               /* Z types are in the 1000 range */
+               if ( TYPE_HASZ(type) )
+                       wkb_type += 1000;
+               /* M types are in the 2000 range */
+               if ( TYPE_HASM(type) )
+                       wkb_type += 2000;
+               /* ZM types are in the 1000 + 2000 = 3000 range */
+       }       
+       return wkb_type;
+}
+
+/**
+* Endian
+*/
+static char* endian_to_wkb_buf(char *buf, uchar variant)
+{
+       buf[0] = ((variant & WKB_NDR) ? 1 : 0);
+       return buf + 1;
+}
+
+
+/**
+* Integer32
+*/
+static char* int32_to_wkb_buf(const int i, char *buf, uchar variant)
+{
+       if( sizeof(int) != WKB_INT_SIZE )
+       {
+               lwerror("Machine int size is not %d bytes!", WKB_INT_SIZE);
+       }
+       /* If machine arch and requested arch match, don't flip byte order */
+       if( ((variant & WKB_NDR) && (BYTE_ORDER == LITTLE_ENDIAN)) ||
+           ((! (variant & WKB_NDR)) && (BYTE_ORDER == BIG_ENDIAN)) )
+       {
+               memcpy(buf, &i, WKB_INT_SIZE);
+       }
+       /* Machine/request arch mismatch, so flip byte order */
+       else
+       {
+               int j = 0;
+               for ( j = 0; j < WKB_INT_SIZE; j++ )
+               {
+                       buf[j] = (&i)[WKB_INT_SIZE - j];
+               }
+       }
+       return buf + WKB_INT_SIZE;
+}
+
+/**
+* Float64
+*/
+static char* double_to_wkb_buf(const double d, char *buf, uchar variant)
+{
+       if( sizeof(double) != WKB_DOUBLE_SIZE )
+       {
+               lwerror("Machine double size is not %d bytes!", WKB_DOUBLE_SIZE);
+       }
+       /* If machine arch and requested arch match, don't flip byte order */
+       if( ((variant & WKB_NDR) && (BYTE_ORDER == LITTLE_ENDIAN)) ||
+           ((! (variant & WKB_NDR)) && (BYTE_ORDER == BIG_ENDIAN)) )
+       {
+               memcpy(buf, &d, WKB_DOUBLE_SIZE);
+       }
+       /* Machine/request arch mismatch, so flip byte order */
+       else
+       {
+               int j = 0;
+               for ( j = 0; j < WKB_DOUBLE_SIZE; j++ )
+               {
+                       buf[j] = (&d)[WKB_DOUBLE_SIZE - j];
+               }
+       }
+       return buf + WKB_DOUBLE_SIZE;
+}
+
+
+/**
+* Empty
+*/
+static size_t empty_to_wkb_size(const LWGEOM *geom, uchar variant)
+{
+       size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE;
+
+       if( lwgeom_wkb_needs_srid(geom, variant) )
+               size += WKB_INT_SIZE;
+               
+       return size;
+}
+
+static char* empty_to_wkb_buf(const LWGEOM *geom, char *buf, uchar variant)
+{
+       unsigned int wkb_type = lwgeom_wkb_type(geom, variant);
+
+       if( TYPE_GETTYPE(geom->type) == POINTTYPE )
+               wkb_type += 3; /* Change POINT to MULTIPOINT */
+
+       /* Set the endian flag */
+       buf = endian_to_wkb_buf(buf, variant);
+
+       /* Set the geometry type */
+       buf = int32_to_wkb_buf(wkb_type, buf, variant);
+
+       /* Set the SRID if necessary */
+       if( lwgeom_wkb_needs_srid(geom, variant) )
+               buf = int32_to_wkb_buf(geom->SRID, buf, variant);
+
+       /* Set nrings/npoints/ngeoms to zero */
+       buf = int32_to_wkb_buf(0, buf, variant);
+       return buf;
+}
+
+/**
+* POINTARRAY
+*/
+static size_t ptarray_to_wkb_size(const POINTARRAY *pa, uchar variant)
+{
+       int dims = 2;
+       if( pa->npoints < 1 )
+               return 0;
+       if( variant & (WKB_ISO | WKB_EXTENDED) )
+               dims = TYPE_NDIMS(pa->dims);
+
+       /* size of the double list */
+       return pa->npoints * dims * WKB_DOUBLE_SIZE;
+}
+
+static char* ptarray_to_wkb_buf(const POINTARRAY *pa, char *buf, uchar variant)
+{
+       int dims = 2;
+       int i, j;
+       double *dbl_ptr;
+       
+       if( pa->npoints < 1 )
+               return buf;
+       if( (variant & WKB_ISO) || (variant & WKB_EXTENDED) )
+               dims = TYPE_NDIMS(pa->dims);
+
+       /* Set the number of points */
+       buf = int32_to_wkb_buf(pa->npoints, buf, variant);
+       
+       /* Set the ordinates. */
+       /* TODO: Ensure that getPoint_internal is always aligned so 
+                this doesn't fail on RiSC architectures */
+       /* TODO: Make this faster by bulk copying the coordinates when
+                the output endian/dims match the internal endian/dims */
+       for( i = 0; i < pa->npoints; i++ )
+       {
+               dbl_ptr = (double*)getPoint_internal(pa, i);
+               for( j = 0; j < dims; j++ )
+               {
+                       buf = double_to_wkb_buf(dbl_ptr[j], buf, variant);
+               }
+       }
+       return buf;
+}
+
+/**
+* POINT
+*/
+static size_t lwpoint_to_wkb_size(const LWPOINT *pt, uchar variant)
+{
+       /* Endian flag + type number */
+       size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE;
+
+       /* Extended WKB needs space for optional SRID integer */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) )
+               size += WKB_INT_SIZE;
+
+       /* Points */
+       size += ptarray_to_wkb_size(pt->point, variant);
+       return size;
+}
+
+static char* lwpoint_to_wkb_buf(const LWPOINT *pt, char *buf, uchar variant)
+{
+       /* Set the endian flag */
+       buf = endian_to_wkb_buf(buf, variant);
+       /* Set the geometry type */
+       buf = int32_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)pt, variant), buf, variant);
+       /* Set the optional SRID for extended variant */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) )
+               buf = int32_to_wkb_buf(pt->SRID, buf, variant);
+       /* Set the coordinates */
+       buf = ptarray_to_wkb_buf(pt->point, buf, variant);
+       return buf;
+}
+
+/**
+* LINESTRING, CIRCULARSTRING
+*/
+static size_t lwline_to_wkb_size(const LWLINE *line, uchar variant)
+{
+       /* Endian flag + type number */
+       size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE;
+       
+       /* Extended WKB needs space for optional SRID integer */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) )
+               size += WKB_INT_SIZE;
+                       
+       /* Number of points + points */
+       size +=  WKB_INT_SIZE + ptarray_to_wkb_size(line->points, variant);
+       return size;
+}
+
+static char* lwline_to_wkb_buf(const LWLINE *line, char *buf, uchar variant)
+{
+       /* Set the endian flag */
+       buf = endian_to_wkb_buf(buf, variant);
+       /* Set the geometry type */
+       buf = int32_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)line, variant), buf, variant);
+       /* Set the optional SRID for extended variant */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) )
+               buf = int32_to_wkb_buf(line->SRID, buf, variant);
+       /* Set the coordinates */
+       buf = ptarray_to_wkb_buf(line->points, buf, variant);
+       return buf;
+}
+
+/**
+* POLYGON
+*/
+static size_t lwpoly_to_wkb_size(const LWPOLY *poly, uchar variant)
+{
+       /* endian flag + type number + number of rings */
+       size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE;
+       int i = 0;
+       
+       /* Extended WKB needs space for optional SRID integer */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) )
+               size += WKB_INT_SIZE;
+                       
+       for( i = 0; i < poly->nrings; i++ )
+       {
+               /* number of points + points */
+               size += WKB_INT_SIZE + ptarray_to_wkb_size(poly->rings[i], variant);
+       }
+       
+       return size;
+}
+
+static char* lwpoly_to_wkb_buf(const LWPOLY *poly, char *buf, uchar variant)
+{
+       int i;
+
+       /* Set the endian flag */
+       buf = endian_to_wkb_buf(buf, variant);
+       /* Set the geometry type */
+       buf = int32_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)poly, variant), buf, variant);
+       /* Set the optional SRID for extended variant */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) )
+               buf = int32_to_wkb_buf(poly->SRID, buf, variant);
+       /* Set the number of rings */
+       buf = int32_to_wkb_buf(poly->nrings, buf, variant);
+       
+       for( i = 0; i < poly->nrings; i++ )
+       {
+               buf = ptarray_to_wkb_buf(poly->rings[i], buf, variant);
+       }
+       
+       return buf;
+}
+
+/**
+* MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION
+* MULTICURVE, COMPOUNDCURVE, MULTISURFACE, CURVEPOLYGON
+*/
+static size_t lwcollection_to_wkb_size(const LWCOLLECTION *col, uchar variant)
+{
+       /* Endian flag + type number + number of subgeoms */
+       size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE;
+       int i = 0;
+       
+       /* Extended WKB needs space for optional SRID integer */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) )
+               size += WKB_INT_SIZE;
+               
+       for( i = 0; i < col->ngeoms; i++ )
+       {
+               /* size of subgeom */
+               size += lwgeom_to_wkb_size((LWGEOM*)col->geoms[i], variant);
+       }
+       
+       return size;
+}
+
+static char* lwcollection_to_wkb_buf(const LWCOLLECTION *col, char *buf, uchar variant)
+{
+       int i;
+
+       /* Set the endian flag */
+       buf = endian_to_wkb_buf(buf, variant);
+       /* Set the geometry type */
+       buf = int32_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)col, variant), buf, variant);
+       /* Set the optional SRID for extended variant */
+       if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) )
+               buf = int32_to_wkb_buf(col->SRID, buf, variant);
+       /* Set the number of sub-geometries */
+       buf = int32_to_wkb_buf(col->ngeoms, buf, variant);
+       
+       for( i = 0; i < col->ngeoms; i++ )
+       {
+               buf = lwgeom_to_wkb_buf(col->geoms[i], buf, variant);
+       }
+       
+       return buf;
+}
+
+/**
+* GEOMETRY
+*/
+static size_t lwgeom_to_wkb_size(const LWGEOM *geom, uchar variant)
+{
+       size_t size = 0;
+       
+       if ( geom == NULL ) 
+               return 0;
+
+       /* Short circuit out empty geometries */
+       if ( lwgeom_is_empty(geom) )
+       {
+               return empty_to_wkb_size(geom, variant);
+       }
+                       
+       switch( TYPE_GETTYPE(geom->type) )
+       {
+               case POINTTYPE:
+                       size += lwpoint_to_wkb_size((LWPOINT*)geom, variant);
+                       break;
+
+               /* LineString and CircularString both have points elements */
+               case CIRCSTRINGTYPE:
+               case LINETYPE:
+                       size += lwline_to_wkb_size((LWLINE*)geom, variant);
+                       break;
+
+               /* Polygon has nrings and rings elements */
+               case POLYGONTYPE:
+                       size += lwpoly_to_wkb_size((LWPOLY*)geom, variant);
+                       break;
+
+               /* All these Collection types have ngeoms and geoms elements */
+               case MULTIPOINTTYPE:
+               case MULTILINETYPE:
+               case MULTIPOLYGONTYPE:
+               case COMPOUNDTYPE:
+               case CURVEPOLYTYPE:
+               case MULTICURVETYPE:
+               case MULTISURFACETYPE:
+               case COLLECTIONTYPE:
+                       size += lwcollection_to_wkb_size((LWCOLLECTION*)geom, variant);
+                       break;
+
+               /* Unknown type! */
+               default:
+                       lwerror("Unsupported geometry type: %s [%d]", lwgeom_typename(geom->type), TYPE_GETTYPE(geom->type));
+       }
+       /* Return value to keep compiler happy. */
+       return 0;
+}
+
+static char* lwgeom_to_wkb_buf(const LWGEOM *geom, char *buf, uchar variant)
+{
+
+       if ( lwgeom_is_empty(geom) )
+               return empty_to_wkb_buf(geom, buf, variant);
+
+       switch( TYPE_GETTYPE(geom->type) )
+       {
+               case POINTTYPE:
+                       return lwpoint_to_wkb_buf((LWPOINT*)geom, buf, variant);
+
+               /* LineString and CircularString both have 'points' elements */
+               case CIRCSTRINGTYPE:
+               case LINETYPE:
+                       return lwline_to_wkb_buf((LWLINE*)geom, buf, variant);
+
+               /* Polygon has 'nrings' and 'rings' elements */
+               case POLYGONTYPE:
+                       return lwpoly_to_wkb_buf((LWPOLY*)geom, buf, variant);
+
+               /* All these Collection types have 'ngeoms' and 'geoms' elements */
+               case MULTIPOINTTYPE:
+               case MULTILINETYPE:
+               case MULTIPOLYGONTYPE:
+               case COMPOUNDTYPE:
+               case CURVEPOLYTYPE:
+               case MULTICURVETYPE:
+               case MULTISURFACETYPE:
+               case COLLECTIONTYPE:
+                       return lwcollection_to_wkb_buf((LWCOLLECTION*)geom, buf, variant);
+
+               /* Unknown type! */
+               default:
+                       lwerror("Unsupported geometry type: %s [%d]", lwgeom_typename(geom->type), TYPE_GETTYPE(geom->type));
+       }
+       /* Return value to keep compiler happy. */
+       return 0;
+}
+
+/**
+* Convert LWGEOM to a char* in WKB format. Caller is responsible for freeing
+* the returned array.
+*
+* Accepts variants:
+* One of: WKB_ISO, WKB_EXTENDED, WKB_SFSQL
+* Any of: WKB_NDR
+* For example, a variant = ( WKT_ISO | WKT_NDR ) would return the little-endian 
+* ISO form of WKB.
+*/
+char* lwgeom_to_wkb(const LWGEOM *geom, uchar variant, size_t *size_out)
+{
+       size_t buf_size;
+       char *buf = NULL;
+       char *wkb_out = NULL;
+       
+       /* Initialize output size */
+       if( size_out ) *size_out = 0;
+       
+       if( geom == NULL ) 
+       {
+               lwerror("Cannot convert NULL into WKB.");
+               return NULL;
+       }
+       
+       /* Calculate the required size of the output buffer */
+       buf_size = lwgeom_to_wkb_size(geom, variant);
+       LWDEBUGF(4, "WKB output size: %d", buf_size);
+
+       if( buf_size == 0 )
+       {
+               lwerror("Error calculating output WKB buffer size.");
+               return NULL;
+       }
+       
+       buf = lwalloc(buf_size);
+       
+       if( buf == NULL )
+       {
+               lwerror("Unable to allocate %d bytes for WKB output buffer.", buf_size);
+               return NULL;
+       }
+               
+       /* Retain a pointer to the front of the buffer for later */
+       wkb_out = buf;
+       
+       /* Write the WKB into the output buffer */
+       buf = lwgeom_to_wkb_buf(geom, buf, variant);
+       
+       /* The buffer pointer should land at the end of the allocated buffer space. Let's check. */
+       if( buf_size != (buf - wkb_out) )
+       {
+               lwerror("Output WKB is not the same size as the allocated buffer.");
+               lwfree(wkb_out);
+               return NULL;
+       }
+       
+       return wkb_out;
+}
index 102831c2695aea1c483a5426b886008130629be9..b4cf31477607825389def3b8f45f02c31e9e89a1 100644 (file)
@@ -522,9 +522,14 @@ static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precisi
 }
 
 /**
-* Public WKT emitter function
+* WKT emitter function. Allocates a new *char and fills it with the WKT 
+* representation. If size_out is not NULL, it will be set to the size of the
+* allocated *char.
+* 
+* Accepts variants:
+* One of: WKT_ISO, WKT_SFSQL, WKT_EXTENDED
 */
-char *lwgeom_to_wkt(const LWGEOM *geom, int precision, uchar variant)
+char* lwgeom_to_wkt(const LWGEOM *geom, uchar variant, int precision, size_t *size_out)
 {
        stringbuffer_t *sb;
        char *str = NULL;
@@ -542,7 +547,9 @@ char *lwgeom_to_wkt(const LWGEOM *geom, int precision, uchar variant)
                lwerror("Uh oh");
                return NULL;
        }
-       str = strdup(stringbuffer_getstring(sb));
+       str = stringbuffer_getstringcopy(sb);
+       if( size_out ) 
+               *size_out = stringbuffer_getlength(sb) + 1;
        stringbuffer_destroy(sb);
        return str;
 }