/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1999, 2013 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#ifndef	_DB_QAM_H_
#define	_DB_QAM_H_

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * QAM data elements: a status field and the data.
 */
typedef struct _qamdata {
	u_int8_t  flags;	/* 00: delete bit. */
#define	QAM_VALID	0x01
#define	QAM_SET		0x02
	u_int8_t  data[1];	/* Record. */
} QAMDATA;

struct __queue;		typedef struct __queue QUEUE;
struct __qcursor;	typedef struct __qcursor QUEUE_CURSOR;

struct __qcursor {
	/* struct __dbc_internal */
	__DBC_INTERNAL

	/* Queue private part */

	/* Per-thread information: queue private. */
	db_recno_t	 recno;		/* Current record number. */

	u_int32_t	 flags;
};

typedef struct __mpfarray {
	u_int32_t n_extent;		/* Number of extents in table. */
	u_int32_t low_extent;		/* First extent open. */
	u_int32_t hi_extent;		/* Last extent open. */
	struct __qmpf {
		int pinref;
		DB_MPOOLFILE *mpf;
	} *mpfarray;			 /* Array of open extents. */
} MPFARRAY;

/*
 * The in-memory, per-tree queue data structure.
 */
struct __queue {
	db_pgno_t q_meta;		/* Database meta-data page. */
	db_pgno_t q_root;		/* Database root page. */

	int	  re_pad;		/* Fixed-length padding byte. */
	u_int32_t re_len;		/* Length for fixed-length records. */
	u_int32_t rec_page;		/* records per page */
	u_int32_t page_ext;		/* Pages per extent */
	MPFARRAY array1, array2;	/* File arrays. */

					/* Extent file configuration: */
	DBT pgcookie;			/* Initialized pgcookie. */
	DB_PGINFO pginfo;		/* Initialized pginfo struct. */

	char *path;			/* Space allocated to file pathname. */
	char *name;			/* The name of the file. */
	char *dir;			/* The dir of the file. */
	int mode;			/* Mode to open extents. */
};

/* Format for queue extent names. */
#define	QUEUE_EXTENT		"%s%c__dbq.%s.%d"
#define	QUEUE_EXTENT_HEAD	"__dbq.%s."
#define	QUEUE_EXTENT_PREFIX	"__dbq."

typedef struct __qam_filelist {
	DB_MPOOLFILE *mpf;
	u_int32_t id;
} QUEUE_FILELIST;

/*
 * Calculate the page number of a recno.
 *
 * Number of records per page =
 *	Divide the available space on the page by the record len + header.
 *
 * Page number for record =
 *	divide the physical record number by the records per page
 *	add the root page number
 *	For now the root page will always be 1, but we might want to change
 *	in the future (e.g. multiple fixed len queues per file).
 *
 * Index of record on page =
 *	physical record number, less the logical pno times records/page
 */
#define	CALC_QAM_RECNO_PER_PAGE(dbp)					\
    (((dbp)->pgsize - QPAGE_SZ(dbp)) /					\
    (u_int32_t)DB_ALIGN((uintmax_t)SSZA(QAMDATA, data) +		\
    ((QUEUE *)(dbp)->q_internal)->re_len, sizeof(u_int32_t)))

#define	QAM_RECNO_PER_PAGE(dbp)	(((QUEUE*)(dbp)->q_internal)->rec_page)

#define	QAM_RECNO_PAGE(dbp, recno)					\
    (((QUEUE *)(dbp)->q_internal)->q_root				\
    + (((recno) - 1) / QAM_RECNO_PER_PAGE(dbp)))

#define	QAM_PAGE_EXTENT(dbp, pgno)					\
    (((pgno) - 1) / ((QUEUE *)(dbp)->q_internal)->page_ext)

#define	QAM_RECNO_EXTENT(dbp, recno)					\
    QAM_PAGE_EXTENT(dbp, QAM_RECNO_PAGE(dbp, recno))

#define	QAM_RECNO_INDEX(dbp, pgno, recno)				\
    (u_int32_t)(((recno) - 1) - (QAM_RECNO_PER_PAGE(dbp)		\
    * (pgno - ((QUEUE *)(dbp)->q_internal)->q_root)))

#define	QAM_GET_RECORD(dbp, page, index)				\
    ((QAMDATA *)((u_int8_t *)(page) + (QPAGE_SZ(dbp) +			\
    (DB_ALIGN((uintmax_t)SSZA(QAMDATA, data) +				\
    ((QUEUE *)(dbp)->q_internal)->re_len, sizeof(u_int32_t)) * index))))

#define QAM_OUTSIDE_QUEUE(meta, recno)					\
	(((meta)->cur_recno >= (meta)->first_recno ?			\
	    ((recno) < (meta)->first_recno ||				\
	         (recno) > (meta)->cur_recno) :				\
	    ((recno) > (meta)->cur_recno && 				\
	        (recno) < (meta)->first_recno)))

#define	QAM_AFTER_CURRENT(meta, recno)					\
	((recno) == (meta)->cur_recno ||				\
	(QAM_OUTSIDE_QUEUE(meta, recno) &&				\
        ((recno) - (meta)->cur_recno) <= ((meta)->first_recno - (recno))))

#define	QAM_BEFORE_FIRST(meta, recno)					\
	(QAM_OUTSIDE_QUEUE(meta, recno) &&				\
	((meta)->first_recno - (recno)) < ((recno) - (meta)->cur_recno))

#define	QAM_NOT_VALID(meta, recno)					\
    (recno == RECNO_OOB ||						\
	QAM_BEFORE_FIRST(meta, recno) || QAM_AFTER_CURRENT(meta, recno))

#define QAM_WAKEUP(dbc, ret) do {					\
	if (STD_LOCKING(dbc)) {						\
		dbc->lock.pgno = PGNO_INVALID;				\
		dbc->lock.type = DB_PAGE_LOCK;				\
		ret = __lock_wakeup((dbc)->dbp->env, &(dbc)->lock_dbt);	\
	} else								\
		ret = 0;						\
} while (0)

/* Handle wrap around. */
#define QAM_INC_RECNO(recno) do {					\
	recno++;							\
} while (recno == RECNO_OOB)

#define QAM_DEC_RECNO(recno) do {					\
	recno--;							\
} while (recno == RECNO_OOB)


/*
 * Log opcodes for the mvptr routine.
 */
#define	QAM_SETFIRST		0x01
#define	QAM_SETCUR		0x02
#define	QAM_TRUNCATE		0x04

typedef enum {
	QAM_PROBE_GET,
	QAM_PROBE_PUT,
	QAM_PROBE_DIRTY,
	QAM_PROBE_MPF
} qam_probe_mode;

/*
 * Ops for __qam_nameop.
 */
typedef enum {
	QAM_NAME_DISCARD,
	QAM_NAME_RENAME,
	QAM_NAME_REMOVE
} qam_name_op;

#define	__qam_fget(dbc, pgnoaddr, flags, addrp)		\
	__qam_fprobe(dbc, *pgnoaddr,					\
	    addrp, QAM_PROBE_GET, DB_PRIORITY_UNCHANGED, flags)

#define	__qam_fput(dbc, pgno, addrp, priority)			\
	__qam_fprobe(dbc, pgno, addrp, QAM_PROBE_PUT, priority, 0)

#define	__qam_dirty(dbc, pgno, pagep, priority)		\
	__qam_fprobe(dbc, pgno, pagep, QAM_PROBE_DIRTY, priority, 0)

#if defined(__cplusplus)
}
#endif

#include "dbinc_auto/qam_auto.h"
#include "dbinc_auto/qam_ext.h"
#endif /* !_DB_QAM_H_ */
