mcap/modules/Basic/serialization.jai

144 lines
4.4 KiB
Plaintext

Deserializer :: struct { // downcasts to []u8
count: s64;
data: *u8;
cursor: s64;
ok := true; // cleared by any read that would run past the end; never set back
}
to_deserializer :: (view: $T/.[[]u8, string]) -> Deserializer #expand {
return .{view.count, view.data, 0, true};
}
deserializer_can_read :: inline (d: *Deserializer, byte_count: s64) -> bool {
if byte_count < 0 || d.cursor < 0 || d.cursor > d.count || byte_count > d.count - d.cursor {
d.ok = false;
return false;
}
return true;
}
Read :: inline (d: *Deserializer, $T: Type) -> T {
mem: T = ---;
if !deserializer_can_read(d, size_of(T)) {
Initialize(*mem); // a failed read yields a zero value
return mem;
}
memcpy(*mem, d.data + d.cursor, size_of(T));
d.cursor += size_of(T);
return mem;
}
ReadArrayView :: inline (d: *Deserializer, view_count: s64, $T: Type) -> []T {
// Range-check the count before multiplying, so a hostile count cannot overflow it.
if view_count < 0 || d.cursor < 0 || d.cursor > d.count
|| view_count > (d.count - d.cursor) / size_of(T) {
d.ok = false;
return .[];
}
array: []T = .{view_count, cast(*T)(d.data + d.cursor)};
d.cursor += view_count * size_of(T);
return array;
}
ReadSizedArray :: inline (d: *Deserializer, $T: Type, $count_type: Type = s64) -> []T {
view_count := xx Read(d, count_type);
return ReadArrayView(d, view_count, T);
}
ReadStringView :: inline (d: *Deserializer, view_count: s64) -> string {
if !deserializer_can_read(d, view_count) return "";
s := string.{view_count, d.data + d.cursor};
d.cursor += view_count;
return s;
}
ReadSizedString :: inline (d: *Deserializer, $count_type: Type = s64) -> string {
view_count : s64 = xx Read(d, count_type);
return ReadStringView(d, view_count);
}
// Use Resizable_Array APIs directly: e.g. array_reset_keeping_memory, array_free, array_reset...
Serializer :: [..]u8;
new_serializer :: inline (reserve_count: s64) -> Serializer {
serializer: Serializer;
array_reserve(*serializer, reserve_count);
return serializer;
}
Add :: inline (s: *Serializer, item: $T) {
// we use the offset, do NOT use s.data, because the pointer
// can move on resize, so it is no longer valid!
starting_offset := s.count;
array_resize(s, s.count + size_of(T));
memcpy(s.data + starting_offset, *item, size_of(T));
}
AddArray_NoSize :: inline (s: *Serializer, view: []$T) {
starting_offset := s.count;
array_resize(s, s.count + (view.count * size_of(T)));
memcpy(s.data + starting_offset, view.data, view.count * size_of(T));
}
AddSizedArray :: inline (s: *Serializer, view: []$T, $count_type: Type = s64) -> bool {
// A count that does not survive count_type would be written truncated and then followed by
// the full payload, so every field after it reads from the wrong offset. The round trip
// catches that for any count_type; cast,no_check keeps a debug build from aborting on the
// cast before this can report it.
view_count := cast,no_check(count_type) view.count;
if cast(s64) view_count != view.count {
log_error("[AddSizedArray] a % element array does not fit a % length prefix.", view.count, count_type);
return false;
}
Add(s, view_count);
AddArray_NoSize(s, view);
return true;
}
AddString :: inline (s: *Serializer, view: string, $count_type: Type = s64) -> bool {
view_count := cast,no_check(count_type) view.count;
if cast(s64) view_count != view.count {
log_error("[AddString] a % byte string does not fit a % length prefix.", view.count, count_type);
return false;
}
Add(s, view_count);
AddArray_NoSize(s, cast([]u8)view);
return true;
}
// This has specialized use cases where the offsets are saved in a separate array. e.g. SoA filesystem string storage.
AddString_NoSize :: inline (s: *Serializer, data: *u8, count: $T, $offset_type: Type = s32) -> offset_type {
offset := cast(offset_type) s.count;
AddArray_NoSize(s, []u8.{count, data});
return offset;
}
AddString_PaddedTo8ByteAlignment :: (s: *Serializer, view: string) {
// The purpose of this function is to zero-pad a string to
// 8-bytes so we don't run into annoying unaligned copy issues.
Add(s, view.count);
AddArray_NoSize(s, cast([]u8)view);
starting_offset := s.count;
remainder := align_forward(view.count, size_of(s64)) - view.count;
array_resize(s, s.count + remainder);
// Fill remaining space with zeros
memset(s.data + starting_offset, 0, remainder);
}