/src/postgis/liblwgeom/lwin_twkb.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * PostGIS - Spatial Types for PostgreSQL |
4 | | * http://postgis.net |
5 | | * Copyright (C) 2026 Darafei Praliaskouski <me@komzpa.net> |
6 | | * |
7 | | * PostGIS is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation, either version 2 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * PostGIS is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with PostGIS. If not, see <http://www.gnu.org/licenses/>. |
19 | | * |
20 | | ********************************************************************** |
21 | | * |
22 | | * Copyright (C) 2014 Nicklas Avén |
23 | | * |
24 | | **********************************************************************/ |
25 | | |
26 | | #include <math.h> |
27 | | #include <limits.h> |
28 | | #include <string.h> |
29 | | #include "liblwgeom_internal.h" |
30 | | #include "lwgeom_log.h" |
31 | | #include "varint.h" |
32 | | |
33 | 176k | #define TWKB_IN_MAXCOORDS 4 |
34 | | /** Max depth in a geometry. Matches the WKB parser recursion limit. */ |
35 | 6.28k | #define LW_PARSER_MAX_DEPTH 200 |
36 | | |
37 | | /** |
38 | | * Used for passing the parse state between the parsing functions. |
39 | | */ |
40 | | typedef struct |
41 | | { |
42 | | /* Pointers to the bytes */ |
43 | | const uint8_t *twkb; /* Points to start of TWKB */ |
44 | | const uint8_t *twkb_end; /* Points to end of TWKB */ |
45 | | const uint8_t *pos; /* Current read position */ |
46 | | |
47 | | uint32_t check; /* Simple validity checks on geometries */ |
48 | | uint32_t lwtype; /* Current type we are handling */ |
49 | | uint8_t depth; /* Current recursion level, to prevent stack overflows */ |
50 | | |
51 | | uint8_t has_bbox; |
52 | | uint8_t has_size; |
53 | | uint8_t has_idlist; |
54 | | uint8_t has_z; |
55 | | uint8_t has_m; |
56 | | uint8_t is_empty; |
57 | | uint8_t error; |
58 | | |
59 | | /* Precision factors to convert ints to double */ |
60 | | double factor; |
61 | | double factor_z; |
62 | | double factor_m; |
63 | | |
64 | | uint64_t size; |
65 | | |
66 | | /* Info about current geometry */ |
67 | | uint8_t magic_byte; /* the magic byte contain info about if twkb contain id, size info, bboxes and precision */ |
68 | | |
69 | | int ndims; /* Number of dimensions */ |
70 | | |
71 | | int64_t *coords; /* An array to keep delta values from 4 dimensions */ |
72 | | |
73 | | } twkb_parse_state; |
74 | | |
75 | | |
76 | | /** |
77 | | * Internal function declarations. |
78 | | */ |
79 | | LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s); |
80 | | |
81 | | |
82 | | /**********************************************************************/ |
83 | | |
84 | | /** |
85 | | * Check that we are not about to read off the end of the WKB |
86 | | * array. |
87 | | */ |
88 | | static inline void twkb_parse_state_advance(twkb_parse_state *s, size_t next) |
89 | 1.72M | { |
90 | 1.72M | size_t remaining = (s->pos <= s->twkb_end) ? (size_t)(s->twkb_end - s->pos) : 0; |
91 | | |
92 | 1.72M | if (next > remaining) |
93 | 93 | { |
94 | 93 | s->error = LW_TRUE; |
95 | 93 | lwerror("%s: TWKB structure does not match expected size!", __func__); |
96 | 93 | s->pos = s->twkb_end; |
97 | 93 | return; |
98 | 93 | } |
99 | | |
100 | 1.72M | s->pos += next; |
101 | 1.72M | } |
102 | | |
103 | | static inline int64_t twkb_parse_state_varint(twkb_parse_state *s) |
104 | 833k | { |
105 | 833k | size_t size; |
106 | 833k | int64_t val = varint_s64_decode(s->pos, s->twkb_end, &size); |
107 | 833k | if (size == 0) |
108 | 0 | s->error = LW_TRUE; |
109 | 833k | twkb_parse_state_advance(s, size); |
110 | 833k | return val; |
111 | 833k | } |
112 | | |
113 | | static inline uint64_t twkb_parse_state_uvarint(twkb_parse_state *s) |
114 | 304k | { |
115 | 304k | size_t size; |
116 | 304k | uint64_t val = varint_u64_decode(s->pos, s->twkb_end, &size); |
117 | 304k | if (size == 0) |
118 | 0 | s->error = LW_TRUE; |
119 | 304k | twkb_parse_state_advance(s, size); |
120 | 304k | return val; |
121 | 304k | } |
122 | | |
123 | | static inline double twkb_parse_state_double(twkb_parse_state *s, double factor) |
124 | 21.1k | { |
125 | 21.1k | size_t size; |
126 | 21.1k | int64_t val = varint_s64_decode(s->pos, s->twkb_end, &size); |
127 | 21.1k | if (size == 0) |
128 | 0 | s->error = LW_TRUE; |
129 | 21.1k | twkb_parse_state_advance(s, size); |
130 | 21.1k | return val / factor; |
131 | 21.1k | } |
132 | | |
133 | | static inline void twkb_parse_state_varint_skip(twkb_parse_state *s) |
134 | 494k | { |
135 | 494k | size_t size = varint_size(s->pos, s->twkb_end); |
136 | | |
137 | 494k | if ( ! size ) |
138 | 186 | { |
139 | 186 | s->error = LW_TRUE; |
140 | 186 | lwerror("%s: no varint to skip", __func__); |
141 | 186 | } |
142 | | |
143 | 494k | twkb_parse_state_advance(s, size); |
144 | 494k | return; |
145 | 494k | } |
146 | | |
147 | | static inline uint32_t |
148 | | twkb_parse_state_uvarint32(twkb_parse_state *s) |
149 | 303k | { |
150 | 303k | uint64_t val = twkb_parse_state_uvarint(s); |
151 | | |
152 | 303k | if (val > UINT32_MAX) |
153 | 543 | { |
154 | 543 | s->error = LW_TRUE; |
155 | 543 | lwerror("%s: TWKB count exceeds uint32_t", __func__); |
156 | 543 | return 0; |
157 | 543 | } |
158 | | |
159 | 302k | return (uint32_t)val; |
160 | 303k | } |
161 | | |
162 | | static inline int |
163 | | twkb_parse_state_has_min_bytes(twkb_parse_state *s, uint32_t count, size_t min_bytes) |
164 | 211k | { |
165 | 211k | const size_t remaining = (size_t)(s->twkb_end - s->pos); |
166 | | |
167 | | /* TWKB counts are trusted only after proving that the remaining buffer can |
168 | | * hold the smallest possible payload for that many elements. This rejects |
169 | | * impossible allocation requests before constructors size their arrays. |
170 | | */ |
171 | 211k | if (min_bytes != 0 && count > remaining / min_bytes) |
172 | 746 | { |
173 | 746 | s->error = LW_TRUE; |
174 | 746 | lwerror("%s: TWKB element count exceeds remaining payload", __func__); |
175 | 746 | return LW_FALSE; |
176 | 746 | } |
177 | | |
178 | 210k | return LW_TRUE; |
179 | 211k | } |
180 | | |
181 | | /** Implements *pa += b using explicit wraparound semantics. */ |
182 | | #if defined(__clang__) && __clang_major__ >= 4 |
183 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
184 | | #endif |
185 | | static inline void |
186 | | safe_add_int64(int64_t *pa, int64_t b) |
187 | 833k | { |
188 | 833k | uint64_t u_a = (uint64_t)*pa; |
189 | 833k | uint64_t u_b = (uint64_t)b; |
190 | 833k | u_a += u_b; |
191 | 833k | memcpy(pa, &u_a, sizeof(int64_t)); |
192 | 833k | } |
193 | | |
194 | | static inline void |
195 | | twkb_parse_state_accum_coord(twkb_parse_state *s, int coord_index) |
196 | 833k | { |
197 | 833k | safe_add_int64(&s->coords[coord_index], twkb_parse_state_varint(s)); |
198 | 833k | } |
199 | | |
200 | | static uint32_t lwtype_from_twkb_type(uint8_t twkb_type) |
201 | 35.3k | { |
202 | 35.3k | switch (twkb_type) |
203 | 35.3k | { |
204 | 13.9k | case 1: |
205 | 13.9k | return POINTTYPE; |
206 | 1.20k | case 2: |
207 | 1.20k | return LINETYPE; |
208 | 1.12k | case 3: |
209 | 1.12k | return POLYGONTYPE; |
210 | 7.25k | case 4: |
211 | 7.25k | return MULTIPOINTTYPE; |
212 | 1.78k | case 5: |
213 | 1.78k | return MULTILINETYPE; |
214 | 2.46k | case 6: |
215 | 2.46k | return MULTIPOLYGONTYPE; |
216 | 7.48k | case 7: |
217 | 7.48k | return COLLECTIONTYPE; |
218 | | |
219 | 39 | default: /* Error! */ |
220 | 39 | lwerror("Unknown WKB type"); |
221 | 39 | return 0; |
222 | 35.3k | } |
223 | 0 | return 0; |
224 | 35.3k | } |
225 | | |
226 | | /** |
227 | | * Byte |
228 | | * Read a byte and advance the parse state forward. |
229 | | */ |
230 | | static uint8_t byte_from_twkb_state(twkb_parse_state *s) |
231 | 71.8k | { |
232 | 71.8k | uint8_t val = 0; |
233 | | |
234 | | /* CUnit records lwerror and continues, while PostgreSQL and the fuzzers |
235 | | * abort through their error handlers. Keep the bounds check local so this |
236 | | * byte helper is safe under both execution models. |
237 | | */ |
238 | 71.8k | if ((s->twkb_end - s->pos) >= WKB_BYTE_SIZE) |
239 | 71.8k | val = *(s->pos); |
240 | 71.8k | twkb_parse_state_advance(s, WKB_BYTE_SIZE); |
241 | 71.8k | return val; |
242 | 71.8k | } |
243 | | |
244 | | |
245 | | /** |
246 | | * POINTARRAY |
247 | | * Read a dynamically sized point array and advance the parse state forward. |
248 | | */ |
249 | | static POINTARRAY* ptarray_from_twkb_state(twkb_parse_state *s, uint32_t npoints) |
250 | 197k | { |
251 | 197k | POINTARRAY *pa = NULL; |
252 | 197k | uint32_t ndims = s->ndims; |
253 | 197k | uint32_t i; |
254 | 197k | uint32_t j; |
255 | 197k | double *dlist; |
256 | 197k | double factors[TWKB_IN_MAXCOORDS]; |
257 | | |
258 | 197k | LWDEBUG(2,"Entering ptarray_from_twkb_state"); |
259 | 197k | LWDEBUGF(4,"Pointarray has %d points", npoints); |
260 | | |
261 | | /* Empty! */ |
262 | 197k | if( npoints == 0 ) |
263 | 30 | return ptarray_construct_empty(s->has_z, s->has_m, 0); |
264 | | |
265 | 197k | if (!twkb_parse_state_has_min_bytes(s, npoints, s->ndims)) |
266 | 0 | return NULL; |
267 | | |
268 | 197k | factors[0] = s->factor; |
269 | 197k | factors[1] = s->factor; |
270 | 197k | j = 2; |
271 | 197k | if (s->has_z) |
272 | 940 | factors[j++] = s->factor_z; |
273 | 197k | if (s->has_m) |
274 | 1.16k | factors[j++] = s->factor_m; |
275 | | |
276 | 197k | pa = ptarray_construct(s->has_z, s->has_m, npoints); |
277 | 197k | dlist = (double*)(pa->serialized_pointlist); |
278 | 607k | for( i = 0; i < npoints; i++ ) |
279 | 409k | { |
280 | 1.24M | for (j = 0; j < ndims; j++) |
281 | 833k | { |
282 | 833k | twkb_parse_state_accum_coord(s, j); |
283 | 833k | dlist[ndims * i + j] = s->coords[j] / factors[j]; |
284 | 833k | } |
285 | 409k | } |
286 | | |
287 | 197k | return pa; |
288 | 197k | } |
289 | | |
290 | | /** |
291 | | * POINT |
292 | | */ |
293 | | static LWPOINT* lwpoint_from_twkb_state(twkb_parse_state *s) |
294 | 172k | { |
295 | 172k | static uint32_t npoints = 1; |
296 | 172k | POINTARRAY *pa; |
297 | | |
298 | 172k | LWDEBUG(2,"Entering lwpoint_from_twkb_state"); |
299 | | |
300 | 172k | if ( s->is_empty ) |
301 | 13.4k | return lwpoint_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); |
302 | | |
303 | 158k | pa = ptarray_from_twkb_state(s, npoints); |
304 | 158k | return lwpoint_construct(SRID_UNKNOWN, NULL, pa); |
305 | 172k | } |
306 | | |
307 | | /** |
308 | | * LINESTRING |
309 | | */ |
310 | | static LWLINE* lwline_from_twkb_state(twkb_parse_state *s) |
311 | 184k | { |
312 | 184k | uint32_t npoints; |
313 | 184k | POINTARRAY *pa; |
314 | | |
315 | 184k | LWDEBUG(2,"Entering lwline_from_twkb_state"); |
316 | | |
317 | 184k | if ( s->is_empty ) |
318 | 718 | return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); |
319 | | |
320 | | /* Read number of points */ |
321 | 183k | npoints = twkb_parse_state_uvarint32(s); |
322 | | |
323 | 183k | if ( npoints == 0 ) |
324 | 182k | return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); |
325 | | |
326 | | /* Read coordinates */ |
327 | 1.07k | pa = ptarray_from_twkb_state(s, npoints); |
328 | | |
329 | 1.07k | if (pa == NULL) |
330 | 0 | return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); |
331 | | |
332 | 1.07k | if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) |
333 | 0 | { |
334 | 0 | lwerror("%s must have at least two points", lwtype_name(s->lwtype)); |
335 | 0 | return NULL; |
336 | 0 | } |
337 | | |
338 | 1.07k | return lwline_construct(SRID_UNKNOWN, NULL, pa); |
339 | 1.07k | } |
340 | | |
341 | | /** |
342 | | * POLYGON |
343 | | */ |
344 | | static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s) |
345 | 71.1k | { |
346 | 71.1k | uint32_t nrings; |
347 | 71.1k | uint32_t i; |
348 | 71.1k | LWPOLY *poly; |
349 | | |
350 | 71.1k | LWDEBUG(2,"Entering lwpoly_from_twkb_state"); |
351 | | |
352 | 71.1k | if ( s->is_empty ) |
353 | 503 | return lwpoly_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); |
354 | | |
355 | | /* Read number of rings */ |
356 | 70.6k | nrings = twkb_parse_state_uvarint32(s); |
357 | | |
358 | | /* Start w/ empty polygon */ |
359 | 70.6k | poly = lwpoly_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); |
360 | | |
361 | 70.6k | LWDEBUGF(4,"Polygon has %d rings", nrings); |
362 | | |
363 | | /* Empty polygon? */ |
364 | 70.6k | if( nrings == 0 ) |
365 | 68.9k | return poly; |
366 | | |
367 | 1.63k | if (!twkb_parse_state_has_min_bytes(s, nrings, 1)) |
368 | 0 | return poly; |
369 | | |
370 | 40.1k | for( i = 0; i < nrings; i++ ) |
371 | 38.5k | { |
372 | | /* Ret number of points */ |
373 | 38.5k | uint32_t npoints = twkb_parse_state_uvarint32(s); |
374 | 38.5k | POINTARRAY *pa = ptarray_from_twkb_state(s, npoints); |
375 | | |
376 | | /* Skip empty rings */ |
377 | 38.5k | if (pa == NULL) |
378 | 0 | continue; |
379 | | |
380 | | /* Force first and last points to be the same. */ |
381 | 38.5k | if( ! ptarray_is_closed_2d(pa) ) |
382 | 3.00k | { |
383 | 3.00k | POINT4D pt; |
384 | 3.00k | getPoint4d_p(pa, 0, &pt); |
385 | 3.00k | ptarray_append_point(pa, &pt, LW_FALSE); |
386 | 3.00k | } |
387 | | |
388 | | /* Check for at least four points. */ |
389 | 38.5k | if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) |
390 | 0 | { |
391 | 0 | LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype)); |
392 | 0 | lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype)); |
393 | 0 | return NULL; |
394 | 0 | } |
395 | | |
396 | | /* Add ring to polygon */ |
397 | 38.5k | if ( lwpoly_add_ring(poly, pa) == LW_FAILURE ) |
398 | 0 | { |
399 | 0 | LWDEBUG(2, "Unable to add ring to polygon"); |
400 | 0 | lwerror("Unable to add ring to polygon"); |
401 | 0 | } |
402 | | |
403 | 38.5k | } |
404 | 1.63k | return poly; |
405 | 1.63k | } |
406 | | |
407 | | |
408 | | /** |
409 | | * MULTIPOINT |
410 | | */ |
411 | | static LWCOLLECTION* lwmultipoint_from_twkb_state(twkb_parse_state *s) |
412 | 7.24k | { |
413 | 7.24k | uint32_t ngeoms, i; |
414 | 7.24k | LWGEOM *geom = NULL; |
415 | 7.24k | LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); |
416 | | |
417 | 7.24k | LWDEBUG(2,"Entering lwmultipoint_from_twkb_state"); |
418 | | |
419 | 7.24k | if ( s->is_empty ) |
420 | 5.99k | return col; |
421 | | |
422 | | /* Read number of geometries */ |
423 | 1.24k | ngeoms = twkb_parse_state_uvarint32(s); |
424 | 1.24k | LWDEBUGF(4,"Number of geometries %d", ngeoms); |
425 | | |
426 | 1.24k | if (s->has_idlist && !twkb_parse_state_has_min_bytes(s, ngeoms, 1)) |
427 | 0 | return col; |
428 | | |
429 | | /* It has an idlist, we need to skip that */ |
430 | 1.24k | if ( s->has_idlist ) |
431 | 525 | { |
432 | 130k | for ( i = 0; i < ngeoms; i++ ) |
433 | 130k | twkb_parse_state_varint_skip(s); |
434 | 525 | } |
435 | | |
436 | 1.24k | if (!twkb_parse_state_has_min_bytes(s, ngeoms, s->ndims)) |
437 | 0 | return col; |
438 | | |
439 | 159k | for ( i = 0; i < ngeoms; i++ ) |
440 | 158k | { |
441 | 158k | geom = lwpoint_as_lwgeom(lwpoint_from_twkb_state(s)); |
442 | 158k | if ( lwcollection_add_lwgeom(col, geom) == NULL ) |
443 | 0 | { |
444 | 0 | lwerror("Unable to add geometry (%p) to collection (%p)", (void *) geom, (void *) col); |
445 | 0 | return NULL; |
446 | 0 | } |
447 | 158k | } |
448 | | |
449 | 1.24k | return col; |
450 | 1.24k | } |
451 | | |
452 | | /** |
453 | | * MULTILINESTRING |
454 | | */ |
455 | | static LWCOLLECTION* lwmultiline_from_twkb_state(twkb_parse_state *s) |
456 | 1.76k | { |
457 | 1.76k | uint32_t ngeoms, i; |
458 | 1.76k | LWGEOM *geom = NULL; |
459 | 1.76k | LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); |
460 | | |
461 | 1.76k | LWDEBUG(2,"Entering lwmultilinestring_from_twkb_state"); |
462 | | |
463 | 1.76k | if ( s->is_empty ) |
464 | 564 | return col; |
465 | | |
466 | | /* Read number of geometries */ |
467 | 1.20k | ngeoms = twkb_parse_state_uvarint32(s); |
468 | | |
469 | 1.20k | LWDEBUGF(4,"Number of geometries %d",ngeoms); |
470 | | |
471 | 1.20k | if (s->has_idlist && !twkb_parse_state_has_min_bytes(s, ngeoms, 1)) |
472 | 0 | return col; |
473 | | |
474 | | /* It has an idlist, we need to skip that */ |
475 | 1.20k | if ( s->has_idlist ) |
476 | 481 | { |
477 | 111k | for ( i = 0; i < ngeoms; i++ ) |
478 | 110k | twkb_parse_state_varint_skip(s); |
479 | 481 | } |
480 | | |
481 | 1.20k | if (!twkb_parse_state_has_min_bytes(s, ngeoms, 1)) |
482 | 0 | return col; |
483 | | |
484 | 184k | for ( i = 0; i < ngeoms; i++ ) |
485 | 183k | { |
486 | 183k | geom = lwline_as_lwgeom(lwline_from_twkb_state(s)); |
487 | 183k | if ( lwcollection_add_lwgeom(col, geom) == NULL ) |
488 | 0 | { |
489 | 0 | lwerror("Unable to add geometry (%p) to collection (%p)", (void *) geom, (void *) col); |
490 | 0 | return NULL; |
491 | 0 | } |
492 | 183k | } |
493 | | |
494 | 1.20k | return col; |
495 | 1.20k | } |
496 | | |
497 | | /** |
498 | | * MULTIPOLYGON |
499 | | */ |
500 | | static LWCOLLECTION* lwmultipoly_from_twkb_state(twkb_parse_state *s) |
501 | 2.45k | { |
502 | 2.45k | uint32_t ngeoms, i; |
503 | 2.45k | LWGEOM *geom = NULL; |
504 | 2.45k | LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); |
505 | | |
506 | 2.45k | LWDEBUG(2,"Entering lwmultipolygon_from_twkb_state"); |
507 | | |
508 | 2.45k | if ( s->is_empty ) |
509 | 915 | return col; |
510 | | |
511 | | /* Read number of geometries */ |
512 | 1.54k | ngeoms = twkb_parse_state_uvarint32(s); |
513 | 1.54k | LWDEBUGF(4,"Number of geometries %d",ngeoms); |
514 | | |
515 | 1.54k | if (s->has_idlist && !twkb_parse_state_has_min_bytes(s, ngeoms, 1)) |
516 | 0 | return col; |
517 | | |
518 | | /* It has an idlist, we need to skip that */ |
519 | 1.54k | if ( s->has_idlist ) |
520 | 516 | { |
521 | 111k | for ( i = 0; i < ngeoms; i++ ) |
522 | 110k | twkb_parse_state_varint_skip(s); |
523 | 516 | } |
524 | | |
525 | 1.54k | if (!twkb_parse_state_has_min_bytes(s, ngeoms, 1)) |
526 | 0 | return col; |
527 | | |
528 | 71.5k | for ( i = 0; i < ngeoms; i++ ) |
529 | 70.0k | { |
530 | 70.0k | geom = lwpoly_as_lwgeom(lwpoly_from_twkb_state(s)); |
531 | 70.0k | if ( lwcollection_add_lwgeom(col, geom) == NULL ) |
532 | 0 | { |
533 | 0 | lwerror("Unable to add geometry (%p) to collection (%p)", (void *) geom, (void *) col); |
534 | 0 | return NULL; |
535 | 0 | } |
536 | 70.0k | } |
537 | | |
538 | 1.54k | return col; |
539 | 1.54k | } |
540 | | |
541 | | |
542 | | /** |
543 | | * COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE |
544 | | **/ |
545 | | static LWCOLLECTION* lwcollection_from_twkb_state(twkb_parse_state *s) |
546 | 7.45k | { |
547 | 7.45k | uint32_t ngeoms, i; |
548 | 7.45k | LWGEOM *geom = NULL; |
549 | 7.45k | LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); |
550 | 7.45k | uint8_t start_depth = s->depth; |
551 | | |
552 | 7.45k | LWDEBUG(2,"Entering lwcollection_from_twkb_state"); |
553 | | |
554 | 7.45k | if ( s->is_empty ) |
555 | 1.17k | return col; |
556 | | |
557 | | /* Read number of geometries */ |
558 | 6.28k | ngeoms = twkb_parse_state_uvarint32(s); |
559 | | |
560 | 6.28k | LWDEBUGF(4,"Number of geometries %d",ngeoms); |
561 | | |
562 | 6.28k | if (s->has_idlist && !twkb_parse_state_has_min_bytes(s, ngeoms, 1)) |
563 | 0 | return col; |
564 | | |
565 | | /* It has an idlist, we need to skip that */ |
566 | 6.28k | if ( s->has_idlist ) |
567 | 988 | { |
568 | 143k | for ( i = 0; i < ngeoms; i++ ) |
569 | 142k | twkb_parse_state_varint_skip(s); |
570 | 988 | } |
571 | | |
572 | 6.28k | if (!twkb_parse_state_has_min_bytes(s, ngeoms, 2)) |
573 | 0 | return col; |
574 | | |
575 | 6.28k | s->depth++; |
576 | 6.28k | if (s->depth >= LW_PARSER_MAX_DEPTH) |
577 | 3 | { |
578 | 3 | lwcollection_free(col); |
579 | 3 | lwerror("Geometry has too many chained collections"); |
580 | 3 | s->depth = start_depth; |
581 | 3 | return NULL; |
582 | 3 | } |
583 | | |
584 | 39.0k | for ( i = 0; i < ngeoms; i++ ) |
585 | 32.7k | { |
586 | 32.7k | geom = lwgeom_from_twkb_state(s); |
587 | 32.7k | if (!geom || lwcollection_add_lwgeom(col, geom) == NULL) |
588 | 0 | { |
589 | 0 | lwgeom_free(geom); |
590 | 0 | lwcollection_free(col); |
591 | 0 | s->depth = start_depth; |
592 | 0 | return NULL; |
593 | 0 | } |
594 | 32.7k | } |
595 | | |
596 | 6.28k | s->depth--; |
597 | | |
598 | 6.28k | return col; |
599 | 6.28k | } |
600 | | |
601 | | |
602 | | static void header_from_twkb_state(twkb_parse_state *s) |
603 | 35.3k | { |
604 | 35.3k | LWDEBUG(2,"Entering magicbyte_from_twkb_state"); |
605 | | |
606 | 35.3k | uint8_t extended_dims; |
607 | | |
608 | | /* Read the first two bytes */ |
609 | 35.3k | uint8_t type_precision = byte_from_twkb_state(s); |
610 | 35.3k | uint8_t metadata = byte_from_twkb_state(s); |
611 | | |
612 | | /* Strip type and precision out of first byte */ |
613 | 35.3k | uint8_t type = type_precision & 0x0F; |
614 | 35.3k | int8_t precision = unzigzag8((type_precision & 0xF0) >> 4); |
615 | | |
616 | | /* Convert TWKB type to internal type */ |
617 | 35.3k | s->lwtype = lwtype_from_twkb_type(type); |
618 | | |
619 | | /* Convert the precision into factor */ |
620 | 35.3k | s->factor = pow(10, (double)precision); |
621 | | |
622 | | /* Strip metadata flags out of second byte */ |
623 | 35.3k | s->has_bbox = metadata & 0x01; |
624 | 35.3k | s->has_size = (metadata & 0x02) >> 1; |
625 | 35.3k | s->has_idlist = (metadata & 0x04) >> 2; |
626 | 35.3k | extended_dims = (metadata & 0x08) >> 3; |
627 | 35.3k | s->is_empty = (metadata & 0x10) >> 4; |
628 | | |
629 | | /* Flag for higher dims means read a third byte */ |
630 | 35.3k | if ( extended_dims ) |
631 | 1.19k | { |
632 | 1.19k | int8_t precision_z, precision_m; |
633 | | |
634 | 1.19k | extended_dims = byte_from_twkb_state(s); |
635 | | |
636 | | /* Strip Z/M presence and precision from ext byte */ |
637 | 1.19k | s->has_z = (extended_dims & 0x01); |
638 | 1.19k | s->has_m = (extended_dims & 0x02) >> 1; |
639 | 1.19k | precision_z = (extended_dims & 0x1C) >> 2; |
640 | 1.19k | precision_m = (extended_dims & 0xE0) >> 5; |
641 | | |
642 | | /* Convert the precision into factor */ |
643 | 1.19k | s->factor_z = pow(10, (double)precision_z); |
644 | 1.19k | s->factor_m = pow(10, (double)precision_m); |
645 | 1.19k | } |
646 | 34.1k | else |
647 | 34.1k | { |
648 | 34.1k | s->has_z = 0; |
649 | 34.1k | s->has_m = 0; |
650 | 34.1k | s->factor_z = 0; |
651 | 34.1k | s->factor_m = 0; |
652 | 34.1k | } |
653 | | |
654 | | /* Read the size, if there is one */ |
655 | 35.3k | if ( s->has_size ) |
656 | 1.53k | { |
657 | 1.53k | s->size = twkb_parse_state_uvarint(s); |
658 | 1.53k | } |
659 | | |
660 | | /* Calculate the number of dimensions */ |
661 | 35.3k | s->ndims = 2 + s->has_z + s->has_m; |
662 | | |
663 | 35.3k | return; |
664 | 35.3k | } |
665 | | |
666 | | |
667 | | |
668 | | /** |
669 | | * Generic handling for TWKB geometries. The front of every TWKB geometry |
670 | | * (including those embedded in collections) is a type byte and metadata byte, |
671 | | * then optional size, bbox, etc. Read those, then switch to particular type |
672 | | * handling code. |
673 | | */ |
674 | | LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s) |
675 | 35.3k | { |
676 | 35.3k | GBOX bbox; |
677 | 35.3k | LWGEOM *geom = NULL; |
678 | 35.3k | uint32_t has_bbox = LW_FALSE; |
679 | 35.3k | int i; |
680 | | |
681 | | /* Read the first two bytes, and optional */ |
682 | | /* extended precision info and optional size info */ |
683 | 35.3k | header_from_twkb_state(s); |
684 | | |
685 | | /* Just experienced a geometry header, so now we */ |
686 | | /* need to reset our coordinate deltas */ |
687 | 176k | for ( i = 0; i < TWKB_IN_MAXCOORDS; i++ ) |
688 | 141k | { |
689 | 141k | s->coords[i] = 0.0; |
690 | 141k | } |
691 | | |
692 | | /* Read the bounding box, is there is one */ |
693 | 35.3k | if ( s->has_bbox ) |
694 | 4.89k | { |
695 | | /* Initialize */ |
696 | 4.89k | has_bbox = s->has_bbox; |
697 | 4.89k | memset(&bbox, 0, sizeof(GBOX)); |
698 | 4.89k | bbox.flags = lwflags(s->has_z, s->has_m, 0); |
699 | | |
700 | | /* X */ |
701 | 4.89k | bbox.xmin = twkb_parse_state_double(s, s->factor); |
702 | 4.89k | bbox.xmax = bbox.xmin + twkb_parse_state_double(s, s->factor); |
703 | | /* Y */ |
704 | 4.89k | bbox.ymin = twkb_parse_state_double(s, s->factor); |
705 | 4.89k | bbox.ymax = bbox.ymin + twkb_parse_state_double(s, s->factor); |
706 | | /* Z */ |
707 | 4.89k | if ( s->has_z ) |
708 | 325 | { |
709 | 325 | bbox.zmin = twkb_parse_state_double(s, s->factor_z); |
710 | 325 | bbox.zmax = bbox.zmin + twkb_parse_state_double(s, s->factor_z); |
711 | 325 | } |
712 | | /* M */ |
713 | 4.89k | if ( s->has_m ) |
714 | 553 | { |
715 | 553 | bbox.mmin = twkb_parse_state_double(s, s->factor_m); |
716 | 553 | bbox.mmax = bbox.mmin + twkb_parse_state_double(s, s->factor_m); |
717 | 553 | } |
718 | 4.89k | } |
719 | | |
720 | | /* Switch to code for the particular type we're dealing with */ |
721 | 35.3k | switch( s->lwtype ) |
722 | 35.3k | { |
723 | 13.9k | case POINTTYPE: |
724 | 13.9k | geom = lwpoint_as_lwgeom(lwpoint_from_twkb_state(s)); |
725 | 13.9k | break; |
726 | 1.19k | case LINETYPE: |
727 | 1.19k | geom = lwline_as_lwgeom(lwline_from_twkb_state(s)); |
728 | 1.19k | break; |
729 | 1.11k | case POLYGONTYPE: |
730 | 1.11k | geom = lwpoly_as_lwgeom(lwpoly_from_twkb_state(s)); |
731 | 1.11k | break; |
732 | 7.24k | case MULTIPOINTTYPE: |
733 | 7.24k | geom = lwcollection_as_lwgeom(lwmultipoint_from_twkb_state(s)); |
734 | 7.24k | break; |
735 | 1.76k | case MULTILINETYPE: |
736 | 1.76k | geom = lwcollection_as_lwgeom(lwmultiline_from_twkb_state(s)); |
737 | 1.76k | break; |
738 | 2.45k | case MULTIPOLYGONTYPE: |
739 | 2.45k | geom = lwcollection_as_lwgeom(lwmultipoly_from_twkb_state(s)); |
740 | 2.45k | break; |
741 | 7.45k | case COLLECTIONTYPE: |
742 | 7.45k | geom = lwcollection_as_lwgeom(lwcollection_from_twkb_state(s)); |
743 | 7.45k | break; |
744 | | /* Unknown type! */ |
745 | 0 | default: |
746 | 0 | s->error = LW_TRUE; |
747 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(s->lwtype)); |
748 | 0 | break; |
749 | 35.3k | } |
750 | | |
751 | 29.4k | if (s->error) |
752 | 0 | { |
753 | 0 | lwgeom_free(geom); |
754 | 0 | return NULL; |
755 | 0 | } |
756 | | |
757 | 29.4k | if (has_bbox && geom) |
758 | 4.19k | geom->bbox = gbox_clone(&bbox); |
759 | | |
760 | 29.4k | return geom; |
761 | 29.4k | } |
762 | | |
763 | | |
764 | | /** |
765 | | * WKB inputs *must* have a declared size, to prevent malformed WKB from reading |
766 | | * off the end of the memory segment (this stops a malevolent user from declaring |
767 | | * a one-ring polygon to have 10 rings, causing the WKB reader to walk off the |
768 | | * end of the memory). |
769 | | * |
770 | | * Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD, |
771 | | * LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL |
772 | | */ |
773 | | LWGEOM* lwgeom_from_twkb(const uint8_t *twkb, size_t twkb_size, char check) |
774 | 2.62k | { |
775 | 2.62k | int64_t coords[TWKB_IN_MAXCOORDS] = {0, 0, 0, 0}; |
776 | 2.62k | twkb_parse_state s; |
777 | | |
778 | 2.62k | LWDEBUG(2,"Entering lwgeom_from_twkb"); |
779 | 2.62k | LWDEBUGF(4,"twkb_size: %d",(int) twkb_size); |
780 | | |
781 | | /* Zero out the state */ |
782 | 2.62k | memset(&s, 0, sizeof(twkb_parse_state)); |
783 | | |
784 | | /* Initialize the state appropriately */ |
785 | 2.62k | s.twkb = s.pos = twkb; |
786 | 2.62k | s.twkb_end = twkb + twkb_size; |
787 | 2.62k | s.check = check; |
788 | 2.62k | s.coords = coords; |
789 | | |
790 | | /* Read the rest of the geometry */ |
791 | 2.62k | return lwgeom_from_twkb_state(&s); |
792 | 2.62k | } |