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