/*
 *	Copyright (c) 1994, by Sun Microsytems, Inc.
 */

#ifndef _LIBTNF_H
#define	_LIBTNF_H

#pragma ident	"@(#)libtnf.h	1.20	94/10/04 SMI"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

#include "tnf/tnf.h"
#include "machlibtnf.h"

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Info flags
 */

typedef unsigned long	tag_props_t;

#define	TAG_PROP_INLINE		(1<<0)
#define	TAG_PROP_TAGGED		(1<<1)
#define	TAG_PROP_SCALAR		(1<<2)
#define	TAG_PROP_DERIVED	(1<<3)
#define	TAG_PROP_ARRAY		(1<<4)
#define	TAG_PROP_STRING		(1<<5)
#define	TAG_PROP_STRUCT		(1<<6)
#define	TAG_PROP_TYPE		(1<<7)

/*
 * Type tag information
 */

struct taginfo {
	struct taginfo	*link;		/* hash link */
#define	INFO_MEMBER_0	link
	TNF		*tnf;		/* TNF handle */
	tnf_ref32_t	*tag;		/* tag record in file */
	char		*name;		/* chars in file */
	tnf_kind_t	kind;		/* data classification */
	tag_props_t	props;		/* tag property flags */
	struct taginfo	*meta;		/* meta tag info */
	struct taginfo	*base;		/* last derived base or elttype */
	size_t		size;		/* storage size or -1 */
	size_t		align;		/* slot alignment */
	size_t		hdrsize;	/* array header size */
	struct slotinfo {		/* aggregate slot information */
		unsigned	slot_count;
		/* Embedded array */
		struct slot {
			struct taginfo	*slot_type;
			char		*slot_name;
			unsigned	slot_offset;
		} slots[1];
	} *slotinfo;
};

#define	INFO_PROP(ip, p)	((ip)->props & (p))

#define	INFO_INLINE(ip)		INFO_PROP(ip, TAG_PROP_INLINE)
#define	INFO_TAGGED(ip)		INFO_PROP(ip, TAG_PROP_TAGGED)
#define	INFO_SCALAR(ip)		INFO_PROP(ip, TAG_PROP_SCALAR)
#define	INFO_DERIVED(ip)	INFO_PROP(ip, TAG_PROP_DERIVED)
#define	INFO_ARRAY(ip)		INFO_PROP(ip, TAG_PROP_ARRAY)
#define	INFO_STRING(ip)		INFO_PROP(ip, TAG_PROP_STRING)
#define	INFO_STRUCT(ip)		INFO_PROP(ip, TAG_PROP_STRUCT)
#define	INFO_TYPE(ip)		INFO_PROP(ip, TAG_PROP_TYPE)

#define	INFO_REF_SIZE(ip)	(INFO_TAGGED(ip)? 4: (ip)->size)
#define	INFO_ELEMENT_SIZE(ip)	INFO_REF_SIZE(ip)

/* Alignment is stored for all but records and derivations thereof */
#define	INFO_ALIGN(ip)		(INFO_TAGGED(ip)? 4: (ip)->align)

#define	ALIGN(n, a)		\
	(((a) == 0) ? (n) : (((n) + (a) - 1) & ~((a) - 1)))

/*
 * Tag lookup
 */

/* Number of directory entries */
#define	TAGDIRCNT(x) 	((x) / sizeof (tnf_ref32_t))

/* Number of hash table buckets */
#define	TAGTABCNT	1024
#define	TAGTABMASK	(TAGTABCNT-1)

/* A tag is at least 32 bytes; with strings & props, assume 128 bytes */
#define	TAGTABSHIFT	7

/* Hash tag by bits 17:7 of offset within data area */
#define	TAGOFF(tnf, p)	((unsigned)((caddr_t)(p) - (tnf)->data_start))
#define	TAGHASH(tnf, p)	((TAGOFF(tnf, p) >> TAGTABSHIFT) & TAGTABMASK)

/*
 * TNF handle
 */

struct TNF {
	/*
	 * Client-supplied bounds
	 */
	caddr_t		file_start;
	size_t		file_size;
	caddr_t		file_end;	/* file_start + file_size */

	/*
	 * File information
	 */
	unsigned	file_magic;	/* magic number of file */
	int		file_native;	/* endian flag */

	/* file header */
	tnf_ref32_t	*file_header;	/* first record in file */
	size_t		block_size;	/* size of a block */
	size_t		directory_size;	/* size of directory area */

	unsigned	block_count;	/* number of data blocks */
	caddr_t		data_start;	/* file_start + 64KB */

	unsigned	generation_shift;
	unsigned	address_mask;

	/* block headers */
	unsigned	block_shift;	/* index -> bhdr */
	unsigned	block_mask;	/* ptr -> bhdr */
	unsigned	block_generation_offset;
	unsigned	block_bytes_valid_offset;

	/* root tag */
	tnf_ref32_t	*root_tag;

	/* important taginfo */
	struct taginfo	*file_header_info;
	struct taginfo	*block_header_info;

	/* tag lookup tables */
	struct taginfo	**tag_table;	/* by address */
	struct taginfo	**tag_directory; /* by index */

};

/*
 * File operations for reading integers
 */

#define	_GET_UINT32(tnf, ptr)				\
	((tnf)->file_native ?				\
		*(tnf_uint32_t *)(ptr) :		\
		_tnf_swap32(*(tnf_uint32_t *)(ptr)))

#define	_GET_INT32(tnf, ptr)				\
	((tnf_int32_t)_GET_UINT32(tnf, ptr))

#define	_GET_UINT16(tnf, ptr)				\
	((tnf)->file_native ?				\
		*(tnf_uint16_t *)(ptr) :		\
		_tnf_swap16(*(tnf_uint16_t *)(ptr)))

#define	_GET_INT16(tnf, ptr)				\
	((tnf_int16_t)_GET_UINT16(tnf, ptr))

/*
 * TNF reference-chasing operations
 */

tnf_ref32_t * _tnf_get_ref32(TNF *, tnf_ref32_t *);
tnf_ref32_t * _tnf_get_ref16(TNF *, tnf_ref32_t *);

#define	_GET_REF32(tnf, ptr)	_tnf_get_ref32(tnf, ptr)
#define	_GET_REF16(tnf, ptr)	_tnf_get_ref16(tnf, ptr)

/*
 * Block header record operations
 * Only applicable in data area
 */

#define	_GET_BLOCK(tnf, ptr)					\
	((tnf_ref32_t *)((unsigned)(ptr) & (tnf)->block_mask))

#define	_GET_BLOCK_INDEX(tnf, bhdr)				\
	(((caddr_t)(bhdr) - (tnf)->data_start) >> (tnf)->block_shift)

#define	_GET_INDEX_BLOCK(tnf, index)				\
	((tnf_ref32_t *)((tnf)->data_start + ((index) << (tnf)->block_shift)))

#define	_GET_BLOCK_GENERATION(tnf, bhdr)			\
	_GET_UINT32(tnf, (caddr_t)bhdr + tnf->block_generation_offset)

#define	_GET_BLOCK_BYTES_VALID(tnf, bhdr)			\
	(!(bhdr) ? 0 : _GET_UINT16(tnf, (caddr_t)bhdr +		\
				tnf->block_bytes_valid_offset))

/*
 * Datum operations
 */

#ifndef	_DATUM_MACROS

tnf_datum_t _tnf_datum(struct taginfo *, caddr_t);
struct taginfo * _tnf_datum_info(tnf_datum_t);
caddr_t	_tnf_datum_val(tnf_datum_t);

#define	DATUM(x, y)	_tnf_datum(x, y)
#define	DATUM_INFO(x)	_tnf_datum_info(x)
#define	DATUM_VAL(x)	_tnf_datum_val(x)

#else  /* _DATUM_MACROS */

/* Some degree of type safety: */
#define	DATUM(x, y)	_DATUM(&(x)->INFO_MEMBER_0, y)
#define	DATUM_INFO(d)	((struct taginfo *)_DATUM_HI(d))
#define	DATUM_VAL(d)	((caddr_t)_DATUM_LO(d))

#endif /* _DATUM_MACROS */

#define	_DATUM(hi, lo)	(((unsigned long long)(hi) << 32) | (unsigned)(lo))
#define	_DATUM_HI(x) 	((unsigned) ((x) >> 32))
#define	_DATUM_LO(x) 	((unsigned) (x))

#define	DATUM_RECORD(x)		\
	((tnf_ref32_t *)DATUM_VAL(x))

#define	RECORD_DATUM(tnf, rec)	\
	DATUM(_tnf_record_info(tnf, rec), (caddr_t)rec)

#define	DATUM_TNF(x)		DATUM_INFO(x)->tnf
#define	DATUM_TAG(x)		DATUM_INFO(x)->tag

/*
 * Type checking operations
 */

void _tnf_check_datum(tnf_datum_t);
#define	CHECK_DATUM(x)	_tnf_check_datum(x)

void _tnf_check_record(tnf_datum_t);
#define	CHECK_RECORD(x)	_tnf_check_record(x)

void _tnf_check_slots(tnf_datum_t);
#define	CHECK_SLOTS(x)	_tnf_check_slots(x)

void _tnf_check_array(tnf_datum_t);
#define	CHECK_ARRAY(x)	_tnf_check_array(x)

void _tnf_check_type(tnf_datum_t);
#define	CHECK_TYPE(x)	_tnf_check_type(x)

/*
 * Operations based on ABI layouts and bootstrap assumptions
 */

tnf_ref32_t * _tnf_get_tag(TNF *, tnf_ref32_t *);
tnf_ref32_t * _tnf_get_tag_arg(TNF *, tnf_ref32_t *);
size_t _tnf_get_self_size(TNF *, tnf_ref32_t *);
unsigned _tnf_get_element_count(TNF *, tnf_ref32_t *, unsigned);
caddr_t _tnf_get_elements(TNF *, tnf_ref32_t *);
char * _tnf_get_chars(TNF *, tnf_ref32_t *);
char * _tnf_get_name(TNF *, tnf_ref32_t *);
tnf_ref32_t * _tnf_get_properties(TNF *, tnf_ref32_t *);
tnf_ref32_t * _tnf_get_slot_types(TNF *, tnf_ref32_t *);
size_t _tnf_get_header_size(TNF *, tnf_ref32_t *);
tnf_ref32_t * _tnf_get_derived_base(TNF *, tnf_ref32_t *);

tnf_ref32_t * _tnf_get_root_tag(TNF *, tnf_ref32_t *);
tnf_ref32_t * _tnf_get_property(TNF *, tnf_ref32_t *, char *);
tnf_ref32_t * _tnf_get_element_named(TNF *, tnf_ref32_t *, char *);
tnf_ref32_t * _tnf_get_base_tag(TNF *, tnf_ref32_t *);

size_t _tnf_get_storage_size(TNF *, tnf_ref32_t *);
size_t _tnf_get_ref_size(TNF *, tnf_ref32_t *);

unsigned _tnf_get_align(TNF *, tnf_ref32_t *);

caddr_t	_tnf_get_slot_typed(TNF *, tnf_ref32_t *, char *);
caddr_t	_tnf_get_slot_named(TNF *, tnf_ref32_t *, char *);

#define	HAS_PROPERTY(tnf, tag, name)	\
	(_tnf_get_property(tnf, tag, name) != TNF_NULL)

/*
 * Call the installed error handler with installed arg
 */

void _tnf_error(TNF *, tnf_errcode_t);

/*
 * Tag lookup operations
 */

struct taginfo * _tnf_get_info(TNF *, tnf_ref32_t *);
struct taginfo * _tnf_record_info(TNF *, tnf_ref32_t *);

tnf_errcode_t _tnf_init_tags(TNF *);
tnf_errcode_t _tnf_fini_tags(TNF *);

/*
 * Classify a tag into its props and data kind
 */

tag_props_t _tnf_get_props(TNF *, tnf_ref32_t *);
tnf_kind_t _tnf_get_kind(TNF *, tnf_ref32_t *);

caddr_t	_tnf_get_member(TNF *, caddr_t, struct taginfo *);

#ifdef __cplusplus
}
#endif

#endif /* _LIBTNF_H */
