Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Purpose:  Implementation of the CBandInterleavedChannel class.
4
 *
5
 * This class is used to implement band interleaved channels within a
6
 * PCIDSK file (which are always packed, and FILE interleaved data from
7
 * external raw files which may not be packed.
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 2009
11
 * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
#include "pcidsk_config.h"
17
#include "pcidsk_types.h"
18
#include "pcidsk_channel.h"
19
#include "pcidsk_buffer.h"
20
#include "pcidsk_exception.h"
21
#include "pcidsk_file.h"
22
#include "core/pcidsk_utils.h"
23
#include "core/cpcidskfile.h"
24
#include "core/clinksegment.h"
25
#include "channel/cbandinterleavedchannel.h"
26
#include <cassert>
27
#include <cstring>
28
#include <cstdio>
29
#include <cstdlib>
30
#include <climits>
31
#include <limits>
32
33
using namespace PCIDSK;
34
35
/************************************************************************/
36
/*                      CBandInterleavedChannel()                       */
37
/************************************************************************/
38
39
CBandInterleavedChannel::CBandInterleavedChannel( PCIDSKBuffer &image_header,
40
                                                  uint64 ih_offsetIn,
41
                                                  CPL_UNUSED PCIDSKBuffer &file_header,
42
                                                  int channelnum,
43
                                                  CPCIDSKFile *fileIn,
44
                                                  uint64 image_offset,
45
                                                  eChanType pixel_typeIn )
46
795k
        : CPCIDSKChannel( image_header, ih_offsetIn, fileIn, pixel_typeIn, channelnum)
47
48
795k
{
49
795k
    io_handle_p = nullptr;
50
795k
    io_mutex_p = nullptr;
51
52
/* -------------------------------------------------------------------- */
53
/*      Establish the data layout.                                      */
54
/* -------------------------------------------------------------------- */
55
795k
    if( strcmp(file->GetInterleaving().c_str(),"FILE") == 0 )
56
4
    {
57
4
        start_byte = atouint64(image_header.Get( 168, 16 ));
58
4
        pixel_offset = atouint64(image_header.Get( 184, 8 ));
59
4
        line_offset = atouint64(image_header.Get( 192, 8 ));
60
4
    }
61
795k
    else
62
795k
    {
63
795k
        start_byte = image_offset;
64
795k
        pixel_offset = DataTypeSize(pixel_type);
65
795k
        line_offset = pixel_offset * width;
66
795k
    }
67
68
/* -------------------------------------------------------------------- */
69
/*      Establish the file we will be accessing.                        */
70
/* -------------------------------------------------------------------- */
71
795k
    image_header.Get(64,64,filename);
72
73
795k
    filename = MassageLink( filename );
74
75
795k
    if( filename.length() == 0 )
76
13.1k
        file->GetIODetails( &io_handle_p, &io_mutex_p );
77
78
782k
    else
79
782k
        filename = file->GetInterfaces()->MergeRelativePath( file->GetInterfaces()->io,
80
782k
                                     file->GetFilename(),
81
782k
                                     filename );
82
795k
}
83
84
/************************************************************************/
85
/*                      ~CBandInterleavedChannel()                      */
86
/************************************************************************/
87
88
CBandInterleavedChannel::~CBandInterleavedChannel()
89
90
795k
{
91
795k
}
92
93
/************************************************************************/
94
/*                             ReadBlock()                              */
95
/************************************************************************/
96
97
int CBandInterleavedChannel::ReadBlock( int block_index, void *buffer,
98
                                        int xoff, int yoff,
99
                                        int xsize, int ysize )
100
101
3.28k
{
102
3.28k
    PCIDSKInterfaces *interfaces = file->GetInterfaces();
103
104
/* -------------------------------------------------------------------- */
105
/*      Check if we are reading from a valid channel.                   */
106
/* -------------------------------------------------------------------- */
107
3.28k
    if (line_offset > std::numeric_limits<uint64>::max() / height)
108
0
    {
109
0
        return ThrowPCIDSKException(0, "Invalid line_offset: " PCIDSK_FRMT_UINT64,
110
0
                                    line_offset);
111
0
    }
112
113
3.28k
    if (start_byte > std::numeric_limits<uint64>::max() - line_offset * height)
114
0
    {
115
0
        return ThrowPCIDSKException(0, "Invalid start_byte: " PCIDSK_FRMT_UINT64,
116
0
                                    start_byte);
117
0
    }
118
119
/* -------------------------------------------------------------------- */
120
/*      Default window if needed.                                       */
121
/* -------------------------------------------------------------------- */
122
3.28k
    if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 )
123
3.28k
    {
124
3.28k
        xoff = 0;
125
3.28k
        yoff = 0;
126
3.28k
        xsize = GetBlockWidth();
127
3.28k
        ysize = GetBlockHeight();
128
3.28k
    }
129
130
/* -------------------------------------------------------------------- */
131
/*      Validate Window                                                 */
132
/* -------------------------------------------------------------------- */
133
3.28k
    if( xoff < 0 || xoff + xsize > GetBlockWidth()
134
3.28k
        || yoff < 0 || yoff + ysize > GetBlockHeight() )
135
0
    {
136
0
        return ThrowPCIDSKException( 0,
137
0
            "Invalid window in ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d",
138
0
            xoff, yoff, xsize, ysize );
139
0
    }
140
141
/* -------------------------------------------------------------------- */
142
/*      Establish region to read.                                       */
143
/* -------------------------------------------------------------------- */
144
3.28k
    int    pixel_size = DataTypeSize( pixel_type );
145
146
3.28k
    if (pixel_offset == 0 || pixel_size == 0)
147
1
    {
148
1
        return ThrowPCIDSKException( 0, "Invalid data type." );
149
1
    }
150
3.28k
    if( xsize > 1 && pixel_offset > static_cast<uint64>(INT_MAX / (xsize - 1)) )
151
0
    {
152
0
        return ThrowPCIDSKException( 0, "Int overflow in ReadBlock() ");
153
0
    }
154
3.28k
    if( pixel_offset*(xsize-1) > static_cast<uint64>(INT_MAX - pixel_size) )
155
0
    {
156
0
        return ThrowPCIDSKException( 0, "Int overflow in ReadBlock() ");
157
0
    }
158
3.28k
    int    window_size = (int) (pixel_offset*(xsize-1) + pixel_size);
159
160
/* -------------------------------------------------------------------- */
161
/*      Get file access handles if we don't already have them.          */
162
/* -------------------------------------------------------------------- */
163
3.28k
    if( io_handle_p == nullptr )
164
721
        file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(),
165
721
                            file->GetUpdatable() );
166
167
/* -------------------------------------------------------------------- */
168
/*      If the imagery is packed, we can read directly into the         */
169
/*      target buffer.                                                  */
170
/* -------------------------------------------------------------------- */
171
3.28k
    uint64 offset = start_byte + line_offset * block_index
172
3.28k
        + pixel_offset * xoff;
173
174
3.28k
    if( pixel_size == (int) pixel_offset )
175
2.70k
    {
176
2.70k
        MutexHolder holder( *io_mutex_p );
177
178
2.70k
        interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
179
2.70k
        interfaces->io->Read( buffer, 1, window_size, *io_handle_p );
180
2.70k
    }
181
182
/* -------------------------------------------------------------------- */
183
/*      Otherwise we allocate a working buffer that holds the whole     */
184
/*      line, read into that, and pick out our data of interest.        */
185
/* -------------------------------------------------------------------- */
186
588
    else
187
588
    {
188
588
        int  i;
189
588
        PCIDSKBuffer line_from_disk( window_size );
190
588
        char *this_pixel;
191
192
588
        MutexHolder holder( *io_mutex_p );
193
194
588
        interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
195
588
        interfaces->io->Read( line_from_disk.buffer,
196
588
                              1, line_from_disk.buffer_size,
197
588
                              *io_handle_p );
198
199
588
        for( i = 0, this_pixel = line_from_disk.buffer; i < xsize; i++ )
200
0
        {
201
0
            memcpy( ((char *) buffer) + pixel_size * i,
202
0
                    this_pixel, pixel_size );
203
0
            this_pixel += pixel_offset;
204
0
        }
205
588
    }
206
207
/* -------------------------------------------------------------------- */
208
/*      Do byte swapping if needed.                                     */
209
/* -------------------------------------------------------------------- */
210
3.28k
    if( needs_swap )
211
18
        SwapPixels( buffer, pixel_type, xsize );
212
213
3.28k
    return 1;
214
3.28k
}
215
216
/************************************************************************/
217
/*                             WriteBlock()                             */
218
/************************************************************************/
219
220
int CBandInterleavedChannel::WriteBlock( int block_index, void *buffer )
221
222
0
{
223
0
    PCIDSKInterfaces *interfaces = file->GetInterfaces();
224
225
/* -------------------------------------------------------------------- */
226
/*      Check if we are writing to a valid channel.                     */
227
/* -------------------------------------------------------------------- */
228
0
    if (line_offset > std::numeric_limits<uint64>::max() / height)
229
0
    {
230
0
        return ThrowPCIDSKException(0, "Invalid line_offset: " PCIDSK_FRMT_UINT64,
231
0
                                    line_offset);
232
0
    }
233
234
0
    if (pixel_offset > line_offset)
235
0
    {
236
0
        return ThrowPCIDSKException(0, "Invalid pixel_offset: " PCIDSK_FRMT_UINT64,
237
0
                                    pixel_offset);
238
0
    }
239
240
0
    if (start_byte > std::numeric_limits<uint64>::max() - line_offset * height)
241
0
    {
242
0
        return ThrowPCIDSKException(0, "Invalid start_byte: " PCIDSK_FRMT_UINT64,
243
0
                                    start_byte);
244
0
    }
245
246
0
    if( !file->GetUpdatable() )
247
0
        return ThrowPCIDSKException(0, "File not open for update in WriteBlock()" );
248
249
0
    InvalidateOverviews();
250
251
/* -------------------------------------------------------------------- */
252
/*      Establish region to read.                                       */
253
/* -------------------------------------------------------------------- */
254
0
    int    pixel_size = DataTypeSize( pixel_type );
255
256
0
    if (pixel_offset == 0 || pixel_size == 0)
257
0
        return ThrowPCIDSKException( 0, "Invalid data type." );
258
259
0
    uint64 offset = start_byte + line_offset * block_index;
260
0
    int    window_size = (int) (pixel_offset*(width-1) + pixel_size);
261
262
/* -------------------------------------------------------------------- */
263
/*      Get file access handles if we don't already have them.          */
264
/* -------------------------------------------------------------------- */
265
0
    if( io_handle_p == nullptr )
266
0
        file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(),
267
0
                            file->GetUpdatable() );
268
269
/* -------------------------------------------------------------------- */
270
/*      If the imagery is packed, we can read directly into the         */
271
/*      target buffer.                                                  */
272
/* -------------------------------------------------------------------- */
273
0
    if( pixel_size == (int) pixel_offset )
274
0
    {
275
0
        MutexHolder holder( *io_mutex_p );
276
277
0
        if( needs_swap ) // swap before write.
278
0
            SwapPixels( buffer, pixel_type, width );
279
280
0
        interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
281
0
        interfaces->io->Write( buffer, 1, window_size, *io_handle_p );
282
283
0
        if( needs_swap ) // restore to original order.
284
0
            SwapPixels( buffer, pixel_type, width );
285
0
    }
286
287
/* -------------------------------------------------------------------- */
288
/*      Otherwise we allocate a working buffer that holds the whole     */
289
/*      line, read into that, and pick out our data of interest.        */
290
/* -------------------------------------------------------------------- */
291
0
    else
292
0
    {
293
0
        int  i;
294
0
        PCIDSKBuffer line_from_disk( window_size );
295
0
        char *this_pixel;
296
297
0
        MutexHolder holder( *io_mutex_p );
298
299
0
        interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
300
0
        interfaces->io->Read( buffer, 1, line_from_disk.buffer_size,
301
0
                              *io_handle_p );
302
303
0
        for( i = 0, this_pixel = line_from_disk.buffer; i < width; i++ )
304
0
        {
305
0
            memcpy( this_pixel, ((char *) buffer) + pixel_size * i,
306
0
                    pixel_size );
307
308
0
            if( needs_swap ) // swap before write.
309
0
                SwapPixels( this_pixel, pixel_type, 1 );
310
311
0
            this_pixel += pixel_size;
312
0
        }
313
314
0
        interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
315
0
        interfaces->io->Write( buffer, 1, line_from_disk.buffer_size,
316
0
                               *io_handle_p );
317
0
    }
318
319
/* -------------------------------------------------------------------- */
320
/*      Do byte swapping if needed.                                     */
321
/* -------------------------------------------------------------------- */
322
323
0
    return 1;
324
0
}
325
326
/************************************************************************/
327
/*                            GetChanInfo()                             */
328
/************************************************************************/
329
void CBandInterleavedChannel
330
::GetChanInfo( std::string &filename_ret, uint64 &image_offset,
331
               uint64 &pixel_offsetOut, uint64 &line_offsetOut,
332
               bool &little_endian ) const
333
334
141k
{
335
141k
    image_offset = start_byte;
336
141k
    pixel_offsetOut = this->pixel_offset;
337
141k
    line_offsetOut = this->line_offset;
338
141k
    little_endian = (byte_order == 'S');
339
340
/* -------------------------------------------------------------------- */
341
/*      We fetch the filename from the header since it will be the      */
342
/*      "clean" version without any paths.                              */
343
/* -------------------------------------------------------------------- */
344
141k
    PCIDSKBuffer ih(64);
345
141k
    file->ReadFromFile( ih.buffer, ih_offset+64, 64 );
346
347
141k
    ih.Get(0,64,filename_ret);
348
141k
    filename_ret = MassageLink( filename_ret );
349
141k
}
350
351
/************************************************************************/
352
/*                            SetChanInfo()                             */
353
/************************************************************************/
354
355
void CBandInterleavedChannel
356
::SetChanInfo( std::string filenameIn, uint64 image_offset,
357
               uint64 pixel_offsetIn, uint64 line_offsetIn,
358
               bool little_endian )
359
360
0
{
361
0
    if( ih_offset == 0 )
362
0
        return ThrowPCIDSKException( "No Image Header available for this channel." );
363
364
/* -------------------------------------------------------------------- */
365
/*      Fetch the existing image header.                                */
366
/* -------------------------------------------------------------------- */
367
0
    PCIDSKBuffer ih(1024);
368
369
0
    file->ReadFromFile( ih.buffer, ih_offset, 1024 );
370
371
/* -------------------------------------------------------------------- */
372
/*      If the linked filename is too long to fit in the 64             */
373
/*      character IHi.2 field, then we need to use a link segment to    */
374
/*      store the filename.                                             */
375
/* -------------------------------------------------------------------- */
376
0
    std::string IHi2_filename;
377
378
0
    if( filenameIn.size() > 64 )
379
0
    {
380
0
        int link_segment;
381
382
0
        ih.Get( 64, 64, IHi2_filename );
383
384
0
        if( IHi2_filename.substr(0,3) == "LNK" )
385
0
        {
386
0
            link_segment = std::atoi( IHi2_filename.c_str() + 4 );
387
0
        }
388
0
        else
389
0
        {
390
0
            char link_filename[64];
391
392
0
            link_segment =
393
0
                file->CreateSegment( "Link    ",
394
0
                                     "Long external channel filename link.",
395
0
                                     SEG_SYS, 1 );
396
397
0
            snprintf( link_filename, sizeof(link_filename), "LNK %4d", link_segment );
398
0
            IHi2_filename = link_filename;
399
0
        }
400
401
0
        CLinkSegment *link =
402
0
            dynamic_cast<CLinkSegment*>( file->GetSegment( link_segment ) );
403
404
0
        if( link != nullptr )
405
0
        {
406
0
            link->SetPath( filenameIn );
407
0
            link->Synchronize();
408
0
        }
409
0
    }
410
411
/* -------------------------------------------------------------------- */
412
/*      If we used to have a link segment but no longer need it, we     */
413
/*      need to delete the link segment.                                */
414
/* -------------------------------------------------------------------- */
415
0
    else
416
0
    {
417
0
        ih.Get( 64, 64, IHi2_filename );
418
419
0
        if( IHi2_filename.substr(0,3) == "LNK" )
420
0
        {
421
0
            int link_segment = std::atoi( IHi2_filename.c_str() + 4 );
422
423
0
            file->DeleteSegment( link_segment );
424
0
        }
425
426
0
        IHi2_filename = filenameIn;
427
0
    }
428
429
/* -------------------------------------------------------------------- */
430
/*      Update the image header.                                        */
431
/* -------------------------------------------------------------------- */
432
    // IHi.2
433
0
    ih.Put( IHi2_filename.c_str(), 64, 64 );
434
435
    // IHi.6.1
436
0
    ih.Put( image_offset, 168, 16 );
437
438
    // IHi.6.2
439
0
    ih.Put( pixel_offsetIn, 184, 8 );
440
441
    // IHi.6.3
442
0
    ih.Put( line_offsetIn, 192, 8 );
443
444
    // IHi.6.5
445
0
    if( little_endian )
446
0
        ih.Put( "S", 201, 1 );
447
0
    else
448
0
        ih.Put( "N", 201, 1 );
449
450
0
    file->WriteToFile( ih.buffer, ih_offset, 1024 );
451
452
/* -------------------------------------------------------------------- */
453
/*      Update local configuration.                                     */
454
/* -------------------------------------------------------------------- */
455
0
    this->filename = file->GetInterfaces()->MergeRelativePath( file->GetInterfaces()->io,
456
0
                                        file->GetFilename(),
457
0
                                        filenameIn );
458
459
0
    start_byte = image_offset;
460
0
    this->pixel_offset = pixel_offsetIn;
461
0
    this->line_offset = line_offsetIn;
462
463
0
    if( little_endian )
464
0
        byte_order = 'S';
465
0
    else
466
0
        byte_order = 'N';
467
468
/* -------------------------------------------------------------------- */
469
/*      Determine if we need byte swapping.                             */
470
/* -------------------------------------------------------------------- */
471
0
    unsigned short test_value = 1;
472
473
0
    if( (reinterpret_cast<uint8 *>(&test_value))[0] == 1 )
474
0
        needs_swap = (byte_order != 'S');
475
0
    else
476
0
        needs_swap = (byte_order == 'S');
477
478
0
    if( pixel_type == CHN_8U )
479
0
        needs_swap = 0;
480
0
}
481
482
/************************************************************************/
483
/*                            MassageLink()                             */
484
/*                                                                      */
485
/*      Return the filename after applying translation of long          */
486
/*      linked filenames using a link segment.                          */
487
/************************************************************************/
488
489
std::string CBandInterleavedChannel::MassageLink( std::string filename_in ) const
490
491
937k
{
492
937k
    if (filename_in.find("LNK") == 0)
493
0
    {
494
0
        std::string seg_str(filename_in, 4, 4);
495
0
        unsigned int seg_num = std::atoi(seg_str.c_str());
496
497
0
        if (seg_num == 0)
498
0
        {
499
0
            ThrowPCIDSKException("Unable to find link segment. Link name: %s",
500
0
                                  filename_in.c_str());
501
0
            return "";
502
0
        }
503
504
0
        CLinkSegment* link_seg =
505
0
            dynamic_cast<CLinkSegment*>(file->GetSegment(seg_num));
506
0
        if (link_seg == nullptr)
507
0
        {
508
0
            ThrowPCIDSKException("Failed to get Link Information Segment.");
509
0
            return "";
510
0
        }
511
512
0
        filename_in = link_seg->GetPath();
513
0
    }
514
515
937k
    return filename_in;
516
937k
}