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