Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the VecSegIndex class.
4 : *
5 : * This class is used to manage a vector segment data block index. There
6 : * will be two instances created, one for the record data (sec_record) and
7 : * one for the vertices (sec_vert). This class is exclusively a private
8 : * helper class for VecSegHeader.
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2010
12 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : ****************************************************************************/
32 :
33 : #include "pcidsk.h"
34 : #include "core/pcidsk_utils.h"
35 : #include "segment/cpcidskvectorsegment.h"
36 : #include <cassert>
37 : #include <cstring>
38 : #include <cstdio>
39 : #include <limits>
40 :
41 : using namespace PCIDSK;
42 :
43 : /* -------------------------------------------------------------------- */
44 : /* Size of a block in the record/vertex block tables. This is */
45 : /* determined by the PCIDSK format and may not be changed. */
46 : /* -------------------------------------------------------------------- */
47 : static const int block_page_size = 8192;
48 :
49 : /************************************************************************/
50 : /* VecSegDataIndex() */
51 : /************************************************************************/
52 :
53 2370 : VecSegDataIndex::VecSegDataIndex()
54 :
55 : {
56 2370 : block_initialized = false;
57 2370 : vs = nullptr;
58 2370 : dirty = false;
59 2370 : section = 0;
60 2370 : offset_on_disk_within_section = 0;
61 2370 : size_on_disk = 0;
62 2370 : block_count = 0;
63 2370 : bytes = 0;
64 2370 : }
65 :
66 : /************************************************************************/
67 : /* ~VecSegDataIndex() */
68 : /************************************************************************/
69 :
70 2370 : VecSegDataIndex::~VecSegDataIndex()
71 :
72 : {
73 2370 : }
74 :
75 : /************************************************************************/
76 : /* Initialize() */
77 : /************************************************************************/
78 :
79 2366 : void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vsIn, int sectionIn )
80 :
81 : {
82 2366 : this->section = sectionIn;
83 2366 : this->vs = vsIn;
84 :
85 2366 : if( section == sec_vert )
86 1183 : offset_on_disk_within_section = 0;
87 : else
88 1183 : offset_on_disk_within_section = vs->di[sec_vert].SerializedSize();
89 :
90 2366 : uint32 offset = offset_on_disk_within_section
91 2366 : + vs->vh.section_offsets[hsec_shape];
92 :
93 2366 : memcpy( &block_count, vs->GetData(sec_raw,offset,nullptr,4), 4);
94 2366 : memcpy( &bytes, vs->GetData(sec_raw,offset+4,nullptr,4), 4);
95 :
96 2366 : bool needs_swap = !BigEndianSystem();
97 :
98 2366 : if( needs_swap )
99 : {
100 2366 : SwapData( &block_count, 4, 1 );
101 2366 : SwapData( &bytes, 4, 1 );
102 : }
103 :
104 2366 : if( block_count > (std::numeric_limits<uint32>::max() - 8) /4 )
105 : {
106 0 : throw PCIDSKException("Invalid block_count: %u", block_count);
107 : }
108 :
109 2366 : size_on_disk = block_count * 4 + 8;
110 2366 : }
111 :
112 : /************************************************************************/
113 : /* SerializedSize() */
114 : /************************************************************************/
115 :
116 3629 : uint32 VecSegDataIndex::SerializedSize()
117 :
118 : {
119 3629 : return 8 + 4 * block_count;
120 : }
121 :
122 : /************************************************************************/
123 : /* GetBlockIndex() */
124 : /************************************************************************/
125 :
126 508 : const std::vector<uint32> *VecSegDataIndex::GetIndex()
127 :
128 : {
129 : /* -------------------------------------------------------------------- */
130 : /* Load block map if needed. */
131 : /* -------------------------------------------------------------------- */
132 508 : if( !block_initialized )
133 : {
134 94 : bool needs_swap = !BigEndianSystem();
135 :
136 : try
137 : {
138 94 : block_index.resize( block_count );
139 : }
140 0 : catch( const std::exception& ex )
141 : {
142 : throw PCIDSKException("Out of memory allocating block_index(%u): %s",
143 0 : block_count, ex.what());
144 : }
145 94 : if( block_count > 0 )
146 : {
147 14 : vs->ReadFromFile( &(block_index[0]),
148 14 : offset_on_disk_within_section
149 14 : + vs->vh.section_offsets[hsec_shape] + 8,
150 14 : 4 * block_count );
151 :
152 14 : if( needs_swap )
153 14 : SwapData( &(block_index[0]), 4, block_count );
154 : }
155 :
156 94 : block_initialized = true;
157 : }
158 :
159 508 : return &block_index;
160 : }
161 :
162 : /************************************************************************/
163 : /* Flush() */
164 : /************************************************************************/
165 :
166 6742 : void VecSegDataIndex::Flush()
167 :
168 : {
169 6742 : if( !dirty )
170 6662 : return;
171 :
172 80 : GetIndex(); // force loading if not already loaded!
173 :
174 80 : PCIDSKBuffer wbuf( SerializedSize() );
175 :
176 80 : memcpy( wbuf.buffer + 0, &block_count, 4 );
177 80 : memcpy( wbuf.buffer + 4, &bytes, 4 );
178 80 : memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count );
179 :
180 80 : bool needs_swap = !BigEndianSystem();
181 :
182 80 : if( needs_swap )
183 80 : SwapData( wbuf.buffer, 4, block_count+2 );
184 :
185 : // Make sure this section of the header is large enough.
186 80 : int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk;
187 :
188 80 : if( shift != 0 )
189 : {
190 80 : uint32 old_section_size = vs->vh.section_sizes[hsec_shape];
191 :
192 : // fprintf( stderr, "Shifting section %d by %d bytes.\n",
193 : // section, shift );
194 :
195 80 : vs->vh.GrowSection( hsec_shape, old_section_size + shift );
196 :
197 80 : if( section == sec_vert )
198 : {
199 : // move record block index and shape index.
200 45 : vs->MoveData( vs->vh.section_offsets[hsec_shape]
201 45 : + vs->di[sec_vert].size_on_disk,
202 45 : vs->vh.section_offsets[hsec_shape]
203 45 : + vs->di[sec_vert].size_on_disk + shift,
204 45 : old_section_size - size_on_disk );
205 : }
206 : else
207 : {
208 : // only move shape index.
209 35 : vs->MoveData( vs->vh.section_offsets[hsec_shape]
210 35 : + vs->di[sec_vert].size_on_disk
211 35 : + vs->di[sec_record].size_on_disk,
212 35 : vs->vh.section_offsets[hsec_shape]
213 35 : + vs->di[sec_vert].size_on_disk
214 35 : + vs->di[sec_record].size_on_disk
215 35 : + shift,
216 : old_section_size
217 35 : - vs->di[sec_vert].size_on_disk
218 35 : - vs->di[sec_record].size_on_disk );
219 : }
220 :
221 80 : if( section == sec_vert )
222 45 : vs->di[sec_record].offset_on_disk_within_section += shift;
223 : }
224 :
225 : // Actually write to disk.
226 80 : vs->WriteToFile( wbuf.buffer,
227 80 : offset_on_disk_within_section
228 80 : + vs->vh.section_offsets[hsec_shape],
229 80 : wbuf.buffer_size );
230 :
231 80 : size_on_disk = wbuf.buffer_size;
232 80 : dirty = false;
233 : }
234 :
235 : /************************************************************************/
236 : /* GetSectionEnd() */
237 : /************************************************************************/
238 :
239 783 : uint32 VecSegDataIndex::GetSectionEnd()
240 :
241 : {
242 783 : return bytes;
243 : }
244 :
245 : /************************************************************************/
246 : /* SetSectionEnd() */
247 : /************************************************************************/
248 :
249 182 : void VecSegDataIndex::SetSectionEnd( uint32 new_end )
250 :
251 : {
252 : // should we keep track of the need to write this back to disk?
253 182 : bytes = new_end;
254 182 : }
255 :
256 : /************************************************************************/
257 : /* AddBlockToIndex() */
258 : /************************************************************************/
259 :
260 80 : void VecSegDataIndex::AddBlockToIndex( uint32 block )
261 :
262 : {
263 80 : GetIndex(); // force loading.
264 :
265 80 : block_index.push_back( block );
266 80 : block_count++;
267 80 : dirty = true;
268 80 : }
269 :
270 : /************************************************************************/
271 : /* SetDirty() */
272 : /* */
273 : /* This method is primarily used to mark the need to write the */
274 : /* index when the location changes. */
275 : /************************************************************************/
276 :
277 0 : void VecSegDataIndex::SetDirty()
278 :
279 : {
280 0 : dirty = true;
281 0 : }
282 :
283 : /************************************************************************/
284 : /* VacateBlockRange() */
285 : /* */
286 : /* Move any blocks in the indicated block range to the end of */
287 : /* the segment to make space for a growing header. */
288 : /************************************************************************/
289 :
290 0 : void VecSegDataIndex::VacateBlockRange( uint32 start, uint32 count )
291 :
292 : {
293 0 : GetIndex(); // make sure loaded.
294 :
295 : unsigned int i;
296 0 : uint32 next_block = (uint32) (vs->GetContentSize() / block_page_size);
297 :
298 0 : for( i = 0; i < block_count; i++ )
299 : {
300 0 : if( block_index[i] >= start && block_index[i] < start+count )
301 : {
302 0 : vs->MoveData( block_index[i] * block_page_size,
303 0 : next_block * block_page_size,
304 : block_page_size );
305 0 : block_index[i] = next_block;
306 0 : dirty = true;
307 0 : next_block++;
308 : }
309 : }
310 0 : }
|