// distribution boxbackup-0.10 (svn version: 494)
//  
// Copyright (c) 2003 - 2006
//      Ben Summers and contributors.  All rights reserved.
//  
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must 
//    display the following acknowledgment:
//        This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
//    products derived from this software without specific prior written
//    permission.
// 
// [Where legally impermissible the Authors do not disclaim liability for 
// direct physical injury or death caused solely by defects in the software 
// unless it is modified by a third party.]
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//  
//  
//  
// --------------------------------------------------------------------------
//
// File
//		Name:    BackupStoreCheckData.cpp
//		Purpose: Data handling for store checking
//		Created: 21/4/04
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <stdlib.h>
#include <memory>

#include "BackupStoreCheck.h"
#include "autogen_BackupStoreException.h"

#include "MemLeakFindOn.h"


// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreCheck::FreeInfo()
//		Purpose: Free all the data stored
//		Created: 21/4/04
//
// --------------------------------------------------------------------------
void BackupStoreCheck::FreeInfo()
{
	// Free all the blocks
	for(Info_t::iterator i(mInfo.begin()); i != mInfo.end(); ++i)
	{
		::free(i->second);
	}
	
	// Clear the contents of the map
	mInfo.clear();
	
	// Reset the last ID, just in case
	mpInfoLastBlock = 0;
	mInfoLastBlockEntries = 0;
	mLastIDInInfo = 0;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreCheck::AddID(BackupStoreCheck_ID_t, BackupStoreCheck_ID_t, bool)
//		Purpose: Add an ID to the list
//		Created: 21/4/04
//
// --------------------------------------------------------------------------
void BackupStoreCheck::AddID(BackupStoreCheck_ID_t ID,
	BackupStoreCheck_ID_t Container, BackupStoreCheck_Size_t ObjectSize, bool IsFile)
{
	// Check ID is OK.
	if(ID <= mLastIDInInfo)
	{
		THROW_EXCEPTION(BackupStoreException, InternalAlgorithmErrorCheckIDNotMonotonicallyIncreasing)
	}
	
	// Can this go in the current block?
	if(mpInfoLastBlock == 0 || mInfoLastBlockEntries >= BACKUPSTORECHECK_BLOCK_SIZE)
	{
		// No. Allocate a new one
		IDBlock *pblk = (IDBlock*)::malloc(sizeof(IDBlock));
		if(pblk == 0)
		{
			throw std::bad_alloc();
		}
		// Zero all the flags entries
		for(int z = 0; z < (BACKUPSTORECHECK_BLOCK_SIZE * Flags__NumFlags / Flags__NumItemsPerEntry); ++z)
		{
			pblk->mFlags[z] = 0;
		} 
		// Store in map
		mInfo[ID] = pblk;
		// Allocated and stored OK, setup for use
		mpInfoLastBlock = pblk;
		mInfoLastBlockEntries = 0;
	}
	ASSERT(mpInfoLastBlock != 0 && mInfoLastBlockEntries < BACKUPSTORECHECK_BLOCK_SIZE);
	
	// Add to block
	mpInfoLastBlock->mID[mInfoLastBlockEntries] = ID;
	mpInfoLastBlock->mContainer[mInfoLastBlockEntries] = Container;
	mpInfoLastBlock->mObjectSizeInBlocks[mInfoLastBlockEntries] = ObjectSize;
	SetFlags(mpInfoLastBlock, mInfoLastBlockEntries, IsFile?(0):(Flags_IsDir));
	
	// Increment size
	++mInfoLastBlockEntries;
	
	// Store last ID
	mLastIDInInfo = ID;
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreCheck::LookupID(BackupStoreCheck_ID_t, int32_t
//		Purpose: Look up an ID. Return the block it's in, or zero if not found, and the 
//				 index within that block if the thing is found.
//		Created: 21/4/04
//
// --------------------------------------------------------------------------
BackupStoreCheck::IDBlock *BackupStoreCheck::LookupID(BackupStoreCheck_ID_t ID, int32_t &rIndexOut)
{
	IDBlock *pblock = 0;

	// Find the lower matching block who's first entry is not less than ID
	Info_t::const_iterator ib(mInfo.lower_bound(ID));
	
	// Was there a block
	if(ib == mInfo.end())
	{
		// Block wasn't found... could be in last block
		pblock = mpInfoLastBlock;
	}
	else
	{
		// Found it as first entry?
		if(ib->first == ID)
		{
			rIndexOut = 0;
			return ib->second;
		}
		
		// Go back one block as it's not the first entry in this one
		if(ib == mInfo.begin())
		{
			// Was first block, can't go back
			return 0;
		}
		// Go back...
		--ib;

		// So, the ID will be in this block, if it's in anything
		pblock = ib->second;
	}

	ASSERT(pblock != 0);
	if(pblock == 0) return 0;
	
	// How many entries are there in the block
	int32_t bentries = (pblock == mpInfoLastBlock)?mInfoLastBlockEntries:BACKUPSTORECHECK_BLOCK_SIZE;
	
	// Do binary search within block
	int high = bentries;
	int low = -1;
	while(high - low > 1)
	{
		int i = (high + low) / 2;
		if(ID <= pblock->mID[i])
		{
			high = i;
		}
		else
		{
			low = i;
		}
	}
	if(ID == pblock->mID[high])
	{
		// Found
		rIndexOut = high;
		return pblock;
	}

	// Not found
	return 0;
}


#ifndef NDEBUG
// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreCheck::DumpObjectInfo()
//		Purpose: Debug only. Trace out all object info.
//		Created: 22/4/04
//
// --------------------------------------------------------------------------
void BackupStoreCheck::DumpObjectInfo()
{
	for(Info_t::const_iterator i(mInfo.begin()); i != mInfo.end(); ++i)
	{
		IDBlock *pblock = i->second;
		int32_t bentries = (pblock == mpInfoLastBlock)?mInfoLastBlockEntries:BACKUPSTORECHECK_BLOCK_SIZE;
		TRACE2("BLOCK @ 0x%08x, %d entries\n", pblock, bentries);
		
		for(int e = 0; e < bentries; ++e)
		{
			uint8_t flags = GetFlags(pblock, e);
			TRACE4("id %llx, c %llx, %s, %s\n",
				pblock->mID[e], pblock->mContainer[e],
				(flags & Flags_IsDir)?"dir":"file",
				(flags & Flags_IsContained)?"contained":"unattached");
		}
	}
}
#endif



syntax highlighted by Code2HTML, v. 0.9.1