summaryrefslogtreecommitdiff
path: root/src/stable/myriad.cpp
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2012-04-13 23:34:27 +0000
committerMike Buland <eichlan@xagasoft.com>2012-04-13 23:34:27 +0000
commit91f9d6e8b371f339dbcc16541054f9cb371d0ec9 (patch)
tree406ce502763a58a8badea89ad35ec554a6c1a27b /src/stable/myriad.cpp
parent4c86d59be19a5cb64e1eb98504ab5a844a042977 (diff)
downloadlibbu++-91f9d6e8b371f339dbcc16541054f9cb371d0ec9.tar.gz
libbu++-91f9d6e8b371f339dbcc16541054f9cb371d0ec9.tar.bz2
libbu++-91f9d6e8b371f339dbcc16541054f9cb371d0ec9.tar.xz
libbu++-91f9d6e8b371f339dbcc16541054f9cb371d0ec9.zip
Myriad is actually fine, I double checked it for cross-platformed-ness. It
doesn't yet normalize the endian-ness, and I guess at this point to maintain compatibility I'll have to make it a little endian format. I would still like to add better thread-safety to it, but that's about it.
Diffstat (limited to 'src/stable/myriad.cpp')
-rw-r--r--src/stable/myriad.cpp663
1 files changed, 663 insertions, 0 deletions
diff --git a/src/stable/myriad.cpp b/src/stable/myriad.cpp
new file mode 100644
index 0000000..de44930
--- /dev/null
+++ b/src/stable/myriad.cpp
@@ -0,0 +1,663 @@
1/*
2 * Copyright (C) 2007-2011 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#include "bu/myriad.h"
9#include "bu/stream.h"
10#include "bu/myriadstream.h"
11#include <stdio.h>
12
13#include "bu/sio.h"
14using Bu::sio;
15using Bu::Fmt;
16
17#define Myriad_MAGIC_CODE ((unsigned char *)"\x0a\xd3\xfa\x84")
18
19namespace Bu
20{
21 subExceptionDef( MyriadException )
22 template<typename t> t blkDiv( t total, t block ) {
23 return (total/block)+((total%block==0)?(0):(1));
24 }
25}
26
27Bu::Myriad::Myriad( Bu::Stream &sStore, int iBlockSize, int iPreallocate ) :
28 sStore( sStore ),
29 iBlockSize( iBlockSize ),
30 iBlocks( 0 ),
31 iUsed( 0 ),
32 bHeaderChanged( false )
33{
34 try
35 {
36 initialize();
37 }
38 catch( Bu::MyriadException &e )
39 {
40 if( e.getErrorCode() == MyriadException::emptyStream )
41 {
42 initialize( iBlockSize, iPreallocate );
43 }
44 else
45 {
46 throw;
47 }
48 }
49}
50
51Bu::Myriad::~Myriad()
52{
53 if( !hActiveBlocks.isEmpty() )
54 {
55 sio << "Bu::Myriad::~Myriad(): Error: There are "
56 << hActiveBlocks.getSize() << " unsynced blocks!" << sio.nl;
57 }
58 sync();
59
60 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
61 {
62 delete *i;
63 }
64}
65
66void Bu::Myriad::sync()
67{
68 updateHeader();
69
70 for( BlockHash::iterator i = hActiveBlocks.begin(); i; i++ )
71 {
72 if( (*i)->bChanged )
73 {
74 syncBlock( *i );
75 }
76 }
77}
78
79void Bu::Myriad::initialize()
80{
81 sStore.setPosEnd( 0 );
82 int iSize = sStore.tell();
83 sStore.setPos( 0 );
84
85 unsigned char buf[4];
86 if( sStore.read( buf, 4 ) < 4 )
87 throw MyriadException( MyriadException::emptyStream,
88 "Input stream appears to be empty.");
89 if( memcmp( buf, Myriad_MAGIC_CODE, 4 ) )
90 {
91 throw MyriadException( MyriadException::invalidFormat,
92 "Stream does not appear to be a valid Myriad format.");
93 }
94 sStore.read( buf, 2 );
95 if( buf[0] != 1 )
96 throw MyriadException( MyriadException::badVersion,
97 "We can only handle version 1 for now.");
98 if( buf[1] != 32 )
99 throw MyriadException( MyriadException::invalidWordSize,
100 "We can only handle 32-bit words at the moment.");
101 sStore.read( &iBlockSize, 4 );
102 int iStreams;
103 sStore.read( &iStreams, 4 );
104
105 iBlocks = iSize/iBlockSize;
106 //sio << "Myriad: iSize=" << iSize << ", iBlockSize=" << iBlockSize
107 // << ", iBlocks=" << iBlocks << ", iStreams=" << iStreams << sio.nl;
108
109 int iHeaderSize = 14 + 8 + 4;
110 int iHeaderBlocks = 0; //blkDiv( iHeaderSize+4, iBlockSize );
111
112 while( iHeaderSize > iHeaderBlocks*iBlockSize )
113 {
114 iHeaderBlocks = blkDiv( iHeaderSize+4, iBlockSize );
115 iHeaderSize = 14 + 8 + 4*iHeaderBlocks;
116 }
117
118 //sio << "Myriad: iHeaderSize=" << iHeaderSize
119 // << ", iHeaderBlocks=" << iHeaderBlocks << sio.nl;
120
121 Stream *pFakeHdr = new Stream;
122 pFakeHdr->iId = 0;
123 pFakeHdr->iSize = iHeaderSize;
124 for( int j = 0; j < iHeaderBlocks; j++ )
125 {
126 pFakeHdr->aBlocks.append( j );
127 }
128
129 bsBlockUsed.setSize( iBlocks, true );
130
131// bool bCanSkip = false; // Can skip around, post initial header stream i/o
132 MyriadStream *pIn = new MyriadStream( *this, pFakeHdr );
133 pIn->setPos( sStore.tell() );
134 for( int j = 0; j < iStreams; j++ )
135 {
136 aStreams.append( new Stream() );
137 Stream &s = *aStreams[j];
138 pIn->read( &s.iId, 4 );
139 pIn->read( &s.iSize, 4 );
140 int iSBlocks = blkDiv(s.iSize, iBlockSize);
141 // sio << "Myriad: - Stream::iId=" << s.iId
142 // << ", Stream::iSize=" << s.iSize
143 // << ", Stream::aBlocks=" << iSBlocks
144 // << ", pIn->tell()=" << pIn->tell() << sio.nl;
145 for( int k = 0; k < iSBlocks; k++ )
146 {
147 int iBId;
148 pIn->read( &iBId, 4 );
149 // sio << "Myriad: - iBId=" << iBId
150 // << ", iStartPos=" << iBId*iBlockSize
151 // << ", pIn->tell()=" << pIn->tell() << sio.nl;
152 s.aBlocks.append( iBId );
153 bsBlockUsed.setBit( iBId );
154 iUsed++;
155 if( (j == 0 && k == iHeaderBlocks-1) )
156 {
157 // sio << "Myriad: - End of prepartition, unlocking skipping."
158 // << sio.nl;
159// bCanSkip = true;
160 MyriadStream *pTmp = new MyriadStream( *this, aStreams[0] );
161 // sio << "Myriad - Position = " << pIn->tell() << sio.nl;
162 pTmp->setPos( pIn->tell() );
163 delete pIn;
164 delete pFakeHdr;
165 pIn = pTmp;
166 }
167 }
168 }
169 delete pIn;
170
171 //sio << "Myriad: Blocks used: " << bsBlockUsed.toString() << sio.nl;
172}
173
174void Bu::Myriad::initialize( int iBlockSize, int iPreAllocate )
175{
176 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
177 {
178 delete *i;
179 }
180 aStreams.clear();
181 iUsed = 0;
182
183 int iHeaderSize = 14 + 8 + 4;
184 int iHeaderBlocks = 0; //blkDiv( iHeaderSize+4, iBlockSize );
185 char cBuf = 1;
186 int iBuf = 0;
187
188 Stream *pStr = new Stream;
189 pStr->iId = 0;
190
191 while( iHeaderSize > iHeaderBlocks*iBlockSize )
192 {
193 iHeaderBlocks = blkDiv( iHeaderSize+4, iBlockSize );
194 iHeaderSize = 14 + 8 + 4*iHeaderBlocks;
195 }
196
197 iPreAllocate += iHeaderBlocks;
198
199 //sio << "Myriad: iHeaderSize=" << iHeaderSize << ", iBlockSize="
200 // << iBlockSize << ", iHeaderBlocks=" << iHeaderBlocks << sio.nl;
201
202 bsBlockUsed.setSize( iPreAllocate, true );
203 iUsed++;
204
205 char *pBlock = new char[iBlockSize];
206 memset( pBlock, 0, iBlockSize );
207 for( int j = 0; j < iPreAllocate; j++ )
208 {
209 sStore.write( pBlock, iBlockSize );
210 }
211 delete[] (char *)pBlock;
212
213 sStore.setPos( 0 );
214
215 // Magic number
216 sStore.write( Myriad_MAGIC_CODE, 4 );
217
218 // Version (0)
219 sStore.write( &cBuf, 1 );
220
221 // Bits per int
222 cBuf = 32;
223 sStore.write( &cBuf, 1 );
224
225 // The size of each block
226 sStore.write( &iBlockSize, 4 );
227
228 iBuf = 1;
229 // The number of streams
230 sStore.write( &iBuf, 4 );
231
232 // Stream header
233 iBuf = 0;
234 sStore.write( &iBuf, 4 );
235 sStore.write( &iHeaderSize, 4 );
236 for( iBuf = 0; iBuf < iHeaderBlocks; iBuf++ )
237 {
238 sStore.write( &iBuf, 4 );
239 }
240
241 this->iBlockSize = iBlockSize;
242 this->iBlocks = iPreAllocate;
243
244 pStr->iSize = sStore.tell();
245// sio << "Myriad: Actual end of header stream = " << pStr->iSize << sio.nl;
246
247 pStr->iSize = iHeaderSize;
248 for( int j = 0; j < iHeaderBlocks; j++ )
249 {
250 pStr->aBlocks.append( j );
251 bsBlockUsed.setBit( j );
252 iUsed++;
253 }
254
255 aStreams.append( pStr );
256
257 //sio << bsBlockUsed.toString() << " - " << pStr->aBlocks << sio.nl;
258
259 bHeaderChanged = true;
260 //hStreams.insert( 0, BlockArray( 0 ) );
261}
262
263void Bu::Myriad::updateHeader()
264{
265 if( bHeaderChanged == false )
266 return;
267 if( !sStore.canWrite() )
268 return;
269
270 char cBuf;
271 int iBuf;
272
273 //for( StreamArray::iterator i = aStreams.begin(); i; i++ )
274 //{
275 // sio << "Myriad: Stream " << Fmt(4) << (*i)->iId << ": " << (*i)->aBlocks << sio.nl;
276 //}
277
278 // Compute the new size of the header.
279 int iHeaderSize = 14 + 8*aStreams.getSize();
280// sio << "Myriad: updateHeader: aStreams.getSize() = " << aStreams.getSize()
281// << sio.nl;
282 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
283 {
284 iHeaderSize += 4*(*i)->aBlocks.getSize();
285// sio << "Myriad: updateHeader: (*i)->aBlocks.getSize() = "
286// << (*i)->aBlocks.getSize() << sio.nl;
287 }
288 int iNewBlocks = blkDiv( iHeaderSize, iBlockSize );
289 while( iNewBlocks > aStreams[0]->aBlocks.getSize() )
290 {
291 int iBlock = findEmptyBlock();
292// sio << "Myriad: updateHeader: Appending block " << iBlock
293// << " to header." << sio.nl;
294 aStreams[0]->aBlocks.append( iBlock );
295 bsBlockUsed.setBit( iBlock );
296 iUsed++;
297 iHeaderSize += 4;
298 iNewBlocks = blkDiv( iHeaderSize, iBlockSize );
299 }
300 aStreams[0]->iSize = iHeaderSize;
301// sio << "Myriad: updateHeader: iHeaderSize=" << iHeaderSize
302// << ", iNewBlocks=" << iNewBlocks << ", curBlocks="
303// << aStreams[0]->aBlocks.getSize() << sio.nl;
304
305 MyriadStream sHdr( *this, aStreams[0] );
306 sHdr.write( Myriad_MAGIC_CODE, 4 );
307
308 // Version (1)
309 cBuf = 1;
310 sHdr.write( &cBuf, 1 );
311
312 // Bits per int
313 cBuf = 32;
314 sHdr.write( &cBuf, 1 );
315
316 // The size of each block
317 sHdr.write( &iBlockSize, 4 );
318
319 iBuf = aStreams.getSize();
320 // The number of streams
321 sHdr.write( &iBuf, 4 );
322
323 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
324 {
325 sHdr.write( &(*i)->iId, 4 );
326 sHdr.write( &(*i)->iSize, 4 );
327 int iUsedBlocks = blkDiv( (*i)->iSize, iBlockSize );
328// for( BlockArray::iterator j = (*i)->aBlocks.begin(); j; j++ )
329 for( int j = 0; j < iUsedBlocks; j++ )
330 {
331 sHdr.write( &(*i)->aBlocks[j], 4 );
332 }
333 }
334
335 bHeaderChanged = false;
336}
337
338int Bu::Myriad::createStream( int iPreAllocate )
339{
340 Stream *pStr = new Stream();
341 pStr->iId = aStreams.last()->iId+1;
342 //sio << "Myriad: New stream id=" << pStr->iId << ", iPreAllocate="
343 // << iPreAllocate << sio.nl;
344 pStr->iSize = 0;
345 aStreams.append( pStr );
346
347 for( int j = 0; j < iPreAllocate; j++ )
348 {
349 int iFreeBlock = findEmptyBlock();
350// sio << "Myriad: Adding block " << iFreeBlock << sio.nl;
351 pStr->aBlocks.append( iFreeBlock );
352 bsBlockUsed.setBit( iFreeBlock );
353 iUsed++;
354 }
355
356 bHeaderChanged = true;
357
358 return pStr->iId;
359}
360
361int Bu::Myriad::createStreamWithId( int iId, int iPreAllocate )
362{
363 try
364 {
365 findStream( iId );
366 throw MyriadException( MyriadException::streamExists,
367 "There is already a stream with the given id.");
368 }
369 catch( MyriadException &e )
370 {
371 Stream *pStr = new Stream();
372 pStr->iId = iId;
373 //sio << "Myriad: New stream id=" << pStr->iId << ", iPreAllocate="
374 // << iPreAllocate << sio.nl;
375 pStr->iSize = 0;
376 if( aStreams.last()->iId < iId )
377 {
378 aStreams.append( pStr );
379 }
380 else
381 {
382 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
383 {
384 if( (*i)->iId > iId )
385 {
386 aStreams.insert( i, pStr );
387 break;
388 }
389 }
390 }
391
392 for( int j = 0; j < iPreAllocate; j++ )
393 {
394 int iFreeBlock = findEmptyBlock();
395 // sio << "Myriad: Adding block " << iFreeBlock << sio.nl;
396 pStr->aBlocks.append( iFreeBlock );
397 bsBlockUsed.setBit( iFreeBlock );
398 iUsed++;
399 }
400
401 bHeaderChanged = true;
402
403 return pStr->iId;
404 }
405}
406
407int Bu::Myriad::findEmptyBlock()
408{
409 bHeaderChanged = true;
410
411 for( int j = 0; j < bsBlockUsed.getSize(); j++ )
412 {
413 if( bsBlockUsed.getBit( j ) == false )
414 return j;
415 }
416// sio << "Myriad: findEmptyBlock(): No empty blocks, adding new one." << sio.nl;
417
418 bsBlockUsed.setSize( bsBlockUsed.getSize()+1, false );
419 /*
420 sStore.setPos( iBlockSize*iBlocks );
421
422 char *pBlock = new char[iBlockSize];
423 memset( pBlock, 0, iBlockSize );
424 sStore.write( pBlock, iBlockSize );
425 delete[] pBlock;
426 */
427
428 sStore.setSize( (iBlocks+1)*iBlockSize );
429
430 return iBlocks++;
431}
432
433void Bu::Myriad::deleteStream( int iId )
434{
435 if( iId < 0 )
436 throw MyriadException( MyriadException::invalidStreamId,
437 "Invalid stream id.");
438 if( iId == 0 )
439 throw MyriadException( MyriadException::protectedStream,
440 "You cannot delete stream zero, it is protected.");
441 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
442 {
443 if( (*i)->iId == iId )
444 {
445 Stream *pStream = *i;
446 for( BlockArray::iterator j = pStream->aBlocks.begin(); j; j++ )
447 {
448 bsBlockUsed.setBit( *j, false );
449 iUsed--;
450 }
451 aStreams.erase( i );
452 bHeaderChanged = true;
453 delete pStream;
454 return;
455 }
456 }
457}
458
459Bu::Array<int> Bu::Myriad::getStreamIds()
460{
461 Bu::Array<int> aRet( aStreams.getSize() );
462 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
463 {
464 aRet.append( (*i)->iId );
465 }
466
467 return aRet;
468}
469
470int Bu::Myriad::getStreamSize( int iId )
471{
472 return findStream( iId )->iSize;
473}
474
475bool Bu::Myriad::hasStream( int iId )
476{
477 try
478 {
479 findStream( iId );
480 return true;
481 }catch(...)
482 {
483 return false;
484 }
485}
486
487Bu::MyriadStream Bu::Myriad::openStream( int iId )
488{
489 //sio << "Myriad: Request to open stream: " << iId << sio.nl;
490 return MyriadStream( *this, findStream( iId ) );
491}
492
493int Bu::Myriad::getNumStreams()
494{
495 return aStreams.getSize();
496}
497
498int Bu::Myriad::getBlockSize()
499{
500 return iBlockSize;
501}
502
503int Bu::Myriad::getNumBlocks()
504{
505 return iBlocks;
506}
507
508int Bu::Myriad::getNumUsedBlocks()
509{
510 return iUsed;
511}
512
513int Bu::Myriad::getTotalUsedBytes()
514{
515 int iTotalSize = 0;
516 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
517 {
518 iTotalSize += (*i)->iSize;
519 }
520 return iTotalSize;
521}
522
523int Bu::Myriad::getTotalUnusedBytes()
524{
525 int iTotalSize = (iBlocks-iUsed)*iBlockSize;
526 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
527 {
528 iTotalSize += iBlockSize - ((*i)->iSize%iBlockSize);
529 }
530 return iTotalSize;
531}
532
533int Bu::Myriad::getTotalUnusedBytes( int iFakeBlockSize )
534{
535 int iTotalSize = (iBlocks-iUsed)*iFakeBlockSize;
536 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
537 {
538 iTotalSize += iFakeBlockSize - ((*i)->iSize%iFakeBlockSize);
539 }
540 return iTotalSize;
541}
542
543Bu::Myriad::Stream *Bu::Myriad::findStream( int iId )
544{
545 for( StreamArray::iterator i = aStreams.begin(); i; i++ )
546 {
547 if( (*i)->iId == iId )
548 return *i;
549 }
550
551 throw MyriadException( MyriadException::noSuchStream,
552 "The requested stream doesn't exist and cannot be opened." );
553
554 return NULL;
555}
556
557Bu::Myriad::Block *Bu::Myriad::getBlock( int iBlock )
558{
559// sio << "Myriad: Reading block " << iBlock << ", bytes "
560// << iBlockSize*iBlock << "-" << iBlockSize*(iBlock+1) << sio.nl;
561 Block *pBlock = new Block;
562 pBlock->pData = new char[iBlockSize];
563 sStore.setPos( iBlockSize * iBlock );
564 sStore.read( pBlock->pData, iBlockSize );
565 pBlock->bChanged = false;
566 pBlock->iBlockIndex = iBlock;
567
568 hActiveBlocks.insert( iBlock, pBlock );
569
570 return pBlock;
571}
572
573void Bu::Myriad::releaseBlock( Bu::Myriad::Block *pBlock )
574{
575 if( pBlock == NULL )
576 return;
577// sio << "Myriad: Releasing block " << pBlock->iBlockIndex << sio.nl;
578 syncBlock( pBlock );
579 hActiveBlocks.erase( pBlock->iBlockIndex );
580 delete[] pBlock->pData;
581 delete pBlock;
582}
583
584void Bu::Myriad::syncBlock( Block *pBlock )
585{
586 if( pBlock->bChanged )
587 {
588// sio << "Myriad: - Block changed, writing back to stream." << sio.nl;
589 sStore.setPos( iBlockSize * pBlock->iBlockIndex );
590 sStore.write( pBlock->pData, iBlockSize );
591 pBlock->bChanged = false;
592 }
593}
594
595int Bu::Myriad::streamAddBlock( Stream *pStream )
596{
597 int iBlock = findEmptyBlock();
598 pStream->aBlocks.append( iBlock );
599 bsBlockUsed.setBit( iBlock );
600 bHeaderChanged = true;
601 iUsed++;
602 return iBlock;
603}
604
605void Bu::Myriad::setStreamSize( Stream *pStream, long iSize )
606{
607 if( pStream->iSize == iSize )
608 {
609 return;
610 }
611 else if( pStream->iSize > iSize )
612 {
613 // Shrink
614 for( int iNewSize = pStream->aBlocks.getSize()*iBlockSize;
615 iNewSize-iBlockSize > iSize; iNewSize -= iBlockSize )
616 {
617 if( bsBlockUsed.getBit( pStream->aBlocks.last() ) )
618 iUsed--;
619 bsBlockUsed.setBit( pStream->aBlocks.last(), false );
620 pStream->aBlocks.eraseLast();
621 }
622 pStream->iSize = iSize;
623 bHeaderChanged = true;
624 }
625 else
626 {
627 // Grow
628 for( int iNewSize = pStream->aBlocks.getSize()*iBlockSize;
629 iNewSize < iSize; iNewSize += iBlockSize )
630 {
631 streamAddBlock( pStream );
632 }
633 pStream->iSize = iSize;
634 bHeaderChanged = true;
635 }
636}
637
638void Bu::Myriad::headerChanged()
639{
640 bHeaderChanged = true;
641}
642
643bool Bu::Myriad::isMyriad( Bu::Stream &sStore )
644{
645 sStore.setPos( 0 );
646
647 unsigned char buf[4];
648 if( sStore.read( buf, 4 ) < 4 )
649 throw MyriadException( MyriadException::emptyStream,
650 "Input stream appears to be empty.");
651 sStore.setPos( 0 );
652 if( memcmp( buf, Myriad_MAGIC_CODE, 4 ) )
653 {
654 return false;
655 }
656 return true;
657}
658
659const Bu::BitString &Bu::Myriad::getBlocksUsed() const
660{
661 return bsBlockUsed;
662}
663