/* pobj.h
 *  Copyright (C) 2001-2015, Parrot Foundation.
 *  Overview:
 *     Parrot Object data members and flags enum
 *  Data Structure and Algorithms:
 *  History:
 *  Notes:
 *  References: memory_internals.pod (out of date as of 8/2010).
 */

#ifndef PARROT_POBJ_H_GUARD
#define PARROT_POBJ_H_GUARD

#include "parrot/config.h"

/* This is the base Parrot object structure. Every object begins with
   this slot, then has additional slots as required. */

typedef struct pobj_t {
    Parrot_UInt flags;                  /* Lots of flags (see below). */
} PObj;

/* This is a buffer header object, "inheriting" from PObj. */

typedef struct buffer_t {
    Parrot_UInt flags;
    void *     _bufstart;               /* Pointer to start of buffer data
                                           (not buffer prolog). */
    size_t     _buflen;                 /* Length of buffer data. */
} Parrot_Buffer;

typedef enum Forward_flag {
    Buffer_moved_FLAG   = 1 << 0,
    Buffer_shared_FLAG  = 1 << 1
} Forward_flags;

/* Use these macros to access the two buffer header slots. */

#define Buffer_bufstart(buffer)    (buffer)->_bufstart
#define Buffer_buflen(buffer)      (buffer)->_buflen

/* A buffer header object points to a buffer in a Memory_Block.
   The buffer includes a prolog, but _bufstart points to the data
   portion. Here is how it works:

    Buffer header                         buffer
   +-------------------+                 +------------------------+
   |       flags       |                 |  (possible padding)    | }
   +-------------------+                 +---------------------+--+  > prolog
   |      _bufstart    | ------+         |    *Memory_Block    |fl| }
   +-------------------+       |         +---------------------+--+
   |      _buflen      |       +-------> |    data portion        |
   +-------------------+                 |                        |
                                         ~                        ~
                                         |                        |
                                         +------------------------+

   The buffer prolog consists of possible padding and a pointer to the
   Memory_Block containing the buffer. There are two flags in the low-order
   bits of the pointer (see string.h). Padding is only required if the
   alignment of the data portion is higher than that of a pointer.
   This was not the case as of 8/2010.
*/

/* These macros let us address the prolog of a buffer. */

#define Buffer_prolog_offset (sizeof (void*))
#define Buffer_bufprolog(b) ((char *)Buffer_bufstart(b) - Buffer_prolog_offset)

/* This macro gives us the address of the buffer prolog treated as
   a pointer to the flags. */

#define Buffer_bufflagsptr(b) ((INTVAL *)Buffer_bufprolog(b))

/* These macros give us the Memory_Block pointer and pointer-pointer,
   eliminating the flags. */

#define Buffer_pool(b) ((Memory_Block *)( *(INTVAL*)(Buffer_bufprolog(b)) & ~3 ))
#define Buffer_poolptr(b) ((Memory_Block **)Buffer_bufprolog(b))


/* Here is the Parrot string header object, "inheriting" from Buffer. */

struct parrot_string_t {
    Parrot_UInt flags;
    void *     _bufstart;
    size_t     _buflen;
    char       *strstart;               /* Pointer to start of string
                                           (not necessarily at _bufstart). */
    UINTVAL     bufused;                /* Length of string in bytes. */
    UINTVAL     strlen;                 /* Length of string in characters. */
    UINTVAL     hashval;                /* Cached hash value. */

    /*    parrot_string_representation_t representation;*/
    const struct _str_vtable *encoding; /* Pointer to string vtable. */
};

/* Here is the Parrot PMC object, "inheriting" from PObj. */

struct PMC {
    Parrot_UInt    flags;
    VTABLE         *vtable;             /* Pointer to vtable. */
    DPOINTER       *data;               /* Pointer to attribute structure. */
    PMC            *_metadata;          /* Pointer to metadata PMC. */
#ifdef THREAD_DEBUG
    Parrot_Interp  orig_interp;
#endif
};

/* Use these macros to access the data and metadata. */

#define PMC_data(pmc)                   (pmc)->data
#define PMC_data_typed(pmc, type) (type)(pmc)->data
#define PMC_metadata(pmc)         ((pmc)->_metadata)

#define POBJ_FLAG(n) ((UINTVAL)1 << (n))
/* PObj flags */
typedef enum PObj_enum {
    /* This first 8 flags may be used privately by a Parrot Object.
     * You should alias these within an individual class's header file.
     *
     * Note:  If the meanings of these flags are changed, then the symbolic
     * names kept in flag_bit_names (see src/packdump.c) must also be updated.
     */
    PObj_private0_FLAG          = POBJ_FLAG(0),
    PObj_private1_FLAG          = POBJ_FLAG(1),
    PObj_private2_FLAG          = POBJ_FLAG(2),
    PObj_private3_FLAG          = POBJ_FLAG(3),
    PObj_private4_FLAG          = POBJ_FLAG(4),
    PObj_private5_FLAG          = POBJ_FLAG(5),
    PObj_private6_FLAG          = POBJ_FLAG(6),
    PObj_private7_FLAG          = POBJ_FLAG(7),

/* Object specification FLAGs */
    /* PObj is a string */
    PObj_is_string_FLAG         = POBJ_FLAG(8),  /* 0x100 */
    /* PObj is a PMC */
    PObj_is_PMC_FLAG            = POBJ_FLAG(9),  /* 0x200 */
    /* PObj is a copy of a string that doesn't own the string buffer */
    PObj_is_string_copy_FLAG    = POBJ_FLAG(10), /* 0x400 */
    /* the PMC is a shared PMC */
    PObj_is_PMC_shared_FLAG     = POBJ_FLAG(11), /* 0x800 Same as PObj_is_shared_FLAG */
    /* PObj is otherwise shared */
    PObj_is_shared_FLAG         = POBJ_FLAG(11), /* Same as PObj_is_PMC_shared_FLAG */

/* Memory management FLAGs */
    /* This is a constant--don't kill it! */
    PObj_constant_FLAG          = POBJ_FLAG(12), /* 0x1000 */
    /* Marks the contents as coming from a non-Parrot source */
    PObj_external_FLAG          = POBJ_FLAG(13), /* 0x2000 */
    /* Mark the buffer as pointing to system memory */
    PObj_sysmem_FLAG            = POBJ_FLAG(14), /* 0x4000 */

/* PObj usage FLAGs, COW & GC */
    /* The Buffer allows COW copies, and may have some. */
    PObj_is_COWable_FLAG        = POBJ_FLAG(15), /* 0x8000 */
    /* Private flag for the GC system. Set if the PObj's in use as
     * far as the GC's concerned */
    b_PObj_live_FLAG            = POBJ_FLAG(16), /* 0x10000 */
    /* Mark the object as on the free list */
    b_PObj_on_free_list_FLAG    = POBJ_FLAG(17), /* 0x20000 */

/* GC FLAGS */
    /* Set to true if the PObj has a custom mark routine */
    PObj_custom_mark_FLAG       = POBJ_FLAG(18), /* 0x40000 */
    /* Set if the PObj has a destroy method that must be called */
    PObj_custom_destroy_FLAG    = POBJ_FLAG(19), /* 0x80000 */
    /* For debugging, report when this buffer gets moved around */
    PObj_report_FLAG            = POBJ_FLAG(20), /* 0x100000 */

    /* used by Proxy PMC to identify PMCs created on the current interp */
    PObj_is_new_FLAG            = POBJ_FLAG(21), /* 0x200000 */

    /* Flags used by generation GC to determine generation object belong */
    PObj_GC_generation_0_FLAG   = POBJ_FLAG(22), /* 0x400000 */
    PObj_GC_generation_1_FLAG   = POBJ_FLAG(23), /* 0x800000 */
    PObj_GC_generation_2_FLAG   = POBJ_FLAG(24), /* 0x1000000 */

    /* Object was marked dirty by write barrier */
    PObj_GC_on_dirty_list_FLAG  = POBJ_FLAG(25), /* 0x2000000 */

    /* Object requires write barrier */
    PObj_GC_need_write_barrier_FLAG = POBJ_FLAG(26), /* 0x4000000 */

    /* Object on C stack will require implicit put to dirty list in GMS */
    PObj_GC_soil_root_FLAG      = POBJ_FLAG(27), /* 0x8000000 */

    /* For simplify some cleanup/setup */
    PObj_GC_all_generation_FLAGS = PObj_GC_generation_0_FLAG
                                 | PObj_GC_generation_1_FLAG
                                 | PObj_GC_generation_2_FLAG,

    PObj_GC_all_FLAGS            = PObj_GC_all_generation_FLAGS
                                 | PObj_GC_on_dirty_list_FLAG
                                 | PObj_GC_need_write_barrier_FLAG,

/* PMC specific FLAGs */
    /* true if this is connected by some route to a needs_early_gc object */
    PObj_needs_early_gc_FLAG    = POBJ_FLAG(28), /* 0x10000000 */

    /* True if the PMC is a class */
    PObj_is_class_FLAG          = POBJ_FLAG(29), /* 0x20000000 */
    /* True if the PMC is a parrot object */
    PObj_is_object_FLAG         = POBJ_FLAG(30)  /* 0x40000000 */

} PObj_flags;
#undef POBJ_FLAG

/*
 * flag access macros:
 * directly using any flags is STRONGLY discouraged, please use
 * these macros
 */

#  define PObj_live_FLAG              b_PObj_live_FLAG
#  define PObj_on_free_list_FLAG      b_PObj_on_free_list_FLAG

#  define gc_flag_TEST(flag, o)      PObj_flag_TEST(flag, o)
#  define gc_flag_SET(flag, o)       PObj_flag_SET(flag, o)
#  define gc_flag_CLEAR(flag, o)     PObj_flag_CLEAR(flag, o)

#define PObj_get_FLAGS(o) ((o)->flags)

#define PObj_flag_TEST(flag, o) (PObj_get_FLAGS(o) & PObj_ ## flag ## _FLAG)
#define PObj_flag_SET(flag, o) (PObj_get_FLAGS(o) |= PObj_ ## flag ## _FLAG)
#define PObj_flag_CLEAR(flag, o) \
        (PObj_get_FLAGS(o) &= ~(UINTVAL)(PObj_ ## flag ## _FLAG))

#define PObj_flags_SETTO(o, f) PObj_get_FLAGS(o) = (f)
#define PObj_flags_CLEARALL(o) PObj_flags_SETTO((o), 0)

#define PObj_is_COWable_TEST(o) PObj_flag_TEST(is_COWable, o)
#define PObj_is_COWable_SET(o) PObj_flag_SET(is_COWable, o)

#define PObj_constant_TEST(o) PObj_flag_TEST(constant, o)
#define PObj_constant_SET(o) PObj_flag_SET(constant, o)
#define PObj_constant_CLEAR(o) PObj_flag_CLEAR(constant, o)

#define PObj_external_TEST(o) PObj_flag_TEST(external, o)
#define PObj_external_SET(o) PObj_flag_SET(external, o)
#define PObj_external_CLEAR(o) PObj_flag_CLEAR(external, o)

#define PObj_report_TEST(o) PObj_flag_TEST(report, o)
#define PObj_report_SET(o) PObj_flag_SET(report, o)
#define PObj_report_CLEAR(o) PObj_flag_CLEAR(report, o)

#define PObj_on_free_list_TEST(o) gc_flag_TEST(on_free_list, o)
#define PObj_on_free_list_SET(o) gc_flag_SET(on_free_list, o)
#define PObj_on_free_list_CLEAR(o) gc_flag_CLEAR(on_free_list, o)

#define PObj_live_TEST(o) gc_flag_TEST(live, o)
#define PObj_live_SET(o) gc_flag_SET(live, o)
#define PObj_live_CLEAR(o) gc_flag_CLEAR(live, o)

#define PObj_is_string_TEST(o) PObj_flag_TEST(is_string, o)
#define PObj_is_string_SET(o) PObj_flag_SET(is_string, o)
#define PObj_is_string_CLEAR(o) PObj_flag_CLEAR(is_string, o)

#define PObj_is_string_copy_TEST(o) PObj_flag_TEST(is_string_copy, o)
#define PObj_is_string_copy_SET(o) PObj_flag_SET(is_string_copy, o)
#define PObj_is_string_copy_CLEAR(o) PObj_flag_CLEAR(is_string_copy, o)

#define PObj_sysmem_TEST(o) PObj_flag_TEST(sysmem, o)
#define PObj_sysmem_SET(o) PObj_flag_SET(sysmem, o)
#define PObj_sysmem_CLEAR(o) PObj_flag_CLEAR(sysmem, o)


#define PObj_needs_early_gc_TEST(o) PObj_flag_TEST(needs_early_gc, o)
#define PObj_needs_early_gc_SET(o) PObj_flag_SET(needs_early_gc, o)
#define PObj_needs_early_gc_CLEAR(o) PObj_flag_CLEAR(needs_early_gc, o)

#define PObj_high_priority_gc_TEST(o)   PObj_flag_TEST(high_priority_gc, o)
#define PObj_high_priority_gc_SET(o)     PObj_flag_SET(high_priority_gc, o)
#define PObj_high_priority_gc_CLEAR(o) PObj_flag_CLEAR(high_priority_gc, o)

#define PObj_custom_mark_SET(o)   PObj_flag_SET(custom_mark, o)
#define PObj_custom_mark_CLEAR(o)   PObj_flag_CLEAR(custom_mark, o)
#define PObj_custom_mark_TEST(o)   PObj_flag_TEST(custom_mark, o)

#define PObj_custom_destroy_SET(o)   PObj_flag_SET(custom_destroy,   o)
#define PObj_custom_destroy_TEST(o)  PObj_flag_TEST(custom_destroy,  o)
#define PObj_custom_destroy_CLEAR(o) PObj_flag_CLEAR(custom_destroy, o)

#define PObj_is_class_SET(o) PObj_flag_SET(is_class, o)
#define PObj_is_class_TEST(o) PObj_flag_TEST(is_class, o)
#define PObj_is_class_CLEAR(o) PObj_flag_CLEAR(is_class, o)

#define PObj_is_object_SET(o) PObj_flag_SET(is_object, o)
#define PObj_is_object_TEST(o) PObj_flag_TEST(is_object, o)
#define PObj_is_object_CLEAR(o) PObj_flag_CLEAR(is_object, o)

#define PObj_is_PMC_TEST(o) PObj_flag_TEST(is_PMC, o)

#define PObj_is_PMC_shared_TEST(o) PObj_flag_TEST(is_PMC_shared, o)
#define PObj_is_PMC_shared_SET(o)  PObj_flag_SET(is_PMC_shared, o)
#define PObj_is_PMC_shared_CLEAR(o) PObj_flag_CLEAR(is_PMC_shared, o)

#define PObj_is_shared_TEST(o) PObj_flag_TEST(is_shared, o)
#define PObj_is_shared_SET(o)  PObj_flag_SET(is_shared, o)
#define PObj_is_shared_CLEAR(o) PObj_flag_CLEAR(is_shared, o)

#define PObj_is_new_TEST(o) PObj_flag_TEST(is_new, o)
#define PObj_is_new_SET(o)  PObj_flag_SET(is_new, o)
#define PObj_is_new_CLEAR(o) PObj_flag_CLEAR(is_new, o)

#define PObj_GC_on_dirty_list_TEST(o)  PObj_flag_TEST(GC_on_dirty_list, o)
#define PObj_GC_on_dirty_list_SET(o)   PObj_flag_SET(GC_on_dirty_list, o)
#define PObj_GC_on_dirty_list_CLEAR(o) PObj_flag_CLEAR(GC_on_dirty_list, o)

#define PObj_GC_need_write_barrier_TEST(o)  PObj_flag_TEST(GC_need_write_barrier, o)
#define PObj_GC_need_write_barrier_SET(o)   PObj_flag_SET(GC_need_write_barrier, o)
#define PObj_GC_need_write_barrier_CLEAR(o) PObj_flag_CLEAR(GC_need_write_barrier, o)

#define PObj_GC_soil_root_TEST(o)  PObj_flag_TEST(GC_soil_root, o)
#define PObj_GC_soil_root_SET(o)   PObj_flag_SET(GC_soil_root, o)
#define PObj_GC_soil_root_CLEAR(o) PObj_flag_CLEAR(GC_soil_root, o)


/* some combinations */
#define PObj_is_external_or_free_TESTALL(o) (PObj_get_FLAGS(o) & \
            (UINTVAL)(PObj_external_FLAG|PObj_on_free_list_FLAG))

#define PObj_is_external_CLEARALL(o) (PObj_get_FLAGS(o) &= \
            ~(UINTVAL)(PObj_external_FLAG|PObj_sysmem_FLAG))

#define PObj_is_live_or_free_TESTALL(o) (PObj_get_FLAGS(o) & \
        (PObj_live_FLAG | PObj_on_free_list_FLAG))

#define PObj_is_movable_TESTALL(o) (!(PObj_get_FLAGS(o) & \
        (PObj_sysmem_FLAG | PObj_on_free_list_FLAG | \
         PObj_constant_FLAG | PObj_external_FLAG)))

#define PObj_is_growable_TESTALL(o) (!(PObj_get_FLAGS(o) & \
        (PObj_sysmem_FLAG | PObj_is_string_copy_FLAG | \
         PObj_constant_FLAG | PObj_external_FLAG)))

#define PObj_custom_mark_destroy_SETALL(o) \
    (PObj_get_FLAGS(o) |= PObj_custom_mark_FLAG | PObj_custom_destroy_FLAG)

#define PObj_gc_CLEAR(o) (PObj_get_FLAGS(o) \
    &= ~PObj_custom_destroy_FLAG \
     & ~PObj_custom_mark_FLAG \
     & ~PObj_live_FLAG)

/* Thread debugging aid for catching PMCs ending up on the wrong interp */

#ifdef THREAD_DEBUG
#  define PARROT_ASSERT_INTERP(pmc, interp) \
    PARROT_ASSERT((pmc) == NULL || PMC_IS_NULL(pmc) || \
            PObj_is_shared_TEST(pmc) || (pmc)->orig_interp == (interp))
#else
#  define PARROT_ASSERT_INTERP(pmc, interp)
#endif

#endif /* PARROT_POBJ_H_GUARD */

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
 */
