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