/src/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | * SPDX-License-Identifier: MIT |
15 | | ****************************************************************************/ |
16 | | |
17 | | #include "pcidsk.h" |
18 | | #include "core/pcidsk_utils.h" |
19 | | #include "segment/cpcidskvectorsegment.h" |
20 | | #include <cassert> |
21 | | #include <cstring> |
22 | | #include <cstdio> |
23 | | #include <limits> |
24 | | |
25 | | using namespace PCIDSK; |
26 | | |
27 | | /* -------------------------------------------------------------------- */ |
28 | | /* Size of a block in the record/vertex block tables. This is */ |
29 | | /* determined by the PCIDSK format and may not be changed. */ |
30 | | /* -------------------------------------------------------------------- */ |
31 | | static const int block_page_size = 8192; |
32 | | |
33 | | /************************************************************************/ |
34 | | /* VecSegDataIndex() */ |
35 | | /************************************************************************/ |
36 | | |
37 | | VecSegDataIndex::VecSegDataIndex() |
38 | | |
39 | 3.75k | { |
40 | 3.75k | block_initialized = false; |
41 | 3.75k | vs = nullptr; |
42 | 3.75k | dirty = false; |
43 | 3.75k | section = 0; |
44 | 3.75k | offset_on_disk_within_section = 0; |
45 | 3.75k | size_on_disk = 0; |
46 | 3.75k | block_count = 0; |
47 | 3.75k | bytes = 0; |
48 | 3.75k | } |
49 | | |
50 | | /************************************************************************/ |
51 | | /* ~VecSegDataIndex() */ |
52 | | /************************************************************************/ |
53 | | |
54 | | VecSegDataIndex::~VecSegDataIndex() |
55 | | |
56 | 3.75k | { |
57 | 3.75k | } |
58 | | |
59 | | /************************************************************************/ |
60 | | /* Initialize() */ |
61 | | /************************************************************************/ |
62 | | |
63 | | void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vsIn, int sectionIn ) |
64 | | |
65 | 3.60k | { |
66 | 3.60k | this->section = sectionIn; |
67 | 3.60k | this->vs = vsIn; |
68 | | |
69 | 3.60k | if( section == sec_vert ) |
70 | 1.80k | offset_on_disk_within_section = 0; |
71 | 1.80k | else |
72 | 1.80k | offset_on_disk_within_section = vs->di[sec_vert].SerializedSize(); |
73 | | |
74 | 3.60k | uint32 offset = offset_on_disk_within_section |
75 | 3.60k | + vs->vh.section_offsets[hsec_shape]; |
76 | | |
77 | 3.60k | memcpy( &block_count, vs->GetData(sec_raw,offset,nullptr,4), 4); |
78 | 3.60k | memcpy( &bytes, vs->GetData(sec_raw,offset+4,nullptr,4), 4); |
79 | | |
80 | 3.60k | bool needs_swap = !BigEndianSystem(); |
81 | | |
82 | 3.60k | if( needs_swap ) |
83 | 3.60k | { |
84 | 3.60k | SwapData( &block_count, 4, 1 ); |
85 | 3.60k | SwapData( &bytes, 4, 1 ); |
86 | 3.60k | } |
87 | | |
88 | 3.60k | if( block_count > (std::numeric_limits<uint32>::max() - 8) /4 ) |
89 | 0 | { |
90 | 0 | throw PCIDSKException("Invalid block_count: %u", block_count); |
91 | 0 | } |
92 | | |
93 | 3.60k | size_on_disk = block_count * 4 + 8; |
94 | 3.60k | } |
95 | | |
96 | | /************************************************************************/ |
97 | | /* SerializedSize() */ |
98 | | /************************************************************************/ |
99 | | |
100 | | uint32 VecSegDataIndex::SerializedSize() |
101 | | |
102 | 6.88k | { |
103 | 6.88k | return 8 + 4 * block_count; |
104 | 6.88k | } |
105 | | |
106 | | /************************************************************************/ |
107 | | /* GetBlockIndex() */ |
108 | | /************************************************************************/ |
109 | | |
110 | | const std::vector<uint32> *VecSegDataIndex::GetIndex() |
111 | | |
112 | 26.3k | { |
113 | | /* -------------------------------------------------------------------- */ |
114 | | /* Load block map if needed. */ |
115 | | /* -------------------------------------------------------------------- */ |
116 | 26.3k | if( !block_initialized ) |
117 | 1.35k | { |
118 | 1.35k | bool needs_swap = !BigEndianSystem(); |
119 | | |
120 | 1.35k | try |
121 | 1.35k | { |
122 | 1.35k | block_index.resize( block_count ); |
123 | 1.35k | } |
124 | 1.35k | catch( const std::exception& ex ) |
125 | 1.35k | { |
126 | 0 | throw PCIDSKException("Out of memory allocating block_index(%u): %s", |
127 | 0 | block_count, ex.what()); |
128 | 0 | } |
129 | 1.35k | if( block_count > 0 ) |
130 | 0 | { |
131 | 0 | vs->ReadFromFile( &(block_index[0]), |
132 | 0 | offset_on_disk_within_section |
133 | 0 | + vs->vh.section_offsets[hsec_shape] + 8, |
134 | 0 | 4 * block_count ); |
135 | |
|
136 | 0 | if( needs_swap ) |
137 | 0 | SwapData( &(block_index[0]), 4, block_count ); |
138 | 0 | } |
139 | | |
140 | 1.35k | block_initialized = true; |
141 | 1.35k | } |
142 | | |
143 | 26.3k | return &block_index; |
144 | 26.3k | } |
145 | | |
146 | | /************************************************************************/ |
147 | | /* Flush() */ |
148 | | /************************************************************************/ |
149 | | |
150 | | void VecSegDataIndex::Flush() |
151 | | |
152 | 8.45k | { |
153 | 8.45k | if( !dirty ) |
154 | 6.97k | return; |
155 | | |
156 | 1.47k | GetIndex(); // force loading if not already loaded! |
157 | | |
158 | 1.47k | PCIDSKBuffer wbuf( SerializedSize() ); |
159 | | |
160 | 1.47k | memcpy( wbuf.buffer + 0, &block_count, 4 ); |
161 | 1.47k | memcpy( wbuf.buffer + 4, &bytes, 4 ); |
162 | 1.47k | memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count ); |
163 | | |
164 | 1.47k | bool needs_swap = !BigEndianSystem(); |
165 | | |
166 | 1.47k | if( needs_swap ) |
167 | 1.47k | SwapData( wbuf.buffer, 4, block_count+2 ); |
168 | | |
169 | | // Make sure this section of the header is large enough. |
170 | 1.47k | int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk; |
171 | | |
172 | 1.47k | if( shift != 0 ) |
173 | 1.35k | { |
174 | 1.35k | uint32 old_section_size = vs->vh.section_sizes[hsec_shape]; |
175 | | |
176 | | // fprintf( stderr, "Shifting section %d by %d bytes.\n", |
177 | | // section, shift ); |
178 | | |
179 | 1.35k | vs->vh.GrowSection( hsec_shape, old_section_size + shift ); |
180 | | |
181 | 1.35k | if( section == sec_vert ) |
182 | 677 | { |
183 | | // move record block index and shape index. |
184 | 677 | vs->MoveData( vs->vh.section_offsets[hsec_shape] |
185 | 677 | + vs->di[sec_vert].size_on_disk, |
186 | 677 | vs->vh.section_offsets[hsec_shape] |
187 | 677 | + vs->di[sec_vert].size_on_disk + shift, |
188 | 677 | old_section_size - size_on_disk ); |
189 | 677 | } |
190 | 674 | else |
191 | 674 | { |
192 | | // only move shape index. |
193 | 674 | vs->MoveData( vs->vh.section_offsets[hsec_shape] |
194 | 674 | + vs->di[sec_vert].size_on_disk |
195 | 674 | + vs->di[sec_record].size_on_disk, |
196 | 674 | vs->vh.section_offsets[hsec_shape] |
197 | 674 | + vs->di[sec_vert].size_on_disk |
198 | 674 | + vs->di[sec_record].size_on_disk |
199 | 674 | + shift, |
200 | 674 | old_section_size |
201 | 674 | - vs->di[sec_vert].size_on_disk |
202 | 674 | - vs->di[sec_record].size_on_disk ); |
203 | 674 | } |
204 | | |
205 | 1.35k | if( section == sec_vert ) |
206 | 677 | vs->di[sec_record].offset_on_disk_within_section += shift; |
207 | 1.35k | } |
208 | | |
209 | | // Actually write to disk. |
210 | 1.47k | vs->WriteToFile( wbuf.buffer, |
211 | 1.47k | offset_on_disk_within_section |
212 | 1.47k | + vs->vh.section_offsets[hsec_shape], |
213 | 1.47k | wbuf.buffer_size ); |
214 | | |
215 | 1.47k | size_on_disk = wbuf.buffer_size; |
216 | 1.47k | dirty = false; |
217 | 1.47k | } |
218 | | |
219 | | /************************************************************************/ |
220 | | /* GetSectionEnd() */ |
221 | | /************************************************************************/ |
222 | | |
223 | | uint32 VecSegDataIndex::GetSectionEnd() |
224 | | |
225 | 3.47M | { |
226 | 3.47M | return bytes; |
227 | 3.47M | } |
228 | | |
229 | | /************************************************************************/ |
230 | | /* SetSectionEnd() */ |
231 | | /************************************************************************/ |
232 | | |
233 | | void VecSegDataIndex::SetSectionEnd( uint32 new_end ) |
234 | | |
235 | 1.73M | { |
236 | | // should we keep track of the need to write this back to disk? |
237 | 1.73M | bytes = new_end; |
238 | 1.73M | } |
239 | | |
240 | | /************************************************************************/ |
241 | | /* AddBlockToIndex() */ |
242 | | /************************************************************************/ |
243 | | |
244 | | void VecSegDataIndex::AddBlockToIndex( uint32 block ) |
245 | | |
246 | 5.04k | { |
247 | 5.04k | GetIndex(); // force loading. |
248 | | |
249 | 5.04k | block_index.push_back( block ); |
250 | 5.04k | block_count++; |
251 | 5.04k | dirty = true; |
252 | 5.04k | } |
253 | | |
254 | | /************************************************************************/ |
255 | | /* SetDirty() */ |
256 | | /* */ |
257 | | /* This method is primarily used to mark the need to write the */ |
258 | | /* index when the location changes. */ |
259 | | /************************************************************************/ |
260 | | |
261 | | void VecSegDataIndex::SetDirty() |
262 | | |
263 | 0 | { |
264 | 0 | dirty = true; |
265 | 0 | } |
266 | | |
267 | | /************************************************************************/ |
268 | | /* VacateBlockRange() */ |
269 | | /* */ |
270 | | /* Move any blocks in the indicated block range to the end of */ |
271 | | /* the segment to make space for a growing header. */ |
272 | | /************************************************************************/ |
273 | | |
274 | | void VecSegDataIndex::VacateBlockRange( uint32 start, uint32 count ) |
275 | | |
276 | 1.70k | { |
277 | 1.70k | GetIndex(); // make sure loaded. |
278 | | |
279 | 1.70k | unsigned int i; |
280 | 1.70k | uint32 next_block = (uint32) (vs->GetContentSize() / block_page_size); |
281 | | |
282 | 20.7k | for( i = 0; i < block_count; i++ ) |
283 | 19.0k | { |
284 | 19.0k | if( block_index[i] >= start && block_index[i] < start+count ) |
285 | 1.14k | { |
286 | 1.14k | vs->MoveData( block_index[i] * block_page_size, |
287 | 1.14k | next_block * block_page_size, |
288 | 1.14k | block_page_size ); |
289 | 1.14k | block_index[i] = next_block; |
290 | 1.14k | dirty = true; |
291 | 1.14k | next_block++; |
292 | 1.14k | } |
293 | 19.0k | } |
294 | 1.70k | } |