Coverage Report

Created: 2026-11-12 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/gserialized1.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
#include "liblwgeom_internal.h"
28
#include "lwgeom_log.h"
29
#include "lwgeodetic.h"
30
#include "gserialized1.h"
31
32
#include <stddef.h>
33
#if defined(__has_feature)
34
#if __has_feature(address_sanitizer)
35
#define POSTGIS_ASAN_ALLOCATOR_SIZE 1
36
#endif
37
#endif
38
#if defined(__SANITIZE_ADDRESS__)
39
#define POSTGIS_ASAN_ALLOCATOR_SIZE 1
40
#endif
41
#if defined(POSTGIS_ASAN_ALLOCATOR_SIZE)
42
#include <sanitizer/allocator_interface.h>
43
#endif
44
45
/***********************************************************************
46
* GSERIALIZED metadata utility functions.
47
*/
48
49
static int gserialized1_read_gbox_p(const GSERIALIZED *g, GBOX *gbox);
50
static int gserialized1_payload_bounds(const GSERIALIZED *g, uint8_t **start, uint8_t **end);
51
static int gserialized1_validate_geometry_buffer(uint8_t *data_ptr, uint8_t *data_end, lwflags_t lwflags, size_t *size);
52
53
static size_t
54
gserialized1_buffer_size(const GSERIALIZED *g)
55
0
{
56
0
  size_t gsize = LWSIZE_GET(g->size);
57
#if defined(POSTGIS_ASAN_ALLOCATOR_SIZE)
58
  size_t allocated_size = __sanitizer_get_allocated_size(g);
59
  if (allocated_size > 0 && allocated_size < gsize)
60
    return allocated_size;
61
#endif
62
0
  return gsize;
63
0
}
64
65
lwflags_t gserialized1_get_lwflags(const GSERIALIZED *g)
66
0
{
67
0
  lwflags_t lwflags = 0;
68
0
  uint8_t gflags = g->gflags;
69
0
  FLAGS_SET_Z(lwflags, G1FLAGS_GET_Z(gflags));
70
0
  FLAGS_SET_M(lwflags, G1FLAGS_GET_M(gflags));
71
0
  FLAGS_SET_BBOX(lwflags, G1FLAGS_GET_BBOX(gflags));
72
0
  FLAGS_SET_GEODETIC(lwflags, G1FLAGS_GET_GEODETIC(gflags));
73
0
  FLAGS_SET_SOLID(lwflags, G1FLAGS_GET_SOLID(gflags));
74
0
  return lwflags;
75
0
}
76
77
uint8_t lwflags_get_g1flags(lwflags_t lwflags)
78
0
{
79
0
  uint8_t gflags = 0;
80
0
  G1FLAGS_SET_Z(gflags, FLAGS_GET_Z(lwflags));
81
0
  G1FLAGS_SET_M(gflags, FLAGS_GET_M(lwflags));
82
0
  G1FLAGS_SET_BBOX(gflags, FLAGS_GET_BBOX(lwflags));
83
0
  G1FLAGS_SET_GEODETIC(gflags, FLAGS_GET_GEODETIC(lwflags));
84
0
  G1FLAGS_SET_SOLID(gflags, FLAGS_GET_SOLID(lwflags));
85
0
  return gflags;
86
0
}
87
88
89
static size_t gserialized1_box_size(const GSERIALIZED *g)
90
0
{
91
0
  if (G1FLAGS_GET_GEODETIC(g->gflags))
92
0
    return 6 * sizeof(float);
93
0
  else
94
0
    return 2 * G1FLAGS_NDIMS(g->gflags) * sizeof(float);
95
0
}
96
97
/* handle missaligned uint32_t data */
98
static inline uint32_t gserialized1_get_uint32_t(const uint8_t *loc)
99
0
{
100
0
  return *((uint32_t*)loc);
101
0
}
102
103
uint8_t g1flags(int has_z, int has_m, int is_geodetic)
104
0
{
105
0
  uint8_t gflags = 0;
106
0
  if (has_z)
107
0
    G1FLAGS_SET_Z(gflags, 1);
108
0
  if (has_m)
109
0
    G1FLAGS_SET_M(gflags, 1);
110
0
  if (is_geodetic)
111
0
    G1FLAGS_SET_GEODETIC(gflags, 1);
112
0
  return gflags;
113
0
}
114
115
int gserialized1_has_bbox(const GSERIALIZED *gser)
116
0
{
117
0
  return G1FLAGS_GET_BBOX(gser->gflags);
118
0
}
119
120
int gserialized1_has_z(const GSERIALIZED *gser)
121
0
{
122
0
  return G1FLAGS_GET_Z(gser->gflags);
123
0
}
124
125
int gserialized1_has_m(const GSERIALIZED *gser)
126
0
{
127
0
  return G1FLAGS_GET_M(gser->gflags);
128
0
}
129
130
int gserialized1_ndims(const GSERIALIZED *gser)
131
0
{
132
0
  return G1FLAGS_NDIMS(gser->gflags);
133
0
}
134
135
int gserialized1_is_geodetic(const GSERIALIZED *gser)
136
0
{
137
0
    return G1FLAGS_GET_GEODETIC(gser->gflags);
138
0
}
139
140
uint32_t gserialized1_max_header_size(void)
141
0
{
142
  /* GSERIALIZED size + max bbox according gbox_serialized_size (XYZM*2) + extended flags + type */
143
0
  return offsetof(GSERIALIZED, data) + 8 * sizeof(float) + sizeof(uint32_t);
144
0
}
145
146
static uint32_t gserialized1_header_size(const GSERIALIZED *gser)
147
0
{
148
0
  uint32_t sz = 8; /* varsize (4) + srid(3) + flags (1) */
149
150
0
  if (gserialized1_has_bbox(gser))
151
0
    sz += gserialized1_box_size(gser);
152
153
0
  return sz;
154
0
}
155
156
uint32_t gserialized1_get_type(const GSERIALIZED *g)
157
0
{
158
0
  uint32_t *ptr;
159
0
  size_t hsz = gserialized1_header_size(g);
160
0
  size_t gsize = gserialized1_buffer_size(g);
161
0
  if (gsize < hsz + sizeof(uint32_t))
162
0
  {
163
0
    lwerror("%s: GSERIALIZED too small for geometry type", __func__);
164
0
    return 0;
165
0
  }
166
0
  ptr = (uint32_t*)(g->data);
167
0
  if ( G1FLAGS_GET_BBOX(g->gflags) )
168
0
  {
169
0
    ptr += (gserialized1_box_size(g) / sizeof(uint32_t));
170
0
  }
171
0
  return *ptr;
172
0
}
173
174
int32_t gserialized1_get_srid(const GSERIALIZED *s)
175
0
{
176
0
  uint32_t srid = 0;
177
0
  srid = srid | ((uint32_t)s->srid[0] << 16);
178
0
  srid = srid | ((uint32_t)s->srid[1] << 8);
179
0
  srid = srid | (uint32_t)s->srid[2];
180
  /* Only the first 21 bits are set. Sign-extend without signed shift UB. */
181
0
  if (srid & 0x00100000)
182
0
    srid |= 0xFFE00000;
183
184
  /* 0 is our internal unknown value. We'll map back and forth here for now */
185
0
  if ( srid == 0 )
186
0
    return SRID_UNKNOWN;
187
0
  else
188
0
    return (int32_t)srid;
189
0
}
190
191
void gserialized1_set_srid(GSERIALIZED *s, int32_t srid)
192
0
{
193
0
  LWDEBUGF(3, "%s called with srid = %d", __func__, srid);
194
195
0
  srid = clamp_srid(srid);
196
197
  /* 0 is our internal unknown value.
198
   * We'll map back and forth here for now */
199
0
  if ( srid == SRID_UNKNOWN )
200
0
    srid = 0;
201
202
0
  s->srid[0] = (srid & 0x001F0000) >> 16;
203
0
  s->srid[1] = (srid & 0x0000FF00) >> 8;
204
0
  s->srid[2] = (srid & 0x000000FF);
205
0
}
206
207
static int
208
gserialized1_range_available(const uint8_t *ptr, const uint8_t *end, size_t len)
209
0
{
210
0
  return ptr <= end && len <= (size_t)(end - ptr);
211
0
}
212
213
static int
214
gserialized1_checked_mul(size_t a, size_t b, size_t *out)
215
0
{
216
0
  if (a != 0 && b > SIZE_MAX / a)
217
0
    return LW_FAILURE;
218
0
  *out = a * b;
219
0
  return LW_SUCCESS;
220
0
}
221
222
static int
223
gserialized1_checked_add(size_t a, size_t b, size_t *out)
224
0
{
225
0
  if (b > SIZE_MAX - a)
226
0
    return LW_FAILURE;
227
0
  *out = a + b;
228
0
  return LW_SUCCESS;
229
0
}
230
231
static int
232
gserialized1_payload_bounds(const GSERIALIZED *g, uint8_t **start, uint8_t **end)
233
0
{
234
0
  size_t hsz;
235
0
  size_t gsize;
236
237
0
  if (!g)
238
0
    return LW_FAILURE;
239
240
0
  hsz = gserialized1_header_size(g);
241
0
  gsize = gserialized1_buffer_size(g);
242
0
  if (gsize < hsz)
243
0
    return LW_FAILURE;
244
245
0
  if (start)
246
0
    *start = (uint8_t *)g + hsz;
247
0
  if (end)
248
0
    *end = (uint8_t *)g + gsize;
249
0
  return LW_SUCCESS;
250
0
}
251
252
static uint32_t
253
gserialized1_read_uint32_checked(uint8_t *ptr, uint8_t *end, const char *field)
254
0
{
255
0
  uint32_t value = 0;
256
0
  if (!gserialized1_range_available(ptr, end, sizeof(uint32_t)))
257
0
  {
258
0
    lwerror("%s: GSERIALIZED too small for %s", __func__, field);
259
0
    return 0;
260
0
  }
261
0
  memcpy(&value, ptr, sizeof(uint32_t));
262
0
  return value;
263
0
}
264
265
static int
266
gserialized1_pointarray_payload_size(uint32_t npoints, lwflags_t lwflags, size_t *nbytes)
267
0
{
268
0
  if (gserialized1_checked_mul((size_t)npoints, sizeof(double) * FLAGS_NDIMS(lwflags), nbytes) == LW_FAILURE)
269
0
  {
270
0
    lwerror("%s: GSERIALIZED point count overflows payload size", __func__);
271
0
    return LW_FAILURE;
272
0
  }
273
0
  return LW_SUCCESS;
274
0
}
275
276
static int
277
gserialized1_validate_geometry_buffer(uint8_t *data_ptr, uint8_t *data_end, lwflags_t lwflags, size_t *size)
278
0
{
279
0
  uint32_t type, count;
280
0
  size_t consumed = 0;
281
282
0
  if (!gserialized1_range_available(data_ptr, data_end, 2 * sizeof(uint32_t)))
283
0
  {
284
0
    lwerror("%s: GSERIALIZED geometry payload is too short", __func__);
285
0
    return LW_FAILURE;
286
0
  }
287
288
0
  type = gserialized1_read_uint32_checked(data_ptr, data_end, "geometry type");
289
0
  count = gserialized1_read_uint32_checked(data_ptr + sizeof(uint32_t), data_end, "geometry count");
290
291
0
  switch (type)
292
0
  {
293
0
  case POINTTYPE:
294
0
    if (count > 1)
295
0
    {
296
0
      lwerror("%s: invalid point count %u", __func__, count);
297
0
      return LW_FAILURE;
298
0
    }
299
    /* fall through */
300
0
  case LINETYPE:
301
0
  case CIRCSTRINGTYPE:
302
0
  case TRIANGLETYPE: {
303
0
    size_t point_bytes;
304
0
    if (gserialized1_pointarray_payload_size(count, lwflags, &point_bytes) == LW_FAILURE)
305
0
      return LW_FAILURE;
306
0
    consumed = 2 * sizeof(uint32_t) + point_bytes;
307
0
    break;
308
0
  }
309
310
0
  case POLYGONTYPE: {
311
0
    size_t ring_counts_size, ring_counts_padded_size, point_bytes_sum = 0;
312
0
    uint8_t *ring_counts = data_ptr + 2 * sizeof(uint32_t);
313
0
    uint32_t i;
314
315
0
    if (gserialized1_checked_mul((size_t)count, sizeof(uint32_t), &ring_counts_size) == LW_FAILURE)
316
0
    {
317
0
      lwerror("%s: GSERIALIZED ring count overflows payload size", __func__);
318
0
      return LW_FAILURE;
319
0
    }
320
0
    ring_counts_padded_size = ring_counts_size + ((count % 2) ? sizeof(uint32_t) : 0);
321
0
    if (!gserialized1_range_available(ring_counts, data_end, ring_counts_padded_size))
322
0
    {
323
0
      lwerror("%s: GSERIALIZED polygon ring table exceeds payload size", __func__);
324
0
      return LW_FAILURE;
325
0
    }
326
327
0
    for (i = 0; i < count; i++)
328
0
    {
329
0
      size_t ring_point_bytes;
330
0
      uint32_t npoints = gserialized1_read_uint32_checked(
331
0
          ring_counts + i * sizeof(uint32_t), data_end, "ring point count");
332
0
      if (gserialized1_pointarray_payload_size(npoints, lwflags, &ring_point_bytes) == LW_FAILURE)
333
0
        return LW_FAILURE;
334
0
      if (gserialized1_checked_add(point_bytes_sum, ring_point_bytes, &point_bytes_sum) == LW_FAILURE)
335
0
      {
336
0
        lwerror("%s: GSERIALIZED polygon coordinate size overflows", __func__);
337
0
        return LW_FAILURE;
338
0
      }
339
0
    }
340
0
    consumed = 2 * sizeof(uint32_t) + ring_counts_padded_size;
341
0
    if (gserialized1_checked_add(consumed, point_bytes_sum, &consumed) == LW_FAILURE)
342
0
    {
343
0
      lwerror("%s: GSERIALIZED polygon size overflows", __func__);
344
0
      return LW_FAILURE;
345
0
    }
346
0
    break;
347
0
  }
348
349
0
  case MULTIPOINTTYPE:
350
0
  case MULTILINETYPE:
351
0
  case MULTIPOLYGONTYPE:
352
0
  case COMPOUNDTYPE:
353
0
  case CURVEPOLYTYPE:
354
0
  case MULTICURVETYPE:
355
0
  case MULTISURFACETYPE:
356
0
  case POLYHEDRALSURFACETYPE:
357
0
  case TINTYPE:
358
0
  case COLLECTIONTYPE: {
359
0
    uint32_t i;
360
0
    uint8_t *subgeom_ptr = data_ptr + 2 * sizeof(uint32_t);
361
0
    lwflags_t subflags = lwflags;
362
0
    FLAGS_SET_BBOX(subflags, 0);
363
0
    for (i = 0; i < count; i++)
364
0
    {
365
0
      size_t subsize = 0;
366
0
      uint32_t subtype =
367
0
          gserialized1_read_uint32_checked(subgeom_ptr, data_end, "collection subtype");
368
0
      if (!lwcollection_allows_subtype(type, subtype))
369
0
      {
370
0
        lwerror("Invalid subtype (%s) for collection type (%s)",
371
0
          lwtype_name(subtype),
372
0
          lwtype_name(type));
373
0
        return LW_FAILURE;
374
0
      }
375
0
      if (gserialized1_validate_geometry_buffer(subgeom_ptr, data_end, subflags, &subsize) ==
376
0
          LW_FAILURE)
377
0
        return LW_FAILURE;
378
0
      if (subsize == 0)
379
0
      {
380
0
        lwerror("%s: GSERIALIZED collection member has zero size", __func__);
381
0
        return LW_FAILURE;
382
0
      }
383
0
      subgeom_ptr += subsize;
384
0
    }
385
0
    consumed = (size_t)(subgeom_ptr - data_ptr);
386
0
    break;
387
0
  }
388
389
0
  default:
390
0
    lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type));
391
0
    return LW_FAILURE;
392
0
  }
393
394
0
  if (!gserialized1_range_available(data_ptr, data_end, consumed))
395
0
  {
396
0
    lwerror("%s: GSERIALIZED geometry payload exceeds declared size", __func__);
397
0
    return LW_FAILURE;
398
0
  }
399
400
0
  if (size)
401
0
    *size = consumed;
402
0
  return LW_SUCCESS;
403
0
}
404
405
static size_t gserialized1_is_empty_recurse(const uint8_t *p, int *isempty);
406
static size_t gserialized1_is_empty_recurse(const uint8_t *p, int *isempty)
407
0
{
408
0
  int i;
409
0
  int32_t type, num;
410
411
0
  memcpy(&type, p, 4);
412
0
  memcpy(&num, p+4, 4);
413
414
0
  if ( lwtype_is_collection(type) )
415
0
  {
416
0
    size_t lz = 8;
417
0
    for ( i = 0; i < num; i++ )
418
0
    {
419
0
      lz += gserialized1_is_empty_recurse(p+lz, isempty);
420
0
      if ( ! *isempty )
421
0
        return lz;
422
0
    }
423
0
    *isempty = LW_TRUE;
424
0
    return lz;
425
0
  }
426
0
  else
427
0
  {
428
0
    *isempty = (num == 0 ? LW_TRUE : LW_FALSE);
429
0
    return 8;
430
0
  }
431
0
}
432
433
int gserialized1_is_empty(const GSERIALIZED *g)
434
0
{
435
0
  uint8_t *p, *end;
436
0
  int isempty = 0;
437
0
  assert(g);
438
439
0
  if (gserialized1_payload_bounds(g, &p, &end) == LW_FAILURE)
440
0
  {
441
0
    lwerror("%s: invalid GSERIALIZED header size", __func__);
442
0
    return LW_TRUE;
443
0
  }
444
0
  if (gserialized1_validate_geometry_buffer(p, end, gserialized1_get_lwflags(g), NULL) == LW_FAILURE)
445
0
    return LW_TRUE;
446
447
0
  gserialized1_is_empty_recurse(p, &isempty);
448
0
  return isempty;
449
0
}
450
451
452
/* Prototype for lookup3.c */
453
/* key = the key to hash */
454
/* length = length of the key */
455
/* pc = IN: primary initval, OUT: primary hash */
456
/* pb = IN: secondary initval, OUT: secondary hash */
457
void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb);
458
459
int32_t
460
gserialized1_hash(const GSERIALIZED *g1)
461
0
{
462
0
  int32_t hval;
463
0
  int32_t pb = 0, pc = 0;
464
  /* Point to just the type/coordinate part of buffer */
465
0
  size_t hsz1 = gserialized1_header_size(g1);
466
0
  uint8_t *b1 = (uint8_t*)g1 + hsz1;
467
  /* Calculate size of type/coordinate buffer */
468
0
  size_t sz1 = LWSIZE_GET(g1->size);
469
0
  size_t bsz1 = sz1 - hsz1;
470
  /* Calculate size of srid/type/coordinate buffer */
471
0
  int32_t srid = gserialized1_get_srid(g1);
472
0
  size_t bsz2 = bsz1 + sizeof(int);
473
0
  uint8_t *b2 = lwalloc(bsz2);
474
  /* Copy srid into front of combined buffer */
475
0
  memcpy(b2, &srid, sizeof(int));
476
  /* Copy type/coordinates into rest of combined buffer */
477
0
  memcpy(b2+sizeof(int), b1, bsz1);
478
  /* Hash combined buffer */
479
0
  hashlittle2(b2, bsz2, (uint32_t *)&pb, (uint32_t *)&pc);
480
0
  lwfree(b2);
481
0
  hval = pb ^ pc;
482
0
  return hval;
483
0
}
484
485
int gserialized1_read_gbox_p(const GSERIALIZED *g, GBOX *gbox)
486
0
{
487
488
  /* Null input! */
489
0
  if ( ! ( g && gbox ) ) return LW_FAILURE;
490
0
  if (gserialized1_buffer_size(g) < offsetof(GSERIALIZED, data) + gserialized1_box_size(g))
491
0
    return LW_FAILURE;
492
493
  /* Initialize the flags on the box */
494
0
  gbox->flags = gserialized1_get_lwflags(g);
495
496
  /* Has pre-calculated box */
497
0
  if ( G1FLAGS_GET_BBOX(g->gflags) )
498
0
  {
499
0
    int i = 0;
500
0
    float *fbox = (float*)(g->data);
501
0
    gbox->xmin = fbox[i++];
502
0
    gbox->xmax = fbox[i++];
503
0
    gbox->ymin = fbox[i++];
504
0
    gbox->ymax = fbox[i++];
505
506
    /* Geodetic? Read next dimension (geocentric Z) and return */
507
0
    if ( G1FLAGS_GET_GEODETIC(g->gflags) )
508
0
    {
509
0
      gbox->zmin = fbox[i++];
510
0
      gbox->zmax = fbox[i++];
511
0
      return LW_SUCCESS;
512
0
    }
513
    /* Cartesian? Read extra dimensions (if there) and return */
514
0
    if ( G1FLAGS_GET_Z(g->gflags) )
515
0
    {
516
0
      gbox->zmin = fbox[i++];
517
0
      gbox->zmax = fbox[i++];
518
0
    }
519
0
    if ( G1FLAGS_GET_M(g->gflags) )
520
0
    {
521
0
      gbox->mmin = fbox[i++];
522
0
      gbox->mmax = fbox[i++];
523
0
    }
524
0
    return LW_SUCCESS;
525
0
  }
526
0
  return LW_FAILURE;
527
0
}
528
529
/*
530
* Populate a bounding box *without* allocating an LWGEOM. Useful
531
* for some performance purposes.
532
*/
533
int
534
gserialized1_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox)
535
0
{
536
0
  uint32_t type;
537
0
  uint8_t *geometry_start = NULL;
538
0
  uint8_t *geometry_end = NULL;
539
540
0
  if (gserialized1_payload_bounds(g, &geometry_start, &geometry_end) == LW_FAILURE)
541
0
    return LW_FAILURE;
542
0
  if (gserialized1_validate_geometry_buffer(geometry_start, geometry_end, gserialized1_get_lwflags(g), NULL) ==
543
0
      LW_FAILURE)
544
0
    return LW_FAILURE;
545
0
  type = gserialized1_get_type(g);
546
547
  /* Peeking doesn't help if you already have a box or are geodetic */
548
0
  if ( G1FLAGS_GET_GEODETIC(g->gflags) || G1FLAGS_GET_BBOX(g->gflags) )
549
0
  {
550
0
    return LW_FAILURE;
551
0
  }
552
553
  /* Boxes of points are easy peasy */
554
0
  if ( type == POINTTYPE )
555
0
  {
556
0
    int i = 1; /* Start past <pointtype><padding> */
557
0
    double *dptr = (double*)(g->data);
558
559
    /* Read the empty flag */
560
0
    int32_t *iptr = (int32_t *)(g->data);
561
0
    int isempty = (iptr[1] == 0);
562
563
    /* EMPTY point has no box */
564
0
    if ( isempty ) return LW_FAILURE;
565
566
0
    gbox->xmin = gbox->xmax = dptr[i++];
567
0
    gbox->ymin = gbox->ymax = dptr[i++];
568
0
    gbox->flags = gserialized1_get_lwflags(g);
569
0
    if ( G1FLAGS_GET_Z(g->gflags) )
570
0
    {
571
0
      gbox->zmin = gbox->zmax = dptr[i++];
572
0
    }
573
0
    if ( G1FLAGS_GET_M(g->gflags) )
574
0
    {
575
0
      gbox->mmin = gbox->mmax = dptr[i++];
576
0
    }
577
0
    gbox_float_round(gbox);
578
0
    return LW_SUCCESS;
579
0
  }
580
  /* We can calculate the box of a two-point cartesian line trivially */
581
0
  else if ( type == LINETYPE )
582
0
  {
583
0
    int ndims = G1FLAGS_NDIMS(g->gflags);
584
0
    int i = 0; /* Start at <linetype><npoints> */
585
0
    double *dptr = (double*)(g->data);
586
0
    int32_t *iptr = (int32_t *)(g->data);
587
0
    int npoints = iptr[1]; /* Read the npoints */
588
589
    /* This only works with 2-point lines */
590
0
    if ( npoints != 2 )
591
0
      return LW_FAILURE;
592
593
    /* Advance to X */
594
    /* Past <linetype><npoints> */
595
0
    i++;
596
0
    gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]);
597
0
    gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]);
598
599
    /* Advance to Y */
600
0
    i++;
601
0
    gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]);
602
0
    gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]);
603
604
0
    gbox->flags = gserialized1_get_lwflags(g);
605
0
    if ( G1FLAGS_GET_Z(g->gflags) )
606
0
    {
607
      /* Advance to Z */
608
0
      i++;
609
0
      gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]);
610
0
      gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]);
611
0
    }
612
0
    if ( G1FLAGS_GET_M(g->gflags) )
613
0
    {
614
      /* Advance to M */
615
0
      i++;
616
0
      gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]);
617
0
      gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]);
618
0
    }
619
0
    gbox_float_round(gbox);
620
0
    return LW_SUCCESS;
621
0
  }
622
  /* We can also do single-entry multi-points */
623
0
  else if ( type == MULTIPOINTTYPE )
624
0
  {
625
0
    int i = 0; /* Start at <multipointtype><ngeoms> */
626
0
    double *dptr = (double*)(g->data);
627
0
    int32_t *iptr = (int32_t *)(g->data);
628
0
    int ngeoms = iptr[1]; /* Read the ngeoms */
629
0
    int npoints;
630
631
    /* This only works with single-entry multipoints */
632
0
    if ( ngeoms != 1 )
633
0
      return LW_FAILURE;
634
635
    /* Npoints is at <multipointtype><ngeoms><pointtype><npoints> */
636
0
    npoints = iptr[3];
637
638
    /* The check below is necessary because we can have a MULTIPOINT
639
     * that contains a single, empty POINT (ngeoms = 1, npoints = 0) */
640
0
    if ( npoints != 1 )
641
0
      return LW_FAILURE;
642
643
    /* Move forward two doubles (four ints) */
644
    /* Past <multipointtype><ngeoms> */
645
    /* Past <pointtype><npoints> */
646
0
    i += 2;
647
648
    /* Read the doubles from the one point */
649
0
    gbox->xmin = gbox->xmax = dptr[i++];
650
0
    gbox->ymin = gbox->ymax = dptr[i++];
651
0
    gbox->flags = gserialized1_get_lwflags(g);
652
0
    if ( G1FLAGS_GET_Z(g->gflags) )
653
0
    {
654
0
      gbox->zmin = gbox->zmax = dptr[i++];
655
0
    }
656
0
    if ( G1FLAGS_GET_M(g->gflags) )
657
0
    {
658
0
      gbox->mmin = gbox->mmax = dptr[i++];
659
0
    }
660
0
    gbox_float_round(gbox);
661
0
    return LW_SUCCESS;
662
0
  }
663
  /* And we can do single-entry multi-lines with two vertices (!!!) */
664
0
  else if ( type == MULTILINETYPE )
665
0
  {
666
0
    int ndims = G1FLAGS_NDIMS(g->gflags);
667
0
    int i = 0; /* Start at <multilinetype><ngeoms> */
668
0
    double *dptr = (double*)(g->data);
669
0
    int32_t *iptr = (int32_t *)(g->data);
670
0
    int ngeoms = iptr[1]; /* Read the ngeoms */
671
0
    int npoints;
672
673
    /* This only works with 1-line multilines */
674
0
    if ( ngeoms != 1 )
675
0
      return LW_FAILURE;
676
677
    /* Npoints is at <multilinetype><ngeoms><linetype><npoints> */
678
0
    npoints = iptr[3];
679
680
0
    if ( npoints != 2 )
681
0
      return LW_FAILURE;
682
683
    /* Advance to X */
684
    /* Move forward two doubles (four ints) */
685
    /* Past <multilinetype><ngeoms> */
686
    /* Past <linetype><npoints> */
687
0
    i += 2;
688
0
    gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]);
689
0
    gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]);
690
691
    /* Advance to Y */
692
0
    i++;
693
0
    gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]);
694
0
    gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]);
695
696
0
    gbox->flags = gserialized1_get_lwflags(g);
697
0
    if ( G1FLAGS_GET_Z(g->gflags) )
698
0
    {
699
      /* Advance to Z */
700
0
      i++;
701
0
      gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]);
702
0
      gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]);
703
0
    }
704
0
    if ( G1FLAGS_GET_M(g->gflags) )
705
0
    {
706
      /* Advance to M */
707
0
      i++;
708
0
      gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]);
709
0
      gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]);
710
0
    }
711
0
    gbox_float_round(gbox);
712
0
    return LW_SUCCESS;
713
0
  }
714
715
0
  return LW_FAILURE;
716
0
}
717
718
static inline void
719
gserialized1_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point)
720
0
{
721
0
  uint8_t dim = 0;
722
0
  out_point->x = dptr[dim++];
723
0
  out_point->y = dptr[dim++];
724
725
0
  if (G1FLAGS_GET_Z(flags))
726
0
  {
727
0
    out_point->z = dptr[dim++];
728
0
  }
729
0
  if (G1FLAGS_GET_M(flags))
730
0
  {
731
0
    out_point->m = dptr[dim];
732
0
  }
733
0
}
734
735
int
736
gserialized1_peek_first_point(const GSERIALIZED *g, POINT4D *out_point)
737
0
{
738
0
  uint8_t *geometry_start = NULL;
739
0
  uint8_t *geometry_end = NULL;
740
741
0
  if (gserialized1_payload_bounds(g, &geometry_start, &geometry_end) == LW_FAILURE)
742
0
    return LW_FAILURE;
743
0
  if (gserialized1_validate_geometry_buffer(geometry_start, geometry_end, gserialized1_get_lwflags(g), NULL) ==
744
0
      LW_FAILURE)
745
0
    return LW_FAILURE;
746
747
0
  uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0;
748
0
  if (isEmpty)
749
0
  {
750
0
    return LW_FAILURE;
751
0
  }
752
753
0
  uint32_t type = (((uint32_t *)geometry_start)[0]);
754
  /* Setup double_array_start depending on the geometry type */
755
0
  double *double_array_start = NULL;
756
0
  switch (type)
757
0
  {
758
0
  case (POINTTYPE):
759
    /* For points we only need to jump over the type and npoints 32b ints */
760
0
    double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t));
761
0
    break;
762
763
0
  default:
764
0
    lwerror("%s is currently not implemented for type %d", __func__, type);
765
0
    return LW_FAILURE;
766
0
  }
767
768
0
  gserialized1_copy_point(double_array_start, g->gflags, out_point);
769
0
  return LW_SUCCESS;
770
0
}
771
772
/**
773
* Read the bounding box off a serialization and calculate one if
774
* it is not already there.
775
*/
776
int gserialized1_get_gbox_p(const GSERIALIZED *g, GBOX *box)
777
0
{
778
  /* Try to just read the serialized box. */
779
0
  if ( gserialized1_read_gbox_p(g, box) == LW_SUCCESS )
780
0
  {
781
0
    return LW_SUCCESS;
782
0
  }
783
  /* No box? Try to peek into simpler geometries and */
784
  /* derive a box without creating an lwgeom */
785
0
  else if ( gserialized1_peek_gbox_p(g, box) == LW_SUCCESS )
786
0
  {
787
0
    return LW_SUCCESS;
788
0
  }
789
  /* Damn! Nothing for it but to create an lwgeom... */
790
  /* See http://trac.osgeo.org/postgis/ticket/1023 */
791
0
  else
792
0
  {
793
0
    LWGEOM *lwgeom = lwgeom_from_gserialized(g);
794
0
    int ret = lwgeom_calculate_gbox(lwgeom, box);
795
0
    gbox_float_round(box);
796
0
    lwgeom_free(lwgeom);
797
0
    return ret;
798
0
  }
799
0
}
800
801
/**
802
* Read the bounding box off a serialization and fail if
803
* it is not already there.
804
*/
805
int gserialized1_fast_gbox_p(const GSERIALIZED *g, GBOX *box)
806
0
{
807
  /* Try to just read the serialized box. */
808
0
  if ( gserialized1_read_gbox_p(g, box) == LW_SUCCESS )
809
0
  {
810
0
    return LW_SUCCESS;
811
0
  }
812
  /* No box? Try to peek into simpler geometries and */
813
  /* derive a box without creating an lwgeom */
814
0
  else if ( gserialized1_peek_gbox_p(g, box) == LW_SUCCESS )
815
0
  {
816
0
    return LW_SUCCESS;
817
0
  }
818
0
  else
819
0
  {
820
0
    return LW_FAILURE;
821
0
  }
822
0
}
823
824
825
826
827
/***********************************************************************
828
* Calculate the GSERIALIZED size for an LWGEOM.
829
*/
830
831
/* Private functions */
832
833
static size_t gserialized1_from_any_size(const LWGEOM *geom); /* Local prototype */
834
835
static size_t gserialized1_from_lwpoint_size(const LWPOINT *point)
836
0
{
837
0
  size_t size = 4; /* Type number. */
838
839
0
  assert(point);
840
841
0
  size += 4; /* Number of points (one or zero (empty)). */
842
0
  size += sizeof(double) * point->point->npoints * FLAGS_NDIMS(point->flags);
843
844
0
  LWDEBUGF(3, "point size = %zu", size);
845
846
0
  return size;
847
0
}
848
849
static size_t gserialized1_from_lwline_size(const LWLINE *line)
850
0
{
851
0
  size_t size = 4; /* Type number. */
852
853
0
  assert(line);
854
855
0
  size += 4; /* Number of points (zero => empty). */
856
0
  size += sizeof(double) * line->points->npoints * FLAGS_NDIMS(line->flags);
857
858
0
  LWDEBUGF(3, "linestring size = %zu", size);
859
860
0
  return size;
861
0
}
862
863
static size_t gserialized1_from_lwtriangle_size(const LWTRIANGLE *triangle)
864
0
{
865
0
  size_t size = 4; /* Type number. */
866
867
0
  assert(triangle);
868
869
0
  size += 4; /* Number of points (zero => empty). */
870
0
  size += sizeof(double) * triangle->points->npoints * FLAGS_NDIMS(triangle->flags);
871
872
0
  LWDEBUGF(3, "triangle size = %zu", size);
873
874
0
  return size;
875
0
}
876
877
static size_t gserialized1_from_lwpoly_size(const LWPOLY *poly)
878
0
{
879
0
  size_t size = 4; /* Type number. */
880
0
  uint32_t i = 0;
881
882
0
  assert(poly);
883
884
0
  size += 4; /* Number of rings (zero => empty). */
885
0
  if ( poly->nrings % 2 )
886
0
    size += 4; /* Padding to double alignment. */
887
888
0
  for ( i = 0; i < poly->nrings; i++ )
889
0
  {
890
0
    size += 4; /* Number of points in ring. */
891
0
    size += sizeof(double) * poly->rings[i]->npoints * FLAGS_NDIMS(poly->flags);
892
0
  }
893
894
0
  LWDEBUGF(3, "polygon size = %zu", size);
895
896
0
  return size;
897
0
}
898
899
static size_t gserialized1_from_lwcircstring_size(const LWCIRCSTRING *curve)
900
0
{
901
0
  size_t size = 4; /* Type number. */
902
903
0
  assert(curve);
904
905
0
  size += 4; /* Number of points (zero => empty). */
906
0
  size += sizeof(double) * curve->points->npoints * FLAGS_NDIMS(curve->flags);
907
908
0
  LWDEBUGF(3, "circstring size = %zu", size);
909
910
0
  return size;
911
0
}
912
913
static size_t gserialized1_from_lwcollection_size(const LWCOLLECTION *col)
914
0
{
915
0
  size_t size = 4; /* Type number. */
916
0
  uint32_t i = 0;
917
918
0
  assert(col);
919
920
0
  size += 4; /* Number of sub-geometries (zero => empty). */
921
922
0
  for ( i = 0; i < col->ngeoms; i++ )
923
0
  {
924
0
    size_t subsize = gserialized1_from_any_size(col->geoms[i]);
925
0
    size += subsize;
926
0
    LWDEBUGF(3, "lwcollection subgeom(%d) size = %zu", i, subsize);
927
0
  }
928
929
0
  LWDEBUGF(3, "lwcollection size = %zu", size);
930
931
0
  return size;
932
0
}
933
934
static size_t gserialized1_from_any_size(const LWGEOM *geom)
935
0
{
936
0
  LWDEBUGF(2, "Input type: %s", lwtype_name(geom->type));
937
938
0
  switch (geom->type)
939
0
  {
940
0
  case POINTTYPE:
941
0
    return gserialized1_from_lwpoint_size((LWPOINT *)geom);
942
0
  case LINETYPE:
943
0
    return gserialized1_from_lwline_size((LWLINE *)geom);
944
0
  case POLYGONTYPE:
945
0
    return gserialized1_from_lwpoly_size((LWPOLY *)geom);
946
0
  case TRIANGLETYPE:
947
0
    return gserialized1_from_lwtriangle_size((LWTRIANGLE *)geom);
948
0
  case CIRCSTRINGTYPE:
949
0
    return gserialized1_from_lwcircstring_size((LWCIRCSTRING *)geom);
950
0
  case CURVEPOLYTYPE:
951
0
  case COMPOUNDTYPE:
952
0
  case MULTIPOINTTYPE:
953
0
  case MULTILINETYPE:
954
0
  case MULTICURVETYPE:
955
0
  case MULTIPOLYGONTYPE:
956
0
  case MULTISURFACETYPE:
957
0
  case POLYHEDRALSURFACETYPE:
958
0
  case TINTYPE:
959
0
  case COLLECTIONTYPE:
960
0
    return gserialized1_from_lwcollection_size((LWCOLLECTION *)geom);
961
0
  default:
962
0
    lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type));
963
0
    return 0;
964
0
  }
965
0
}
966
967
/* Public function */
968
969
size_t gserialized1_from_lwgeom_size(const LWGEOM *geom)
970
0
{
971
0
  size_t size = 8; /* Header overhead. */
972
0
  assert(geom);
973
974
0
  if (geom->bbox)
975
0
    size += gbox_serialized_size(geom->flags);
976
977
0
  size += gserialized1_from_any_size(geom);
978
0
  LWDEBUGF(3, "%s size = %zu", __func__, size);
979
980
0
  return size;
981
0
}
982
983
/***********************************************************************
984
* Serialize an LWGEOM into GSERIALIZED.
985
*/
986
987
/* Private functions */
988
989
static size_t gserialized1_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf);
990
991
static size_t gserialized1_from_lwpoint(const LWPOINT *point, uint8_t *buf)
992
0
{
993
0
  uint8_t *loc;
994
0
  int ptsize = ptarray_point_size(point->point);
995
0
  int type = POINTTYPE;
996
997
0
  assert(point);
998
0
  assert(buf);
999
1000
0
  if ( FLAGS_GET_ZM(point->flags) != FLAGS_GET_ZM(point->point->flags) )
1001
0
    lwerror("Dimensions mismatch in lwpoint");
1002
1003
0
  LWDEBUGF(2, "%s (%p, %p) called", __func__, point, buf);
1004
1005
0
  loc = buf;
1006
1007
  /* Write in the type. */
1008
0
  memcpy(loc, &type, sizeof(uint32_t));
1009
0
  loc += sizeof(uint32_t);
1010
  /* Write in the number of points (0 => empty). */
1011
0
  memcpy(loc, &(point->point->npoints), sizeof(uint32_t));
1012
0
  loc += sizeof(uint32_t);
1013
1014
  /* Copy in the ordinates. */
1015
0
  if ( point->point->npoints > 0 )
1016
0
  {
1017
0
    memcpy(loc, getPoint_internal(point->point, 0), ptsize);
1018
0
    loc += ptsize;
1019
0
  }
1020
1021
0
  return (size_t)(loc - buf);
1022
0
}
1023
1024
static size_t gserialized1_from_lwline(const LWLINE *line, uint8_t *buf)
1025
0
{
1026
0
  uint8_t *loc;
1027
0
  int ptsize;
1028
0
  size_t size;
1029
0
  int type = LINETYPE;
1030
1031
0
  assert(line);
1032
0
  assert(buf);
1033
1034
0
  LWDEBUGF(2, "%s (%p, %p) called", __func__, line, buf);
1035
1036
0
  if ( FLAGS_GET_Z(line->flags) != FLAGS_GET_Z(line->points->flags) )
1037
0
    lwerror("Dimensions mismatch in lwline");
1038
1039
0
  ptsize = ptarray_point_size(line->points);
1040
1041
0
  loc = buf;
1042
1043
  /* Write in the type. */
1044
0
  memcpy(loc, &type, sizeof(uint32_t));
1045
0
  loc += sizeof(uint32_t);
1046
1047
  /* Write in the npoints. */
1048
0
  memcpy(loc, &(line->points->npoints), sizeof(uint32_t));
1049
0
  loc += sizeof(uint32_t);
1050
1051
0
  LWDEBUGF(3, "%s added npoints (%d)", __func__, line->points->npoints);
1052
1053
  /* Copy in the ordinates. */
1054
0
  if ( line->points->npoints > 0 )
1055
0
  {
1056
0
    size = (size_t)line->points->npoints * ptsize;
1057
0
    memcpy(loc, getPoint_internal(line->points, 0), size);
1058
0
    loc += size;
1059
0
  }
1060
0
  LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * line->points->npoints);
1061
1062
0
  return (size_t)(loc - buf);
1063
0
}
1064
1065
static size_t gserialized1_from_lwpoly(const LWPOLY *poly, uint8_t *buf)
1066
0
{
1067
0
  uint32_t i;
1068
0
  uint8_t *loc;
1069
0
  int ptsize;
1070
0
  int type = POLYGONTYPE;
1071
1072
0
  assert(poly);
1073
0
  assert(buf);
1074
1075
0
  LWDEBUGF(2, "%s called", __func__);
1076
1077
0
  ptsize = sizeof(double) * FLAGS_NDIMS(poly->flags);
1078
0
  loc = buf;
1079
1080
  /* Write in the type. */
1081
0
  memcpy(loc, &type, sizeof(uint32_t));
1082
0
  loc += sizeof(uint32_t);
1083
1084
  /* Write in the nrings. */
1085
0
  memcpy(loc, &(poly->nrings), sizeof(uint32_t));
1086
0
  loc += sizeof(uint32_t);
1087
1088
  /* Write in the npoints per ring. */
1089
0
  for ( i = 0; i < poly->nrings; i++ )
1090
0
  {
1091
0
    memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t));
1092
0
    loc += sizeof(uint32_t);
1093
0
  }
1094
1095
  /* Add in padding if necessary to remain double aligned. */
1096
0
  if ( poly->nrings % 2 )
1097
0
  {
1098
0
    memset(loc, 0, sizeof(uint32_t));
1099
0
    loc += sizeof(uint32_t);
1100
0
  }
1101
1102
  /* Copy in the ordinates. */
1103
0
  for ( i = 0; i < poly->nrings; i++ )
1104
0
  {
1105
0
    POINTARRAY *pa = poly->rings[i];
1106
0
    size_t pasize;
1107
1108
0
    if ( FLAGS_GET_ZM(poly->flags) != FLAGS_GET_ZM(pa->flags) )
1109
0
      lwerror("Dimensions mismatch in lwpoly");
1110
1111
0
    pasize = (size_t)pa->npoints * ptsize;
1112
0
    if ( pa->npoints > 0 )
1113
0
      memcpy(loc, getPoint_internal(pa, 0), pasize);
1114
0
    loc += pasize;
1115
0
  }
1116
0
  return (size_t)(loc - buf);
1117
0
}
1118
1119
static size_t gserialized1_from_lwtriangle(const LWTRIANGLE *triangle, uint8_t *buf)
1120
0
{
1121
0
  uint8_t *loc;
1122
0
  int ptsize;
1123
0
  size_t size;
1124
0
  int type = TRIANGLETYPE;
1125
1126
0
  assert(triangle);
1127
0
  assert(buf);
1128
1129
0
  LWDEBUGF(2, "%s (%p, %p) called", __func__, triangle, buf);
1130
1131
0
  if ( FLAGS_GET_ZM(triangle->flags) != FLAGS_GET_ZM(triangle->points->flags) )
1132
0
    lwerror("Dimensions mismatch in lwtriangle");
1133
1134
0
  ptsize = ptarray_point_size(triangle->points);
1135
1136
0
  loc = buf;
1137
1138
  /* Write in the type. */
1139
0
  memcpy(loc, &type, sizeof(uint32_t));
1140
0
  loc += sizeof(uint32_t);
1141
1142
  /* Write in the npoints. */
1143
0
  memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t));
1144
0
  loc += sizeof(uint32_t);
1145
1146
0
  LWDEBUGF(3, "%s added npoints (%d)", __func__, triangle->points->npoints);
1147
1148
  /* Copy in the ordinates. */
1149
0
  if ( triangle->points->npoints > 0 )
1150
0
  {
1151
0
    size = (size_t)triangle->points->npoints * ptsize;
1152
0
    memcpy(loc, getPoint_internal(triangle->points, 0), size);
1153
0
    loc += size;
1154
0
  }
1155
0
  LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * triangle->points->npoints);
1156
1157
0
  return (size_t)(loc - buf);
1158
0
}
1159
1160
static size_t gserialized1_from_lwcircstring(const LWCIRCSTRING *curve, uint8_t *buf)
1161
0
{
1162
0
  uint8_t *loc;
1163
0
  int ptsize;
1164
0
  size_t size;
1165
0
  int type = CIRCSTRINGTYPE;
1166
1167
0
  assert(curve);
1168
0
  assert(buf);
1169
1170
0
  if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags))
1171
0
    lwerror("Dimensions mismatch in lwcircstring");
1172
1173
1174
0
  ptsize = ptarray_point_size(curve->points);
1175
0
  loc = buf;
1176
1177
  /* Write in the type. */
1178
0
  memcpy(loc, &type, sizeof(uint32_t));
1179
0
  loc += sizeof(uint32_t);
1180
1181
  /* Write in the npoints. */
1182
0
  memcpy(loc, &curve->points->npoints, sizeof(uint32_t));
1183
0
  loc += sizeof(uint32_t);
1184
1185
  /* Copy in the ordinates. */
1186
0
  if ( curve->points->npoints > 0 )
1187
0
  {
1188
0
    size = (size_t)curve->points->npoints * ptsize;
1189
0
    memcpy(loc, getPoint_internal(curve->points, 0), size);
1190
0
    loc += size;
1191
0
  }
1192
1193
0
  return (size_t)(loc - buf);
1194
0
}
1195
1196
static size_t gserialized1_from_lwcollection(const LWCOLLECTION *coll, uint8_t *buf)
1197
0
{
1198
0
  size_t subsize = 0;
1199
0
  uint8_t *loc;
1200
0
  uint32_t i;
1201
0
  int type;
1202
1203
0
  assert(coll);
1204
0
  assert(buf);
1205
1206
0
  type = coll->type;
1207
0
  loc = buf;
1208
1209
  /* Write in the type. */
1210
0
  memcpy(loc, &type, sizeof(uint32_t));
1211
0
  loc += sizeof(uint32_t);
1212
1213
  /* Write in the number of subgeoms. */
1214
0
  memcpy(loc, &coll->ngeoms, sizeof(uint32_t));
1215
0
  loc += sizeof(uint32_t);
1216
1217
  /* Serialize subgeoms. */
1218
0
  for ( i=0; i<coll->ngeoms; i++ )
1219
0
  {
1220
0
    if (FLAGS_GET_ZM(coll->flags) != FLAGS_GET_ZM(coll->geoms[i]->flags))
1221
0
      lwerror("Dimensions mismatch in lwcollection");
1222
0
    subsize = gserialized1_from_lwgeom_any(coll->geoms[i], loc);
1223
0
    loc += subsize;
1224
0
  }
1225
1226
0
  return (size_t)(loc - buf);
1227
0
}
1228
1229
static size_t gserialized1_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf)
1230
0
{
1231
0
  assert(geom);
1232
0
  assert(buf);
1233
1234
0
  LWDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d",
1235
0
    geom->type, lwtype_name(geom->type),
1236
0
    FLAGS_GET_Z(geom->flags), FLAGS_GET_M(geom->flags));
1237
0
  LWDEBUGF(2, "LWGEOM(%p) uint8_t(%p)", geom, buf);
1238
1239
0
  switch (geom->type)
1240
0
  {
1241
0
  case POINTTYPE:
1242
0
    return gserialized1_from_lwpoint((LWPOINT *)geom, buf);
1243
0
  case LINETYPE:
1244
0
    return gserialized1_from_lwline((LWLINE *)geom, buf);
1245
0
  case POLYGONTYPE:
1246
0
    return gserialized1_from_lwpoly((LWPOLY *)geom, buf);
1247
0
  case TRIANGLETYPE:
1248
0
    return gserialized1_from_lwtriangle((LWTRIANGLE *)geom, buf);
1249
0
  case CIRCSTRINGTYPE:
1250
0
    return gserialized1_from_lwcircstring((LWCIRCSTRING *)geom, buf);
1251
0
  case CURVEPOLYTYPE:
1252
0
  case COMPOUNDTYPE:
1253
0
  case MULTIPOINTTYPE:
1254
0
  case MULTILINETYPE:
1255
0
  case MULTICURVETYPE:
1256
0
  case MULTIPOLYGONTYPE:
1257
0
  case MULTISURFACETYPE:
1258
0
  case POLYHEDRALSURFACETYPE:
1259
0
  case TINTYPE:
1260
0
  case COLLECTIONTYPE:
1261
0
    return gserialized1_from_lwcollection((LWCOLLECTION *)geom, buf);
1262
0
  default:
1263
0
    lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type));
1264
0
    return 0;
1265
0
  }
1266
0
  return 0;
1267
0
}
1268
1269
static size_t gserialized1_from_gbox(const GBOX *gbox, uint8_t *buf)
1270
0
{
1271
0
  uint8_t *loc = buf;
1272
0
  float f;
1273
0
  size_t return_size;
1274
1275
0
  assert(buf);
1276
1277
0
  f = next_float_down(gbox->xmin);
1278
0
  memcpy(loc, &f, sizeof(float));
1279
0
  loc += sizeof(float);
1280
1281
0
  f = next_float_up(gbox->xmax);
1282
0
  memcpy(loc, &f, sizeof(float));
1283
0
  loc += sizeof(float);
1284
1285
0
  f = next_float_down(gbox->ymin);
1286
0
  memcpy(loc, &f, sizeof(float));
1287
0
  loc += sizeof(float);
1288
1289
0
  f = next_float_up(gbox->ymax);
1290
0
  memcpy(loc, &f, sizeof(float));
1291
0
  loc += sizeof(float);
1292
1293
0
  if ( FLAGS_GET_GEODETIC(gbox->flags) )
1294
0
  {
1295
0
    f = next_float_down(gbox->zmin);
1296
0
    memcpy(loc, &f, sizeof(float));
1297
0
    loc += sizeof(float);
1298
1299
0
    f = next_float_up(gbox->zmax);
1300
0
    memcpy(loc, &f, sizeof(float));
1301
0
    loc += sizeof(float);
1302
1303
0
    return_size = (size_t)(loc - buf);
1304
0
    LWDEBUGF(4, "returning size %zu", return_size);
1305
0
    return return_size;
1306
0
  }
1307
1308
0
  if ( FLAGS_GET_Z(gbox->flags) )
1309
0
  {
1310
0
    f = next_float_down(gbox->zmin);
1311
0
    memcpy(loc, &f, sizeof(float));
1312
0
    loc += sizeof(float);
1313
1314
0
    f = next_float_up(gbox->zmax);
1315
0
    memcpy(loc, &f, sizeof(float));
1316
0
    loc += sizeof(float);
1317
1318
0
  }
1319
1320
0
  if ( FLAGS_GET_M(gbox->flags) )
1321
0
  {
1322
0
    f = next_float_down(gbox->mmin);
1323
0
    memcpy(loc, &f, sizeof(float));
1324
0
    loc += sizeof(float);
1325
1326
0
    f = next_float_up(gbox->mmax);
1327
0
    memcpy(loc, &f, sizeof(float));
1328
0
    loc += sizeof(float);
1329
0
  }
1330
0
  return_size = (size_t)(loc - buf);
1331
0
  LWDEBUGF(4, "returning size %zu", return_size);
1332
0
  return return_size;
1333
0
}
1334
1335
/* Public function */
1336
1337
GSERIALIZED* gserialized1_from_lwgeom(LWGEOM *geom, size_t *size)
1338
0
{
1339
0
  size_t expected_size = 0;
1340
0
  size_t return_size = 0;
1341
0
  uint8_t *serialized = NULL;
1342
0
  uint8_t *ptr = NULL;
1343
0
  GSERIALIZED *g = NULL;
1344
0
  assert(geom);
1345
1346
  /*
1347
  ** See if we need a bounding box, add one if we don't have one.
1348
  */
1349
0
  if ( (! geom->bbox) && lwgeom_needs_bbox(geom) && (!lwgeom_is_empty(geom)) )
1350
0
  {
1351
0
    lwgeom_add_bbox(geom);
1352
0
  }
1353
1354
  /*
1355
  ** Harmonize the flags to the state of the lwgeom
1356
  */
1357
0
  if ( geom->bbox )
1358
0
    FLAGS_SET_BBOX(geom->flags, 1);
1359
0
  else
1360
0
    FLAGS_SET_BBOX(geom->flags, 0);
1361
1362
  /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */
1363
0
  expected_size = gserialized1_from_lwgeom_size(geom);
1364
0
  serialized = lwalloc(expected_size);
1365
0
  ptr = serialized;
1366
1367
  /* Move past size, srid and flags. */
1368
0
  ptr += 8;
1369
1370
  /* Write in the serialized form of the gbox, if necessary. */
1371
0
  if ( geom->bbox )
1372
0
    ptr += gserialized1_from_gbox(geom->bbox, ptr);
1373
1374
  /* Write in the serialized form of the geometry. */
1375
0
  ptr += gserialized1_from_lwgeom_any(geom, ptr);
1376
1377
  /* Calculate size as returned by data processing functions. */
1378
0
  return_size = ptr - serialized;
1379
1380
0
  if ( expected_size != return_size ) /* Uh oh! */
1381
0
  {
1382
0
    lwerror("Return size (%zu) not equal to expected size (%zu)!", return_size, expected_size);
1383
0
    return NULL;
1384
0
  }
1385
1386
0
  if ( size ) /* Return the output size to the caller if necessary. */
1387
0
    *size = return_size;
1388
1389
0
  g = (GSERIALIZED*)serialized;
1390
1391
  /*
1392
  ** We are aping PgSQL code here, PostGIS code should use
1393
  ** VARSIZE to set this for real.
1394
  */
1395
0
  g->size = return_size << 2;
1396
1397
  /* Set the SRID! */
1398
0
  gserialized1_set_srid(g, geom->srid);
1399
1400
0
  g->gflags = lwflags_get_g1flags(geom->flags);
1401
1402
0
  return g;
1403
0
}
1404
1405
/***********************************************************************
1406
* De-serialize GSERIALIZED into an LWGEOM.
1407
*/
1408
1409
static LWGEOM* lwgeom_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size);
1410
1411
static LWPOINT* lwpoint_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1412
0
{
1413
0
  uint8_t *start_ptr = data_ptr;
1414
0
  LWPOINT *point;
1415
0
  uint32_t npoints = 0;
1416
1417
0
  assert(data_ptr);
1418
1419
0
  point = (LWPOINT*)lwalloc(sizeof(LWPOINT));
1420
0
  point->srid = SRID_UNKNOWN; /* Default */
1421
0
  point->bbox = NULL;
1422
0
  point->type = POINTTYPE;
1423
0
  point->flags = lwflags;
1424
1425
0
  data_ptr += 4; /* Skip past the type. */
1426
0
  npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1427
0
  data_ptr += 4; /* Skip past the npoints. */
1428
1429
0
  if ( npoints > 0 )
1430
0
    point->point = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 1, data_ptr);
1431
0
  else
1432
0
    point->point = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty point */
1433
1434
0
  data_ptr += sizeof(double) * npoints * FLAGS_NDIMS(lwflags);
1435
1436
0
  if ( size )
1437
0
    *size = data_ptr - start_ptr;
1438
1439
0
  return point;
1440
0
}
1441
1442
static LWLINE* lwline_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1443
0
{
1444
0
  uint8_t *start_ptr = data_ptr;
1445
0
  LWLINE *line;
1446
0
  uint32_t npoints = 0;
1447
1448
0
  assert(data_ptr);
1449
1450
0
  line = (LWLINE*)lwalloc(sizeof(LWLINE));
1451
0
  line->srid = SRID_UNKNOWN; /* Default */
1452
0
  line->bbox = NULL;
1453
0
  line->type = LINETYPE;
1454
0
  line->flags = lwflags;
1455
1456
0
  data_ptr += 4; /* Skip past the type. */
1457
0
  npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1458
0
  data_ptr += 4; /* Skip past the npoints. */
1459
1460
0
  if ( npoints > 0 )
1461
0
    line->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1462
1463
0
  else
1464
0
    line->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty linestring */
1465
1466
0
  data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1467
1468
0
  if ( size )
1469
0
    *size = data_ptr - start_ptr;
1470
1471
0
  return line;
1472
0
}
1473
1474
static LWPOLY* lwpoly_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1475
0
{
1476
0
  uint8_t *start_ptr = data_ptr;
1477
0
  LWPOLY *poly;
1478
0
  uint8_t *ordinate_ptr;
1479
0
  uint32_t nrings = 0;
1480
0
  uint32_t i = 0;
1481
1482
0
  assert(data_ptr);
1483
1484
0
  poly = (LWPOLY*)lwalloc(sizeof(LWPOLY));
1485
0
  poly->srid = SRID_UNKNOWN; /* Default */
1486
0
  poly->bbox = NULL;
1487
0
  poly->type = POLYGONTYPE;
1488
0
  poly->flags = lwflags;
1489
1490
0
  data_ptr += 4; /* Skip past the polygontype. */
1491
0
  nrings = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1492
0
  poly->nrings = nrings;
1493
0
  LWDEBUGF(4, "nrings = %d", nrings);
1494
0
  data_ptr += 4; /* Skip past the nrings. */
1495
1496
0
  ordinate_ptr = data_ptr; /* Start the ordinate pointer. */
1497
0
  if ( nrings > 0)
1498
0
  {
1499
0
    poly->rings = (POINTARRAY**)lwalloc( sizeof(POINTARRAY*) * nrings );
1500
0
    poly->maxrings = nrings;
1501
0
    ordinate_ptr += nrings * 4; /* Move past all the npoints values. */
1502
0
    if ( nrings % 2 ) /* If there is padding, move past that too. */
1503
0
      ordinate_ptr += 4;
1504
0
  }
1505
0
  else /* Empty polygon */
1506
0
  {
1507
0
    poly->rings = NULL;
1508
0
    poly->maxrings = 0;
1509
0
  }
1510
1511
0
  for ( i = 0; i < nrings; i++ )
1512
0
  {
1513
0
    uint32_t npoints = 0;
1514
1515
    /* Read in the number of points. gserialized1_validate_geometry_buffer() has
1516
     * already bounds-checked every ring's declared point count against the
1517
     * remaining buffer and rejected overflowing byte sizes; a ring with fewer
1518
     * than four points is geometrically invalid but not a memory-safety
1519
     * problem, and PostGIS intentionally stores and reports such rings (see
1520
     * Trac #408, #4470). */
1521
0
    npoints = gserialized1_get_uint32_t(data_ptr);
1522
0
    data_ptr += 4;
1523
1524
    /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */
1525
0
    poly->rings[i] = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, ordinate_ptr);
1526
1527
0
    ordinate_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1528
0
  }
1529
1530
0
  if ( size )
1531
0
    *size = ordinate_ptr - start_ptr;
1532
1533
0
  return poly;
1534
0
}
1535
1536
static LWTRIANGLE* lwtriangle_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1537
0
{
1538
0
  uint8_t *start_ptr = data_ptr;
1539
0
  LWTRIANGLE *triangle;
1540
0
  uint32_t npoints = 0;
1541
1542
0
  assert(data_ptr);
1543
1544
0
  triangle = (LWTRIANGLE*)lwalloc(sizeof(LWTRIANGLE));
1545
0
  triangle->srid = SRID_UNKNOWN; /* Default */
1546
0
  triangle->bbox = NULL;
1547
0
  triangle->type = TRIANGLETYPE;
1548
0
  triangle->flags = lwflags;
1549
1550
0
  data_ptr += 4; /* Skip past the type. */
1551
0
  npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1552
0
  data_ptr += 4; /* Skip past the npoints. */
1553
1554
0
  if ( npoints > 0 )
1555
0
    triangle->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1556
0
  else
1557
0
    triangle->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty triangle */
1558
1559
0
  data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1560
1561
0
  if ( size )
1562
0
    *size = data_ptr - start_ptr;
1563
1564
0
  return triangle;
1565
0
}
1566
1567
static LWCIRCSTRING* lwcircstring_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1568
0
{
1569
0
  uint8_t *start_ptr = data_ptr;
1570
0
  LWCIRCSTRING *circstring;
1571
0
  uint32_t npoints = 0;
1572
1573
0
  assert(data_ptr);
1574
1575
0
  circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING));
1576
0
  circstring->srid = SRID_UNKNOWN; /* Default */
1577
0
  circstring->bbox = NULL;
1578
0
  circstring->type = CIRCSTRINGTYPE;
1579
0
  circstring->flags = lwflags;
1580
1581
0
  data_ptr += 4; /* Skip past the circstringtype. */
1582
0
  npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1583
0
  data_ptr += 4; /* Skip past the npoints. */
1584
1585
0
  if ( npoints > 0 )
1586
0
    circstring->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1587
0
  else
1588
0
    circstring->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty circularstring */
1589
1590
0
  data_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1591
1592
0
  if ( size )
1593
0
    *size = data_ptr - start_ptr;
1594
1595
0
  return circstring;
1596
0
}
1597
1598
static LWCOLLECTION* lwcollection_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1599
0
{
1600
0
  uint32_t type;
1601
0
  uint8_t *start_ptr = data_ptr;
1602
0
  LWCOLLECTION *collection;
1603
0
  uint32_t ngeoms = 0;
1604
0
  uint32_t i = 0;
1605
1606
0
  assert(data_ptr);
1607
1608
0
  type = gserialized1_get_uint32_t(data_ptr);
1609
0
  data_ptr += 4; /* Skip past the type. */
1610
1611
0
  collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION));
1612
0
  collection->srid = SRID_UNKNOWN; /* Default */
1613
0
  collection->bbox = NULL;
1614
0
  collection->type = type;
1615
0
  collection->flags = lwflags;
1616
1617
0
  ngeoms = gserialized1_get_uint32_t(data_ptr);
1618
0
  collection->ngeoms = ngeoms; /* Zero => empty geometry */
1619
0
  data_ptr += 4; /* Skip past the ngeoms. */
1620
1621
0
  if ( ngeoms > 0 )
1622
0
  {
1623
0
    collection->geoms = lwalloc(sizeof(LWGEOM*) * ngeoms);
1624
0
    collection->maxgeoms = ngeoms;
1625
0
  }
1626
0
  else
1627
0
  {
1628
0
    collection->geoms = NULL;
1629
0
    collection->maxgeoms = 0;
1630
0
  }
1631
1632
  /* Sub-geometries are never de-serialized with boxes (#1254) */
1633
0
  FLAGS_SET_BBOX(lwflags, 0);
1634
1635
0
  for ( i = 0; i < ngeoms; i++ )
1636
0
  {
1637
0
    uint32_t subtype = gserialized1_get_uint32_t(data_ptr);
1638
0
    size_t subsize = 0;
1639
1640
0
    if ( ! lwcollection_allows_subtype(type, subtype) )
1641
0
    {
1642
0
      lwerror("Invalid subtype (%s) for collection type (%s)", lwtype_name(subtype), lwtype_name(type));
1643
0
      lwfree(collection);
1644
0
      return NULL;
1645
0
    }
1646
0
    collection->geoms[i] = lwgeom_from_gserialized1_buffer(data_ptr, lwflags, &subsize);
1647
0
    data_ptr += subsize;
1648
0
  }
1649
1650
0
  if ( size )
1651
0
    *size = data_ptr - start_ptr;
1652
1653
0
  return collection;
1654
0
}
1655
1656
LWGEOM* lwgeom_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *g_size)
1657
0
{
1658
0
  uint32_t type;
1659
1660
0
  assert(data_ptr);
1661
1662
0
  type = gserialized1_get_uint32_t(data_ptr);
1663
1664
0
  LWDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, lwtype_name(type),
1665
0
    FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), FLAGS_GET_GEODETIC(lwflags), FLAGS_GET_BBOX(lwflags));
1666
1667
0
  switch (type)
1668
0
  {
1669
0
  case POINTTYPE:
1670
0
    return (LWGEOM *)lwpoint_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1671
0
  case LINETYPE:
1672
0
    return (LWGEOM *)lwline_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1673
0
  case CIRCSTRINGTYPE:
1674
0
    return (LWGEOM *)lwcircstring_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1675
0
  case POLYGONTYPE:
1676
0
    return (LWGEOM *)lwpoly_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1677
0
  case TRIANGLETYPE:
1678
0
    return (LWGEOM *)lwtriangle_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1679
0
  case MULTIPOINTTYPE:
1680
0
  case MULTILINETYPE:
1681
0
  case MULTIPOLYGONTYPE:
1682
0
  case COMPOUNDTYPE:
1683
0
  case CURVEPOLYTYPE:
1684
0
  case MULTICURVETYPE:
1685
0
  case MULTISURFACETYPE:
1686
0
  case POLYHEDRALSURFACETYPE:
1687
0
  case TINTYPE:
1688
0
  case COLLECTIONTYPE:
1689
0
    return (LWGEOM *)lwcollection_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1690
0
  default:
1691
0
    lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type));
1692
0
    return NULL;
1693
0
  }
1694
0
}
1695
1696
LWGEOM* lwgeom_from_gserialized1(const GSERIALIZED *g)
1697
0
{
1698
0
  lwflags_t lwflags = 0;
1699
0
  int32_t srid = 0;
1700
0
  uint32_t lwtype = 0;
1701
0
  uint8_t *data_ptr = NULL;
1702
0
  LWGEOM *lwgeom = NULL;
1703
0
  GBOX bbox;
1704
0
  size_t size = 0;
1705
1706
0
  assert(g);
1707
1708
0
  srid = gserialized1_get_srid(g);
1709
0
  lwflags = gserialized1_get_lwflags(g);
1710
1711
0
  if (gserialized1_payload_bounds(g, &data_ptr, NULL) == LW_FAILURE)
1712
0
  {
1713
0
    lwerror("%s: invalid GSERIALIZED header size", __func__);
1714
0
    return NULL;
1715
0
  }
1716
0
  if (gserialized1_validate_geometry_buffer(
1717
0
    data_ptr, (uint8_t *)g + gserialized1_buffer_size(g), lwflags, NULL) == LW_FAILURE)
1718
0
    return NULL;
1719
0
  lwtype = gserialized1_get_type(g);
1720
1721
0
  LWDEBUGF(4, "Got type %d (%s), srid=%d", lwtype, lwtype_name(lwtype), srid);
1722
1723
0
  lwgeom = lwgeom_from_gserialized1_buffer(data_ptr, lwflags, &size);
1724
1725
0
  if ( ! lwgeom )
1726
0
  {
1727
0
    lwerror("%s: unable create geometry", __func__); /* Ooops! */
1728
0
    return NULL;
1729
0
  }
1730
1731
0
  lwgeom->type = lwtype;
1732
0
  lwgeom->flags = lwflags;
1733
1734
0
  if ( gserialized1_read_gbox_p(g, &bbox) == LW_SUCCESS )
1735
0
  {
1736
0
    lwgeom->bbox = gbox_copy(&bbox);
1737
0
  }
1738
0
  else if ( lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS) )
1739
0
  {
1740
0
    lwgeom->bbox = gbox_copy(&bbox);
1741
0
  }
1742
0
  else
1743
0
  {
1744
0
    lwgeom->bbox = NULL;
1745
0
  }
1746
1747
0
  lwgeom_set_srid(lwgeom, srid);
1748
1749
0
  return lwgeom;
1750
0
}
1751
1752
const float * gserialized1_get_float_box_p(const GSERIALIZED *g, size_t *ndims)
1753
0
{
1754
0
  if (ndims)
1755
0
    *ndims = G1FLAGS_NDIMS_BOX(g->gflags);
1756
0
  if (!g) return NULL;
1757
0
  if (!G1FLAGS_GET_BBOX(g->gflags)) return NULL;
1758
0
  if (gserialized1_buffer_size(g) < offsetof(GSERIALIZED, data) + gserialized1_box_size(g))
1759
0
    return NULL;
1760
0
  return (const float *)(g->data);
1761
0
}
1762
1763
/**
1764
* Update the bounding box of a #GSERIALIZED, allocating a fresh one
1765
* if there is not enough space to just write the new box in.
1766
* <em>WARNING</em> if a new object needs to be created, the
1767
* input pointer will have to be freed by the caller! Check
1768
* to see if input == output. Returns null if there's a problem
1769
* like mismatched dimensions.
1770
*/
1771
GSERIALIZED* gserialized1_set_gbox(GSERIALIZED *g, GBOX *gbox)
1772
0
{
1773
1774
0
  int g_ndims = G1FLAGS_NDIMS_BOX(g->gflags);
1775
0
  int box_ndims = FLAGS_NDIMS_BOX(gbox->flags);
1776
0
  GSERIALIZED *g_out = NULL;
1777
0
  size_t box_size = 2 * g_ndims * sizeof(float);
1778
0
  float *fbox;
1779
0
  int fbox_pos = 0;
1780
1781
  /* The dimensionality of the inputs has to match or we are SOL. */
1782
0
  if ( g_ndims != box_ndims )
1783
0
  {
1784
0
    return NULL;
1785
0
  }
1786
1787
  /* Serialized already has room for a box. */
1788
0
  if (G1FLAGS_GET_BBOX(g->gflags))
1789
0
  {
1790
0
    g_out = g;
1791
0
  }
1792
  /* Serialized has no box. We need to allocate enough space for the old
1793
     data plus the box, and leave a gap in the memory segment to write
1794
     the new values into.
1795
  */
1796
0
  else
1797
0
  {
1798
0
    size_t varsize_new = LWSIZE_GET(g->size) + box_size;
1799
0
    uint8_t *ptr;
1800
0
    g_out = lwalloc(varsize_new);
1801
    /* Copy the head of g into place */
1802
0
    memcpy(g_out, g, 8);
1803
    /* Copy the body of g into place after leaving space for the box */
1804
0
    ptr = g_out->data;
1805
0
    ptr += box_size;
1806
0
    memcpy(ptr, g->data, LWSIZE_GET(g->size) - 8);
1807
0
    G1FLAGS_SET_BBOX(g_out->gflags, 1);
1808
0
    LWSIZE_SET(g_out->size, varsize_new);
1809
0
  }
1810
1811
  /* Move bounds to nearest float values */
1812
0
  gbox_float_round(gbox);
1813
  /* Now write the float box values into the memory segment */
1814
0
  fbox = (float*)(g_out->data);
1815
  /* Copy in X/Y */
1816
0
  fbox[fbox_pos++] = gbox->xmin;
1817
0
  fbox[fbox_pos++] = gbox->xmax;
1818
0
  fbox[fbox_pos++] = gbox->ymin;
1819
0
  fbox[fbox_pos++] = gbox->ymax;
1820
  /* Optionally copy in higher dims */
1821
0
  if(gserialized1_has_z(g) || gserialized1_is_geodetic(g))
1822
0
  {
1823
0
    fbox[fbox_pos++] = gbox->zmin;
1824
0
    fbox[fbox_pos++] = gbox->zmax;
1825
0
  }
1826
0
  if(gserialized1_has_m(g) && ! gserialized1_is_geodetic(g))
1827
0
  {
1828
0
    fbox[fbox_pos++] = gbox->mmin;
1829
0
    fbox[fbox_pos++] = gbox->mmax;
1830
0
  }
1831
1832
0
  return g_out;
1833
0
}
1834
1835
1836
/**
1837
* Remove the bounding box from a #GSERIALIZED. Returns a freshly
1838
* allocated #GSERIALIZED every time.
1839
*/
1840
GSERIALIZED* gserialized1_drop_gbox(GSERIALIZED *g)
1841
0
{
1842
0
  int g_ndims = G1FLAGS_NDIMS_BOX(g->gflags);
1843
0
  size_t box_size = 2 * g_ndims * sizeof(float);
1844
0
  size_t g_out_size = LWSIZE_GET(g->size) - box_size;
1845
0
  GSERIALIZED *g_out = lwalloc(g_out_size);
1846
1847
  /* Copy the contents while omitting the box */
1848
0
  if ( G1FLAGS_GET_BBOX(g->gflags) )
1849
0
  {
1850
0
    uint8_t *outptr = (uint8_t*)g_out;
1851
0
    uint8_t *inptr = (uint8_t*)g;
1852
    /* Copy the header (size+type) of g into place */
1853
0
    memcpy(outptr, inptr, 8);
1854
0
    outptr += 8;
1855
0
    inptr += 8 + box_size;
1856
    /* Copy parts after the box into place */
1857
0
    memcpy(outptr, inptr, g_out_size - 8);
1858
0
    G1FLAGS_SET_BBOX(g_out->gflags, 0);
1859
0
    LWSIZE_SET(g_out->size, g_out_size);
1860
0
  }
1861
  /* No box? Nothing to do but copy and return. */
1862
0
  else
1863
0
  {
1864
0
    memcpy(g_out, g, g_out_size);
1865
0
  }
1866
1867
0
  return g_out;
1868
0
}