Как я могу определить размер / сопоставить компонент записи при чтении его данных из потока в Ada? - PullRequest
3 голосов
/ 23 сентября 2011

Очень конкретный вопрос, но у нас здесь есть несколько хороших ада, поэтому я хотел бы услышать мысли Я читаю данные из файла, используемого для встроенных систем. У блоков данных, с которыми я работаю, всегда есть предсказуемый формат заголовка ... но есть одна проблема ... длина полезной нагрузки данных указывается как часть формата непосредственно перед тем, как полезная нагрузка возникает. Поэтому я не знаю размер полезной нагрузки, пока не прочитаю определенный байт в известной позиции в заголовке. Куски происходят один за другим.

Буквально формат ([] используется для удобства чтения) :

[ 2-байтовая TAG ] [ 1-байтовая длина полезной нагрузки (LSB) ] [ 1-байтовая длина полезной нагрузки (MSB) ] [ PAYLOAD ]

Полезная нагрузка - это читаемый человеком текст конфигурации. Следующим тегом будут следующие два байта после предыдущей полезной нагрузки и так далее, пока я не увижу совпадения с известным тегом после последней полезной нагрузки. Тогда я знаю, что я закончил.

Я читаю это из файла, используя поток direct_IO, но я мог бы переключиться на более общий поток и просто начать выполнять преобразования.

Я бы хотел сохранить все это в простой записи в конце дня Я ищу метод, в котором я могу читать данные и читать 3-й байт. Теперь я знаю размер полезной нагрузки и могу измерить размер массива или компонента String для правильного хранения данных в тот момент, когда запись уже действует как чтение. буфер. То есть мне нужно прочитать данные TAG и длины, поэтому я хотел бы сразу сохранить их в записи. Я хочу сохранить полезную нагрузку в той же записи, если смогу. Я мог бы рассмотреть возможность использования типа доступа и динамического создания хранилища полезных данных, но это означает, что я должен остановить чтение после 3 байтов, выполнить работу и затем продолжить. Кроме того, это означает, что запись будет иметь ту же проблему, поскольку представление объекта больше не соответствует ожидаемому формату фрагмента.

Я думал о попытке использовать запись для хранения всего этого с дискриминантом для размера полезной нагрузки и использовать условие представления для этой записи, чтобы имитировать точный вышеописанный формат. Поскольку дискриминант является третьим байтом как в записи, так и во фрагменте данных, я мог бы вести диалог и просто «укладывать» данные в объект… но у меня нет способа измерить компонент, когда я создаю экземпляр запись без уже прочитанного тега и длины. Я предполагаю, что не могу прочитать И создать объект одновременно, поэтому для создания объекта мне нужна длина. Хотя я мог бы продолжать играть с позицией файла и читать то, что мне нужно, затем вернуться к началу, а затем создать и использовать весь кусок, который, как я знаю, должен быть лучше «Ады».

Есть ли способ, с помощью которого я могу использовать условие представления, чтобы заполнить заголовок в записи, а когда дискриминант заполнен значением из данных, будет установлен размер массива записей или компонента String Payload?

Кроме того, это не только для чтения, мне нужно найти хороший способ вывода этого точного формата в файл при изменении конфигурации. Поэтому я надеялся использовать предложение представления, чтобы соответствовать базовому формату, чтобы я мог буквально «записать» объект в файл, и это был бы правильный формат. Я надеялся сделать то же самое для чтения.

Все примеры чтения Ada, которые я до сих пор видел, с записями известной длины (или известной максимальной длины), где запись читается в блоке данных статического размера.

Есть ли у кого-нибудь пример или может указать мне правильное направление, как я мог бы использовать этот подход при работе с этой полезной нагрузкой переменного размера?

Спасибо за любую помощь, вы можете предоставить,

-Josh

Ответы [ 3 ]

4 голосов
/ 23 сентября 2011

По сути, способ сделать это - выполнить частичное чтение, достаточное для получения количества байтов, а затем прочитать остальные данные в различаемую запись.

Что-то вроде следующего в псевдо-Аде:

type Payloads is array (Payload_Sizes range <>) of Your_One_Byte_Payload_Type;

type Data (Payload_Length : Payload_Sizes) is
   record
      Tag : Tag_Type;
      Payload : Payloads(1 .. Payload_Length);
   end record;

for Data use record
   Tag            at 0 range 0 .. 15;
   Payload_Length at 2 range 0 .. 15;
   -- Omit a rep spec for Payload
end record;

Обычно компилятор находит данные о полезной нагрузке сразу после последнего поля rep-spec'ed, но вам необходимо проверить это у поставщика или выполнить несколько тестовых программ.(Может быть более явный способ указать это, и я готов обновить этот ответ, если кто-то предоставит работоспособный подход.)

И не предоставляют значение по умолчанию дляДискриминант Payload_Length, который заставляет экземпляры записи всегда резервировать максимальный объем памяти, необходимый для наибольшего значения.

Затем в коде чтения данных что-то вроде:

loop
   Get(Data_File, Tag);
   Get(Data_File, Payload_Length)

   declare
      Data_Item : Data(Payload_Length);
   begin
      Data_Item.Tag := Tag;
      Get(Data_File, Data_Item.Payload);
      Process_Data(Data_Item);
   end;
   ...
   exit when Whatever;
end loop;

(Вам нужно будет выработать критерии выхода.)

Затем Data_Item будет динамически изменяться для Payload_Length.Однако будьте осторожны, если эта длина нечетная, так как может произойти заполнение ...

3 голосов
/ 23 сентября 2011

Эта ситуация как раз то, для чего используется атрибут 'input в языке.

Если у вас также есть код, который в первую очередь записывает эти данные в поток, то это легко.Просто используйте

Myobject : Discriminated_Record := Discriminated_Record'input (Some_Stream'access);

(и, конечно, используйте 'output при записи).

Если вместо этого вам нужно прочитать данные, отформатированные кем-то другим, это будет немного сложнее.Вам нужно будет реализовать собственную подпрограмму 'input.

function Discriminated_Record_Input 
    (Stream : access Ada.Streams.Root_Stream_Type'class) 
return Discriminated_Record;
for Discriminated_Record'input use Discriminated_Record_Input;

В реализации Discriminated_Record_Input вы можете обойти проблему дискриминанта, выполнив все действия в разделе объявлений или используя локальный блок объявления,(предупреждение: не скомпилированный код)

function Discriminated_Record_Input
    (Stream : access Ada.Streams.Root_Stream_Type'class) 
return Discriminated_Record is

    Size : constant Natural := Natural'input(Stream);
    Data : constant Discriminated_Record_Input 
        := (Size, (others => Byte_Type'input(Stream));
begin
    return Data;
end Discriminated_Record_Input;

Главный недостаток этого заключается в том, что ваши данные могут быть скопированы дважды (один раз в локальную константу, затем еще раз оттуда в MyObject).Хороший оптимизатор может исправить это, как и добавление lvalue ссылок на язык (но я не знаю, рассматривается ли это).

0 голосов
/ 30 сентября 2011

Опираясь на ответы Marc Cs & TEDs, необходимо учесть несколько моментов:

  1. Поскольку вы упомянули, что это встроенная система, я бы также прочитал требования проекта, касающиеся динамического взаимодействия.выделение памяти, так как многие встроенные системы явно запрещают динамическое выделение / освобождение памяти.(проверьте вашу реализацию ada.streams)

  2. Описанный вами формат напоминает Спутниковый Форматы полезной нагрузки, я прав?Если это так, внимательно прочитайте спецификацию, поскольку эти « размеры » часто правильнее называть « максимальное индексируемое смещение от этой точки, начиная с 0 », то есть с размером -1.

  3. Дискриминирующая запись, вероятно, является подходящим вариантом, но вам, возможно, придется сделать ее неизменной записью с полем длины (не дискриминантом), чтобы гарантировать, что ваша программа не выделит слишком много памяти.Исправьте это к своему «разумному максимуму».

...