/* This represents the root of the serialization data; everything hangs
 * off this. In read mode, we don't do much besides populate and then
 * read this. In write mode, however, the tables and data chunks will be
 * filled out and grown as needed. */
struct MVMSerializationRoot {
    /* The version of the serialization format. */
    MVMint32 version;

    /* How many parameterized type intern entries we have */
    MVMint32  num_param_interns;

    /* The SC we're serializing/deserializing. */
    MVMSerializationContext *sc;

    /* List of the serialization context objects that we depend on. */
    MVMSerializationContext **dependent_scs;

    /* The number of dependencies, as well as a pointer to the
     * dependencies table. */
    char     *dependencies_table;
    MVMuint32 num_dependencies;

    /* The number of STables, as well as pointers to the STables
     * table and data chunk. */
    MVMint32  num_stables;
    char     *stables_table;
    char     *stables_data;

    /* The number of objects, as well as pointers to the objects
     * table and data chunk. */
    char     *objects_table;
    char     *objects_data;
    MVMint32  num_objects;

    /* The number of closures, as we as a pointer to the closures
     * table. */
    MVMint32  num_closures;
    char     *closures_table;

    /* The number of contexts (e.g. frames), as well as pointers to
     * the contexts table and data chunk. */
    char     *contexts_table;
    char     *contexts_data;
    MVMuint32 num_contexts;

    /* The number of repossessions and pointer to repossessions table. */
    MVMint32  num_repos;
    char     *repos_table;

    /* The the data segment containing them parameterized type intern entries */
    char     *param_interns_data;

    /* Array of strings making up the string heap we are constructing. If we
     * are reading, this will either contain a string heap array or be NULL
     * and the next field will be set. */
    MVMObject *string_heap;

    /* The compilation unit whose string heap we will use to locate strings.
     * This must be set of string_heap about is set to NULL. */
    MVMCompUnit *string_comp_unit;
};

/* Indexes the deserializer still has to work on. */
struct MVMDeserializeWorklist {
    MVMuint32 *indexes;
    MVMuint32  num_indexes;
    MVMuint32  alloc_indexes;
};

/* Represents the serialization reader and the various functions available
 * on it. */
struct MVMSerializationReader {
    /* Serialization root data. */
    MVMSerializationRoot root;

    /* Current offsets for the data chunks (also correspond to the amount of
     * data written in to them). */
    MVMint32 stables_data_offset;
    MVMint32 objects_data_offset;
    MVMint32 contexts_data_offset;
    MVMint32 param_interns_data_offset;

    /* Limits up to where we can read stables, objects and contexts data. */
    char *stables_data_end;
    char *objects_data_end;
    char *contexts_data_end;
    char *param_interns_data_end;

    /* Where to find details related to the current buffer we're reading from:
     * the buffer pointer itself, the current offset and the amount that is
     * allocated. These are all pointers back into this data structure. */
    char     **cur_read_buffer;
    MVMint32  *cur_read_offset;
    char     **cur_read_end;

    /* List of code objects (static first, then all the closures). */
    MVMObject *codes_list;

    /* Number of static code objects. */
    MVMuint32 num_static_codes;

    /* Whether we're already working on these worklists. */
    AO_t working;

    /* Array of contexts (num_contexts in length). */
    MVMFrame **contexts;

    /* Set of current worklists, for things we need to fully desrialize. When
     * they are all empty, the current (usually lazy) deserialization work is
     * done, and we have the required object graph. */
    MVMDeserializeWorklist wl_objects;
    MVMDeserializeWorklist wl_stables;

    /* The current object we're deserializing. */
    MVMObject *current_object;

    /* The data, which we may want to free when the SC goes away; a flag
     * indicates when it should be. */
    char      *data;
    MVMuint32  data_needs_free;
};

/* Represents the serialization writer and the various functions available
 * on it. */
struct MVMSerializationWriter {
    /* Serialization root data. */
    MVMSerializationRoot root;

    /* The code refs we're working through/adding to. */
    MVMObject *codes_list;

    /* Frames that we're to serialize, along with memory management. */
    MVMFrame **contexts_list;
    MVMuint32 num_contexts;
    MVMuint32 alloc_contexts;

    /* Current position in the stables, objects and contexts lists. */
    MVMuint64 stables_list_pos;
    MVMuint64 objects_list_pos;
    MVMuint64 contexts_list_pos;

    /* Hash of strings we've already seen while serializing to the index they
     * are placed at in the string heap. */
    MVMObject *seen_strings;

    /* Amount of memory allocated for various things. */
    MVMuint32 dependencies_table_alloc;
    MVMuint32 stables_table_alloc;
    MVMuint32 stables_data_alloc;
    MVMuint32 objects_table_alloc;
    MVMuint32 objects_data_alloc;
    MVMuint32 closures_table_alloc;
    MVMuint32 contexts_table_alloc;
    MVMuint32 contexts_data_alloc;
    MVMuint32 repos_table_alloc;
    MVMuint32 param_interns_data_alloc;

    /* Current offsets for the data chunks (also correspond to the amount of
     * data written in to them). */
    MVMuint32 stables_data_offset;
    MVMuint32 objects_data_offset;
    MVMuint32 contexts_data_offset;
    MVMuint32 param_interns_data_offset;

    /* Where to find details related to the current buffer we're writing in
     * to: the buffer pointer itself, the current offset and the amount that
     * is allocated. These are all pointers back into this data structure. */
    char      **cur_write_buffer;
    MVMuint32  *cur_write_offset;
    MVMuint32  *cur_write_limit;
};

/* Core serialize and deserialize functions. */
void MVM_serialization_deserialize(MVMThreadContext *tc, MVMSerializationContext *sc,
    MVMObject *string_heap, MVMObject *codes_static, MVMObject *repo_conflicts,
    MVMString *data);
MVMString * MVM_sha1(MVMThreadContext *tc, MVMString *str);
MVMObject * MVM_serialization_serialize(MVMThreadContext *tc, MVMSerializationContext *sc,
    MVMObject *empty_string_heap, MVMObject *type);

/* Functions for demanding an object/STable/code be made available (that is,
 * by lazily deserializing it). */
MVMObject * MVM_serialization_demand_object(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx);
MVMSTable * MVM_serialization_demand_stable(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx);
MVMObject * MVM_serialization_demand_code(MVMThreadContext *tc, MVMSerializationContext *sc, MVMint64 idx);

/* Reader/writer functions. */
MVMint64 MVM_serialization_read_int64(MVMThreadContext *tc, MVMSerializationReader *reader);
MVMint64 MVM_serialization_read_int(MVMThreadContext *tc, MVMSerializationReader *reader);
MVMnum64 MVM_serialization_read_num(MVMThreadContext *tc, MVMSerializationReader *reader);
MVMString * MVM_serialization_read_str(MVMThreadContext *tc, MVMSerializationReader *reader);
void * MVM_serialization_read_ptr(MVMThreadContext *tc, MVMSerializationReader *reader, size_t *size);
char * MVM_serialization_read_cstr(MVMThreadContext *tc, MVMSerializationReader *reader, size_t *len);
MVM_PUBLIC MVMObject * MVM_serialization_read_ref(MVMThreadContext *tc, MVMSerializationReader *reader);
MVMSTable * MVM_serialization_read_stable_ref(MVMThreadContext *tc, MVMSerializationReader *reader);
void MVM_serialization_force_stable(MVMThreadContext *tc, MVMSerializationReader *reader, MVMSTable *st);
void MVM_serialization_write_int(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMint64 value);
void MVM_serialization_write_num(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMnum64 value);
void MVM_serialization_write_str(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMString *value);
void MVM_serialization_write_ptr(MVMThreadContext *tc, MVMSerializationWriter *writer, const void *ptr, size_t size);
void MVM_serialization_write_cstr(MVMThreadContext *tc, MVMSerializationWriter *writer, const char *cstr);
MVM_PUBLIC void MVM_serialization_write_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *ref);
void MVM_serialization_write_stable_ref(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMSTable *st);
