Coverage Report

Created: 2026-08-14 06:58

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