/* Copyright (C) 2005 PrimeBase Technologies GmbH
 *
 * PrimeBase XT
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * 2005-05-24	Paul McCullagh
 *
 * H&G2JCtL
 */
#ifndef __xt_cache_h__
#define __xt_cache_h__

//#define XT_USE_MYSYS

#include "filesys_xt.h"
#include "index_xt.h"

struct XTOpenTable;
struct XTIdxReadBuffer;

#ifdef DEBUG
//#define XT_USE_CACHE_DEBUG_SIZES
//#define CHECK_BLOCK_TRAILERS
#endif

#ifdef XT_USE_CACHE_DEBUG_SIZES
#define XT_INDEX_CACHE_SEGMENT_SHIFTS	1
#else
#define XT_INDEX_CACHE_SEGMENT_SHIFTS	3
#endif

#define IDX_CAC_BLOCK_FREE				0
#define IDX_CAC_BLOCK_CLEAN				1
#define IDX_CAC_BLOCK_DIRTY				2				/* On the dirty list! */
#define IDX_CAC_BLOCK_FLUSHING			3				/* This page needs to be written to the index log. */
#define IDX_CAC_BLOCK_LOGGED			4				/* This page needs to be transfered from the index log to the index. */

#define IDX_CAC_MODIFYABLE(x)			((x) == IDX_CAC_BLOCK_CLEAN || (x) == IDX_CAC_BLOCK_DIRTY || (x) == IDX_CAC_BLOCK_LOGGED)
#define IDX_CAC_NOT_FREE(x)				((x) != IDX_CAC_BLOCK_FREE)

#ifdef XT_NO_ATOMICS
#define XT_IPAGE_USE_PTHREAD_RW
#else
#define XT_IPAGE_USE_SPINXSLOCK
#endif

#if defined(XT_IPAGE_USE_PTHREAD_RW)
#define XT_IPAGE_LOCK_TYPE				xt_rwlock_type
#define XT_IPAGE_INIT_LOCK(s, i)		xt_init_rwlock_with_autoname(s, i)
#define XT_IPAGE_FREE_LOCK(s, i)		xt_free_rwlock(i)	
#define XT_IPAGE_READ_LOCK(i)			xt_slock_rwlock_ns(i)
#define XT_IPAGE_WRITE_LOCK(i, s)		xt_xlock_rwlock_ns(i)
#define XT_IPAGE_WRITE_TRY_LOCK(i, s)	xt_xlock_try_rwlock_ns(i)
#define XT_IPAGE_UNLOCK(i, x)			xt_unlock_rwlock_ns(i)
#elif defined(XT_IPAGE_USE_SPINXSLOCK)
#define XT_IPAGE_LOCK_TYPE				XTSpinXSLockRec
#define XT_IPAGE_INIT_LOCK(s, i)		xt_spinxslock_init_with_autoname(s, i)
#define XT_IPAGE_FREE_LOCK(s, i)		xt_spinxslock_free(s, i)	
#define XT_IPAGE_READ_LOCK(i)			xt_spinxslock_slock(i)
#define XT_IPAGE_WRITE_LOCK(i, o)		xt_spinxslock_xlock(i, FALSE, o)
#define XT_IPAGE_WRITE_TRY_LOCK(i, o)	xt_spinxslock_xlock(i, TRUE, o)
#define XT_IPAGE_UNLOCK(i, x)			xt_spinxslock_unlock(i, x)
#else // XT_IPAGE_USE_SKEW_RW
#error Please define the lock type
#endif

enum XTPageLockType { XT_LOCK_READ, XT_LOCK_WRITE, XT_XLOCK_LEAF, XT_XLOCK_DEL_LEAF };
enum XTPageUnlockType { XT_UNLOCK_NONE, XT_UNLOCK_READ, XT_UNLOCK_WRITE, XT_UNLOCK_R_UPDATE, XT_UNLOCK_W_UPDATE };

/* A block is X locked if it is being changed or freed.
 * A block is S locked if it is being read.
 */
typedef struct XTIndBlock {
	xtIndexNodeID		cb_address;						/* The block address. */
	u_int				cb_file_id;						/* The file id of the block. */
	/* This is protected by cs_lock */
	struct XTIndBlock	*cb_next;						/* Pointer to next block on hash list, or next free block on free list. */
	/* This is protected by mi_dirty_lock */
	struct XTIndBlock	*cb_dirty_next;					/* Double link for dirty blocks, next pointer. */
	struct XTIndBlock	*cb_dirty_prev;					/* Double link for dirty blocks, previous pointer. */
	/* This is protected by cg_lock */
	xtWord4				cb_ru_time;						/* If this is in the top 1/4 don't change position in MRU list. */
	struct XTIndBlock	*cb_mr_used;					/* More recently used blocks. */
	struct XTIndBlock	*cb_lr_used;					/* Less recently used blocks. */
	/* Protected by cb_lock: */
	XT_IPAGE_LOCK_TYPE	cb_lock;
	xtWord1				cb_state;						/* Block status. */
#ifdef IND_OPT_DATA_WRITTEN
	xtBool1				cb_header;						/* TRUE if the header changed. */
	xtWord2				cb_min_pos;						/* The minimum dirty byte. */
	xtWord2				cb_max_pos;						/* The maximum dirty byte. */
#endif
	volatile xtWord2	cb_handle_count;				/* TRUE if this page is referenced by a handle. */
	xtWord2				cp_del_count;					/* Number of deleted entries. */
#ifdef XT_USE_DIRECT_IO_ON_INDEX
	xtWord1				*cb_data;
#else
	xtWord1				cb_data[XT_INDEX_PAGE_SIZE];
#endif
#ifdef CHECK_BLOCK_TRAILERS
	xtWord1				cp_check[4];
#endif
} XTIndBlockRec, *XTIndBlockPtr;

typedef struct XTIndReference {
	xtBool					ir_xlock;					/* Set to TRUE if the cache block is X locked. */
	xtBool					ir_updated;					/* Set to TRUE if the cache block is updated. */
	XTIndBlockPtr			ir_block;
	XTIdxBranchDPtr			ir_branch;
} XTIndReferenceRec, *XTIndReferencePtr;

typedef struct XTIndFreeBlock {
	XTDiskValue1			if_zero1_1;					/* Must be set to zero. */
	XTDiskValue1			if_zero2_1;					/* Must be set to zero. */
	XTDiskValue1			if_status_1;
	XTDiskValue1			if_unused1_1;
	XTDiskValue4			if_unused2_4;
	XTDiskValue8			if_next_block_8;
} XTIndFreeBlockRec, *XTIndFreeBlockPtr;

typedef struct XTIndHandleBlock {
	xtWord4					hb_ref_count;
	struct XTIndHandleBlock	*hb_next;
	XTIdxBranchDRec			hb_branch;
} XTIndHandleBlockRec, *XTIndHandleBlockPtr;

typedef struct XTIndHandle {
	struct XTIndHandle		*ih_next;
	struct XTIndHandle		*ih_prev;
	XTSpinLockRec			ih_lock;
	xtIndexNodeID			ih_address;
	xtBool					ih_cache_reference;		/* True if this handle references the cache. */
	union {
		XTIndBlockPtr		ih_cache_block;
		XTIndHandleBlockPtr	ih_handle_block;
	} x;
	XTIdxBranchDPtr			ih_branch;
} XTIndHandleRec, *XTIndHandlePtr;

void			xt_ind_init(XTThreadPtr self, size_t cache_size);
void			xt_ind_exit(XTThreadPtr self);

xtInt8			xt_ind_get_usage();
xtInt8			xt_ind_get_size();
u_int			xt_ind_get_blocks();
u_int			xt_ind_get_free_blocks();
xtBool			xt_ind_write(struct XTOpenTable *ot, XTIndexPtr ind, xtIndexNodeID offset, size_t size, xtWord1 *data);
xtBool			xt_ind_write_cache(struct XTOpenTable *ot, xtIndexNodeID offset, size_t size, xtWord1 *data);
xtBool			xt_ind_free_block(struct XTOpenTable *ot, XTIndexPtr ind, xtIndexNodeID offset);
xtBool			xt_ind_read_bytes(struct XTOpenTable *ot, XTIndexPtr ind, xtIndexNodeID offset, size_t size, xtWord1 *data);
void			xt_ind_check_cache(XTIndexPtr ind);
xtBool			xt_ind_reserve(struct XTOpenTable *ot, u_int count, XTIdxBranchDPtr not_this);
void			xt_ind_free_reserved(struct XTOpenTable *ot);
void			xt_ind_unreserve(struct XTOpenTable *ot);

xtBool			xt_ind_fetch(struct XTOpenTable *ot, XTIndexPtr ind, xtIndexNodeID node, XTPageLockType ltype, XTIndReferencePtr iref);
xtBool			xt_ind_get(struct XTOpenTable *ot, xtIndexNodeID address, XTIndReferencePtr iref);
xtBool			xt_ind_release(struct XTOpenTable *ot, XTIndexPtr ind, XTPageUnlockType utype, XTIndReferencePtr iref);

void			xt_ind_lock_handle(XTIndHandlePtr handle);
void			xt_ind_unlock_handle(XTIndHandlePtr handle);
xtBool			xt_ind_copy_on_write(XTIndReferencePtr iref);

XTIndHandlePtr	xt_ind_get_handle(struct XTOpenTable *ot, XTIndexPtr ind, XTIndReferencePtr iref);
void			xt_ind_release_handle(XTIndHandlePtr handle, xtBool have_lock, XTThreadPtr thread);

#ifdef CHECK_BLOCK_TRAILERS
extern void check_block_trailers();
#endif

#ifdef DEBUG
//#define DEBUG_CHECK_IND_CACHE
#endif

//#define XT_TRACE_INDEX

#ifdef XT_TRACE_INDEX
#define IDX_TRACE(x, y, z)		xt_trace(x, y, z)
#else
#define IDX_TRACE(x, y, z)
#endif

#endif
