Coverage Report

Created: 2026-09-01 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwgeom_api.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 2001-2006 Refractions Research Inc.
22
 * Copyright 2017-2026 Darafei Praliaskouski <me@komzpa.net>
23
 *
24
 **********************************************************************/
25
26
#include "liblwgeom_internal.h"
27
#include "lwgeom_log.h"
28
29
#include <stdio.h>
30
#include <assert.h>
31
32
0
#define xstr(s) str(s)
33
0
#define str(s) #s
34
35
const char *
36
lwgeom_version(void)
37
0
{
38
0
  static char *ptr = NULL;
39
0
  static char buf[256];
40
0
  if ( ! ptr )
41
0
  {
42
0
    ptr = buf;
43
0
    snprintf(ptr, 256, LIBLWGEOM_VERSION" " xstr(POSTGIS_REVISION));
44
0
  }
45
46
0
  return ptr;
47
0
}
48
49
50
inline float
51
next_float_down(double d)
52
10.9M
{
53
10.9M
  float result;
54
10.9M
  if (d > (double)FLT_MAX)
55
21.6k
    return FLT_MAX;
56
10.9M
  if (d <= (double)-FLT_MAX)
57
57.1k
    return -FLT_MAX;
58
10.8M
  result = d;
59
60
10.8M
  if ( ((double)result) <=d )
61
10.8M
    return result;
62
63
62.0k
  return nextafterf(result, -1*FLT_MAX);
64
65
10.8M
}
66
67
/*
68
 * Returns the float that's very close to the input, but >=.
69
 * handles the funny differences in float4 and float8 reps.
70
 */
71
inline float
72
next_float_up(double d)
73
10.9M
{
74
10.9M
  float result;
75
10.9M
  if (d >= (double)FLT_MAX)
76
28.0k
    return FLT_MAX;
77
10.9M
  if (d < (double)-FLT_MAX)
78
37.1k
    return -FLT_MAX;
79
10.8M
  result = d;
80
81
10.8M
  if ( ((double)result) >=d )
82
10.7M
    return result;
83
84
114k
  return nextafterf(result, FLT_MAX);
85
10.8M
}
86
87
88
89
90
/************************************************************************
91
 * POINTARRAY support functions
92
 *
93
 * TODO: should be moved to ptarray.c probably
94
 *
95
 ************************************************************************/
96
97
/*
98
 * Copies a point from the point array into the parameter point
99
 * will set point's z=NO_Z_VALUE if pa is 2d
100
 * will set point's m=NO_M_VALUE if pa is 3d or 2d
101
 *
102
 * NOTE: point is a real POINT3D *not* a pointer
103
 */
104
POINT4D
105
getPoint4d(const POINTARRAY *pa, uint32_t n)
106
0
{
107
0
  POINT4D result;
108
0
  if (getPoint4d_p(pa, n, &result) == 0)
109
0
    lwerror("%s [%d] error returned by getPoint4d_p", __FILE__, __LINE__);
110
0
  return result;
111
0
}
112
113
/*
114
 * Copies a point from the point array into the parameter point
115
 * will set point's z=NO_Z_VALUE  if pa is 2d
116
 * will set point's m=NO_M_VALUE  if pa is 3d or 2d
117
 *
118
 * NOTE: this will modify the point4d pointed to by 'point'.
119
 *
120
 * @return 0 on error, 1 on success
121
 */
122
int
123
getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *op)
124
15.2M
{
125
15.2M
  uint8_t *ptr;
126
15.2M
  int zmflag;
127
128
15.2M
  if ( ! pa )
129
0
  {
130
0
    lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__);
131
0
    return 0;
132
0
  }
133
134
15.2M
  if ( n>=pa->npoints )
135
40
  {
136
40
    lwerror("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints);
137
40
    return 0;
138
40
  }
139
140
15.2M
  LWDEBUG(4, "getPoint4d_p called.");
141
142
  /* Get a pointer to nth point offset and zmflag */
143
15.2M
  ptr=getPoint_internal(pa, n);
144
15.2M
  zmflag=FLAGS_GET_ZM(pa->flags);
145
146
15.2M
  LWDEBUGF(4, "ptr %p, zmflag %d", ptr, zmflag);
147
148
15.2M
  switch (zmflag)
149
15.2M
  {
150
7.34M
  case 0: /* 2d  */
151
7.34M
    memcpy(op, ptr, sizeof(POINT2D));
152
7.34M
    op->m=NO_M_VALUE;
153
7.34M
    op->z=NO_Z_VALUE;
154
7.34M
    break;
155
156
278k
  case 3: /* ZM */
157
278k
    memcpy(op, ptr, sizeof(POINT4D));
158
278k
    break;
159
160
4.62M
  case 2: /* Z */
161
4.62M
    memcpy(op, ptr, sizeof(POINT3DZ));
162
4.62M
    op->m=NO_M_VALUE;
163
4.62M
    break;
164
165
2.98M
  case 1: /* M */
166
2.98M
    memcpy(op, ptr, sizeof(POINT3DM));
167
2.98M
    op->m=op->z; /* we use Z as temporary storage */
168
2.98M
    op->z=NO_Z_VALUE;
169
2.98M
    break;
170
171
0
  default:
172
0
    lwerror("Unknown ZM flag ??");
173
0
    return 0;
174
15.2M
  }
175
15.2M
  return 1;
176
177
15.2M
}
178
179
/*
180
 * Copy a point from the point array into the parameter point
181
 * will set point's z=NO_Z_VALUE if pa is 2d
182
 * NOTE: point is a real POINT3DZ *not* a pointer
183
 */
184
POINT3DZ
185
getPoint3dz(const POINTARRAY *pa, uint32_t n)
186
0
{
187
0
  POINT3DZ result;
188
0
  getPoint3dz_p(pa, n, &result);
189
0
  return result;
190
0
}
191
192
/*
193
 * Copy a point from the point array into the parameter point
194
 * will set point's z=NO_Z_VALUE if pa is 2d
195
 *
196
 * NOTE: point is a real POINT3DZ *not* a pointer
197
 */
198
POINT3DM
199
getPoint3dm(const POINTARRAY *pa, uint32_t n)
200
0
{
201
0
  POINT3DM result;
202
0
  getPoint3dm_p(pa, n, &result);
203
0
  return result;
204
0
}
205
206
/*
207
 * Copy a point from the point array into the parameter point
208
 * will set point's z=NO_Z_VALUE if pa is 2d
209
 *
210
 * NOTE: this will modify the point3dz pointed to by 'point'.
211
 */
212
int
213
getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *op)
214
0
{
215
0
  uint8_t *ptr;
216
217
0
  if ( ! pa )
218
0
  {
219
0
    lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__);
220
0
    return 0;
221
0
  }
222
223
  //assert(n < pa->npoints); --causes point empty/point empty to crash
224
0
  if ( n>=pa->npoints )
225
0
  {
226
0
    lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints);
227
0
    return 0;
228
0
  }
229
230
0
  LWDEBUGF(2, "getPoint3dz_p called on array of %d-dimensions / %u pts",
231
0
           FLAGS_NDIMS(pa->flags), pa->npoints);
232
233
  /* Get a pointer to nth point offset */
234
0
  ptr=getPoint_internal(pa, n);
235
236
  /*
237
   * if input POINTARRAY has the Z, it is always
238
   * at third position so make a single copy
239
   */
240
0
  if ( FLAGS_GET_Z(pa->flags) )
241
0
  {
242
0
    memcpy(op, ptr, sizeof(POINT3DZ));
243
0
  }
244
245
  /*
246
   * Otherwise copy the 2d part and initialize
247
   * Z to NO_Z_VALUE
248
   */
249
0
  else
250
0
  {
251
0
    memcpy(op, ptr, sizeof(POINT2D));
252
0
    op->z=NO_Z_VALUE;
253
0
  }
254
255
0
  return 1;
256
257
0
}
258
259
/*
260
 * Copy a point from the point array into the parameter point
261
 * will set point's m=NO_Z_VALUE if pa has no M
262
 *
263
 * NOTE: this will modify the point3dm pointed to by 'point'.
264
 */
265
int
266
getPoint3dm_p(const POINTARRAY *pa, uint32_t n, POINT3DM *op)
267
0
{
268
0
  uint8_t *ptr;
269
0
  int zmflag;
270
271
0
  if (!pa)
272
0
  {
273
0
    lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__);
274
0
    return LW_FALSE;
275
0
  }
276
277
0
  if (n >= pa->npoints)
278
0
  {
279
0
    lwerror("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints);
280
0
    return LW_FALSE;
281
0
  }
282
283
  /* Get a pointer to nth point offset and zmflag */
284
0
  ptr = getPoint_internal(pa, n);
285
0
  zmflag = FLAGS_GET_ZM(pa->flags);
286
287
  /*
288
   * if input POINTARRAY has the M and NO Z,
289
   * we can issue a single memcpy
290
   */
291
0
  if (zmflag == 1)
292
0
  {
293
0
    memcpy(op, ptr, sizeof(POINT3DM));
294
0
    return LW_TRUE;
295
0
  }
296
297
  /*
298
   * Otherwise copy the 2d part and
299
   * initialize M to NO_M_VALUE
300
   */
301
0
  memcpy(op, ptr, sizeof(POINT2D));
302
303
  /*
304
   * Then, if input has Z skip it and
305
   * copy next double, otherwise initialize
306
   * M to NO_M_VALUE
307
   */
308
0
  if (zmflag == 3)
309
0
  {
310
0
    ptr += sizeof(POINT3DZ);
311
0
    memcpy(&(op->m), ptr, sizeof(double));
312
0
  }
313
0
  else
314
0
    op->m = NO_M_VALUE;
315
316
0
  return LW_TRUE;
317
0
}
318
319
/*
320
 * Copy a point from the point array into the parameter point
321
 * z value (if present) is not returned.
322
 *
323
 * NOTE: point is a real POINT2D *not* a pointer
324
 */
325
POINT2D
326
getPoint2d(const POINTARRAY *pa, uint32_t n)
327
0
{
328
0
  const POINT2D *result;
329
0
  result = getPoint2d_cp(pa, n);
330
0
  return *result;
331
0
}
332
333
/*
334
 * Copy a point from the point array into the parameter point
335
 * z value (if present) is not returned.
336
 *
337
 * NOTE: this will modify the point2d pointed to by 'point'.
338
 */
339
int
340
getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
341
0
{
342
0
  if ( ! pa )
343
0
  {
344
0
    lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__);
345
0
    return 0;
346
0
  }
347
348
0
  if ( n>=pa->npoints )
349
0
  {
350
0
    lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints);
351
0
    return 0;
352
0
  }
353
354
  /* this does x,y */
355
0
  memcpy(point, getPoint_internal(pa, n), sizeof(POINT2D));
356
0
  return 1;
357
0
}
358
359
/*
360
 * set point N to the given value
361
 * NOTE that the pointarray can be of any
362
 * dimension, the appropriate ordinate values
363
 * will be extracted from it
364
 *
365
 */
366
void
367
ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
368
2.30M
{
369
2.30M
  uint8_t *ptr;
370
2.30M
  assert(n < pa->npoints);
371
2.30M
  ptr = getPoint_internal(pa, n);
372
2.30M
  switch ( FLAGS_GET_ZM(pa->flags) )
373
2.30M
  {
374
45.9k
  case 3:
375
45.9k
    memcpy(ptr, p4d, sizeof(POINT4D));
376
45.9k
    break;
377
557k
  case 2:
378
557k
    memcpy(ptr, p4d, sizeof(POINT3DZ));
379
557k
    break;
380
107k
  case 1:
381
107k
    memcpy(ptr, p4d, sizeof(POINT2D));
382
107k
    ptr+=sizeof(POINT2D);
383
107k
    memcpy(ptr, &(p4d->m), sizeof(double));
384
107k
    break;
385
1.59M
  case 0:
386
1.59M
    memcpy(ptr, p4d, sizeof(POINT2D));
387
1.59M
    break;
388
2.30M
  }
389
2.30M
}
390
391
void
392
ptarray_copy_point(POINTARRAY *pa, uint32_t from, uint32_t to)
393
0
{
394
0
  int ndims = FLAGS_NDIMS(pa->flags);
395
0
  switch (ndims)
396
0
  {
397
0
    case 2:
398
0
    {
399
0
      POINT2D *p_from = (POINT2D*)(getPoint_internal(pa, from));
400
0
      POINT2D *p_to = (POINT2D*)(getPoint_internal(pa, to));
401
0
      *p_to = *p_from;
402
0
      return;
403
0
    }
404
0
    case 3:
405
0
    {
406
0
      POINT3D *p_from = (POINT3D*)(getPoint_internal(pa, from));
407
0
      POINT3D *p_to = (POINT3D*)(getPoint_internal(pa, to));
408
0
      *p_to = *p_from;
409
0
      return;
410
0
    }
411
0
    case 4:
412
0
    {
413
0
      POINT4D *p_from = (POINT4D*)(getPoint_internal(pa, from));
414
0
      POINT4D *p_to = (POINT4D*)(getPoint_internal(pa, to));
415
0
      *p_to = *p_from;
416
0
      return;
417
0
    }
418
0
    default:
419
0
    {
420
0
      lwerror("%s: unsupported number of dimensions - %d", __func__, ndims);
421
0
      return;
422
0
    }
423
0
  }
424
0
  return;
425
0
}
426
427
428
/************************************************
429
 * debugging routines
430
 ************************************************/
431
432
void printBOX3D(BOX3D *box)
433
0
{
434
0
  lwnotice("BOX3D: %g %g, %g %g", box->xmin, box->ymin,
435
0
           box->xmax, box->ymax);
436
0
}
437
438
void printPA(POINTARRAY *pa)
439
0
{
440
0
  uint32_t t;
441
0
  POINT4D pt;
442
0
  char *mflag;
443
444
0
  if (!pa)
445
0
  {
446
0
    lwnotice("                    PTARRAY is null pointer!");
447
0
    return;
448
0
  }
449
0
  if ( FLAGS_GET_M(pa->flags) ) mflag = "M";
450
0
  else mflag = "";
451
452
0
  lwnotice("      POINTARRAY%s{", mflag);
453
0
  lwnotice(
454
0
      "                 ndims=%i,   ptsize=%lu", FLAGS_NDIMS(pa->flags), (unsigned long)ptarray_point_size(pa));
455
0
  lwnotice("                 npoints = %u", pa->npoints);
456
457
0
  for (t = 0; t < pa->npoints; t++)
458
0
  {
459
0
    getPoint4d_p(pa, t, &pt);
460
0
    if (FLAGS_NDIMS(pa->flags) == 2)
461
0
      lwnotice("                    %i : %lf,%lf", t, pt.x, pt.y);
462
0
    if (FLAGS_NDIMS(pa->flags) == 3)
463
0
      lwnotice("                    %i : %lf,%lf,%lf", t, pt.x, pt.y, pt.z);
464
0
    if (FLAGS_NDIMS(pa->flags) == 4)
465
0
      lwnotice("                    %i : %lf,%lf,%lf,%lf", t, pt.x, pt.y, pt.z, pt.m);
466
0
  }
467
468
0
  lwnotice("      }");
469
0
}
470
471
472
/**
473
 * Given a string with at least 2 chars in it, convert them to
474
 * a byte value.  No error checking done!
475
 */
476
uint8_t
477
parse_hex(char *str)
478
0
{
479
  /* do this a little brute force to make it faster */
480
481
0
  uint8_t   result_high = 0;
482
0
  uint8_t   result_low = 0;
483
484
0
  switch (str[0])
485
0
  {
486
0
  case '0' :
487
0
    result_high = 0;
488
0
    break;
489
0
  case '1' :
490
0
    result_high = 1;
491
0
    break;
492
0
  case '2' :
493
0
    result_high = 2;
494
0
    break;
495
0
  case '3' :
496
0
    result_high = 3;
497
0
    break;
498
0
  case '4' :
499
0
    result_high = 4;
500
0
    break;
501
0
  case '5' :
502
0
    result_high = 5;
503
0
    break;
504
0
  case '6' :
505
0
    result_high = 6;
506
0
    break;
507
0
  case '7' :
508
0
    result_high = 7;
509
0
    break;
510
0
  case '8' :
511
0
    result_high = 8;
512
0
    break;
513
0
  case '9' :
514
0
    result_high = 9;
515
0
    break;
516
0
  case 'A' :
517
0
  case 'a' :
518
0
    result_high = 10;
519
0
    break;
520
0
  case 'B' :
521
0
  case 'b' :
522
0
    result_high = 11;
523
0
    break;
524
0
  case 'C' :
525
0
  case 'c' :
526
0
    result_high = 12;
527
0
    break;
528
0
  case 'D' :
529
0
  case 'd' :
530
0
    result_high = 13;
531
0
    break;
532
0
  case 'E' :
533
0
  case 'e' :
534
0
    result_high = 14;
535
0
    break;
536
0
  case 'F' :
537
0
  case 'f' :
538
0
    result_high = 15;
539
0
    break;
540
0
  }
541
0
  switch (str[1])
542
0
  {
543
0
  case '0' :
544
0
    result_low = 0;
545
0
    break;
546
0
  case '1' :
547
0
    result_low = 1;
548
0
    break;
549
0
  case '2' :
550
0
    result_low = 2;
551
0
    break;
552
0
  case '3' :
553
0
    result_low = 3;
554
0
    break;
555
0
  case '4' :
556
0
    result_low = 4;
557
0
    break;
558
0
  case '5' :
559
0
    result_low = 5;
560
0
    break;
561
0
  case '6' :
562
0
    result_low = 6;
563
0
    break;
564
0
  case '7' :
565
0
    result_low = 7;
566
0
    break;
567
0
  case '8' :
568
0
    result_low = 8;
569
0
    break;
570
0
  case '9' :
571
0
    result_low = 9;
572
0
    break;
573
0
  case 'A' :
574
0
  case 'a' :
575
0
    result_low = 10;
576
0
    break;
577
0
  case 'B' :
578
0
  case 'b' :
579
0
    result_low = 11;
580
0
    break;
581
0
  case 'C' :
582
0
  case 'c' :
583
0
    result_low = 12;
584
0
    break;
585
0
  case 'D' :
586
0
  case 'd' :
587
0
    result_low = 13;
588
0
    break;
589
0
  case 'E' :
590
0
  case 'e' :
591
0
    result_low = 14;
592
0
    break;
593
0
  case 'F' :
594
0
  case 'f' :
595
0
    result_low = 15;
596
0
    break;
597
0
  }
598
0
  return (uint8_t) ((result_high<<4) + result_low);
599
0
}
600
601
602
/**
603
 * Given one byte, populate result with two byte representing
604
 * the hex number.
605
 *
606
 * Ie. deparse_hex( 255, mystr)
607
 *    -> mystr[0] = 'F' and mystr[1] = 'F'
608
 *
609
 * No error checking done
610
 */
611
void
612
deparse_hex(uint8_t str, char *result)
613
0
{
614
0
  int input_high;
615
0
  int  input_low;
616
0
  static char outchr[]=
617
0
  {
618
0
    "0123456789ABCDEF"
619
0
  };
620
621
0
  input_high = (str>>4);
622
0
  input_low = (str & 0x0F);
623
624
0
  result[0] = outchr[input_high];
625
0
  result[1] = outchr[input_low];
626
627
0
}
628
629
630
/**
631
 * Find interpolation point I
632
 * between point A and point B
633
 * so that the len(AI) == len(AB)*F
634
 * and I falls on AB segment.
635
 *
636
 * Example:
637
 *
638
 *   F=0.5  :    A----I----B
639
 *   F=1    :    A---------B==I
640
 *   F=0    : A==I---------B
641
 *   F=.2   :    A-I-------B
642
 */
643
void
644
interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F)
645
0
{
646
0
  assert(F >= 0 && F <= 1);
647
0
  I->x=A->x+((B->x-A->x)*F);
648
0
  I->y=A->y+((B->y-A->y)*F);
649
0
  I->z=A->z+((B->z-A->z)*F);
650
0
  I->m=A->m+((B->m-A->m)*F);
651
0
}
652
653
654
int _lwgeom_interrupt_requested = 0;
655
void
656
0
lwgeom_request_interrupt(void) {
657
0
  _lwgeom_interrupt_requested = 1;
658
0
}
659
void
660
0
lwgeom_cancel_interrupt(void) {
661
0
  _lwgeom_interrupt_requested = 0;
662
0
}
663
664
lwinterrupt_callback *_lwgeom_interrupt_callback = 0;
665
lwinterrupt_callback *
666
0
lwgeom_register_interrupt_callback(lwinterrupt_callback *cb) {
667
0
  lwinterrupt_callback *old = _lwgeom_interrupt_callback;
668
0
  _lwgeom_interrupt_callback = cb;
669
0
  return old;
670
0
}