Coverage Report

Created: 2025-06-09 08:44

/src/gdal/frmts/netcdf/netcdfsgwriterutil.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  netCDF read/write Driver
4
 * Purpose:  GDAL bindings over netCDF library.
5
 * Author:   Winor Chen <wchen329 at wisc.edu>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2019, Winor Chen <wchen329 at wisc.edu>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
#include "netcdfsgwriterutil.h"
13
#include "netcdfdataset.h"
14
15
namespace nccfdriver
16
{
17
SGeometry_Feature::SGeometry_Feature(OGRFeature &ft)
18
0
{
19
0
    this->hasInteriorRing = false;
20
0
    const OGRGeometry *geom = ft.GetGeometryRef();
21
22
0
    if (geom == nullptr)
23
0
    {
24
0
        throw SGWriter_Exception_NullGeometry();
25
0
    }
26
27
0
    OGRwkbGeometryType ogwkt = geom->getGeometryType();
28
0
    this->type = OGRtoRaw(ogwkt);
29
30
0
    if (this->type == POINT)
31
0
    {
32
        // Set total node count (1)
33
0
        this->total_point_count = 1;
34
35
        // Also single part geometry (1)
36
0
        this->total_part_count = 1;
37
38
        // One part per part
39
0
        ppart_node_count.push_back(1);
40
0
    }
41
42
0
    else if (this->type == MULTIPOINT)
43
0
    {
44
0
        const auto mp = geom->toMultiPoint();
45
46
        // Set total node count
47
0
        this->total_point_count = mp->getNumGeometries();
48
49
        // The amount of nodes is also the amount of parts
50
0
        for (size_t pc = 0; pc < total_point_count; pc++)
51
0
        {
52
0
            ppart_node_count.push_back(1);
53
0
        }
54
55
        // total part count == total node count
56
0
        this->total_part_count = this->total_point_count;
57
0
    }
58
59
0
    else if (this->type == LINE)
60
0
    {
61
0
        const auto line = geom->toLineString();
62
        // to do: check for std::bad_cast somewhere?
63
64
        // Get node count
65
0
        this->total_point_count = line->getNumPoints();
66
67
        // Single line: 1 part count == node count
68
0
        this->ppart_node_count.push_back(line->getNumPoints());
69
70
        // One part
71
0
        this->total_part_count = 1;
72
0
    }
73
74
0
    else if (this->type == MULTILINE)
75
0
    {
76
0
        const auto mls = geom->toMultiLineString();
77
0
        this->total_point_count = 0;
78
0
        this->total_part_count = mls->getNumGeometries();
79
80
        // Take each geometry, just add up the corresponding parts
81
82
0
        for (const auto ls : *mls)
83
0
        {
84
0
            int pt_count = ls->getNumPoints();
85
86
0
            this->ppart_node_count.push_back(pt_count);
87
0
            this->total_point_count += pt_count;
88
0
        }
89
0
    }
90
91
0
    else if (this->type == POLYGON)
92
0
    {
93
0
        const auto poly = geom->toPolygon();
94
95
0
        this->total_point_count = 0;
96
0
        this->total_part_count = 0;
97
98
        // Get node count
99
        // First count exterior ring
100
0
        const auto exterior_ring = poly->getExteriorRing();
101
0
        const size_t outer_ring_ct =
102
0
            exterior_ring ? exterior_ring->getNumPoints() : 0;
103
104
0
        this->total_point_count += outer_ring_ct;
105
0
        this->ppart_node_count.push_back(outer_ring_ct);
106
0
        this->total_part_count++;
107
108
        // Then count all from the interior rings
109
        // While doing the following
110
        // Get per part node count (per part RING count)
111
        // Get part count (in this case it's the amount of RINGS)
112
113
0
        for (int iRingCt = 0; iRingCt < poly->getNumInteriorRings(); iRingCt++)
114
0
        {
115
0
            this->hasInteriorRing = true;
116
0
            const auto iring = poly->getInteriorRing(iRingCt);
117
0
            if (iring)
118
0
            {
119
0
                this->total_point_count += iring->getNumPoints();
120
0
                this->ppart_node_count.push_back(iring->getNumPoints());
121
0
                this->total_part_count++;
122
0
            }
123
0
        }
124
0
    }
125
126
0
    else if (this->type == MULTIPOLYGON)
127
0
    {
128
0
        const auto poMP = geom->toMultiPolygon();
129
130
0
        this->total_point_count = 0;
131
0
        this->total_part_count = 0;
132
133
0
        for (const auto poly : *poMP)
134
0
        {
135
0
            const auto exterior_ring = poly->getExteriorRing();
136
0
            const size_t outer_ring_ct =
137
0
                exterior_ring ? exterior_ring->getNumPoints() : 0;
138
139
0
            this->total_point_count += outer_ring_ct;
140
0
            this->ppart_node_count.push_back(outer_ring_ct);
141
0
            this->total_part_count++;
142
0
            this->part_at_ind_interior.push_back(false);
143
144
            // Then count all from the interior rings
145
            // While doing the following
146
            // Get per part node count (per part RING count)
147
            // Get part count (in this case it's the amount of RINGS)
148
149
0
            for (int iRingCt = 0; iRingCt < poly->getNumInteriorRings();
150
0
                 iRingCt++)
151
0
            {
152
0
                const auto iring = poly->getInteriorRing(iRingCt);
153
0
                if (iring)
154
0
                {
155
0
                    this->hasInteriorRing = true;
156
0
                    this->total_point_count += iring->getNumPoints();
157
0
                    this->ppart_node_count.push_back(iring->getNumPoints());
158
0
                    this->total_part_count++;
159
0
                    this->part_at_ind_interior.push_back(true);
160
0
                }
161
0
            }
162
0
        }
163
0
    }
164
165
0
    else
166
0
    {
167
0
        throw SG_Exception_BadFeature();
168
0
    }
169
170
0
    this->geometry_ref = geom;
171
0
}
172
173
inline void WBuffer::addCount(unsigned long long memuse)
174
0
{
175
0
    this->used_mem += memuse;
176
0
}
177
178
inline void WBuffer::subCount(unsigned long long memfree)
179
0
{
180
    /* detect underflow cases-
181
     * better not subtract more than we already counted...
182
     */
183
0
    CPLAssert(this->used_mem >= memfree);
184
185
0
    this->used_mem -= memfree;
186
0
}
187
188
void ncLayer_SG_Metadata::initializeNewContainer(int containerVID)
189
0
{
190
0
    this->containerVar_realID = containerVID;
191
192
0
    netCDFVID &ncdf = this->vDataset;
193
0
    geom_t geo = this->writableType;
194
195
    // Define some virtual dimensions, and some virtual variables
196
0
    char container_name[NC_MAX_CHAR + 1] = {0};
197
0
    char node_coord_names[NC_MAX_CHAR + 1] = {0};
198
199
    // Set default values
200
0
    pnc_varID = INVALID_VAR_ID;
201
0
    pnc_dimID = INVALID_DIM_ID;
202
0
    intring_varID = INVALID_VAR_ID;
203
204
0
    int err_code;
205
0
    err_code = nc_inq_varname(ncID, containerVar_realID, container_name);
206
0
    NCDF_ERR(err_code);
207
0
    if (err_code != NC_NOERR)
208
0
    {
209
0
        throw SGWriter_Exception_NCInqFailure("new layer", "geometry container",
210
0
                                              "var name of");
211
0
    }
212
213
0
    this->containerVarName = std::string(container_name);
214
215
    // Node Coordinates - Dim
216
0
    std::string nodecoord_name =
217
0
        containerVarName + "_" + std::string(CF_SG_NODE_COORDINATES);
218
219
0
    node_coordinates_dimID = ncdf.nc_def_vdim(nodecoord_name.c_str(), 1);
220
221
    // Node Coordinates - Variable Names
222
0
    err_code = nc_get_att_text(ncID, containerVar_realID,
223
0
                               CF_SG_NODE_COORDINATES, node_coord_names);
224
0
    NCDF_ERR(err_code);
225
0
    if (err_code != NC_NOERR)
226
0
    {
227
0
        throw SGWriter_Exception_NCInqFailure(
228
0
            containerVarName.c_str(), CF_SG_NODE_COORDINATES, "varName");
229
0
    }
230
231
    // Node Count
232
0
    if (geo != POINT)
233
0
    {
234
0
        std::string nodecount_name =
235
0
            containerVarName + "_" + std::string(CF_SG_NODE_COUNT);
236
0
        node_count_dimID = ncdf.nc_def_vdim(nodecount_name.c_str(), 1);
237
0
        node_count_varID = ncdf.nc_def_vvar(nodecount_name.c_str(), NC_INT, 1,
238
0
                                            &node_count_dimID);
239
0
    }
240
241
    // Do the same for part node count, if it exists
242
0
    char pnc_name[NC_MAX_CHAR + 1] = {0};
243
0
    err_code = nc_get_att_text(ncID, containerVar_realID, CF_SG_PART_NODE_COUNT,
244
0
                               pnc_name);
245
246
0
    if (err_code == NC_NOERR)
247
0
    {
248
0
        pnc_dimID = ncdf.nc_def_vdim(pnc_name, 1);
249
0
        pnc_varID = ncdf.nc_def_vvar(pnc_name, NC_INT, 1, &pnc_dimID);
250
251
0
        char ir_name[NC_MAX_CHAR + 1] = {0};
252
0
        nc_get_att_text(ncID, containerVar_realID, CF_SG_INTERIOR_RING,
253
0
                        ir_name);
254
255
        // For interior ring too (for POLYGON and MULTIPOLYGON); there's always
256
        // an assumption that interior rings really do exist until the very end
257
        // in which case it's known whether or not that assumption was true or
258
        // false (if false, this (and PNC attribute for Polygons) will just be
259
        // deleted)
260
0
        if (this->writableType == POLYGON || this->writableType == MULTIPOLYGON)
261
0
        {
262
0
            intring_varID = ncdf.nc_def_vvar(ir_name, NC_INT, 1, &pnc_dimID);
263
0
        }
264
0
    }
265
266
    // Node coordinates Var Definitions
267
0
    int new_varID;
268
0
    CPLStringList aosNcoord(CSLTokenizeString2(node_coord_names, " ", 0));
269
270
0
    if (aosNcoord.size() < 2)
271
0
        throw SGWriter_Exception();
272
273
    // first it's X
274
0
    new_varID =
275
0
        ncdf.nc_def_vvar(aosNcoord[0], NC_DOUBLE, 1, &node_coordinates_dimID);
276
0
    ncdf.nc_put_vatt_text(new_varID, CF_AXIS, CF_SG_X_AXIS);
277
278
0
    this->node_coordinates_varIDs.push_back(new_varID);
279
280
    // second it's Y
281
0
    new_varID =
282
0
        ncdf.nc_def_vvar(aosNcoord[1], NC_DOUBLE, 1, &node_coordinates_dimID);
283
0
    ncdf.nc_put_vatt_text(new_varID, CF_AXIS, CF_SG_Y_AXIS);
284
285
0
    this->node_coordinates_varIDs.push_back(new_varID);
286
287
    // (and perhaps) third it's Z
288
0
    if (aosNcoord.size() > 2)
289
0
    {
290
0
        new_varID = ncdf.nc_def_vvar(aosNcoord[2], NC_DOUBLE, 1,
291
0
                                     &node_coordinates_dimID);
292
0
        ncdf.nc_put_vatt_text(new_varID, CF_AXIS, CF_SG_Z_AXIS);
293
294
0
        this->node_coordinates_varIDs.push_back(new_varID);
295
0
    }
296
0
}
297
298
ncLayer_SG_Metadata::ncLayer_SG_Metadata(int &i_ncID, geom_t geo,
299
                                         netCDFVID &ncdf, OGR_NCScribe &ncs)
300
2.88k
    : ncID(i_ncID), vDataset(ncdf), ncb(ncs), writableType(geo)
301
2.88k
{
302
2.88k
}
303
304
const OGRPoint &SGeometry_Feature::getPoint(size_t part_no,
305
                                            int point_index) const
306
0
{
307
0
    if (this->type == POINT)
308
0
    {
309
        // Point case: always return the single point regardless of any thing
310
311
0
        const OGRPoint *as_p_ref = geometry_ref->toPoint();
312
0
        return *as_p_ref;
313
0
    }
314
315
0
    if (this->type == MULTIPOINT)
316
0
    {
317
0
        const OGRMultiPoint *as_mp_ref = geometry_ref->toMultiPoint();
318
0
        int part_ind = static_cast<int>(part_no);
319
0
        const OGRPoint *pt = as_mp_ref->getGeometryRef(part_ind);
320
0
        return *pt;
321
0
    }
322
323
0
    if (this->type == LINE)
324
0
    {
325
0
        const OGRLineString *as_line_ref = geometry_ref->toLineString();
326
0
        as_line_ref->getPoint(point_index, &pt_buffer);
327
0
    }
328
329
0
    if (this->type == MULTILINE)
330
0
    {
331
0
        const OGRMultiLineString *as_mline_ref =
332
0
            geometry_ref->toMultiLineString();
333
0
        CPLAssert(as_mline_ref);
334
0
        int part_ind = static_cast<int>(part_no);
335
0
        const OGRLineString *lstring = as_mline_ref->getGeometryRef(part_ind);
336
0
        CPLAssert(lstring);
337
0
        lstring->getPoint(point_index, &pt_buffer);
338
0
    }
339
340
0
    if (this->type == POLYGON)
341
0
    {
342
0
        const OGRPolygon *as_polygon_ref = geometry_ref->toPolygon();
343
0
        int ring_ind = static_cast<int>(part_no);
344
345
0
        if (part_no == 0)
346
0
        {
347
0
            as_polygon_ref->getExteriorRing()->getPoint(point_index,
348
0
                                                        &pt_buffer);
349
0
        }
350
351
0
        else
352
0
        {
353
0
            as_polygon_ref->getInteriorRing(ring_ind - 1)
354
0
                ->getPoint(point_index, &pt_buffer);
355
0
        }
356
0
    }
357
358
0
    if (this->type == MULTIPOLYGON)
359
0
    {
360
0
        const OGRMultiPolygon *as_mpolygon_ref = geometry_ref->toMultiPolygon();
361
0
        int polygon_num = 0;
362
0
        int ring_number = 0;
363
0
        int pno_itr = static_cast<int>(part_no);
364
365
        // Find the right polygon, and the right ring number
366
0
        for (int pind = 0; pind < as_mpolygon_ref->getNumGeometries(); pind++)
367
0
        {
368
0
            const OGRPolygon *itr_poly = as_mpolygon_ref->getGeometryRef(pind);
369
0
            if (pno_itr < (itr_poly->getNumInteriorRings() +
370
0
                           1))  // + 1 is counting the EXTERIOR ring
371
0
            {
372
0
                ring_number = static_cast<int>(pno_itr);
373
0
                break;
374
0
            }
375
376
0
            else
377
0
            {
378
0
                pno_itr -= (itr_poly->getNumInteriorRings() + 1);
379
0
                polygon_num++;
380
0
            }
381
0
        }
382
383
0
        const OGRPolygon *key_polygon =
384
0
            as_mpolygon_ref->getGeometryRef(polygon_num);
385
386
0
        if (ring_number == 0)
387
0
        {
388
0
            key_polygon->getExteriorRing()->getPoint(point_index, &pt_buffer);
389
0
        }
390
391
0
        else
392
0
        {
393
0
            key_polygon->getInteriorRing(ring_number - 1)
394
0
                ->getPoint(point_index, &pt_buffer);
395
0
        }
396
0
    }
397
398
0
    return pt_buffer;
399
0
}
400
401
void ncLayer_SG_Metadata::writeSGeometryFeature(SGeometry_Feature &ft)
402
0
{
403
0
    if (ft.getType() == NONE)
404
0
    {
405
0
        throw SG_Exception_BadFeature();
406
0
    }
407
408
    // Write each point from each part in node coordinates
409
0
    for (size_t part_no = 0; part_no < ft.getTotalPartCount(); part_no++)
410
0
    {
411
0
        if (this->writableType == POLYGON || this->writableType == MULTIPOLYGON)
412
0
        {
413
0
            int interior_ring_fl = 1;
414
415
0
            if (this->writableType == POLYGON)
416
0
            {
417
0
                interior_ring_fl = part_no == 0 ? 0 : 1;
418
0
            }
419
420
0
            else if (this->writableType == MULTIPOLYGON)
421
0
            {
422
0
                if (ft.IsPartAtIndInteriorRing(part_no))
423
0
                {
424
0
                    interior_ring_fl = 1;
425
0
                }
426
427
0
                else
428
0
                {
429
0
                    interior_ring_fl = 0;
430
0
                }
431
0
            }
432
433
0
            if (interior_ring_fl)
434
0
            {
435
0
                this->interiorRingDetected = true;
436
0
            }
437
438
0
            ncb.enqueue_transaction(MTPtr(new OGR_SGFS_NC_Int_Transaction(
439
0
                intring_varID, interior_ring_fl)));
440
0
        }
441
442
0
        if (this->writableType == POLYGON || this->writableType == MULTILINE ||
443
0
            this->writableType == MULTIPOLYGON)
444
0
        {
445
0
            int pnc_writable =
446
0
                static_cast<int>(ft.getPerPartNodeCount()[part_no]);
447
0
            ncb.enqueue_transaction(MTPtr(
448
0
                new OGR_SGFS_NC_Int_Transaction(pnc_varID, pnc_writable)));
449
0
            this->next_write_pos_pnc++;
450
0
        }
451
452
0
        for (size_t pt_ind = 0; pt_ind < ft.getPerPartNodeCount()[part_no];
453
0
             pt_ind++)
454
0
        {
455
0
            int pt_ind_int = static_cast<int>(pt_ind);
456
0
            const OGRPoint &write_pt = ft.getPoint(part_no, pt_ind_int);
457
458
            // Write each node coordinate
459
0
            double x = write_pt.getX();
460
0
            ncb.enqueue_transaction(MTPtr(new OGR_SGFS_NC_Double_Transaction(
461
0
                node_coordinates_varIDs[0], x)));
462
463
0
            double y = write_pt.getY();
464
0
            ncb.enqueue_transaction(MTPtr(new OGR_SGFS_NC_Double_Transaction(
465
0
                node_coordinates_varIDs[1], y)));
466
467
0
            if (this->node_coordinates_varIDs.size() > 2)
468
0
            {
469
0
                double z = write_pt.getZ();
470
0
                ncb.enqueue_transaction(
471
0
                    MTPtr(new OGR_SGFS_NC_Double_Transaction(
472
0
                        node_coordinates_varIDs[2], z)));
473
0
            }
474
0
        }
475
476
0
        this->next_write_pos_node_coord += ft.getPerPartNodeCount()[part_no];
477
0
    }
478
479
    // Append node counts from the end, if not a POINT
480
0
    if (this->writableType != POINT)
481
0
    {
482
0
        int ncount_add = static_cast<int>(ft.getTotalNodeCount());
483
0
        ncb.enqueue_transaction(MTPtr(
484
0
            new OGR_SGFS_NC_Int_Transaction(node_count_varID, ncount_add)));
485
0
        this->next_write_pos_node_count++;
486
487
        // Special case: The "empty" MultiPolygon type
488
        // MultiPolygon part_node_counts are counted in terms of "rings" not
489
        // parts contrary to the name so an empty multipolygon with no rings
490
        // will slip past the regular part_node_count placement In essence this
491
        // is probably taken as "if there are no rings" then "there are also no
492
        // points"
493
0
        if (ft.getTotalPartCount() == 0 && this->writableType == MULTIPOLYGON &&
494
0
            (ft.getType() == POLYGON || ft.getType() == MULTIPOLYGON))
495
0
        {
496
0
            ncb.enqueue_transaction(
497
0
                MTPtr(new OGR_SGFS_NC_Int_Transaction(pnc_varID, 0)));
498
0
            this->next_write_pos_pnc++;
499
0
        }
500
0
    }
501
0
}
502
503
static std::string sgwe_msg_builder(const char *layer_name,
504
                                    const char *failure_name,
505
                                    const char *failure_type,
506
                                    const char *special_msg)
507
0
{
508
0
    return std::string("[") + std::string(layer_name) + std::string("] ") +
509
0
           std::string(failure_type) + std::string(" ") +
510
0
           std::string(failure_name) + std::string(" ") +
511
0
           std::string(special_msg);
512
0
}
513
514
// Exception related definitions
515
SGWriter_Exception_NCWriteFailure::SGWriter_Exception_NCWriteFailure(
516
    const char *layer_name, const char *failure_name, const char *failure_type)
517
0
    : SGWriter_Exception(
518
0
          sgwe_msg_builder(layer_name, failure_name, failure_type,
519
0
                           "could not be written to (write failure)."))
520
0
{
521
0
}
522
523
SGWriter_Exception_NCInqFailure::SGWriter_Exception_NCInqFailure(
524
    const char *layer_name, const char *failure_name, const char *failure_type)
525
0
    : SGWriter_Exception(sgwe_msg_builder(
526
0
          layer_name, failure_name, failure_type,
527
0
          "could not be read from (property inquiry failure)."))
528
0
{
529
0
}
530
531
SGWriter_Exception_NCDefFailure::SGWriter_Exception_NCDefFailure(
532
    const char *layer_name, const char *failure_name, const char *failure_type)
533
0
    : SGWriter_Exception(sgwe_msg_builder(
534
0
          layer_name, failure_name, failure_type,
535
0
          "could not be defined in the dataset (definition failure)."))
536
0
{
537
0
}
538
539
// OGR_NCScribe
540
void OGR_NCScribe::enqueue_transaction(MTPtr transactionAdd)
541
0
{
542
0
    if (transactionAdd.get() == nullptr)
543
0
    {
544
0
        return;
545
0
    }
546
547
    // See if the variable name is already being written to
548
0
    if (this->varMaxInds.count(transactionAdd->getVarId()) > 0)
549
0
    {
550
0
        size_t varWriteLength = this->varMaxInds[transactionAdd->getVarId()];
551
0
        varWriteLength++;
552
0
        this->varMaxInds[transactionAdd->getVarId()] = varWriteLength;
553
0
    }
554
555
0
    else
556
0
    {
557
        // Otherwise, just add it to the list of variable names being written to
558
0
        std::pair<int, size_t> entry(transactionAdd->getVarId(), 1);
559
0
        this->varMaxInds.insert(entry);
560
0
    }
561
562
    // Add sizes to memory count
563
0
    this->buf.addCount(sizeof(transactionAdd));   // account for pointer
564
0
    this->buf.addCount(transactionAdd->count());  // account for pointee
565
566
    // Finally push the transaction in
567
0
    this->transactionQueue.push(MTPtr(transactionAdd.release()));
568
0
}
569
570
void OGR_NCScribe::commit_transaction()
571
0
{
572
0
    wl.startRead();
573
574
0
    NCWMap writerMap;
575
0
    std::vector<int> varV;
576
577
0
    MTPtr t;
578
0
    t = this->pop();
579
580
0
    while (t.get() != nullptr)
581
0
    {
582
0
        int varId = t->getVarId();
583
0
        size_t writeInd;
584
585
        // First, find where to write. If doesn't exist, write to index 0
586
0
        if (this->varWriteInds.count(varId) > 0)
587
0
        {
588
0
            writeInd = this->varWriteInds[varId];
589
0
        }
590
591
0
        else
592
0
        {
593
0
            std::pair<int, size_t> insertable(varId, 0);
594
0
            this->varWriteInds.insert(insertable);
595
0
            writeInd = 0;
596
0
        }
597
598
        // Then write
599
        // Subtract sizes from memory count
600
0
        this->buf.subCount(sizeof(t));   // account for pointer
601
0
        this->buf.subCount(t->count());  // account for pointee
602
603
0
        try
604
0
        {
605
            // If variable length type, for now, continue using old committing
606
            // scheme Maybe some future work: optimize this in the similar
607
            // manner to other types However, CHAR and STRING have huge copying
608
            // overhead and are more complicated to memory manage correctly
609
0
            if (t->getType() == NC_CHAR || t->getType() == NC_STRING ||
610
0
                singleDatumMode)
611
0
            {
612
0
                t->commit(ncvd, writeInd);
613
0
            }
614
615
            // Other types: Use a more optimized approach
616
0
            else
617
0
            {
618
0
                int wvid = t->getVarId();
619
0
                size_t numEntries = this->varMaxInds.at(wvid);
620
0
                nc_type ncw = t->getType();
621
0
                size_t curWrInd = this->varWriteInds.at(wvid);
622
623
                // If entry doesn't exist in map, then add it
624
0
                switch (ncw)
625
0
                {
626
0
                    case NC_BYTE:
627
0
                    {
628
0
                        NCWMapAllocIfNeeded<signed char>(wvid, writerMap,
629
0
                                                         numEntries, varV);
630
0
                        auto byte_trn =
631
0
                            cpl::down_cast<OGR_SGFS_NC_Byte_Transaction *>(
632
0
                                t.get());
633
0
                        NCWMapWriteAndCommit<signed char>(
634
0
                            wvid, writerMap, curWrInd, numEntries,
635
0
                            byte_trn->getData(), this->ncvd);
636
0
                        break;
637
0
                    }
638
0
                    case NC_SHORT:
639
0
                    {
640
0
                        NCWMapAllocIfNeeded<short>(wvid, writerMap, numEntries,
641
0
                                                   varV);
642
0
                        auto short_trn =
643
0
                            cpl::down_cast<OGR_SGFS_NC_Short_Transaction *>(
644
0
                                t.get());
645
0
                        NCWMapWriteAndCommit<short>(
646
0
                            wvid, writerMap, curWrInd, numEntries,
647
0
                            short_trn->getData(), this->ncvd);
648
0
                        break;
649
0
                    }
650
0
                    case NC_INT:
651
0
                    {
652
0
                        NCWMapAllocIfNeeded<int>(wvid, writerMap, numEntries,
653
0
                                                 varV);
654
0
                        auto int_trn =
655
0
                            cpl::down_cast<OGR_SGFS_NC_Int_Transaction *>(
656
0
                                t.get());
657
0
                        NCWMapWriteAndCommit<int>(
658
0
                            wvid, writerMap, curWrInd, numEntries,
659
0
                            int_trn->getData(), this->ncvd);
660
0
                        break;
661
0
                    }
662
0
                    case NC_FLOAT:
663
0
                    {
664
0
                        NCWMapAllocIfNeeded<float>(wvid, writerMap, numEntries,
665
0
                                                   varV);
666
0
                        auto float_trn =
667
0
                            cpl::down_cast<OGR_SGFS_NC_Float_Transaction *>(
668
0
                                t.get());
669
0
                        NCWMapWriteAndCommit<float>(
670
0
                            wvid, writerMap, curWrInd, numEntries,
671
0
                            float_trn->getData(), this->ncvd);
672
0
                        break;
673
0
                    }
674
0
                    case NC_DOUBLE:
675
0
                    {
676
0
                        NCWMapAllocIfNeeded<double>(wvid, writerMap, numEntries,
677
0
                                                    varV);
678
0
                        auto double_trn =
679
0
                            cpl::down_cast<OGR_SGFS_NC_Double_Transaction *>(
680
0
                                t.get());
681
0
                        NCWMapWriteAndCommit<double>(
682
0
                            wvid, writerMap, curWrInd, numEntries,
683
0
                            double_trn->getData(), this->ncvd);
684
0
                        break;
685
0
                    }
686
0
                    case NC_UINT:
687
0
                    {
688
0
                        NCWMapAllocIfNeeded<unsigned>(wvid, writerMap,
689
0
                                                      numEntries, varV);
690
0
                        auto uint_trn =
691
0
                            cpl::down_cast<OGR_SGFS_NC_UInt_Transaction *>(
692
0
                                t.get());
693
0
                        NCWMapWriteAndCommit<unsigned>(
694
0
                            wvid, writerMap, curWrInd, numEntries,
695
0
                            uint_trn->getData(), this->ncvd);
696
0
                        break;
697
0
                    }
698
0
                    case NC_UINT64:
699
0
                    {
700
0
                        NCWMapAllocIfNeeded<unsigned long long>(
701
0
                            wvid, writerMap, numEntries, varV);
702
0
                        auto uint64_trn =
703
0
                            cpl::down_cast<OGR_SGFS_NC_UInt64_Transaction *>(
704
0
                                t.get());
705
0
                        NCWMapWriteAndCommit<unsigned long long>(
706
0
                            wvid, writerMap, curWrInd, numEntries,
707
0
                            uint64_trn->getData(), this->ncvd);
708
0
                        break;
709
0
                    }
710
0
                    case NC_INT64:
711
0
                    {
712
0
                        NCWMapAllocIfNeeded<long long>(wvid, writerMap,
713
0
                                                       numEntries, varV);
714
0
                        auto int64_trn =
715
0
                            cpl::down_cast<OGR_SGFS_NC_Int64_Transaction *>(
716
0
                                t.get());
717
0
                        NCWMapWriteAndCommit<long long>(
718
0
                            wvid, writerMap, curWrInd, numEntries,
719
0
                            int64_trn->getData(), this->ncvd);
720
0
                        break;
721
0
                    }
722
0
                    case NC_UBYTE:
723
0
                    {
724
0
                        NCWMapAllocIfNeeded<unsigned char>(wvid, writerMap,
725
0
                                                           numEntries, varV);
726
0
                        auto ubyte_trn =
727
0
                            cpl::down_cast<OGR_SGFS_NC_UByte_Transaction *>(
728
0
                                t.get());
729
0
                        NCWMapWriteAndCommit<unsigned char>(
730
0
                            wvid, writerMap, curWrInd, numEntries,
731
0
                            ubyte_trn->getData(), this->ncvd);
732
0
                        break;
733
0
                    }
734
0
                    case NC_USHORT:
735
0
                    {
736
0
                        NCWMapAllocIfNeeded<unsigned short>(wvid, writerMap,
737
0
                                                            numEntries, varV);
738
0
                        auto ushort_trn =
739
0
                            cpl::down_cast<OGR_SGFS_NC_UShort_Transaction *>(
740
0
                                t.get());
741
0
                        NCWMapWriteAndCommit<unsigned short>(
742
0
                            wvid, writerMap, curWrInd, numEntries,
743
0
                            ushort_trn->getData(), this->ncvd);
744
0
                        break;
745
0
                    }
746
0
                    default:
747
0
                    {
748
0
                        break;
749
0
                    }
750
0
                }
751
0
            }
752
0
        }
753
0
        catch (SG_Exception &sge)
754
0
        {
755
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", sge.get_err_msg());
756
0
        }
757
758
        // increment index
759
0
        this->varWriteInds[varId]++;
760
0
        t = this->pop();
761
0
    }
762
763
    // Clean up afterwards, potential miswrites
764
0
    for (size_t vcount = 0; vcount < varV.size(); vcount++)
765
0
    {
766
0
        int cleanid = varV[vcount];
767
768
0
        if (writerMap.count(cleanid) > 0)
769
0
        {
770
0
            CPLFree(writerMap.at(cleanid));
771
0
            CPLError(CE_Failure, CPLE_FileIO,
772
0
                     "Transaction corruption detected. The target variable "
773
0
                     "will most likely be missing data.");
774
0
        }
775
0
    }
776
0
}
777
778
MTPtr OGR_NCScribe::pop()
779
0
{
780
    // Buffered changes are the earliest, so commit those first
781
0
    MTPtr m = this->wl.pop();
782
0
    if (m.get() != nullptr)
783
0
    {
784
        // add it to the buffer count
785
0
        this->buf.addCount(sizeof(m));
786
0
        this->buf.addCount(m->count());
787
0
        return m;
788
0
    }
789
790
0
    else if (!transactionQueue.empty())
791
0
    {
792
0
        OGR_SGFS_Transaction *value =
793
0
            this->transactionQueue.front()
794
0
                .release();  // due to delete copy A.K.A uniqueness of
795
                             // unique_ptr
796
0
        this->transactionQueue.pop();
797
798
0
        return MTPtr(value);
799
0
    }
800
0
    else
801
0
    {
802
0
        return MTPtr();
803
0
    }
804
0
}
805
806
void OGR_NCScribe::log_transaction()
807
0
{
808
0
    if (wl.logIsNull())
809
0
        wl.startLog();
810
811
0
    while (!transactionQueue.empty())
812
0
    {
813
        // coverity[var_deref_model]
814
0
        wl.push(MTPtr(transactionQueue.front().release()));
815
0
        this->transactionQueue.pop();
816
0
    }
817
0
    this->buf.reset();
818
0
}
819
820
// WBufferManager
821
bool WBufferManager::isOverQuota()
822
0
{
823
0
    unsigned long long sum = 0;
824
0
    for (size_t s = 0; s < bufs.size(); s++)
825
0
    {
826
0
        WBuffer &b = *(bufs[s]);
827
0
        sum += b.getUsage();
828
0
    }
829
830
0
    return sum > this->buffer_soft_limit;
831
0
}
832
833
// Transactions
834
void OGR_SGFS_NC_Char_Transaction::appendToLog(VSILFILE *f)
835
0
{
836
0
    int vid = OGR_SGFS_Transaction::getVarId();
837
0
    int type = NC_CHAR;
838
0
    int8_t OP = 0;
839
0
    size_t DATA_SIZE = char_rep.length();
840
841
0
    VSIFWriteL(&vid, sizeof(int), 1, f);           // write varID data
842
0
    VSIFWriteL(&type, sizeof(int), 1, f);          // write NC type
843
0
    VSIFWriteL(&OP, sizeof(int8_t), 1, f);         // write "OP" flag
844
0
    VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f);  // write length
845
0
    VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f);  // write data
846
0
}
847
848
void OGR_SGFS_NC_String_Transaction::appendToLog(VSILFILE *f)
849
0
{
850
0
    int vid = OGR_SGFS_Transaction::getVarId();
851
0
    int type = NC_STRING;
852
0
    size_t DATA_SIZE = char_rep.length();
853
854
0
    VSIFWriteL(&vid, sizeof(int), 1, f);           // write varID data
855
0
    VSIFWriteL(&type, sizeof(int), 1, f);          // write NC type
856
0
    VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f);  // write length
857
0
    VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f);  // write data
858
0
}
859
860
void OGR_SGFS_NC_CharA_Transaction::appendToLog(VSILFILE *f)
861
0
{
862
0
    int vid = OGR_SGFS_Transaction::getVarId();
863
0
    int type = NC_CHAR;
864
0
    int8_t OP = 1;
865
0
    size_t DATA_SIZE = char_rep.length();
866
867
0
    VSIFWriteL(&vid, sizeof(int), 1, f);           // write varID data
868
0
    VSIFWriteL(&type, sizeof(int), 1, f);          // write NC type
869
0
    VSIFWriteL(&OP, sizeof(int8_t), 1, f);         // write "OP" flag
870
0
    VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f);  // write length
871
0
    VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f);  // write data
872
0
}
873
874
// WTransactionLog
875
46.3k
WTransactionLog::WTransactionLog(const std::string &logName) : wlogName(logName)
876
46.3k
{
877
46.3k
}
878
879
void WTransactionLog::startLog()
880
0
{
881
0
    log = VSIFOpenL(wlogName.c_str(), "w");
882
0
}
883
884
void WTransactionLog::startRead()
885
0
{
886
0
    if (log == nullptr)
887
0
        return;
888
889
0
    VSIFCloseL(this->log);
890
0
    this->log = VSIFOpenL(wlogName.c_str(), "r");
891
0
}
892
893
void WTransactionLog::push(MTPtr t)
894
0
{
895
0
    t->appendToLog(this->log);
896
0
}
897
898
MTPtr WTransactionLog::pop()
899
0
{
900
0
    if (log == nullptr)
901
0
        return MTPtr(nullptr);
902
903
0
    int varId;
904
0
    nc_type ntype;
905
0
    size_t itemsread;
906
0
    itemsread = VSIFReadL(&varId, sizeof(int), 1, log);
907
0
    itemsread &= VSIFReadL(&ntype, sizeof(nc_type), 1, log);
908
909
    // If one of the two reads failed, then return nullptr
910
0
    if (!itemsread)
911
0
        return MTPtr(nullptr);
912
913
    // If not, continue on and parse additional fields
914
0
    switch (ntype)
915
0
    {
916
        // NC-3 Primitives
917
0
        case NC_BYTE:
918
0
            return genericLogDataRead<OGR_SGFS_NC_Byte_Transaction,
919
0
                                      signed char>(varId, log);
920
0
        case NC_SHORT:
921
0
            return genericLogDataRead<OGR_SGFS_NC_Short_Transaction, short>(
922
0
                varId, log);
923
0
        case NC_INT:
924
0
            return genericLogDataRead<OGR_SGFS_NC_Int_Transaction, int>(varId,
925
0
                                                                        log);
926
0
        case NC_FLOAT:
927
0
            return genericLogDataRead<OGR_SGFS_NC_Float_Transaction, float>(
928
0
                varId, log);
929
0
        case NC_DOUBLE:
930
0
            return genericLogDataRead<OGR_SGFS_NC_Double_Transaction, double>(
931
0
                varId, log);
932
0
        case NC_UBYTE:
933
0
            return genericLogDataRead<OGR_SGFS_NC_UByte_Transaction,
934
0
                                      unsigned char>(varId, log);
935
0
        case NC_USHORT:
936
0
            return genericLogDataRead<OGR_SGFS_NC_UShort_Transaction,
937
0
                                      unsigned short>(varId, log);
938
0
        case NC_UINT:
939
0
            return genericLogDataRead<OGR_SGFS_NC_UInt_Transaction,
940
0
                                      unsigned int>(varId, log);
941
0
        case NC_INT64:
942
0
            return genericLogDataRead<OGR_SGFS_NC_Int64_Transaction, long long>(
943
0
                varId, log);
944
0
        case NC_UINT64:
945
0
            return genericLogDataRead<OGR_SGFS_NC_UInt64_Transaction,
946
0
                                      unsigned long long>(varId, log);
947
0
        case NC_CHAR:
948
0
        {
949
0
            size_t readcheck;  // 0 means at least one read 0 bytes
950
951
            // Check what type of OP is requested
952
0
            int8_t op = 0;
953
0
            readcheck = VSIFReadL(&op, sizeof(int8_t), 1, log);
954
0
            if (!readcheck)
955
0
                return MTPtr();  // read failure
956
957
0
            size_t strsize;
958
959
            // get how much data to read
960
0
            readcheck = VSIFReadL(&strsize, sizeof(size_t), 1, log);
961
0
            if (!readcheck)
962
0
                return MTPtr();  // read failure
963
964
0
            std::string data;
965
0
            data.resize(strsize);
966
967
            // read that data and return it
968
0
            readcheck = VSIFReadL(&data[0], sizeof(char), strsize, log);
969
0
            if (!readcheck)
970
0
                return MTPtr();  // read failure
971
972
            // case: its a standard CHAR op
973
0
            if (!op)
974
0
            {
975
0
                return MTPtr(new OGR_SGFS_NC_Char_Transaction(
976
0
                    varId, &data[0]));  // data is copied so okay!
977
0
            }
978
979
            // case: its a CHARA op, additional processing
980
0
            else
981
0
            {
982
0
                return MTPtr(
983
0
                    new OGR_SGFS_NC_CharA_Transaction(varId, &data[0]));
984
0
            }
985
0
        }
986
987
0
        case NC_STRING:
988
0
        {
989
0
            size_t readcheck;  // 0 means at least one read 0 bytes
990
991
0
            size_t strsize;
992
993
            // get how much data to read
994
0
            readcheck = VSIFReadL(&strsize, sizeof(size_t), 1, log);
995
996
0
            if (!readcheck)
997
0
                return MTPtr();  // read failure
998
999
0
            std::string data;
1000
0
            data.resize(strsize);
1001
1002
            // read that data and return it
1003
0
            readcheck = VSIFReadL(&data[0], sizeof(char), strsize, log);
1004
1005
0
            if (!readcheck)
1006
0
                return MTPtr();  // read failure
1007
1008
0
            return MTPtr(new OGR_SGFS_NC_String_Transaction(
1009
0
                varId, &data[0]));  // data is copied so okay!
1010
0
        }
1011
1012
0
        default:
1013
            // Unsupported type
1014
0
            return MTPtr();
1015
0
    }
1016
0
}
1017
1018
WTransactionLog::~WTransactionLog()
1019
46.3k
{
1020
46.3k
    if (log != nullptr)
1021
0
    {
1022
0
        VSIFCloseL(log);
1023
0
        VSIUnlink(this->wlogName.c_str());
1024
0
    }
1025
46.3k
}
1026
1027
// Helper function definitions
1028
int write_Geometry_Container(
1029
    int ncID, const std::string &name, geom_t geometry_type,
1030
    const std::vector<std::string> &node_coordinate_names)
1031
0
{
1032
1033
0
    int write_var_id;
1034
0
    int err_code;
1035
1036
    // Define geometry container variable
1037
0
    err_code =
1038
0
        nc_def_var(ncID, name.c_str(), NC_FLOAT, 0, nullptr, &write_var_id);
1039
    // todo: exception handling of err_code
1040
0
    NCDF_ERR(err_code);
1041
0
    if (err_code != NC_NOERR)
1042
0
    {
1043
0
        throw SGWriter_Exception_NCDefFailure(name.c_str(),
1044
0
                                              "geometry_container", "variable");
1045
0
    }
1046
1047
    /* Geometry Type Attribute
1048
     * -
1049
     */
1050
1051
    // Next, go on to add attributes needed for each geometry type
1052
0
    std::string geometry_str =
1053
0
        (geometry_type == POINT || geometry_type == MULTIPOINT)
1054
0
            ? CF_SG_TYPE_POINT
1055
0
        : (geometry_type == LINE || geometry_type == MULTILINE)
1056
0
            ? CF_SG_TYPE_LINE
1057
0
        : (geometry_type == POLYGON || geometry_type == MULTIPOLYGON)
1058
0
            ? CF_SG_TYPE_POLY
1059
0
            : "";  // obviously an error condition...
1060
1061
0
    if (geometry_str == "")
1062
0
    {
1063
0
        throw SG_Exception_BadFeature();
1064
0
    }
1065
1066
    // Add the geometry type attribute
1067
0
    err_code = nc_put_att_text(ncID, write_var_id, CF_SG_GEOMETRY_TYPE,
1068
0
                               geometry_str.size(), geometry_str.c_str());
1069
0
    NCDF_ERR(err_code);
1070
0
    if (err_code != NC_NOERR)
1071
0
    {
1072
0
        throw SGWriter_Exception_NCWriteFailure(
1073
0
            name.c_str(), CF_SG_GEOMETRY_TYPE,
1074
0
            "attribute in geometry_container");
1075
0
    }
1076
1077
    /* Node Coordinates Attribute
1078
     * -
1079
     */
1080
0
    std::string ncoords_atr_str = "";
1081
1082
0
    for (size_t itr = 0; itr < node_coordinate_names.size(); itr++)
1083
0
    {
1084
0
        ncoords_atr_str += node_coordinate_names[itr];
1085
0
        if (itr < node_coordinate_names.size() - 1)
1086
0
        {
1087
0
            ncoords_atr_str += " ";
1088
0
        }
1089
0
    }
1090
1091
0
    err_code = nc_put_att_text(ncID, write_var_id, CF_SG_NODE_COORDINATES,
1092
0
                               ncoords_atr_str.size(), ncoords_atr_str.c_str());
1093
1094
0
    NCDF_ERR(err_code);
1095
0
    if (err_code != NC_NOERR)
1096
0
    {
1097
0
        throw SGWriter_Exception_NCWriteFailure(
1098
0
            name.c_str(), CF_SG_NODE_COORDINATES,
1099
0
            "attribute in geometry_container");
1100
0
    }
1101
    // The previous two attributes are all that are required from POINT
1102
1103
    /* Node_Count Attribute
1104
     * (not needed for POINT)
1105
     */
1106
0
    if (geometry_type != POINT)
1107
0
    {
1108
0
        std::string nodecount_atr_str = name + "_node_count";
1109
1110
0
        err_code = nc_put_att_text(ncID, write_var_id, CF_SG_NODE_COUNT,
1111
0
                                   nodecount_atr_str.size(),
1112
0
                                   nodecount_atr_str.c_str());
1113
0
        NCDF_ERR(err_code);
1114
0
        if (err_code != NC_NOERR)
1115
0
        {
1116
0
            throw SGWriter_Exception_NCWriteFailure(
1117
0
                name.c_str(), CF_SG_NODE_COUNT,
1118
0
                "attribute in geometry_container");
1119
0
        }
1120
0
    }
1121
1122
    /* Part_Node_Count Attribute
1123
     * (only needed for MULTILINE, MULTIPOLYGON, and (potentially) POLYGON)
1124
     */
1125
0
    if (geometry_type == MULTILINE || geometry_type == MULTIPOLYGON ||
1126
0
        geometry_type == POLYGON)
1127
0
    {
1128
0
        std::string pnc_atr_str = name + "_part_node_count";
1129
1130
0
        err_code = nc_put_att_text(ncID, write_var_id, CF_SG_PART_NODE_COUNT,
1131
0
                                   pnc_atr_str.size(), pnc_atr_str.c_str());
1132
1133
0
        NCDF_ERR(err_code);
1134
0
        if (err_code != NC_NOERR)
1135
0
        {
1136
0
            throw SGWriter_Exception_NCWriteFailure(
1137
0
                name.c_str(), CF_SG_PART_NODE_COUNT,
1138
0
                "attribute in geometry_container");
1139
0
        }
1140
0
    }
1141
1142
    /* Interior Ring Attribute
1143
     * (only needed potentially for MULTIPOLYGON and POLYGON)
1144
     */
1145
1146
0
    if (geometry_type == MULTIPOLYGON || geometry_type == POLYGON)
1147
0
    {
1148
0
        std::string ir_atr_str = name + "_interior_ring";
1149
1150
0
        err_code = nc_put_att_text(ncID, write_var_id, CF_SG_INTERIOR_RING,
1151
0
                                   ir_atr_str.size(), ir_atr_str.c_str());
1152
0
        NCDF_ERR(err_code);
1153
0
        if (err_code != NC_NOERR)
1154
0
        {
1155
0
            throw nccfdriver::SGWriter_Exception_NCWriteFailure(
1156
0
                name.c_str(), CF_SG_INTERIOR_RING,
1157
0
                "attribute in geometry_container");
1158
0
        }
1159
0
    }
1160
1161
0
    return write_var_id;
1162
0
}
1163
1164
0
OGR_SGFS_Transaction::~OGR_SGFS_Transaction() = default;
1165
1166
}  // namespace nccfdriver