Coverage Report

Created: 2026-07-16 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/gserialized2.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 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22
 * Copyright 2017 Darafei Praliaskouski <me@komzpa.net>
23
 *
24
 **********************************************************************/
25
26
/*
27
*  GSERIALIZED version 2 includes an optional extended flags uint64_t
28
*  before the optional bounding box. There may be other optional
29
*  components before the data area, but they all must be double
30
*  aligned to that the ordinates remain double aligned.
31
*
32
*  <size> size        Used by PgSQL VARSIZE   g->size
33
*  <srid               3 bytes                g->srid
34
*   gflags>            1 byte                 g->gflags
35
*  [<extendedflags>   Optional extended flags (check flags for cue)
36
*   <extendedflags>]
37
*  [<bbox-xmin>       Optional bounding box (check flags for cue)
38
*   <bbox-xmax>       Number of dimensions is variable
39
*   <bbox-ymin>       and also indicated in the flags
40
*   <bbox-ymax>]
41
*  ...
42
*  data area
43
*/
44
45
#include "liblwgeom_internal.h"
46
#include "lwgeom_log.h"
47
#include "lwgeodetic.h"
48
#include "gserialized2.h"
49
50
#include <stddef.h>
51
52
/***********************************************************************
53
* GSERIALIZED metadata utility functions.
54
*/
55
56
static int gserialized2_read_gbox_p(const GSERIALIZED *g, GBOX *gbox);
57
58
59
lwflags_t gserialized2_get_lwflags(const GSERIALIZED *g)
60
0
{
61
0
  lwflags_t lwflags = 0;
62
0
  uint8_t gflags = g->gflags;
63
0
  FLAGS_SET_Z(lwflags, G2FLAGS_GET_Z(gflags));
64
0
  FLAGS_SET_M(lwflags, G2FLAGS_GET_M(gflags));
65
0
  FLAGS_SET_BBOX(lwflags, G2FLAGS_GET_BBOX(gflags));
66
0
  FLAGS_SET_GEODETIC(lwflags, G2FLAGS_GET_GEODETIC(gflags));
67
0
  if (G2FLAGS_GET_EXTENDED(gflags))
68
0
  {
69
0
    uint64_t xflags = 0;
70
0
    memcpy(&xflags, g->data, sizeof(uint64_t));
71
0
    FLAGS_SET_SOLID(lwflags, xflags & G2FLAG_X_SOLID);
72
0
  }
73
0
  return lwflags;
74
0
}
75
76
static int lwflags_uses_extended_flags(lwflags_t lwflags)
77
0
{
78
0
  lwflags_t core_lwflags = LWFLAG_Z | LWFLAG_M | LWFLAG_BBOX | LWFLAG_GEODETIC;
79
0
  return (lwflags & (~core_lwflags)) != 0;
80
0
}
81
82
83
static inline size_t gserialized2_box_size(const GSERIALIZED *g)
84
0
{
85
0
  if (G2FLAGS_GET_GEODETIC(g->gflags))
86
0
    return 6 * sizeof(float);
87
0
  else
88
0
    return 2 * G2FLAGS_NDIMS(g->gflags) * sizeof(float);
89
0
}
90
91
static inline size_t gserialized2_header_size(const GSERIALIZED *g)
92
0
{
93
0
  uint32_t sz = 8; /* varsize (4) + srid(3) + flags (1) */
94
95
0
  if (gserialized2_has_extended(g))
96
0
    sz += 8;
97
98
0
  if (gserialized2_has_bbox(g))
99
0
    sz += gserialized2_box_size(g);
100
101
0
  return sz;
102
0
}
103
104
/* Returns a pointer to the start of the geometry data */
105
static inline uint8_t *
106
gserialized2_get_geometry_p(const GSERIALIZED *g)
107
0
{
108
0
  uint32_t extra_data_bytes = 0;
109
0
  if (gserialized2_has_extended(g))
110
0
    extra_data_bytes += sizeof(uint64_t);
111
112
0
  if (gserialized2_has_bbox(g))
113
0
    extra_data_bytes += gserialized2_box_size(g);
114
115
0
  return ((uint8_t *)g->data) + extra_data_bytes;
116
0
}
117
118
uint8_t lwflags_get_g2flags(lwflags_t lwflags)
119
0
{
120
0
  uint8_t gflags = 0;
121
0
  G2FLAGS_SET_Z(gflags, FLAGS_GET_Z(lwflags));
122
0
  G2FLAGS_SET_M(gflags, FLAGS_GET_M(lwflags));
123
0
  G2FLAGS_SET_BBOX(gflags, FLAGS_GET_BBOX(lwflags));
124
0
  G2FLAGS_SET_GEODETIC(gflags, FLAGS_GET_GEODETIC(lwflags));
125
0
  G2FLAGS_SET_EXTENDED(gflags, lwflags_uses_extended_flags(lwflags));
126
0
  G2FLAGS_SET_VERSION(gflags, 1);
127
0
  return gflags;
128
0
}
129
130
/* handle missaligned uint32_t data */
131
static inline uint32_t gserialized2_get_uint32_t(const uint8_t *loc)
132
0
{
133
0
  return *((uint32_t*)loc);
134
0
}
135
136
uint8_t g2flags(int has_z, int has_m, int is_geodetic)
137
0
{
138
0
  uint8_t gflags = 0;
139
0
  if (has_z)
140
0
    G2FLAGS_SET_Z(gflags, 1);
141
0
  if (has_m)
142
0
    G2FLAGS_SET_M(gflags, 1);
143
0
  if (is_geodetic)
144
0
    G2FLAGS_SET_GEODETIC(gflags, 1);
145
0
  return gflags;
146
0
}
147
148
int gserialized2_has_bbox(const GSERIALIZED *g)
149
0
{
150
0
  return G2FLAGS_GET_BBOX(g->gflags);
151
0
}
152
153
int gserialized2_has_extended(const GSERIALIZED *g)
154
0
{
155
0
  return G2FLAGS_GET_EXTENDED(g->gflags);
156
0
}
157
158
int gserialized2_has_z(const GSERIALIZED *g)
159
0
{
160
0
  return G2FLAGS_GET_Z(g->gflags);
161
0
}
162
163
int gserialized2_has_m(const GSERIALIZED *g)
164
0
{
165
0
  return G2FLAGS_GET_M(g->gflags);
166
0
}
167
168
int gserialized2_ndims(const GSERIALIZED *g)
169
0
{
170
0
  return G2FLAGS_NDIMS(g->gflags);
171
0
}
172
173
int gserialized2_is_geodetic(const GSERIALIZED *g)
174
0
{
175
0
    return G2FLAGS_GET_GEODETIC(g->gflags);
176
0
}
177
178
uint32_t gserialized2_max_header_size(void)
179
0
{
180
  /* GSERIALIZED size + max bbox according gbox_serialized_size (XYZM*2) + extended flags + type */
181
0
  return offsetof(GSERIALIZED, data) + 8 * sizeof(float) + sizeof(uint64_t) + sizeof(uint32_t);
182
0
}
183
184
185
uint32_t gserialized2_get_type(const GSERIALIZED *g)
186
0
{
187
0
  uint8_t *ptr = gserialized2_get_geometry_p(g);
188
0
  return *((uint32_t*)(ptr));
189
0
}
190
191
int32_t gserialized2_get_srid(const GSERIALIZED *g)
192
0
{
193
0
  int32_t srid = 0;
194
0
  srid = srid | (g->srid[0] << 16);
195
0
  srid = srid | (g->srid[1] << 8);
196
0
  srid = srid | (g->srid[2]);
197
  /* Only the first 21 bits are set. Slide up and back to pull
198
     the negative bits down, if we need them. */
199
0
  srid = (srid<<11)>>11;
200
201
  /* 0 is our internal unknown value. We'll map back and forth here for now */
202
0
  if (srid == 0)
203
0
    return SRID_UNKNOWN;
204
0
  else
205
0
    return srid;
206
0
}
207
208
void gserialized2_set_srid(GSERIALIZED *g, int32_t srid)
209
0
{
210
0
  LWDEBUGF(3, "%s called with srid = %d", __func__, srid);
211
212
0
  srid = clamp_srid(srid);
213
214
  /* 0 is our internal unknown value.
215
   * We'll map back and forth here for now */
216
0
  if (srid == SRID_UNKNOWN)
217
0
    srid = 0;
218
219
0
  g->srid[0] = (srid & 0x001F0000) >> 16;
220
0
  g->srid[1] = (srid & 0x0000FF00) >> 8;
221
0
  g->srid[2] = (srid & 0x000000FF);
222
0
}
223
224
static size_t gserialized2_is_empty_recurse(const uint8_t *p, int *isempty);
225
static size_t gserialized2_is_empty_recurse(const uint8_t *p, int *isempty)
226
0
{
227
0
  uint32_t type = 0, num = 0;
228
229
  /* Short circuit if we found any non-empty component */
230
0
  if (!*isempty) return 0;
231
232
0
  memcpy(&type, p, 4);
233
0
  memcpy(&num, p+4, 4);
234
235
0
  if (lwtype_is_collection(type))
236
0
  {
237
    /* Recurse into collections */
238
0
    size_t lz = 8;
239
0
    for ( uint32_t i = 0; i < num; i++ )
240
0
    {
241
0
      lz += gserialized2_is_empty_recurse(p+lz, isempty);
242
0
      if (!*isempty)
243
0
        return lz;
244
0
    }
245
0
    *isempty = LW_TRUE;
246
0
    return lz;
247
0
  }
248
0
  else
249
0
  {
250
0
    size_t lz = 8;
251
    /* Any non-collection with zero elements is empty */
252
0
    if ( num == 0 )
253
0
    {
254
0
      if ( type == NURBSCURVETYPE )
255
0
      {
256
0
        uint32_t nweights = 0, nknots = 0;
257
0
        memcpy(&nweights, p+12, 4);
258
0
        memcpy(&nknots, p+16, 4);
259
0
        lz = 24 + (sizeof(double) * ((size_t)nweights + nknots));
260
0
      }
261
0
      *isempty = LW_TRUE;
262
0
    }
263
    /*
264
     * Special case to handle polygon with a non-zero
265
     * set of empty rings
266
     * https://trac.osgeo.org/postgis/ticket/6028
267
     */
268
0
    else if ( num > 0 && type == POLYGONTYPE )
269
0
    {
270
0
      for ( uint32_t i = 0; i < num; i++ )
271
0
      {
272
0
        uint32_t lrnum;
273
0
        memcpy(&lrnum, p+lz, 4);
274
0
        lz += 4;
275
0
        if ( lrnum > 0 )
276
0
        {
277
0
          *isempty = LW_FALSE;
278
0
          return lz;
279
0
        }
280
0
      }
281
0
      *isempty = LW_TRUE;
282
0
    }
283
    /* Any other non-collection with more than zero elements is not empty */
284
0
    else
285
0
    {
286
0
      *isempty = LW_FALSE;
287
0
    }
288
0
    return lz;
289
0
  }
290
0
}
291
292
int gserialized2_is_empty(const GSERIALIZED *g)
293
0
{
294
0
  int isempty = LW_TRUE;
295
0
  uint8_t *p = gserialized2_get_geometry_p(g);
296
0
  gserialized2_is_empty_recurse(p, &isempty);
297
0
  return isempty;
298
0
}
299
300
301
/* Prototype for lookup3.c */
302
/* key = the key to hash */
303
/* length = length of the key */
304
/* pc = IN: primary initval, OUT: primary hash */
305
/* pb = IN: secondary initval, OUT: secondary hash */
306
void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb);
307
308
int32_t
309
gserialized2_hash(const GSERIALIZED *g1)
310
0
{
311
0
  int32_t hval;
312
0
  int32_t pb = 0, pc = 0;
313
  /* Point to just the type/coordinate part of buffer */
314
0
  size_t hsz1 = gserialized2_header_size(g1);
315
0
  uint8_t *b1 = (uint8_t *)g1 + hsz1;
316
  /* Calculate size of type/coordinate buffer */
317
0
  size_t sz1 = LWSIZE_GET(g1->size);
318
0
  size_t bsz1 = sz1 - hsz1;
319
  /* Calculate size of srid/type/coordinate buffer */
320
0
  int32_t srid = gserialized2_get_srid(g1);
321
0
  size_t bsz2 = bsz1 + sizeof(int);
322
0
  uint8_t *b2 = lwalloc(bsz2);
323
  /* Copy srid into front of combined buffer */
324
0
  memcpy(b2, &srid, sizeof(int));
325
  /* Copy type/coordinates into rest of combined buffer */
326
0
  memcpy(b2+sizeof(int), b1, bsz1);
327
  /* Hash combined buffer */
328
0
  hashlittle2(b2, bsz2, (uint32_t *)&pb, (uint32_t *)&pc);
329
0
  lwfree(b2);
330
0
  hval = pb ^ pc;
331
0
  return hval;
332
0
}
333
334
335
const float * gserialized2_get_float_box_p(const GSERIALIZED *g, size_t *ndims)
336
0
{
337
  /* Cannot do anything if there's no box */
338
0
  if (!(g && gserialized_has_bbox(g)))
339
0
    return NULL;
340
341
0
  uint8_t *ptr = (uint8_t*)(g->data);
342
0
  size_t bndims = G2FLAGS_NDIMS_BOX(g->gflags);
343
344
0
  if (ndims)
345
0
    *ndims = bndims;
346
347
  /* Advance past optional extended flags */
348
0
  if (gserialized2_has_extended(g))
349
0
    ptr += 8;
350
351
0
  return (const float *)(ptr);
352
0
}
353
354
int gserialized2_read_gbox_p(const GSERIALIZED *g, GBOX *gbox)
355
0
{
356
  /* Null input! */
357
0
  if (!(g && gbox)) return LW_FAILURE;
358
359
0
  uint8_t gflags = g->gflags;
360
361
  /* Initialize the flags on the box */
362
0
  gbox->flags = gserialized2_get_lwflags(g);
363
364
  /* Has pre-calculated box */
365
0
  if (G2FLAGS_GET_BBOX(gflags))
366
0
  {
367
0
    int i = 0;
368
0
    const float *fbox = gserialized2_get_float_box_p(g, NULL);
369
0
    gbox->xmin = fbox[i++];
370
0
    gbox->xmax = fbox[i++];
371
0
    gbox->ymin = fbox[i++];
372
0
    gbox->ymax = fbox[i++];
373
374
    /* Geodetic? Read next dimension (geocentric Z) and return */
375
0
    if (G2FLAGS_GET_GEODETIC(gflags))
376
0
    {
377
0
      gbox->zmin = fbox[i++];
378
0
      gbox->zmax = fbox[i++];
379
0
      return LW_SUCCESS;
380
0
    }
381
    /* Cartesian? Read extra dimensions (if there) and return */
382
0
    if (G2FLAGS_GET_Z(gflags))
383
0
    {
384
0
      gbox->zmin = fbox[i++];
385
0
      gbox->zmax = fbox[i++];
386
0
    }
387
0
    if (G2FLAGS_GET_M(gflags))
388
0
    {
389
0
      gbox->mmin = fbox[i++];
390
0
      gbox->mmax = fbox[i++];
391
0
    }
392
0
    return LW_SUCCESS;
393
0
  }
394
0
  return LW_FAILURE;
395
0
}
396
397
/*
398
* Populate a bounding box *without* allocating an LWGEOM. Useful
399
* for some performance purposes.
400
*/
401
int
402
gserialized2_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox)
403
0
{
404
0
  uint32_t type = gserialized2_get_type(g);
405
0
  uint8_t *geometry_start = gserialized2_get_geometry_p(g);
406
0
  double *dptr = (double *)(geometry_start);
407
0
  int32_t *iptr = (int32_t *)(geometry_start);
408
409
  /* Peeking doesn't help if you already have a box or are geodetic */
410
0
  if (G2FLAGS_GET_GEODETIC(g->gflags) || G2FLAGS_GET_BBOX(g->gflags))
411
0
  {
412
0
    return LW_FAILURE;
413
0
  }
414
415
  /* Boxes of points are easy peasy */
416
0
  if (type == POINTTYPE)
417
0
  {
418
0
    int i = 1; /* Start past <pointtype><padding> */
419
420
    /* Read the npoints flag */
421
0
    int isempty = (iptr[1] == 0);
422
423
    /* EMPTY point has no box */
424
0
    if (isempty) return LW_FAILURE;
425
426
0
    gbox->xmin = gbox->xmax = dptr[i++];
427
0
    gbox->ymin = gbox->ymax = dptr[i++];
428
0
    gbox->flags = gserialized2_get_lwflags(g);
429
0
    if (G2FLAGS_GET_Z(g->gflags))
430
0
    {
431
0
      gbox->zmin = gbox->zmax = dptr[i++];
432
0
    }
433
0
    if (G2FLAGS_GET_M(g->gflags))
434
0
    {
435
0
      gbox->mmin = gbox->mmax = dptr[i++];
436
0
    }
437
0
    gbox_float_round(gbox);
438
0
    return LW_SUCCESS;
439
0
  }
440
  /* We can calculate the box of a two-point cartesian line trivially */
441
0
  else if (type == LINETYPE)
442
0
  {
443
0
    int ndims = G2FLAGS_NDIMS(g->gflags);
444
0
    int i = 0; /* Start at <linetype><npoints> */
445
0
    int npoints = iptr[1]; /* Read the npoints */
446
447
    /* This only works with 2-point lines */
448
0
    if (npoints != 2)
449
0
      return LW_FAILURE;
450
451
    /* Advance to X */
452
    /* Past <linetype><npoints> */
453
0
    i++;
454
0
    gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]);
455
0
    gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]);
456
457
    /* Advance to Y */
458
0
    i++;
459
0
    gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]);
460
0
    gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]);
461
462
0
    gbox->flags = gserialized2_get_lwflags(g);
463
0
    if (G2FLAGS_GET_Z(g->gflags))
464
0
    {
465
      /* Advance to Z */
466
0
      i++;
467
0
      gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]);
468
0
      gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]);
469
0
    }
470
0
    if (G2FLAGS_GET_M(g->gflags))
471
0
    {
472
      /* Advance to M */
473
0
      i++;
474
0
      gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]);
475
0
      gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]);
476
0
    }
477
0
    gbox_float_round(gbox);
478
0
    return LW_SUCCESS;
479
0
  }
480
  /* We can also do single-entry multi-points */
481
0
  else if (type == MULTIPOINTTYPE)
482
0
  {
483
0
    int i = 0; /* Start at <multipointtype><ngeoms> */
484
0
    int ngeoms = iptr[1]; /* Read the ngeoms */
485
0
    int npoints;
486
487
    /* This only works with single-entry multipoints */
488
0
    if (ngeoms != 1)
489
0
      return LW_FAILURE;
490
491
    /* Npoints is at <multipointtype><ngeoms><pointtype><npoints> */
492
0
    npoints = iptr[3];
493
494
    /* The check below is necessary because we can have a MULTIPOINT
495
     * that contains a single, empty POINT (ngeoms = 1, npoints = 0) */
496
0
    if (npoints != 1)
497
0
      return LW_FAILURE;
498
499
    /* Move forward two doubles (four ints) */
500
    /* Past <multipointtype><ngeoms> */
501
    /* Past <pointtype><npoints> */
502
0
    i += 2;
503
504
    /* Read the doubles from the one point */
505
0
    gbox->xmin = gbox->xmax = dptr[i++];
506
0
    gbox->ymin = gbox->ymax = dptr[i++];
507
0
    gbox->flags = gserialized2_get_lwflags(g);
508
0
    if (G2FLAGS_GET_Z(g->gflags))
509
0
    {
510
0
      gbox->zmin = gbox->zmax = dptr[i++];
511
0
    }
512
0
    if (G2FLAGS_GET_M(g->gflags))
513
0
    {
514
0
      gbox->mmin = gbox->mmax = dptr[i++];
515
0
    }
516
0
    gbox_float_round(gbox);
517
0
    return LW_SUCCESS;
518
0
  }
519
  /* And we can do single-entry multi-lines with two vertices (!!!) */
520
0
  else if (type == MULTILINETYPE)
521
0
  {
522
0
    int ndims = G2FLAGS_NDIMS(g->gflags);
523
0
    int i = 0; /* Start at <multilinetype><ngeoms> */
524
0
    int ngeoms = iptr[1]; /* Read the ngeoms */
525
0
    int npoints;
526
527
    /* This only works with 1-line multilines */
528
0
    if (ngeoms != 1)
529
0
      return LW_FAILURE;
530
531
    /* Npoints is at <multilinetype><ngeoms><linetype><npoints> */
532
0
    npoints = iptr[3];
533
534
0
    if (npoints != 2)
535
0
      return LW_FAILURE;
536
537
    /* Advance to X */
538
    /* Move forward two doubles (four ints) */
539
    /* Past <multilinetype><ngeoms> */
540
    /* Past <linetype><npoints> */
541
0
    i += 2;
542
0
    gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]);
543
0
    gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]);
544
545
    /* Advance to Y */
546
0
    i++;
547
0
    gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]);
548
0
    gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]);
549
550
0
    gbox->flags = gserialized2_get_lwflags(g);
551
0
    if (G2FLAGS_GET_Z(g->gflags))
552
0
    {
553
      /* Advance to Z */
554
0
      i++;
555
0
      gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]);
556
0
      gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]);
557
0
    }
558
0
    if (G2FLAGS_GET_M(g->gflags))
559
0
    {
560
      /* Advance to M */
561
0
      i++;
562
0
      gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]);
563
0
      gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]);
564
0
    }
565
0
    gbox_float_round(gbox);
566
0
    return LW_SUCCESS;
567
0
  }
568
569
0
  return LW_FAILURE;
570
0
}
571
572
static inline void
573
gserialized2_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point)
574
0
{
575
0
  uint8_t dim = 0;
576
0
  out_point->x = dptr[dim++];
577
0
  out_point->y = dptr[dim++];
578
579
0
  if (G2FLAGS_GET_Z(flags))
580
0
  {
581
0
    out_point->z = dptr[dim++];
582
0
  }
583
0
  if (G2FLAGS_GET_M(flags))
584
0
  {
585
0
    out_point->m = dptr[dim];
586
0
  }
587
0
}
588
589
int
590
gserialized2_peek_first_point(const GSERIALIZED *g, POINT4D *out_point)
591
0
{
592
0
  uint8_t *geometry_start = gserialized2_get_geometry_p(g);
593
594
0
  uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0;
595
0
  if (isEmpty)
596
0
  {
597
0
    return LW_FAILURE;
598
0
  }
599
600
0
  uint32_t type = (((uint32_t *)geometry_start)[0]);
601
  /* Setup double_array_start depending on the geometry type */
602
0
  double *double_array_start = NULL;
603
0
  switch (type)
604
0
  {
605
0
  case (POINTTYPE):
606
    /* For points we only need to jump over the type and npoints 32b ints */
607
0
    double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t));
608
0
    break;
609
610
0
  default:
611
0
    lwerror("%s is currently not implemented for type %d", __func__, type);
612
0
    return LW_FAILURE;
613
0
  }
614
615
0
  gserialized2_copy_point(double_array_start, g->gflags, out_point);
616
0
  return LW_SUCCESS;
617
0
}
618
619
/**
620
* Read the bounding box off a serialization and calculate one if
621
* it is not already there.
622
*/
623
int gserialized2_get_gbox_p(const GSERIALIZED *g, GBOX *box)
624
0
{
625
  /* Try to just read the serialized box. */
626
0
  if (gserialized2_read_gbox_p(g, box) == LW_SUCCESS)
627
0
  {
628
0
    return LW_SUCCESS;
629
0
  }
630
  /* No box? Try to peek into simpler geometries and */
631
  /* derive a box without creating an lwgeom */
632
0
  else if (gserialized2_peek_gbox_p(g, box) == LW_SUCCESS)
633
0
  {
634
0
    return LW_SUCCESS;
635
0
  }
636
  /* Damn! Nothing for it but to create an lwgeom... */
637
  /* See http://trac.osgeo.org/postgis/ticket/1023 */
638
0
  else
639
0
  {
640
0
    LWGEOM *lwgeom = lwgeom_from_gserialized(g);
641
0
    int ret = lwgeom_calculate_gbox(lwgeom, box);
642
0
    gbox_float_round(box);
643
0
    lwgeom_free(lwgeom);
644
0
    return ret;
645
0
  }
646
0
}
647
648
/**
649
* Read the bounding box off a serialization and fail if
650
* it is not already there.
651
*/
652
int gserialized2_fast_gbox_p(const GSERIALIZED *g, GBOX *box)
653
0
{
654
  /* Try to just read the serialized box. */
655
0
  if (gserialized2_read_gbox_p(g, box) == LW_SUCCESS)
656
0
  {
657
0
    return LW_SUCCESS;
658
0
  }
659
  /* No box? Try to peek into simpler geometries and */
660
  /* derive a box without creating an lwgeom */
661
0
  else if (gserialized2_peek_gbox_p(g, box) == LW_SUCCESS)
662
0
  {
663
0
    return LW_SUCCESS;
664
0
  }
665
0
  else
666
0
  {
667
0
    return LW_FAILURE;
668
0
  }
669
0
}
670
671
672
673
674
/***********************************************************************
675
* Calculate the GSERIALIZED size for an LWGEOM.
676
*/
677
678
/* Private functions */
679
680
static size_t gserialized2_from_any_size(const LWGEOM *geom); /* Local prototype */
681
682
static size_t gserialized2_from_lwpoint_size(const LWPOINT *point)
683
0
{
684
0
  size_t size = 4; /* Type number. */
685
686
0
  assert(point);
687
688
0
  size += 4; /* Number of points (one or zero (empty)). */
689
0
  size += sizeof(double) * point->point->npoints * FLAGS_NDIMS(point->flags);
690
691
0
  LWDEBUGF(3, "point size = %zu", size);
692
693
0
  return size;
694
0
}
695
696
static size_t gserialized2_from_lwline_size(const LWLINE *line)
697
0
{
698
0
  size_t size = 4; /* Type number. */
699
700
0
  assert(line);
701
702
0
  size += 4; /* Number of points (zero => empty). */
703
0
  size += sizeof(double) * line->points->npoints * FLAGS_NDIMS(line->flags);
704
705
0
  LWDEBUGF(3, "linestring size = %zu", size);
706
707
0
  return size;
708
0
}
709
710
static size_t gserialized2_from_lwtriangle_size(const LWTRIANGLE *triangle)
711
0
{
712
0
  size_t size = 4; /* Type number. */
713
714
0
  assert(triangle);
715
716
0
  size += 4; /* Number of points (zero => empty). */
717
0
  size += sizeof(double)* triangle->points->npoints * FLAGS_NDIMS(triangle->flags);
718
719
0
  LWDEBUGF(3, "triangle size = %zu", size);
720
721
0
  return size;
722
0
}
723
724
static size_t gserialized2_from_lwpoly_size(const LWPOLY *poly)
725
0
{
726
0
  size_t size = 4; /* Type number. */
727
0
  uint32_t i = 0;
728
0
  const size_t point_size = FLAGS_NDIMS(poly->flags) * sizeof(double);
729
730
0
  assert(poly);
731
732
0
  size += 4; /* Number of rings (zero => empty). */
733
0
  if (poly->nrings % 2)
734
0
    size += 4; /* Padding to double alignment. */
735
736
0
  for (i = 0; i < poly->nrings; i++)
737
0
  {
738
0
    size += 4; /* Number of points in ring. */
739
0
    size += poly->rings[i]->npoints * point_size;
740
0
  }
741
742
0
  LWDEBUGF(3, "polygon size = %zu", size);
743
744
0
  return size;
745
0
}
746
747
static size_t gserialized2_from_lwcircstring_size(const LWCIRCSTRING *curve)
748
0
{
749
0
  size_t size = 4; /* Type number. */
750
751
0
  assert(curve);
752
753
0
  size += 4; /* Number of points (zero => empty). */
754
0
  size += sizeof(double) * curve->points->npoints * FLAGS_NDIMS(curve->flags);
755
756
0
  LWDEBUGF(3, "circstring size = %zu", size);
757
758
0
  return size;
759
0
}
760
761
/**
762
 * Compute the number of bytes required to serialize an LWCOLLECTION into GSERIALIZED v2.
763
 *
764
 * The size includes the 4-byte type field, a 4-byte count of sub-geometries, and the
765
 * concatenated serialized sizes of each child geometry as returned by gserialized2_from_any_size().
766
 *
767
 * @param col Collection whose serialized size is being computed (must be non-NULL).
768
 * @return Total size in bytes required to store the collection payload (type + count + children).
769
 */
770
static size_t gserialized2_from_lwcollection_size(const LWCOLLECTION *col)
771
0
{
772
0
  size_t size = 4; /* Type number. */
773
0
  uint32_t i = 0;
774
775
0
  assert(col);
776
777
0
  size += 4; /* Number of sub-geometries (zero => empty). */
778
779
0
  for (i = 0; i < col->ngeoms; i++)
780
0
  {
781
0
    size_t subsize = gserialized2_from_any_size(col->geoms[i]);
782
0
    size += subsize;
783
0
    LWDEBUGF(3, "lwcollection subgeom(%d) size = %zu", i, subsize);
784
0
  }
785
786
0
  LWDEBUGF(3, "lwcollection size = %zu", size);
787
788
0
  return size;
789
0
}
790
791
/**
792
 * Compute the number of bytes required to serialize a NURBS curve (NURBSCURVETYPE)
793
 * into GSERIALIZED v2 format.
794
 *
795
 * The size includes:
796
 * - 4 bytes for the type field,
797
 * - 4 bytes each for degree, nweights, nknots, and the control-point count,
798
 * - optional weights array (nweights * sizeof(double)) if present,
799
 * - optional knots array (nknots * sizeof(double)) if present,
800
 * - control point coordinates (npoints * ndims * sizeof(double)), where ndims is
801
 *   derived from the curve's flags via FLAGS_NDIMS(curve->flags).
802
 *
803
 * @param curve NURBS curve to measure (must be non-NULL).
804
 * @return Number of bytes required to serialize the curve.
805
 */
806
static size_t gserialized2_from_lwnurbscurve_size(const LWNURBSCURVE *curve)
807
0
{
808
0
  size_t size = 4; /* Type number. */
809
0
  uint32_t npoints;
810
811
0
  assert(curve);
812
0
  npoints = curve->points ? curve->points->npoints : 0;
813
814
  /* Validate nweights and nknots consistency with their pointers */
815
0
  if (curve->nweights > 0 && curve->weights == NULL)
816
0
  {
817
0
    lwerror("NURBS curve has nweights > 0 but weights is NULL");
818
0
    return 0;
819
0
  }
820
0
  if (curve->nknots > 0 && curve->knots == NULL)
821
0
  {
822
0
    lwerror("NURBS curve has nknots > 0 but knots is NULL");
823
0
    return 0;
824
0
  }
825
0
  if (curve->weights != NULL && curve->nweights <= 0)
826
0
  {
827
0
    lwerror("NURBS curve has non-NULL weights but nweights <= 0");
828
0
    return 0;
829
0
  }
830
0
  if (curve->knots != NULL && curve->nknots <= 0)
831
0
  {
832
0
    lwerror("NURBS curve has non-NULL knots but nknots <= 0");
833
0
    return 0;
834
0
  }
835
0
  if (curve->nweights > 0 && curve->nweights != npoints)
836
0
  {
837
0
    lwerror("NURBS curve weights count (%d) does not match control points count (%d)",
838
0
            curve->nweights, npoints);
839
0
    return 0;
840
0
  }
841
0
  if (curve->nknots > 0 && curve->nknots != npoints + curve->degree + 1)
842
0
  {
843
0
    lwerror("NURBS curve knots count (%d) does not match expected count (%d)",
844
0
            curve->nknots, npoints + curve->degree + 1);
845
0
    return 0;
846
0
  }
847
848
0
  size += 4; /* degree */
849
0
  size += 4; /* nweights */
850
0
  size += 4; /* nknots */
851
0
  size += 4; /* Number of control points (zero => empty). */
852
0
  size += 4; /* padding to keep next doubles 8-byte aligned */
853
854
0
  if (curve->weights && curve->nweights > 0)
855
0
    size += sizeof(double) * curve->nweights;
856
0
  if (curve->knots && curve->nknots > 0)
857
0
    size += sizeof(double) * curve->nknots;
858
0
  if (curve->points)
859
0
    size += sizeof(double) * curve->points->npoints * FLAGS_NDIMS(curve->flags);
860
861
0
  LWDEBUGF(3, "nurbscurve size = %zu", size);
862
0
  return size;
863
0
}
864
865
/**
866
 * Compute the GSERIALIZED v2 payload size for a given LWGEOM.
867
 *
868
 * Dispatches to the appropriate per-geometry helper to determine how many bytes
869
 * the geometry's serialized data will occupy (the geometry payload written
870
 * after the GSERIALIZED header). Does not include the outer GSERIALIZED header
871
 * or any additional container overhead.
872
 *
873
 * @returns The number of bytes required to serialize the geometry payload, or
874
 *          0 if the geometry type is unknown or an error occurs.
875
 */
876
static size_t gserialized2_from_any_size(const LWGEOM *geom)
877
0
{
878
0
  LWDEBUGF(2, "Input type: %s", lwtype_name(geom->type));
879
880
0
  switch (geom->type)
881
0
  {
882
0
  case POINTTYPE:
883
0
    return gserialized2_from_lwpoint_size((LWPOINT *)geom);
884
0
  case LINETYPE:
885
0
    return gserialized2_from_lwline_size((LWLINE *)geom);
886
0
  case POLYGONTYPE:
887
0
    return gserialized2_from_lwpoly_size((LWPOLY *)geom);
888
0
  case TRIANGLETYPE:
889
0
    return gserialized2_from_lwtriangle_size((LWTRIANGLE *)geom);
890
0
  case CIRCSTRINGTYPE:
891
0
    return gserialized2_from_lwcircstring_size((LWCIRCSTRING *)geom);
892
0
  case CURVEPOLYTYPE:
893
0
  case COMPOUNDTYPE:
894
0
  case MULTIPOINTTYPE:
895
0
  case MULTILINETYPE:
896
0
  case MULTICURVETYPE:
897
0
  case MULTIPOLYGONTYPE:
898
0
  case MULTISURFACETYPE:
899
0
  case POLYHEDRALSURFACETYPE:
900
0
  case TINTYPE:
901
0
  case COLLECTIONTYPE:
902
0
    return gserialized2_from_lwcollection_size((LWCOLLECTION *)geom);
903
0
  case NURBSCURVETYPE:
904
0
    return gserialized2_from_lwnurbscurve_size((LWNURBSCURVE *)geom);
905
0
  default:
906
0
    lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type));
907
0
    return 0;
908
0
  }
909
0
}
910
911
/* Public function */
912
913
size_t gserialized2_from_lwgeom_size(const LWGEOM *geom)
914
0
{
915
0
  size_t size = 8; /* Header overhead (varsize+flags+srid) */
916
0
  assert(geom);
917
918
  /* Reserve space for extended flags */
919
0
  if (lwflags_uses_extended_flags(geom->flags))
920
0
    size += 8;
921
922
  /* Reserve space for bounding box */
923
0
  if (geom->bbox)
924
0
    size += gbox_serialized_size(geom->flags);
925
926
0
  size += gserialized2_from_any_size(geom);
927
0
  LWDEBUGF(3, "%s size = %zu", __func__, size);
928
929
0
  return size;
930
0
}
931
932
/***********************************************************************
933
* Serialize an LWGEOM into GSERIALIZED.
934
*/
935
936
/* Private functions */
937
938
static size_t gserialized2_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf);
939
940
static size_t gserialized2_from_lwpoint(const LWPOINT *point, uint8_t *buf)
941
0
{
942
0
  uint8_t *loc;
943
0
  int ptsize = ptarray_point_size(point->point);
944
0
  int type = POINTTYPE;
945
946
0
  assert(point);
947
0
  assert(buf);
948
949
0
  if (FLAGS_GET_ZM(point->flags) != FLAGS_GET_ZM(point->point->flags))
950
0
    lwerror("Dimensions mismatch in lwpoint");
951
952
0
  LWDEBUGF(2, "%s (%p, %p) called", __func__, point, buf);
953
954
0
  loc = buf;
955
956
  /* Write in the type. */
957
0
  memcpy(loc, &type, sizeof(uint32_t));
958
0
  loc += sizeof(uint32_t);
959
  /* Write in the number of points (0 => empty). */
960
0
  memcpy(loc, &(point->point->npoints), sizeof(uint32_t));
961
0
  loc += sizeof(uint32_t);
962
963
  /* Copy in the ordinates. */
964
0
  if (point->point->npoints > 0)
965
0
  {
966
0
    memcpy(loc, getPoint_internal(point->point, 0), ptsize);
967
0
    loc += ptsize;
968
0
  }
969
970
0
  return (size_t)(loc - buf);
971
0
}
972
973
static size_t gserialized2_from_lwline(const LWLINE *line, uint8_t *buf)
974
0
{
975
0
  uint8_t *loc;
976
0
  int ptsize;
977
0
  size_t size;
978
0
  int type = LINETYPE;
979
980
0
  assert(line);
981
0
  assert(buf);
982
983
0
  LWDEBUGF(2, "%s (%p, %p) called", __func__, line, buf);
984
985
0
  if (FLAGS_GET_Z(line->flags) != FLAGS_GET_Z(line->points->flags))
986
0
    lwerror("Dimensions mismatch in lwline");
987
988
0
  ptsize = ptarray_point_size(line->points);
989
990
0
  loc = buf;
991
992
  /* Write in the type. */
993
0
  memcpy(loc, &type, sizeof(uint32_t));
994
0
  loc += sizeof(uint32_t);
995
996
  /* Write in the npoints. */
997
0
  memcpy(loc, &(line->points->npoints), sizeof(uint32_t));
998
0
  loc += sizeof(uint32_t);
999
1000
0
  LWDEBUGF(3, "%s added npoints (%d)", __func__, line->points->npoints);
1001
1002
  /* Copy in the ordinates. */
1003
0
  if (line->points->npoints > 0)
1004
0
  {
1005
0
    size = (size_t)line->points->npoints * ptsize;
1006
0
    memcpy(loc, getPoint_internal(line->points, 0), size);
1007
0
    loc += size;
1008
0
  }
1009
0
  LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * line->points->npoints);
1010
1011
0
  return (size_t)(loc - buf);
1012
0
}
1013
1014
static size_t gserialized2_from_lwpoly(const LWPOLY *poly, uint8_t *buf)
1015
0
{
1016
0
  uint32_t i;
1017
0
  uint8_t *loc;
1018
0
  int ptsize;
1019
0
  int type = POLYGONTYPE;
1020
1021
0
  assert(poly);
1022
0
  assert(buf);
1023
1024
0
  LWDEBUGF(2, "%s called", __func__);
1025
1026
0
  ptsize = sizeof(double) * FLAGS_NDIMS(poly->flags);
1027
0
  loc = buf;
1028
1029
  /* Write in the type. */
1030
0
  memcpy(loc, &type, sizeof(uint32_t));
1031
0
  loc += sizeof(uint32_t);
1032
1033
  /* Write in the nrings. */
1034
0
  memcpy(loc, &(poly->nrings), sizeof(uint32_t));
1035
0
  loc += sizeof(uint32_t);
1036
1037
  /* Write in the npoints per ring. */
1038
0
  for (i = 0; i < poly->nrings; i++)
1039
0
  {
1040
0
    memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t));
1041
0
    loc += sizeof(uint32_t);
1042
0
  }
1043
1044
  /* Add in padding if necessary to remain double aligned. */
1045
0
  if (poly->nrings % 2)
1046
0
  {
1047
0
    memset(loc, 0, sizeof(uint32_t));
1048
0
    loc += sizeof(uint32_t);
1049
0
  }
1050
1051
  /* Copy in the ordinates. */
1052
0
  for (i = 0; i < poly->nrings; i++)
1053
0
  {
1054
0
    POINTARRAY *pa = poly->rings[i];
1055
0
    size_t pasize;
1056
1057
0
    if (FLAGS_GET_ZM(poly->flags) != FLAGS_GET_ZM(pa->flags))
1058
0
      lwerror("Dimensions mismatch in lwpoly");
1059
1060
0
    pasize = (size_t)pa->npoints * ptsize;
1061
0
    if ( pa->npoints > 0 )
1062
0
      memcpy(loc, getPoint_internal(pa, 0), pasize);
1063
0
    loc += pasize;
1064
0
  }
1065
0
  return (size_t)(loc - buf);
1066
0
}
1067
1068
static size_t gserialized2_from_lwtriangle(const LWTRIANGLE *triangle, uint8_t *buf)
1069
0
{
1070
0
  uint8_t *loc;
1071
0
  int ptsize;
1072
0
  size_t size;
1073
0
  int type = TRIANGLETYPE;
1074
1075
0
  assert(triangle);
1076
0
  assert(buf);
1077
1078
0
  LWDEBUGF(2, "%s (%p, %p) called", __func__, triangle, buf);
1079
1080
0
  if (FLAGS_GET_ZM(triangle->flags) != FLAGS_GET_ZM(triangle->points->flags))
1081
0
    lwerror("Dimensions mismatch in lwtriangle");
1082
1083
0
  ptsize = ptarray_point_size(triangle->points);
1084
1085
0
  loc = buf;
1086
1087
  /* Write in the type. */
1088
0
  memcpy(loc, &type, sizeof(uint32_t));
1089
0
  loc += sizeof(uint32_t);
1090
1091
  /* Write in the npoints. */
1092
0
  memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t));
1093
0
  loc += sizeof(uint32_t);
1094
1095
0
  LWDEBUGF(3, "%s added npoints (%d)", __func__, triangle->points->npoints);
1096
1097
  /* Copy in the ordinates. */
1098
0
  if (triangle->points->npoints > 0)
1099
0
  {
1100
0
    size = (size_t)triangle->points->npoints * ptsize;
1101
0
    memcpy(loc, getPoint_internal(triangle->points, 0), size);
1102
0
    loc += size;
1103
0
  }
1104
0
  LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * triangle->points->npoints);
1105
1106
0
  return (size_t)(loc - buf);
1107
0
}
1108
1109
static size_t gserialized2_from_lwcircstring(const LWCIRCSTRING *curve, uint8_t *buf)
1110
0
{
1111
0
  uint8_t *loc;
1112
0
  int ptsize;
1113
0
  size_t size;
1114
0
  int type = CIRCSTRINGTYPE;
1115
1116
0
  assert(curve);
1117
0
  assert(buf);
1118
1119
0
  if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags))
1120
0
    lwerror("Dimensions mismatch in lwcircstring");
1121
1122
1123
0
  ptsize = ptarray_point_size(curve->points);
1124
0
  loc = buf;
1125
1126
  /* Write in the type. */
1127
0
  memcpy(loc, &type, sizeof(uint32_t));
1128
0
  loc += sizeof(uint32_t);
1129
1130
  /* Write in the npoints. */
1131
0
  memcpy(loc, &curve->points->npoints, sizeof(uint32_t));
1132
0
  loc += sizeof(uint32_t);
1133
1134
  /* Copy in the ordinates. */
1135
0
  if (curve->points->npoints > 0)
1136
0
  {
1137
0
    size = (size_t)curve->points->npoints * ptsize;
1138
0
    memcpy(loc, getPoint_internal(curve->points, 0), size);
1139
0
    loc += size;
1140
0
  }
1141
1142
0
  return (size_t)(loc - buf);
1143
0
}
1144
1145
/**
1146
 * Serialize an LWCOLLECTION into GSERIALIZED v2 format.
1147
 *
1148
 * Writes the collection header (type and number of sub-geometries) followed
1149
 * by the serialized form of each contained geometry into the provided buffer.
1150
 * The caller must ensure buf has at least the size returned by
1151
 * gserialized2_from_lwcollection_size(coll).
1152
 *
1153
 * @param coll Collection to serialize.
1154
 * @param buf  Destination buffer to write serialized bytes into.
1155
 * @return Number of bytes written into buf.
1156
 *
1157
 * Note: If a sub-geometry's dimensionality (Z/M) differs from the collection's
1158
 * flags, this function signals an error via lwerror but continues serialization.
1159
 */
1160
static size_t gserialized2_from_lwcollection(const LWCOLLECTION *coll, uint8_t *buf)
1161
0
{
1162
0
  size_t subsize = 0;
1163
0
  uint8_t *loc;
1164
0
  uint32_t i;
1165
0
  int type;
1166
1167
0
  assert(coll);
1168
0
  assert(buf);
1169
1170
0
  type = coll->type;
1171
0
  loc = buf;
1172
1173
  /* Write in the type. */
1174
0
  memcpy(loc, &type, sizeof(uint32_t));
1175
0
  loc += sizeof(uint32_t);
1176
1177
  /* Write in the number of subgeoms. */
1178
0
  memcpy(loc, &coll->ngeoms, sizeof(uint32_t));
1179
0
  loc += sizeof(uint32_t);
1180
1181
  /* Serialize subgeoms. */
1182
0
  for (i = 0; i < coll->ngeoms; i++)
1183
0
  {
1184
0
    if (FLAGS_GET_ZM(coll->flags) != FLAGS_GET_ZM(coll->geoms[i]->flags))
1185
0
      lwerror("Dimensions mismatch in lwcollection");
1186
0
    subsize = gserialized2_from_lwgeom_any(coll->geoms[i], loc);
1187
0
    loc += subsize;
1188
0
  }
1189
1190
0
  return (size_t)(loc - buf);
1191
0
}
1192
1193
/**
1194
 * Serialize a NURBS curve into a GSERIALIZED v2 geometry payload.
1195
 *
1196
 * Writes a NURBSCURVETYPE payload starting at buf and returns the number of
1197
 * bytes written. The serialized layout places the number of control points
1198
 * at bytes 4–7 (the "critical count") so that emptiness detection routines
1199
 * (e.g. gserialized2_is_empty_recurse) can determine emptiness by reading
1200
 * that position. The function writes, in order: type, npoints, degree,
1201
 * nweights, nknots, optional weights (double[]), optional knots (double[]),
1202
 * and the control point coordinates (native point-array layout).
1203
 *
1204
 * @param curve NURBS curve to serialize; must be non-NULL and its point array
1205
 *              flags must be dimensionally consistent with curve->flags.
1206
 * @param buf   Destination buffer; must be non-NULL and large enough to hold
1207
 *              the serialized payload as computed by the corresponding
1208
 *              size function.
1209
 * @return The number of bytes written into buf.
1210
 */
1211
static size_t gserialized2_from_lwnurbscurve(const LWNURBSCURVE *curve, uint8_t *buf)
1212
0
{
1213
0
    uint8_t *loc;
1214
0
    int ptsize;
1215
0
    size_t size;
1216
0
    int type = NURBSCURVETYPE;
1217
1218
0
    assert(curve);
1219
0
    assert(buf);
1220
1221
    /* Validate dimensional consistency between curve flags and point array flags */
1222
0
    if (curve->points && FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags))
1223
0
        lwerror("Dimensions mismatch in lwnurbscurve");
1224
1225
    /* Validate NURBS invariants before writing */
1226
0
    uint32_t npoints = curve->points ? curve->points->npoints : 0;
1227
1228
0
    if (curve->nweights > 0)
1229
0
    {
1230
0
        if (curve->weights == NULL)
1231
0
        {
1232
0
            lwerror("NURBS curve has nweights > 0 but weights is NULL");
1233
0
            return 0;
1234
0
        }
1235
0
        if (curve->nweights != npoints)
1236
0
        {
1237
0
            lwerror("NURBS curve weights count (%d) does not match control points count (%d)", curve->nweights, npoints);
1238
0
            return 0;
1239
0
        }
1240
0
    }
1241
1242
0
    if (curve->nknots > 0)
1243
0
    {
1244
0
        if (curve->knots == NULL)
1245
0
        {
1246
0
            lwerror("NURBS curve has nknots > 0 but knots is NULL");
1247
0
            return 0;
1248
0
        }
1249
0
        if (curve->nknots != (npoints + curve->degree + 1))
1250
0
        {
1251
0
            lwerror("NURBS curve knots count (%d) does not match expected count (%d = npoints + degree + 1)", curve->nknots, npoints + curve->degree + 1);
1252
0
            return 0;
1253
0
        }
1254
0
    }
1255
1256
0
    if (npoints > 0)
1257
0
    {
1258
0
        if (curve->points == NULL)
1259
0
        {
1260
0
            lwerror("NURBS curve has npoints > 0 but points is NULL");
1261
0
            return 0;
1262
0
        }
1263
0
        ptsize = ptarray_point_size(curve->points);
1264
0
        if (ptsize <= 0)
1265
0
        {
1266
0
            lwerror("NURBS curve has invalid point size");
1267
0
            return 0;
1268
0
        }
1269
0
        if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags))
1270
0
        {
1271
0
            lwerror("NURBS curve flags mismatch between curve and points");
1272
0
            return 0;
1273
0
        }
1274
0
    }
1275
0
    else
1276
0
    {
1277
0
        ptsize = 0;
1278
0
    }
1279
1280
0
    loc = buf;
1281
1282
    /*
1283
     * BYTES 0-3: Write geometry type identifier
1284
     * This tells PostGIS what kind of geometry we're dealing with
1285
     */
1286
0
    memcpy(loc, &type, sizeof(uint32_t));
1287
0
    loc += sizeof(uint32_t);
1288
1289
    /*
1290
     * BYTES 4-7: Write number of control points - THE CRITICAL COUNT
1291
     *
1292
     * This is the most important placement in the entire serialization!
1293
     * The gserialized2_is_empty_recurse function reads exactly this position
1294
     * to determine if the geometry is empty. For NURBS curves, the curve
1295
     * is empty if and only if it has zero control points.
1296
     *
1297
     * This follows the same pattern as other PostGIS geometries:
1298
     * - LINESTRING: number of points at position 4-7
1299
     * - POLYGON: number of rings at position 4-7
1300
     * - POINT: coordinate presence indicator at position 4-7
1301
     */
1302
0
    memcpy(loc, &npoints, sizeof(uint32_t));
1303
0
    loc += sizeof(uint32_t);
1304
1305
    /*
1306
     * BYTES 8-11: Write curve degree
1307
     *
1308
     * The degree defines the polynomial order of the NURBS curve.
1309
     * While mathematically important, it's not used for emptiness detection,
1310
     * so it can be placed after the critical count.
1311
     */
1312
0
    memcpy(loc, &(curve->degree), sizeof(uint32_t));
1313
0
    loc += sizeof(uint32_t);
1314
1315
    /*
1316
     * BYTES 12-15: Write number of weights
1317
     *
1318
     * Weights are used for rational NURBS curves. If nweights == 0,
1319
     * the curve is non-rational (all weights implicitly equal to 1.0).
1320
     * This count tells the deserializer how many weight values to expect.
1321
     */
1322
0
    memcpy(loc, &(curve->nweights), sizeof(uint32_t));
1323
0
    loc += sizeof(uint32_t);
1324
1325
    /*
1326
     * BYTES 16-19: Write number of knots
1327
     *
1328
     * Knots define the parameter space of the NURBS curve. If nknots == 0,
1329
     * a uniform knot vector is assumed. This count tells the deserializer
1330
     * how many knot values to expect.
1331
     */
1332
0
    memcpy(loc, &(curve->nknots), sizeof(uint32_t));
1333
0
    loc += sizeof(uint32_t);
1334
1335
    /* Pad 4 bytes so subsequent double arrays are 8-byte aligned */
1336
0
    {
1337
0
        uint32_t pad = 0;
1338
0
        memcpy(loc, &pad, sizeof(uint32_t));
1339
0
        loc += sizeof(uint32_t);
1340
0
    }
1341
1342
    /*
1343
     * VARIABLE SECTION 1: Write weight values (if any)
1344
     *
1345
     * Each weight is a double-precision floating point number.
1346
     * Weights must correspond 1:1 with control points for rational curves.
1347
     */
1348
0
    if (curve->weights && curve->nweights > 0) {
1349
0
        memcpy(loc, curve->weights, sizeof(double) * curve->nweights);
1350
0
        loc += sizeof(double) * curve->nweights;
1351
0
    }
1352
1353
    /*
1354
     * VARIABLE SECTION 2: Write knot values (if any)
1355
     *
1356
     * Each knot is a double-precision floating point number.
1357
     * The knot vector must satisfy: nknots = npoints + degree + 1
1358
     * for a proper NURBS curve definition.
1359
     */
1360
0
    if (curve->knots && curve->nknots > 0) {
1361
0
        memcpy(loc, curve->knots, sizeof(double) * curve->nknots);
1362
0
        loc += sizeof(double) * curve->nknots;
1363
0
    }
1364
1365
    /*
1366
     * VARIABLE SECTION 3: Write control point coordinates
1367
     *
1368
     * This uses PostGIS's standard point array serialization.
1369
     * The coordinates are written in the native format (XY, XYZ, XYM, or XYZM)
1370
     * as determined by the curve's dimensional flags.
1371
     *
1372
     * If npoints is 0 (empty curve), no coordinate data is written.
1373
     */
1374
0
    if (curve->points && curve->points->npoints > 0) {
1375
0
        size = (size_t)curve->points->npoints * ptsize;
1376
0
        memcpy(loc, getPoint_internal(curve->points, 0), size);
1377
0
        loc += size;
1378
0
    }
1379
1380
    /* Return total bytes written to buffer */
1381
0
    return (size_t)(loc - buf);
1382
0
}
1383
1384
/**
1385
 * Serialize an LWGEOM into GSERIALIZED2 geometry payload bytes.
1386
 *
1387
 * Dispatches to the appropriate per-geometry serialization routine based on
1388
 * geom->type and writes the geometry payload into the caller-provided buffer.
1389
 * The function asserts that both `geom` and `buf` are non-NULL.
1390
 *
1391
 * @param geom Geometry to serialize (must be a valid LWGEOM pointer).
1392
 * @param buf Destination buffer to receive the serialized geometry payload.
1393
 *            Caller must ensure the buffer is large enough for the serialized data.
1394
 * @return Number of bytes written into `buf`, or 0 if the geometry type is unknown
1395
 *         or serialization failed.
1396
 */
1397
static size_t gserialized2_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf)
1398
0
{
1399
0
  assert(geom);
1400
0
  assert(buf);
1401
1402
0
  LWDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d",
1403
0
    geom->type, lwtype_name(geom->type),
1404
0
    FLAGS_GET_Z(geom->flags), FLAGS_GET_M(geom->flags));
1405
0
  LWDEBUGF(2, "LWGEOM(%p) uint8_t(%p)", geom, buf);
1406
1407
0
  switch (geom->type)
1408
0
  {
1409
0
  case POINTTYPE:
1410
0
    return gserialized2_from_lwpoint((LWPOINT *)geom, buf);
1411
0
  case LINETYPE:
1412
0
    return gserialized2_from_lwline((LWLINE *)geom, buf);
1413
0
  case POLYGONTYPE:
1414
0
    return gserialized2_from_lwpoly((LWPOLY *)geom, buf);
1415
0
  case TRIANGLETYPE:
1416
0
    return gserialized2_from_lwtriangle((LWTRIANGLE *)geom, buf);
1417
0
  case CIRCSTRINGTYPE:
1418
0
    return gserialized2_from_lwcircstring((LWCIRCSTRING *)geom, buf);
1419
0
  case CURVEPOLYTYPE:
1420
0
  case COMPOUNDTYPE:
1421
0
  case MULTIPOINTTYPE:
1422
0
  case MULTILINETYPE:
1423
0
  case MULTICURVETYPE:
1424
0
  case MULTIPOLYGONTYPE:
1425
0
  case MULTISURFACETYPE:
1426
0
  case POLYHEDRALSURFACETYPE:
1427
0
  case TINTYPE:
1428
0
  case COLLECTIONTYPE:
1429
0
    return gserialized2_from_lwcollection((LWCOLLECTION *)geom, buf);
1430
0
  case NURBSCURVETYPE:
1431
0
    return gserialized2_from_lwnurbscurve((LWNURBSCURVE *)geom, buf);
1432
0
  default:
1433
0
    lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type));
1434
0
    return 0;
1435
0
  }
1436
0
  return 0;
1437
0
}
1438
1439
static size_t gserialized2_from_extended_flags(lwflags_t lwflags, uint8_t *buf)
1440
0
{
1441
0
  if (lwflags_uses_extended_flags(lwflags))
1442
0
  {
1443
0
    uint64_t xflags = 0;
1444
0
    if (FLAGS_GET_SOLID(lwflags))
1445
0
      xflags |= G2FLAG_X_SOLID;
1446
1447
    // G2FLAG_X_CHECKED_VALID
1448
    // G2FLAG_X_IS_VALID
1449
    // G2FLAG_X_HAS_HASH
1450
1451
0
    memcpy(buf, &xflags, sizeof(uint64_t));
1452
0
    return sizeof(uint64_t);
1453
0
  }
1454
0
  return 0;
1455
0
}
1456
1457
static size_t gserialized2_from_gbox(const GBOX *gbox, uint8_t *buf)
1458
0
{
1459
0
  uint8_t *loc = buf;
1460
0
  float *f;
1461
0
  uint8_t i = 0;
1462
0
  size_t return_size;
1463
1464
0
  assert(buf);
1465
1466
0
  f = (float *)buf;
1467
0
  f[i++] = next_float_down(gbox->xmin);
1468
0
  f[i++] = next_float_up(gbox->xmax);
1469
0
  f[i++] = next_float_down(gbox->ymin);
1470
0
  f[i++] = next_float_up(gbox->ymax);
1471
0
  loc += 4 * sizeof(float);
1472
1473
0
  if (FLAGS_GET_GEODETIC(gbox->flags))
1474
0
  {
1475
0
    f[i++] = next_float_down(gbox->zmin);
1476
0
    f[i++] = next_float_up(gbox->zmax);
1477
0
    loc += 2 * sizeof(float);
1478
1479
0
    return_size = (size_t)(loc - buf);
1480
0
    LWDEBUGF(4, "returning size %zu", return_size);
1481
0
    return return_size;
1482
0
  }
1483
1484
0
  if (FLAGS_GET_Z(gbox->flags))
1485
0
  {
1486
0
    f[i++] = next_float_down(gbox->zmin);
1487
0
    f[i++] = next_float_up(gbox->zmax);
1488
0
    loc += 2 * sizeof(float);
1489
0
  }
1490
1491
0
  if (FLAGS_GET_M(gbox->flags))
1492
0
  {
1493
0
    f[i++] = next_float_down(gbox->mmin);
1494
0
    f[i++] = next_float_up(gbox->mmax);
1495
0
    loc += 2 * sizeof(float);
1496
0
  }
1497
0
  return_size = (size_t)(loc - buf);
1498
0
  LWDEBUGF(4, "returning size %zu", return_size);
1499
0
  return return_size;
1500
0
}
1501
1502
/* Public function */
1503
1504
GSERIALIZED* gserialized2_from_lwgeom(LWGEOM *geom, size_t *size)
1505
0
{
1506
0
  size_t expected_size = 0;
1507
0
  size_t return_size = 0;
1508
0
  uint8_t *ptr = NULL;
1509
0
  GSERIALIZED *g = NULL;
1510
0
  assert(geom);
1511
1512
  /*
1513
  ** See if we need a bounding box, add one if we don't have one.
1514
  */
1515
0
  if ((!geom->bbox) && lwgeom_needs_bbox(geom) && (!lwgeom_is_empty(geom)))
1516
0
  {
1517
0
    lwgeom_add_bbox(geom);
1518
0
  }
1519
1520
  /*
1521
  ** Harmonize the flags to the state of the lwgeom
1522
  */
1523
0
  FLAGS_SET_BBOX(geom->flags, (geom->bbox ? 1 : 0));
1524
1525
  /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */
1526
0
  expected_size = gserialized2_from_lwgeom_size(geom);
1527
0
  ptr = lwalloc(expected_size);
1528
0
  g = (GSERIALIZED*)(ptr);
1529
1530
  /* Set the SRID! */
1531
0
  gserialized2_set_srid(g, geom->srid);
1532
  /*
1533
  ** We are aping PgSQL code here, PostGIS code should use
1534
  ** VARSIZE to set this for real.
1535
  */
1536
0
  LWSIZE_SET(g->size, expected_size);
1537
0
  g->gflags = lwflags_get_g2flags(geom->flags);
1538
1539
  /* Move write head past size, srid and flags. */
1540
0
  ptr += 8;
1541
1542
  /* Write in the extended flags if necessary */
1543
0
  ptr += gserialized2_from_extended_flags(geom->flags, ptr);
1544
1545
  /* Write in the serialized form of the gbox, if necessary. */
1546
0
  if (geom->bbox)
1547
0
    ptr += gserialized2_from_gbox(geom->bbox, ptr);
1548
1549
  /* Write in the serialized form of the geometry. */
1550
0
  ptr += gserialized2_from_lwgeom_any(geom, ptr);
1551
1552
  /* Calculate size as returned by data processing functions. */
1553
0
  return_size = ptr - (uint8_t*)g;
1554
1555
0
  assert(expected_size == return_size);
1556
0
  if (size) /* Return the output size to the caller if necessary. */
1557
0
    *size = return_size;
1558
1559
0
  return g;
1560
0
}
1561
1562
/***********************************************************************
1563
* De-serialize GSERIALIZED into an LWGEOM.
1564
*/
1565
1566
static LWGEOM *lwgeom_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid);
1567
1568
static LWPOINT *
1569
lwpoint_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1570
0
{
1571
0
  uint8_t *start_ptr = data_ptr;
1572
0
  LWPOINT *point;
1573
0
  uint32_t npoints = 0;
1574
1575
0
  assert(data_ptr);
1576
1577
0
  point = (LWPOINT*)lwalloc(sizeof(LWPOINT));
1578
0
  point->srid = srid;
1579
0
  point->bbox = NULL;
1580
0
  point->type = POINTTYPE;
1581
0
  point->flags = lwflags;
1582
1583
0
  data_ptr += 4; /* Skip past the type. */
1584
0
  npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */
1585
0
  data_ptr += 4; /* Skip past the npoints. */
1586
1587
0
  if (npoints > 0)
1588
0
    point->point = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 1, data_ptr);
1589
0
  else
1590
0
    point->point = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty point */
1591
1592
0
  data_ptr += sizeof(double) * npoints * FLAGS_NDIMS(lwflags);
1593
1594
0
  if (size)
1595
0
    *size = data_ptr - start_ptr;
1596
1597
0
  return point;
1598
0
}
1599
1600
static LWLINE *
1601
lwline_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1602
0
{
1603
0
  uint8_t *start_ptr = data_ptr;
1604
0
  LWLINE *line;
1605
0
  uint32_t npoints = 0;
1606
1607
0
  assert(data_ptr);
1608
1609
0
  line = (LWLINE*)lwalloc(sizeof(LWLINE));
1610
0
  line->srid = srid;
1611
0
  line->bbox = NULL;
1612
0
  line->type = LINETYPE;
1613
0
  line->flags = lwflags;
1614
1615
0
  data_ptr += 4; /* Skip past the type. */
1616
0
  npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */
1617
0
  data_ptr += 4; /* Skip past the npoints. */
1618
1619
0
  if (npoints > 0)
1620
0
    line->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1621
1622
0
  else
1623
0
    line->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty linestring */
1624
1625
0
  data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1626
1627
0
  if (size)
1628
0
    *size = data_ptr - start_ptr;
1629
1630
0
  return line;
1631
0
}
1632
1633
static LWPOLY *
1634
lwpoly_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1635
0
{
1636
0
  uint8_t *start_ptr = data_ptr;
1637
0
  LWPOLY *poly;
1638
0
  uint8_t *ordinate_ptr;
1639
0
  uint32_t nrings = 0;
1640
0
  uint32_t i = 0;
1641
1642
0
  assert(data_ptr);
1643
1644
0
  poly = (LWPOLY*)lwalloc(sizeof(LWPOLY));
1645
0
  poly->srid = srid;
1646
0
  poly->bbox = NULL;
1647
0
  poly->type = POLYGONTYPE;
1648
0
  poly->flags = lwflags;
1649
1650
0
  data_ptr += 4; /* Skip past the polygontype. */
1651
0
  nrings = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */
1652
0
  poly->nrings = nrings;
1653
0
  LWDEBUGF(4, "nrings = %d", nrings);
1654
0
  data_ptr += 4; /* Skip past the nrings. */
1655
1656
0
  ordinate_ptr = data_ptr; /* Start the ordinate pointer. */
1657
0
  if (nrings > 0)
1658
0
  {
1659
0
    poly->rings = (POINTARRAY**)lwalloc( sizeof(POINTARRAY*) * nrings );
1660
0
    poly->maxrings = nrings;
1661
0
    ordinate_ptr += nrings * 4; /* Move past all the npoints values. */
1662
0
    if (nrings % 2) /* If there is padding, move past that too. */
1663
0
      ordinate_ptr += 4;
1664
0
  }
1665
0
  else /* Empty polygon */
1666
0
  {
1667
0
    poly->rings = NULL;
1668
0
    poly->maxrings = 0;
1669
0
  }
1670
1671
0
  for (i = 0; i < nrings; i++)
1672
0
  {
1673
0
    uint32_t npoints = 0;
1674
1675
    /* Read in the number of points. */
1676
0
    npoints = gserialized2_get_uint32_t(data_ptr);
1677
0
    data_ptr += 4;
1678
1679
    /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */
1680
0
    poly->rings[i] = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, ordinate_ptr);
1681
1682
0
    ordinate_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1683
0
  }
1684
1685
0
  if (size)
1686
0
    *size = ordinate_ptr - start_ptr;
1687
1688
0
  return poly;
1689
0
}
1690
1691
static LWTRIANGLE *
1692
lwtriangle_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1693
0
{
1694
0
  uint8_t *start_ptr = data_ptr;
1695
0
  LWTRIANGLE *triangle;
1696
0
  uint32_t npoints = 0;
1697
1698
0
  assert(data_ptr);
1699
1700
0
  triangle = (LWTRIANGLE*)lwalloc(sizeof(LWTRIANGLE));
1701
0
  triangle->srid = srid; /* Default */
1702
0
  triangle->bbox = NULL;
1703
0
  triangle->type = TRIANGLETYPE;
1704
0
  triangle->flags = lwflags;
1705
1706
0
  data_ptr += 4; /* Skip past the type. */
1707
0
  npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */
1708
0
  data_ptr += 4; /* Skip past the npoints. */
1709
1710
0
  if (npoints > 0)
1711
0
    triangle->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1712
0
  else
1713
0
    triangle->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty triangle */
1714
1715
0
  data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1716
1717
0
  if (size)
1718
0
    *size = data_ptr - start_ptr;
1719
1720
0
  return triangle;
1721
0
}
1722
1723
static LWCIRCSTRING *
1724
lwcircstring_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1725
0
{
1726
0
  uint8_t *start_ptr = data_ptr;
1727
0
  LWCIRCSTRING *circstring;
1728
0
  uint32_t npoints = 0;
1729
1730
0
  assert(data_ptr);
1731
1732
0
  circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING));
1733
0
  circstring->srid = srid;
1734
0
  circstring->bbox = NULL;
1735
0
  circstring->type = CIRCSTRINGTYPE;
1736
0
  circstring->flags = lwflags;
1737
1738
0
  data_ptr += 4; /* Skip past the circstringtype. */
1739
0
  npoints = gserialized2_get_uint32_t(data_ptr); /* Zero => empty geometry */
1740
0
  data_ptr += 4; /* Skip past the npoints. */
1741
1742
0
  if (npoints > 0)
1743
0
    circstring->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1744
0
  else
1745
0
    circstring->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty circularstring */
1746
1747
0
  data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1748
1749
0
  if (size)
1750
0
    *size = data_ptr - start_ptr;
1751
1752
0
  return circstring;
1753
0
}
1754
1755
/**
1756
 * Deserialize a GSERIALIZED v2 collection payload into an LWCOLLECTION.
1757
 *
1758
 * Reads a collection type and its contained sub-geometries from the buffer at
1759
 * data_ptr, constructing and returning an allocated LWCOLLECTION whose
1760
 * sub-geometries are deserialized in-place from the buffer. Sub-geometries are
1761
 * deserialized without bounding boxes. The function validates that each
1762
 * contained geometry's subtype is allowed for the collection type; on invalid
1763
 * subtype an error is logged and NULL is returned.
1764
 *
1765
 * @param data_ptr Pointer to the start of the serialized collection payload
1766
 *                 (points to the 32-bit type field).
1767
 * @param lwflags  Flags to apply to the resulting LWGEOM/LWCOLLECTION (Z/M/GEODETIC/etc.).
1768
 * @param size     Optional out parameter; set to the number of bytes consumed
1769
 *                 from data_ptr during deserialization when non-NULL.
1770
 * @param srid     SRID to assign to the created LWCOLLECTION and its sub-geometries.
1771
 * @return Pointer to a newly allocated LWCOLLECTION on success (caller owns the memory),
1772
 *         or NULL on error (e.g., invalid subtype).
1773
 */
1774
static LWCOLLECTION *
1775
lwcollection_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1776
0
{
1777
0
  uint32_t type;
1778
0
  uint8_t *start_ptr = data_ptr;
1779
0
  LWCOLLECTION *collection;
1780
0
  uint32_t ngeoms = 0;
1781
0
  uint32_t i = 0;
1782
1783
0
  assert(data_ptr);
1784
1785
0
  type = gserialized2_get_uint32_t(data_ptr);
1786
0
  data_ptr += 4; /* Skip past the type. */
1787
1788
0
  collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION));
1789
0
  collection->srid = srid;
1790
0
  collection->bbox = NULL;
1791
0
  collection->type = type;
1792
0
  collection->flags = lwflags;
1793
1794
0
  ngeoms = gserialized2_get_uint32_t(data_ptr);
1795
0
  collection->ngeoms = ngeoms; /* Zero => empty geometry */
1796
0
  data_ptr += 4; /* Skip past the ngeoms. */
1797
1798
0
  if (ngeoms > 0)
1799
0
  {
1800
0
    collection->geoms = lwalloc(sizeof(LWGEOM*) * ngeoms);
1801
0
    collection->maxgeoms = ngeoms;
1802
0
  }
1803
0
  else
1804
0
  {
1805
0
    collection->geoms = NULL;
1806
0
    collection->maxgeoms = 0;
1807
0
  }
1808
1809
  /* Sub-geometries are never de-serialized with boxes (#1254) */
1810
0
  FLAGS_SET_BBOX(lwflags, 0);
1811
1812
0
  for (i = 0; i < ngeoms; i++)
1813
0
  {
1814
0
    uint32_t subtype = gserialized2_get_uint32_t(data_ptr);
1815
0
    size_t subsize = 0;
1816
1817
0
    if (!lwcollection_allows_subtype(type, subtype))
1818
0
    {
1819
0
      lwerror("Invalid subtype (%s) for collection type (%s)", lwtype_name(subtype), lwtype_name(type));
1820
0
      lwfree(collection);
1821
0
      return NULL;
1822
0
    }
1823
0
    collection->geoms[i] = lwgeom_from_gserialized2_buffer(data_ptr, lwflags, &subsize, srid);
1824
0
    data_ptr += subsize;
1825
0
  }
1826
1827
0
  if (size)
1828
0
    *size = data_ptr - start_ptr;
1829
1830
0
  return collection;
1831
0
}
1832
1833
/**
1834
 * Deserialize a NURBS curve from a GSERIALIZED v2 buffer.
1835
 *
1836
 * Reads a NURBS payload written by gserialized2_from_lwnurbscurve. Expects the
1837
 * byte layout:
1838
 *   [Type:4][NPoints:4][Degree:4][NWeights:4][NKnots:4][Weights:var][Knots:var][Points:var]
1839
 *
1840
 * The function allocates and returns a newly allocated LWNURBSCURVE. It may
1841
 * allocate additional arrays for weights and knots and constructs a POINTARRAY
1842
 * for control points (by reference to the serialized coordinate data when
1843
 * non-empty). The returned curve has SRID = SRID_UNKNOWN and bbox = NULL;
1844
 * SRID and bbox are handled at higher levels.
1845
 *
1846
 * Note: gserialized2_is_empty_recurse depends on the NPoints field being at
1847
 * bytes 4-7; this function reads that field first and treats npoints == 0 as
1848
 * an empty curve.
1849
 *
1850
 * @param data_ptr Pointer to the start of the serialized geometry payload (type at bytes 0-3).
1851
 * @param lwflags  Dimensional flags (Z/M/GEODETIC) describing point coordinate layout.
1852
 * @param size     If non-NULL, set to the number of bytes consumed from data_ptr.
1853
 * @return Pointer to a newly allocated LWNURBSCURVE on success; caller owns the memory.
1854
 */
1855
static LWNURBSCURVE *
1856
lwnurbscurve_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size, int32_t srid)
1857
0
{
1858
0
    uint8_t *start_ptr = data_ptr;
1859
0
    LWNURBSCURVE *curve;
1860
0
    uint32_t npoints, degree, nweights, nknots;
1861
0
    double *weights = NULL;
1862
0
    double *knots = NULL;
1863
1864
0
    assert(data_ptr);
1865
1866
    /* Allocate and initialize the NURBS curve structure */
1867
0
    curve = (LWNURBSCURVE*)lwalloc(sizeof(LWNURBSCURVE));
1868
0
    curve->srid = srid;  /* Use the SRID passed from caller */
1869
0
    curve->bbox = NULL;          /* Bounding box computed separately if needed */
1870
0
    curve->type = NURBSCURVETYPE;
1871
0
    curve->flags = lwflags;      /* Dimensional flags passed from caller */
1872
1873
    /*
1874
     * Skip past the geometry type (bytes 0-3)
1875
     * We already know this is a NURBS curve from the calling context
1876
     */
1877
0
    data_ptr += 4;
1878
1879
    /*
1880
     * BYTES 4-7: Read number of control points - THE CRITICAL COUNT
1881
     *
1882
     * This is the same value that gserialized2_is_empty_recurse examines
1883
     * for emptiness detection. If npoints == 0, the curve is empty.
1884
     * This must be read first among the NURBS-specific parameters.
1885
     */
1886
0
    npoints = gserialized2_get_uint32_t(data_ptr);
1887
0
    data_ptr += 4;
1888
1889
    /*
1890
     * BYTES 8-11: Read curve degree
1891
     *
1892
     * The degree must be >= 1 and typically <= 10 for practical curves.
1893
     * This parameter controls the polynomial order of the curve segments.
1894
     */
1895
0
    degree = gserialized2_get_uint32_t(data_ptr);
1896
0
    curve->degree = degree;
1897
0
    data_ptr += 4;
1898
1899
    /*
1900
     * BYTES 12-15: Read number of weights
1901
     *
1902
     * If nweights == 0, this is a non-rational NURBS (polynomial curve).
1903
     * If nweights > 0, it should equal npoints for a valid rational curve.
1904
     */
1905
0
    nweights = gserialized2_get_uint32_t(data_ptr);
1906
0
    curve->nweights = nweights;
1907
0
    data_ptr += 4;
1908
1909
    /*
1910
     * BYTES 16-19: Read number of knots
1911
     *
1912
     * If nknots == 0, a uniform knot vector is implied.
1913
     * If nknots > 0, it should equal (npoints + degree + 1) for a valid curve.
1914
     */
1915
0
    nknots = gserialized2_get_uint32_t(data_ptr);
1916
0
    curve->nknots = nknots;
1917
0
    data_ptr += 4;
1918
1919
    /* Skip 4-byte pad to align following doubles (weights/knots/coords) */
1920
0
    data_ptr += sizeof(uint32_t);
1921
1922
    /*
1923
     * VARIABLE SECTION 1: Read weight values (if any)
1924
     *
1925
     * Weights are double-precision values that make the curve "rational".
1926
     * Each weight corresponds to one control point. All weights must be > 0.
1927
     */
1928
0
    if (nweights > 0) {
1929
0
        weights = lwalloc(sizeof(double) * nweights);
1930
0
        memcpy(weights, data_ptr, sizeof(double) * nweights);
1931
0
        data_ptr += sizeof(double) * nweights;
1932
0
    }
1933
0
    curve->weights = weights;
1934
1935
    /*
1936
     * VARIABLE SECTION 2: Read knot values (if any)
1937
     *
1938
     * Knots define the parameter domain of the curve. They must be
1939
     * non-decreasing: knot[i] <= knot[i+1] for all valid indices.
1940
     */
1941
0
    if (nknots > 0) {
1942
0
        knots = lwalloc(sizeof(double) * nknots);
1943
0
        memcpy(knots, data_ptr, sizeof(double) * nknots);
1944
0
        data_ptr += sizeof(double) * nknots;
1945
0
    }
1946
0
    curve->knots = knots;
1947
1948
    /*
1949
     * VARIABLE SECTION 3: Read control point coordinates
1950
     *
1951
     * This is the most complex part because we must handle empty curves
1952
     * and dimensional variations (2D, 3D, 4D coordinates) correctly.
1953
     *
1954
     * For empty curves (npoints == 0), we create an empty point array
1955
     * that maintains the correct dimensional flags but contains no actual points.
1956
     */
1957
0
    if (npoints > 0) {
1958
        /*
1959
         * Non-empty curve: construct point array with reference to serialized data
1960
         *
1961
         * ptarray_construct_reference_data creates a POINTARRAY that directly
1962
         * references the serialized coordinate data without copying it.
1963
         * This is efficient and maintains the exact coordinate values.
1964
         */
1965
0
        curve->points = ptarray_construct_reference_data(
1966
0
            FLAGS_GET_Z(lwflags),    /* Has Z coordinate? */
1967
0
            FLAGS_GET_M(lwflags),    /* Has M coordinate? */
1968
0
            npoints,                 /* Number of points */
1969
0
            data_ptr                 /* Raw coordinate data */
1970
0
        );
1971
0
    } else {
1972
        /*
1973
         * Empty curve: construct an empty point array with correct dimensions
1974
         *
1975
         * Even empty curves need a valid POINTARRAY structure to maintain
1976
         * dimensional consistency and prevent null pointer access.
1977
         */
1978
0
        curve->points = ptarray_construct(
1979
0
            FLAGS_GET_Z(lwflags),    /* Preserve Z dimension flag */
1980
0
            FLAGS_GET_M(lwflags),    /* Preserve M dimension flag */
1981
0
            0                        /* Zero points = empty */
1982
0
        );
1983
0
    }
1984
1985
    /*
1986
     * Advance data pointer past coordinate data
1987
     *
1988
     * Each coordinate has a size determined by the dimensional flags:
1989
     * - 2D: 16 bytes (2 * sizeof(double))
1990
     * - 3D: 24 bytes (3 * sizeof(double))
1991
     * - 4D: 32 bytes (4 * sizeof(double))
1992
     */
1993
0
    data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1994
1995
    /*
1996
     * Calculate and return total bytes consumed
1997
     *
1998
     * This is important for reading multiple geometries from a buffer
1999
     * or for validation purposes in the calling code.
2000
     */
2001
0
    if (size)
2002
0
        *size = data_ptr - start_ptr;
2003
2004
0
    return curve;
2005
0
}
2006
/**
2007
 * Deserialize a geometry payload (GSERIALIZED v2 body) into an LWGEOM.
2008
 *
2009
 * Reads the geometry type from the provided data pointer and dispatches to the
2010
 * appropriate per-type deserializer to construct an LWGEOM. The deserializers
2011
 * consume bytes from the data pointer and may write the number of consumed
2012
 * bytes into g_size.
2013
 *
2014
 * @param data_ptr Pointer to the start of the geometry payload (type field first).
2015
 * @param lwflags Flags that describe dimensionality and other geometry attributes
2016
 *                (used to guide deserialization).
2017
 * @param g_size If non-NULL, receives the number of bytes consumed from data_ptr
2018
 *               by the deserialized geometry payload.
2019
 * @param srid SRID to assign to the resulting geometry (passed through to
2020
 *             deserializers that set SRID).
2021
 * @return Pointer to a newly allocated LWGEOM on success, or NULL if the type
2022
 *         is unknown or deserialization fails.
2023
 */
2024
LWGEOM *
2025
lwgeom_from_gserialized2_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *g_size, int32_t srid)
2026
0
{
2027
0
  uint32_t type;
2028
2029
0
  assert(data_ptr);
2030
2031
0
  type = gserialized2_get_uint32_t(data_ptr);
2032
2033
0
  LWDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, lwtype_name(type),
2034
0
    FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), FLAGS_GET_GEODETIC(lwflags), FLAGS_GET_BBOX(lwflags));
2035
2036
0
  switch (type)
2037
0
  {
2038
0
  case POINTTYPE:
2039
0
    return (LWGEOM *)lwpoint_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2040
0
  case LINETYPE:
2041
0
    return (LWGEOM *)lwline_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2042
0
  case CIRCSTRINGTYPE:
2043
0
    return (LWGEOM *)lwcircstring_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2044
0
  case POLYGONTYPE:
2045
0
    return (LWGEOM *)lwpoly_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2046
0
  case TRIANGLETYPE:
2047
0
    return (LWGEOM *)lwtriangle_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2048
0
  case MULTIPOINTTYPE:
2049
0
  case MULTILINETYPE:
2050
0
  case MULTIPOLYGONTYPE:
2051
0
  case COMPOUNDTYPE:
2052
0
  case CURVEPOLYTYPE:
2053
0
  case MULTICURVETYPE:
2054
0
  case MULTISURFACETYPE:
2055
0
  case POLYHEDRALSURFACETYPE:
2056
0
  case TINTYPE:
2057
0
  case COLLECTIONTYPE:
2058
0
    return (LWGEOM *)lwcollection_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2059
0
  case NURBSCURVETYPE:
2060
0
    return (LWGEOM *)lwnurbscurve_from_gserialized2_buffer(data_ptr, lwflags, g_size, srid);
2061
0
  default:
2062
0
    lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type));
2063
0
    return NULL;
2064
0
  }
2065
0
}
2066
2067
LWGEOM* lwgeom_from_gserialized2(const GSERIALIZED *g)
2068
0
{
2069
0
  lwflags_t lwflags = 0;
2070
0
  int32_t srid = 0;
2071
0
  uint32_t lwtype = 0;
2072
0
  uint8_t *data_ptr = NULL;
2073
0
  LWGEOM *lwgeom = NULL;
2074
0
  GBOX bbox;
2075
0
  size_t size = 0;
2076
2077
0
  assert(g);
2078
2079
0
  srid = gserialized2_get_srid(g);
2080
0
  lwtype = gserialized2_get_type(g);
2081
0
  lwflags = gserialized2_get_lwflags(g);
2082
2083
0
  LWDEBUGF(4, "Got type %d (%s), srid=%d", lwtype, lwtype_name(lwtype), srid);
2084
2085
0
  data_ptr = (uint8_t*)g->data;
2086
2087
  /* Skip optional flags */
2088
0
  if (G2FLAGS_GET_EXTENDED(g->gflags))
2089
0
  {
2090
0
    data_ptr += sizeof(uint64_t);
2091
0
  }
2092
2093
  /* Skip over optional bounding box */
2094
0
  if (FLAGS_GET_BBOX(lwflags))
2095
0
    data_ptr += gbox_serialized_size(lwflags);
2096
2097
0
  lwgeom = lwgeom_from_gserialized2_buffer(data_ptr, lwflags, &size, srid);
2098
2099
0
  if (!lwgeom)
2100
0
    lwerror("%s: unable create geometry", __func__); /* Ooops! */
2101
2102
0
  lwgeom->type = lwtype;
2103
0
  lwgeom->flags = lwflags;
2104
2105
0
  if (gserialized2_read_gbox_p(g, &bbox) == LW_SUCCESS)
2106
0
  {
2107
0
    lwgeom->bbox = gbox_copy(&bbox);
2108
0
  }
2109
0
  else if (lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS))
2110
0
  {
2111
0
    lwgeom->bbox = gbox_copy(&bbox);
2112
0
  }
2113
0
  else
2114
0
  {
2115
0
    lwgeom->bbox = NULL;
2116
0
  }
2117
2118
0
  return lwgeom;
2119
0
}
2120
2121
/**
2122
* Update the bounding box of a #GSERIALIZED, allocating a fresh one
2123
* if there is not enough space to just write the new box in.
2124
* <em>WARNING</em> if a new object needs to be created, the
2125
* input pointer will have to be freed by the caller! Check
2126
* to see if input == output. Returns null if there's a problem
2127
* like mismatched dimensions.
2128
*/
2129
GSERIALIZED* gserialized2_set_gbox(GSERIALIZED *g, GBOX *gbox)
2130
0
{
2131
2132
0
  int g_ndims = G2FLAGS_NDIMS_BOX(g->gflags);
2133
0
  int box_ndims = FLAGS_NDIMS_BOX(gbox->flags);
2134
0
  GSERIALIZED *g_out = NULL;
2135
0
  size_t box_size = 2 * g_ndims * sizeof(float);
2136
0
  float *fbox;
2137
0
  int fbox_pos = 0;
2138
2139
  /* The dimensionality of the inputs has to match or we are SOL. */
2140
0
  if (g_ndims != box_ndims)
2141
0
  {
2142
0
    return NULL;
2143
0
  }
2144
2145
  /* Serialized already has room for a box. */
2146
0
  if (G2FLAGS_GET_BBOX(g->gflags))
2147
0
  {
2148
0
    g_out = g;
2149
0
  }
2150
  /* Serialized has no box. We need to allocate enough space for the old
2151
     data plus the box, and leave a gap in the memory segment to write
2152
     the new values into.
2153
  */
2154
0
  else
2155
0
  {
2156
0
    size_t varsize_in = LWSIZE_GET(g->size);
2157
0
    size_t varsize_out = varsize_in + box_size;
2158
0
    uint8_t *ptr_out, *ptr_in, *ptr;
2159
0
    g_out = lwalloc(varsize_out);
2160
0
    ptr_out = (uint8_t*)g_out;
2161
0
    ptr = ptr_in = (uint8_t*)g;
2162
    /* Copy the head of g into place */
2163
0
    memcpy(ptr_out, ptr_in, 8); ptr_out += 8; ptr_in += 8;
2164
    /* Optionally copy extended bit into place */
2165
0
    if (G2FLAGS_GET_EXTENDED(g->gflags))
2166
0
    {
2167
0
      memcpy(ptr_out, ptr_in, 8); ptr_out += 8; ptr_in += 8;
2168
0
    }
2169
    /* Copy the body of g into place after leaving space for the box */
2170
0
    ptr_out += box_size;
2171
0
    memcpy(ptr_out, ptr_in, varsize_in - (ptr_in - ptr));
2172
0
    G2FLAGS_SET_BBOX(g_out->gflags, 1);
2173
0
    LWSIZE_SET(g_out->size, varsize_out);
2174
0
  }
2175
2176
  /* Move bounds to nearest float values */
2177
0
  gbox_float_round(gbox);
2178
  /* Now write the float box values into the memory segment */
2179
0
  fbox = (float*)(g_out->data);
2180
  /* Copy in X/Y */
2181
0
  fbox[fbox_pos++] = gbox->xmin;
2182
0
  fbox[fbox_pos++] = gbox->xmax;
2183
0
  fbox[fbox_pos++] = gbox->ymin;
2184
0
  fbox[fbox_pos++] = gbox->ymax;
2185
  /* Optionally copy in higher dims */
2186
0
  if(gserialized2_has_z(g) || gserialized2_is_geodetic(g))
2187
0
  {
2188
0
    fbox[fbox_pos++] = gbox->zmin;
2189
0
    fbox[fbox_pos++] = gbox->zmax;
2190
0
  }
2191
0
  if(gserialized2_has_m(g) && ! gserialized2_is_geodetic(g))
2192
0
  {
2193
0
    fbox[fbox_pos++] = gbox->mmin;
2194
0
    fbox[fbox_pos++] = gbox->mmax;
2195
0
  }
2196
2197
0
  return g_out;
2198
0
}
2199
2200
2201
/**
2202
* Remove the bounding box from a #GSERIALIZED. Returns a freshly
2203
* allocated #GSERIALIZED every time.
2204
*/
2205
GSERIALIZED* gserialized2_drop_gbox(GSERIALIZED *g)
2206
0
{
2207
0
  int g_ndims = G2FLAGS_NDIMS_BOX(g->gflags);
2208
0
  size_t box_size = 2 * g_ndims * sizeof(float);
2209
0
  size_t g_out_size = LWSIZE_GET(g->size) - box_size;
2210
0
  GSERIALIZED *g_out = lwalloc(g_out_size);
2211
2212
  /* Copy the contents while omitting the box */
2213
0
  if (G2FLAGS_GET_BBOX(g->gflags))
2214
0
  {
2215
0
    uint8_t *outptr = (uint8_t*)g_out;
2216
0
    uint8_t *inptr = (uint8_t*)g;
2217
    /* Copy the header (size+type) of g into place */
2218
0
    memcpy(outptr, inptr, 8); outptr += 8; inptr += 8;
2219
    /* Copy extended flags, if there are any */
2220
0
    if (G2FLAGS_GET_EXTENDED(g->gflags))
2221
0
    {
2222
0
      memcpy(outptr, inptr, 8); outptr += 8; inptr += 8;
2223
0
    }
2224
    /* Advance past box */
2225
0
    inptr += box_size;
2226
    /* Copy parts after the box into place */
2227
0
    memcpy(outptr, inptr, g_out_size - 8);
2228
0
    G2FLAGS_SET_BBOX(g_out->gflags, 0);
2229
0
    LWSIZE_SET(g_out->size, g_out_size);
2230
0
  }
2231
  /* No box? Nothing to do but copy and return. */
2232
0
  else
2233
0
  {
2234
0
    memcpy(g_out, g, g_out_size);
2235
0
  }
2236
2237
0
  return g_out;
2238
0
}