The trade-off of course is that your data structure design is kinda sticky, insofar as you need to be able to open files for older versions.
The trade-off of course is that your data structure design is kinda sticky, insofar as you need to be able to open files for older versions.
The missing piece is a way to evolve those data structures over time, in a similar way to database migrations. Only when loading data from an older version of the app those operations would need to be issued, and then the app could save the updated version in disk immediately to avoid incurring that cost again.
An example implementation of this concept is this project [1] that was created in the context or CRDTs. If it is not directly applicable, at least it should be a good inspiration.
It was a complete pain in the ass. You were constantly future-proofing your data structures because you knew you were going to be stuck with them for all eternity because the I/O framework was going to serialize them verbatim whether you liked it or not. Those were dark days...
The other comment pointed out that you can make a fall back migration code path that migrates over older file versions. That's the escape hatch if you have no other options
https://learn.microsoft.com/en-us/openspecs/office_file_form...
Yeah. There is a reason XML was seen as the future back in the 90s.
Custom databases often get extended into custom filesystems. And these systems on top of systems get obscure features (like embedding excel sheets inside of Word) and... Thing get hairy.
This is usually orders of magnitude faster than serializing to/deserializing from a different storage format.
All this look like BS to justify laziness to me, and makes loading/saving operations fragile and setting in-memory structures in stone.
Once upon a time, DMA did not meaningfully exist in the x86 world, so an IO-bound task was also CPU-bound.
just because it's unreadable in a text editor doesn't mean it'll be fast
This was a common source of portability issues for game saves (as well as blowing your saves on updates), as they'd commonly just blit internal data structure, with no formalised interface.
You can version the structs/loaders by keeping the relevant header files in their own version subfolders, and copious usage of sed. I've done this in a Django project to maintain support for ancient untouchable clients using an old version of the API, it's pretty manageable.
It's not that unusual in C and C++ code to define a struct that has a specific and well-defined memory layout. It's kosher, as long as you accept that you're working with a specific set of real compilers and use the appropriate #pragmas or other controls as needed to avoid undefined behaviour.