typedef ArenaArray<u8> Serializer;

force_inline Serializer* new_serializer (Arena_Reserve new_reserve, string label="") {
  string serializer_label = format_string(temp(), "new_serializer: (%s)", label.data);
  return arena_array_new<u8>(1, new_reserve, serializer_label);
}

force_inline void reset_serializer (Serializer* serializer) {
#if BUILD_DEBUG
  array_poison_range(*serializer, 0, serializer->count);
#endif
  reset_keeping_memory(*serializer);
}

force_inline void free_serializer (Serializer* serializer) {
  arena_array_free(*serializer);
}

// force_inline ArrayView<u8> to_view (Serializer* serializer); 
// #redundant, just call to_view (ArenaArray<T>&)

force_inline s64 count_bytes (Serializer* serializer) {
  return serializer->count;
}

template <typename T> force_inline void Add (Serializer* serializer, T item) {
  u8* current_point = &serializer->data[serializer->count];
  s64 final_count = serializer->allocated + sizeof(T);
  
  if (serializer->allocated < final_count) {
    array_reserve(*serializer, final_count);
  }
  
  memcpy(current_point, &item, sizeof(T));
  
  serializer->count += sizeof(T);
}

template <typename T> force_inline void AddArray_NoSize (Serializer* serializer, ArrayView<T> view) {
  u8* current_point = &serializer->data[serializer->count];
  s64 final_count = serializer->allocated + (view.count * sizeof(T));
  
  if (serializer->allocated < final_count) {
    array_reserve(*serializer, final_count);
  }
  
  memcpy(current_point, view.data, view.count * sizeof(T));
  
  serializer->count += view.count * sizeof(T);
}

template <typename T> force_inline void AddArray (Serializer* serializer, ArrayView<T> view) {
  Add(serializer, view.count);
  AddArray_NoSize(serializer, view);
}

force_inline void AddString (Serializer* serializer, string s) {
  Add(serializer, s.count);
  AddArray_NoSize(serializer, to_view(s));
}

force_inline void AddString32 (Serializer* serializer, string s) {
  u32 string_length = (u32)s.count;
  Add(serializer, string_length);
  AddArray_NoSize(serializer, to_view(s));
}

force_inline void AddString16 (Serializer* serializer, string s) {
  u16 string_length = (u16)s.count;
  Add(serializer, string_length);
  AddArray_NoSize(serializer, to_view(s));
}

struct Deserializer { // #downcasts to ArrayView<u8>
  s64 count;
  u8* data;
  s64 cursor;
  
  Deserializer (ArrayView<u8> view) {
    count  = view.count;
    data   = view.data;
    cursor = 0;
  }
};

template <typename T> force_inline void Read (Deserializer* ds, T* item) {
  u8* current_point = &ds->data[ds->cursor];
  
  memcpy(item, current_point, sizeof(T));
  
  ds->cursor += sizeof(T);
}

template <typename T> force_inline void ReadArrayView (Deserializer* ds, ArrayView<T>& view, s64 view_count) {
  view.count = view_count;
  view.data  = (T*)&ds->data[ds->cursor];
  
  ds->cursor += (view_count * sizeof(T));
}

// Here array should be allocated and have a non-zero count!
template <typename T> force_inline void ReadArray (Deserializer* ds, ArrayView<T> view) {
  u8* current_point = &ds->data[ds->cursor];
  memcpy(view.data, current_point, view.count * sizeof(T));
  
  ds->cursor += view.count * sizeof(T);
}

template <typename T> force_inline ArrayView<T> ReadSizedArray (Deserializer* ds) { // #allocates
  ArrayView<T> array;
  
  Read(ds, &array.count);
  array.data = NewArray<T>(array.count, false);
  ReadArray(ds, array);
  
  return array;
}

template <typename T> void ReadToArenaArray (Deserializer* ds, ArenaArray<T>* aa) {
  ArrayView<T> temp;
  Read(ds, &temp.count);
  ReadArrayView(ds, temp, temp.count);
  copy_from_view(aa, temp);
}

force_inline void ReadStringView (Deserializer* ds, string& sv, s64 view_count) {
  sv.count = view_count;
  sv.data  = &ds->data[ds->cursor];
  
  ds->cursor += view_count;
}

force_inline void ReadString (Deserializer* ds, string& s) { // #no_alloc
  Read(ds, &s.count);
  ReadStringView(ds, s, s.count);
}

force_inline void ReadString32 (Deserializer* ds, string& s) { // #no_alloc
  u32 str_len = 0;
  Read(ds, &str_len);
  ReadStringView(ds, s, (s64)str_len);
}

force_inline void ReadString16 (Deserializer* ds, string& s) { // #no_alloc
  u16 str_len = 0;
  Read(ds, &str_len);
  ReadStringView(ds, s, (s64)str_len);
}

// This is specialized for filesystem storage of strings.
force_inline u32 AddString_NoCount (Serializer* serializer, u8* data, s16 count) {
  u32 original_count = (u32)serializer->count;
  u8* current_point = &serializer->data[original_count];
  
  s64 final_count = serializer->allocated + (count * sizeof(u8));
  
  if (serializer->allocated < final_count) {
    array_reserve(*serializer, final_count);
  }
  
  memcpy(current_point, data, count * sizeof(u8));
  serializer->count += count * sizeof(u8);
  
  return original_count;
}
