非常具体的问题,但我们在这里有一些好的阿达人,所以我想听听他们的想法。我正在从用于嵌入式系统的文件中读取数据。我正在使用的数据块总是具有可预测的头格式......但是有一个问题......数据有效载荷长度是在有效载荷发生之前作为格式的一部分给出的。因此,在读取标头中已知位置的某个字节之前,我不知道有效负载大小。这些块一个接一个地出现。
字面上格式为([]用于可读性):
[ 2字节TAG ] [ 1字节有效负载长度(LSB)] [ 1字节有效负载长度(MSB)] [ PAYLOAD
有效负载是人类可读的配置文本。下一个TAG将是前一个有效载荷之后的下两个字节,依此类推,直到我在最后一个有效载荷之后看不到任何匹配的已知TAG。然后我知道我已经完成了。
我正在使用direct_IO流从文件中读取此内容,但我可能会切换到更常规的流并开始执行转换。
我想在一天结束时将所有这些存储在一个简单的记录中 我正在寻找一种技术,我可以读取数据并读取第3个字节,我现在知道有效负载大小,并且可以调整数组或字符串组件的大小,以便在记录已经充当读取的那一刻保持数据正确缓冲。那就是我需要读取TAG和长度数据,所以我想将它们立即存储在记录中。如果可以,我想将有效负载存储在同一记录中。我可以考虑使用访问类型并动态创建有效负载存储,但这意味着我必须在3个字节后停止读取,然后继续执行。此外,这意味着写入将具有相同的问题,因为对象的表示不再与期望的块格式匹配。
我正在考虑尝试使用记录来保存所有这些,并对有效负载大小进行判别,并在该记录上使用表示子句来模仿上述格式。由于判别式是记录和数据块中的第三个字节,我可能能够进行对话并简单地将数据“放置”到对象中......但是当我实例化时,我没有办法调整组件的大小。没有读过TAG和长度的记录。我假设我无法同时读取AND创建对象,因此要创建对象我需要一个长度。虽然我可以继续摆弄文件位置并阅读我需要的内容,然后回到开头,然后创建并使用整个块,我知道必须有一个更好的“Ada”方式。
有没有办法可以使用representation子句将头填充到记录中,当判别符用数据填充时,记录数组或String Payload组件大小会被设置?
此外,这不只是用于阅读,我需要找到一种在配置更改时将这种确切格式输出到文件中的好方法。所以我希望使用表示子句匹配底层格式,这样我就可以将对象“写”到文件中,这将是正确的格式。我希望对读取做同样的事情。
我到目前为止看到的所有Ada读取示例都包含已知长度(或已知最大长度)的记录,其中记录读取静态大小的数据块。
是否有人有一个例子或者能指出我如何使用这种方法来处理这种可变大小的有效载荷?
感谢您提供的任何帮助,
-Josh
答案 0 :(得分:4)
基本上,这样做的方法是进行部分读取,足以获得字节数,然后将其余数据读入区分记录。
像伪Ada中的以下内容:
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字段之后立即找到Payload数据,但您需要与供应商进行验证,或者执行一些测试程序。 (可能有更明确的方式来指定这一点,如果有人提供可行的方法,我愿意更新此答案。)
并且不为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;
(您需要制定退出标准。)
然后将为Payload_Length动态调整Data_Item的大小。但要注意,如果长度是奇数,则可能会出现填充...
答案 1 :(得分:3)
这种情况正是属性'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
)。一个好的优化器可能会解决这个问题,就像在语言中添加左值引用一样(但我不知道是否正在考虑这个)。
答案 2 :(得分:0)
建立在Marc Cs&amp; TED答案,需要考虑几点:
正如您所说,这是一个嵌入式系统,我也会 阅读有关动态内存分配的项目要求, 许多嵌入式系统明确禁止动态内存 分配/释放。 (检查你的ada.streams的实现)
您描述的格式让人联想到 Satellite Payload 格式,我是对的吗?如果是这样,请仔细阅读规范 ' sizes '通常更准确地称为'最大可索引偏移量 从这一点开始,从0 ',又名大小-1。
可能是一种极好的记录,但你可能不得不这么做 使它成为具有长度字段的不可变记录(不是 判别)保证你的程序也不会分配 很多记忆。将其修复为“合理的最大值”。