Coverage Report

Created: 2025-06-13 06:18

/src/MapServer/src/flatgeobuf/flatgeobuf_c.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "flatgeobuf_c.h"
2
3
#include "feature_generated.h"
4
#include "geometryreader.h"
5
#include "packedrtree.h"
6
#include <stdexcept>
7
8
using namespace mapserver::flatbuffers;
9
using namespace mapserver::FlatGeobuf;
10
11
typedef flatgeobuf_ctx ctx;
12
13
uint8_t flatgeobuf_magicbytes[] = { 0x66, 0x67, 0x62, 0x03, 0x66, 0x67, 0x62, 0x01 };
14
uint8_t FLATGEOBUF_MAGICBYTES_SIZE = sizeof(flatgeobuf_magicbytes);
15
uint32_t INIT_BUFFER_SIZE = 1024 * 4;
16
17
template <typename T>
18
void parse_value(uint8_t *data, char **values, uint16_t i, uint32_t &offset, bool found)
19
0
{
20
0
    using std::to_string;
21
0
    if (found)
22
0
    {
23
0
        msFree(values[i]);
24
0
        values[i] = msStrdup(to_string(*((T*) (data + offset))).c_str());
25
0
    }
26
0
    offset += sizeof(T);
27
0
}
Unexecuted instantiation: void parse_value<unsigned char>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<signed char>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<short>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<unsigned short>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<int>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<unsigned int>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<long>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<unsigned long>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<float>(unsigned char*, char**, unsigned short, unsigned int&, bool)
Unexecuted instantiation: void parse_value<double>(unsigned char*, char**, unsigned short, unsigned int&, bool)
28
29
ctx *flatgeobuf_init_ctx()
30
0
{
31
0
    ctx *c = (ctx *) malloc(sizeof(ctx));
32
0
    memset(c, 0, sizeof(ctx));
33
0
    c->is_null_geom = false;
34
0
    c->done = false;
35
0
    return c;
36
0
}
37
38
void flatgeobuf_free_ctx(ctx *ctx)
39
0
{
40
0
    if (ctx->columns) {
41
0
        for (uint32_t i = 0; i < ctx->columns_len; i++)
42
0
            free(ctx->columns[i].name);
43
0
        free(ctx->columns);
44
0
    }
45
0
    if (ctx->search_result)
46
0
        free(ctx->search_result);
47
0
    if (ctx->buf)
48
0
        free(ctx->buf);
49
0
    if (ctx->wkt)
50
0
        free(ctx->wkt);
51
0
    free(ctx);
52
0
}
53
54
void flatgeobuf_ensure_line(ctx *ctx, uint32_t len)
55
0
{
56
0
    if (!ctx->line) {
57
0
        ctx->line_len = len;
58
0
        ctx->line = (lineObj *) malloc(ctx->line_len * sizeof(lineObj));
59
0
        return;
60
0
    }
61
0
    if (ctx->line_len < len) {
62
0
        ctx->line_len = len;
63
0
        ctx->line = (lineObj *) realloc(ctx->buf, ctx->line_len * sizeof(lineObj));
64
0
    }
65
0
}
66
67
void flatgeobuf_ensure_point(ctx *ctx, uint32_t len)
68
0
{
69
0
    if (!ctx->point) {
70
0
        ctx->point_len = len;
71
0
        ctx->point = (pointObj *) malloc(ctx->point_len * sizeof(pointObj));
72
0
        return;
73
0
    }
74
0
    if (ctx->point_len < len) {
75
0
        ctx->point_len = len;
76
0
        ctx->point = (pointObj *) realloc(ctx->buf, ctx->point_len * sizeof(pointObj));
77
0
    }
78
0
}
79
80
int flatgeobuf_ensure_buf(ctx *ctx, uint32_t size)
81
0
{
82
0
    if (size > 100 * 1024 * 1024) {
83
0
        msSetError(MS_FGBERR, "Invalid buffer size requested", "flatgeobuf_ensure_buf");
84
0
        return -1;
85
0
    }
86
0
    if (!ctx->buf) {
87
0
        ctx->buf_size = std::max(INIT_BUFFER_SIZE, size);
88
0
        ctx->buf = (uint8_t *) malloc(ctx->buf_size);
89
0
        if (ctx->buf == NULL) {
90
0
            msSetError(MS_FGBERR, "Failed to allocate buffer", "flatgeobuf_ensure_buf");
91
0
            return -1;
92
0
        }
93
0
        return 0;
94
0
    }
95
0
    if (ctx->buf_size < size) {
96
0
        ctx->buf_size = std::max(ctx->buf_size * 2, size);
97
0
        auto buf = (uint8_t *) realloc(ctx->buf, ctx->buf_size);
98
0
        if (buf == NULL) {
99
0
            msSetError(MS_FGBERR, "Failed to reallocate buffer", "flatgeobuf_ensure_buf");
100
0
            return -1;
101
0
        }
102
0
        ctx->buf = buf;
103
0
    }
104
0
    return 0;
105
0
}
106
107
int flatgeobuf_decode_feature(ctx *ctx, layerObj *layer, shapeObj *shape)
108
0
{
109
0
    ctx->is_null_geom = false;
110
111
0
    uint32_t featureSize;
112
0
    if (VSIFReadL(&featureSize, sizeof(featureSize), 1, ctx->file) != 1) {
113
0
        if (VSIFEofL(ctx->file)) {
114
0
            ctx->done = true;
115
0
            return 0;
116
0
        }
117
0
        return -1;
118
0
    }
119
120
0
    ctx->offset += sizeof(uoffset_t);
121
0
    if (flatgeobuf_ensure_buf(ctx, featureSize) != 0) {
122
0
        return -1;
123
0
    }
124
125
0
    if (VSIFReadL(ctx->buf, 1, featureSize, ctx->file) != featureSize) {
126
0
        msSetError(MS_FGBERR, "Failed to read feature", "flatgeobuf_decode_feature");
127
0
        return -1;
128
0
    }
129
0
    ctx->offset += featureSize;
130
0
    auto feature = GetFeature(ctx->buf);
131
0
    const auto geometry = feature->geometry();
132
0
    if (geometry) {
133
0
        GeometryReader(ctx, geometry).read(shape);
134
0
    } else {
135
0
        ctx->is_null_geom = true;
136
0
        return 0;
137
0
    }
138
0
    auto properties = feature->properties();
139
0
    if (properties && properties->size() != 0) {
140
0
        ctx->properties = (uint8_t *) properties->data();
141
0
        ctx->properties_size = properties->size();
142
0
        flatgeobuf_decode_properties(ctx, layer, shape);
143
0
    } else {
144
0
        ctx->properties_size = 0;
145
0
    }
146
147
0
    return 0;
148
0
}
149
150
int flatgeobuf_decode_properties(ctx *ctx, layerObj *layer, shapeObj *shape)
151
0
{
152
0
  uint8_t type;
153
0
  uint32_t offset = 0;
154
0
  uint8_t *data = ctx->properties;
155
0
  uint32_t size = ctx->properties_size;
156
0
    uint16_t numvalues = layer->numitems;
157
0
    bool found;
158
0
    char **values;
159
160
0
    if (numvalues == 0)
161
0
        return 0;
162
163
0
    values = (char **) msSmallCalloc(sizeof(char *), numvalues);
164
0
    if (shape->values) msFreeCharArray(shape->values, shape->numvalues);
165
0
    shape->numvalues = numvalues;
166
0
    shape->values = values;
167
168
0
    if (size > 0 && size < (sizeof(uint16_t) + sizeof(uint8_t))) {
169
0
        msSetError(MS_FGBERR, "Unexpected properties data size", "flatgeobuf_decode_properties");
170
0
        return -1;
171
0
    }
172
0
  while (offset + 1 < size) {
173
0
        uint16_t i;
174
0
        memcpy(&i, data + offset, sizeof(uint16_t));
175
0
    offset += sizeof(uint16_t);
176
0
    if (i >= ctx->columns_len) {
177
0
            msSetError(MS_FGBERR, "Column index out of range", "flatgeobuf_decode_properties");
178
0
            return -1;
179
0
        }
180
0
    const auto& column = ctx->columns[i];
181
0
        const int32_t ii = column.itemindex;
182
0
        found = ii != -1;
183
0
    type = column.type;
184
0
    switch (type) {
185
0
    case flatgeobuf_column_type_bool:
186
0
            parse_value<uint8_t>(data, values, ii, offset, found);
187
0
      break;
188
0
    case flatgeobuf_column_type_byte:
189
0
            parse_value<int8_t>(data, values, ii, offset, found);
190
0
      break;
191
0
    case flatgeobuf_column_type_ubyte:
192
0
            parse_value<uint8_t>(data, values, ii, offset, found);
193
0
      break;
194
0
    case flatgeobuf_column_type_short:
195
0
      parse_value<int16_t>(data, values, ii, offset, found);
196
0
      break;
197
0
    case flatgeobuf_column_type_ushort:
198
0
            parse_value<uint16_t>(data, values, ii, offset, found);
199
0
      break;
200
0
    case flatgeobuf_column_type_int:
201
0
            parse_value<int32_t>(data, values, ii, offset, found);
202
0
            break;
203
0
    case flatgeobuf_column_type_uint:
204
0
      parse_value<uint32_t>(data, values, ii, offset, found);
205
0
      break;
206
0
    case flatgeobuf_column_type_long:
207
0
            parse_value<int64_t>(data, values, ii, offset, found);
208
0
            break;
209
0
    case flatgeobuf_column_type_ulong:
210
0
      parse_value<uint64_t>(data, values, ii, offset, found);
211
0
      break;
212
0
    case flatgeobuf_column_type_float:
213
0
            parse_value<float>(data, values, ii, offset, found);
214
0
      break;
215
0
    case flatgeobuf_column_type_double:
216
0
      parse_value<double>(data, values, ii, offset, found);
217
0
      break;
218
0
        case flatgeobuf_column_type_datetime:
219
0
    case flatgeobuf_column_type_string: {
220
0
      uint32_t len;
221
0
      if (offset + sizeof(len) > size){
222
0
                msSetError(MS_FGBERR, "Invalid size for string value", "flatgeobuf_decode_properties");
223
0
                return -1;
224
0
            }
225
0
      memcpy(&len, data + offset, sizeof(uint32_t));
226
0
      offset += sizeof(len);
227
0
      if (found) {
228
0
                char *str = (char *) msSmallMalloc(len + 1);
229
0
                memcpy(str, data + offset, len);
230
0
                str[len] = '\0';
231
0
                msFree(values[ii]);
232
0
                values[ii] = str;
233
0
            }
234
0
      offset += len;
235
0
      break;
236
0
    }
237
0
        }
238
0
    }
239
240
0
    for(int i = 0; i < shape->numvalues; i++)
241
0
    {
242
0
        if( shape->values[i] == NULL)
243
0
            shape->values[i] = msStrdup("");
244
0
    }
245
246
0
    return 0;
247
0
}
248
249
int flatgeobuf_check_magicbytes(ctx *ctx)
250
0
{
251
0
    if (ctx->offset != 0) {
252
0
        msSetError(MS_FGBERR, "Unexpected offset", "flatgeobuf_check_magicbytes");
253
0
        return -1;
254
0
    }
255
0
    if (flatgeobuf_ensure_buf(ctx, FLATGEOBUF_MAGICBYTES_SIZE) != 0)
256
0
        return -1;
257
0
    if (VSIFReadL(ctx->buf, 8, 1, ctx->file) != 1) {
258
0
        msSetError(MS_FGBERR, "Failed to read magicbytes", "flatgeobuf_check_magicbytes");
259
0
        return -1;
260
0
    }
261
0
  uint32_t i;
262
0
  for (i = 0; i < FLATGEOBUF_MAGICBYTES_SIZE / 2; i++) {
263
0
        if (ctx->buf[i] != flatgeobuf_magicbytes[i]) {
264
0
            msSetError(MS_FGBERR, "Data is not FlatGeobuf", "flatgeobuf_check_magicbytes");
265
0
            return -1;
266
0
        }
267
0
    }
268
0
  ctx->offset += FLATGEOBUF_MAGICBYTES_SIZE;
269
0
    return 0;
270
0
}
271
272
int flatgeobuf_decode_header(ctx *ctx)
273
0
{
274
0
    if (ctx->offset != FLATGEOBUF_MAGICBYTES_SIZE) {
275
0
        msSetError(MS_FGBERR, "Unexpected offset", "flatgeobuf_decode_header");
276
0
        return -1;
277
0
    }
278
0
    if (VSIFSeekL(ctx->file, ctx->offset, SEEK_SET) == -1) {
279
0
        msSetError(MS_FGBERR, "Unable to get seek in file", "flatgeobuf_decode_header");
280
0
        return -1;
281
0
    }
282
0
    uint32_t headerSize;
283
0
    if (VSIFReadL(&headerSize, 4, 1, ctx->file) != 1) {
284
0
        msSetError(MS_FGBERR, "Failed to read header size", "flatgeobuf_decode_header");
285
0
        return -1;
286
0
    }
287
0
    ctx->offset += sizeof(uoffset_t);
288
0
    if (flatgeobuf_ensure_buf(ctx, headerSize) != 0) {
289
0
        return -1;
290
0
    }
291
0
    if (VSIFReadL(ctx->buf, 1, headerSize, ctx->file) != headerSize) {
292
0
        msSetError(MS_FGBERR, "Failed to read header", "flatgeobuf_decode_header");
293
0
        return -1;
294
0
    }
295
0
    auto header = GetHeader(ctx->buf);
296
0
  ctx->offset += headerSize;
297
298
0
  ctx->geometry_type = (uint8_t) header->geometry_type();
299
0
    ctx->features_count = header->features_count();
300
0
    auto envelope = header->envelope();
301
0
    if (envelope != nullptr) {
302
0
        ctx->has_extent = true;
303
0
        ctx->xmin = envelope->Get(0);
304
0
        ctx->ymin = envelope->Get(1);
305
0
        ctx->xmax = envelope->Get(2);
306
0
        ctx->ymax = envelope->Get(3);
307
0
        ctx->bounds.minx = ctx->xmin;
308
0
        ctx->bounds.miny = ctx->ymin;
309
0
        ctx->bounds.maxx = ctx->xmax;
310
0
        ctx->bounds.maxy = ctx->ymax;
311
0
    }
312
0
  ctx->has_z = header->has_z();
313
0
    ctx->has_m = header->has_m();
314
0
    ctx->has_t = header->has_t();
315
0
    ctx->has_tm = header->has_tm();
316
0
    ctx->index_node_size = header->index_node_size();
317
0
    auto crs = header->crs();
318
0
    if (crs != nullptr) {
319
0
        ctx->srid = crs->code();
320
0
        auto wkt = crs->wkt();
321
0
        if (wkt != nullptr)
322
0
            ctx->wkt = msStrdup(wkt->c_str());
323
0
    }
324
0
    auto columns = header->columns();
325
0
    if (columns != nullptr) {
326
0
        auto size = columns->size();
327
0
        ctx->columns = (flatgeobuf_column *) malloc(size * sizeof(flatgeobuf_column));
328
0
        memset(ctx->columns, 0, size * sizeof(flatgeobuf_column));
329
0
        ctx->columns_len = size;
330
0
        for (uint32_t i = 0; i < size; i++) {
331
0
            auto column = columns->Get(i);
332
0
            ctx->columns[i].name = msStrdup(column->name()->c_str());
333
0
            ctx->columns[i].type = (uint8_t) column->type();
334
0
            ctx->columns[i].itemindex = -1;
335
0
        }
336
0
    }
337
338
0
    ctx->index_offset = ctx->offset;
339
0
    ctx->feature_offset = ctx->offset;
340
0
    if ( ctx->index_node_size > 0)
341
0
        ctx->feature_offset += PackedRTree::size(ctx->features_count, ctx->index_node_size);
342
343
0
    return 0;
344
0
}
345
346
int flatgeobuf_index_search(ctx *ctx, rectObj *rect)
347
0
{
348
0
    const auto treeOffset = ctx->offset;
349
0
    const auto readNode = [treeOffset, ctx] (uint8_t *buf, size_t i, size_t s) {
350
0
        if (VSIFSeekL(ctx->file, treeOffset + i, SEEK_SET) == -1)
351
0
            throw std::runtime_error("Unable to seek in file");
352
0
        if (VSIFReadL(buf, 1, s, ctx->file) != s)
353
0
            throw std::runtime_error("Unable to read file");
354
0
    };
355
0
    NodeItem n { rect->minx, rect->miny, rect->maxx, rect->maxy, 0 };
356
0
    try {
357
0
        const auto foundItems = PackedRTree::streamSearch(ctx->features_count, ctx->index_node_size, n, readNode);
358
0
        ctx->search_result = (flatgeobuf_search_item *) malloc(foundItems.size() * sizeof(flatgeobuf_search_item));
359
0
        memcpy(ctx->search_result, foundItems.data(), foundItems.size() * sizeof(flatgeobuf_search_item));
360
0
        ctx->search_result_len = (uint32_t) foundItems.size();
361
0
    } catch (const std::exception &e) {
362
0
        msSetError(MS_FGBERR, "Unable to seek or read file", "flatgeobuf_index_search");
363
0
        return -1;
364
0
    }
365
0
    return 0;
366
0
}
367
368
int flatgeobuf_index_skip(ctx *ctx)
369
0
{
370
0
    auto treeSize = PackedRTree::size(ctx->features_count, ctx->index_node_size);
371
0
    ctx->offset += treeSize;
372
0
    if (VSIFSeekL(ctx->file, ctx->offset, SEEK_SET) == -1) {
373
0
        msSetError(MS_FGBERR, "Unable to seek in file", "flatgeobuf_index_skip");
374
0
        return -1;
375
0
    }
376
0
    return 0;
377
0
}
378
379
int flatgeobuf_read_feature_offset(ctx *ctx, uint64_t index, uint64_t *featureOffset)
380
0
{
381
0
    try {
382
0
        const auto levelBounds = PackedRTree::generateLevelBounds(ctx->features_count, ctx->index_node_size);
383
0
        const auto bottomLevelOffset = ctx->index_offset + (levelBounds.front().first * sizeof(NodeItem));
384
0
        const auto nodeItemOffset = bottomLevelOffset + (index * sizeof(NodeItem));
385
0
        const auto featureOffsetOffset = nodeItemOffset + (sizeof(double) * 4);
386
0
        if (VSIFSeekL(ctx->file, featureOffsetOffset, SEEK_SET) == -1) {
387
0
            msSetError(MS_FGBERR, "Failed to seek feature offset", "flatgeobuf_read_feature_offset");
388
0
            return -1;
389
0
        }
390
0
        if (VSIFReadL(featureOffset, sizeof(uint64_t), 1, ctx->file) != 1) {
391
0
            msSetError(MS_FGBERR, "Failed to read feature offset", "flatgeobuf_read_feature_offset");
392
0
            return -1;
393
0
        }
394
0
        return 0;
395
0
    } catch (const std::exception& e) {
396
0
        msSetError(MS_FGBERR, "Failed to calculate tree size", "flatgeobuf_read_feature_offset");
397
0
        return -1;
398
0
    }
399
0
}