Сильно отредактированное вступление Сначала я раскритиковал описание проблемы как сбивающее с толку. Мои проблемы проистекают из того, как в вопросе используется понятие «файл». Вопрос подразумевает, что диск, программы и дорожки хранятся в одном «файле». Я думал, что спрашивающий строит свою собственную файловую систему, что сделает эту структуру «все в одном файле» странной, но теперь я решил, что он, вероятно, этого не делает, и в этом случае это менее странно. Поэтому я продолжу и предложу реальный ответ, исходя из предположения, что он использует существующую (возможно, стандартную) файловую систему, и вся его структура данных хранится в одном файле в этой файловой системе. Если я ошибаюсь, без сомнения, меня поправят.
Сначала я предложу один общий совет для подобных ситуаций; Сначала посмотрите на вещи с точки зрения пользователя API. Затем спроектируйте свой API так, чтобы код, который он будет писать, работал легко, без необходимости работать с деталями, которые правильно находятся в вашем домене.
Один из способов работы над дизайном API - сначала написать некоторый код пользователя и определить API, чтобы его было легко писать. В качестве бонуса, после того, как вы на самом деле реализуете API, у вас будет тестовый код, чтобы опробовать его.
Переходя к более конкретному совету;
Вот каталог трех типов данных в системе. Мы можем обращаться с ними как с абстрактными типами данных или «объектами», если вам нравится, и определять структуру с определением типа (скажем, DISC, PROGRAM, TRACK) для представления каждого из них.
disc = a collection of programs stored in a file
+-----------+
|file |
+-----------+
|program |
+-----------+
|program |
+-----------+
|... |
+-----------+
|program |
+-----------+
program = a collection of tracks
+-----------+
|ptr->disc |
+-----------+
|name |
+-----------+
|file offset|
+-----------+
|track |
+-----------+
|track |
+-----------+
|... |
+-----------+
|track |
+-----------+
track = a collection of audio samples
+------------------+
|ptr->program |
+------------------+
|name |
+------------------+
|file offset+length|
+------------------+
|file offset+length|
+------------------+
|... |
+------------------+
|file offset+length|
+------------------+
Я бы предложил, чтобы вы не заставляли пользователей выбирать данные из структур. Вы не можете на самом деле скрыть внутреннюю структуру структур в C (не перепрыгивая через обручи с приведением и т. Д.), Но вы можете предоставить семейство функций, которые позволяют вашим пользователям делать то, что им нужно, без доступа к содержимому абстрактных типов. самих себя. Например, наше семейство функций может выглядеть так:
// DISC functions
DISC *dopen( const char *disc_name );
void dstats( int *ptr_nbr_programs, FILE **ptr_file );
void dclose( DISC *disc );
// PROGRAM functions
PROGRAM *popen_name( DISC *disc, const char *program_name );
PROGRAM *popen_idx ( DISC *disc, int program_idx );
void pstats( int *ptr_nbr_tracks );
void pclose( PROGRAM *program );
// TRACK functions
TRACK *topen_name( PROGRAM *program, const char *track_name );
TRACK *topen_idx ( PROGRAM *program, int track_idx );
int tread( unsigned char *buf, int nbytes_to_read );
void tseek( unsigned long offset );
void tclose( TRACK *track );
Все это должно быть достаточно самоочевидным - оно смоделировано на основе существующей стандартной парадигмы C FILE.
Сначала ваш пользователь получает ptr для DISC с помощью dopen (). Предполагая, что это работает (он вернет NULL, если это не так), он может получить любую глобальную информацию DISC с помощью dstats (). Более того, он может получить ptr для PROGAM в пределах DISC с одной из функций popen ().
С помощью ptr to PROGRAM он может продолжить детализацию и получить один трек с одной из функций семейства topen ().
Очень важным моментом является то, что вы не заставляете своего пользователя самому проходить по аудиофрагментам для получения данных из TRACK. Пользователь предоставляет буфер для чтения сэмплов, и при необходимости вы выполняете итерации по фрагментам для заполнения этого буфера. Функция tseek () обеспечивает ему произвольный доступ.
Я не пытался проработать детали каждого параметра и способы обработки ошибок и т. Д., Я просто представляю концепцию, которую нужно уточнить.
Обратите внимание, что парадигма «сэндвич» используется повсеместно. Вводные «открытые» и завершающие «закрывающие» бутербродные операции для каждого типа.