/*
 * depot.c
 * Copyright (C) 1998-2000 A.J. van Os; Released under GPL
 *
 * Description:
 * Functions to compute the depot offset
 */

#include "antiword.h"

#define SIZE_RATIO	(BIG_BLOCK_SIZE/SMALL_BLOCK_SIZE)

static long	*alSmallBlockList = NULL;
static int	iSmallBlockListLen = 0;


/*
 * vDestroySmallBlockList - destroy the small block list
 */
void
vDestroySmallBlockList(void)
{
	DBG_MSG("vDestroySmallBlockList");

	alSmallBlockList = xfree(alSmallBlockList);
	iSmallBlockListLen = 0;
} /* end of vDestroySmalBlockList */

/*
 * vCreateSmallBlockList - create the small block list
 *
 * returns: TRUE when successful, otherwise FALSE
 */
BOOL
bCreateSmallBlockList(long lStartblock, const long *alBBD, size_t tBBDLen)
{
	long	lTmp;
	size_t	tSize;
	int	iIndex;

	fail(alSmallBlockList != NULL || iSmallBlockListLen != 0);
	fail(lStartblock < 0 && lStartblock != END_OF_CHAIN);
	fail(alBBD == NULL || tBBDLen == 0);

	/* Find the length of the small block list */
	for (iSmallBlockListLen = 0, lTmp = lStartblock;
	     iSmallBlockListLen < (int)tBBDLen && lTmp != END_OF_CHAIN;
	     iSmallBlockListLen++, lTmp = alBBD[lTmp]) {
		if (lTmp < 0 || lTmp >= (long)tBBDLen) {
			werr(1, "The Big Block Depot is corrupt");
		}
	}
	DBG_DEC(iSmallBlockListLen);

	if (iSmallBlockListLen <= 0) {
		/* There is no small block list */
		fail(lStartblock != END_OF_CHAIN);
		alSmallBlockList = NULL;
		iSmallBlockListLen = 0;
		return TRUE;
	}

	/* Create the small block list */
	tSize = (size_t)iSmallBlockListLen * sizeof(long);
	alSmallBlockList = xmalloc(tSize);
	for (iIndex = 0, lTmp = lStartblock;
	     iIndex < (int)tBBDLen && lTmp != END_OF_CHAIN;
	     iIndex++, lTmp = alBBD[lTmp]) {
		if (lTmp < 0 || lTmp >= (long)tBBDLen) {
			werr(1, "The Big Block Depot is corrupt");
		}
		alSmallBlockList[iIndex] = lTmp;
		NO_DBG_DEC(alSmallBlockList[iIndex]);
	}
	return TRUE;
} /* end of bCreateSmallBlockList */

/*
 * lDepotOffset - get the depot offset the block list
 */
long
lDepotOffset(long lIndex, size_t tBlockSize)
{
	int	iTmp1, iTmp2;

	fail(lIndex < 0);

	switch (tBlockSize) {
	case BIG_BLOCK_SIZE:
		return (lIndex + 1) * BIG_BLOCK_SIZE;
	case SMALL_BLOCK_SIZE:
		iTmp1 = (int)(lIndex / SIZE_RATIO);
		iTmp2 = (int)(lIndex % SIZE_RATIO);
		if (alSmallBlockList == NULL ||
		    iSmallBlockListLen <= iTmp1) {
			return 0;
		}
		return ((alSmallBlockList[iTmp1] + 1) * SIZE_RATIO +
				iTmp2) * SMALL_BLOCK_SIZE;
	default:
		return 0;
	}
} /* end of lDepotOffset */
