#ifndef _SPADFS_SFSAPAGE_H
#define _SPADFS_SFSAPAGE_H

#include "STRUCT.H"

extern char APAGE_ERROR[];
int SPAD_APAGE_FREE(struct apage_head *a, __d_off off, __u32 len);
int SPAD_APAGE_MAKE(struct apage_head *a, unsigned n_entries, unsigned sectors_per_block_bits);

static __finline__ __u32 max(__u32 a, __u32 b)
{
	return a < b ? b : a;
}

/* note: APTR_ALIGN(returned_blk->next) MUST be valid (< depth).
   if not, check all places that call this function */
static __finline__ int SPAD_APAGE_FIND_BLOCK_BEFORE(struct apage_head *a, __d_off o, int depth)
{
#define aa(i) (*(struct aentry *)((char *)a + (i)))
	int i;
	int rlimit;
	int st = 0;
	int nx;
	__d_off mx = 0;
	i = 4 * sizeof(struct aentry);
	do {
		/* one cacheline ... 64 bytes			^^^^^ make it
		   alias after two cycles --- do not pollute cache much */
		int j;
		__PREFETCHT0((char *)a + i + 128 * sizeof(struct aentry));
		__PREFETCHT0_IF32((char *)a + i + 128 * sizeof(struct aentry) + 32);
		for (j = 0; j < 4; j++) {
			if (aa(i).len && aa(i).start <= o && __unlikely(aa(i).start >= mx)) mx = aa(i).start, st = i;
			i += sizeof(struct aentry);
		}
	} while ((int)(i += 124 * sizeof(struct aentry)) <= (int)(depth - (4 + 128) * sizeof(struct aentry)));
	rlimit = depth;
	do {
		nx = APTR_ALIGN(aa(st).next);
		if (__unlikely(nx >= depth)) return -1;
		if (__unlikely(!nx) || aa(nx).start > o) return st;
		st = nx;
	} while (__likely(rlimit -= sizeof(struct aentry)));
	return -1;
#undef aa
}

static __finline__ int SPAD_DELETE_BLOCK(struct apage_head *a, int blk, int depth)
{
#define aa(i) (*(struct aentry *)((char *)a + (i)))
	int preblk = APTR_ALIGN(aa(blk).prev);
	int postblk = APTR_ALIGN(aa(blk).next);
	if (__unlikely(preblk >= depth) || __unlikely(postblk >= depth)) {
		return -1;
	}
	aa(preblk).next = postblk;
	aa(postblk).prev = preblk;
	aa(blk).start = 0;
	aa(blk).len = 0;
	aa(blk).prev = 0;
	aa(blk).next = a->s.u.l.freelist;
	a->s.u.l.freelist = blk;
	return 0;
#undef aa
}

#endif
