Я отправил коммерческое программное обеспечение, которое делает именно это. В последней итерации мы закончили сортировку блоков файла по «типу» и «индексу», чтобы вы могли прочитать или записать «третий блок типа foo». Файл в итоге был структурирован как:
1) Заголовок файла. Указывает на мастер тип списка.
2) Данные. Каждый блок имеет заголовок с типом, индексом, логическим размером и дополненным размером.
3) Массивы (смещение, размер) кортежей для каждого данного типа.
4) Массив (тип, смещение, количество), который отслеживает типы.
Мы определили это так, чтобы каждый блок был атомной единицей. Вы начали писать новый блок и заканчивали писать, прежде чем начинать что-либо еще. Вы также можете «установить» содержимое блока. Начало нового блока всегда добавляется в конец файла, так что вы можете добавлять столько, сколько хотите, без фрагментации блока. «Установка» блока может повторно использовать пустой блок.
Когда вы открыли файл, мы загрузили все индексы в оперативную память. Когда вы очищаете или закрываете файл, мы перезаписываем каждый индекс, который изменился, в конце файла, затем перезаписываем индекс индекса в конце файла, затем обновляем заголовок спереди. Это означает, что все изменения в файле были атомарными - либо вы фиксируете в точке, где обновляется заголовок, либо нет. (Некоторые системы используют две копии заголовка на расстоянии 8 кБ для сохранения заголовков, даже если сектор диска выходит из строя; мы не зашли так далеко)
Один из блоков "types" был "free block". При перезаписи измененных индексов и при замене содержимого блока старое пространство на диске было объединено в свободный список, содержащийся в массиве свободных блоков. Смежные свободные блоки были объединены в один больший блок. Свободные блоки использовались повторно, когда вы «устанавливали содержимое» или для обновленных индексов блоков типа, но не для индекса индекса, который всегда записывался последним.
Поскольку индексы всегда хранились в памяти, работа с открытым файлом была очень быстрой - обычно всего одно чтение, чтобы получить данные одного блока (или получить дескриптор блока для потоковой передачи). Открытие и закрытие было немного сложнее, так как нужно было загружать и очищать индексы. Если это станет проблемой, мы могли бы загружать вторичный индекс типа по требованию, а не авансом, чтобы амортизировать эти затраты, но это никогда не было проблемой для нас.
Главный приоритет для постоянного (на диске) хранилища: надежность! Не теряйте данные, даже если компьютер теряет питание во время работы с файлом!
Второй приоритет для хранения на диске: не делайте больше операций ввода-вывода, чем необходимо! Ищет дорого. На флеш-накопителях каждый отдельный ввод-вывод дорог, а запись - вдвойне. Попробуйте выровнять и пакетировать ввод / вывод. Использование чего-то вроде malloc () для хранения на диске, как правило, не очень хорошо, потому что оно выполняет слишком много операций поиска. Это также причина, по которой мне не нравятся файлы с отображением в памяти - люди склонны относиться к ним как к ОЗУ, и тогда шаблон ввода-вывода становится очень дорогим.