Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKSegment class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "segment/cpcidsksegment.h"
13 : #include "core/metadataset.h"
14 : #include "core/cpcidskfile.h"
15 : #include "core/pcidsk_utils.h"
16 : #include "pcidsk_buffer.h"
17 : #include "pcidsk_exception.h"
18 : #include <cassert>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <limits>
22 : #include <vector>
23 : #include <string>
24 :
25 : using namespace PCIDSK;
26 :
27 : PCIDSKSegment::~PCIDSKSegment() = default;
28 :
29 : /************************************************************************/
30 : /* PCIDSKSegment() */
31 : /************************************************************************/
32 :
33 1466 : CPCIDSKSegment::CPCIDSKSegment( PCIDSKFile *fileIn, int segmentIn,
34 1466 : const char *segment_pointer )
35 :
36 : {
37 1466 : this->file = fileIn;
38 1466 : this->segment = segmentIn;
39 :
40 1466 : LoadSegmentPointer( segment_pointer );
41 1466 : LoadSegmentHeader(); // eventually we might want to defer this.
42 :
43 : /* -------------------------------------------------------------------- */
44 : /* Initialize the metadata object, but do not try to load till */
45 : /* needed. */
46 : /* -------------------------------------------------------------------- */
47 1466 : metadata = new MetadataSet;
48 1466 : metadata->Initialize( file, SegmentTypeName(segment_type), segment );
49 1466 : }
50 :
51 : /************************************************************************/
52 : /* ~PCIDSKSegment() */
53 : /************************************************************************/
54 :
55 1494 : CPCIDSKSegment::~CPCIDSKSegment()
56 :
57 : {
58 1466 : delete metadata;
59 1494 : }
60 :
61 : /************************************************************************/
62 : /* GetMetadataValue() */
63 : /************************************************************************/
64 1183 : std::string CPCIDSKSegment::GetMetadataValue( const std::string &key ) const
65 : {
66 1183 : return metadata->GetMetadataValue(key);
67 : }
68 :
69 : /************************************************************************/
70 : /* SetMetadataValue() */
71 : /************************************************************************/
72 33 : void CPCIDSKSegment::SetMetadataValue( const std::string &key, const std::string &value )
73 : {
74 33 : metadata->SetMetadataValue(key,value);
75 33 : }
76 :
77 : /************************************************************************/
78 : /* GetMetadataKeys() */
79 : /************************************************************************/
80 1 : std::vector<std::string> CPCIDSKSegment::GetMetadataKeys() const
81 : {
82 1 : return metadata->GetMetadataKeys();
83 : }
84 :
85 : /************************************************************************/
86 : /* LoadSegmentPointer() */
87 : /************************************************************************/
88 :
89 2651 : void CPCIDSKSegment::LoadSegmentPointer( const char *segment_pointer )
90 :
91 : {
92 2651 : PCIDSKBuffer segptr( segment_pointer, 32 );
93 :
94 2651 : segment_flag = segptr.buffer[0];
95 2651 : const int segment_type_int = atoi(segptr.Get(1,3));
96 2651 : segment_type = EQUAL(SegmentTypeName(segment_type_int), "UNKNOWN") ?
97 : SEG_UNKNOWN : static_cast<eSegType>(segment_type_int);
98 2651 : data_offset = atouint64(segptr.Get(12,11));
99 2651 : if( data_offset == 0 )
100 0 : data_offset = 0; // throw exception maybe ?
101 : else
102 : {
103 2651 : if( data_offset-1 > std::numeric_limits<uint64>::max() / 512 )
104 : {
105 0 : return ThrowPCIDSKException("too large data_offset");
106 : }
107 2651 : data_offset = (data_offset-1) * 512;
108 : }
109 2651 : data_size = atouint64(segptr.Get(23,9));
110 2651 : data_size_limit = 999999999ULL * 512;
111 :
112 2651 : if( data_size > 999999999ULL )
113 : {
114 0 : return ThrowPCIDSKException("too large data_size");
115 : }
116 2651 : data_size *= 512;
117 :
118 2651 : segptr.Get(4,8,segment_name);
119 : }
120 :
121 : /************************************************************************/
122 : /* LoadSegmentHeader() */
123 : /************************************************************************/
124 : #include <iostream>
125 1466 : void CPCIDSKSegment::LoadSegmentHeader()
126 :
127 : {
128 1466 : header.SetSize(1024);
129 :
130 1466 : file->ReadFromFile( header.buffer, data_offset, 1024 );
131 :
132 : // Read the history from the segment header. PCIDSK supports
133 : // 8 history entries per segment.
134 2932 : std::string hist_msg;
135 1466 : history_.clear();
136 13194 : for (unsigned int i = 0; i < 8; i++)
137 : {
138 11728 : header.Get(384 + i * 80, 80, hist_msg);
139 :
140 : // Some programs seem to push history records with a trailing '\0'
141 : // so do some extra processing to cleanup. FUN records on segment
142 : // 3 of eltoro.pix are an example of this.
143 11728 : size_t size = hist_msg.size();
144 0 : while( size > 0
145 11728 : && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
146 0 : size--;
147 :
148 11728 : hist_msg.resize(size);
149 :
150 11728 : history_.push_back(hist_msg);
151 : }
152 1466 : }
153 :
154 : /************************************************************************/
155 : /* FlushHeader() */
156 : /* */
157 : /* This is used primarily after this class or subclasses have */
158 : /* modified the header buffer and need it pushed back to disk. */
159 : /************************************************************************/
160 :
161 1145 : void CPCIDSKSegment::FlushHeader()
162 :
163 : {
164 1145 : file->WriteToFile( header.buffer, data_offset, 1024 );
165 1145 : }
166 :
167 : /************************************************************************/
168 : /* ReadFromFile() */
169 : /************************************************************************/
170 :
171 2020 : void CPCIDSKSegment::ReadFromFile( void *buffer, uint64 offset, uint64 size )
172 :
173 : {
174 2020 : if( offset+size+1024 > data_size )
175 0 : return ThrowPCIDSKException("Attempt to read past end of segment %d: "
176 : "Segment Size: " PCIDSK_FRMT_UINT64 ", "
177 : "Read Offset: " PCIDSK_FRMT_UINT64 ", "
178 : "Read Size: " PCIDSK_FRMT_UINT64,
179 0 : segment, data_size, offset, size);
180 :
181 2020 : file->ReadFromFile( buffer, offset + data_offset + 1024, size );
182 : }
183 :
184 : /************************************************************************/
185 : /* WriteToFile() */
186 : /************************************************************************/
187 :
188 2064 : void CPCIDSKSegment::WriteToFile( const void *buffer, uint64 offset, uint64 size )
189 : {
190 2064 : if( offset+size > data_size-1024 )
191 : {
192 1168 : CPCIDSKFile *poFile = dynamic_cast<CPCIDSKFile *>(file);
193 :
194 1168 : if (poFile == nullptr) {
195 0 : return ThrowPCIDSKException("Attempt to dynamic_cast the file interface "
196 : "to a CPCIDSKFile failed. This is a programmer error, and should "
197 0 : "be reported to your software provider.");
198 : }
199 :
200 1168 : uint64 blocks_to_add =
201 1168 : ((offset+size+511) - (data_size - 1024)) / 512;
202 :
203 : // prezero if we aren't directly writing all the new blocks.
204 1168 : poFile->ExtendSegment( segment, blocks_to_add,
205 2336 : !(offset == data_size - 1024
206 1168 : && size == blocks_to_add * 512) );
207 : // ExtendSegment() will call LoadSegmentPointer() to update data_size.
208 : }
209 :
210 2064 : assert(file); // avoid CLang Static Analyzer false positive
211 2064 : file->WriteToFile( buffer, offset + data_offset + 1024, size );
212 : }
213 :
214 : /************************************************************************/
215 : /* GetDescription() */
216 : /************************************************************************/
217 :
218 0 : std::string CPCIDSKSegment::GetDescription()
219 : {
220 0 : std::string target;
221 :
222 0 : header.Get( 0, 64, target );
223 :
224 0 : return target;
225 : }
226 :
227 : /************************************************************************/
228 : /* SetDescription() */
229 : /************************************************************************/
230 :
231 0 : void CPCIDSKSegment::SetDescription( const std::string &description )
232 : {
233 0 : header.Put( description.c_str(), 0, 64);
234 :
235 0 : file->WriteToFile( header.buffer, data_offset, 1024 );
236 0 : }
237 :
238 : /************************************************************************/
239 : /* IsAtEOF() */
240 : /************************************************************************/
241 :
242 1176 : bool CPCIDSKSegment::IsAtEOF()
243 : {
244 1176 : return data_offset + data_size == file->GetFileSize() * 512;
245 : }
246 :
247 : /************************************************************************/
248 : /* CanExtend() */
249 : /************************************************************************/
250 0 : bool CPCIDSKSegment::CanExtend(uint64 size) const
251 : {
252 0 : return data_size + size <= data_size_limit;
253 : }
254 :
255 : /************************************************************************/
256 : /* GetHistoryEntries() */
257 : /************************************************************************/
258 :
259 0 : std::vector<std::string> CPCIDSKSegment::GetHistoryEntries() const
260 : {
261 0 : return history_;
262 : }
263 :
264 : /************************************************************************/
265 : /* SetHistoryEntries() */
266 : /************************************************************************/
267 :
268 0 : void CPCIDSKSegment::SetHistoryEntries(const std::vector<std::string> &entries)
269 :
270 : {
271 0 : for( unsigned int i = 0; i < 8; i++ )
272 : {
273 0 : const char *msg = "";
274 0 : if( entries.size() > i )
275 0 : msg = entries[i].c_str();
276 :
277 0 : header.Put( msg, 384 + i * 80, 80 );
278 : }
279 :
280 0 : FlushHeader();
281 :
282 : // Force reloading of history_
283 0 : LoadSegmentHeader();
284 0 : }
285 :
286 : /************************************************************************/
287 : /* PushHistory() */
288 : /************************************************************************/
289 :
290 0 : void CPCIDSKSegment::PushHistory( const std::string &app,
291 : const std::string &message )
292 :
293 : {
294 : #define MY_MIN(a,b) ((a<b) ? a : b)
295 :
296 : char current_time[17];
297 : char history[81];
298 :
299 0 : GetCurrentDateTime( current_time );
300 :
301 0 : memset( history, ' ', 80 );
302 0 : history[80] = '\0';
303 :
304 0 : memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
305 0 : history[7] = ':';
306 :
307 0 : memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
308 0 : memcpy( history + 64, current_time, 16 );
309 :
310 0 : std::vector<std::string> history_entries = GetHistoryEntries();
311 :
312 : // coverity[string_null]
313 0 : history_entries.insert( history_entries.begin(), history );
314 0 : history_entries.resize(8);
315 :
316 0 : SetHistoryEntries( history_entries );
317 0 : }
318 :
319 :
320 : /************************************************************************/
321 : /* MoveData() */
322 : /* */
323 : /* Move a chunk of data within a segment. Overlapping source */
324 : /* and destination are permitted. */
325 : /************************************************************************/
326 :
327 160 : void CPCIDSKSegment::MoveData( uint64 src_offset, uint64 dst_offset,
328 : uint64 size_in_bytes )
329 :
330 : {
331 160 : bool copy_backwards = false;
332 :
333 : // We move things backwards if the areas overlap and the destination
334 : // is further on in the segment.
335 160 : if( dst_offset > src_offset
336 160 : && src_offset + size_in_bytes > dst_offset )
337 45 : copy_backwards = true;
338 :
339 :
340 : // Move the segment data to the new location.
341 : uint8 copy_buf[16384];
342 : uint64 bytes_to_go;
343 :
344 160 : bytes_to_go = size_in_bytes;
345 :
346 320 : while( bytes_to_go > 0 )
347 : {
348 160 : uint64 bytes_this_chunk = sizeof(copy_buf);
349 160 : if( bytes_to_go < bytes_this_chunk )
350 160 : bytes_this_chunk = bytes_to_go;
351 :
352 160 : if( copy_backwards )
353 : {
354 45 : ReadFromFile( copy_buf,
355 45 : src_offset + bytes_to_go - bytes_this_chunk,
356 45 : bytes_this_chunk );
357 45 : WriteToFile( copy_buf,
358 45 : dst_offset + bytes_to_go - bytes_this_chunk,
359 45 : bytes_this_chunk );
360 : }
361 : else
362 : {
363 115 : ReadFromFile( copy_buf, src_offset, bytes_this_chunk );
364 115 : WriteToFile( copy_buf, dst_offset, bytes_this_chunk );
365 :
366 115 : src_offset += bytes_this_chunk;
367 115 : dst_offset += bytes_this_chunk;
368 : }
369 :
370 160 : bytes_to_go -= bytes_this_chunk;
371 : }
372 160 : }
|