/* Copyright (C) 2001-2019 Artifex Software, Inc.
   All Rights Reserved.

   This software is provided AS-IS with no warranty, either express or
   implied.

   This software is distributed under license and may not be copied,
   modified or distributed except as expressly authorized under the terms
   of the license contained in the file LICENSE in this distribution.

   Refer to licensing information at http://www.artifex.com or contact
   Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
   CA 94945, U.S.A., +1(415)492-9861, for further information.
*/


/* Memory manager implementation structures for Ghostscript */

#ifndef gxobj_INCLUDED
#  define gxobj_INCLUDED

#include "gxbitmap.h"
#include "gsstruct.h"
#include "memento.h" /* Because we use "free" below */

#ifndef IGC_PTR_STABILITY_CHECK
#  define IGC_PTR_STABILITY_CHECK 0
#endif

#ifndef GS_USE_MEMORY_HEADER_ID
#define GS_USE_MEMORY_HEADER_ID 1
#endif

#if GS_USE_MEMORY_HEADER_ID

  typedef gs_id hdr_id_t;

  extern hdr_id_t hdr_id;

# define HDR_ID_OFFSET (sizeof(obj_header_t) - offset_of(obj_header_t, d.o.hdr_id))

# ifdef DEBUG

# define ASSIGN_HDR_ID(obj) (*(hdr_id_t *)(((byte *)obj) - HDR_ID_OFFSET)) = hdr_id++

  gs_id get_mem_hdr_id (void *ptr);

# else /* DEBUG */

#  define ASSIGN_HDR_ID(obj_hdr)

# endif /* DEBUG */

#else

# define ASSIGN_HDR_ID(obj_hdr)
# define HDR_ID_OFFSET 0

#endif /* GS_USE_MEMORY_HEADER_ID */

/* ================ Objects ================ */

/*
 * Object headers have the form:
        -l- -mark/back-
        -size-
        -type/reloc-
 * l (aLone) is a single bit.  Mark/back is 1 bit shorter than a uint.  We
 * round the header size up to the next multiple of the most severe
 * alignment restriction (4 or 8 bytes).
 *
 * The mark/back field is used for the mark during the marking phase of
 * garbage collection, and for a back pointer value during the compaction
 * phase.  Since we want to be able to collect local VM independently of
 * global VM, we need two different distinguished mark values:
 *      - For local objects that have not been traced and should be freed
 *      (compacted out), we use 1...11 in the mark field (o_unmarked).
 *      - For global objects that have not been traced but should be kept,
 *      we use 1...10 in the mark field (o_untraced).
 * Note that neither of these values is a possible real relocation value.
 *
 * The back pointer's meaning depends on whether the object is
 * free (unmarked) or in use (marked):
 *      - In free objects, the back pointer is an offset from the object
 * header back to a clump_head_t structure that contains the location
 * to which all the data in this clump will get moved; the reloc field
 * contains the amount by which the following run of useful objects
 * will be relocated downwards.
 *      - In useful objects, the back pointer is an offset from the object
 * back to the previous free object; the reloc field is not used (it
 * overlays the type field).
 * These two cases can be distinguished when scanning a clump linearly,
 * but when simply examining an object via a pointer, the clump pointer
 * is also needed.
 */
#define obj_flag_bits 1
#define obj_mb_bits (ARCH_SIZEOF_INT * 8 - obj_flag_bits)
#define o_unmarked (((uint)1 << obj_mb_bits) - 1)
#define o_set_unmarked(pp)\
  ((pp)->o_smark = o_unmarked)
#define o_is_unmarked(pp)\
  ((pp)->o_smark == o_unmarked)
#define o_untraced (((uint)1 << obj_mb_bits) - 2)
#define o_set_untraced(pp)\
  ((pp)->o_smark = o_untraced)
#define o_is_untraced(pp)\
  ((pp)->o_smark == o_untraced)
#define o_marked 0
#define o_mark(pp)\
  ((pp)->o_smark = o_marked)
#define obj_back_shift obj_flag_bits
#define obj_back_scale (1 << obj_back_shift)
typedef struct obj_header_data_s {
    union _f {
        struct _h {
            unsigned alone:1, pad:obj_mb_bits;
        } h;
        struct _m {
            unsigned _:1, smark:obj_mb_bits;
        } m;
        struct _b {
            unsigned _:1, back:obj_mb_bits;
        } b;
    } f;
    obj_size_t size; /* Note, not size_t! */
    union _t {
        gs_memory_type_ptr_t type;
        size_t reloc;
    } t;
#   if IGC_PTR_STABILITY_CHECK
    unsigned space_id:3; /* r_space_bits + 1 bit for "instability". */
#   endif

#   if GS_USE_MEMORY_HEADER_ID
    hdr_id_t hdr_id; /* should be last, to save wasting space in the "strings" case. Makes object easier to trace thru GC */
#   endif
} obj_header_data_t;

/*
 * Define the alignment modulus for aligned objects.  We assume all
 * alignment values are powers of 2; we can avoid nested 'max'es that way.
 * The final | is because back pointer values are divided by obj_back_scale,
 * so objects must be aligned at least 0 mod obj_back_scale.
 *
 * Note: OBJECTS ARE NOT GUARANTEED to be aligned any more strictly than
 * required by the hardware, regardless of the value of obj_align_mod.
 * See gsmemraw.h for more information about this.
 */
#define obj_align_mod\
  (((ARCH_ALIGN_MEMORY_MOD - 1) |\
    (align_bitmap_mod - 1) |\
    (obj_back_scale - 1)) + 1)
/* The only possible values for obj_align_mod are 4, 8, or 16.... */
#if obj_align_mod == 4
#  define log2_obj_align_mod 2
#else
#if obj_align_mod == 8
#  define log2_obj_align_mod 3
#else
#if obj_align_mod == 16
#  define log2_obj_align_mod 4
#endif
#endif
#endif
#define obj_align_mask (obj_align_mod-1)
#define obj_align_round(siz)\
  (obj_size_t)(((siz) + obj_align_mask) & -obj_align_mod)
#define obj_size_round(siz)\
  obj_align_round((siz) + sizeof(obj_header_t))

/* Define the real object header type, taking alignment into account. */
struct obj_header_s {		/* must be a struct because of forward reference */
    union _d {
        obj_header_data_t o;
        byte _pad[ROUND_UP(sizeof(obj_header_data_t), obj_align_mod)];
    }
    d;
};

/* Define some reasonable abbreviations for the fields. */
#define o_alone d.o.f.h.alone
#define o_pad d.o.f.h.pad
#define o_back d.o.f.b.back
#define o_smark d.o.f.m.smark
#define o_size d.o.size
#define o_type d.o.t.type
#define o_nreloc d.o.t.reloc

/*
 * The macros for getting the sizes of objects all take pointers to
 * the object header, for use when scanning storage linearly.
 */
#define pre_obj_contents_size(pp)\
  ((pp)->o_size)

#define pre_obj_rounded_size(pp)\
  obj_size_round(pre_obj_contents_size(pp))
#define pre_obj_next(pp)\
  ((obj_header_t *)((byte *)(pp) + obj_align_round(\
    pre_obj_contents_size(pp) + sizeof(obj_header_t) )))

/*
 * Define the header that free objects point back to when relocating.
 * Every clump, including inner clumps, has one of these.
 */
typedef struct clump_head_s {
    byte *dest;			/* destination for objects */
#if obj_align_mod > ARCH_SIZEOF_PTR
    byte *_pad[obj_align_mod / ARCH_SIZEOF_PTR - 1];
#endif
    obj_header_t free;		/* header for a free object, */
    /* in case the first real object */
    /* is in use */
} clump_head_t;

#endif /* gxobj_INCLUDED */
