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 
VecSegDataIndex()53 VecSegDataIndex::VecSegDataIndex()
54 
55 {
56     block_initialized = false;
57     vs = nullptr;
58     dirty = false;
59     section = 0;
60     offset_on_disk_within_section = 0;
61     size_on_disk = 0;
62     block_count = 0;
63     bytes = 0;
64 }
65 
66 /************************************************************************/
67 /*                          ~VecSegDataIndex()                          */
68 /************************************************************************/
69 
~VecSegDataIndex()70 VecSegDataIndex::~VecSegDataIndex()
71 
72 {
73 }
74 
75 /************************************************************************/
76 /*                             Initialize()                             */
77 /************************************************************************/
78 
Initialize(CPCIDSKVectorSegment * vsIn,int sectionIn)79 void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vsIn, int sectionIn )
80 
81 {
82     this->section = sectionIn;
83     this->vs = vsIn;
84 
85     if( section == sec_vert )
86         offset_on_disk_within_section = 0;
87     else
88         offset_on_disk_within_section = vs->di[sec_vert].SerializedSize();
89 
90     uint32 offset = offset_on_disk_within_section
91         + vs->vh.section_offsets[hsec_shape];
92 
93     memcpy( &block_count, vs->GetData(sec_raw,offset,nullptr,4), 4);
94     memcpy( &bytes, vs->GetData(sec_raw,offset+4,nullptr,4), 4);
95 
96     bool needs_swap = !BigEndianSystem();
97 
98     if( needs_swap )
99     {
100         SwapData( &block_count, 4, 1 );
101         SwapData( &bytes, 4, 1 );
102     }
103 
104     if( block_count > (std::numeric_limits<uint32>::max() - 8) /4 )
105     {
106         throw PCIDSKException("Invalid block_count: %u", block_count);
107     }
108 
109     size_on_disk = block_count * 4 + 8;
110 }
111 
112 /************************************************************************/
113 /*                           SerializedSize()                           */
114 /************************************************************************/
115 
SerializedSize()116 uint32 VecSegDataIndex::SerializedSize()
117 
118 {
119     return 8 + 4 * block_count;
120 }
121 
122 /************************************************************************/
123 /*                           GetBlockIndex()                            */
124 /************************************************************************/
125 
GetIndex()126 const std::vector<uint32> *VecSegDataIndex::GetIndex()
127 
128 {
129 /* -------------------------------------------------------------------- */
130 /*      Load block map if needed.                                       */
131 /* -------------------------------------------------------------------- */
132     if( !block_initialized )
133     {
134         bool needs_swap = !BigEndianSystem();
135 
136         try
137         {
138             block_index.resize( block_count );
139         }
140         catch( const std::exception& ex )
141         {
142             throw PCIDSKException("Out of memory allocating block_index(%u): %s",
143                                   block_count, ex.what());
144         }
145         if( block_count > 0 )
146         {
147             vs->ReadFromFile( &(block_index[0]),
148                               offset_on_disk_within_section
149                               + vs->vh.section_offsets[hsec_shape] + 8,
150                               4 * block_count );
151 
152             if( needs_swap )
153                 SwapData( &(block_index[0]), 4, block_count );
154         }
155 
156         block_initialized = true;
157     }
158 
159     return &block_index;
160 }
161 
162 /************************************************************************/
163 /*                               Flush()                                */
164 /************************************************************************/
165 
Flush()166 void VecSegDataIndex::Flush()
167 
168 {
169     if( !dirty )
170         return;
171 
172     GetIndex(); // force loading if not already loaded!
173 
174     PCIDSKBuffer wbuf( SerializedSize() );
175 
176     memcpy( wbuf.buffer + 0, &block_count, 4 );
177     memcpy( wbuf.buffer + 4, &bytes, 4 );
178     memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count );
179 
180     bool needs_swap = !BigEndianSystem();
181 
182     if( needs_swap )
183         SwapData( wbuf.buffer, 4, block_count+2 );
184 
185     // Make sure this section of the header is large enough.
186     int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk;
187 
188     if( shift != 0 )
189     {
190         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         vs->vh.GrowSection( hsec_shape, old_section_size + shift );
196 
197         if( section == sec_vert )
198         {
199             // move record block index and shape index.
200             vs->MoveData( vs->vh.section_offsets[hsec_shape]
201                           + vs->di[sec_vert].size_on_disk,
202                           vs->vh.section_offsets[hsec_shape]
203                           + vs->di[sec_vert].size_on_disk + shift,
204                           old_section_size - size_on_disk );
205         }
206         else
207         {
208             // only move shape index.
209             vs->MoveData( vs->vh.section_offsets[hsec_shape]
210                           + vs->di[sec_vert].size_on_disk
211                           + vs->di[sec_record].size_on_disk,
212                           vs->vh.section_offsets[hsec_shape]
213                           + vs->di[sec_vert].size_on_disk
214                           + vs->di[sec_record].size_on_disk
215                           + shift,
216                           old_section_size
217                           - vs->di[sec_vert].size_on_disk
218                           - vs->di[sec_record].size_on_disk );
219         }
220 
221         if( section == sec_vert )
222             vs->di[sec_record].offset_on_disk_within_section += shift;
223     }
224 
225     // Actually write to disk.
226     vs->WriteToFile( wbuf.buffer,
227                      offset_on_disk_within_section
228                      + vs->vh.section_offsets[hsec_shape],
229                      wbuf.buffer_size );
230 
231     size_on_disk = wbuf.buffer_size;
232     dirty = false;
233 }
234 
235 /************************************************************************/
236 /*                           GetSectionEnd()                            */
237 /************************************************************************/
238 
GetSectionEnd()239 uint32 VecSegDataIndex::GetSectionEnd()
240 
241 {
242     return bytes;
243 }
244 
245 /************************************************************************/
246 /*                           SetSectionEnd()                            */
247 /************************************************************************/
248 
SetSectionEnd(uint32 new_end)249 void VecSegDataIndex::SetSectionEnd( uint32 new_end )
250 
251 {
252     // should we keep track of the need to write this back to disk?
253     bytes = new_end;
254 }
255 
256 /************************************************************************/
257 /*                          AddBlockToIndex()                           */
258 /************************************************************************/
259 
AddBlockToIndex(uint32 block)260 void VecSegDataIndex::AddBlockToIndex( uint32 block )
261 
262 {
263     GetIndex(); // force loading.
264 
265     block_index.push_back( block );
266     block_count++;
267     dirty = true;
268 }
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 
SetDirty()277 void VecSegDataIndex::SetDirty()
278 
279 {
280     dirty = true;
281 }
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 
VacateBlockRange(uint32 start,uint32 count)290 void VecSegDataIndex::VacateBlockRange( uint32 start, uint32 count )
291 
292 {
293     GetIndex(); // make sure loaded.
294 
295     unsigned int i;
296     uint32  next_block = (uint32) (vs->GetContentSize() / block_page_size);
297 
298     for( i = 0; i < block_count; i++ )
299     {
300         if( block_index[i] >= start && block_index[i] < start+count )
301         {
302             vs->MoveData( block_index[i] * block_page_size,
303                           next_block * block_page_size,
304                           block_page_size );
305             block_index[i] = next_block;
306             dirty = true;
307             next_block++;
308         }
309     }
310 }
311