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