Ada 编程/属性/'位序
R'Bit_Order 是一个用于指定表示属性,用于指定位编号的记录表示子句(对于记录类型)。 位排序是System.Bit_Order 类型,可以是System.Bit_Order,可以是
假设位序列被解释为整数值。常量System.Default_Bit_Order表示平台的本机位编号。
值得注意的是,位排序只影响记录表示子句的位编号,而不是其(多字节)字段的字节序。[1]
存储单元按其地址排序。让我们看看占用多个字节的整数(假设字节为 8 位)。
- 在大端(BE)机器上,整数的最高有效字节存储在最低地址。
- 在小端(LE)机器上,其最低有效字节存储在最低地址。
因此,为了能够跨字节边界连续计数位,Ada 按照大端的最高有效位(MSB)0 到最低有效位(LSB)的顺序计数字节内的位,而在小端上则反过来。[2]
这就是它的样子(在大端上方便地从左到右写,在小端上从右到左写)
BE Byte 0 1 2 … (counting bytes, i.e. addresses, left to right; higher addresses to the right) LE Byte … 2 1 0 (counting bytes right to left; higher addresses to the left) MSB LSB BE Bit 0 1 2 3 4 5 6 7 (counting bits within a byte left to right) LE Bit 7 6 5 4 3 2 1 0 (counting bits right to left)
我们习惯了从左到右写,但是对于小端,正如你所见,从右到左写地址很方便。因此,在大端上,字节和位的序列按以下方式计数
Byte 00 01 02 Bit 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 … 00 01 02 03 04 05 06 07 08 09 10 11 12 13 … (we can equally begin to count at byte 01)
为了能够从小端连续写入和计数,我们必须像阿拉伯语或希伯来语脚本那样从右到左写(MSB 始终在左侧;这似乎是所有现代脚本中的一个全球性特征)
Byte 02 01 00 Bit … 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 … 13 12 11 10 09 08 07 06 05 04 03 02 01 00
在以下记录表示中(当然,只有指定 X 的两行中的一行可能存在)
for T use record X at 0 range 13 .. 17; -- these two lines… X at 1 range 5 .. 9; -- … are equivalent end record;
组件 X 占用粗体所示的位。字节编号(0 或者 1)称为 Position,边界称为 First_Bit(13 或者 5)和 Last_Bit(17 或者 9);有相应的属性'Position,'First_Bit 和'Last_Bit。(组件 X 的含义在这里无关紧要,只有它的大小相关)。正如你所见,X 是一个跨边界的组件。因此,将 'Bit_Order 属性应用于具有跨边界组件的记录的结果取决于 Ada 的版本。
Ada 95 仅在应用于存储单元内或项目完全填充存储单元序列时,才定义属性在非本机位序上的结果。通过将属性应用于记录,我们强制编译器以指示的方式计数位,独立于机器体系结构。
type
Recis
record
Aat
0range
0 .. 5; Bat
0range
6 .. 7;end
record
;for
Rec'Bit_Orderuse
High_Order_First;
组件 B 占用粗体所示的位
Byte 0 Bit 0 1 2 3 4 5 6 7
此记录的布局在 BE 和 LE 机器上将相同,即表示是与字节序无关的。
我们也可以这样定义此记录,并获得相同的与字节序无关的布局
type
Recis
record
Aat
0range
2 .. 7; Bat
0range
0 .. 1;end
record
;for
Rec'Bit_Orderuse
Low_Order_First;
Byte 0 Bit 7 6 5 4 3 2 1 0
但是,以下记录,其中 B 是跨边界的,仅在 BE 机器上有效,即在本机位序中。
type
Recis
record
Aat
0range
0 .. 5; Bat
0range
6 .. 9;end
record
;for
Rec'Bit_Orderuse
High_Order_First;
Byte 0 1 Bit 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 # 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
在 LE 机器上编译时,编译器会抱怨 B 占用非连续存储并拒绝代码,因为字节序不受属性影响(记住,下一个带有位号 8 到 15 的字节必须放在左侧)
Byte 1 0 Bit 00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07 # 08 09 10 11 12 13 14 15 00 01 02 03 04 05 06 07
作为项目填充完整存储单元范围的记录示例,请参见
type
Recis
record
Aat
0range
0 .. 7; Bat
1range
0 .. 15;end
record
;for
Rec'Bit_Orderuse
High_Order_First;
这在两种体系结构上都有效,导致以下布局
BE 0 1 2 Bit 00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
LE 2 1 0 Bit 08 09 10 11 12 13 14 15 00 01 02 03 04 05 06 07 00 01 02 03 04 05 06 07
其中 A 填充普通字体中的位,B 填充粗体字体中的位。正如你所见,在 LE 上以这种奇怪的方式计数位并不重要,因为组件 B 完全填充了它的两个字节。
对于本机位序,没有变化。记录表示规范从 Ada 83 开始就有效。只有 'Bit_Order 属性在 Ada 95 中引入,并具有上述限制。
为了改善非本机位序的情况,Ada 2005 引入了机器标量的概念。机器标量是一个概念上的基于硬件的无符号整数,在其中计数位并按要求定位组件。
现在我们需要一个更详细的记录示例(相应的属性 'Position、'First_ 和 'Last_Bit(在本机位序中)返回的值作为注释给出;注意,返回的位号始终从组件开始的字节开始计数)
for
Tuse
record
Iat
0range
0 .. 15; -- at 0 range 0 .. 15 Aat
2range
0 .. 12; -- at 2 range 0 .. 12 Bat
2range
13 .. 17; -- at 3 range 5 .. 9 Cat
2range
18 .. 23; -- at 4 range 2 .. 7 Dat
5range
0 .. 7; -- at 5 range 0 .. 7end
record
;
假设 I 是一个 16 位(有符号或无符号)整数,其他组件是某种未指定的类型,具有给定的尺寸要求。整个记录的大小为 48。这就是它在 BE 和 LE 机器上的样子
Byte 0 1 2 3 4 5 BE 012345678901234501234567890123456789012301234567 IIIIIIIIIIIIIIIIAAAAAAAAAAAAABBBBBCCCCCCDDDDDDDD DDDDDDDDCCCCCCBBBBBAAAAAAAAAAAAAIIIIIIIIIIIIIIII LE 765432103210987654321098765432105432109876543210 Byte 5 4 3 2 1 0
在下面,我们将展示为了实现表示的与字节序无关性,我们能走多远。结果将是,当正确使用新的 Ada 2005 功能时,我们只需要在从一种体系结构传输到另一种体系结构后交换字节。
对于以下内容,我们将假设我们是在一个大端机器上,因此我们将相应的属性附加到表示中
for
Tuse
record
Iat
0range
0 .. 15; Aat
2range
0 .. 12; Bat
2range
13 .. 17; Cat
2range
18 .. 23; Dat
5range
0 .. 7;end
record
;for
T'Bit_Orderuse
High_Order_First;
当这在小端机器上编译时,所有具有相同 Position 的组件将被组合在一起并放入匹配的机器标量中。机器标量位于给定的 Position 处,但在内部,位从相反方向计数。
让我们以第一个组件 I 为例。它使用 16 位,因此可以使用 16 位机器标量。它位于字节 0 处,但在内部,编译器将从高位端计数。这就是它的样子(NNBO - 非本机位序)
IIIIIIIIIIIIIIII NNBO 0123456789012345 Byte 5 4 3 2 1 0
现在到下一个 Position 2。相应的组件 A、B、C 总共使用三个字节,因此需要一个 32 位机器标量。它位于字节 2 处,在内部计数将再次从相反方向开始。这就是它的样子
IIIIIIIIIIIIIIII NNBO 012345678901234567890123456789010123456789012345 Byte 5 4 3 2 1 0
位计数从字节 5 的最高有效位开始,一直持续到字节 2 的最低有效位。组件 A、B、C 在此标量内部根据各自的范围进行定位。因此,我们得到了以下布局
AAAAAAAAAAAAABBBBBCCCCCC IIIIIIIIIIIIIIII NNBO 012345678901234567890123456789010123456789012345 Byte 5 4 3 2 1 0
我们立即看到与组件 D 的冲突,它的范围已被 A 占用,编译器当然会报错并拒绝代码。解决方案很简单:只需将 D 的位置从 5 更改为 (在 BE 上) 等效行,如下所示
for
Tuse
record
Iat
0range
0 .. 15; Aat
2range
0 .. 12; Bat
2range
13 .. 17; Cat
2range
18 .. 23; --D at 5 range 0 .. 7; Dat
2range
24 .. 31;end
record
;for
T'Bit_Orderuse
High_Order_First;
并且,鼓声响起,在 LE 上,我们现在有(为了比较,还给出了本机布局)
AAAAAAAAAAAAABBBBBCCCCCCDDDDDDDDIIIIIIIIIIIIIIII NNBO 012345678901234567890123456789010123456789012345 Byte 5 4 3 2 1 0 IIIIIIIIIIIIIIIIAAAAAAAAAAAAABBBBBCCCCCCDDDDDDDD BE 012345678901234501234567890123456789012345678901 Byte 0 1 2 3 4 5
在非本机位顺序中,由相应属性“Position”、“First_”和“Last_Bit”返回的值与记录规范中给出的值完全一致。
作为一项额外服务,GNAT 的编译输出将为您提供在机器标量中以本机位顺序计算的值
range "I" 0 .. 15 "A" 19 .. 31 "B" 14 .. 18 "C" 8 .. 13 "D" 0 .. 7 AAAAAAAAAAAAABBBBBCCCCCCDDDDDDDDIIIIIIIIIIIIIIII NNBO 012345678901234567890123456789010123456789012345 LE 109876543210987654321098765432105432109876543210 Byte 5 4 3 2 1 0
这是我们在当前 Ada 标准下所能达到的极限。
让我们将此记录的值从本机大端机传输到小端机。为了演示目的,跨边界项目的最高位部分显示为大写,最低位部分显示为小写。
IIIIIIIIiiiiiiiiAAAAAAAAaaaaaBBBbbCCCCCCDDDDDDDD BE 012345678901234501234567890123456789012345678901 Byte 0 1 2 3 4 5
字节将按给定顺序传输。由于位顺序属性不会在传输后重新排序字节,因此在目标机器上,我们将以乱序接收数据
DDDDDDDDbbCCCCCCaaaaaBBBAAAAAAAAiiiiiiiiIIIIIIII NNBO 012345678901234567890123456789010123456789012345 Byte 5 4 3 2 1 0
我们所要做的就是交换字节 0↔1、2↔5、3↔4,以达到预期的结果
AAAAAAAAaaaaaBBBbbCCCCCCDDDDDDDDIIIIIIIIiiiiiiii NNBO 012345678901234567890123456789010123456789012345 Byte 5 4 3 2 1 0
以下两组表示子句在任何机器/编译器(Ada 2005 及更高版本)中指定了相同的寄存器布局
type
Device_Registeris
record
Ready : Status_Flag; Error : Error_Flag; Data : Unsigned_16;end
record
;for
Device_Registeruse
record
Readyat
0range
0 .. 0; Errorat
0range
1 .. 1; -- Reserved bits Dataat
0range
16 .. 31;end
record
;for
Device_Register'Sizeuse
32;for
Device_Register'Bit_Orderuse
System.Low_Order_First;pragma
Atomic (Device_Register);
如果位顺序修改,则必须反转记录表示子句中所有元素的位编号
type
Device_Registeris
record
Ready : Status_Flag; Error : Error_Flag; Data : Unsigned_16;end
record
;for
Device_Registeruse
record
Readyat
0range
31 .. 31; -- Bit numbering has changed Errorat
0range
30 .. 30; -- Bit numbering has changed -- Reserved bits Dataat
0range
0 .. 15; -- Bit numbering has changed (but byte order is not affected)end
record
;for
Device_Register'Sizeuse
32;for
Device_Register'Bit_Orderuse
System.High_Order_First; -- Reverse bit orderpragma
Atomic (Device_Register);
两者可以在同一台机器上互换使用。但请注意,在两台具有不同字节序的机器中数据字段将具有本机字节序,而与表示子句中指定的位顺序无关。
'Bit_Order 属性并非旨在将数据在大小端机器之间转换(它影响位编号,而不是字节顺序)。当指定非本机位顺序时,编译器不会生成代码来重新排序多字节字段。[3][4][5]
- ↑ 请注意,当 ARM 在 'Bit_Order 属性的定义中谈论“大端”和“小端”时,它实际上指的是位字节序,而不是字节字节序。(如今,术语字节序通常保留用于讨论字节顺序,尽管它也可以用于位编号。)因此,在这种情况下,当 ARM 说“小端”时,它指的是“LSB 0”,而当它说“大端”时,它与“MSB 0”相同:
«High_Order_First(在口语中称为“大端”)表示存储元素的第一个位(位 0)是最重要的位(将表示组件的位序列解释为无符号整数)。Low_Order_First(在口语中称为“小端”)表示相反:第一个位是最不重要的位。» [LRM, §13.5.3(2)]
- ↑ ISO/IEC 8652:1987. "13.4 Record Representation Clauses". Ada 83 Reference Manual.
The range defines the bit positions of the storage place, relative to the storage unit. The first storage unit of a record is numbered zero. The first bit of a storage unit is numbered zero. The ordering of bits in a storage unit is machine_dependent and may extend to adjacent storage units.
{{cite book}}
: Unknown parameter|chapterurl=
ignored (|chapter-url=
suggested) (help) - ↑ AI95-00133-01 (1996-05-07). "Controlling bit ordering". Class: binding interpretation. Ada Rapporteur Group.
Bit_Order clauses are concerned with the numbering of bits and not concerned with data flipping interoperability.
- ↑ ISO/IEC 8652:2007. "13.5.3 Bit Ordering (9/2)". Ada 2005 Reference Manual. Retrieved 2008-06-02.
Bit_Order clauses make it possible to write record_representation_clauses that can be ported between machines having different bit ordering. They do not guarantee transparent exchange of data between such machines.
{{cite book}}
: Unknown parameter|chapterurl=
ignored (|chapter-url=
suggested) (help) - ↑ Thomas Quinot (2013). "Gem #140: Bridging the Endianness Gap". AdaCore. Retrieved 2013-01-31.
the order in which the bytes that constitute machine scalars are written to memory is not changed by the Bit_Order attribute -- only the indices of bits within machine scalars are changed.
{{cite web}}
: Unknown parameter|month=
ignored (help)
据作者所知,记录表示子句是 Ada 语言的独特功能,因此不需要在其他编程语言中使用类似于属性 'Bit_Order 的功能。在其他编程语言中,通常的做法是显式使用掩码和位运算,因此必须始终使用本机位编号。
- 13.5.3 位排序 (带注释)
- 附录 K 语言定义属性 (带注释)
- Ada 95: 13.1 数据表示
- Ada 2005: 9.2.1 与原始 Ada 95 的不兼容性
- Norman H. Cohen (1994). "与字节序无关的记录表示子句" (PDF). ACM SIGAda Ada Letters. New York, NY, USA: Association for Computing Machinery. XIV (1): 27–29. ISSN 1094-3641. 检索于 2008-12-20.
- Randal P. Andress (2005). "对最外层 Ada 记录对象进行整体字节反转以实现通信数据类型的字节序独立性" (PDF). ACM SIGAda Ada Letters. New York, NY, USA: Association for Computing Machinery. XXV (3): 19–27. ISSN 1094-3641. 检索于 2008-12-20.