1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the SysBlockMap class.
4 : *
5 : * This class is used to manage access to the SYS virtual block map segment
6 : * (named SysBMDir). This segment is used to keep track of one or more
7 : * virtual files stored in SysBData segments. These virtual files are normally
8 : * used to hold tiled images for primary bands or overviews.
9 : *
10 : * This class is closely partnered with the SysVirtualFile class, and the
11 : * primary client is the CTiledChannel class.
12 : *
13 : ******************************************************************************
14 : * Copyright (c) 2009
15 : * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada
16 : *
17 : * Permission is hereby granted, free of charge, to any person obtaining a
18 : * copy of this software and associated documentation files (the "Software"),
19 : * to deal in the Software without restriction, including without limitation
20 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21 : * and/or sell copies of the Software, and to permit persons to whom the
22 : * Software is furnished to do so, subject to the following conditions:
23 : *
24 : * The above copyright notice and this permission notice shall be included
25 : * in all copies or substantial portions of the Software.
26 : *
27 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 : * DEALINGS IN THE SOFTWARE.
34 : ****************************************************************************/
35 :
36 : #include "pcidsk_exception.h"
37 : #include "pcidsk_file.h"
38 : #include "core/sysvirtualfile.h"
39 : #include "segment/sysblockmap.h"
40 : #include "core/cpcidskfile.h"
41 :
42 : #include <cassert>
43 : #include <vector>
44 : #include <cstring>
45 :
46 : using namespace PCIDSK;
47 :
48 : /************************************************************************/
49 : /* SysBlockMap() */
50 : /************************************************************************/
51 :
52 3 : SysBlockMap::SysBlockMap( PCIDSKFile *file, int segment,
53 : const char *segment_pointer )
54 3 : : CPCIDSKSegment( file, segment, segment_pointer )
55 :
56 : {
57 3 : loaded = false;
58 3 : dirty = false;
59 3 : growing_segment = 0;
60 3 : }
61 :
62 : /************************************************************************/
63 : /* ~SysBlockMap() */
64 : /************************************************************************/
65 :
66 6 : SysBlockMap::~SysBlockMap()
67 :
68 : {
69 : size_t i;
70 :
71 6 : for( i = 0; i < virtual_files.size(); i++ )
72 : {
73 3 : delete virtual_files[i];
74 3 : virtual_files[i] = NULL;
75 : }
76 :
77 3 : Sync();
78 6 : }
79 :
80 : /************************************************************************/
81 : /* Initialize() */
82 : /* */
83 : /* This method is used after creation of the SysBMDir segment */
84 : /* to fill in valid contents. Prepares bare minimum contents. */
85 : /************************************************************************/
86 :
87 0 : void SysBlockMap::Initialize()
88 :
89 : {
90 0 : PCIDSKBuffer init_data(512);
91 :
92 0 : init_data.Put( "VERSION 1", 0, 10 );
93 0 : init_data.Put( 0, 10, 8 );
94 0 : init_data.Put( 0, 18, 8 );
95 0 : init_data.Put( -1, 26, 8 );
96 0 : init_data.Put( "", 34, 512-34 );
97 :
98 0 : WriteToFile( init_data.buffer, 0, init_data.buffer_size );
99 : #ifdef notdef
100 : // arbitrarily grow the segment a bit to avoid having to move it too soon.
101 : WriteToFile( "\0", 8191, 1 );
102 : #endif
103 0 : }
104 :
105 : /************************************************************************/
106 : /* Load() */
107 : /************************************************************************/
108 :
109 3 : void SysBlockMap::Load()
110 :
111 : {
112 3 : if( loaded )
113 0 : return;
114 :
115 : // TODO: this should likely be protected by a mutex.
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Load the segment contents into a buffer. */
119 : /* -------------------------------------------------------------------- */
120 3 : seg_data.SetSize( (int) (data_size - 1024) );
121 :
122 3 : ReadFromFile( seg_data.buffer, 0, data_size - 1024 );
123 :
124 3 : if( strncmp(seg_data.buffer,"VERSION",7) != 0 )
125 0 : ThrowPCIDSKException( "SysBlockMap::Load() - block map corrupt." );
126 :
127 3 : if( seg_data.GetInt( 7, 3 ) != 1 )
128 0 : ThrowPCIDSKException( "SysBlockMap::Load() - unsupported version." );
129 :
130 : /* -------------------------------------------------------------------- */
131 : /* Establish our SysVirtualFile array based on the number of */
132 : /* images listed in the image list. */
133 : /* -------------------------------------------------------------------- */
134 3 : int layer_count = seg_data.GetInt( 10, 8 );
135 :
136 3 : block_count = seg_data.GetInt( 18, 8 );
137 3 : first_free_block = seg_data.GetInt( 26, 8 );
138 :
139 3 : virtual_files.resize( layer_count );
140 :
141 3 : block_map_offset = 512;
142 3 : layer_list_offset = block_map_offset + 28 * block_count;
143 :
144 3 : loaded = true;
145 : }
146 :
147 : /************************************************************************/
148 : /* Sync() */
149 : /************************************************************************/
150 :
151 3 : void SysBlockMap::Sync()
152 :
153 : {
154 3 : if( !loaded || !dirty )
155 3 : return;
156 :
157 0 : WriteToFile( seg_data.buffer, 0, seg_data.buffer_size );
158 :
159 0 : dirty = false;
160 : }
161 :
162 : /************************************************************************/
163 : /* AllocateBlocks() */
164 : /* */
165 : /* Allocate a bunch of new blocks and attach to the free list. */
166 : /************************************************************************/
167 :
168 0 : void SysBlockMap::AllocateBlocks()
169 :
170 : {
171 : /* -------------------------------------------------------------------- */
172 : /* Find a segment we can extend. We consider any SYS segments */
173 : /* with a name of SysBData. */
174 : /* -------------------------------------------------------------------- */
175 : PCIDSKSegment *seg;
176 :
177 0 : if( growing_segment > 0 )
178 : {
179 0 : seg = file->GetSegment( growing_segment );
180 0 : if( !seg->IsAtEOF() )
181 0 : growing_segment = 0;
182 : }
183 :
184 0 : if( growing_segment == 0 )
185 : {
186 : PCIDSKSegment *seg;
187 0 : int previous = 0;
188 :
189 0 : while( (seg=file->GetSegment( SEG_SYS, "SysBData", previous )) != NULL )
190 : {
191 0 : previous = seg->GetSegmentNumber();
192 :
193 0 : if( seg->IsAtEOF() )
194 : {
195 0 : growing_segment = previous;
196 0 : break;
197 : }
198 : }
199 : }
200 :
201 : /* -------------------------------------------------------------------- */
202 : /* If we didn't find one, then create a new segment. */
203 : /* -------------------------------------------------------------------- */
204 0 : if( growing_segment == 0 )
205 : {
206 : growing_segment =
207 : file->CreateSegment( "SysBData",
208 : "System Block Data for Tiles and Overviews "
209 : "- Do not modify",
210 0 : SEG_SYS, 0L );
211 : }
212 :
213 : /* -------------------------------------------------------------------- */
214 : /* Allocate another set of space. */
215 : /* -------------------------------------------------------------------- */
216 0 : uint64 new_big_blocks = 16;
217 0 : int new_bytes = new_big_blocks * SysVirtualFile::block_size;
218 0 : seg = file->GetSegment( growing_segment );
219 : int block_index_in_segment =
220 0 : seg->GetContentSize() / SysVirtualFile::block_size;
221 :
222 0 : seg->WriteToFile( "\0", seg->GetContentSize() + new_bytes - 1, 1 );
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Resize the SysBMDir segment data, growing the block map area. */
226 : /* -------------------------------------------------------------------- */
227 0 : if( block_map_offset + 28 * (block_count + new_big_blocks)
228 : + virtual_files.size() * 24 > (unsigned int) seg_data.buffer_size )
229 : seg_data.SetSize(
230 : block_map_offset + 28 * (block_count + new_big_blocks)
231 0 : + virtual_files.size() * 24 );
232 :
233 : // push the layer list on.
234 : memmove( seg_data.buffer + layer_list_offset + new_big_blocks*28,
235 : seg_data.buffer + layer_list_offset,
236 0 : virtual_files.size() * 24 );
237 :
238 : /* -------------------------------------------------------------------- */
239 : /* Fill in info on the new blocks. */
240 : /* -------------------------------------------------------------------- */
241 : uint64 block_index;
242 :
243 0 : for( block_index = block_count;
244 : block_index < block_count + new_big_blocks;
245 : block_index++ )
246 : {
247 0 : uint64 bi_offset = block_map_offset + block_index * 28;
248 :
249 0 : seg_data.Put( growing_segment, bi_offset, 4 );
250 0 : seg_data.Put( block_index_in_segment++, bi_offset+4, 8 );
251 0 : seg_data.Put( -1, bi_offset+12, 8 );
252 :
253 0 : if( block_index == block_count + new_big_blocks - 1 )
254 0 : seg_data.Put( -1, bi_offset+20, 8 );
255 : else
256 0 : seg_data.Put( block_index+1, bi_offset+20, 8 );
257 : }
258 :
259 0 : first_free_block = block_count;
260 0 : seg_data.Put( first_free_block, 26, 8 );
261 :
262 0 : block_count += new_big_blocks;
263 0 : seg_data.Put( block_count, 18, 8 );
264 :
265 0 : layer_list_offset = block_map_offset + 28 * block_count;
266 :
267 0 : dirty = true;
268 0 : }
269 :
270 : /************************************************************************/
271 : /* GrowVirtualFile() */
272 : /* */
273 : /* Get one more block for this virtual file. */
274 : /************************************************************************/
275 :
276 0 : int SysBlockMap::GrowVirtualFile( int image, int &last_block,
277 : int &block_segment_ret )
278 :
279 : {
280 0 : Load();
281 :
282 : /* -------------------------------------------------------------------- */
283 : /* Do we need to create new free blocks? */
284 : /* -------------------------------------------------------------------- */
285 0 : if( first_free_block == -1 )
286 0 : AllocateBlocks();
287 :
288 : /* -------------------------------------------------------------------- */
289 : /* Return the first free block, and update the next pointer of */
290 : /* the previous block. */
291 : /* -------------------------------------------------------------------- */
292 0 : int alloc_block = first_free_block;
293 :
294 : // update first free block to point to the next free block.
295 0 : first_free_block = seg_data.GetInt( block_map_offset+alloc_block*28+20, 8);
296 0 : seg_data.Put( first_free_block, 26, 8 );
297 :
298 : // mark block as owned by this layer/image.
299 0 : seg_data.Put( image, block_map_offset + alloc_block*28 + 12, 8 );
300 :
301 : // clear next free block on allocated block - it is the last in the chain
302 0 : seg_data.Put( -1, block_map_offset + alloc_block*28 + 20, 8 );
303 :
304 : // point the previous "last block" for this virtual file to this new block
305 0 : if( last_block != -1 )
306 0 : seg_data.Put( alloc_block, block_map_offset + last_block*28 + 20, 8 );
307 : else
308 0 : seg_data.Put( alloc_block, layer_list_offset + image*24 + 4, 8 );
309 :
310 0 : dirty = true;
311 :
312 0 : block_segment_ret = seg_data.GetInt( block_map_offset+alloc_block*28, 4 );
313 0 : last_block = alloc_block;
314 :
315 0 : return seg_data.GetInt( block_map_offset+alloc_block*28+4, 8 );
316 : }
317 :
318 : /************************************************************************/
319 : /* SetVirtualFileSize() */
320 : /************************************************************************/
321 :
322 0 : void SysBlockMap::SetVirtualFileSize( int image_index, uint64 file_length )
323 :
324 : {
325 0 : seg_data.Put( file_length, layer_list_offset + 24*image_index + 12, 12 );
326 0 : dirty = true;
327 0 : }
328 :
329 : /************************************************************************/
330 : /* GetVirtualFile() */
331 : /************************************************************************/
332 :
333 3 : SysVirtualFile *SysBlockMap::GetVirtualFile( int image )
334 :
335 : {
336 3 : Load();
337 :
338 3 : if( image < 0 || image >= (int) virtual_files.size() )
339 : ThrowPCIDSKException( "GetImageSysFile(%d): invalid image index",
340 0 : image );
341 :
342 3 : if( virtual_files[image] != NULL )
343 0 : return virtual_files[image];
344 :
345 : uint64 vfile_length =
346 3 : seg_data.GetUInt64( layer_list_offset + 24*image + 12, 12 );
347 : int start_block =
348 3 : seg_data.GetInt( layer_list_offset + 24*image + 4, 8 );
349 :
350 : virtual_files[image] =
351 : new SysVirtualFile( dynamic_cast<CPCIDSKFile *>(file), start_block, vfile_length, seg_data,
352 3 : this, image );
353 :
354 3 : return virtual_files[image];
355 : }
356 :
357 : /************************************************************************/
358 : /* CreateVirtualFile() */
359 : /************************************************************************/
360 :
361 0 : int SysBlockMap::CreateVirtualFile()
362 :
363 : {
364 0 : Load();
365 :
366 : /* -------------------------------------------------------------------- */
367 : /* Is there an existing dead layer we can reuse? */
368 : /* -------------------------------------------------------------------- */
369 : unsigned int layer_index;
370 :
371 0 : for( layer_index = 0; layer_index < virtual_files.size(); layer_index++ )
372 : {
373 0 : if( seg_data.GetInt( layer_list_offset + 24*layer_index + 0, 4 )
374 : == 1 /* dead */ )
375 : {
376 0 : break;
377 : }
378 : }
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* If not, extend the layer table. */
382 : /* -------------------------------------------------------------------- */
383 0 : if( layer_index == virtual_files.size() )
384 : {
385 0 : seg_data.Put( (int) layer_index+1, 10, 8 );
386 :
387 0 : if( layer_list_offset + (virtual_files.size()+1) * 24
388 : > (unsigned int) seg_data.buffer_size )
389 0 : seg_data.SetSize( layer_list_offset + (virtual_files.size()+1) * 24 );
390 :
391 0 : virtual_files.resize(layer_index+1);
392 0 : virtual_files[layer_index] = NULL;
393 : }
394 :
395 : /* -------------------------------------------------------------------- */
396 : /* Set all the entries for this layer. */
397 : /* -------------------------------------------------------------------- */
398 0 : dirty = true;
399 :
400 0 : seg_data.Put( 2, layer_list_offset + 24*layer_index + 0, 4 );
401 0 : seg_data.Put( -1, layer_list_offset + 24*layer_index + 4, 8 );
402 0 : seg_data.Put( 0, layer_list_offset + 24*layer_index + 12, 12 );
403 :
404 0 : return layer_index;
405 : }
406 :
407 : /************************************************************************/
408 : /* CreateVirtualImageFile() */
409 : /************************************************************************/
410 :
411 0 : int SysBlockMap::CreateVirtualImageFile( int width, int height,
412 : int block_width, int block_height,
413 : eChanType chan_type,
414 : std::string compression )
415 :
416 : {
417 0 : if( compression == "" )
418 0 : compression = "NONE";
419 :
420 : /* -------------------------------------------------------------------- */
421 : /* Create the underlying virtual file. */
422 : /* -------------------------------------------------------------------- */
423 0 : int img_index = CreateVirtualFile();
424 0 : SysVirtualFile *vfile = GetVirtualFile( img_index );
425 :
426 : /* -------------------------------------------------------------------- */
427 : /* Set up the image header. */
428 : /* -------------------------------------------------------------------- */
429 0 : PCIDSKBuffer theader(128);
430 :
431 0 : theader.Put( "", 0, 128 );
432 :
433 0 : theader.Put( width, 0, 8 );
434 0 : theader.Put( height, 8, 8 );
435 0 : theader.Put( block_width, 16, 8 );
436 0 : theader.Put( block_height, 24, 8 );
437 0 : theader.Put( DataTypeName(chan_type).c_str(), 32, 4 );
438 0 : theader.Put( compression.c_str(), 54, 8 );
439 :
440 0 : vfile->WriteToFile( theader.buffer, 0, 128 );
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Setup the tile map - initially with no tiles referenced. */
444 : /* -------------------------------------------------------------------- */
445 0 : int tiles_per_row = (width + block_width - 1) / block_width;
446 0 : int tiles_per_col = (height + block_height - 1) / block_height;
447 0 : int tile_count = tiles_per_row * tiles_per_col;
448 : int i;
449 :
450 0 : PCIDSKBuffer tmap( tile_count * 20 );
451 :
452 0 : for( i = 0; i < tile_count; i++ )
453 : {
454 0 : tmap.Put( -1, i*12, 12 );
455 0 : tmap.Put( 0, tile_count*12 + i*8, 8 );
456 : }
457 :
458 0 : vfile->WriteToFile( tmap.buffer, 128, tile_count*20 );
459 :
460 0 : return img_index;
461 : }
|