/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include "../postgis_config.h" /*#define POSTGIS_DEBUG_LEVEL 4*/ #include "liblwgeom_internal.h" /* NOTE: includes lwgeom_log.h */ #include "lwgeom_log.h" #include #include /** Max depth in a geometry. Matches the default YYINITDEPTH for WKT */ #define LW_PARSER_MAX_DEPTH 200 /** * Used for passing the parse state between the parsing functions. */ typedef struct { const uint8_t *wkb; /* Points to start of WKB */ int32_t srid; /* Current SRID we are handling */ size_t wkb_size; /* Expected size of WKB */ int8_t swap_bytes; /* Do an endian flip? */ int8_t check; /* Simple validity checks on geometries */ int8_t lwtype; /* Current type we are handling */ int8_t has_z; /* Z? */ int8_t has_m; /* M? */ int8_t has_srid; /* SRID? */ int8_t error; /* An error was found (not enough bytes to read) */ uint8_t depth; /* Current recursion level (to prevent stack overflows). Maxes at LW_PARSER_MAX_DEPTH */ const uint8_t *pos; /* Current parse position */ } wkb_parse_state; /** * Internal function declarations. */ LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s); /**********************************************************************/ /* Our static character->number map. Anything > 15 is invalid */ static uint8_t hex2char[256] = { /* not Hex characters */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* 0-9 */ 0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20, /* A-F */ 20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20, /* not Hex characters */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* a-f */ 20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* not Hex characters (upper 128 characters) */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 }; uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize) { uint8_t *buf = NULL; register uint8_t h1, h2; uint32_t i; if( hexsize % 2 ) lwerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize); buf = lwalloc(hexsize/2); if( ! buf ) lwerror("Unable to allocate memory buffer."); for( i = 0; i < hexsize/2; i++ ) { h1 = hex2char[(int)hexbuf[2*i]]; h2 = hex2char[(int)hexbuf[2*i+1]]; if( h1 > 15 ) lwerror("Invalid hex character (%c) encountered", hexbuf[2*i]); if( h2 > 15 ) lwerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]); /* First character is high bits, second is low bits */ buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F); } return buf; } /**********************************************************************/ /** * Check that we are not about to read off the end of the WKB * array. */ static inline void wkb_parse_state_check(wkb_parse_state *s, size_t next) { if( (s->pos + next) > (s->wkb + s->wkb_size) ) { lwerror("WKB structure does not match expected size!"); s->error = LW_TRUE; } } /** * Take in an unknown kind of wkb type number and ensure it comes out * as an extended WKB type number (with Z/M/SRID flags masked onto the * high bits). */ static void lwtype_from_wkb_state(wkb_parse_state *s, uint32_t wkb_type) { uint32_t wkb_simple_type; LWDEBUG(4, "Entered function"); s->has_z = LW_FALSE; s->has_m = LW_FALSE; s->has_srid = LW_FALSE; /* If any of the higher bits are set, this is probably an extended type. */ if( wkb_type & 0xF0000000 ) { if( wkb_type & WKBZOFFSET ) s->has_z = LW_TRUE; if( wkb_type & WKBMOFFSET ) s->has_m = LW_TRUE; if( wkb_type & WKBSRIDFLAG ) s->has_srid = LW_TRUE; LWDEBUGF(4, "Extended type: has_z=%d has_m=%d has_srid=%d", s->has_z, s->has_m, s->has_srid); } /* Mask off the flags */ wkb_type = wkb_type & 0x0FFFFFFF; /* Catch strange Oracle WKB type numbers */ if ( wkb_type >= 4000 ) { lwerror("Unknown WKB type (%d)!", wkb_type); return; } /* Strip out just the type number (1-12) from the ISO number (eg 3001-3012) */ wkb_simple_type = wkb_type % 1000; /* Extract the Z/M information from ISO style numbers */ if( wkb_type >= 3000 && wkb_type < 4000 ) { s->has_z = LW_TRUE; s->has_m = LW_TRUE; } else if ( wkb_type >= 2000 && wkb_type < 3000 ) { s->has_m = LW_TRUE; } else if ( wkb_type >= 1000 && wkb_type < 2000 ) { s->has_z = LW_TRUE; } switch (wkb_simple_type) { case WKB_POINT_TYPE: s->lwtype = POINTTYPE; break; case WKB_LINESTRING_TYPE: s->lwtype = LINETYPE; break; case WKB_POLYGON_TYPE: s->lwtype = POLYGONTYPE; break; case WKB_MULTIPOINT_TYPE: s->lwtype = MULTIPOINTTYPE; break; case WKB_MULTILINESTRING_TYPE: s->lwtype = MULTILINETYPE; break; case WKB_MULTIPOLYGON_TYPE: s->lwtype = MULTIPOLYGONTYPE; break; case WKB_GEOMETRYCOLLECTION_TYPE: s->lwtype = COLLECTIONTYPE; break; case WKB_CIRCULARSTRING_TYPE: s->lwtype = CIRCSTRINGTYPE; break; case WKB_COMPOUNDCURVE_TYPE: s->lwtype = COMPOUNDTYPE; break; case WKB_CURVEPOLYGON_TYPE: s->lwtype = CURVEPOLYTYPE; break; case WKB_MULTICURVE_TYPE: s->lwtype = MULTICURVETYPE; break; case WKB_MULTISURFACE_TYPE: s->lwtype = MULTISURFACETYPE; break; case WKB_POLYHEDRALSURFACE_TYPE: s->lwtype = POLYHEDRALSURFACETYPE; break; case WKB_TIN_TYPE: s->lwtype = TINTYPE; break; case WKB_TRIANGLE_TYPE: s->lwtype = TRIANGLETYPE; break; /* PostGIS 1.5 emits 13, 14 for CurvePolygon, MultiCurve */ /* These numbers aren't SQL/MM (numbers currently only */ /* go up to 12. We can handle the old data here (for now??) */ /* converting them into the lwtypes that are intended. */ case WKB_CURVE_TYPE: s->lwtype = CURVEPOLYTYPE; break; case WKB_SURFACE_TYPE: s->lwtype = MULTICURVETYPE; break; default: /* Error! */ lwerror("Unknown WKB type (%d)! Full WKB type number was (%d).", wkb_simple_type, wkb_type); break; } LWDEBUGF(4,"Got lwtype %s (%u)", lwtype_name(s->lwtype), s->lwtype); return; } /** * Byte * Read a byte and advance the parse state forward. */ static char byte_from_wkb_state(wkb_parse_state *s) { char char_value = 0; LWDEBUG(4, "Entered function"); wkb_parse_state_check(s, WKB_BYTE_SIZE); if (s->error) return 0; LWDEBUG(4, "Passed state check"); char_value = s->pos[0]; LWDEBUGF(4, "Read byte value: %x", char_value); s->pos += WKB_BYTE_SIZE; return char_value; } /** * Int32 * Read 4-byte integer and advance the parse state forward. */ static uint32_t integer_from_wkb_state(wkb_parse_state *s) { uint32_t i = 0; wkb_parse_state_check(s, WKB_INT_SIZE); if (s->error) return 0; memcpy(&i, s->pos, WKB_INT_SIZE); /* Swap? Copy into a stack-allocated integer. */ if( s->swap_bytes ) { int j = 0; uint8_t tmp; for( j = 0; j < WKB_INT_SIZE/2; j++ ) { tmp = ((uint8_t*)(&i))[j]; ((uint8_t*)(&i))[j] = ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1]; ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1] = tmp; } } s->pos += WKB_INT_SIZE; return i; } /** * Double * Read an 8-byte double and advance the parse state forward. */ static double double_from_wkb_state(wkb_parse_state *s) { double d = 0; memcpy(&d, s->pos, WKB_DOUBLE_SIZE); /* Swap? Copy into a stack-allocated integer. */ if( s->swap_bytes ) { int i = 0; uint8_t tmp; for( i = 0; i < WKB_DOUBLE_SIZE/2; i++ ) { tmp = ((uint8_t*)(&d))[i]; ((uint8_t*)(&d))[i] = ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1]; ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1] = tmp; } } s->pos += WKB_DOUBLE_SIZE; return d; } /** * POINTARRAY * Read a dynamically sized point array and advance the parse state forward. * First read the number of points, then read the points. */ static POINTARRAY* ptarray_from_wkb_state(wkb_parse_state *s) { POINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; uint32_t npoints = 0; static uint32_t maxpoints = UINT_MAX / WKB_DOUBLE_SIZE / 4; /* Calculate the size of this point array. */ npoints = integer_from_wkb_state(s); if (s->error) return NULL; if (npoints > maxpoints) { s->error = LW_TRUE; lwerror("Pointarray length (%d) is too large", npoints); return NULL; } LWDEBUGF(4,"Pointarray has %d points", npoints); if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = npoints * ndims * WKB_DOUBLE_SIZE; /* Empty! */ if( npoints == 0 ) return ptarray_construct(s->has_z, s->has_m, npoints); /* Does the data we want to read exist? */ wkb_parse_state_check(s, pa_size); if (s->error) return NULL; /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately. */ else { uint32_t i = 0; double *dlist; pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < npoints * ndims; i++ ) { dlist[i] = double_from_wkb_state(s); } } return pa; } /** * POINT * Read a WKB point, starting just after the endian byte, * type number and optional srid number. * Advance the parse state forward appropriately. * WKB point has just a set of doubles, with the quantity depending on the * dimension of the point, so this looks like a special case of the above * with only one point. */ static LWPOINT* lwpoint_from_wkb_state(wkb_parse_state *s) { static uint32_t npoints = 1; POINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; const POINT2D *pt; /* Count the dimensions. */ if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = ndims * WKB_DOUBLE_SIZE; /* Does the data we want to read exist? */ wkb_parse_state_check(s, pa_size); if (s->error) return NULL; /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately */ else { uint32_t i = 0; double *dlist; pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < ndims; i++ ) { dlist[i] = double_from_wkb_state(s); } } /* Check for POINT(NaN NaN) ==> POINT EMPTY */ pt = getPoint2d_cp(pa, 0); if ( isnan(pt->x) && isnan(pt->y) ) { ptarray_free(pa); return lwpoint_construct_empty(s->srid, s->has_z, s->has_m); } else { return lwpoint_construct(s->srid, NULL, pa); } } /** * LINESTRING * Read a WKB linestring, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * There is only one pointarray in a linestring. Optionally * check for minimal following of rules (two point minimum). */ static LWLINE* lwline_from_wkb_state(wkb_parse_state *s) { POINTARRAY *pa = ptarray_from_wkb_state(s); if (s->error) return NULL; if( pa == NULL || pa->npoints == 0 ) { if (pa) ptarray_free(pa); return lwline_construct_empty(s->srid, s->has_z, s->has_m); } if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) { lwerror("%s must have at least two points", lwtype_name(s->lwtype)); return NULL; } return lwline_construct(s->srid, NULL, pa); } /** * CIRCULARSTRING * Read a WKB circularstring, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * There is only one pointarray in a linestring. Optionally * check for minimal following of rules (three point minimum, * odd number of points). */ static LWCIRCSTRING* lwcircstring_from_wkb_state(wkb_parse_state *s) { POINTARRAY *pa = ptarray_from_wkb_state(s); if (s->error) return NULL; if( pa == NULL || pa->npoints == 0 ) { if (pa) ptarray_free(pa); return lwcircstring_construct_empty(s->srid, s->has_z, s->has_m); } if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 3 ) { lwerror("%s must have at least three points", lwtype_name(s->lwtype)); return NULL; } if( s->check & LW_PARSER_CHECK_ODD && ! (pa->npoints % 2) ) { lwerror("%s must have an odd number of points", lwtype_name(s->lwtype)); return NULL; } return lwcircstring_construct(s->srid, NULL, pa); } /** * POLYGON * Read a WKB polygon, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * First read the number of rings, then read each ring * (which are structured as point arrays) */ static LWPOLY* lwpoly_from_wkb_state(wkb_parse_state *s) { uint32_t nrings = integer_from_wkb_state(s); if (s->error) return NULL; uint32_t i = 0; LWPOLY *poly = lwpoly_construct_empty(s->srid, s->has_z, s->has_m); LWDEBUGF(4,"Polygon has %d rings", nrings); /* Empty polygon? */ if( nrings == 0 ) return poly; for( i = 0; i < nrings; i++ ) { POINTARRAY *pa = ptarray_from_wkb_state(s); if (pa == NULL) { lwpoly_free(poly); return NULL; } /* Check for at least four points. */ if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4) { lwpoly_free(poly); ptarray_free(pa); LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype)); lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype)); return NULL; } /* Check that first and last points are the same. */ if( s->check & LW_PARSER_CHECK_CLOSURE && ! ptarray_is_closed_2d(pa) ) { lwpoly_free(poly); ptarray_free(pa); LWDEBUGF(2, "%s must have closed rings", lwtype_name(s->lwtype)); lwerror("%s must have closed rings", lwtype_name(s->lwtype)); return NULL; } /* Add ring to polygon */ if ( lwpoly_add_ring(poly, pa) == LW_FAILURE ) { lwpoly_free(poly); ptarray_free(pa); LWDEBUG(2, "Unable to add ring to polygon"); lwerror("Unable to add ring to polygon"); return NULL; } } return poly; } /** * TRIANGLE * Read a WKB triangle, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * Triangles are encoded like polygons in WKB, but more like linestrings * as lwgeometries. */ static LWTRIANGLE* lwtriangle_from_wkb_state(wkb_parse_state *s) { uint32_t nrings = integer_from_wkb_state(s); if (s->error) return NULL; /* Empty triangle? */ if( nrings == 0 ) return lwtriangle_construct_empty(s->srid, s->has_z, s->has_m); /* Should be only one ring. */ if (nrings != 1) { lwerror("Triangle has wrong number of rings: %d", nrings); } /* There's only one ring, we hope? */ POINTARRAY *pa = ptarray_from_wkb_state(s); /* If there's no points, return an empty triangle. */ if (pa == NULL) return lwtriangle_construct_empty(s->srid, s->has_z, s->has_m); /* Check for at least four points. */ if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4) { ptarray_free(pa); lwerror("%s must have at least four points", lwtype_name(s->lwtype)); return NULL; } if (s->check & LW_PARSER_CHECK_ZCLOSURE && !ptarray_is_closed_z(pa)) { ptarray_free(pa); lwerror("%s must have closed rings", lwtype_name(s->lwtype)); return NULL; } /* Empty TRIANGLE starts w/ empty POINTARRAY, free it first */ return lwtriangle_construct(s->srid, NULL, pa); } /** * CURVEPOLYTYPE */ static LWCURVEPOLY* lwcurvepoly_from_wkb_state(wkb_parse_state *s) { uint32_t ngeoms = integer_from_wkb_state(s); if (s->error) return NULL; LWCURVEPOLY *cp = lwcurvepoly_construct_empty(s->srid, s->has_z, s->has_m); LWGEOM *geom = NULL; uint32_t i; /* Empty collection? */ if ( ngeoms == 0 ) return cp; for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_wkb_state(s); if ( lwcurvepoly_add_ring(cp, geom) == LW_FAILURE ) { lwgeom_free(geom); lwgeom_free((LWGEOM *)cp); lwerror("Unable to add geometry (%p) to curvepoly (%p)", geom, cp); return NULL; } } return cp; } /** * POLYHEDRALSURFACETYPE */ /** * COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE, COMPOUNDTYPE, * MULTICURVETYPE, MULTISURFACETYPE, * TINTYPE */ static LWCOLLECTION* lwcollection_from_wkb_state(wkb_parse_state *s) { uint32_t ngeoms = integer_from_wkb_state(s); if (s->error) return NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, s->srid, s->has_z, s->has_m); LWGEOM *geom = NULL; uint32_t i; LWDEBUGF(4,"Collection has %d components", ngeoms); /* Empty collection? */ if ( ngeoms == 0 ) return col; /* Be strict in polyhedral surface closures */ if ( s->lwtype == POLYHEDRALSURFACETYPE ) s->check |= LW_PARSER_CHECK_ZCLOSURE; s->depth++; if (s->depth >= LW_PARSER_MAX_DEPTH) { lwcollection_free(col); lwerror("Geometry has too many chained collections"); return NULL; } for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_wkb_state(s); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwgeom_free(geom); lwgeom_free((LWGEOM *)col); lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } s->depth--; return col; } /** * GEOMETRY * Generic handling for WKB geometries. The front of every WKB geometry * (including those embedded in collections) is an endian byte, a type * number and an optional srid number. We handle all those here, then pass * to the appropriate handler for the specific type. */ LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s) { char wkb_little_endian; uint32_t wkb_type; LWDEBUG(4,"Entered function"); /* Fail when handed incorrect starting byte */ wkb_little_endian = byte_from_wkb_state(s); if (s->error) return NULL; if( wkb_little_endian != 1 && wkb_little_endian != 0 ) { LWDEBUG(4,"Leaving due to bad first byte!"); lwerror("Invalid endian flag value encountered."); return NULL; } /* Check the endianness of our input */ s->swap_bytes = LW_FALSE; /* Machine arch is big endian, request is for little */ if (IS_BIG_ENDIAN && wkb_little_endian) s->swap_bytes = LW_TRUE; /* Machine arch is little endian, request is for big */ else if ((!IS_BIG_ENDIAN) && (!wkb_little_endian)) s->swap_bytes = LW_TRUE; /* Read the type number */ wkb_type = integer_from_wkb_state(s); if (s->error) return NULL; LWDEBUGF(4,"Got WKB type number: 0x%X", wkb_type); lwtype_from_wkb_state(s, wkb_type); /* Read the SRID, if necessary */ if( s->has_srid ) { s->srid = clamp_srid(integer_from_wkb_state(s)); if (s->error) return NULL; /* TODO: warn on explicit UNKNOWN srid ? */ LWDEBUGF(4,"Got SRID: %u", s->srid); } /* Do the right thing */ switch( s->lwtype ) { case POINTTYPE: return (LWGEOM*)lwpoint_from_wkb_state(s); break; case LINETYPE: return (LWGEOM*)lwline_from_wkb_state(s); break; case CIRCSTRINGTYPE: return (LWGEOM*)lwcircstring_from_wkb_state(s); break; case POLYGONTYPE: return (LWGEOM*)lwpoly_from_wkb_state(s); break; case TRIANGLETYPE: return (LWGEOM*)lwtriangle_from_wkb_state(s); break; case CURVEPOLYTYPE: return (LWGEOM*)lwcurvepoly_from_wkb_state(s); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM*)lwcollection_from_wkb_state(s); break; /* Unknown type! */ default: lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(s->lwtype)); } /* Return value to keep compiler happy. */ return NULL; } /* TODO add check for SRID consistency */ /** * WKB inputs *must* have a declared size, to prevent malformed WKB from reading * off the end of the memory segment (this stops a malevolent user from declaring * a one-ring polygon to have 10 rings, causing the WKB reader to walk off the * end of the memory). * * Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD, * LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL */ LWGEOM* lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check) { wkb_parse_state s; /* Initialize the state appropriately */ s.wkb = wkb; s.wkb_size = wkb_size; s.swap_bytes = LW_FALSE; s.check = check; s.lwtype = 0; s.srid = SRID_UNKNOWN; s.has_z = LW_FALSE; s.has_m = LW_FALSE; s.has_srid = LW_FALSE; s.error = LW_FALSE; s.pos = wkb; s.depth = 1; if (!wkb || !wkb_size) return NULL; return lwgeom_from_wkb_state(&s); } LWGEOM* lwgeom_from_hexwkb(const char *hexwkb, const char check) { int hexwkb_len; uint8_t *wkb; LWGEOM *lwgeom; if ( ! hexwkb ) { lwerror("lwgeom_from_hexwkb: null input"); return NULL; } hexwkb_len = strlen(hexwkb); wkb = bytes_from_hexbytes(hexwkb, hexwkb_len); lwgeom = lwgeom_from_wkb(wkb, hexwkb_len/2, check); lwfree(wkb); return lwgeom; }