struct MVMHeapDumpIndexSnapshotEntry {
    MVMuint64 collectables_size;
    MVMuint64 full_refs_size;
    MVMuint64 refs_middlepoint;
    MVMuint64 incremental_data;
};

/* Used in version 2 of the heap snapshot format */
struct MVMHeapDumpIndex {
    MVMuint64 stringheap_size;
    MVMuint64 types_size;
    MVMuint64 staticframes_size;

    MVMuint64 snapshot_size_entries;
    MVMHeapDumpIndexSnapshotEntry *snapshot_sizes;

    MVMuint64 snapshot_sizes_alloced;
};

/* Used in version 3 of the heap snapshot format.
 * There is one top-level TOC for the whole file,
 * but every snapshot in the file has a TOC of its
 * own that's got an entry in the top-level TOC.
 */
struct MVMHeapDumpTableOfContents {
    MVMuint32 toc_entry_alloc;
    MVMuint32 toc_entry_used;

    char **toc_words;
    MVMuint64 *toc_positions;
};

struct MVMHeapSnapshotStats {
    MVMuint64 type_stats_alloc;

    MVMuint32 *type_counts;
    MVMuint64 *type_size_sum;

    MVMuint64 sf_stats_alloc;

    MVMuint32 *sf_counts;
    MVMuint64 *sf_size_sum;
};

/* A collection of heap snapshots, with common type and static frame names.
 * Note that we take care to never refer to heap objects themselves in here,
 * including for types and frames, since to do so would extend their lifetime
 * for the whole program, which would render the results pretty bogus. */
struct MVMHeapSnapshotCollection {
    /* Snapshot we are currently taking and its index */
    MVMHeapSnapshot *snapshot;
    MVMuint64 snapshot_idx;

    /* Known types/REPRs. Just a list for now, but we might like to look at a
     * hash or trie if this ends up making taking a snapshot wicked slow. */
    MVMHeapSnapshotType *types;
    MVMuint64 num_types;
    MVMuint64 alloc_types;

    /* Known static frames. Same applies to searching this as to the above. */
    MVMHeapSnapshotStaticFrame *static_frames;
    MVMuint64 num_static_frames;
    MVMuint64 alloc_static_frames;

    /* Strings, referenced by index from various places. Also a "should we
     * free it" flag for each one. */
    char **strings;
    MVMuint64 num_strings;
    MVMuint64 alloc_strings;
    char *strings_free;
    MVMuint64 num_strings_free;
    MVMuint64 alloc_strings_free;

    MVMuint64 types_written;
    MVMuint64 static_frames_written;
    MVMuint64 strings_written;

    /* For heap snapshot format 2, an index */
    MVMHeapDumpIndex *index;

    /* For heap snapshot format 3, table of contents */
    MVMHeapDumpTableOfContents *toplevel_toc;
    MVMHeapDumpTableOfContents *second_level_toc;

    /* When the heap snapshot recording was started */
    MVMuint64 start_time;

    /* Counts for the current recording to make an overview */
    MVMuint64 total_heap_size;
    MVMuint64 total_objects;
    MVMuint64 total_typeobjects;
    MVMuint64 total_stables;
    MVMuint64 total_frames;

    /* The file handle we are outputting to */
    FILE *fh;
};

/* An individual heap snapshot. */
struct MVMHeapSnapshot {
    /* Array of data about collectables on the heap. */
    MVMHeapSnapshotCollectable *collectables;
    MVMuint64 num_collectables;
    MVMuint64 alloc_collectables;

    /* References.  */
    MVMHeapSnapshotReference *references;
    MVMuint64 num_references;
    MVMuint64 alloc_references;

    MVMHeapSnapshotStats *stats;

    /* When the snapshot was recorded */
    MVMuint64 record_time;
};

/* An object/type object/STable type in the snapshot. */
struct MVMHeapSnapshotType {
    /* String heap index of the REPR name. */
    MVMuint32 repr_name;

    /* String heap index of the type's debug name. */
    MVMuint32 type_name;
};

/* A static frame in the snapshot. */
struct MVMHeapSnapshotStaticFrame {
    /* The static frame name; index into the snapshot collection string heap. */
    MVMuint32 name;

    /* The static frame compilation unit ID, for added uniqueness checking.
     * Also an index into the string heap. */
    MVMuint32 cuid;

    /* The line number where it's declared. */
    MVMuint32 line;

    /* And the filename; also an index into snapshot collection string heap. */
    MVMuint32 file;
};

/* Kinds of collectable, plus a few "virtual" kinds to cover the various places
 * we find roots. MVM_SNAPSHOT_COL_KIND_ROOT is the ultimate root of the heap
 * snapshot and everything hangs off it. */
#define MVM_SNAPSHOT_COL_KIND_OBJECT            1
#define MVM_SNAPSHOT_COL_KIND_TYPE_OBJECT       2
#define MVM_SNAPSHOT_COL_KIND_STABLE            3
#define MVM_SNAPSHOT_COL_KIND_FRAME             4
#define MVM_SNAPSHOT_COL_KIND_PERM_ROOTS        5
#define MVM_SNAPSHOT_COL_KIND_INSTANCE_ROOTS    6
#define MVM_SNAPSHOT_COL_KIND_CSTACK_ROOTS      7
#define MVM_SNAPSHOT_COL_KIND_THREAD_ROOTS      8
#define MVM_SNAPSHOT_COL_KIND_ROOT              9
#define MVM_SNAPSHOT_COL_KIND_INTERGEN_ROOTS    10
#define MVM_SNAPSHOT_COL_KIND_CALLSTACK_ROOTS   11

/* Data about an individual collectable in the heap snapshot. Ordered to avoid
 * holes. */
struct MVMHeapSnapshotCollectable {
    /* What kind of collectable is it? */
    MVMuint16 kind;

    /* Self-size (from the collectable header). */
    MVMuint16 collectable_size;

    /* Index into the snapshot collection type name or frame info array,
     * depending on kind. */
    MVMuint32 type_or_frame_index;

    /* The number of other collectables this one references. */
    MVMuint32 num_refs;

    /* Index into the references info list. */
    MVMuint64 refs_start;

    /* Unmanaged size (memory held but not under the GC's contorl). */
    MVMuint64 unmanaged_size;
};

/* Reference identifier kinds. */
#define MVM_SNAPSHOT_REF_KIND_UNKNOWN   0
#define MVM_SNAPSHOT_REF_KIND_INDEX     1
#define MVM_SNAPSHOT_REF_KIND_STRING    2

/* Number of bits needed for ref kind. */
#define MVM_SNAPSHOT_REF_KIND_BITS      2

/* A reference from one collectable to another. */
struct MVMHeapSnapshotReference {
    /* The lower MVM_SNAPSHOT_REF_KIND_BITS bits indicate the type of reference.
     * After shifting those away, we either have a numeric index (e.g. for
     * array indexes) or an index into the string heap (for lexicals in frames
     * and attributes in objects). If kind is MVM_SNAPSHOT_REF_KIND_UNKNOWN the
     * rest of the bits will be zero; we know nothing of the relationship. */
    MVMuint64 description;

    /* The index of the collectable referenced. */
    MVMuint64 collectable_index;
};

/* Current state object whlie taking a heap snapshot. */
struct MVMHeapSnapshotState {
    /* The heap snapshot collection and current working snapshot. */
    MVMHeapSnapshotCollection *col;
    MVMHeapSnapshot *hs;

    /* Our current collectable worklist. */
    MVMHeapSnapshotWorkItem *workitems;
    MVMuint64 num_workitems;
    MVMuint64 alloc_workitems;

    /* The collectable we're currently adding references for. */
    MVMuint64 ref_from;

    /* The seen hash of collectables (including frames). */
    MVMPtrHashTable seen;

    /* We sometimes use GC mark functions to find references. Keep a worklist
     * around for those times (much cheaper than allocating it whenever we
     * need it). */
    MVMGCWorklist *gcwl;

    /* Many reprs have only one type (BOOTCode) or two
     * types (P6int, BOOTInt), so caching string index
     * per repr id is worth a lot. */
    MVMuint64 repr_str_idx_cache[MVM_REPR_MAX_COUNT];
    MVMuint64 type_str_idx_cache[MVM_REPR_MAX_COUNT];
    MVMuint64 anon_repr_type_str_idx_cache[MVM_REPR_MAX_COUNT];

    MVMuint32 type_of_type_idx_cache[8];
    MVMuint32 repr_of_type_idx_cache[8];
    MVMuint32 type_idx_cache[8];

    MVMuint8 type_idx_rotating_insert_slot;
};

/* Work item used while taking a heap snapshot. */
struct MVMHeapSnapshotWorkItem {
    /* The kind of collectable. */
    MVMuint16 kind;

    /* Index in the collectables (assigned upon adding to the worklist). */
    MVMuint64 col_idx;

    /* Target collectable, if any. */
    void *target;
};

MVMint32 MVM_profile_heap_profiling(MVMThreadContext *tc);
void MVM_profile_heap_start(MVMThreadContext *tc, MVMObject *config);
void MVM_profile_heap_take_snapshot(MVMThreadContext *tc);
MVMObject * MVM_profile_heap_end(MVMThreadContext *tc);

/* API for things that want to contribute more detailed data to the heap
 * profile. */
MVM_PUBLIC void MVM_profile_heap_add_collectable_rel_const_cstr(MVMThreadContext *tc,
    MVMHeapSnapshotState *ss, MVMCollectable *collectable, const char *desc);
MVM_PUBLIC void MVM_profile_heap_add_collectable_rel_const_cstr_cached(MVMThreadContext *tc,
    MVMHeapSnapshotState *ss, MVMCollectable *collectable, const char *desc, MVMuint64 *cache);
MVM_PUBLIC void MVM_profile_heap_add_collectable_rel_vm_str(MVMThreadContext *tc,
    MVMHeapSnapshotState *ss, MVMCollectable *collectable, MVMString *desc);
MVM_PUBLIC void MVM_profile_heap_add_collectable_rel_idx(MVMThreadContext *tc,
    MVMHeapSnapshotState *ss, MVMCollectable *collectable, MVMuint64 idx);
