/src/postgis/liblwgeom/lwout_wkb.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 (C) 2009 Paul Ramsey <pramsey@cleverelephant.ca> |
22 | | * |
23 | | **********************************************************************/ |
24 | | |
25 | | |
26 | | #include <math.h> |
27 | | #include <stddef.h> // for ptrdiff_t |
28 | | |
29 | | #include "liblwgeom_internal.h" |
30 | | #include "lwgeom_log.h" |
31 | | |
32 | | static uint8_t* lwgeom_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant); |
33 | | static size_t lwgeom_to_wkb_size(const LWGEOM *geom, uint8_t variant); |
34 | | |
35 | | /* |
36 | | * Look-up table for hex writer |
37 | | */ |
38 | | static char *hexchr = "0123456789ABCDEF"; |
39 | | |
40 | | char* hexbytes_from_bytes(const uint8_t *bytes, size_t size) |
41 | 0 | { |
42 | 0 | char *hex; |
43 | 0 | uint32_t i; |
44 | 0 | if ( ! bytes || ! size ) |
45 | 0 | { |
46 | 0 | lwerror("hexbutes_from_bytes: invalid input"); |
47 | 0 | return NULL; |
48 | 0 | } |
49 | 0 | hex = lwalloc(size * 2 + 1); |
50 | 0 | hex[2*size] = '\0'; |
51 | 0 | for( i = 0; i < size; i++ ) |
52 | 0 | { |
53 | | /* Top four bits to 0-F */ |
54 | 0 | hex[2*i] = hexchr[bytes[i] >> 4]; |
55 | | /* Bottom four bits to 0-F */ |
56 | 0 | hex[2*i+1] = hexchr[bytes[i] & 0x0F]; |
57 | 0 | } |
58 | 0 | return hex; |
59 | 0 | } |
60 | | |
61 | | /* |
62 | | * Optional SRID |
63 | | */ |
64 | | static int lwgeom_wkb_needs_srid(const LWGEOM *geom, uint8_t variant) |
65 | 0 | { |
66 | | /* Sub-components of collections inherit their SRID from the parent. |
67 | | We force that behavior with the WKB_NO_SRID flag */ |
68 | 0 | if ( variant & WKB_NO_SRID ) |
69 | 0 | return LW_FALSE; |
70 | | |
71 | | /* We can only add an SRID if the geometry has one, and the |
72 | | WKB form is extended */ |
73 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_has_srid(geom) ) |
74 | 0 | return LW_TRUE; |
75 | | |
76 | | /* Everything else doesn't get an SRID */ |
77 | 0 | return LW_FALSE; |
78 | 0 | } |
79 | | |
80 | | /** |
81 | | * Compute the WKB geometry type code for a given LWGEOM and WKB variant. |
82 | | * |
83 | | * Maps the internal geometry type to the appropriate WKB type code and |
84 | | * applies dimension and SRID encoding offsets/flags according to the |
85 | | * selected variant. NURBSCURVETYPE always uses ISO-style dimension offsets |
86 | | * (1000/2000/3000). For non-NURBS geometries: |
87 | | * - WKB_EXTENDED sets Z/M bit flags and may set the SRID flag (determined |
88 | | * by lwgeom_wkb_needs_srid). |
89 | | * - WKB_ISO adds numeric dimension offsets (1000 for Z, 2000 for M). |
90 | | * |
91 | | * If the geometry type is unsupported the function logs an error and |
92 | | * returns 0. |
93 | | * |
94 | | * @param geom Geometry to encode (must be non-NULL). |
95 | | * @param variant Bitflags describing WKB variant/encoding (e.g. WKB_EXTENDED, WKB_ISO, WKB_NO_SRID). |
96 | | * @return WKB geometry type code with any applicable dimension/SRID modifiers. |
97 | | */ |
98 | | static uint32_t lwgeom_wkb_type(const LWGEOM *geom, uint8_t variant) |
99 | 0 | { |
100 | 0 | uint32_t wkb_type = 0; |
101 | |
|
102 | 0 | switch ( geom->type ) |
103 | 0 | { |
104 | 0 | case POINTTYPE: |
105 | 0 | wkb_type = WKB_POINT_TYPE; |
106 | 0 | break; |
107 | 0 | case LINETYPE: |
108 | 0 | wkb_type = WKB_LINESTRING_TYPE; |
109 | 0 | break; |
110 | 0 | case NURBSCURVETYPE: |
111 | 0 | wkb_type = WKB_NURBSCURVE_TYPE; |
112 | 0 | break; |
113 | 0 | case POLYGONTYPE: |
114 | 0 | wkb_type = WKB_POLYGON_TYPE; |
115 | 0 | break; |
116 | 0 | case MULTIPOINTTYPE: |
117 | 0 | wkb_type = WKB_MULTIPOINT_TYPE; |
118 | 0 | break; |
119 | 0 | case MULTILINETYPE: |
120 | 0 | wkb_type = WKB_MULTILINESTRING_TYPE; |
121 | 0 | break; |
122 | 0 | case MULTIPOLYGONTYPE: |
123 | 0 | wkb_type = WKB_MULTIPOLYGON_TYPE; |
124 | 0 | break; |
125 | 0 | case COLLECTIONTYPE: |
126 | 0 | wkb_type = WKB_GEOMETRYCOLLECTION_TYPE; |
127 | 0 | break; |
128 | 0 | case CIRCSTRINGTYPE: |
129 | 0 | wkb_type = WKB_CIRCULARSTRING_TYPE; |
130 | 0 | break; |
131 | 0 | case COMPOUNDTYPE: |
132 | 0 | wkb_type = WKB_COMPOUNDCURVE_TYPE; |
133 | 0 | break; |
134 | 0 | case CURVEPOLYTYPE: |
135 | 0 | wkb_type = WKB_CURVEPOLYGON_TYPE; |
136 | 0 | break; |
137 | 0 | case MULTICURVETYPE: |
138 | 0 | wkb_type = WKB_MULTICURVE_TYPE; |
139 | 0 | break; |
140 | 0 | case MULTISURFACETYPE: |
141 | 0 | wkb_type = WKB_MULTISURFACE_TYPE; |
142 | 0 | break; |
143 | 0 | case POLYHEDRALSURFACETYPE: |
144 | 0 | wkb_type = WKB_POLYHEDRALSURFACE_TYPE; |
145 | 0 | break; |
146 | 0 | case TINTYPE: |
147 | 0 | wkb_type = WKB_TIN_TYPE; |
148 | 0 | break; |
149 | 0 | case TRIANGLETYPE: |
150 | 0 | wkb_type = WKB_TRIANGLE_TYPE; |
151 | 0 | break; |
152 | 0 | default: |
153 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); |
154 | 0 | } |
155 | | |
156 | | /* NURBS curves always use ISO dimension encoding */ |
157 | 0 | if ( geom->type == NURBSCURVETYPE ) |
158 | 0 | { |
159 | 0 | if ( FLAGS_GET_Z(geom->flags) && !FLAGS_GET_M(geom->flags) ) |
160 | 0 | wkb_type += 1000; /* Z: 1000 offset */ |
161 | 0 | else if ( FLAGS_GET_M(geom->flags) && !FLAGS_GET_Z(geom->flags) ) |
162 | 0 | wkb_type += 2000; /* M: 2000 offset */ |
163 | 0 | else if ( FLAGS_GET_Z(geom->flags) && FLAGS_GET_M(geom->flags) ) |
164 | 0 | wkb_type += 3000; /* ZM: 3000 offset */ |
165 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_wkb_needs_srid(geom, variant) ) |
166 | 0 | wkb_type |= WKBSRIDFLAG; |
167 | 0 | } |
168 | 0 | else if ( variant & WKB_EXTENDED ) |
169 | 0 | { |
170 | 0 | if ( FLAGS_GET_Z(geom->flags) ) |
171 | 0 | wkb_type |= WKBZOFFSET; |
172 | 0 | if ( FLAGS_GET_M(geom->flags) ) |
173 | 0 | wkb_type |= WKBMOFFSET; |
174 | | /* if ( geom->srid != SRID_UNKNOWN && ! (variant & WKB_NO_SRID) ) */ |
175 | 0 | if ( lwgeom_wkb_needs_srid(geom, variant) ) |
176 | 0 | wkb_type |= WKBSRIDFLAG; |
177 | 0 | } |
178 | 0 | else if ( variant & WKB_ISO ) |
179 | 0 | { |
180 | | /* ISO encoding for other geometry types */ |
181 | 0 | if ( FLAGS_GET_Z(geom->flags) ) |
182 | 0 | wkb_type += 1000; |
183 | 0 | if ( FLAGS_GET_M(geom->flags) ) |
184 | 0 | wkb_type += 2000; |
185 | 0 | } |
186 | 0 | return wkb_type; |
187 | 0 | } |
188 | | |
189 | | /* |
190 | | * Endian |
191 | | */ |
192 | | static uint8_t* endian_to_wkb_buf(uint8_t *buf, uint8_t variant) |
193 | 0 | { |
194 | 0 | if ( variant & WKB_HEX ) |
195 | 0 | { |
196 | 0 | buf[0] = '0'; |
197 | 0 | buf[1] = ((variant & WKB_NDR) ? '1' : '0'); |
198 | 0 | return buf + 2; |
199 | 0 | } |
200 | 0 | else |
201 | 0 | { |
202 | 0 | buf[0] = ((variant & WKB_NDR) ? 1 : 0); |
203 | 0 | return buf + 1; |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | | /* |
208 | | * SwapBytes? |
209 | | */ |
210 | | static inline int wkb_swap_bytes(uint8_t variant) |
211 | | { |
212 | | /* If requested variant matches machine arch, we don't have to swap! */ |
213 | | if (((variant & WKB_NDR) && !IS_BIG_ENDIAN) || |
214 | | ((!(variant & WKB_NDR)) && IS_BIG_ENDIAN)) |
215 | | { |
216 | | return LW_FALSE; |
217 | | } |
218 | | return LW_TRUE; |
219 | | } |
220 | | |
221 | | /** |
222 | | * Write a 32-bit unsigned integer into a WKB buffer honoring variant (binary or hex) |
223 | | * and requested endianness, and return the pointer to the next write position. |
224 | | * |
225 | | * The function writes the 32-bit value `ival` into `buf` using WKB_INT_SIZE bytes |
226 | | * (or twice that many ASCII hex characters when WKB_HEX is set in `variant`). |
227 | | * If the requested byte order differs from the machine's native order the byte |
228 | | * order is swapped before writing. On mismatch between sizeof(int) and |
229 | | * WKB_INT_SIZE an error is logged. |
230 | | * |
231 | | * @param ival The 32-bit unsigned integer to encode. |
232 | | * @param buf Destination buffer where encoded bytes (or hex chars) are written. |
233 | | * Must have space for at least WKB_INT_SIZE bytes (or 2*WKB_INT_SIZE |
234 | | * chars for hex variants). |
235 | | * @param variant Bitmask specifying WKB encoding variant (may include WKB_HEX and |
236 | | * endianness flags). Controls hex vs binary output and byte order. |
237 | | * @return Pointer into `buf` immediately after the bytes written. |
238 | | */ |
239 | | static uint8_t * |
240 | | integer_to_wkb_buf(const uint32_t ival, uint8_t *buf, uint8_t variant) |
241 | 0 | { |
242 | 0 | uint8_t *iptr = (uint8_t *)(&ival); |
243 | 0 | int i = 0; |
244 | |
|
245 | 0 | if ( sizeof(int) != WKB_INT_SIZE ) |
246 | 0 | { |
247 | 0 | lwerror("Machine int size is not %d bytes!", WKB_INT_SIZE); |
248 | 0 | } |
249 | 0 | LWDEBUGF(4, "Writing value '%u'", ival); |
250 | 0 | if ( variant & WKB_HEX ) |
251 | 0 | { |
252 | 0 | int swap = wkb_swap_bytes(variant); |
253 | | /* Machine/request arch mismatch, so flip byte order */ |
254 | 0 | for ( i = 0; i < WKB_INT_SIZE; i++ ) |
255 | 0 | { |
256 | 0 | int j = (swap ? WKB_INT_SIZE - 1 - i : i); |
257 | 0 | uint8_t b = iptr[j]; |
258 | | /* Top four bits to 0-F */ |
259 | 0 | buf[2*i] = hexchr[b >> 4]; |
260 | | /* Bottom four bits to 0-F */ |
261 | 0 | buf[2*i+1] = hexchr[b & 0x0F]; |
262 | 0 | } |
263 | 0 | return buf + (2 * WKB_INT_SIZE); |
264 | 0 | } |
265 | 0 | else |
266 | 0 | { |
267 | | /* Machine/request arch mismatch, so flip byte order */ |
268 | 0 | if ( wkb_swap_bytes(variant) ) |
269 | 0 | { |
270 | 0 | for ( i = 0; i < WKB_INT_SIZE; i++ ) |
271 | 0 | { |
272 | 0 | buf[i] = iptr[WKB_INT_SIZE - 1 - i]; |
273 | 0 | } |
274 | 0 | } |
275 | | /* If machine arch and requested arch match, don't flip byte order */ |
276 | 0 | else |
277 | 0 | { |
278 | 0 | memcpy(buf, iptr, WKB_INT_SIZE); |
279 | 0 | } |
280 | 0 | return buf + WKB_INT_SIZE; |
281 | 0 | } |
282 | 0 | } |
283 | | |
284 | | /** |
285 | | * Write a single byte into a WKB buffer, using hex-encoding when requested. |
286 | | * |
287 | | * If the WKB_HEX bit is set in variant, writes two ASCII hex characters |
288 | | * representing the byte and returns buf + 2; otherwise writes the raw byte |
289 | | * and returns buf + 1. |
290 | | * |
291 | | * @param bval Byte value to write. |
292 | | * @param buf Destination buffer to write into; must have at least 2 bytes if hex encoding is used. |
293 | | * @param variant Bitmask of WKB variant flags (tests WKB_HEX). |
294 | | * @returns Pointer to the next write position in buf after the written data. |
295 | | */ |
296 | | static uint8_t* byte_to_wkb_buf(const uint8_t bval, uint8_t *buf, uint8_t variant) |
297 | 0 | { |
298 | 0 | if ( variant & WKB_HEX ) |
299 | 0 | { |
300 | | /* Convert single byte to 2 hex characters */ |
301 | 0 | buf[0] = hexchr[bval >> 4]; /* Top four bits to 0-F */ |
302 | 0 | buf[1] = hexchr[bval & 0x0F]; /* Bottom four bits to 0-F */ |
303 | 0 | return buf + 2; |
304 | 0 | } |
305 | 0 | else |
306 | 0 | { |
307 | 0 | buf[0] = bval; |
308 | 0 | return buf + 1; |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | /** |
313 | | * Write an IEEE-754 NaN double into a WKB buffer. |
314 | | * |
315 | | * Writes an 8-byte NaN pattern to buf using either little-endian (NDR) or |
316 | | * big-endian (XDR) byte order and either binary or hex-encoded output, |
317 | | * controlled by flags in variant (WKB_NDR and WKB_HEX). Advances and returns |
318 | | * a pointer to the position immediately after the written data. |
319 | | * |
320 | | * @param buf Buffer to write into; must have space for 8 raw bytes or 16 hex bytes. |
321 | | * @param variant Bitmask of WKB flags (may include WKB_NDR for NDR vs XDR and |
322 | | * WKB_HEX to request hex-encoding). |
323 | | * @return Pointer to buf advanced past the written NaN bytes (buf + 8 for binary, |
324 | | * buf + 16 for hex). |
325 | | */ |
326 | | static uint8_t* double_nan_to_wkb_buf(uint8_t *buf, uint8_t variant) |
327 | 0 | { |
328 | 0 | #define NAN_SIZE 8 |
329 | 0 | const uint8_t ndr_nan[NAN_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f}; |
330 | 0 | const uint8_t xdr_nan[NAN_SIZE] = {0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
331 | |
|
332 | 0 | if ( variant & WKB_HEX ) |
333 | 0 | { |
334 | 0 | for (int i = 0; i < NAN_SIZE; i++) |
335 | 0 | { |
336 | 0 | uint8_t b = (variant & WKB_NDR) ? ndr_nan[i] : xdr_nan[i]; |
337 | | /* Top four bits to 0-F */ |
338 | 0 | buf[2*i] = hexchr[b >> 4]; |
339 | | /* Bottom four bits to 0-F */ |
340 | 0 | buf[2*i + 1] = hexchr[b & 0x0F]; |
341 | 0 | } |
342 | 0 | return buf + (2 * NAN_SIZE); |
343 | 0 | } |
344 | 0 | else |
345 | 0 | { |
346 | 0 | for (int i = 0; i < NAN_SIZE; i++) |
347 | 0 | { |
348 | 0 | buf[i] = (variant & WKB_NDR) ? ndr_nan[i] : xdr_nan[i];; |
349 | 0 | } |
350 | 0 | return buf + NAN_SIZE; |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | | /* |
355 | | * Float64 |
356 | | */ |
357 | | static uint8_t* double_to_wkb_buf(const double d, uint8_t *buf, uint8_t variant) |
358 | 0 | { |
359 | 0 | uint8_t *dptr = (uint8_t *)(&d); |
360 | 0 | int i = 0; |
361 | |
|
362 | 0 | if ( sizeof(double) != WKB_DOUBLE_SIZE ) |
363 | 0 | { |
364 | 0 | lwerror("Machine double size is not %d bytes!", WKB_DOUBLE_SIZE); |
365 | 0 | } |
366 | |
|
367 | 0 | if ( variant & WKB_HEX ) |
368 | 0 | { |
369 | 0 | int swap = wkb_swap_bytes(variant); |
370 | | /* Machine/request arch mismatch, so flip byte order */ |
371 | 0 | for ( i = 0; i < WKB_DOUBLE_SIZE; i++ ) |
372 | 0 | { |
373 | 0 | int j = (swap ? WKB_DOUBLE_SIZE - 1 - i : i); |
374 | 0 | uint8_t b = dptr[j]; |
375 | | /* Top four bits to 0-F */ |
376 | 0 | buf[2*i] = hexchr[b >> 4]; |
377 | | /* Bottom four bits to 0-F */ |
378 | 0 | buf[2*i+1] = hexchr[b & 0x0F]; |
379 | 0 | } |
380 | 0 | return buf + (2 * WKB_DOUBLE_SIZE); |
381 | 0 | } |
382 | 0 | else |
383 | 0 | { |
384 | | /* Machine/request arch mismatch, so flip byte order */ |
385 | 0 | if ( wkb_swap_bytes(variant) ) |
386 | 0 | { |
387 | 0 | for ( i = 0; i < WKB_DOUBLE_SIZE; i++ ) |
388 | 0 | { |
389 | 0 | buf[i] = dptr[WKB_DOUBLE_SIZE - 1 - i]; |
390 | 0 | } |
391 | 0 | } |
392 | | /* If machine arch and requested arch match, don't flip byte order */ |
393 | 0 | else |
394 | 0 | { |
395 | 0 | memcpy(buf, dptr, WKB_DOUBLE_SIZE); |
396 | 0 | } |
397 | 0 | return buf + WKB_DOUBLE_SIZE; |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | |
402 | | /* |
403 | | * Empty |
404 | | */ |
405 | | static size_t empty_to_wkb_size(const LWGEOM *geom, uint8_t variant) |
406 | 0 | { |
407 | | /* endian byte + type integer */ |
408 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; |
409 | | |
410 | | /* optional srid integer */ |
411 | 0 | if ( lwgeom_wkb_needs_srid(geom, variant) ) |
412 | 0 | size += WKB_INT_SIZE; |
413 | | |
414 | | /* Represent POINT EMPTY as POINT(NaN NaN) */ |
415 | 0 | if ( geom->type == POINTTYPE ) |
416 | 0 | { |
417 | 0 | const LWPOINT *pt = (LWPOINT*)geom; |
418 | 0 | size += WKB_DOUBLE_SIZE * FLAGS_NDIMS(pt->point->flags); |
419 | 0 | } |
420 | | /* num-elements */ |
421 | 0 | else |
422 | 0 | { |
423 | 0 | size += WKB_INT_SIZE; |
424 | 0 | } |
425 | |
|
426 | 0 | return size; |
427 | 0 | } |
428 | | |
429 | | static uint8_t* empty_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant) |
430 | 0 | { |
431 | 0 | uint32_t wkb_type = lwgeom_wkb_type(geom, variant); |
432 | | |
433 | | /* Set the endian flag */ |
434 | 0 | buf = endian_to_wkb_buf(buf, variant); |
435 | | |
436 | | /* Set the geometry type */ |
437 | 0 | buf = integer_to_wkb_buf(wkb_type, buf, variant); |
438 | | |
439 | | /* Set the SRID if necessary */ |
440 | 0 | if ( lwgeom_wkb_needs_srid(geom, variant) ) |
441 | 0 | buf = integer_to_wkb_buf(geom->srid, buf, variant); |
442 | | |
443 | | /* Represent POINT EMPTY as POINT(NaN NaN) */ |
444 | 0 | if ( geom->type == POINTTYPE ) |
445 | 0 | { |
446 | 0 | const LWPOINT *pt = (LWPOINT*)geom; |
447 | 0 | for (int i = 0; i < FLAGS_NDIMS(pt->point->flags); i++) |
448 | 0 | { |
449 | 0 | buf = double_nan_to_wkb_buf(buf, variant); |
450 | 0 | } |
451 | 0 | } |
452 | | /* Everything else is flagged as empty using num-elements == 0 */ |
453 | 0 | else |
454 | 0 | { |
455 | | /* Set nrings/npoints/ngeoms to zero */ |
456 | 0 | buf = integer_to_wkb_buf(0, buf, variant); |
457 | 0 | } |
458 | |
|
459 | 0 | return buf; |
460 | 0 | } |
461 | | |
462 | | /* |
463 | | * POINTARRAY |
464 | | */ |
465 | | static size_t ptarray_to_wkb_size(const POINTARRAY *pa, uint8_t variant) |
466 | 0 | { |
467 | 0 | int dims = 2; |
468 | 0 | size_t size = 0; |
469 | |
|
470 | 0 | if ( variant & (WKB_ISO | WKB_EXTENDED) ) |
471 | 0 | dims = FLAGS_NDIMS(pa->flags); |
472 | | |
473 | | /* Include the npoints if it's not a POINT type) */ |
474 | 0 | if ( ! ( variant & WKB_NO_NPOINTS ) ) |
475 | 0 | size += WKB_INT_SIZE; |
476 | | |
477 | | /* size of the double list */ |
478 | 0 | size += (size_t)pa->npoints * dims * WKB_DOUBLE_SIZE; |
479 | |
|
480 | 0 | return size; |
481 | 0 | } |
482 | | |
483 | | static uint8_t* ptarray_to_wkb_buf(const POINTARRAY *pa, uint8_t *buf, uint8_t variant) |
484 | 0 | { |
485 | 0 | uint32_t dims = 2; |
486 | 0 | uint32_t pa_dims = FLAGS_NDIMS(pa->flags); |
487 | 0 | uint32_t i, j; |
488 | 0 | double *dbl_ptr; |
489 | | |
490 | | /* SFSQL is always 2-d. Extended and ISO use all available dimensions */ |
491 | 0 | if ( (variant & WKB_ISO) || (variant & WKB_EXTENDED) ) |
492 | 0 | dims = pa_dims; |
493 | | |
494 | | /* Set the number of points (if it's not a POINT type) */ |
495 | 0 | if ( ! ( variant & WKB_NO_NPOINTS ) ) |
496 | 0 | buf = integer_to_wkb_buf(pa->npoints, buf, variant); |
497 | | |
498 | | /* Bulk copy the coordinates when: dimensionality matches, output format */ |
499 | | /* is not hex, and output endian matches internal endian. */ |
500 | 0 | if ( pa->npoints && (dims == pa_dims) && ! wkb_swap_bytes(variant) && ! (variant & WKB_HEX) ) |
501 | 0 | { |
502 | 0 | size_t size = (size_t)pa->npoints * dims * WKB_DOUBLE_SIZE; |
503 | 0 | memcpy(buf, getPoint_internal(pa, 0), size); |
504 | 0 | buf += size; |
505 | 0 | } |
506 | | /* Copy coordinates one-by-one otherwise */ |
507 | 0 | else |
508 | 0 | { |
509 | 0 | for ( i = 0; i < pa->npoints; i++ ) |
510 | 0 | { |
511 | 0 | LWDEBUGF(4, "Writing point #%d", i); |
512 | 0 | dbl_ptr = (double*)getPoint_internal(pa, i); |
513 | 0 | for ( j = 0; j < dims; j++ ) |
514 | 0 | { |
515 | 0 | LWDEBUGF(4, "Writing dimension #%d (buf = %p)", j, buf); |
516 | 0 | buf = double_to_wkb_buf(dbl_ptr[j], buf, variant); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | } |
520 | 0 | LWDEBUGF(4, "Done (buf = %p)", buf); |
521 | 0 | return buf; |
522 | 0 | } |
523 | | |
524 | | /* |
525 | | * POINT |
526 | | */ |
527 | | static size_t lwpoint_to_wkb_size(const LWPOINT *pt, uint8_t variant) |
528 | 0 | { |
529 | | /* Endian flag + type number */ |
530 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; |
531 | | |
532 | | /* Only process empty at this level in the EXTENDED case */ |
533 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)pt) ) |
534 | 0 | return empty_to_wkb_size((LWGEOM*)pt, variant); |
535 | | |
536 | | /* Extended WKB needs space for optional SRID integer */ |
537 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) ) |
538 | 0 | size += WKB_INT_SIZE; |
539 | | |
540 | | /* Points */ |
541 | 0 | size += ptarray_to_wkb_size(pt->point, variant | WKB_NO_NPOINTS); |
542 | 0 | return size; |
543 | 0 | } |
544 | | |
545 | | static uint8_t* lwpoint_to_wkb_buf(const LWPOINT *pt, uint8_t *buf, uint8_t variant) |
546 | 0 | { |
547 | | /* Only process empty at this level in the EXTENDED case */ |
548 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)pt) ) |
549 | 0 | return empty_to_wkb_buf((LWGEOM*)pt, buf, variant); |
550 | | |
551 | | /* Set the endian flag */ |
552 | 0 | LWDEBUGF(4, "Entering function, buf = %p", buf); |
553 | 0 | buf = endian_to_wkb_buf(buf, variant); |
554 | 0 | LWDEBUGF(4, "Endian set, buf = %p", buf); |
555 | | /* Set the geometry type */ |
556 | 0 | buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)pt, variant), buf, variant); |
557 | 0 | LWDEBUGF(4, "Type set, buf = %p", buf); |
558 | | /* Set the optional SRID for extended variant */ |
559 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)pt, variant) ) |
560 | 0 | { |
561 | 0 | buf = integer_to_wkb_buf(pt->srid, buf, variant); |
562 | 0 | LWDEBUGF(4, "SRID set, buf = %p", buf); |
563 | 0 | } |
564 | | /* Set the coordinates */ |
565 | 0 | buf = ptarray_to_wkb_buf(pt->point, buf, variant | WKB_NO_NPOINTS); |
566 | 0 | LWDEBUGF(4, "Pointarray set, buf = %p", buf); |
567 | 0 | return buf; |
568 | 0 | } |
569 | | |
570 | | /* |
571 | | * LINESTRING, CIRCULARSTRING |
572 | | */ |
573 | | static size_t lwline_to_wkb_size(const LWLINE *line, uint8_t variant) |
574 | 0 | { |
575 | | /* Endian flag + type number */ |
576 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; |
577 | | |
578 | | /* Only process empty at this level in the EXTENDED case */ |
579 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)line) ) |
580 | 0 | return empty_to_wkb_size((LWGEOM*)line, variant); |
581 | | |
582 | | /* Extended WKB needs space for optional SRID integer */ |
583 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) ) |
584 | 0 | size += WKB_INT_SIZE; |
585 | | |
586 | | /* Size of point array */ |
587 | 0 | size += ptarray_to_wkb_size(line->points, variant); |
588 | 0 | return size; |
589 | 0 | } |
590 | | |
591 | | static uint8_t* lwline_to_wkb_buf(const LWLINE *line, uint8_t *buf, uint8_t variant) |
592 | 0 | { |
593 | | /* Only process empty at this level in the EXTENDED case */ |
594 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)line) ) |
595 | 0 | return empty_to_wkb_buf((LWGEOM*)line, buf, variant); |
596 | | |
597 | | /* Set the endian flag */ |
598 | 0 | buf = endian_to_wkb_buf(buf, variant); |
599 | | /* Set the geometry type */ |
600 | 0 | buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)line, variant), buf, variant); |
601 | | /* Set the optional SRID for extended variant */ |
602 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)line, variant) ) |
603 | 0 | buf = integer_to_wkb_buf(line->srid, buf, variant); |
604 | | /* Set the coordinates */ |
605 | 0 | buf = ptarray_to_wkb_buf(line->points, buf, variant); |
606 | 0 | return buf; |
607 | 0 | } |
608 | | |
609 | | /* |
610 | | * TRIANGLE |
611 | | */ |
612 | | static size_t lwtriangle_to_wkb_size(const LWTRIANGLE *tri, uint8_t variant) |
613 | 0 | { |
614 | | /* endian flag + type number + number of rings */ |
615 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE; |
616 | | |
617 | | /* Only process empty at this level in the EXTENDED case */ |
618 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)tri) ) |
619 | 0 | return empty_to_wkb_size((LWGEOM*)tri, variant); |
620 | | |
621 | | /* Extended WKB needs space for optional SRID integer */ |
622 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)tri, variant) ) |
623 | 0 | size += WKB_INT_SIZE; |
624 | | |
625 | | /* How big is this point array? */ |
626 | 0 | size += ptarray_to_wkb_size(tri->points, variant); |
627 | |
|
628 | 0 | return size; |
629 | 0 | } |
630 | | |
631 | | static uint8_t* lwtriangle_to_wkb_buf(const LWTRIANGLE *tri, uint8_t *buf, uint8_t variant) |
632 | 0 | { |
633 | | /* Only process empty at this level in the EXTENDED case */ |
634 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)tri) ) |
635 | 0 | return empty_to_wkb_buf((LWGEOM*)tri, buf, variant); |
636 | | |
637 | | /* Set the endian flag */ |
638 | 0 | buf = endian_to_wkb_buf(buf, variant); |
639 | | |
640 | | /* Set the geometry type */ |
641 | 0 | buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)tri, variant), buf, variant); |
642 | | |
643 | | /* Set the optional SRID for extended variant */ |
644 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)tri, variant) ) |
645 | 0 | buf = integer_to_wkb_buf(tri->srid, buf, variant); |
646 | | |
647 | | /* Set the number of rings (only one, it's a triangle, buddy) */ |
648 | 0 | buf = integer_to_wkb_buf(1, buf, variant); |
649 | | |
650 | | /* Write that ring */ |
651 | 0 | buf = ptarray_to_wkb_buf(tri->points, buf, variant); |
652 | |
|
653 | 0 | return buf; |
654 | 0 | } |
655 | | |
656 | | /* |
657 | | * POLYGON |
658 | | */ |
659 | | static size_t lwpoly_to_wkb_size(const LWPOLY *poly, uint8_t variant) |
660 | 0 | { |
661 | | /* endian flag + type number + number of rings */ |
662 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE; |
663 | 0 | uint32_t i = 0; |
664 | | |
665 | | /* Only process empty at this level in the EXTENDED case */ |
666 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)poly) ) |
667 | 0 | return empty_to_wkb_size((LWGEOM*)poly, variant); |
668 | | |
669 | | /* Extended WKB needs space for optional SRID integer */ |
670 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) ) |
671 | 0 | size += WKB_INT_SIZE; |
672 | |
|
673 | 0 | for ( i = 0; i < poly->nrings; i++ ) |
674 | 0 | { |
675 | | /* Size of ring point array */ |
676 | 0 | size += ptarray_to_wkb_size(poly->rings[i], variant); |
677 | 0 | } |
678 | |
|
679 | 0 | return size; |
680 | 0 | } |
681 | | |
682 | | static uint8_t* lwpoly_to_wkb_buf(const LWPOLY *poly, uint8_t *buf, uint8_t variant) |
683 | 0 | { |
684 | 0 | uint32_t i; |
685 | | |
686 | | /* Only process empty at this level in the EXTENDED case */ |
687 | 0 | if ( (variant & WKB_EXTENDED) && lwgeom_is_empty((LWGEOM*)poly) ) |
688 | 0 | return empty_to_wkb_buf((LWGEOM*)poly, buf, variant); |
689 | | |
690 | | /* Set the endian flag */ |
691 | 0 | buf = endian_to_wkb_buf(buf, variant); |
692 | | /* Set the geometry type */ |
693 | 0 | buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)poly, variant), buf, variant); |
694 | | /* Set the optional SRID for extended variant */ |
695 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)poly, variant) ) |
696 | 0 | buf = integer_to_wkb_buf(poly->srid, buf, variant); |
697 | | /* Set the number of rings */ |
698 | 0 | buf = integer_to_wkb_buf(poly->nrings, buf, variant); |
699 | |
|
700 | 0 | for ( i = 0; i < poly->nrings; i++ ) |
701 | 0 | { |
702 | 0 | buf = ptarray_to_wkb_buf(poly->rings[i], buf, variant); |
703 | 0 | } |
704 | |
|
705 | 0 | return buf; |
706 | 0 | } |
707 | | |
708 | | |
709 | | /* |
710 | | * MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION |
711 | | * MULTICURVE, COMPOUNDCURVE, MULTISURFACE, CURVEPOLYGON, TIN, |
712 | | * POLYHEDRALSURFACE |
713 | | */ |
714 | | static size_t lwcollection_to_wkb_size(const LWCOLLECTION *col, uint8_t variant) |
715 | 0 | { |
716 | | /* Endian flag + type number + number of subgeoms */ |
717 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_INT_SIZE; |
718 | 0 | uint32_t i = 0; |
719 | | |
720 | | /* Extended WKB needs space for optional SRID integer */ |
721 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) ) |
722 | 0 | size += WKB_INT_SIZE; |
723 | |
|
724 | 0 | for ( i = 0; i < col->ngeoms; i++ ) |
725 | 0 | { |
726 | | /* size of subgeom */ |
727 | 0 | size += lwgeom_to_wkb_size((LWGEOM*)col->geoms[i], variant | WKB_NO_SRID); |
728 | 0 | } |
729 | |
|
730 | 0 | return size; |
731 | 0 | } |
732 | | |
733 | | /** |
734 | | * Write a geometry collection into a WKB buffer. |
735 | | * |
736 | | * Encodes the collection header (endianness, geometry type, optional SRID, |
737 | | * and number of sub-geometries) and then serializes each contained geometry. |
738 | | * Sub-geometries inherit the parent's SRID and are written with WKB_NO_SRID. |
739 | | * |
740 | | * @param col Collection to serialize. |
741 | | * @param buf Pointer to the current write position in the output buffer; the |
742 | | * function advances this pointer as it writes. |
743 | | * @param variant Bitmask selecting WKB variant/flags (e.g. NDR/XDR, HEX, |
744 | | * EXTENDED, ISO). Controls SRID inclusion and encoding form. |
745 | | * @return Updated buffer pointer positioned immediately after the written data. |
746 | | */ |
747 | | static uint8_t* lwcollection_to_wkb_buf(const LWCOLLECTION *col, uint8_t *buf, uint8_t variant) |
748 | 0 | { |
749 | 0 | uint32_t i; |
750 | | |
751 | | /* Set the endian flag */ |
752 | 0 | buf = endian_to_wkb_buf(buf, variant); |
753 | | /* Set the geometry type */ |
754 | 0 | buf = integer_to_wkb_buf(lwgeom_wkb_type((LWGEOM*)col, variant), buf, variant); |
755 | | /* Set the optional SRID for extended variant */ |
756 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)col, variant) ) |
757 | 0 | buf = integer_to_wkb_buf(col->srid, buf, variant); |
758 | | /* Set the number of sub-geometries */ |
759 | 0 | buf = integer_to_wkb_buf(col->ngeoms, buf, variant); |
760 | | |
761 | | /* Write the sub-geometries. Sub-geometries do not get SRIDs, they |
762 | | inherit from their parents. */ |
763 | 0 | for ( i = 0; i < col->ngeoms; i++ ) |
764 | 0 | { |
765 | 0 | buf = lwgeom_to_wkb_buf(col->geoms[i], buf, variant | WKB_NO_SRID); |
766 | 0 | } |
767 | |
|
768 | 0 | return buf; |
769 | 0 | } |
770 | | |
771 | | /** |
772 | | * Compute the number of bytes required to encode a NURBS curve as WKB (ISO/IEC 13249-3:2016). |
773 | | * |
774 | | * The computed size accounts for: |
775 | | * - endianness marker and geometry type, |
776 | | * - optional SRID integer when the variant requests it, |
777 | | * - degree and control-point count, |
778 | | * - per-control-point data: a byte-order marker, coordinate doubles for active dimensions, |
779 | | * a one-byte "weight present" flag, and an optional double weight when the point's weight != 1.0, |
780 | | * - knot-count integer and knot double values (a uniform knot vector is generated if the curve has none). |
781 | | * |
782 | | * The caller must pass a valid non-NULL LWNURBSCURVE pointer. The variant argument influences |
783 | | * whether an SRID integer is included (extended/ISO handling). |
784 | | * |
785 | | * @param curve NURBS curve to size (must be non-NULL). |
786 | | * @param variant WKB variant flags that determine SRID inclusion and encoding options. |
787 | | * @return number of bytes required to write the curve in WKB form. |
788 | | */ |
789 | | static size_t lwnurbscurve_to_wkb_size(const LWNURBSCURVE *curve, uint8_t variant) |
790 | 0 | { |
791 | 0 | size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE; /* endian + type */ |
792 | | |
793 | | /* Extended WKB needs space for optional SRID integer */ |
794 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)curve, variant) ) |
795 | 0 | size += WKB_INT_SIZE; |
796 | | |
797 | | /* ISO/IEC 13249-3:2016 compliant structure: |
798 | | * <byte order> <wkbnurbs> [ <wkbdegree> <wkbcontrolpoints binary> <wkbknots binary> ] |
799 | | */ |
800 | |
|
801 | 0 | size += WKB_INT_SIZE; /* degree */ |
802 | | |
803 | | /* Control points count */ |
804 | 0 | size += WKB_INT_SIZE; /* npoints */ |
805 | |
|
806 | 0 | uint32_t npoints = curve->points ? curve->points->npoints : 0; |
807 | 0 | uint32_t dims = curve->points ? FLAGS_NDIMS(curve->points->flags) : 2; |
808 | | |
809 | | /* ISO format: Each control point has individual weight structure |
810 | | * <nurbspoint binary representation> ::= |
811 | | * <byte order> [ <wkbweightedpoint> <bit> [ <wkbweight> ] ] |
812 | | */ |
813 | 0 | for (uint32_t i = 0; i < npoints; i++) { |
814 | 0 | size += WKB_BYTE_SIZE; /* byte order for each point */ |
815 | 0 | size += dims * WKB_DOUBLE_SIZE; /* point coordinates */ |
816 | 0 | size += WKB_BYTE_SIZE; /* weight bit flag */ |
817 | | |
818 | | /* Add weight value if this point has a custom weight */ |
819 | 0 | if (curve->weights && i < curve->nweights && curve->weights[i] != 1.0) { |
820 | 0 | size += WKB_DOUBLE_SIZE; /* weight value */ |
821 | 0 | } |
822 | 0 | } |
823 | | |
824 | | /* Knots are always required in WKB output (generate uniform if not present) */ |
825 | 0 | size += WKB_INT_SIZE; /* nknots count */ |
826 | 0 | uint32_t nknots_for_size = 0; |
827 | 0 | double *knots_for_size = lwnurbscurve_get_or_generate_knots(curve, &nknots_for_size); |
828 | 0 | if (knots_for_size) { |
829 | 0 | size += WKB_DOUBLE_SIZE * nknots_for_size; |
830 | 0 | lwfree(knots_for_size); /* Just needed the count */ |
831 | 0 | } else if (nknots_for_size > 0) { |
832 | | /* Inconsistent state - got count but no array */ |
833 | 0 | lwerror("Failed to get knots for NURBS curve"); |
834 | 0 | return 0; |
835 | 0 | } |
836 | | |
837 | 0 | return size; |
838 | 0 | } |
839 | | |
840 | | /** |
841 | | * Encode a NURBS curve into WKB (ISO/IEC 13249-3:2016) and write into a buffer. |
842 | | * |
843 | | * Produces an ISO-compliant WKB representation for a LWNURBSCURVE: |
844 | | * - writes byte-order marker and geometry type, and optional SRID for extended variants; |
845 | | * - writes curve degree and control-point count; |
846 | | * - for each control point writes a per-point byte-order marker, coordinates (per point dimensionality), |
847 | | * a single-byte weight-presence flag, and the weight value when present (default weight = 1.0 is omitted); |
848 | | * - writes the knot vector length followed by knot values (requests knots via lwnurbscurve_get_or_generate_knots; |
849 | | * if none are returned a zero knot-count is written as a fallback). |
850 | | * |
851 | | * The function advances the provided buffer pointer as it writes. It may allocate a temporary knot array |
852 | | * from lwnurbscurve_get_or_generate_knots and frees it before returning. |
853 | | * |
854 | | * @param curve NURBS curve to encode. |
855 | | * @param buf Pointer to the output buffer where WKB bytes (or hex characters, depending on variant) are written. |
856 | | * @param variant Bitmask selecting WKB variant/encoding options (endianness, extended/ISO/hex modes). |
857 | | * @return Updated buffer pointer positioned immediately after the last written byte/character. |
858 | | */ |
859 | | static uint8_t* lwnurbscurve_to_wkb_buf(const LWNURBSCURVE *curve, uint8_t *buf, uint8_t variant) |
860 | 0 | { |
861 | 0 | uint32_t wkb_type = lwgeom_wkb_type((LWGEOM*)curve, variant); |
862 | | |
863 | | /* Write endian flag */ |
864 | 0 | buf = endian_to_wkb_buf(buf, variant); |
865 | | |
866 | | /* Write type */ |
867 | 0 | buf = integer_to_wkb_buf(wkb_type, buf, variant); |
868 | | |
869 | | /* Set the optional SRID for extended variant */ |
870 | 0 | if ( lwgeom_wkb_needs_srid((LWGEOM*)curve, variant) ) |
871 | 0 | buf = integer_to_wkb_buf(curve->srid, buf, variant); |
872 | | |
873 | | /* ISO/IEC 13249-3:2016 compliant structure */ |
874 | 0 | buf = integer_to_wkb_buf(curve->degree, buf, variant); |
875 | | |
876 | | /* Write control points count */ |
877 | 0 | uint32_t npoints = curve->points ? curve->points->npoints : 0; |
878 | 0 | uint32_t dims = curve->points ? FLAGS_NDIMS(curve->points->flags) : 2; |
879 | 0 | buf = integer_to_wkb_buf(npoints, buf, variant); |
880 | | |
881 | | /* ISO format: Write each control point with individual weight structure |
882 | | * <nurbspoint binary representation> ::= |
883 | | * <byte order> [ <wkbweightedpoint> <bit> [ <wkbweight> ] ] |
884 | | */ |
885 | 0 | for (uint32_t i = 0; i < npoints; i++) { |
886 | | /* Write byte order for this point (ISO requirement) */ |
887 | 0 | buf = endian_to_wkb_buf(buf, variant); |
888 | | |
889 | | /* Write point coordinates */ |
890 | 0 | if (curve->points) { |
891 | 0 | double *coords = (double*)getPoint_internal(curve->points, i); |
892 | 0 | for (uint32_t d = 0; d < dims; d++) { |
893 | 0 | buf = double_to_wkb_buf(coords[d], buf, variant); |
894 | 0 | } |
895 | 0 | } |
896 | | |
897 | | /* Write weight bit flag */ |
898 | 0 | uint8_t has_weight = 0; |
899 | 0 | double weight_value = 1.0; /* default weight */ |
900 | |
|
901 | 0 | if (curve->weights && i < curve->nweights && curve->weights[i] != 1.0) { |
902 | 0 | has_weight = 1; |
903 | 0 | weight_value = curve->weights[i]; |
904 | 0 | } |
905 | |
|
906 | 0 | buf = byte_to_wkb_buf(has_weight, buf, variant); |
907 | | |
908 | | /* Write weight value if flag = 1 */ |
909 | 0 | if (has_weight) { |
910 | 0 | buf = double_to_wkb_buf(weight_value, buf, variant); |
911 | 0 | } |
912 | 0 | } |
913 | | |
914 | | /* Write knots (always required - generate uniform if not present) */ |
915 | 0 | { |
916 | 0 | uint32_t nknots = 0; |
917 | 0 | double *knots = lwnurbscurve_get_or_generate_knots(curve, &nknots); |
918 | 0 | if (knots && nknots > 0) { |
919 | 0 | buf = integer_to_wkb_buf(nknots, buf, variant); |
920 | 0 | for (uint32_t i = 0; i < nknots; i++) { |
921 | 0 | buf = double_to_wkb_buf(knots[i], buf, variant); |
922 | 0 | } |
923 | 0 | lwfree(knots); |
924 | 0 | } else if (npoints == 0) { |
925 | 0 | buf = integer_to_wkb_buf(0, buf, variant); |
926 | 0 | } else { |
927 | 0 | lwerror("Failed to get knots for NURBS curve"); |
928 | 0 | } |
929 | 0 | } |
930 | |
|
931 | 0 | return buf; |
932 | 0 | } |
933 | | |
934 | | /** |
935 | | * Compute the number of bytes required to encode a geometry as WKB for a given variant. |
936 | | * |
937 | | * Determines the exact buffer size needed to write the provided LWGEOM in the specified |
938 | | * WKB variant (handles ISO/extended/hex encodings, SRID inclusion, and per-geometry |
939 | | * element sizing). Empty geometries are handled by the dedicated empty-to-WKB sizing |
940 | | * unless the extended variant bit is set, in which case extended encoding rules apply. |
941 | | * |
942 | | * @param geom Pointer to the geometry to size; returns 0 and logs an error if NULL. |
943 | | * @param variant Bitmask of WKB variant flags (e.g., WKB_EXTENDED, WKB_HEX, NDR/XDR) |
944 | | * that control dimensionality, SRID inclusion, and encoding form. |
945 | | * @return Number of bytes required to encode `geom` under `variant`, or 0 on error. |
946 | | */ |
947 | | static size_t |
948 | | lwgeom_to_wkb_size(const LWGEOM *geom, uint8_t variant) |
949 | 0 | { |
950 | 0 | size_t size = 0; |
951 | |
|
952 | 0 | if (geom == NULL) |
953 | 0 | { |
954 | 0 | LWDEBUG(4, "Cannot convert NULL into WKB."); |
955 | 0 | lwerror("Cannot convert NULL into WKB."); |
956 | 0 | return 0; |
957 | 0 | } |
958 | | |
959 | | /* Short circuit out empty geometries */ |
960 | 0 | if ( geom->type != NURBSCURVETYPE && (!(variant & WKB_EXTENDED)) && lwgeom_is_empty(geom) ) |
961 | 0 | { |
962 | 0 | return empty_to_wkb_size(geom, variant); |
963 | 0 | } |
964 | | |
965 | 0 | switch ( geom->type ) |
966 | 0 | { |
967 | 0 | case POINTTYPE: |
968 | 0 | size += lwpoint_to_wkb_size((LWPOINT*)geom, variant); |
969 | 0 | break; |
970 | | |
971 | | /* LineString and CircularString both have points elements */ |
972 | 0 | case CIRCSTRINGTYPE: |
973 | 0 | case LINETYPE: |
974 | 0 | size += lwline_to_wkb_size((LWLINE*)geom, variant); |
975 | 0 | break; |
976 | | |
977 | | /* Polygon has nrings and rings elements */ |
978 | 0 | case POLYGONTYPE: |
979 | 0 | size += lwpoly_to_wkb_size((LWPOLY*)geom, variant); |
980 | 0 | break; |
981 | | |
982 | | /* Triangle has one ring of three points */ |
983 | 0 | case TRIANGLETYPE: |
984 | 0 | size += lwtriangle_to_wkb_size((LWTRIANGLE*)geom, variant); |
985 | 0 | break; |
986 | | |
987 | | /* All these Collection types have ngeoms and geoms elements */ |
988 | 0 | case MULTIPOINTTYPE: |
989 | 0 | case MULTILINETYPE: |
990 | 0 | case MULTIPOLYGONTYPE: |
991 | 0 | case COMPOUNDTYPE: |
992 | 0 | case CURVEPOLYTYPE: |
993 | 0 | case MULTICURVETYPE: |
994 | 0 | case MULTISURFACETYPE: |
995 | 0 | case COLLECTIONTYPE: |
996 | 0 | case POLYHEDRALSURFACETYPE: |
997 | 0 | case TINTYPE: |
998 | 0 | size += lwcollection_to_wkb_size((LWCOLLECTION*)geom, variant); |
999 | 0 | break; |
1000 | 0 | case NURBSCURVETYPE: |
1001 | 0 | size += lwnurbscurve_to_wkb_size((LWNURBSCURVE*)geom, variant); |
1002 | 0 | break; |
1003 | | /* Unknown type! */ |
1004 | 0 | default: |
1005 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); |
1006 | 0 | } |
1007 | | |
1008 | 0 | return size; |
1009 | 0 | } |
1010 | | |
1011 | | /** |
1012 | | * Serialize a LWGEOM into WKB format, writing into the provided buffer. |
1013 | | * |
1014 | | * Dispatches to the appropriate type-specific writer based on geom->type. |
1015 | | * For empty geometries, if the WKB_EXTENDED flag is not set the geometry is |
1016 | | * written using the canonical "empty" representation; otherwise empties are |
1017 | | * preserved and written by the type-specific writer. |
1018 | | * |
1019 | | * @param geom Geometry to serialize (must be non-NULL). |
1020 | | * @param buf Destination buffer where WKB bytes will be written. The function |
1021 | | * writes starting at this pointer and returns the pointer advanced |
1022 | | * past the written data. |
1023 | | * @param variant Bitflags controlling WKB flavor (e.g., WKB_EXTENDED, WKB_HEX, |
1024 | | * WKB_NDR/WKB_XDR). These flags influence SRID inclusion, hex |
1025 | | * vs binary output, and other variant-specific encoding rules. |
1026 | | * @return Pointer to the buffer position immediately after the written WKB on |
1027 | | * success; NULL (0) on unsupported/unknown geometry type or other error. |
1028 | | */ |
1029 | | |
1030 | | static uint8_t* lwgeom_to_wkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant) |
1031 | 0 | { |
1032 | | |
1033 | | /* Do not simplify empties when outputting to canonical form */ |
1034 | 0 | if (geom->type != NURBSCURVETYPE && lwgeom_is_empty(geom) && !(variant & WKB_EXTENDED)) |
1035 | 0 | return empty_to_wkb_buf(geom, buf, variant); |
1036 | | |
1037 | 0 | switch ( geom->type ) |
1038 | 0 | { |
1039 | 0 | case POINTTYPE: |
1040 | 0 | return lwpoint_to_wkb_buf((LWPOINT*)geom, buf, variant); |
1041 | | |
1042 | | /* LineString and CircularString both have 'points' elements */ |
1043 | 0 | case CIRCSTRINGTYPE: |
1044 | 0 | case LINETYPE: |
1045 | 0 | return lwline_to_wkb_buf((LWLINE*)geom, buf, variant); |
1046 | | |
1047 | | /* Polygon has 'nrings' and 'rings' elements */ |
1048 | 0 | case POLYGONTYPE: |
1049 | 0 | return lwpoly_to_wkb_buf((LWPOLY*)geom, buf, variant); |
1050 | | |
1051 | | /* Triangle has one ring of three points */ |
1052 | 0 | case TRIANGLETYPE: |
1053 | 0 | return lwtriangle_to_wkb_buf((LWTRIANGLE*)geom, buf, variant); |
1054 | | |
1055 | | /* All these Collection types have 'ngeoms' and 'geoms' elements */ |
1056 | 0 | case MULTIPOINTTYPE: |
1057 | 0 | case MULTILINETYPE: |
1058 | 0 | case MULTIPOLYGONTYPE: |
1059 | 0 | case COMPOUNDTYPE: |
1060 | 0 | case CURVEPOLYTYPE: |
1061 | 0 | case MULTICURVETYPE: |
1062 | 0 | case MULTISURFACETYPE: |
1063 | 0 | case COLLECTIONTYPE: |
1064 | 0 | case POLYHEDRALSURFACETYPE: |
1065 | 0 | case TINTYPE: |
1066 | 0 | return lwcollection_to_wkb_buf((LWCOLLECTION*)geom, buf, variant); |
1067 | 0 | case NURBSCURVETYPE: |
1068 | 0 | return lwnurbscurve_to_wkb_buf((LWNURBSCURVE*)geom, buf, variant); |
1069 | | |
1070 | | /* Unknown type! */ |
1071 | 0 | default: |
1072 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(geom->type)); |
1073 | 0 | } |
1074 | | /* Return value to keep compiler happy. */ |
1075 | 0 | return 0; |
1076 | 0 | } |
1077 | | |
1078 | | /** |
1079 | | * Convert LWGEOM to a char* in WKB format. Caller is responsible for freeing |
1080 | | * the returned array. |
1081 | | * |
1082 | | * @param variant. Unsigned bitmask value. Accepts one of: WKB_ISO, WKB_EXTENDED, WKB_SFSQL. |
1083 | | * Accepts any of: WKB_NDR, WKB_HEX. For example: Variant = ( WKB_ISO | WKB_NDR ) would |
1084 | | * return the little-endian ISO form of WKB. For Example: Variant = ( WKB_EXTENDED | WKB_HEX ) |
1085 | | * would return the big-endian extended form of WKB, as hex-encoded ASCII (the "canonical form"). |
1086 | | * @param size_out If supplied, will return the size of the returned memory segment, |
1087 | | * including the null terminator in the case of ASCII. |
1088 | | */ |
1089 | | static ptrdiff_t |
1090 | | lwgeom_to_wkb_write_buf(const LWGEOM *geom, uint8_t variant, uint8_t *buffer) |
1091 | 0 | { |
1092 | | /* If neither or both variants are specified, choose the native order */ |
1093 | 0 | if (!(variant & WKB_NDR || variant & WKB_XDR) || (variant & WKB_NDR && variant & WKB_XDR)) |
1094 | 0 | { |
1095 | 0 | if (IS_BIG_ENDIAN) |
1096 | 0 | variant = variant | WKB_XDR; |
1097 | 0 | else |
1098 | 0 | variant = variant | WKB_NDR; |
1099 | 0 | } |
1100 | | |
1101 | | /* Write the WKB into the output buffer */ |
1102 | 0 | int written_bytes = (lwgeom_to_wkb_buf(geom, buffer, variant) - buffer); |
1103 | |
|
1104 | 0 | return written_bytes; |
1105 | 0 | } |
1106 | | |
1107 | | uint8_t * |
1108 | | lwgeom_to_wkb_buffer(const LWGEOM *geom, uint8_t variant) |
1109 | 0 | { |
1110 | 0 | size_t b_size = lwgeom_to_wkb_size(geom, variant); |
1111 | | /* Hex string takes twice as much space as binary + a null character */ |
1112 | 0 | if (variant & WKB_HEX) |
1113 | 0 | { |
1114 | 0 | b_size = 2 * b_size + 1; |
1115 | 0 | } |
1116 | |
|
1117 | 0 | uint8_t *buffer = (uint8_t *)lwalloc(b_size); |
1118 | 0 | ptrdiff_t written_size = lwgeom_to_wkb_write_buf(geom, variant, buffer); |
1119 | 0 | if (variant & WKB_HEX) |
1120 | 0 | { |
1121 | 0 | buffer[written_size] = '\0'; |
1122 | 0 | written_size++; |
1123 | 0 | } |
1124 | |
|
1125 | 0 | if (written_size != (ptrdiff_t)b_size) |
1126 | 0 | { |
1127 | 0 | char *wkt = lwgeom_to_wkt(geom, WKT_EXTENDED, 15, NULL); |
1128 | 0 | lwerror("Output WKB is not the same size as the allocated buffer. Variant: %u, Geom: %s", variant, wkt); |
1129 | 0 | lwfree(wkt); |
1130 | 0 | lwfree(buffer); |
1131 | 0 | return NULL; |
1132 | 0 | } |
1133 | | |
1134 | 0 | return buffer; |
1135 | 0 | } |
1136 | | |
1137 | | char * |
1138 | | lwgeom_to_hexwkb_buffer(const LWGEOM *geom, uint8_t variant) |
1139 | 0 | { |
1140 | 0 | return (char *)lwgeom_to_wkb_buffer(geom, variant | WKB_HEX); |
1141 | 0 | } |
1142 | | |
1143 | | lwvarlena_t * |
1144 | | lwgeom_to_wkb_varlena(const LWGEOM *geom, uint8_t variant) |
1145 | 0 | { |
1146 | 0 | size_t b_size = lwgeom_to_wkb_size(geom, variant); |
1147 | | /* Hex string takes twice as much space as binary, but No NULL ending in varlena */ |
1148 | 0 | if (variant & WKB_HEX) |
1149 | 0 | { |
1150 | 0 | b_size = 2 * b_size; |
1151 | 0 | } |
1152 | |
|
1153 | 0 | lwvarlena_t *buffer = (lwvarlena_t *)lwalloc(b_size + LWVARHDRSZ); |
1154 | 0 | int written_size = lwgeom_to_wkb_write_buf(geom, variant, (uint8_t *)buffer->data); |
1155 | 0 | if (written_size != (ptrdiff_t)b_size) |
1156 | 0 | { |
1157 | 0 | char *wkt = lwgeom_to_wkt(geom, WKT_EXTENDED, 15, NULL); |
1158 | 0 | lwerror("Output WKB is not the same size as the allocated buffer. Variant: %u, Geom: %s", variant, wkt); |
1159 | 0 | lwfree(wkt); |
1160 | 0 | lwfree(buffer); |
1161 | 0 | return NULL; |
1162 | 0 | } |
1163 | 0 | LWSIZE_SET(buffer->size, written_size + LWVARHDRSZ); |
1164 | 0 | return buffer; |
1165 | 0 | } |
1166 | | |
1167 | | lwvarlena_t * |
1168 | | lwgeom_to_hexwkb_varlena(const LWGEOM *geom, uint8_t variant) |
1169 | 0 | { |
1170 | 0 | return lwgeom_to_wkb_varlena(geom, variant | WKB_HEX); |
1171 | 0 | } |