Coverage Report

Created: 2026-08-14 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwout_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 <math.h>
27
#include <stddef.h> // for ptrdiff_t
28
29
#include "liblwgeom_internal.h"
30
#include "lwgeom_log.h"
31
32
static uint8_t* lwgeom_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant);
33
static size_t lwgeom_to_wkb_size(const LWGEOM *geom, uint8_t variant);
34
35
/*
36
* Look-up table for hex writer
37
*/
38
static char *hexchr = "0123456789ABCDEF";
39
40
char* hexbytes_from_bytes(const uint8_t *bytes, size_t size)
41
0
{
42
0
  char *hex;
43
0
  uint32_t i;
44
0
  if ( ! bytes || ! size )
45
0
  {
46
0
    lwerror("hexbutes_from_bytes: invalid input");
47
0
    return NULL;
48
0
  }
49
0
  hex = lwalloc(size * 2 + 1);
50
0
  hex[2*size] = '\0';
51
0
  for( i = 0; i < size; i++ )
52
0
  {
53
    /* Top four bits to 0-F */
54
0
    hex[2*i] = hexchr[bytes[i] >> 4];
55
    /* Bottom four bits to 0-F */
56
0
    hex[2*i+1] = hexchr[bytes[i] & 0x0F];
57
0
  }
58
0
  return hex;
59
0
}
60
61
/*
62
* Optional SRID
63
*/
64
static int lwgeom_wkb_needs_srid(const LWGEOM *geom, uint8_t variant)
65
0
{
66
  /* Sub-components of collections inherit their SRID from the parent.
67
     We force that behavior with the WKB_NO_SRID flag */
68
0
  if ( variant & WKB_NO_SRID )
69
0
    return LW_FALSE;
70
71
  /* We can only add an SRID if the geometry has one, and the
72
     WKB form is extended */
73
0
  if ( (variant & WKB_EXTENDED) && lwgeom_has_srid(geom) )
74
0
    return LW_TRUE;
75
76
  /* Everything else doesn't get an SRID */
77
0
  return LW_FALSE;
78
0
}
79
80
/**
81
 * Compute the WKB geometry type code for a given LWGEOM and WKB variant.
82
 *
83
 * Maps the internal geometry type to the appropriate WKB type code and
84
 * applies dimension and SRID encoding offsets/flags according to the
85
 * selected variant. NURBSCURVETYPE always uses ISO-style dimension offsets
86
 * (1000/2000/3000). For non-NURBS geometries:
87
 * - WKB_EXTENDED sets Z/M bit flags and may set the SRID flag (determined
88
 *   by lwgeom_wkb_needs_srid).
89
 * - WKB_ISO adds numeric dimension offsets (1000 for Z, 2000 for M).
90
 *
91
 * If the geometry type is unsupported the function logs an error and
92
 * returns 0.
93
 *
94
 * @param geom Geometry to encode (must be non-NULL).
95
 * @param variant Bitflags describing WKB variant/encoding (e.g. WKB_EXTENDED, WKB_ISO, WKB_NO_SRID).
96
 * @return WKB geometry type code with any applicable dimension/SRID modifiers.
97
 */
98
static uint32_t lwgeom_wkb_type(const LWGEOM *geom, uint8_t variant)
99
0
{
100
0
  uint32_t wkb_type = 0;
101
102
0
  switch ( geom->type )
103
0
  {
104
0
  case POINTTYPE:
105
0
    wkb_type = WKB_POINT_TYPE;
106
0
    break;
107
0
  case LINETYPE:
108
0
    wkb_type = WKB_LINESTRING_TYPE;
109
0
    break;
110
0
  case NURBSCURVETYPE:
111
0
    wkb_type = WKB_NURBSCURVE_TYPE;
112
0
    break;
113
0
  case POLYGONTYPE:
114
0
    wkb_type = WKB_POLYGON_TYPE;
115
0
    break;
116
0
  case MULTIPOINTTYPE:
117
0
    wkb_type = WKB_MULTIPOINT_TYPE;
118
0
    break;
119
0
  case MULTILINETYPE:
120
0
    wkb_type = WKB_MULTILINESTRING_TYPE;
121
0
    break;
122
0
  case MULTIPOLYGONTYPE:
123
0
    wkb_type = WKB_MULTIPOLYGON_TYPE;
124
0
    break;
125
0
  case COLLECTIONTYPE:
126
0
    wkb_type = WKB_GEOMETRYCOLLECTION_TYPE;
127
0
    break;
128
0
  case CIRCSTRINGTYPE:
129
0
    wkb_type = WKB_CIRCULARSTRING_TYPE;
130
0
    break;
131
0
  case COMPOUNDTYPE:
132
0
    wkb_type = WKB_COMPOUNDCURVE_TYPE;
133
0
    break;
134
0
  case CURVEPOLYTYPE:
135
0
    wkb_type = WKB_CURVEPOLYGON_TYPE;
136
0
    break;
137
0
  case MULTICURVETYPE:
138
0
    wkb_type = WKB_MULTICURVE_TYPE;
139
0
    break;
140
0
  case MULTISURFACETYPE:
141
0
    wkb_type = WKB_MULTISURFACE_TYPE;
142
0
    break;
143
0
  case POLYHEDRALSURFACETYPE:
144
0
    wkb_type = WKB_POLYHEDRALSURFACE_TYPE;
145
0
    break;
146
0
  case TINTYPE:
147
0
    wkb_type = WKB_TIN_TYPE;
148
0
    break;
149
0
  case TRIANGLETYPE:
150
0
    wkb_type = WKB_TRIANGLE_TYPE;
151
0
    break;
152
0
  default:
153
0
    lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type));
154
0
  }
155
156
  /* NURBS curves always use ISO dimension encoding */
157
0
  if ( geom->type == NURBSCURVETYPE )
158
0
  {
159
0
    if ( FLAGS_GET_Z(geom->flags) && !FLAGS_GET_M(geom->flags) )
160
0
      wkb_type += 1000;  /* Z: 1000 offset */
161
0
    else if ( FLAGS_GET_M(geom->flags) && !FLAGS_GET_Z(geom->flags) )
162
0
      wkb_type += 2000;  /* M: 2000 offset */
163
0
    else if ( FLAGS_GET_Z(geom->flags) && FLAGS_GET_M(geom->flags) )
164
0
      wkb_type += 3000;  /* ZM: 3000 offset */
165
0
    if ( (variant & WKB_EXTENDED) && lwgeom_wkb_needs_srid(geom, variant) )
166
0
      wkb_type |= WKBSRIDFLAG;
167
0
  }
168
0
  else if ( variant & WKB_EXTENDED )
169
0
  {
170
0
    if ( FLAGS_GET_Z(geom->flags) )
171
0
      wkb_type |= WKBZOFFSET;
172
0
    if ( FLAGS_GET_M(geom->flags) )
173
0
      wkb_type |= WKBMOFFSET;
174
/*    if ( geom->srid != SRID_UNKNOWN && ! (variant & WKB_NO_SRID) ) */
175
0
    if ( lwgeom_wkb_needs_srid(geom, variant) )
176
0
      wkb_type |= WKBSRIDFLAG;
177
0
  }
178
0
  else if ( variant & WKB_ISO )
179
0
  {
180
    /* ISO encoding for other geometry types */
181
0
    if ( FLAGS_GET_Z(geom->flags) )
182
0
      wkb_type += 1000;
183
0
    if ( FLAGS_GET_M(geom->flags) )
184
0
      wkb_type += 2000;
185
0
  }
186
0
  return wkb_type;
187
0
}
188
189
/*
190
* Endian
191
*/
192
static uint8_t* endian_to_wkb_buf(uint8_t *buf, uint8_t variant)
193
0
{
194
0
  if ( variant & WKB_HEX )
195
0
  {
196
0
    buf[0] = '0';
197
0
    buf[1] = ((variant & WKB_NDR) ? '1' : '0');
198
0
    return buf + 2;
199
0
  }
200
0
  else
201
0
  {
202
0
    buf[0] = ((variant & WKB_NDR) ? 1 : 0);
203
0
    return buf + 1;
204
0
  }
205
0
}
206
207
/*
208
* SwapBytes?
209
*/
210
static inline int wkb_swap_bytes(uint8_t variant)
211
{
212
  /* If requested variant matches machine arch, we don't have to swap! */
213
  if (((variant & WKB_NDR) && !IS_BIG_ENDIAN) ||
214
      ((!(variant & WKB_NDR)) && IS_BIG_ENDIAN))
215
  {
216
    return LW_FALSE;
217
  }
218
  return LW_TRUE;
219
}
220
221
/**
222
 * Write a 32-bit unsigned integer into a WKB buffer honoring variant (binary or hex)
223
 * and requested endianness, and return the pointer to the next write position.
224
 *
225
 * The function writes the 32-bit value `ival` into `buf` using WKB_INT_SIZE bytes
226
 * (or twice that many ASCII hex characters when WKB_HEX is set in `variant`).
227
 * If the requested byte order differs from the machine's native order the byte
228
 * order is swapped before writing. On mismatch between sizeof(int) and
229
 * WKB_INT_SIZE an error is logged.
230
 *
231
 * @param ival  The 32-bit unsigned integer to encode.
232
 * @param buf   Destination buffer where encoded bytes (or hex chars) are written.
233
 *              Must have space for at least WKB_INT_SIZE bytes (or 2*WKB_INT_SIZE
234
 *              chars for hex variants).
235
 * @param variant Bitmask specifying WKB encoding variant (may include WKB_HEX and
236
 *                endianness flags). Controls hex vs binary output and byte order.
237
 * @return Pointer into `buf` immediately after the bytes written.
238
 */
239
static uint8_t *
240
integer_to_wkb_buf(const uint32_t ival, uint8_t *buf, uint8_t variant)
241
0
{
242
0
  uint8_t *iptr = (uint8_t *)(&ival);
243
0
  int i = 0;
244
245
0
  if ( sizeof(int) != WKB_INT_SIZE )
246
0
  {
247
0
    lwerror("Machine int size is not %d bytes!", WKB_INT_SIZE);
248
0
  }
249
0
  LWDEBUGF(4, "Writing value '%u'", ival);
250
0
  if ( variant & WKB_HEX )
251
0
  {
252
0
    int swap = wkb_swap_bytes(variant);
253
    /* Machine/request arch mismatch, so flip byte order */
254
0
    for ( i = 0; i < WKB_INT_SIZE; i++ )
255
0
    {
256
0
      int j = (swap ? WKB_INT_SIZE - 1 - i : i);
257
0
      uint8_t b = iptr[j];
258
      /* Top four bits to 0-F */
259
0
      buf[2*i] = hexchr[b >> 4];
260
      /* Bottom four bits to 0-F */
261
0
      buf[2*i+1] = hexchr[b & 0x0F];
262
0
    }
263
0
    return buf + (2 * WKB_INT_SIZE);
264
0
  }
265
0
  else
266
0
  {
267
    /* Machine/request arch mismatch, so flip byte order */
268
0
    if ( wkb_swap_bytes(variant) )
269
0
    {
270
0
      for ( i = 0; i < WKB_INT_SIZE; i++ )
271
0
      {
272
0
        buf[i] = iptr[WKB_INT_SIZE - 1 - i];
273
0
      }
274
0
    }
275
    /* If machine arch and requested arch match, don't flip byte order */
276
0
    else
277
0
    {
278
0
      memcpy(buf, iptr, WKB_INT_SIZE);
279
0
    }
280
0
    return buf + WKB_INT_SIZE;
281
0
  }
282
0
}
283
284
/**
285
 * Write a single byte into a WKB buffer, using hex-encoding when requested.
286
 *
287
 * If the WKB_HEX bit is set in variant, writes two ASCII hex characters
288
 * representing the byte and returns buf + 2; otherwise writes the raw byte
289
 * and returns buf + 1.
290
 *
291
 * @param bval Byte value to write.
292
 * @param buf Destination buffer to write into; must have at least 2 bytes if hex encoding is used.
293
 * @param variant Bitmask of WKB variant flags (tests WKB_HEX).
294
 * @returns Pointer to the next write position in buf after the written data.
295
 */
296
static uint8_t* byte_to_wkb_buf(const uint8_t bval, uint8_t *buf, uint8_t variant)
297
0
{
298
0
  if ( variant & WKB_HEX )
299
0
  {
300
    /* Convert single byte to 2 hex characters */
301
0
    buf[0] = hexchr[bval >> 4];      /* Top four bits to 0-F */
302
0
    buf[1] = hexchr[bval & 0x0F];    /* Bottom four bits to 0-F */
303
0
    return buf + 2;
304
0
  }
305
0
  else
306
0
  {
307
0
    buf[0] = bval;
308
0
    return buf + 1;
309
0
  }
310
0
}
311
312
/**
313
 * Write an IEEE-754 NaN double into a WKB buffer.
314
 *
315
 * Writes an 8-byte NaN pattern to buf using either little-endian (NDR) or
316
 * big-endian (XDR) byte order and either binary or hex-encoded output,
317
 * controlled by flags in variant (WKB_NDR and WKB_HEX). Advances and returns
318
 * a pointer to the position immediately after the written data.
319
 *
320
 * @param buf Buffer to write into; must have space for 8 raw bytes or 16 hex bytes.
321
 * @param variant Bitmask of WKB flags (may include WKB_NDR for NDR vs XDR and
322
 *                WKB_HEX to request hex-encoding).
323
 * @return Pointer to buf advanced past the written NaN bytes (buf + 8 for binary,
324
 *         buf + 16 for hex).
325
 */
326
static uint8_t* double_nan_to_wkb_buf(uint8_t *buf, uint8_t variant)
327
0
{
328
0
#define NAN_SIZE 8
329
0
  const uint8_t ndr_nan[NAN_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f};
330
0
  const uint8_t xdr_nan[NAN_SIZE] = {0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
331
332
0
  if ( variant & WKB_HEX )
333
0
  {
334
0
    for (int i = 0; i < NAN_SIZE; i++)
335
0
    {
336
0
      uint8_t b = (variant & WKB_NDR) ? ndr_nan[i] : xdr_nan[i];
337
      /* Top four bits to 0-F */
338
0
      buf[2*i] = hexchr[b >> 4];
339
      /* Bottom four bits to 0-F */
340
0
      buf[2*i + 1] = hexchr[b & 0x0F];
341
0
    }
342
0
    return buf + (2 * NAN_SIZE);
343
0
  }
344
0
  else
345
0
  {
346
0
    for (int i = 0; i < NAN_SIZE; i++)
347
0
    {
348
0
      buf[i] = (variant & WKB_NDR) ? ndr_nan[i] : xdr_nan[i];;
349
0
    }
350
0
    return buf + NAN_SIZE;
351
0
  }
352
0
}
353
354
/*
355
* Float64
356
*/
357
static uint8_t* double_to_wkb_buf(const double d, uint8_t *buf, uint8_t variant)
358
0
{
359
0
  uint8_t *dptr = (uint8_t *)(&d);
360
0
  int i = 0;
361
362
0
  if ( sizeof(double) != WKB_DOUBLE_SIZE )
363
0
  {
364
0
    lwerror("Machine double size is not %d bytes!", WKB_DOUBLE_SIZE);
365
0
  }
366
367
0
  if ( variant & WKB_HEX )
368
0
  {
369
0
    int swap =  wkb_swap_bytes(variant);
370
    /* Machine/request arch mismatch, so flip byte order */
371
0
    for ( i = 0; i < WKB_DOUBLE_SIZE; i++ )
372
0
    {
373
0
      int j = (swap ? WKB_DOUBLE_SIZE - 1 - i : i);
374
0
      uint8_t b = dptr[j];
375
      /* Top four bits to 0-F */
376
0
      buf[2*i] = hexchr[b >> 4];
377
      /* Bottom four bits to 0-F */
378
0
      buf[2*i+1] = hexchr[b & 0x0F];
379
0
    }
380
0
    return buf + (2 * WKB_DOUBLE_SIZE);
381
0
  }
382
0
  else
383
0
  {
384
    /* Machine/request arch mismatch, so flip byte order */
385
0
    if ( wkb_swap_bytes(variant) )
386
0
    {
387
0
      for ( i = 0; i < WKB_DOUBLE_SIZE; i++ )
388
0
      {
389
0
        buf[i] = dptr[WKB_DOUBLE_SIZE - 1 - i];
390
0
      }
391
0
    }
392
    /* If machine arch and requested arch match, don't flip byte order */
393
0
    else
394
0
    {
395
0
      memcpy(buf, dptr, WKB_DOUBLE_SIZE);
396
0
    }
397
0
    return buf + WKB_DOUBLE_SIZE;
398
0
  }
399
0
}
400
401
402
/*
403
* Empty
404
*/
405
static size_t empty_to_wkb_size(const LWGEOM *geom, uint8_t variant)
406
0
{
407
  /* endian byte + type integer */
408
0
  size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE;
409
410
  /* optional srid integer */
411
0
  if ( lwgeom_wkb_needs_srid(geom, variant) )
412
0
    size += WKB_INT_SIZE;
413
414
  /* Represent POINT EMPTY as POINT(NaN NaN) */
415
0
  if ( geom->type == POINTTYPE )
416
0
  {
417
0
    const LWPOINT *pt = (LWPOINT*)geom;
418
0
    size += WKB_DOUBLE_SIZE * FLAGS_NDIMS(pt->point->flags);
419
0
  }
420
  /* num-elements */
421
0
  else
422
0
  {
423
0
    size += WKB_INT_SIZE;
424
0
  }
425
426
0
  return size;
427
0
}
428
429
static uint8_t* empty_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant)
430
0
{
431
0
  uint32_t wkb_type = lwgeom_wkb_type(geom, variant);
432
433
  /* Set the endian flag */
434
0
  buf = endian_to_wkb_buf(buf, variant);
435
436
  /* Set the geometry type */
437
0
  buf = integer_to_wkb_buf(wkb_type, buf, variant);
438
439
  /* Set the SRID if necessary */
440
0
  if ( lwgeom_wkb_needs_srid(geom, variant) )
441
0
    buf = integer_to_wkb_buf(geom->srid, buf, variant);
442
443
  /* Represent POINT EMPTY as POINT(NaN NaN) */
444
0
  if ( geom->type == POINTTYPE )
445
0
  {
446
0
    const LWPOINT *pt = (LWPOINT*)geom;
447
0
    for (int i = 0; i < FLAGS_NDIMS(pt->point->flags); i++)
448
0
    {
449
0
      buf = double_nan_to_wkb_buf(buf, variant);
450
0
    }
451
0
  }
452
  /* Everything else is flagged as empty using num-elements == 0 */
453
0
  else
454
0
  {
455
    /* Set nrings/npoints/ngeoms to zero */
456
0
    buf = integer_to_wkb_buf(0, buf, variant);
457
0
  }
458
459
0
  return buf;
460
0
}
461
462
/*
463
* POINTARRAY
464
*/
465
static size_t ptarray_to_wkb_size(const POINTARRAY *pa, uint8_t variant)
466
0
{
467
0
  int dims = 2;
468
0
  size_t size = 0;
469
470
0
  if ( variant & (WKB_ISO | WKB_EXTENDED) )
471
0
    dims = FLAGS_NDIMS(pa->flags);
472
473
  /* Include the npoints if it's not a POINT type) */
474
0
  if ( ! ( variant & WKB_NO_NPOINTS ) )
475
0
    size += WKB_INT_SIZE;
476
477
  /* size of the double list */
478
0
  size += (size_t)pa->npoints * dims * WKB_DOUBLE_SIZE;
479
480
0
  return size;
481
0
}
482
483
static uint8_t* ptarray_to_wkb_buf(const POINTARRAY *pa, uint8_t *buf, uint8_t variant)
484
0
{
485
0
  uint32_t dims = 2;
486
0
  uint32_t pa_dims = FLAGS_NDIMS(pa->flags);
487
0
  uint32_t i, j;
488
0
  double *dbl_ptr;
489
490
  /* SFSQL is always 2-d. Extended and ISO use all available dimensions */
491
0
  if ( (variant & WKB_ISO) || (variant & WKB_EXTENDED) )
492
0
    dims = pa_dims;
493
494
  /* Set the number of points (if it's not a POINT type) */
495
0
  if ( ! ( variant & WKB_NO_NPOINTS ) )
496
0
    buf = integer_to_wkb_buf(pa->npoints, buf, variant);
497
498
  /* Bulk copy the coordinates when: dimensionality matches, output format */
499
  /* is not hex, and output endian matches internal endian. */
500
0
  if ( pa->npoints && (dims == pa_dims) && ! wkb_swap_bytes(variant) && ! (variant & WKB_HEX)  )
501
0
  {
502
0
    size_t size = (size_t)pa->npoints * dims * WKB_DOUBLE_SIZE;
503
0
    memcpy(buf, getPoint_internal(pa, 0), size);
504
0
    buf += size;
505
0
  }
506
  /* Copy coordinates one-by-one otherwise */
507
0
  else
508
0
  {
509
0
    for ( i = 0; i < pa->npoints; i++ )
510
0
    {
511
0
      LWDEBUGF(4, "Writing point #%d", i);
512
0
      dbl_ptr = (double*)getPoint_internal(pa, i);
513
0
      for ( j = 0; j < dims; j++ )
514
0
      {
515
0
        LWDEBUGF(4, "Writing dimension #%d (buf = %p)", j, buf);
516
0
        buf = double_to_wkb_buf(dbl_ptr[j], buf, variant);
517
0
      }
518
0
    }
519
0
  }
520
0
  LWDEBUGF(4, "Done (buf = %p)", buf);
521
0
  return buf;
522
0
}
523
524
/*
525
* POINT
526
*/
527
static size_t lwpoint_to_wkb_size(const LWPOINT *pt, uint8_t variant)
528
0
{
529
  /* Endian flag + type number */
530
0
  size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE;
531
532
  /* Only process empty at this level in the EXTENDED case */
533
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)pt) )
534
0
    return empty_to_wkb_size((LWGEOM*)pt, variant);
535
536
  /* Extended WKB needs space for optional SRID integer */
537
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) )
538
0
    size += WKB_INT_SIZE;
539
540
  /* Points */
541
0
  size += ptarray_to_wkb_size(pt->point, variant | WKB_NO_NPOINTS);
542
0
  return size;
543
0
}
544
545
static uint8_t* lwpoint_to_wkb_buf(const LWPOINT *pt, uint8_t *buf, uint8_t variant)
546
0
{
547
  /* Only process empty at this level in the EXTENDED case */
548
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)pt) )
549
0
    return empty_to_wkb_buf((LWGEOM*)pt, buf, variant);
550
551
  /* Set the endian flag */
552
0
  LWDEBUGF(4, "Entering function, buf = %p", buf);
553
0
  buf = endian_to_wkb_buf(buf, variant);
554
0
  LWDEBUGF(4, "Endian set, buf = %p", buf);
555
  /* Set the geometry type */
556
0
  buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)pt, variant), buf, variant);
557
0
  LWDEBUGF(4, "Type set, buf = %p", buf);
558
  /* Set the optional SRID for extended variant */
559
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) )
560
0
  {
561
0
    buf = integer_to_wkb_buf(pt->srid, buf, variant);
562
0
    LWDEBUGF(4, "SRID set, buf = %p", buf);
563
0
  }
564
  /* Set the coordinates */
565
0
  buf = ptarray_to_wkb_buf(pt->point, buf, variant | WKB_NO_NPOINTS);
566
0
  LWDEBUGF(4, "Pointarray set, buf = %p", buf);
567
0
  return buf;
568
0
}
569
570
/*
571
* LINESTRING, CIRCULARSTRING
572
*/
573
static size_t lwline_to_wkb_size(const LWLINE *line, uint8_t variant)
574
0
{
575
  /* Endian flag + type number */
576
0
  size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE;
577
578
  /* Only process empty at this level in the EXTENDED case */
579
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)line) )
580
0
    return empty_to_wkb_size((LWGEOM*)line, variant);
581
582
  /* Extended WKB needs space for optional SRID integer */
583
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) )
584
0
    size += WKB_INT_SIZE;
585
586
  /* Size of point array */
587
0
  size += ptarray_to_wkb_size(line->points, variant);
588
0
  return size;
589
0
}
590
591
static uint8_t* lwline_to_wkb_buf(const LWLINE *line, uint8_t *buf, uint8_t variant)
592
0
{
593
  /* Only process empty at this level in the EXTENDED case */
594
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)line) )
595
0
    return empty_to_wkb_buf((LWGEOM*)line, buf, variant);
596
597
  /* Set the endian flag */
598
0
  buf = endian_to_wkb_buf(buf, variant);
599
  /* Set the geometry type */
600
0
  buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)line, variant), buf, variant);
601
  /* Set the optional SRID for extended variant */
602
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) )
603
0
    buf = integer_to_wkb_buf(line->srid, buf, variant);
604
  /* Set the coordinates */
605
0
  buf = ptarray_to_wkb_buf(line->points, buf, variant);
606
0
  return buf;
607
0
}
608
609
/*
610
* TRIANGLE
611
*/
612
static size_t lwtriangle_to_wkb_size(const LWTRIANGLE *tri, uint8_t variant)
613
0
{
614
  /* endian flag + type number + number of rings */
615
0
  size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE;
616
617
  /* Only process empty at this level in the EXTENDED case */
618
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)tri) )
619
0
    return empty_to_wkb_size((LWGEOM*)tri, variant);
620
621
  /* Extended WKB needs space for optional SRID integer */
622
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)tri, variant) )
623
0
    size += WKB_INT_SIZE;
624
625
  /* How big is this point array? */
626
0
  size += ptarray_to_wkb_size(tri->points, variant);
627
628
0
  return size;
629
0
}
630
631
static uint8_t* lwtriangle_to_wkb_buf(const LWTRIANGLE *tri, uint8_t *buf, uint8_t variant)
632
0
{
633
  /* Only process empty at this level in the EXTENDED case */
634
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)tri) )
635
0
    return empty_to_wkb_buf((LWGEOM*)tri, buf, variant);
636
637
  /* Set the endian flag */
638
0
  buf = endian_to_wkb_buf(buf, variant);
639
640
  /* Set the geometry type */
641
0
  buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)tri, variant), buf, variant);
642
643
  /* Set the optional SRID for extended variant */
644
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)tri, variant) )
645
0
    buf = integer_to_wkb_buf(tri->srid, buf, variant);
646
647
  /* Set the number of rings (only one, it's a triangle, buddy) */
648
0
  buf = integer_to_wkb_buf(1, buf, variant);
649
650
  /* Write that ring */
651
0
  buf = ptarray_to_wkb_buf(tri->points, buf, variant);
652
653
0
  return buf;
654
0
}
655
656
/*
657
* POLYGON
658
*/
659
static size_t lwpoly_to_wkb_size(const LWPOLY *poly, uint8_t variant)
660
0
{
661
  /* endian flag + type number + number of rings */
662
0
  size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE;
663
0
  uint32_t i = 0;
664
665
  /* Only process empty at this level in the EXTENDED case */
666
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)poly) )
667
0
    return empty_to_wkb_size((LWGEOM*)poly, variant);
668
669
  /* Extended WKB needs space for optional SRID integer */
670
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) )
671
0
    size += WKB_INT_SIZE;
672
673
0
  for ( i = 0; i < poly->nrings; i++ )
674
0
  {
675
    /* Size of ring point array */
676
0
    size += ptarray_to_wkb_size(poly->rings[i], variant);
677
0
  }
678
679
0
  return size;
680
0
}
681
682
static uint8_t* lwpoly_to_wkb_buf(const LWPOLY *poly, uint8_t *buf, uint8_t variant)
683
0
{
684
0
  uint32_t i;
685
686
  /* Only process empty at this level in the EXTENDED case */
687
0
  if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)poly) )
688
0
    return empty_to_wkb_buf((LWGEOM*)poly, buf, variant);
689
690
  /* Set the endian flag */
691
0
  buf = endian_to_wkb_buf(buf, variant);
692
  /* Set the geometry type */
693
0
  buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)poly, variant), buf, variant);
694
  /* Set the optional SRID for extended variant */
695
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) )
696
0
    buf = integer_to_wkb_buf(poly->srid, buf, variant);
697
  /* Set the number of rings */
698
0
  buf = integer_to_wkb_buf(poly->nrings, buf, variant);
699
700
0
  for ( i = 0; i < poly->nrings; i++ )
701
0
  {
702
0
    buf = ptarray_to_wkb_buf(poly->rings[i], buf, variant);
703
0
  }
704
705
0
  return buf;
706
0
}
707
708
709
/*
710
* MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION
711
* MULTICURVE, COMPOUNDCURVE, MULTISURFACE, CURVEPOLYGON, TIN,
712
* POLYHEDRALSURFACE
713
*/
714
static size_t lwcollection_to_wkb_size(const LWCOLLECTION *col, uint8_t variant)
715
0
{
716
  /* Endian flag + type number + number of subgeoms */
717
0
  size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE;
718
0
  uint32_t i = 0;
719
720
  /* Extended WKB needs space for optional SRID integer */
721
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) )
722
0
    size += WKB_INT_SIZE;
723
724
0
  for ( i = 0; i < col->ngeoms; i++ )
725
0
  {
726
    /* size of subgeom */
727
0
    size += lwgeom_to_wkb_size((LWGEOM*)col->geoms[i], variant | WKB_NO_SRID);
728
0
  }
729
730
0
  return size;
731
0
}
732
733
/**
734
 * Write a geometry collection into a WKB buffer.
735
 *
736
 * Encodes the collection header (endianness, geometry type, optional SRID,
737
 * and number of sub-geometries) and then serializes each contained geometry.
738
 * Sub-geometries inherit the parent's SRID and are written with WKB_NO_SRID.
739
 *
740
 * @param col Collection to serialize.
741
 * @param buf Pointer to the current write position in the output buffer; the
742
 *            function advances this pointer as it writes.
743
 * @param variant Bitmask selecting WKB variant/flags (e.g. NDR/XDR, HEX,
744
 *                EXTENDED, ISO). Controls SRID inclusion and encoding form.
745
 * @return Updated buffer pointer positioned immediately after the written data.
746
 */
747
static uint8_t* lwcollection_to_wkb_buf(const LWCOLLECTION *col, uint8_t *buf, uint8_t variant)
748
0
{
749
0
  uint32_t i;
750
751
  /* Set the endian flag */
752
0
  buf = endian_to_wkb_buf(buf, variant);
753
  /* Set the geometry type */
754
0
  buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)col, variant), buf, variant);
755
  /* Set the optional SRID for extended variant */
756
0
  if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) )
757
0
    buf = integer_to_wkb_buf(col->srid, buf, variant);
758
  /* Set the number of sub-geometries */
759
0
  buf = integer_to_wkb_buf(col->ngeoms, buf, variant);
760
761
  /* Write the sub-geometries. Sub-geometries do not get SRIDs, they
762
     inherit from their parents. */
763
0
  for ( i = 0; i < col->ngeoms; i++ )
764
0
  {
765
0
    buf = lwgeom_to_wkb_buf(col->geoms[i], buf, variant | WKB_NO_SRID);
766
0
  }
767
768
0
  return buf;
769
0
}
770
771
/**
772
 * Compute the number of bytes required to encode a NURBS curve as WKB (ISO/IEC 13249-3:2016).
773
 *
774
 * The computed size accounts for:
775
 * - endianness marker and geometry type,
776
 * - optional SRID integer when the variant requests it,
777
 * - degree and control-point count,
778
 * - per-control-point data: a byte-order marker, coordinate doubles for active dimensions,
779
 *   a one-byte "weight present" flag, and an optional double weight when the point's weight != 1.0,
780
 * - knot-count integer and knot double values (a uniform knot vector is generated if the curve has none).
781
 *
782
 * The caller must pass a valid non-NULL LWNURBSCURVE pointer. The variant argument influences
783
 * whether an SRID integer is included (extended/ISO handling).
784
 *
785
 * @param curve NURBS curve to size (must be non-NULL).
786
 * @param variant WKB variant flags that determine SRID inclusion and encoding options.
787
 * @return number of bytes required to write the curve in WKB form.
788
 */
789
static size_t lwnurbscurve_to_wkb_size(const LWNURBSCURVE *curve, uint8_t variant)
790
0
{
791
0
    size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; /* endian + type */
792
793
    /* Extended WKB needs space for optional SRID integer */
794
0
    if ( lwgeom_wkb_needs_srid((LWGEOM*)curve, variant) )
795
0
        size += WKB_INT_SIZE;
796
797
    /* ISO/IEC 13249-3:2016 compliant structure:
798
     * <byte order> <wkbnurbs> [ <wkbdegree> <wkbcontrolpoints binary> <wkbknots binary> ]
799
     */
800
801
0
    size += WKB_INT_SIZE; /* degree */
802
803
    /* Control points count */
804
0
    size += WKB_INT_SIZE; /* npoints */
805
806
0
    uint32_t npoints = curve->points ? curve->points->npoints : 0;
807
0
    uint32_t dims = curve->points ? FLAGS_NDIMS(curve->points->flags) : 2;
808
809
    /* ISO format: Each control point has individual weight structure
810
     * <nurbspoint binary representation> ::=
811
     * <byte order> [ <wkbweightedpoint> <bit> [ <wkbweight> ] ]
812
     */
813
0
    for (uint32_t i = 0; i < npoints; i++) {
814
0
        size += WKB_BYTE_SIZE; /* byte order for each point */
815
0
        size += dims * WKB_DOUBLE_SIZE; /* point coordinates */
816
0
        size += WKB_BYTE_SIZE; /* weight bit flag */
817
818
        /* Add weight value if this point has a custom weight */
819
0
        if (curve->weights && i < curve->nweights && curve->weights[i] != 1.0) {
820
0
            size += WKB_DOUBLE_SIZE; /* weight value */
821
0
        }
822
0
    }
823
824
    /* Knots are always required in WKB output (generate uniform if not present) */
825
0
    size += WKB_INT_SIZE; /* nknots count */
826
0
    uint32_t nknots_for_size = 0;
827
0
    double *knots_for_size = lwnurbscurve_get_or_generate_knots(curve, &nknots_for_size);
828
0
    if (knots_for_size) {
829
0
      size += WKB_DOUBLE_SIZE * nknots_for_size;
830
0
      lwfree(knots_for_size); /* Just needed the count */
831
0
    } else if (nknots_for_size > 0) {
832
      /* Inconsistent state - got count but no array */
833
0
      lwerror("Failed to get knots for NURBS curve");
834
0
      return 0;
835
0
    }
836
837
0
  return size;
838
0
}
839
840
/**
841
 * Encode a NURBS curve into WKB (ISO/IEC 13249-3:2016) and write into a buffer.
842
 *
843
 * Produces an ISO-compliant WKB representation for a LWNURBSCURVE:
844
 * - writes byte-order marker and geometry type, and optional SRID for extended variants;
845
 * - writes curve degree and control-point count;
846
 * - for each control point writes a per-point byte-order marker, coordinates (per point dimensionality),
847
 *   a single-byte weight-presence flag, and the weight value when present (default weight = 1.0 is omitted);
848
 * - writes the knot vector length followed by knot values (requests knots via lwnurbscurve_get_or_generate_knots;
849
 *   if none are returned a zero knot-count is written as a fallback).
850
 *
851
 * The function advances the provided buffer pointer as it writes. It may allocate a temporary knot array
852
 * from lwnurbscurve_get_or_generate_knots and frees it before returning.
853
 *
854
 * @param curve NURBS curve to encode.
855
 * @param buf   Pointer to the output buffer where WKB bytes (or hex characters, depending on variant) are written.
856
 * @param variant Bitmask selecting WKB variant/encoding options (endianness, extended/ISO/hex modes).
857
 * @return Updated buffer pointer positioned immediately after the last written byte/character.
858
 */
859
static uint8_t* lwnurbscurve_to_wkb_buf(const LWNURBSCURVE *curve, uint8_t *buf, uint8_t variant)
860
0
{
861
0
    uint32_t wkb_type = lwgeom_wkb_type((LWGEOM*)curve, variant);
862
863
    /* Write endian flag */
864
0
    buf = endian_to_wkb_buf(buf, variant);
865
866
    /* Write type */
867
0
    buf = integer_to_wkb_buf(wkb_type, buf, variant);
868
869
    /* Set the optional SRID for extended variant */
870
0
    if ( lwgeom_wkb_needs_srid((LWGEOM*)curve, variant) )
871
0
        buf = integer_to_wkb_buf(curve->srid, buf, variant);
872
873
    /* ISO/IEC 13249-3:2016 compliant structure */
874
0
    buf = integer_to_wkb_buf(curve->degree, buf, variant);
875
876
    /* Write control points count */
877
0
    uint32_t npoints = curve->points ? curve->points->npoints : 0;
878
0
    uint32_t dims = curve->points ? FLAGS_NDIMS(curve->points->flags) : 2;
879
0
    buf = integer_to_wkb_buf(npoints, buf, variant);
880
881
    /* ISO format: Write each control point with individual weight structure
882
     * <nurbspoint binary representation> ::=
883
     * <byte order> [ <wkbweightedpoint> <bit> [ <wkbweight> ] ]
884
     */
885
0
    for (uint32_t i = 0; i < npoints; i++) {
886
        /* Write byte order for this point (ISO requirement) */
887
0
        buf = endian_to_wkb_buf(buf, variant);
888
889
        /* Write point coordinates */
890
0
        if (curve->points) {
891
0
            double *coords = (double*)getPoint_internal(curve->points, i);
892
0
            for (uint32_t d = 0; d < dims; d++) {
893
0
                buf = double_to_wkb_buf(coords[d], buf, variant);
894
0
            }
895
0
        }
896
897
        /* Write weight bit flag */
898
0
        uint8_t has_weight = 0;
899
0
        double weight_value = 1.0; /* default weight */
900
901
0
        if (curve->weights && i < curve->nweights && curve->weights[i] != 1.0) {
902
0
            has_weight = 1;
903
0
            weight_value = curve->weights[i];
904
0
        }
905
906
0
        buf = byte_to_wkb_buf(has_weight, buf, variant);
907
908
        /* Write weight value if flag = 1 */
909
0
        if (has_weight) {
910
0
            buf = double_to_wkb_buf(weight_value, buf, variant);
911
0
        }
912
0
    }
913
914
    /* Write knots (always required - generate uniform if not present) */
915
0
    {
916
0
        uint32_t nknots = 0;
917
0
        double *knots = lwnurbscurve_get_or_generate_knots(curve, &nknots);
918
0
        if (knots && nknots > 0) {
919
0
            buf = integer_to_wkb_buf(nknots, buf, variant);
920
0
            for (uint32_t i = 0; i < nknots; i++) {
921
0
                buf = double_to_wkb_buf(knots[i], buf, variant);
922
0
            }
923
0
            lwfree(knots);
924
0
        } else if (npoints == 0) {
925
0
            buf = integer_to_wkb_buf(0, buf, variant);
926
0
        } else {
927
0
            lwerror("Failed to get knots for NURBS curve");
928
0
        }
929
0
    }
930
931
0
    return buf;
932
0
}
933
934
/**
935
 * Compute the number of bytes required to encode a geometry as WKB for a given variant.
936
 *
937
 * Determines the exact buffer size needed to write the provided LWGEOM in the specified
938
 * WKB variant (handles ISO/extended/hex encodings, SRID inclusion, and per-geometry
939
 * element sizing). Empty geometries are handled by the dedicated empty-to-WKB sizing
940
 * unless the extended variant bit is set, in which case extended encoding rules apply.
941
 *
942
 * @param geom Pointer to the geometry to size; returns 0 and logs an error if NULL.
943
 * @param variant Bitmask of WKB variant flags (e.g., WKB_EXTENDED, WKB_HEX, NDR/XDR)
944
 *        that control dimensionality, SRID inclusion, and encoding form.
945
 * @return Number of bytes required to encode `geom` under `variant`, or 0 on error.
946
 */
947
static size_t
948
lwgeom_to_wkb_size(const LWGEOM *geom, uint8_t variant)
949
0
{
950
0
  size_t size = 0;
951
952
0
  if (geom == NULL)
953
0
  {
954
0
    LWDEBUG(4, "Cannot convert NULL into WKB.");
955
0
    lwerror("Cannot convert NULL into WKB.");
956
0
    return 0;
957
0
  }
958
959
  /* Short circuit out empty geometries */
960
0
  if ( geom->type != NURBSCURVETYPE && (!(variant & WKB_EXTENDED)) && lwgeom_is_empty(geom) )
961
0
  {
962
0
    return empty_to_wkb_size(geom, variant);
963
0
  }
964
965
0
  switch ( geom->type )
966
0
  {
967
0
    case POINTTYPE:
968
0
      size += lwpoint_to_wkb_size((LWPOINT*)geom, variant);
969
0
      break;
970
971
    /* LineString and CircularString both have points elements */
972
0
    case CIRCSTRINGTYPE:
973
0
    case LINETYPE:
974
0
      size += lwline_to_wkb_size((LWLINE*)geom, variant);
975
0
      break;
976
977
    /* Polygon has nrings and rings elements */
978
0
    case POLYGONTYPE:
979
0
      size += lwpoly_to_wkb_size((LWPOLY*)geom, variant);
980
0
      break;
981
982
    /* Triangle has one ring of three points */
983
0
    case TRIANGLETYPE:
984
0
      size += lwtriangle_to_wkb_size((LWTRIANGLE*)geom, variant);
985
0
      break;
986
987
    /* All these Collection types have ngeoms and geoms elements */
988
0
    case MULTIPOINTTYPE:
989
0
    case MULTILINETYPE:
990
0
    case MULTIPOLYGONTYPE:
991
0
    case COMPOUNDTYPE:
992
0
    case CURVEPOLYTYPE:
993
0
    case MULTICURVETYPE:
994
0
    case MULTISURFACETYPE:
995
0
    case COLLECTIONTYPE:
996
0
    case POLYHEDRALSURFACETYPE:
997
0
    case TINTYPE:
998
0
      size += lwcollection_to_wkb_size((LWCOLLECTION*)geom, variant);
999
0
      break;
1000
0
    case NURBSCURVETYPE:
1001
0
      size += lwnurbscurve_to_wkb_size((LWNURBSCURVE*)geom, variant);
1002
0
      break;
1003
    /* Unknown type! */
1004
0
    default:
1005
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type));
1006
0
  }
1007
1008
0
  return size;
1009
0
}
1010
1011
/**
1012
 * Serialize a LWGEOM into WKB format, writing into the provided buffer.
1013
 *
1014
 * Dispatches to the appropriate type-specific writer based on geom->type.
1015
 * For empty geometries, if the WKB_EXTENDED flag is not set the geometry is
1016
 * written using the canonical "empty" representation; otherwise empties are
1017
 * preserved and written by the type-specific writer.
1018
 *
1019
 * @param geom Geometry to serialize (must be non-NULL).
1020
 * @param buf Destination buffer where WKB bytes will be written. The function
1021
 *            writes starting at this pointer and returns the pointer advanced
1022
 *            past the written data.
1023
 * @param variant Bitflags controlling WKB flavor (e.g., WKB_EXTENDED, WKB_HEX,
1024
 *                WKB_NDR/WKB_XDR). These flags influence SRID inclusion, hex
1025
 *                vs binary output, and other variant-specific encoding rules.
1026
 * @return Pointer to the buffer position immediately after the written WKB on
1027
 *         success; NULL (0) on unsupported/unknown geometry type or other error.
1028
 */
1029
1030
static uint8_t* lwgeom_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant)
1031
0
{
1032
1033
  /* Do not simplify empties when outputting to canonical form */
1034
0
  if (geom->type != NURBSCURVETYPE && lwgeom_is_empty(geom) && !(variant & WKB_EXTENDED))
1035
0
    return empty_to_wkb_buf(geom, buf, variant);
1036
1037
0
  switch ( geom->type )
1038
0
  {
1039
0
    case POINTTYPE:
1040
0
      return lwpoint_to_wkb_buf((LWPOINT*)geom, buf, variant);
1041
1042
    /* LineString and CircularString both have 'points' elements */
1043
0
    case CIRCSTRINGTYPE:
1044
0
    case LINETYPE:
1045
0
      return lwline_to_wkb_buf((LWLINE*)geom, buf, variant);
1046
1047
    /* Polygon has 'nrings' and 'rings' elements */
1048
0
    case POLYGONTYPE:
1049
0
      return lwpoly_to_wkb_buf((LWPOLY*)geom, buf, variant);
1050
1051
    /* Triangle has one ring of three points */
1052
0
    case TRIANGLETYPE:
1053
0
      return lwtriangle_to_wkb_buf((LWTRIANGLE*)geom, buf, variant);
1054
1055
    /* All these Collection types have 'ngeoms' and 'geoms' elements */
1056
0
    case MULTIPOINTTYPE:
1057
0
    case MULTILINETYPE:
1058
0
    case MULTIPOLYGONTYPE:
1059
0
    case COMPOUNDTYPE:
1060
0
    case CURVEPOLYTYPE:
1061
0
    case MULTICURVETYPE:
1062
0
    case MULTISURFACETYPE:
1063
0
    case COLLECTIONTYPE:
1064
0
    case POLYHEDRALSURFACETYPE:
1065
0
    case TINTYPE:
1066
0
      return lwcollection_to_wkb_buf((LWCOLLECTION*)geom, buf, variant);
1067
0
    case NURBSCURVETYPE:
1068
0
      return lwnurbscurve_to_wkb_buf((LWNURBSCURVE*)geom, buf, variant);
1069
1070
    /* Unknown type! */
1071
0
    default:
1072
0
      lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type));
1073
0
  }
1074
  /* Return value to keep compiler happy. */
1075
0
  return 0;
1076
0
}
1077
1078
/**
1079
* Convert LWGEOM to a char* in WKB format. Caller is responsible for freeing
1080
* the returned array.
1081
*
1082
* @param variant. Unsigned bitmask value. Accepts one of: WKB_ISO, WKB_EXTENDED, WKB_SFSQL.
1083
* Accepts any of: WKB_NDR, WKB_HEX. For example: Variant = ( WKB_ISO | WKB_NDR ) would
1084
* return the little-endian ISO form of WKB. For Example: Variant = ( WKB_EXTENDED | WKB_HEX )
1085
* would return the big-endian extended form of WKB, as hex-encoded ASCII (the "canonical form").
1086
* @param size_out If supplied, will return the size of the returned memory segment,
1087
* including the null terminator in the case of ASCII.
1088
*/
1089
static ptrdiff_t
1090
lwgeom_to_wkb_write_buf(const LWGEOM *geom, uint8_t variant, uint8_t *buffer)
1091
0
{
1092
  /* If neither or both variants are specified, choose the native order */
1093
0
  if (!(variant & WKB_NDR || variant & WKB_XDR) || (variant & WKB_NDR && variant & WKB_XDR))
1094
0
  {
1095
0
    if (IS_BIG_ENDIAN)
1096
0
      variant = variant | WKB_XDR;
1097
0
    else
1098
0
      variant = variant | WKB_NDR;
1099
0
  }
1100
1101
  /* Write the WKB into the output buffer */
1102
0
  int written_bytes = (lwgeom_to_wkb_buf(geom, buffer, variant) - buffer);
1103
1104
0
  return written_bytes;
1105
0
}
1106
1107
uint8_t *
1108
lwgeom_to_wkb_buffer(const LWGEOM *geom, uint8_t variant)
1109
0
{
1110
0
  size_t b_size = lwgeom_to_wkb_size(geom, variant);
1111
  /* Hex string takes twice as much space as binary + a null character */
1112
0
  if (variant & WKB_HEX)
1113
0
  {
1114
0
    b_size = 2 * b_size + 1;
1115
0
  }
1116
1117
0
  uint8_t *buffer = (uint8_t *)lwalloc(b_size);
1118
0
  ptrdiff_t written_size = lwgeom_to_wkb_write_buf(geom, variant, buffer);
1119
0
  if (variant & WKB_HEX)
1120
0
  {
1121
0
    buffer[written_size] = '\0';
1122
0
    written_size++;
1123
0
  }
1124
1125
0
  if (written_size != (ptrdiff_t)b_size)
1126
0
  {
1127
0
    char *wkt = lwgeom_to_wkt(geom, WKT_EXTENDED, 15, NULL);
1128
0
    lwerror("Output WKB is not the same size as the allocated buffer. Variant: %u, Geom: %s", variant, wkt);
1129
0
    lwfree(wkt);
1130
0
    lwfree(buffer);
1131
0
    return NULL;
1132
0
  }
1133
1134
0
  return buffer;
1135
0
}
1136
1137
char *
1138
lwgeom_to_hexwkb_buffer(const LWGEOM *geom, uint8_t variant)
1139
0
{
1140
0
  return (char *)lwgeom_to_wkb_buffer(geom, variant | WKB_HEX);
1141
0
}
1142
1143
lwvarlena_t *
1144
lwgeom_to_wkb_varlena(const LWGEOM *geom, uint8_t variant)
1145
0
{
1146
0
  size_t b_size = lwgeom_to_wkb_size(geom, variant);
1147
  /* Hex string takes twice as much space as binary, but No NULL ending in varlena */
1148
0
  if (variant & WKB_HEX)
1149
0
  {
1150
0
    b_size = 2 * b_size;
1151
0
  }
1152
1153
0
  lwvarlena_t *buffer = (lwvarlena_t *)lwalloc(b_size + LWVARHDRSZ);
1154
0
  int written_size = lwgeom_to_wkb_write_buf(geom, variant, (uint8_t *)buffer->data);
1155
0
  if (written_size != (ptrdiff_t)b_size)
1156
0
  {
1157
0
    char *wkt = lwgeom_to_wkt(geom, WKT_EXTENDED, 15, NULL);
1158
0
    lwerror("Output WKB is not the same size as the allocated buffer. Variant: %u, Geom: %s", variant, wkt);
1159
0
    lwfree(wkt);
1160
0
    lwfree(buffer);
1161
0
    return NULL;
1162
0
  }
1163
0
  LWSIZE_SET(buffer->size, written_size + LWVARHDRSZ);
1164
0
  return buffer;
1165
0
}
1166
1167
lwvarlena_t *
1168
lwgeom_to_hexwkb_varlena(const LWGEOM *geom, uint8_t variant)
1169
0
{
1170
0
  return lwgeom_to_wkb_varlena(geom, variant | WKB_HEX);
1171
0
}