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