Coverage Report

Created: 2026-07-16 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}