Coverage Report

Created: 2026-07-16 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwin_wkb.c
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * PostGIS - Spatial Types for PostgreSQL
4
 * http://postgis.net
5
 *
6
 * PostGIS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * PostGIS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 **********************************************************************
20
 *
21
 * Copyright (C) 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22
 *
23
 **********************************************************************/
24
25
26
#include "../postgis_config.h"
27
/*#define POSTGIS_DEBUG_LEVEL 4*/
28
#include "liblwgeom_internal.h" /* NOTE: includes lwgeom_log.h */
29
#include "lwgeom_log.h"
30
#include <math.h>
31
#include <limits.h>
32
33
/** Max depth in a geometry. Matches the default YYINITDEPTH for WKT */
34
0
#define LW_PARSER_MAX_DEPTH 200
35
36
/**
37
* Used for passing the parse state between the parsing functions.
38
*/
39
typedef struct
40
{
41
  const uint8_t *wkb; /* Points to start of WKB */
42
  int32_t srid;    /* Current SRID we are handling */
43
  size_t wkb_size; /* Expected size of WKB */
44
  int8_t swap_bytes;  /* Do an endian flip? */
45
  int8_t check;       /* Simple validity checks on geometries */
46
  int8_t lwtype;      /* Current type we are handling */
47
  int8_t has_z;       /* Z? */
48
  int8_t has_m;       /* M? */
49
  int8_t has_srid;    /* SRID? */
50
  int8_t error;       /* An error was found (not enough bytes to read) */
51
  uint8_t depth;      /* Current recursion level (to prevent stack overflows). Maxes at LW_PARSER_MAX_DEPTH */
52
  const uint8_t *pos; /* Current parse position */
53
} wkb_parse_state;
54
55
56
/**
57
* Internal function declarations.
58
*/
59
LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s);
60
61
62
63
/**********************************************************************/
64
65
/* Our static character->number map. Anything > 15 is invalid */
66
static uint8_t hex2char[256] = {
67
    /* not Hex characters */
68
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
69
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
70
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
71
    /* 0-9 */
72
    0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20,
73
    /* A-F */
74
    20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
75
    /* not Hex characters */
76
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
77
  /* a-f */
78
    20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
79
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
80
    /* not Hex characters (upper 128 characters) */
81
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
82
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
83
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
84
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
85
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
86
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
87
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
88
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
89
    };
90
91
92
uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
93
0
{
94
0
  uint8_t *buf = NULL;
95
0
  register uint8_t h1, h2;
96
0
  uint32_t i;
97
98
0
  if( hexsize % 2 )
99
0
    lwerror("Invalid hex string, length (%zu) has to be a multiple of two!", hexsize);
100
101
0
  buf = lwalloc(hexsize/2);
102
103
0
  if( ! buf )
104
0
    lwerror("Unable to allocate memory buffer.");
105
106
0
  for( i = 0; i < hexsize/2; i++ )
107
0
  {
108
0
    h1 = hex2char[(uint8_t)hexbuf[2*i]];
109
0
    h2 = hex2char[(uint8_t)hexbuf[2*i+1]];
110
0
    if( h1 > 15 )
111
0
      lwerror("Invalid hex character (%c) encountered", hexbuf[2*i]);
112
0
    if( h2 > 15 )
113
0
      lwerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]);
114
    /* First character is high bits, second is low bits */
115
0
    buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F);
116
0
  }
117
0
  return buf;
118
0
}
119
120
121
/**********************************************************************/
122
123
124
125
/**
126
* Check that we are not about to read off the end of the WKB
127
* array.
128
*/
129
static inline void wkb_parse_state_check(wkb_parse_state *s, size_t next)
130
0
{
131
0
  if( (s->pos + next) > (s->wkb + s->wkb_size) )
132
0
  {
133
0
    lwerror("WKB structure does not match expected size!");
134
0
    s->error = LW_TRUE;
135
0
  }
136
0
}
137
138
/**
139
 * Resolve a raw WKB type number into the parser state' internal type and flags.
140
 *
141
 * Interprets WKB type encodings (including extended flag bits and ISO-style
142
 * 1000/2000/3000 modifiers) and updates the provided parse state:
143
 * - sets s->has_z, s->has_m, s->has_srid according to detected flags,
144
 * - sets s->lwtype to the corresponding internal lwgeom type enum.
145
 *
146
 * Recognizes standard WKB types, ISO extended encodings (where 1000/2000/3000
147
 * ranges indicate Z/M presence), explicit high-bit flag encodings, and a few
148
 * legacy PostGIS values (e.g., old Curve/Surface codes). On unrecognized or
149
 * out-of-range type numbers an error is reported via lwerror and the state is
150
 * left with whatever values were set before the error.
151
 */
152
static void lwtype_from_wkb_state(wkb_parse_state *s, uint32_t wkb_type)
153
0
{
154
0
  uint32_t wkb_simple_type;
155
156
0
  LWDEBUG(4, "Entered function");
157
158
0
  s->has_z = LW_FALSE;
159
0
  s->has_m = LW_FALSE;
160
0
  s->has_srid = LW_FALSE;
161
162
  /* If any of the higher bits are set, this is probably an extended type. */
163
0
  if( wkb_type & 0xF0000000 )
164
0
  {
165
0
    if( wkb_type & WKBZOFFSET ) s->has_z = LW_TRUE;
166
0
    if( wkb_type & WKBMOFFSET ) s->has_m = LW_TRUE;
167
0
    if( wkb_type & WKBSRIDFLAG ) s->has_srid = LW_TRUE;
168
0
    LWDEBUGF(4, "Extended type: has_z=%d has_m=%d has_srid=%d", s->has_z, s->has_m, s->has_srid);
169
0
  }
170
171
  /* Mask off the flags */
172
0
  wkb_type = wkb_type & 0x0FFFFFFF;
173
174
  /* Catch strange Oracle WKB type numbers */
175
0
  if ( wkb_type >= 4000 ) {
176
0
    lwerror("Unknown WKB type (%d)!", wkb_type);
177
0
    return;
178
0
  }
179
180
  /* Strip out just the type number (1-12) from the ISO number (eg 3001-3012) */
181
0
  wkb_simple_type = wkb_type % 1000;
182
183
  /* Extract the Z/M information from ISO style numbers */
184
0
  if( wkb_type >= 3000 && wkb_type < 4000 )
185
0
  {
186
0
    s->has_z = LW_TRUE;
187
0
    s->has_m = LW_TRUE;
188
0
  }
189
0
  else if ( wkb_type >= 2000 && wkb_type < 3000 )
190
0
  {
191
0
    s->has_m = LW_TRUE;
192
0
  }
193
0
  else if ( wkb_type >= 1000 && wkb_type < 2000 )
194
0
  {
195
0
    s->has_z = LW_TRUE;
196
0
  }
197
198
0
  switch (wkb_simple_type)
199
0
  {
200
0
    case WKB_POINT_TYPE:
201
0
      s->lwtype = POINTTYPE;
202
0
      break;
203
0
    case WKB_LINESTRING_TYPE:
204
0
      s->lwtype = LINETYPE;
205
0
      break;
206
0
    case WKB_POLYGON_TYPE:
207
0
      s->lwtype = POLYGONTYPE;
208
0
      break;
209
0
    case WKB_MULTIPOINT_TYPE:
210
0
      s->lwtype = MULTIPOINTTYPE;
211
0
      break;
212
0
    case WKB_MULTILINESTRING_TYPE:
213
0
      s->lwtype = MULTILINETYPE;
214
0
      break;
215
0
    case WKB_MULTIPOLYGON_TYPE:
216
0
      s->lwtype = MULTIPOLYGONTYPE;
217
0
      break;
218
0
    case WKB_GEOMETRYCOLLECTION_TYPE:
219
0
      s->lwtype = COLLECTIONTYPE;
220
0
      break;
221
0
    case WKB_CIRCULARSTRING_TYPE:
222
0
      s->lwtype = CIRCSTRINGTYPE;
223
0
      break;
224
0
    case WKB_COMPOUNDCURVE_TYPE:
225
0
      s->lwtype = COMPOUNDTYPE;
226
0
      break;
227
0
    case WKB_CURVEPOLYGON_TYPE:
228
0
      s->lwtype = CURVEPOLYTYPE;
229
0
      break;
230
0
    case WKB_MULTICURVE_TYPE:
231
0
      s->lwtype = MULTICURVETYPE;
232
0
      break;
233
0
    case WKB_MULTISURFACE_TYPE:
234
0
      s->lwtype = MULTISURFACETYPE;
235
0
      break;
236
0
    case WKB_POLYHEDRALSURFACE_TYPE:
237
0
      s->lwtype = POLYHEDRALSURFACETYPE;
238
0
      break;
239
0
    case WKB_TIN_TYPE:
240
0
      s->lwtype = TINTYPE;
241
0
      break;
242
0
    case WKB_TRIANGLE_TYPE:
243
0
      s->lwtype = TRIANGLETYPE;
244
0
      break;
245
0
    case WKB_NURBSCURVE_TYPE:
246
0
      s->lwtype = NURBSCURVETYPE;
247
0
      break;
248
249
    /* PostGIS 1.5 emits 13, 14 for CurvePolygon, MultiCurve */
250
    /* These numbers aren't SQL/MM (numbers currently only */
251
    /* go up to 12. We can handle the old data here (for now??) */
252
    /* converting them into the lwtypes that are intended. */
253
0
    case WKB_CURVE_TYPE:
254
0
      s->lwtype = CURVEPOLYTYPE;
255
0
      break;
256
0
    case WKB_SURFACE_TYPE:
257
0
      s->lwtype = MULTICURVETYPE;
258
0
      break;
259
260
0
    default: /* Error! */
261
0
      lwerror("Unknown WKB type (%d)! Full WKB type number was (%d).", wkb_simple_type, wkb_type);
262
0
      break;
263
0
  }
264
265
0
  LWDEBUGF(4,"Got lwtype %s (%u)", lwtype_name(s->lwtype), s->lwtype);
266
267
0
  return;
268
0
}
269
270
/**
271
* Byte
272
* Read a byte and advance the parse state forward.
273
*/
274
static char byte_from_wkb_state(wkb_parse_state *s)
275
0
{
276
0
  char char_value = 0;
277
0
  LWDEBUG(4, "Entered function");
278
279
0
  wkb_parse_state_check(s, WKB_BYTE_SIZE);
280
0
  if (s->error)
281
0
    return 0;
282
0
  LWDEBUG(4, "Passed state check");
283
284
0
  char_value = s->pos[0];
285
0
  LWDEBUGF(4, "Read byte value: %x", char_value);
286
0
  s->pos += WKB_BYTE_SIZE;
287
288
0
  return char_value;
289
0
}
290
291
292
/**
293
* Int32
294
* Read 4-byte integer and advance the parse state forward.
295
*/
296
static uint32_t integer_from_wkb_state(wkb_parse_state *s)
297
0
{
298
0
  uint32_t i = 0;
299
300
0
  wkb_parse_state_check(s, WKB_INT_SIZE);
301
0
  if (s->error)
302
0
    return 0;
303
304
0
  memcpy(&i, s->pos, WKB_INT_SIZE);
305
306
  /* Swap? Copy into a stack-allocated integer. */
307
0
  if( s->swap_bytes )
308
0
  {
309
0
    int j = 0;
310
0
    uint8_t tmp;
311
312
0
    for( j = 0; j < WKB_INT_SIZE/2; j++ )
313
0
    {
314
0
      tmp = ((uint8_t*)(&i))[j];
315
0
      ((uint8_t*)(&i))[j] = ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1];
316
0
      ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1] = tmp;
317
0
    }
318
0
  }
319
320
0
  s->pos += WKB_INT_SIZE;
321
0
  return i;
322
0
}
323
324
/**
325
* Double
326
* Read an 8-byte double and advance the parse state forward.
327
*/
328
static double double_from_wkb_state(wkb_parse_state *s)
329
0
{
330
0
  double d = 0;
331
332
  /* Check bounds before reading */
333
0
  wkb_parse_state_check(s, WKB_DOUBLE_SIZE);
334
0
  if (s->error) return 0.0;
335
336
0
  memcpy(&d, s->pos, WKB_DOUBLE_SIZE);
337
338
  /* Swap? Copy into a stack-allocated integer. */
339
0
  if( s->swap_bytes )
340
0
  {
341
0
    int i = 0;
342
0
    uint8_t tmp;
343
344
0
    for( i = 0; i < WKB_DOUBLE_SIZE/2; i++ )
345
0
    {
346
0
      tmp = ((uint8_t*)(&d))[i];
347
0
      ((uint8_t*)(&d))[i] = ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1];
348
0
      ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1] = tmp;
349
0
    }
350
0
  }
351
352
0
  s->pos += WKB_DOUBLE_SIZE;
353
0
  return d;
354
0
}
355
356
357
358
/**
359
* Set the swap bytes flag depending on the endianness
360
* of the machine and the endianness of the input data.
361
* If they differ, we must swap, otherwise we can copy.
362
*/
363
static void wkb_swap_bytes(wkb_parse_state *s)
364
0
{
365
0
  char wkb_little_endian;
366
367
  /* Fail when handed incorrect starting byte */
368
0
  wkb_little_endian = byte_from_wkb_state(s);
369
0
  if (s->error)
370
0
    lwerror("Invalid endian flag value encountered.");
371
372
0
  if( wkb_little_endian != 1 && wkb_little_endian != 0 )
373
0
    lwerror("Invalid endian flag value encountered.");
374
375
  /* Check the endianness of our input  */
376
0
  s->swap_bytes = LW_FALSE;
377
378
  /* Machine arch is big endian, request is for little */
379
0
  if (IS_BIG_ENDIAN && wkb_little_endian)
380
0
    s->swap_bytes = LW_TRUE;
381
  /* Machine arch is little endian, request is for big */
382
0
  else if ((!IS_BIG_ENDIAN) && (!wkb_little_endian))
383
0
    s->swap_bytes = LW_TRUE;
384
0
}
385
386
387
388
/**
389
* POINTARRAY
390
* Read a dynamically sized point array and advance the parse state forward.
391
* First read the number of points, then read the points.
392
*/
393
static POINTARRAY* ptarray_from_wkb_state(wkb_parse_state *s)
394
0
{
395
0
  POINTARRAY *pa = NULL;
396
0
  size_t pa_size;
397
0
  uint32_t npoints = 0;
398
0
  static uint32_t maxpoints = UINT_MAX / WKB_DOUBLE_SIZE / 4;
399
0
  uint32_t ndims = 2 + s->has_z + s->has_m;
400
401
  /* Calculate the size of this point array. */
402
0
  npoints = integer_from_wkb_state(s);
403
0
  if (s->error)
404
0
    return NULL;
405
406
0
  if (npoints > maxpoints)
407
0
  {
408
0
    s->error = LW_TRUE;
409
0
    lwerror("Pointarray length (%d) is too large", npoints);
410
0
    return NULL;
411
0
  }
412
413
0
  LWDEBUGF(4,"Pointarray has %d points", npoints);
414
415
  /* Empty! */
416
0
  if( npoints == 0 )
417
0
    return ptarray_construct(s->has_z, s->has_m, npoints);
418
419
  /* Does the data we want to read exist? */
420
0
  pa_size = (size_t)npoints * ndims * WKB_DOUBLE_SIZE;
421
0
  wkb_parse_state_check(s, pa_size);
422
0
  if (s->error)
423
0
    return NULL;
424
425
  /* If we're in a native endianness, we can just copy the data directly! */
426
0
  if( ! s->swap_bytes )
427
0
  {
428
0
    pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos);
429
0
    s->pos += pa_size;
430
0
  }
431
  /* Otherwise we have to read each double, separately. */
432
0
  else
433
0
  {
434
0
    uint32_t i = 0;
435
0
    double *dlist;
436
0
    pa = ptarray_construct(s->has_z, s->has_m, npoints);
437
0
    dlist = (double*)(pa->serialized_pointlist);
438
0
    for( i = 0; i < npoints * ndims; i++ )
439
0
    {
440
0
      dlist[i] = double_from_wkb_state(s);
441
0
    }
442
0
  }
443
444
0
  return pa;
445
0
}
446
447
/**
448
* POINT
449
* Read a WKB point, starting just after the endian byte,
450
* type number and optional srid number.
451
* Advance the parse state forward appropriately.
452
* WKB point has just a set of doubles, with the quantity depending on the
453
* dimension of the point, so this looks like a special case of the above
454
* with only one point.
455
*/
456
static LWPOINT* lwpoint_from_wkb_state(wkb_parse_state *s)
457
0
{
458
0
  static uint32_t npoints = 1;
459
0
  POINTARRAY *pa = NULL;
460
0
  size_t pa_size;
461
0
  uint32_t ndims = 2;
462
0
  const POINT2D *pt;
463
464
  /* Count the dimensions. */
465
0
  if( s->has_z ) ndims++;
466
0
  if( s->has_m ) ndims++;
467
0
  pa_size = ndims * WKB_DOUBLE_SIZE;
468
469
  /* Does the data we want to read exist? */
470
0
  wkb_parse_state_check(s, pa_size);
471
0
  if (s->error)
472
0
    return NULL;
473
474
  /* If we're in a native endianness, we can just copy the data directly! */
475
0
  if( ! s->swap_bytes )
476
0
  {
477
0
    pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos);
478
0
    s->pos += pa_size;
479
0
  }
480
  /* Otherwise we have to read each double, separately */
481
0
  else
482
0
  {
483
0
    uint32_t i = 0;
484
0
    double *dlist;
485
0
    pa = ptarray_construct(s->has_z, s->has_m, npoints);
486
0
    dlist = (double*)(pa->serialized_pointlist);
487
0
    for( i = 0; i < ndims; i++ )
488
0
    {
489
0
      dlist[i] = double_from_wkb_state(s);
490
0
    }
491
0
  }
492
493
  /* Check for POINT(NaN NaN) ==> POINT EMPTY */
494
0
  pt = getPoint2d_cp(pa, 0);
495
0
  if ( isnan(pt->x) && isnan(pt->y) )
496
0
  {
497
0
    ptarray_free(pa);
498
0
    return lwpoint_construct_empty(s->srid, s->has_z, s->has_m);
499
0
  }
500
0
  else
501
0
  {
502
0
    return lwpoint_construct(s->srid, NULL, pa);
503
0
  }
504
0
}
505
506
/**
507
* LINESTRING
508
* Read a WKB linestring, starting just after the endian byte,
509
* type number and optional srid number. Advance the parse state
510
* forward appropriately.
511
* There is only one pointarray in a linestring. Optionally
512
* check for minimal following of rules (two point minimum).
513
*/
514
static LWLINE* lwline_from_wkb_state(wkb_parse_state *s)
515
0
{
516
0
  POINTARRAY *pa = ptarray_from_wkb_state(s);
517
0
  if (s->error)
518
0
    return NULL;
519
520
0
  if( pa == NULL || pa->npoints == 0 )
521
0
  {
522
0
    if (pa)
523
0
      ptarray_free(pa);
524
0
    return lwline_construct_empty(s->srid, s->has_z, s->has_m);
525
0
  }
526
527
0
  if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 )
528
0
  {
529
0
    lwerror("%s must have at least two points", lwtype_name(s->lwtype));
530
0
    return NULL;
531
0
  }
532
533
0
  return lwline_construct(s->srid, NULL, pa);
534
0
}
535
536
/**
537
* CIRCULARSTRING
538
* Read a WKB circularstring, starting just after the endian byte,
539
* type number and optional srid number. Advance the parse state
540
* forward appropriately.
541
* There is only one pointarray in a linestring. Optionally
542
* check for minimal following of rules (three point minimum,
543
* odd number of points).
544
*/
545
static LWCIRCSTRING* lwcircstring_from_wkb_state(wkb_parse_state *s)
546
0
{
547
0
  POINTARRAY *pa = ptarray_from_wkb_state(s);
548
0
  if (s->error)
549
0
    return NULL;
550
551
0
  if( pa == NULL || pa->npoints == 0 )
552
0
  {
553
0
    if (pa)
554
0
      ptarray_free(pa);
555
0
    return lwcircstring_construct_empty(s->srid, s->has_z, s->has_m);
556
0
  }
557
558
0
  if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 3 )
559
0
  {
560
0
    lwerror("%s must have at least three points", lwtype_name(s->lwtype));
561
0
    return NULL;
562
0
  }
563
564
0
  if( s->check & LW_PARSER_CHECK_ODD && ! (pa->npoints % 2) )
565
0
  {
566
0
    lwerror("%s must have an odd number of points", lwtype_name(s->lwtype));
567
0
    return NULL;
568
0
  }
569
570
0
  return lwcircstring_construct(s->srid, NULL, pa);
571
0
}
572
573
/**
574
* POLYGON
575
* Read a WKB polygon, starting just after the endian byte,
576
* type number and optional srid number. Advance the parse state
577
* forward appropriately.
578
* First read the number of rings, then read each ring
579
* (which are structured as point arrays)
580
*/
581
static LWPOLY* lwpoly_from_wkb_state(wkb_parse_state *s)
582
0
{
583
0
  uint32_t nrings = integer_from_wkb_state(s);
584
0
  if (s->error)
585
0
    return NULL;
586
0
  uint32_t i = 0;
587
0
  LWPOLY *poly = lwpoly_construct_empty(s->srid, s->has_z, s->has_m);
588
589
0
  LWDEBUGF(4,"Polygon has %d rings", nrings);
590
591
  /* Empty polygon? */
592
0
  if( nrings == 0 )
593
0
    return poly;
594
595
0
  for( i = 0; i < nrings; i++ )
596
0
  {
597
0
    POINTARRAY *pa = ptarray_from_wkb_state(s);
598
0
    if (pa == NULL)
599
0
    {
600
0
      lwpoly_free(poly);
601
0
      return NULL;
602
0
    }
603
604
    /* Check for at least four points. */
605
0
    if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4)
606
0
    {
607
0
      lwpoly_free(poly);
608
0
      ptarray_free(pa);
609
0
      LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype));
610
0
      lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype));
611
0
      return NULL;
612
0
    }
613
614
    /* Check that first and last points are the same. */
615
0
    if( s->check & LW_PARSER_CHECK_CLOSURE && ! ptarray_is_closed_2d(pa) )
616
0
    {
617
0
      lwpoly_free(poly);
618
0
      ptarray_free(pa);
619
0
      LWDEBUGF(2, "%s must have closed rings", lwtype_name(s->lwtype));
620
0
      lwerror("%s must have closed rings", lwtype_name(s->lwtype));
621
0
      return NULL;
622
0
    }
623
624
    /* Skip zero-member rings, they add nothing */
625
0
    if ( pa->npoints == 0)
626
0
    {
627
0
      LWDEBUGF(2, "Skipping empty ring [%d]", i);
628
0
      ptarray_free(pa);
629
0
      continue;
630
0
    }
631
632
    /* Add ring to polygon */
633
0
    if ( lwpoly_add_ring(poly, pa) == LW_FAILURE )
634
0
    {
635
0
      lwpoly_free(poly);
636
0
      ptarray_free(pa);
637
0
      LWDEBUG(2, "Unable to add ring to polygon");
638
0
      lwerror("Unable to add ring to polygon");
639
0
      return NULL;
640
0
    }
641
642
0
  }
643
0
  return poly;
644
0
}
645
646
/**
647
* TRIANGLE
648
* Read a WKB triangle, starting just after the endian byte,
649
* type number and optional srid number. Advance the parse state
650
* forward appropriately.
651
* Triangles are encoded like polygons in WKB, but more like linestrings
652
* as lwgeometries.
653
*/
654
static LWTRIANGLE* lwtriangle_from_wkb_state(wkb_parse_state *s)
655
0
{
656
0
  uint32_t nrings = integer_from_wkb_state(s);
657
0
  if (s->error)
658
0
    return NULL;
659
660
  /* Empty triangle? */
661
0
  if( nrings == 0 )
662
0
    return lwtriangle_construct_empty(s->srid, s->has_z, s->has_m);
663
664
  /* Should be only one ring. */
665
0
  if (nrings != 1)
666
0
  {
667
0
    lwerror("Triangle has wrong number of rings: %d", nrings);
668
0
  }
669
670
  /* There's only one ring, we hope? */
671
0
  POINTARRAY *pa = ptarray_from_wkb_state(s);
672
673
  /* If there's no points, return an empty triangle. */
674
0
  if (pa == NULL)
675
0
    return lwtriangle_construct_empty(s->srid, s->has_z, s->has_m);
676
677
  /* Check for at least four points. */
678
0
  if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4)
679
0
  {
680
0
    ptarray_free(pa);
681
0
    lwerror("%s must have at least four points", lwtype_name(s->lwtype));
682
0
    return NULL;
683
0
  }
684
685
0
  if (s->check & LW_PARSER_CHECK_ZCLOSURE && !ptarray_is_closed_z(pa))
686
0
  {
687
0
    ptarray_free(pa);
688
0
    lwerror("%s must have closed rings", lwtype_name(s->lwtype));
689
0
    return NULL;
690
0
  }
691
692
  /* Empty TRIANGLE starts w/ empty POINTARRAY, free it first */
693
0
  return lwtriangle_construct(s->srid, NULL, pa);
694
0
}
695
696
/**
697
* CURVEPOLYTYPE
698
*/
699
static LWCURVEPOLY* lwcurvepoly_from_wkb_state(wkb_parse_state *s)
700
0
{
701
0
  uint32_t ngeoms = integer_from_wkb_state(s);
702
0
  if (s->error)
703
0
    return NULL;
704
0
  LWCURVEPOLY *cp = lwcurvepoly_construct_empty(s->srid, s->has_z, s->has_m);
705
0
  LWGEOM *geom = NULL;
706
0
  uint32_t i;
707
708
  /* Empty collection? */
709
0
  if ( ngeoms == 0 )
710
0
    return cp;
711
712
0
  s->depth++;
713
0
  if (s->depth >= LW_PARSER_MAX_DEPTH)
714
0
  {
715
0
    lwgeom_free((LWGEOM *)cp);
716
0
    lwerror("Geometry has too many chained curves");
717
0
    return NULL;
718
0
  }
719
0
  for ( i = 0; i < ngeoms; i++ )
720
0
  {
721
0
    geom = lwgeom_from_wkb_state(s);
722
0
    if ( lwcurvepoly_add_ring(cp, geom) == LW_FAILURE )
723
0
    {
724
0
      lwgeom_free(geom);
725
0
      lwgeom_free((LWGEOM *)cp);
726
0
      lwerror("Unable to add geometry (%p) to curvepoly (%p)", (void *) geom, (void *) cp);
727
0
      return NULL;
728
0
    }
729
0
  }
730
0
  s->depth--;
731
732
0
  return cp;
733
0
}
734
735
/**
736
* POLYHEDRALSURFACETYPE
737
*/
738
739
static LWCOLLECTION* lwcollection_from_wkb_state(wkb_parse_state *s);
740
static LWNURBSCURVE* lwnurbscurve_from_wkb_state(wkb_parse_state *s);
741
742
/**
743
 * Parse a collection (MULTI types, COLLECTION, TINTYPE, COMPOUND, CURVEPOLY, etc.) from WKB state.
744
 *
745
 * Reads the number of component geometries from the WKB parse state, constructs an
746
 * empty LWCOLLECTION of the current s->lwtype/srid/dimension flags, then iteratively
747
 * parses and appends each component geometry using lwgeom_from_wkb_state().
748
 *
749
 * Behavior and side effects:
750
 * - Returns a newly allocated LWCOLLECTION containing the parsed components, or NULL on error.
751
 * - For an empty collection (component count == 0) returns the empty collection.
752
 * - If s->lwtype == POLYHEDRALSURFACETYPE, enables strict Z-closure checking by setting
753
 *   the LW_PARSER_CHECK_ZCLOSURE flag in s->check before parsing components.
754
 * - Increments s->depth while parsing to enforce recursion limits, if depth exceeds
755
 *   LW_PARSER_MAX_DEPTH the function frees allocated resources, reports an error, and returns NULL.
756
 * - On failure to parse or to add a component, frees any allocated geometry/collection and returns NULL.
757
 *
758
 * Return:
759
 *   Pointer to a newly constructed LWCOLLECTION on success, or NULL on failure.
760
 */
761
static LWCOLLECTION* lwcollection_from_wkb_state(wkb_parse_state *s)
762
0
{
763
0
  uint32_t ngeoms = integer_from_wkb_state(s);
764
0
  if (s->error)
765
0
    return NULL;
766
0
  LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, s->srid, s->has_z, s->has_m);
767
0
  LWGEOM *geom = NULL;
768
0
  uint32_t i;
769
0
  int8_t prev_check = s->check;
770
0
  uint8_t start_depth = s->depth;
771
772
0
  LWDEBUGF(4,"Collection has %d components", ngeoms);
773
774
  /* Empty collection? */
775
0
  if ( ngeoms == 0 )
776
0
  {
777
0
      s->check = prev_check;
778
0
      return col;
779
0
  }
780
781
  /* Be strict in polyhedral surface closures */
782
0
  if ( s->lwtype == POLYHEDRALSURFACETYPE )
783
0
    s->check |= LW_PARSER_CHECK_ZCLOSURE;
784
785
0
  s->depth++;
786
0
  if (s->depth >= LW_PARSER_MAX_DEPTH)
787
0
  {
788
0
    lwcollection_free(col);
789
0
    lwerror("Geometry has too many chained collections");
790
0
    s->check = prev_check;
791
0
    s->depth = start_depth;
792
0
    return NULL;
793
0
  }
794
0
  for ( i = 0; i < ngeoms; i++ )
795
0
  {
796
0
    geom = lwgeom_from_wkb_state(s);
797
0
    if (!geom || lwcollection_add_lwgeom(col, geom) == NULL )
798
0
    {
799
0
      lwgeom_free(geom);
800
0
      lwgeom_free((LWGEOM *)col);
801
0
      lwerror("Unable to add geometry (%p) to collection (%p)", (void *) geom, (void *) col);
802
0
      s->check = prev_check;
803
0
      s->depth = start_depth;
804
0
      return NULL;
805
0
    }
806
0
  }
807
0
  s->depth--;
808
0
  s->check = prev_check;
809
810
0
  return col;
811
0
}
812
813
/**
814
 * Parse an ISO/IEC 13249-3:2016 NURBS curve from the current position of a WKB parse state.
815
 *
816
 * Reads the NURBSCURVE components in WKB order: degree, number of control points,
817
 * per-control-point byte-order marker, coordinates (X, Y, optional Z and M), a 1-byte
818
 * weight-present flag and optional weight value, then the knot count and knot values.
819
 * If control-point count is zero an empty POINTARRAY is constructed with the current
820
 * dimensionality. Knot count must be > 0 per the standard.
821
 *
822
 * On error (invalid data, unexpected EOF, invalid weight bit, allocation failure, or
823
 * missing required knots) the function logs an error, frees any partially-allocated
824
 * resources and returns NULL.
825
 *
826
 * Returns a newly-allocated LWNURBSCURVE on success; the caller owns the returned object.
827
 *
828
 * @return Pointer to constructed LWNURBSCURVE, or NULL on failure.
829
 */
830
static LWNURBSCURVE* lwnurbscurve_from_wkb_state(wkb_parse_state *s)
831
0
{
832
0
  uint32_t degree = 0, nknots = 0, npoints = 0, ndims;
833
0
  double *weights = NULL, *knots = NULL;
834
0
  POINTARRAY *points = NULL;
835
0
  int all_weights_one = 1;
836
0
  size_t pa_size;
837
0
  static const uint32_t MAXPOINTS = (uint32_t)(UINT_MAX / (WKB_DOUBLE_SIZE * 4));
838
839
  /* ISO/IEC 13249-3:2016 compliant parsing */
840
0
  degree = integer_from_wkb_state(s);
841
0
  if (s->error) return NULL;
842
0
  if (degree < 1 || degree > 10)
843
0
  {
844
0
    lwerror("WKB NURBSCURVE: degree %u outside valid range [1,10]", degree);
845
0
    return NULL;
846
0
  }
847
848
  /* Read control points count */
849
0
  npoints = integer_from_wkb_state(s);
850
0
  if (s->error) return NULL;
851
852
  /* Defensive upper bound (worst-case 4 doubles/point) */
853
0
  if (npoints > MAXPOINTS) {
854
0
    lwerror("WKB NURBSCURVE: control point count (%u) too large", npoints);
855
0
    return NULL;
856
0
  }
857
858
0
  if (npoints > 0 && npoints <= degree) {
859
0
    lwerror("WKB NURBSCURVE: degree %u requires at least %llu control points, got %u",
860
0
            degree, (unsigned long long)degree + 1, npoints);
861
0
    return NULL;
862
0
  }
863
0
  const size_t min_point_size = 2 + (2 + (s->has_z ? 1 : 0) + (s->has_m ? 1 : 0)) * WKB_DOUBLE_SIZE;
864
0
  wkb_parse_state_check(s, (size_t)npoints * min_point_size);
865
0
  if (s->error)
866
0
    return NULL;
867
868
  /* Does the data we want to read exist? */
869
0
  ndims = 2 + s->has_z + s->has_m;
870
0
  pa_size = (size_t)npoints * ndims * WKB_DOUBLE_SIZE; /* coord */
871
0
  pa_size += npoints * sizeof(double); /* weight */
872
0
  pa_size += npoints * sizeof(char) * 2; /* endian_byte + weight_byte */
873
0
  wkb_parse_state_check(s, pa_size);
874
0
  if (s->error)
875
0
    return NULL;
876
877
  /* Initialize points array */
878
0
  if (npoints > 0) {
879
0
    int8_t save_swap_butes = s->swap_bytes;
880
881
0
    points = ptarray_construct(s->has_z, s->has_m, npoints);
882
0
    weights = lwalloc(sizeof(double) * npoints);
883
884
    /* ISO format: each control point has structure:
885
     * <byte order> <wkbweightedpoint> <bit> [<wkbweight>]
886
     */
887
888
0
    for (uint32_t i = 0; i < npoints; i++) {
889
0
      POINT4D pt = {0, 0, 0, 0};
890
891
      /* Read byte order for this point (ISO requirement) */
892
0
      wkb_swap_bytes(s);
893
894
      /* Read point coordinates */
895
0
      pt.x = double_from_wkb_state(s);
896
0
      if (s->error) {
897
0
        lwfree(weights);
898
0
        ptarray_free(points);
899
0
        return NULL;
900
0
      }
901
902
0
      pt.y = double_from_wkb_state(s);
903
0
      if (s->error) {
904
0
        lwfree(weights);
905
0
        ptarray_free(points);
906
0
        return NULL;
907
0
      }
908
909
0
      if (s->has_z) {
910
0
        pt.z = double_from_wkb_state(s);
911
0
        if (s->error) {
912
0
          lwfree(weights);
913
0
          ptarray_free(points);
914
0
          return NULL;
915
0
        }
916
0
      }
917
918
0
      if (s->has_m) {
919
0
        pt.m = double_from_wkb_state(s);
920
0
        if (s->error) {
921
0
          lwfree(weights);
922
0
          ptarray_free(points);
923
0
          return NULL;
924
0
        }
925
0
      }
926
927
0
      ptarray_set_point4d(points, i, &pt);
928
929
      /* Read weight bit flag */
930
0
      uint8_t has_weight = byte_from_wkb_state(s);
931
0
      if (s->error) {
932
0
        lwfree(weights);
933
0
        ptarray_free(points);
934
0
        return NULL;
935
0
      }
936
937
0
      if (has_weight == 0) {
938
        /* Default weight = 1.0 */
939
0
        weights[i] = 1.0;
940
0
      } else if (has_weight == 1) {
941
        /* Custom weight follows */
942
0
        weights[i] = double_from_wkb_state(s);
943
0
        if (weights[i] <= 0.0) {
944
0
          lwerror("WKB NURBSCURVE: non-positive weight for point %d", i);
945
0
          lwfree(weights);
946
0
          ptarray_free(points);
947
0
          return NULL;
948
0
        }
949
0
        if (s->error) {
950
0
          lwfree(weights);
951
0
          ptarray_free(points);
952
0
          return NULL;
953
0
        }
954
0
        if (weights[i] != 1.0) all_weights_one = 0;
955
0
      } else {
956
0
        lwerror("WKB NURBSCURVE: invalid weight bit %d for point %d (must be 0 or 1)", has_weight, i);
957
0
        lwfree(weights);
958
0
        ptarray_free(points);
959
0
        return NULL;
960
0
      }
961
962
0
    }
963
    /* Restore original swap state */
964
0
    s->swap_bytes = save_swap_butes;
965
0
  }
966
967
  /* Read knots (required by WKB standard) */
968
0
  nknots = integer_from_wkb_state(s);
969
0
  if (s->error) {
970
0
    lwfree(weights);
971
0
    ptarray_free(points);
972
0
    return NULL;
973
0
  }
974
975
0
  if (npoints == 0)
976
0
  {
977
0
    if (nknots != 0)
978
0
    {
979
0
      lwerror("WKB NURBSCURVE EMPTY: expected 0 knots, got %u", nknots);
980
0
      return NULL;
981
0
    }
982
0
    return lwnurbscurve_construct_empty(s->srid, s->has_z, s->has_m);
983
0
  }
984
985
0
  if (nknots == 0) {
986
0
    lwerror("WKB NURBSCURVE: knots required by standard (got 0)");
987
0
    lwfree(weights);
988
0
    ptarray_free(points);
989
0
    return NULL;
990
0
  }
991
992
  /* Validate knot count against B-spline formula */
993
0
  uint32_t expected_knots = npoints + degree + 1;
994
0
  if (nknots != expected_knots) {
995
0
    lwerror("WKB NURBSCURVE: expected %d knots for degree %d and %d points, got %d",
996
0
            expected_knots, degree, npoints, nknots);
997
0
    lwfree(weights);
998
0
    ptarray_free(points);
999
0
    return NULL;
1000
0
  }
1001
1002
0
  knots = lwalloc(sizeof(double) * nknots);
1003
0
  for (uint32_t i = 0; i < nknots; i++) {
1004
0
    knots[i] = double_from_wkb_state(s);
1005
0
    if (s->error) {
1006
0
      lwfree(weights);
1007
0
      lwfree(knots);
1008
0
      ptarray_free(points);
1009
0
      return NULL;
1010
0
    }
1011
0
  }
1012
0
  for (uint32_t i = 1; i < nknots; i++) {
1013
0
    if (knots[i] < knots[i-1]) {
1014
0
      lwerror("WKB NURBSCURVE: knot vector must be non-decreasing");
1015
0
      lwfree(weights);
1016
0
      lwfree(knots);
1017
0
      ptarray_free(points);
1018
0
      return NULL;
1019
0
    }
1020
0
  }
1021
1022
0
  uint32_t nweights = all_weights_one ? 0U : npoints;
1023
0
  if (all_weights_one) { lwfree(weights); weights = NULL; }
1024
0
  LWNURBSCURVE *curve = lwnurbscurve_construct(s->srid, NULL, degree, points, weights, knots, nweights, nknots);
1025
0
  if (!curve) {
1026
0
    if (weights) lwfree(weights);
1027
0
    if (knots) lwfree(knots);
1028
0
    if (points) ptarray_free(points); /* constructor did not take ownership on failure */
1029
0
    return NULL;
1030
0
  }
1031
0
  if (weights) lwfree(weights);
1032
0
  if (knots) lwfree(knots);
1033
0
  return curve;
1034
0
}
1035
1036
/**
1037
 * Parse a single WKB geometry from the given parse state and return it as an LWGEOM.
1038
 *
1039
 * Reads the leading endian byte, the WKB type (and optional SRID), updates the parse
1040
 * state (including byte-swap flag, detected lwtype and srid), and dispatches to the
1041
 * specific geometry reader for the detected type. On success returns a newly
1042
 * allocated LWGEOM; on error or unsupported type returns NULL and sets s->error.
1043
 *
1044
 * Side effects:
1045
 * - Advances s->pos as data are consumed.
1046
 * - May set s->swap_bytes, s->lwtype, s->srid and s->error.
1047
 *
1048
 * Return:
1049
 *   Pointer to the parsed LWGEOM, or NULL on error or unsupported geometry type.
1050
 */
1051
LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s)
1052
0
{
1053
0
  uint32_t wkb_type;
1054
1055
0
  LWDEBUG(4,"Entered function");
1056
1057
0
  wkb_swap_bytes(s);
1058
1059
  /* Read the type number */
1060
0
  wkb_type = integer_from_wkb_state(s);
1061
0
  if (s->error)
1062
0
    return NULL;
1063
0
  LWDEBUGF(4,"Got WKB type number: 0x%X", wkb_type);
1064
0
  lwtype_from_wkb_state(s, wkb_type);
1065
1066
  /* Read the SRID, if necessary */
1067
0
  if( s->has_srid )
1068
0
  {
1069
0
    s->srid = clamp_srid(integer_from_wkb_state(s));
1070
0
    if (s->error)
1071
0
      return NULL;
1072
    /* TODO: warn on explicit UNKNOWN srid ? */
1073
0
    LWDEBUGF(4,"Got SRID: %u", s->srid);
1074
0
  }
1075
1076
  /* Do the right thing */
1077
0
  switch( s->lwtype )
1078
0
  {
1079
0
    case POINTTYPE:
1080
0
      return (LWGEOM*)lwpoint_from_wkb_state(s);
1081
0
      break;
1082
0
    case LINETYPE:
1083
0
      return (LWGEOM*)lwline_from_wkb_state(s);
1084
0
      break;
1085
0
    case CIRCSTRINGTYPE:
1086
0
      return (LWGEOM*)lwcircstring_from_wkb_state(s);
1087
0
      break;
1088
0
    case POLYGONTYPE:
1089
0
      return (LWGEOM*)lwpoly_from_wkb_state(s);
1090
0
      break;
1091
0
    case TRIANGLETYPE:
1092
0
      return (LWGEOM*)lwtriangle_from_wkb_state(s);
1093
0
      break;
1094
0
    case CURVEPOLYTYPE:
1095
0
      return (LWGEOM*)lwcurvepoly_from_wkb_state(s);
1096
0
      break;
1097
0
    case MULTIPOINTTYPE:
1098
0
    case MULTILINETYPE:
1099
0
    case MULTIPOLYGONTYPE:
1100
0
    case COMPOUNDTYPE:
1101
0
    case MULTICURVETYPE:
1102
0
    case MULTISURFACETYPE:
1103
0
    case POLYHEDRALSURFACETYPE:
1104
0
    case TINTYPE:
1105
0
    case COLLECTIONTYPE:
1106
0
      return (LWGEOM*)lwcollection_from_wkb_state(s);
1107
0
      break;
1108
0
    case NURBSCURVETYPE:
1109
0
      return (LWGEOM*)lwnurbscurve_from_wkb_state(s);
1110
0
      break;
1111
    /* Unknown type! */
1112
0
    default:
1113
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(s->lwtype));
1114
0
  }
1115
1116
  /* Return value to keep compiler happy. */
1117
0
  return NULL;
1118
1119
0
}
1120
1121
/* TODO add check for SRID consistency */
1122
1123
/**
1124
* WKB inputs *must* have a declared size, to prevent malformed WKB from reading
1125
* off the end of the memory segment (this stops a malevolent user from declaring
1126
* a one-ring polygon to have 10 rings, causing the WKB reader to walk off the
1127
* end of the memory).
1128
*
1129
* Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD,
1130
* LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL
1131
*/
1132
LWGEOM* lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check)
1133
0
{
1134
0
  wkb_parse_state s;
1135
1136
  /* Initialize the state appropriately */
1137
0
  s.wkb = wkb;
1138
0
  s.wkb_size = wkb_size;
1139
0
  s.swap_bytes = LW_FALSE;
1140
0
  s.check = check;
1141
0
  s.lwtype = 0;
1142
0
  s.srid = SRID_UNKNOWN;
1143
0
  s.has_z = LW_FALSE;
1144
0
  s.has_m = LW_FALSE;
1145
0
  s.has_srid = LW_FALSE;
1146
0
  s.error = LW_FALSE;
1147
0
  s.pos = wkb;
1148
0
  s.depth = 1;
1149
1150
0
  if (!wkb || !wkb_size)
1151
0
    return NULL;
1152
1153
0
  return lwgeom_from_wkb_state(&s);
1154
0
}
1155
1156
LWGEOM* lwgeom_from_hexwkb(const char *hexwkb, const char check)
1157
0
{
1158
0
  int hexwkb_len;
1159
0
  uint8_t *wkb;
1160
0
  LWGEOM *lwgeom;
1161
1162
0
  if ( ! hexwkb )
1163
0
  {
1164
0
    lwerror("lwgeom_from_hexwkb: null input");
1165
0
    return NULL;
1166
0
  }
1167
1168
0
  hexwkb_len = strlen(hexwkb);
1169
0
  wkb = bytes_from_hexbytes(hexwkb, hexwkb_len);
1170
0
  lwgeom = lwgeom_from_wkb(wkb, hexwkb_len/2, check);
1171
0
  lwfree(wkb);
1172
0
  return lwgeom;
1173
0
}