Since you asked …
// only determines appropriate filename and subdir balance; does not
// perform maintainence
wxFileName DirManager::MakeBlockFileName()
{
wxFileName ret;
wxString baseFileName;
unsigned int filenum,midnum,topnum,midkey;
while(1){
/* blockfiles are divided up into heirarchical directories.
Each toplevel directory is represented by "e" + two unique
hexadecimal digits, for a total possible number of 256
toplevels. Each toplevel contains up to 256 subdirs named
"d" + two hex digits. Each subdir contains 'a number' of
files. */
filenum=0;
midnum=0;
topnum=0;
// first action: if there is no available two-level directory in
// the available pool, try to make one
if(dirMidPool.empty()){
// is there a toplevel directory with space for a new subdir?
if(!dirTopPool.empty()){
// there's still a toplevel with room for a subdir
DirHash::iterator i = dirTopPool.begin();
int newcount = 0;
topnum = i->first;
// search for unused midlevels; linear search adequate
// add 32 new topnum/midnum dirs full of prospective filenames to midpool
for(midnum=0;midnum<256;midnum++){
midkey=(topnum<<8)+midnum;
if(BalanceMidAdd(topnum,midkey)){
newcount++;
if(newcount>=32)break;
}
}
if(dirMidPool.empty()){
// all the midlevels in this toplevel are in use yet the
// toplevel claims some are free; this implies multiple
// internal logic faults, but simply giving up and going
// into an infinite loop isn't acceptible. Just in case,
// for some reason, we get here, dynamite this toplevel so
// we don't just fail.
// this is 'wrong', but the best we can do given that
// something else is also wrong. It will contain the
// problem so we can keep going without worry.
dirTopPool.erase(topnum);
dirTopFull[topnum]=256;
}
continue;
}
}
if(dirMidPool.empty()){
// still empty, thus an absurdly large project; all dirs are
// full to 256/256/256; keep working, but fall back to 'big
// filenames' and randomized placement
filenum = rand();
midnum = (int)(256.*rand()/(RAND_MAX+1.));
topnum = (int)(256.*rand()/(RAND_MAX+1.));
midkey=(topnum<<8)+midnum;
}else{
DirHash::iterator i = dirMidPool.begin();
midkey = i->first;
// split the retrieved 16 bit directory key into two 8 bit numbers
topnum = midkey >> 8;
midnum = midkey & 0xff;
filenum = (int)(4096.*rand()/(RAND_MAX+1.));
}
baseFileName.Printf(wxT("e%02x%02x%03x"),topnum,midnum,filenum);
if(blockFileHash.find(baseFileName) == blockFileHash.end()){
// not in the hash, good.
if(AssignFile(ret,baseFileName,TRUE)==FALSE){
// this indicates an on-disk collision, likely due to an
// orphaned blockfile. We should try again, but first
// alert the balancing info there's a phantom file here;
// if the directory is nearly full of orphans we neither
// want performance to suffer nor potentially get into an
// infinite loop if all possible filenames are taken by
// orphans (unlikely but possible)
BalanceFileAdd(midkey);
}else break;
}
}
// FIX-ME: Might we get here without midkey having been set?
BalanceFileAdd(midkey);
return ret;
}
The keys in here seem to be BalanceMidAdd and BalanceFileAdd
int DirManager::BalanceMidAdd(int topnum, int midkey)
{
// enter the midlevel directory if it doesn't exist
if(dirMidPool.find(midkey) == dirMidPool.end() &&
dirMidFull.find(midkey) == dirMidFull.end()){
dirMidPool[midkey]=0;
// increment toplevel directory fill
dirTopPool[topnum]++;
if(dirTopPool[topnum]>=256){
// this toplevel is now full; move it to the full hash
dirTopPool.erase(topnum);
dirTopFull[topnum]=256;
}
return 1;
}
return 0;
}
void DirManager::BalanceFileAdd(int midkey)
{
// increment the midlevel directory usage information
if(dirMidPool.find(midkey) != dirMidPool.end()){
dirMidPool[midkey]++;
if(dirMidPool[midkey]>=256){
// this middir is now full; move it to the full hash
dirMidPool.erase(midkey);
dirMidFull[midkey]=256;
}
}else{
// this case only triggers in absurdly large projects; we still
// need to track directory fill even if we're over 256/256/256
dirMidPool[midkey]++;
}
}
I don’t pretend to understand all of this, but looks like “pools” of available directory/file names are being maintained, as well as “lists” of in-use directory/file names. It also looks like a large part of the code is doing error checking - the meat of the code that actually assigns new directory/file names is pretty compact and simple.
Koz, you really should have a look at http://bugzilla.audacityteam.org/show_bug.cgi?id=20 to see the long discussion on resolving the issue of automatic crash recovery. Download a Mac nightly and use it to record something. Force quit while it’s recording then relaunch and you will get back nearly everything you recorded (the very last blockfile will not have been written [no way to avoid that] so you may be missing up to six seconds at the end). Edit away to your heart’s content then force quit, relaunch and recover and you will get back all your edits.
With the improved crash recovery in 1.3.13 users should never have to look inside the _data folder.
– Bill