/***
 *
 * Copyright (c) 1988 by Sun Microsystems, Inc.
 * @(#)sh_class.h 22.1 89/08/10 Copyright 1988 Sun Micro
 *
 * SH_CLASS.H - Class Support Macros
 *
 * The CLASS package provides a way to create and manipulate
 * general purpose objects. It supports memory allocation and
 * freeing and maintains a reference count and a types for each object.
 * Objects can be fixed size or may grow dynamically. This package
 * is documented more fully in the Shapes Internal Documentation
 * (see the Object Extension chapter).
 *
 ***/
#ifndef	_SH_CLASS

#define	CLASS_MAX	50
#define	CLASS_NOSUPER	sh_class_none
#define	CLASS_THIS	0xff
#define	CLASS_VARIABLE	0xfe

typedef void	*OBJID;
typedef OBJID	(*CLASS_OPER)();
typedef Unsgn8	CLASS_TYPE;
typedef Unsgn8	CLASS_ID;

/*
 * Class descriptor
 */
typedef struct
  {
   CLASS_TYPE	type;	/* type of class (implementation) */
   CLASS_ID	id;	/* class identifier */
  } CLASS;

/*
 * Class definition table entry
 */
typedef struct
  {
   CLASS_ID	super;	/* super class */
   CLASS_OPER	*opers;	/* operators */
  } CLASS_DEF;

/***
 *
 * Object Header
 * Each allocated object in the system has a header as follows.
 * The object type determines whether object id numbers, integers or
 * both are stored in the object. The class tells how the data is
 * interpreted by the system (what it means).
 *
 ***/
struct obj_hdr
  {
   unsigned int	obj_flags : 8;	/* object flags */
   unsigned int	obj_size : 24;	/* object size (bytes) */
   CLASS	obj_class;	/* class of object */
   Unsgn16	obj_use;	/* object use count */
   CLASS_OPER	*obj_func;	/* object operator dispatch table */
  };

/*
 * Class table entry
 */
#define	CLASS_NONE	0

struct class
  {
   CLASS	CLASS_SUPER;	/* super class */
   CLASS_ID	CLASS_BASE;	/* base super class */
   Unsgn8	CLASS_NUMOPERS;	/* number of operators */
   Unsgn16	CLASS_SIZE;	/* byte size of class data area */
   CLASS_OPER	*CLASS_OPERS;	/* default operator set */
  };

#define Define_Class(class, super, nops, size, ops, base) { \
	{ 0, CAT(CLASS_,super) }, CAT(CLASS_,base), nops, size, ops }

/***
 *
 * CLASS Operations
 *
 * Get_Class(class, attr)		Get class attribute
 *
 * Set_Class(class, attr, val)		Set class attribute
 *
 * Call_Class((CLASS_ID) id, op)(obj, ...)
 *      Invoke an operator from a particular class. Both the type and id
 *      of the class are given in the "class" argument.
 *
 * Call_Obj((OBJID) obj, (CLASS_ID) id, op)(obj, ...)
 *      Invoke an operator from a class, taking the type from
 *      the object. The "class" argument gives the ID of the class only.
 *
 ****/
#define	Set_Class	sh_Set_Class
#define	Get_Class(c, a) CAT(a,get)(sh_class_type[(c).type][(c).id])
#define Call_Class(t, i, o) (*sh_class_type[t][i].CLASS_OPERS[o])
#define Call_Obj(obj, id, op) (*sh_class_type[Get_Obj(obj, OBJ_CLASS_TYPE)][id].CLASS_OPERS[op])

/***
 *
 * CLASS ATTRIBUTES
 *
 *   CLASS_SIZE		byte size of object in the class
 *   CLASS_NUMOPERS	number of class operators
 *   CLASS_SUPER	CLASS of superclass
 *   CLASS_BASE		CLASS_ID of lowest level superclass
 *   CLASS_OPER		default operator set
 *
 ****/
#define	CLASS_SIZEget(c) (c.CLASS_SIZE)
#define	CLASS_SUPERget(c) (c.CLASS_SUPER)
#define	CLASS_BASEget(c) (c.CLASS_BASE)
#define	CLASS_OPERSget(c) (c.CLASS_OPERS)
#define	CLASS_NUMOPERSget(c) (c.CLASS_NUMOPERS)

typedef enum
  {
   CLASS_SIZE,
   CLASS_NUMOPERS,
   CLASS_SUPER,
   CLASS_BASE,
   CLASS_OPERS,
  } CLASS_attr;

/****
 *
 * USER LEVEL OBJECT ACCESS MACROS
 *
 * Get_Obj(obj, attr)		get object attribute
 * Set_Obj(obj, attr, val)	set object attribute
 * Use_Obj(obj)			reference object
 * Destroy_Obj(obj)		dereference object
 * Assign_Obj(ptr, obj)		assign object to pointer
 * Size_Obj(obj, s)		change object size to S bytes
 * Extend_Obj(obj, s)		extend object size by S additional bytes
 * Copy_Obj(obj, s)		make copy of object (S bytes)
 * OneRef_Obj(obj)		returns true of object has one reference only
 *
 ****/

/***
 *
 * OBJECT ATTRIBUTES
 *
 *   OBJ_SIZE		byte size of object 
 *   OBJ_FLAGS		object flag information
 *   OBJ_CLASS		CLASS of object
 *   OBJ_CLASS_ID	CLASS_ID of object
 *   OBJ_CLASS_TYPE	CLASS_TYPE of object
 *   OBJ_BASE		CLASS_ID of lowest level superclass
 *   OBJ_KEY		general object key
 *
 * Get_Obj(objid, attr)
 * Get object attribute
 *
 * Set_Obj(objid, attr, val)
 * Set object attribute
 *
 * Class_Obj(objid, classid)
 * Initialize object header
 *
 * Static_Obj(name, cstruct)
 * Declare "name" to be an object whose internal structure
 * is of type "cstruct". name_hdr references the object header.
 *
 ****/
#define	Get_Obj(o, a) CAT(a,get)(((struct obj_hdr *) (o)) - 1)

#define	OBJ_SIZEget(o)		((o)->obj_size)
#define	OBJ_FLAGSget(o)		((o)->obj_flags)
#define	OBJ_CLASSget(o)		((o)->obj_class)
#define OBJ_CLASS_IDget(o)      ((o)->obj_class.id)
#define OBJ_CLASS_TYPEget(o)    ((o)->obj_class.type)
#define	OBJ_USEget(o)		((o)->obj_use)
#define	OBJ_OPERSget(o)		((o)->obj_func)
#define	OBJ_BASEget(o)		Get_Class((o)->obj_class, CLASS_BASE)
#define	OBJ_KEYget(o) 		(*((OBJ_KEY *) ((o) + 1)))
#define	GetOper_Obj(o,i) 	(Get_Obj(o, OBJ_OPERS)[i])

#define	Set_Obj(o, a, v)	CAT(a,set)(((struct obj_hdr *) (o) - 1), v)
#define	OBJ_KEYset(o, v)	(*((OBJ_KEY *) (o + 1)) = (v))
#define	OBJ_SIZEset(o, v)	Size_Obj(o + 1, v)
#define	OBJ_FLAGSset(o, v)	((o)->obj_flags |= (v))
#define OBJ_CLASSset(o, v) ( \
        (o->obj_func = Get_Class(v, CLASS_OPERS)), (o->obj_class = v))
#define OBJ_CLASS_TYPEset(o, v) ( \
        (o->obj_class.type = v), \
        (o->obj_func = Get_Class(o->obj_class, CLASS_OPERS)))
#define OBJ_CLASS_IDset(o, v) ( \
        (o->obj_class.id = v), \
        (o->obj_func = Get_Class(o->obj_class, CLASS_OPERS)))

#define	Static_Obj(name, cstruct) \
	struct obj_hdr	CAT(name,_hdr); 	\
	cstruct 	name

#define	Class_Obj(o, c) { \
   (((struct obj_hdr *) (o)) - 1)->obj_flags = 0;		\
   (((struct obj_hdr *) (o)) - 1)->obj_size =		 	\
		sh_class_type[0][c].CLASS_SIZE;			\
   (((struct obj_hdr *) (o)) - 1)->obj_class.type = 0; 		\
   Set_Obj(o, OBJ_CLASS_ID, c); }

/****
 *
 * Use_Obj(obj)
 * Reference (increment the use count) the given object.
 * Check for wrap-around.
 *
 ****/
#define	Use_Obj(obj) ((++Get_Obj(obj, OBJ_USE) == 0) && --Get_Obj(obj, OBJ_USE))
#define	OneRef_Obj(obj) (Get_Obj(obj, OBJ_USE) == 2)

/***
 *
 * VOID Destroy_Obj(objid)
 * Decrement reference count and garbage collect object if necessary
 * Returns zero if object was not removed, else non-zero.
 *
 ***/
#define	Destroy_Obj(o) ((o) && \
		(--((((struct obj_hdr *) (o)) - 1)->obj_use) <= 1) && \
		sh_Kill_Obj(o))

/***
 *
 * OBJID Init_Obj(obj, root, type)
 * Initialize object
 *
 ***/
#define	Init_Obj(o,r,t)	(GetOper_Obj(o,1)(o,r,t))

/****
 *
 * Assign_Obj(objptr, obj)
 * Assigns an object to a pointer. The old object referred to by the
 * pointer is removed (dereferenced) and the new object is referenced.
 * THIS MACRO HAS SIDE EFFECTS. It expands both arguments more than once.
 *
 ****/
#define	Assign_Obj(OBJPTR, NEW) do {		\
register OBJID OLD = *((OBJID *) (OBJPTR));	\
	if (NEW) Use_Obj(NEW);			\
	Destroy_Obj(OLD);			\
	*((OBJID *) (OBJPTR)) = (OBJID) (NEW);	\
} while(0)

/****
 *
 * OBJID Extend_Obj(obj, n)
 * Extend the given object by N bytes. May return a new object.
 *
 ****/
#define	Extend_Obj(obj, n) Size_Obj(obj, (n) + Get_Obj(obj, OBJ_SIZE))

/***
 *
 * Object Flags
 *   OBJ_NOSHRINK       indicates the object cannot be "shrunk".
 *                      That is, the Size_Obj operation cannot make the object
 *                      smaller, only larger.
 *   OBJ_NOKILL         indicates the object cannot be removed.
 *
 ***/
#define OBJ_NOSHRINK    1
#define OBJ_NOKILL      2

/****
 *
 * Object KEY operations
 * A key is an attribute that uniquely describes the state of an object.
 * Each object has a general key (xxx_KEY attribute). Whenever any
 * attribute in an object is modified, its KEY attribute changes.
 *
 ****/
#define	GET_KEY()	(++sh_key)
#define	NEW_KEY(o)	(Get_Obj(o, OBJ_KEY) = GET_KEY())

/****
 *
 * External Routines (all begin with sh_)
 *
 ****/
#define	Create_Class	sh_Create_Class
#define	NewType_Class	sh_NewType_Class
#define	Create_Obj	sh_Create_Obj
#define	Temp_Obj	sh_Temp_Obj
#define	New_Obj		sh_New_Obj
#define	Size_Obj	sh_Size_Obj
#define	Copy_Obj	sh_Copy_Obj

/***
 *
 * General Globals
 *
 ***/
extern	CLASS_TYPE NewType_Class(); /* make new class type */
extern	CLASS_ID Create_Class();	/* make new class */
extern	OBJID	Create_Obj();	/* make "used" object */
extern	OBJID	Temp_Obj();	/* make temporary object */
extern	OBJID	New_Obj();	/* allocate memory for object */
extern	OBJID	Size_Obj();	/* resize object */
extern	OBJID	Copy_Obj();	/* copy object */
extern	void	sh_Set_Class();	/* set class attribute */

extern	CLASS	sh_class_none;	/* class with 0 type and 0 id */
extern	Unsgn8	sh_obj_dbg;
extern	OBJ_KEY	sh_key;
extern	struct class *sh_class_type[];

#define	_SH_CLASS
#endif	/* _SH_CLASS */
