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