diff options
Diffstat (limited to '')
-rw-r--r-- | src/stable/myriad.cpp | 222 |
1 files changed, 200 insertions, 22 deletions
diff --git a/src/stable/myriad.cpp b/src/stable/myriad.cpp index f3ff09a..44a990a 100644 --- a/src/stable/myriad.cpp +++ b/src/stable/myriad.cpp | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "bu/myriad.h" | 1 | #include "bu/myriad.h" |
2 | #include "bu/myriadstream.h" | 2 | #include "bu/myriadstream.h" |
3 | 3 | ||
4 | #include "bu/membuf.h" | ||
4 | #include "bu/mutexlocker.h" | 5 | #include "bu/mutexlocker.h" |
5 | #include "bu/util.h" | 6 | #include "bu/util.h" |
6 | 7 | ||
@@ -27,27 +28,45 @@ Bu::Myriad::Myriad( Bu::Stream &rBacking, int iBlockSize, | |||
27 | rBacking( rBacking ), | 28 | rBacking( rBacking ), |
28 | iBlockSize( iBlockSize ), | 29 | iBlockSize( iBlockSize ), |
29 | iBlockCount( 0 ), | 30 | iBlockCount( 0 ), |
30 | bIsNewStream( true ) | 31 | bIsNewStream( true ), |
32 | bStructureChanged( false ), | ||
33 | iLastUsedIndex( -1 ) | ||
31 | { | 34 | { |
32 | if( !rBacking.isSeekable() ) | 35 | if( !rBacking.isSeekable() ) |
33 | { | 36 | { |
34 | throw Bu::MyriadException( Bu::MyriadException::invalidBackingStream, | 37 | throw Bu::MyriadException( Bu::MyriadException::invalidBackingStream, |
35 | "Myriad backing stream must be random access (seekable)."); | 38 | "Myriad backing stream must be random access (seekable)."); |
36 | } | 39 | } |
37 | if( !loadMyriad() ) | 40 | if( rBacking.getSize() == 0 ) |
38 | { | 41 | { |
39 | createMyriad( iBlockSize, iPreallocateBlocks ); | 42 | createMyriad( iBlockSize, iPreallocateBlocks ); |
40 | } | 43 | } |
44 | else | ||
45 | { | ||
46 | loadMyriad(); | ||
47 | } | ||
41 | } | 48 | } |
42 | 49 | ||
43 | Bu::Myriad::~Myriad() | 50 | Bu::Myriad::~Myriad() |
44 | { | 51 | { |
52 | writeHeader(); | ||
45 | } | 53 | } |
46 | 54 | ||
47 | Bu::MyriadStream Bu::Myriad::create( Bu::Myriad::Mode /*eMode*/, | 55 | Bu::MyriadStream Bu::Myriad::create( Bu::Myriad::Mode eMode, |
48 | int32_t /*iPreallocateBytes*/ ) | 56 | int32_t iPreallocateBytes ) |
49 | { | 57 | { |
50 | return Bu::MyriadStream( *this, NULL, (Mode)0 ); | 58 | Bu::MutexLocker l( mAccess ); |
59 | |||
60 | Stream *pStream = new Stream( *this, ++iLastUsedIndex, 0 ); | ||
61 | int iBlocks = std::max(1, blkDiv( iPreallocateBytes, iBlockSize )); | ||
62 | for( int j = 0; j < iBlocks; j++ ) | ||
63 | { | ||
64 | pStream->aBlocks.append( __allocateBlock() ); | ||
65 | } | ||
66 | hStream.insert( pStream->iStream, pStream ); | ||
67 | bStructureChanged = true; | ||
68 | |||
69 | return Bu::MyriadStream( *this, pStream, (Mode)(eMode&ReadWrite) ); | ||
51 | } | 70 | } |
52 | 71 | ||
53 | Bu::MyriadStream Bu::Myriad::open( Bu::Myriad::StreamId iStream, | 72 | Bu::MyriadStream Bu::Myriad::open( Bu::Myriad::StreamId iStream, |
@@ -110,7 +129,7 @@ bool Bu::Myriad::loadMyriad() | |||
110 | } | 129 | } |
111 | MyriadRead( &uBitsPerInt, 1 ); | 130 | MyriadRead( &uBitsPerInt, 1 ); |
112 | if( uBitsPerInt != 32 ) | 131 | if( uBitsPerInt != 32 ) |
113 | { | 132 | { |
114 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, | 133 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, |
115 | "Only 32 bits per int are supported at this time."); | 134 | "Only 32 bits per int are supported at this time."); |
116 | } | 135 | } |
@@ -128,28 +147,53 @@ bool Bu::Myriad::loadMyriad() | |||
128 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, | 147 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, |
129 | "The first stream defined must be the header/zero stream."); | 148 | "The first stream defined must be the header/zero stream."); |
130 | } | 149 | } |
150 | iLastUsedIndex = iStream; | ||
131 | int32_t iHeaderStreamBytes; | 151 | int32_t iHeaderStreamBytes; |
132 | MyriadRead( &iHeaderStreamBytes, 4 ); | 152 | MyriadRead( &iHeaderStreamBytes, 4 ); |
133 | 153 | ||
134 | Stream *pHeaderStream = new Stream( *this, iStream, iHeaderStreamBytes ); | 154 | Stream *pHeaderStream = new Stream( *this, iStream, iHeaderStreamBytes ); |
135 | int iHeaderStreamBlocks = blkDiv(iHeaderStreamBytes+4, iBlockSize ); | 155 | hStream.insert( iStream, pHeaderStream ); |
136 | 156 | int iHeaderStreamBlocks = blkDiv(iHeaderStreamBytes, iBlockSize ); | |
137 | while( iHeaderStreamBytes+(iHeaderStreamBlocks*4) | 157 | MyriadStream sHeader( *this, pHeaderStream, Read ); |
138 | > iHeaderStreamBlocks*iBlockSize ) | ||
139 | { | ||
140 | iHeaderStreamBlocks = blkDiv( | ||
141 | (iHeaderStreamBytes+((iHeaderStreamBlocks+1)*4)), iBlockSize | ||
142 | ); | ||
143 | } | ||
144 | 158 | ||
159 | // We need to read enough so that we can gurantee that we're within a block | ||
160 | // that we have read the index to, plus one index. | ||
145 | for( int32_t j = 0; j < iHeaderStreamBlocks; j++ ) | 161 | for( int32_t j = 0; j < iHeaderStreamBlocks; j++ ) |
146 | { | 162 | { |
147 | int32_t iBlockIndex; | 163 | int32_t iBlockIndex; |
148 | MyriadRead( &iBlockIndex, 4 ); | 164 | MyriadRead( &iBlockIndex, 4 ); |
149 | pHeaderStream->aBlocks.append( iBlockIndex ); | 165 | pHeaderStream->aBlocks.append( iBlockIndex ); |
166 | if( rBacking.tell()+4 <= (j+1)*iBlockSize ) | ||
167 | break; | ||
150 | } | 168 | } |
151 | 169 | ||
152 | // Bootstrap now using the header stream to read the rest of the data. | 170 | // Bootstrap now using the header stream to read the rest of the data. |
171 | sHeader.setPos( rBacking.tell() ); | ||
172 | while( pHeaderStream->aBlocks.getSize() < iHeaderStreamBlocks ) | ||
173 | { | ||
174 | int32_t iBlockIndex; | ||
175 | sHeader.read( &iBlockIndex, 4 ); | ||
176 | pHeaderStream->aBlocks.append( iBlockIndex ); | ||
177 | } | ||
178 | |||
179 | // Ok, now we can read the rest of the header in. | ||
180 | for( int j = 1; j < iStreamCount; j++ ) | ||
181 | { | ||
182 | int32_t iStreamBytes; | ||
183 | sHeader.read( &iStream, 4 ); | ||
184 | sHeader.read( &iStreamBytes, 4 ); | ||
185 | Stream *pStream = new Stream( *this, iStream, iStreamBytes ); | ||
186 | int32_t iBlocks = blkDiv(iStreamBytes, iBlockSize ); | ||
187 | for( int k = 0; k < iBlocks; k++ ) | ||
188 | { | ||
189 | int32_t iBlockIndex; | ||
190 | sHeader.read( &iBlockIndex, 4 ); | ||
191 | pStream->aBlocks.append( iBlockIndex ); | ||
192 | } | ||
193 | hStream.insert( iStream, pStream ); | ||
194 | if( iLastUsedIndex < iStream ) | ||
195 | iLastUsedIndex = iStream; | ||
196 | } | ||
153 | 197 | ||
154 | return true; | 198 | return true; |
155 | } | 199 | } |
@@ -165,7 +209,8 @@ void Bu::Myriad::createMyriad( int32_t iBlockSize, int32_t iPreallocateBlocks ) | |||
165 | { | 209 | { |
166 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, | 210 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, |
167 | "Backing stream contains data, but not a myriad structure."); | 211 | "Backing stream contains data, but not a myriad structure."); |
168 | } | 212 | } |
213 | |||
169 | /* | 214 | /* |
170 | struct { | 215 | struct { |
171 | char sMagicCode[4]; | 216 | char sMagicCode[4]; |
@@ -253,11 +298,128 @@ void Bu::Myriad::createMyriad( int32_t iBlockSize, int32_t iPreallocateBlocks ) | |||
253 | { | 298 | { |
254 | lFreeBlocks.append( j ); | 299 | lFreeBlocks.append( j ); |
255 | } | 300 | } |
301 | iLastUsedIndex = 0; | ||
302 | } | ||
303 | |||
304 | void Bu::Myriad::writeHeader() | ||
305 | { | ||
306 | if( !rBacking.isWritable() ) | ||
307 | return; | ||
308 | Bu::println("Writing stream breakdown:"); | ||
309 | Bu::MemBuf mbHeader; | ||
310 | { | ||
311 | Bu::MutexLocker l( mAccess ); | ||
312 | |||
313 | int32_t iHdrStreamSize = __calcHeaderSize(); | ||
314 | // Maybe just do stream surgery here. | ||
315 | { | ||
316 | Stream *psHeader = hStream.get( 0 ); | ||
317 | Bu::MutexLocker l2( psHeader->mAccess ); | ||
318 | int iNewBlocks = Bu::blkDiv( iHdrStreamSize, iBlockSize ); | ||
319 | if( iHdrStreamSize < psHeader->iSize ) | ||
320 | { | ||
321 | while( psHeader->aBlocks.getSize() > iNewBlocks ) | ||
322 | { | ||
323 | __releaseBlock( psHeader->aBlocks.last(), false ); | ||
324 | psHeader->aBlocks.eraseLast(); | ||
325 | } | ||
326 | } | ||
327 | else if( iHdrStreamSize > psHeader->iSize ) | ||
328 | { | ||
329 | while( psHeader->aBlocks.getSize() < iNewBlocks ) | ||
330 | { | ||
331 | psHeader->aBlocks.append( __allocateBlock() ); | ||
332 | } | ||
333 | } | ||
334 | psHeader->iSize = iHdrStreamSize; | ||
335 | } | ||
336 | |||
337 | Bu::println("Computed header size: %1 bytes. Ver=%2, Bpi=%3, BlockSize=%4").arg( iHdrStreamSize ).arg( 1 ).arg( 32 ).arg( iBlockSize ); | ||
338 | |||
339 | uint8_t uVer = 1; | ||
340 | uint8_t uBpi = 32; | ||
341 | int32_t iStreamCount = hStream.getSize(); | ||
342 | |||
343 | mbHeader.write( Myriad_MAGIC_CODE, 4 ); | ||
344 | mbHeader.write( &uVer, 1 ); | ||
345 | mbHeader.write( &uBpi, 1 ); | ||
346 | mbHeader.write( &iBlockSize, 4 ); | ||
347 | mbHeader.write( &iStreamCount, 4 ); | ||
348 | StreamHash::KeyList lStreamId = hStream.getKeys(); | ||
349 | lStreamId.sort(); | ||
350 | if( lStreamId.first() != 0 ) | ||
351 | { | ||
352 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, | ||
353 | "There doesn't appear to be a zero (header) stream."); | ||
354 | } | ||
355 | for( StreamHash::KeyList::iterator i = lStreamId.begin(); i; i++ ) | ||
356 | { | ||
357 | uint32_t uStreamId = *i; | ||
358 | Stream *pStream = hStream.get( uStreamId ); | ||
359 | uint32_t uStreamSize = pStream->getSize(); | ||
360 | mbHeader.write( &uStreamId, 4 ); | ||
361 | mbHeader.write( &uStreamSize, 4 ); | ||
362 | Bu::Array<int32_t> aBlocks = pStream->getBlockList(); | ||
363 | |||
364 | Bu::println(" Stream %1 is %2 bytes %3 blocks (%4 blocks computed)") | ||
365 | .arg( *i ).arg( uStreamSize ) | ||
366 | .arg( aBlocks.getSize() ) | ||
367 | .arg( Bu::blkDiv( (int)uStreamSize, (int)iBlockSize ) ); | ||
368 | |||
369 | for( Bu::Array<int32_t>::iterator i = aBlocks.begin(); i; i++ ) | ||
370 | { | ||
371 | int32_t iIdx = *i; | ||
372 | mbHeader.write( &iIdx, 4 ); | ||
373 | } | ||
374 | } | ||
375 | |||
376 | |||
377 | } | ||
378 | |||
379 | Bu::MyriadStream sHeader( *this, hStream.get( 0 ), Bu::Myriad::Write ); | ||
380 | sHeader.write( mbHeader.getString() ); | ||
381 | } | ||
382 | |||
383 | int32_t Bu::Myriad::__calcHeaderSize() | ||
384 | { | ||
385 | int32_t iHdrSize = 4+1+1+4+4; | ||
386 | |||
387 | StreamHash::KeyList lStreamId = hStream.getKeys(); | ||
388 | lStreamId.sort(); | ||
389 | if( lStreamId.first() != 0 ) | ||
390 | { | ||
391 | throw Bu::MyriadException( Bu::MyriadException::invalidFormat, | ||
392 | "There doesn't appear to be a zero (header) stream."); | ||
393 | } | ||
394 | for( StreamHash::KeyList::iterator i = lStreamId.begin(); i; i++ ) | ||
395 | { | ||
396 | iHdrSize += 4+4; | ||
397 | int32_t iStreamSize = hStream.get( *i )->getSize(); | ||
398 | if( (*i) != 0 ) | ||
399 | { | ||
400 | iHdrSize += Bu::blkDiv( iStreamSize, iBlockSize )*4; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | int32_t iNewSize = iHdrSize; | ||
405 | int32_t iOldSize; | ||
406 | |||
407 | do { | ||
408 | iOldSize = iNewSize; | ||
409 | iNewSize = iHdrSize + Bu::blkDiv(iNewSize, iBlockSize); | ||
410 | } while( iOldSize != iNewSize ); | ||
411 | |||
412 | return iNewSize; | ||
256 | } | 413 | } |
257 | 414 | ||
258 | int32_t Bu::Myriad::allocateBlock() | 415 | int32_t Bu::Myriad::allocateBlock() |
259 | { | 416 | { |
260 | Bu::MutexLocker l( mAccess ); | 417 | Bu::MutexLocker l( mAccess ); |
418 | return __allocateBlock(); | ||
419 | } | ||
420 | |||
421 | int32_t Bu::Myriad::__allocateBlock() | ||
422 | { | ||
261 | if( lFreeBlocks.isEmpty() ) | 423 | if( lFreeBlocks.isEmpty() ) |
262 | { | 424 | { |
263 | // Increase the size of the backing stream | 425 | // Increase the size of the backing stream |
@@ -275,6 +437,11 @@ int32_t Bu::Myriad::allocateBlock() | |||
275 | void Bu::Myriad::releaseBlock( int32_t iBlockId, bool bBlank ) | 437 | void Bu::Myriad::releaseBlock( int32_t iBlockId, bool bBlank ) |
276 | { | 438 | { |
277 | Bu::MutexLocker l( mAccess ); | 439 | Bu::MutexLocker l( mAccess ); |
440 | __releaseBlock( iBlockId, bBlank ); | ||
441 | } | ||
442 | |||
443 | void Bu::Myriad::__releaseBlock( int32_t iBlockId, bool bBlank ) | ||
444 | { | ||
278 | lFreeBlocks.append( iBlockId ); | 445 | lFreeBlocks.append( iBlockId ); |
279 | if( bBlank ) | 446 | if( bBlank ) |
280 | { | 447 | { |
@@ -314,10 +481,10 @@ int32_t Bu::Myriad::blockRead( int32_t iBlock, int32_t iStart, | |||
314 | void *pTarget, int32_t iSize ) | 481 | void *pTarget, int32_t iSize ) |
315 | { | 482 | { |
316 | int32_t iUpperSize = iBlockSize - (iStart%iBlockSize); | 483 | int32_t iUpperSize = iBlockSize - (iStart%iBlockSize); |
317 | Bu::println("Max read within block: %1 vs %2 (start=%3, blocksize=%4)") | 484 | /* Bu::println("Max read within block: %1 vs %2 (start=%3, blocksize=%4)") |
318 | .arg( iUpperSize ).arg( iSize ) | 485 | .arg( iUpperSize ).arg( iSize ) |
319 | .arg( iStart ).arg( iBlockSize ); | 486 | .arg( iStart ).arg( iBlockSize ); |
320 | 487 | */ | |
321 | int32_t iAmnt = std::min( iSize, iUpperSize ); | 488 | int32_t iAmnt = std::min( iSize, iUpperSize ); |
322 | Bu::MutexLocker l( mBacking ); | 489 | Bu::MutexLocker l( mBacking ); |
323 | rBacking.setPos( iBlockSize*iBlock + iStart ); | 490 | rBacking.setPos( iBlockSize*iBlock + iStart ); |
@@ -329,10 +496,10 @@ int32_t Bu::Myriad::blockWrite( int32_t iBlock, int32_t iStart, | |||
329 | const void *pTarget, int32_t iSize ) | 496 | const void *pTarget, int32_t iSize ) |
330 | { | 497 | { |
331 | int32_t iUpperSize = iBlockSize - (iStart%iBlockSize); | 498 | int32_t iUpperSize = iBlockSize - (iStart%iBlockSize); |
332 | Bu::println("Max write within block: %1 vs %2 (start=%3, blocksize=%4)") | 499 | /* Bu::println("Max write within block: %1 vs %2 (start=%3, blocksize=%4)") |
333 | .arg( iUpperSize ).arg( iSize ) | 500 | .arg( iUpperSize ).arg( iSize ) |
334 | .arg( iStart ).arg( iBlockSize ); | 501 | .arg( iStart ).arg( iBlockSize ); |
335 | 502 | */ | |
336 | int32_t iAmnt = std::min( iSize, iUpperSize ); | 503 | int32_t iAmnt = std::min( iSize, iUpperSize ); |
337 | Bu::MutexLocker l( mBacking ); | 504 | Bu::MutexLocker l( mBacking ); |
338 | rBacking.setPos( iBlock*iBlockSize + iStart ); | 505 | rBacking.setPos( iBlock*iBlockSize + iStart ); |
@@ -385,10 +552,10 @@ void Bu::Myriad::Stream::setSize( int32_t iNewSize ) | |||
385 | { | 552 | { |
386 | // Two possible modes, shrink or grow. | 553 | // Two possible modes, shrink or grow. |
387 | Bu::MutexLocker l( mAccess ); | 554 | Bu::MutexLocker l( mAccess ); |
555 | int iNewBlocks = Bu::blkDiv( iNewSize, rParent.iBlockSize ); | ||
388 | if( iNewSize < iSize ) | 556 | if( iNewSize < iSize ) |
389 | { | 557 | { |
390 | // Shrink it | 558 | // Shrink it |
391 | int iNewBlocks = Bu::blkDiv( iNewSize, rParent.iBlockSize ); | ||
392 | while( aBlocks.getSize() > iNewBlocks ) | 559 | while( aBlocks.getSize() > iNewBlocks ) |
393 | { | 560 | { |
394 | rParent.releaseBlock( aBlocks.last(), false ); | 561 | rParent.releaseBlock( aBlocks.last(), false ); |
@@ -399,7 +566,6 @@ void Bu::Myriad::Stream::setSize( int32_t iNewSize ) | |||
399 | else if( iNewSize > iSize ) | 566 | else if( iNewSize > iSize ) |
400 | { | 567 | { |
401 | // Grow it | 568 | // Grow it |
402 | int iNewBlocks = Bu::blkDiv( iNewSize, rParent.iBlockSize ); | ||
403 | while( aBlocks.getSize() < iNewBlocks ) | 569 | while( aBlocks.getSize() < iNewBlocks ) |
404 | { | 570 | { |
405 | aBlocks.append( rParent.allocateBlock() ); | 571 | aBlocks.append( rParent.allocateBlock() ); |
@@ -438,6 +604,10 @@ int32_t Bu::Myriad::Stream::write( int32_t iStart, const void *pTarget, | |||
438 | while( iSize > 0 ) | 604 | while( iSize > 0 ) |
439 | { | 605 | { |
440 | int32_t iBlockIdx = iStart/rParent.iBlockSize; | 606 | int32_t iBlockIdx = iStart/rParent.iBlockSize; |
607 | while( iBlockIdx >= aBlocks.getSize() ) | ||
608 | { | ||
609 | aBlocks.append( rParent.allocateBlock() ); | ||
610 | } | ||
441 | int32_t iBlock = aBlocks[iBlockIdx]; | 611 | int32_t iBlock = aBlocks[iBlockIdx]; |
442 | int32_t iChunkWrite = rParent.blockWrite( | 612 | int32_t iChunkWrite = rParent.blockWrite( |
443 | iBlock, iStart%rParent.iBlockSize, pTarget, iSize | 613 | iBlock, iStart%rParent.iBlockSize, pTarget, iSize |
@@ -449,6 +619,8 @@ int32_t Bu::Myriad::Stream::write( int32_t iStart, const void *pTarget, | |||
449 | reinterpret_cast<ptrdiff_t &>(pTarget) += iChunkWrite; | 619 | reinterpret_cast<ptrdiff_t &>(pTarget) += iChunkWrite; |
450 | iSize -= iChunkWrite; | 620 | iSize -= iChunkWrite; |
451 | } | 621 | } |
622 | if( this->iSize < iStart ) | ||
623 | this->iSize = iStart; | ||
452 | 624 | ||
453 | return iWrite; | 625 | return iWrite; |
454 | } | 626 | } |
@@ -460,6 +632,12 @@ Bu::String Bu::Myriad::Stream::getLocation() const | |||
460 | .arg( rParent.getLocation() ).arg( iStream ); | 632 | .arg( rParent.getLocation() ).arg( iStream ); |
461 | } | 633 | } |
462 | 634 | ||
635 | Bu::Array<int32_t> Bu::Myriad::Stream::getBlockList() const | ||
636 | { | ||
637 | Bu::MutexLocker l( mAccess ); | ||
638 | return aBlocks.clone(); | ||
639 | } | ||
640 | |||
463 | void Bu::Myriad::Stream::open() | 641 | void Bu::Myriad::Stream::open() |
464 | { | 642 | { |
465 | Bu::MutexLocker l( mAccess ); | 643 | Bu::MutexLocker l( mAccess ); |