/* This file is part of 'minixfs' Copyright 1991,1992,1993 S.N.Henson */

/* Bitmap handling stuff */

#include "minixfs.h"
#include "proto.h"
#include "global.h"

long count_bits(buf,num)
ushort *buf;
long num;
{
	long i,count;
	count = 0;
	for(i = 0; i < num/16; i++) {
		if(buf[i]==65535U)count+=16;
		else count+=bitcount(buf[i]);
	}
	count += bitcount((ushort)(buf[num/16] & (( 1l<< (num%16)) -1l)));
	return(count);
}

int bitcount(wrd)
unsigned int wrd;
{
	ushort i;
	int count;

	count=0;
	for (i = 1; i != 0; i <<= 1)
	  if (wrd & i)
	    count++;	
	return(count);
}

long alloc_zone(drive)
int drive;
{
	long save;

	super_info *psblk=super_ptr[drive];

	if( !(save=alloc_bit(psblk->zbitmap,psblk->sblk.s_zones
			-psblk->sblk.s_firstdatazn+1,psblk->zlast)) )
		return 0;
	psblk->zdirty=1; /* Mark zone bitmap as dirty */
	if(save>psblk->zlast)psblk->zlast=save;
	return(save+psblk->sblk.s_firstdatazn-1);
}

unshort alloc_inode(drive)
int drive;
{
	ushort save;	
	super_info *psblk=super_ptr[drive];

	if(!(save=alloc_bit(psblk->ibitmap,psblk->sblk.s_ninodes+1l,
								psblk->ilast)))
		return 0;
	psblk->idirty=1;	/* Mark inode bitmap as dirty */
	if(save>psblk->ilast)psblk->ilast=save;
	return(save);
}

/* Release a zone */
int free_zone(zone,drive)
long zone;
int drive;
{
	super_info *psblk=super_ptr[drive];
	long save,ret;
	save=zone+1-psblk->sblk.s_firstdatazn;
	ret=free_bit(psblk->zbitmap,save);
	psblk->zdirty=1; /* Mark zone bitmap as dirty */
	if(save<psblk->zlast)psblk->zlast=save;
	if(!ret) ALERT("Drive %d zone %ld freeing already free zone !",drive,zone);
	return(ret);
}

/* Release an inode */
int free_inode(inum,drive)
unsigned inum;
int drive;
{
	long ret;
	super_info *psblk=super_ptr[drive];
	ret=free_bit(psblk->ibitmap,inum);
	if(inum<psblk->ilast)psblk->ilast=inum;
	psblk->idirty=1;	/* Mark inode bitmap as dirty */
	if(!ret) ALERT("Drive %d inode %d , freeing already free inode!",drive,inum);
	return(ret);
}


/* This routine is used for allocating both free inodes and free zones 
 * Search a bitmap for a zero , then return its bit number and change it
 * to a one ...... but without exceeding 'num' bits 
 */

long alloc_bit(buf,num,last)
ushort *buf;
long num,last;
{
	long i,j,k;

	k=1;
	for(i=last>>4;(i<=(num>>4)) && (buf[i]==65535U);i++)
		;
	if( i > (num>>4) )
		return(0);
	else {
		for(j=0;j<16;j++) {
			if(!(buf[i] & k)) {
				long free;
				free=i*16+j;
				if(free>=num)return 0;
				buf[i]|=k;
				return(free);
			}
			k<<=1;
		}
	}
	ALERT("minixfs: alloc_bit: This can't happen !");
	return 0;
}

/* zero a bit of a bitmap return 0 if already zero */

long free_bit(buf,bitnum)
ushort *buf;
long bitnum;
{
	register long index=bitnum>>4;
	register ushort bit = 1 << (bitnum & 15);
	long ret;

	ret=buf[index] & bit;
	buf[index]&= ~bit;
	return(ret);
}

