/src/postgis/liblwgeom/gserialized.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * PostGIS - Spatial Types for PostgreSQL |
4 | | * http://postgis.net |
5 | | * |
6 | | * PostGIS is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation, either version 2 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * PostGIS is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with PostGIS. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | ********************************************************************** |
20 | | * |
21 | | * Copyright 2022 Loïc Bartoletti <loic.bartoletti@oslandia.com> |
22 | | * Copyright 2019 Darafei Praliaskouski <me@komzpa.net> |
23 | | * Copyright 2019-2020 Raúl Marín <git@rmr.ninja> |
24 | | * Copyright 2019 Regina Obe <lr@pcorp.us> |
25 | | * Copyright 2019 Paul Ramsey <pramsey@cleverelephant.ca> |
26 | | * Copyright 2004-2024 Sandro Santilli <strk@kbt.io> |
27 | | * |
28 | | **********************************************************************/ |
29 | | |
30 | | #include "liblwgeom_internal.h" |
31 | | #include "gserialized1.h" |
32 | | #include "gserialized2.h" |
33 | | |
34 | | /* First four bits don't change between v0 and v1 */ |
35 | 0 | #define GFLAG_Z 0x01 |
36 | 0 | #define GFLAG_M 0x02 |
37 | 0 | #define GFLAG_BBOX 0x04 |
38 | 0 | #define GFLAG_GEODETIC 0x08 |
39 | | /* v1 and v2 MUST share the same version bits */ |
40 | 0 | #define GFLAG_VER_0 0x40 |
41 | 0 | #define GFLAGS_GET_VERSION(gflags) (((gflags) & GFLAG_VER_0)>>6) |
42 | | |
43 | | /** |
44 | | * Read the flags from a #GSERIALIZED and return a standard lwflag |
45 | | * integer |
46 | | */ |
47 | | lwflags_t gserialized_get_lwflags(const GSERIALIZED *g) |
48 | 0 | { |
49 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
50 | 0 | return gserialized2_get_lwflags(g); |
51 | 0 | else |
52 | 0 | return gserialized1_get_lwflags(g); |
53 | 0 | } |
54 | | |
55 | | /** |
56 | | * Copy a new bounding box into an existing gserialized. |
57 | | * If necessary a new #GSERIALIZED will be allocated. Test |
58 | | * that input != output before freeing input. |
59 | | */ |
60 | | GSERIALIZED *gserialized_set_gbox(GSERIALIZED *g, GBOX *gbox) |
61 | 0 | { |
62 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
63 | 0 | return gserialized2_set_gbox(g, gbox); |
64 | 0 | else |
65 | 0 | return gserialized1_set_gbox(g, gbox); |
66 | 0 | } |
67 | | |
68 | | /** |
69 | | * Return the serialization version |
70 | | */ |
71 | | uint32_t gserialized_get_version(const GSERIALIZED *g) |
72 | 0 | { |
73 | 0 | return GFLAGS_GET_VERSION(g->gflags); |
74 | 0 | } |
75 | | |
76 | | |
77 | | /** |
78 | | * Remove the bounding box from a #GSERIALIZED. Returns a freshly |
79 | | * allocated #GSERIALIZED every time. |
80 | | */ |
81 | | GSERIALIZED* gserialized_drop_gbox(GSERIALIZED *g) |
82 | 0 | { |
83 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
84 | 0 | return gserialized2_drop_gbox(g); |
85 | 0 | else |
86 | 0 | return gserialized1_drop_gbox(g); |
87 | 0 | } |
88 | | |
89 | | /** |
90 | | * Read the box from the #GSERIALIZED or calculate it if necessary. |
91 | | * Return #LWFAILURE if box cannot be calculated (NULL or EMPTY |
92 | | * input). |
93 | | */ |
94 | | int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox) |
95 | 0 | { |
96 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
97 | 0 | return gserialized2_get_gbox_p(g, gbox); |
98 | 0 | else |
99 | 0 | return gserialized1_get_gbox_p(g, gbox); |
100 | 0 | } |
101 | | |
102 | | /** |
103 | | * Read the box from the #GSERIALIZED or return #LWFAILURE if |
104 | | * box is unavailable. |
105 | | */ |
106 | | int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *gbox) |
107 | 0 | { |
108 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
109 | 0 | return gserialized2_fast_gbox_p(g, gbox); |
110 | 0 | else |
111 | 0 | return gserialized1_fast_gbox_p(g, gbox); |
112 | 0 | } |
113 | | |
114 | | /** |
115 | | * Extract the geometry type from the serialized form (it hides in |
116 | | * the anonymous data area, so this is a handy function). |
117 | | */ |
118 | | uint32_t gserialized_get_type(const GSERIALIZED *g) |
119 | 0 | { |
120 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
121 | 0 | return gserialized2_get_type(g); |
122 | 0 | else |
123 | 0 | return gserialized1_get_type(g); |
124 | 0 | } |
125 | | |
126 | | /** |
127 | | * Returns the size in bytes to read from toast to get the basic |
128 | | * information from a geometry: GSERIALIZED struct, bbox and type |
129 | | */ |
130 | | uint32_t gserialized_max_header_size(void) |
131 | 0 | { |
132 | 0 | size_t sz1 = gserialized1_max_header_size(); |
133 | 0 | size_t sz2 = gserialized2_max_header_size(); |
134 | 0 | return sz1 > sz2 ? sz1 : sz2; |
135 | 0 | } |
136 | | |
137 | | /** |
138 | | * Returns a hash code for the srid/type/geometry information |
139 | | * in the GSERIALIZED. Ignores metadata like flags and optional |
140 | | * boxes, etc. |
141 | | */ |
142 | | int32_t |
143 | | gserialized_hash(const GSERIALIZED *g) |
144 | 0 | { |
145 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
146 | 0 | return gserialized2_hash(g); |
147 | 0 | else |
148 | 0 | return gserialized1_hash(g); |
149 | 0 | } |
150 | | |
151 | | /** |
152 | | * Extract the SRID from the serialized form (it is packed into |
153 | | * three bytes so this is a handy function). |
154 | | */ |
155 | | int32_t gserialized_get_srid(const GSERIALIZED *g) |
156 | 0 | { |
157 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
158 | 0 | return gserialized2_get_srid(g); |
159 | 0 | else |
160 | 0 | return gserialized1_get_srid(g); |
161 | 0 | } |
162 | | |
163 | | /** |
164 | | * Write the SRID into the serialized form (it is packed into |
165 | | * three bytes so this is a handy function). |
166 | | */ |
167 | | void gserialized_set_srid(GSERIALIZED *g, int32_t srid) |
168 | 0 | { |
169 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
170 | 0 | gserialized2_set_srid(g, srid); |
171 | 0 | else |
172 | 0 | gserialized1_set_srid(g, srid); |
173 | 0 | } |
174 | | |
175 | | /** |
176 | | * Check if a #GSERIALIZED is empty without deserializing first. |
177 | | * Only checks if the number of elements of the parent geometry |
178 | | * is zero, will not catch collections of empty, eg: |
179 | | * GEOMETRYCOLLECTION(POINT EMPTY) |
180 | | */ |
181 | | int gserialized_is_empty(const GSERIALIZED *g) |
182 | 0 | { |
183 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
184 | 0 | return gserialized2_is_empty(g); |
185 | 0 | else |
186 | 0 | return gserialized1_is_empty(g); |
187 | 0 | } |
188 | | |
189 | | /** |
190 | | * Check if a #GSERIALIZED has a bounding box without deserializing first. |
191 | | */ |
192 | | int gserialized_has_bbox(const GSERIALIZED *g) |
193 | 0 | { |
194 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
195 | 0 | return gserialized2_has_bbox(g); |
196 | 0 | else |
197 | 0 | return gserialized1_has_bbox(g); |
198 | 0 | } |
199 | | |
200 | | /** |
201 | | * Check if a #GSERIALIZED has a Z ordinate. |
202 | | */ |
203 | | int gserialized_has_z(const GSERIALIZED *g) |
204 | 0 | { |
205 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
206 | 0 | return gserialized2_has_z(g); |
207 | 0 | else |
208 | 0 | return gserialized1_has_z(g); |
209 | 0 | } |
210 | | |
211 | | /** |
212 | | * Check if a #GSERIALIZED has an M ordinate. |
213 | | */ |
214 | | int gserialized_has_m(const GSERIALIZED *g) |
215 | 0 | { |
216 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
217 | 0 | return gserialized2_has_m(g); |
218 | 0 | else |
219 | 0 | return gserialized1_has_m(g); |
220 | 0 | } |
221 | | |
222 | | /** |
223 | | * Check if a #GSERIALIZED is a geography. |
224 | | */ |
225 | | int gserialized_is_geodetic(const GSERIALIZED *g) |
226 | 0 | { |
227 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
228 | 0 | return gserialized2_is_geodetic(g); |
229 | 0 | else |
230 | 0 | return gserialized1_is_geodetic(g); |
231 | 0 | } |
232 | | |
233 | | /** |
234 | | * Return the number of dimensions (2, 3, 4) in a geometry |
235 | | */ |
236 | | int gserialized_ndims(const GSERIALIZED *g) |
237 | 0 | { |
238 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
239 | 0 | return gserialized2_ndims(g); |
240 | 0 | else |
241 | 0 | return gserialized1_ndims(g); |
242 | 0 | } |
243 | | |
244 | | /** |
245 | | * Allocate a new #GSERIALIZED from an #LWGEOM. For all non-point types, a bounding |
246 | | * box will be calculated and embedded in the serialization. The geodetic flag is used |
247 | | * to control the box calculation (cartesian or geocentric). If set, the size pointer |
248 | | * will contain the size of the final output, which is useful for setting the PgSQL |
249 | | * VARSIZE information. |
250 | | */ |
251 | | GSERIALIZED* gserialized_from_lwgeom(LWGEOM *geom, size_t *size) |
252 | 0 | { |
253 | 0 | return gserialized2_from_lwgeom(geom, size); |
254 | 0 | } |
255 | | |
256 | | /** |
257 | | * Return the memory size a GSERIALIZED will occupy for a given LWGEOM. |
258 | | */ |
259 | | size_t gserialized_from_lwgeom_size(const LWGEOM *geom) |
260 | 0 | { |
261 | 0 | return gserialized2_from_lwgeom_size(geom); |
262 | 0 | } |
263 | | |
264 | | /** |
265 | | * Allocate a new #LWGEOM from a #GSERIALIZED. The resulting #LWGEOM will have coordinates |
266 | | * that are double aligned and suitable for direct reading using getPoint2d_cp |
267 | | */ |
268 | | LWGEOM* lwgeom_from_gserialized(const GSERIALIZED *g) |
269 | 0 | { |
270 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
271 | 0 | return lwgeom_from_gserialized2(g); |
272 | 0 | else |
273 | 0 | return lwgeom_from_gserialized1(g); |
274 | 0 | } |
275 | | |
276 | | |
277 | | const float * gserialized_get_float_box_p(const GSERIALIZED *g, size_t *ndims) |
278 | 0 | { |
279 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
280 | 0 | return gserialized2_get_float_box_p(g, ndims); |
281 | 0 | else |
282 | 0 | return gserialized1_get_float_box_p(g, ndims); |
283 | 0 | } |
284 | | |
285 | | int |
286 | | gserialized_peek_first_point(const GSERIALIZED *g, POINT4D *out_point) |
287 | 0 | { |
288 | 0 | if (GFLAGS_GET_VERSION(g->gflags)) |
289 | 0 | return gserialized2_peek_first_point(g, out_point); |
290 | 0 | else |
291 | 0 | return gserialized1_peek_first_point(g, out_point); |
292 | 0 | } |
293 | | |
294 | | /** |
295 | | * Return -1 if g1 is "less than" g2, 1 if g1 is "greater than" |
296 | | * g2 and 0 if g1 and g2 are the "same". Equality is evaluated |
297 | | * with a memcmp and size check. So it is possible that two |
298 | | * identical objects where one lacks a bounding box could be |
299 | | * evaluated as non-equal initially. Greater and less than |
300 | | * are evaluated by calculating a sortable key from the center |
301 | | * point of the object bounds. |
302 | | * Because this function might have to handle GSERIALIZED |
303 | | * objects of either version, we implement it up here at the |
304 | | * switching layer rather than down lower. |
305 | | */ |
306 | 0 | #define G2FLAG_EXTENDED 0x10 |
307 | | inline static size_t gserialized_header_size(const GSERIALIZED *g) |
308 | 0 | { |
309 | 0 | size_t sz = 8; /* varsize (4) + srid(3) + flags (1) */ |
310 | |
|
311 | 0 | if ((GFLAGS_GET_VERSION(g->gflags)) && |
312 | 0 | (G2FLAG_EXTENDED & g->gflags)) |
313 | 0 | sz += 8; |
314 | |
|
315 | 0 | if (GFLAG_BBOX & g->gflags) |
316 | 0 | { |
317 | 0 | if (GFLAG_GEODETIC & g->gflags) |
318 | 0 | { |
319 | 0 | sz += 6 * sizeof(float); |
320 | 0 | } |
321 | 0 | else |
322 | 0 | { |
323 | 0 | sz += 4 * sizeof(float) + |
324 | 0 | ((GFLAG_Z & g->gflags) ? 2*sizeof(float) : 0) + |
325 | 0 | ((GFLAG_M & g->gflags) ? 2*sizeof(float) : 0); |
326 | 0 | } |
327 | 0 | } |
328 | |
|
329 | 0 | return sz; |
330 | 0 | } |
331 | | |
332 | | inline static int gserialized_cmp_srid(const GSERIALIZED *g1, const GSERIALIZED *g2) |
333 | 0 | { |
334 | 0 | return ( |
335 | 0 | g1->srid[0] == g2->srid[0] && |
336 | 0 | g1->srid[1] == g2->srid[1] && |
337 | 0 | g1->srid[2] == g2->srid[2] |
338 | 0 | ) ? 0 : 1; |
339 | 0 | } |
340 | | |
341 | | /* ORDER BY hash(g), g::bytea, ST_SRID(g), hasz(g), hasm(g) */ |
342 | | int gserialized_cmp(const GSERIALIZED *g1, const GSERIALIZED *g2) |
343 | 0 | { |
344 | 0 | GBOX box1 = {0}, box2 = {0}; |
345 | 0 | uint64_t hash1, hash2; |
346 | 0 | size_t sz1 = LWSIZE_GET(g1->size); |
347 | 0 | size_t sz2 = LWSIZE_GET(g2->size); |
348 | 0 | size_t hsz1 = gserialized_header_size(g1); |
349 | 0 | size_t hsz2 = gserialized_header_size(g2); |
350 | 0 | uint8_t *b1 = (uint8_t*)g1 + hsz1; |
351 | 0 | uint8_t *b2 = (uint8_t*)g2 + hsz2; |
352 | 0 | size_t bsz1 = sz1 - hsz1; |
353 | 0 | size_t bsz2 = sz2 - hsz2; |
354 | 0 | size_t bsz_min = bsz1 < bsz2 ? bsz1 : bsz2; |
355 | | |
356 | | /* Equality fast path */ |
357 | | /* Return equality for perfect equality only */ |
358 | 0 | int cmp_srid = gserialized_cmp_srid(g1, g2); |
359 | 0 | int cmp = memcmp(b1, b2, bsz_min); |
360 | 0 | int g1hasz = gserialized_has_z(g1); |
361 | 0 | int g1hasm = gserialized_has_m(g1); |
362 | 0 | int g2hasz = gserialized_has_z(g2); |
363 | 0 | int g2hasm = gserialized_has_m(g2); |
364 | |
|
365 | 0 | if (bsz1 == bsz2 && cmp_srid == 0 && cmp == 0 && g1hasz == g2hasz && g1hasm == g2hasm) |
366 | 0 | return 0; |
367 | 0 | else |
368 | 0 | { |
369 | 0 | int g1_is_empty = (gserialized_get_gbox_p(g1, &box1) == LW_FAILURE); |
370 | 0 | int g2_is_empty = (gserialized_get_gbox_p(g2, &box2) == LW_FAILURE); |
371 | 0 | int32_t srid1 = gserialized_get_srid(g1); |
372 | 0 | int32_t srid2 = gserialized_get_srid(g2); |
373 | | |
374 | | /* Empty < Non-empty */ |
375 | 0 | if (g1_is_empty && !g2_is_empty) |
376 | 0 | return -1; |
377 | | |
378 | | /* Non-empty > Empty */ |
379 | 0 | if (!g1_is_empty && g2_is_empty) |
380 | 0 | return 1; |
381 | | |
382 | 0 | if (!g1_is_empty && !g2_is_empty) |
383 | 0 | { |
384 | | /* Using the boxes, calculate sortable hash key. */ |
385 | 0 | hash1 = gbox_get_sortable_hash(&box1, srid1); |
386 | 0 | hash2 = gbox_get_sortable_hash(&box2, srid2); |
387 | |
|
388 | 0 | if (hash1 > hash2) |
389 | 0 | return 1; |
390 | 0 | if (hash1 < hash2) |
391 | 0 | return -1; |
392 | 0 | } |
393 | | |
394 | | /* Prefix comes before longer one. */ |
395 | 0 | if (bsz1 != bsz2 && cmp == 0) |
396 | 0 | { |
397 | 0 | if (bsz1 < bsz2) |
398 | 0 | return -1; |
399 | 0 | return 1; |
400 | 0 | } |
401 | | |
402 | | /* If SRID is not equal, sort on it */ |
403 | 0 | if (cmp_srid != 0) |
404 | 0 | return (srid1 > srid2) ? 1 : -1; |
405 | | |
406 | | /* ZM flag sort*/ |
407 | 0 | if (g1hasz != g2hasz) |
408 | 0 | return (g1hasz > g2hasz) ? 1 : -1; |
409 | | |
410 | 0 | if (g1hasm != g2hasm) |
411 | 0 | return (g1hasm > g2hasm) ? 1 : -1; |
412 | | |
413 | 0 | assert(cmp != 0); |
414 | 0 | return cmp > 0 ? 1 : -1; |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | uint64_t |
419 | | gserialized_get_sortable_hash(const GSERIALIZED *g) |
420 | 0 | { |
421 | 0 | GBOX box; |
422 | 0 | int is_empty = (gserialized_get_gbox_p(g, &box) == LW_FAILURE); |
423 | |
|
424 | 0 | if (is_empty) |
425 | 0 | return 0; |
426 | 0 | else |
427 | 0 | return gbox_get_sortable_hash(&box, gserialized_get_srid(g)); |
428 | 0 | } |
429 | | |
430 | | void gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname); |
431 | | void |
432 | | gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname) |
433 | 0 | { |
434 | 0 | int32_t srid1 = gserialized_get_srid(g1); |
435 | 0 | int32_t srid2 = gserialized_get_srid(g2); |
436 | 0 | if (srid1 != srid2) |
437 | 0 | lwerror("%s: Operation on mixed SRID geometries (%s, %d) != (%s, %d)", |
438 | 0 | funcname, |
439 | 0 | lwtype_name(gserialized1_get_type(g1)), |
440 | 0 | srid1, |
441 | 0 | lwtype_name(gserialized_get_type(g2)), |
442 | 0 | srid2); |
443 | 0 | } |
444 | | |
445 | | void gserialized_error_if_srid_mismatch_reference(const GSERIALIZED *g1, const int32_t srid2, const char *funcname); |
446 | | void |
447 | | gserialized_error_if_srid_mismatch_reference(const GSERIALIZED *g1, const int32_t srid2, const char *funcname) |
448 | 0 | { |
449 | 0 | int32_t srid1 = gserialized_get_srid(g1); |
450 | 0 | if (srid1 != srid2) |
451 | 0 | lwerror("%s: Operation on mixed SRID geometries %s %d != %d", |
452 | 0 | funcname, |
453 | 0 | lwtype_name(gserialized1_get_type(g1)), |
454 | 0 | srid1, |
455 | 0 | srid2); |
456 | 0 | } |