更改数组类型:8bit 类型为 6bit 类型

Change array type: 8bit type to 6bit type

我在 file.ads

中有两个类型和两个数组
 type Ebit is mod 2**8;
 type Sbit is mod 2**6;
 type Data_Type is array (Positive range <>) of Ebit;
 type Changed_Data_Type is array (Positive range <>) of Sbit;

和功能:

function ChangeDataType (D : in Data_Type) return Changed_Data_Type
 with
   Pre => D'Length rem 3 = 0 and D'Last < Positive'Last / 4,
 Post => ChangeDataType'Result'Length = 4 * (D'Length / 3)

好的,我能理解这一切。 例如我们有数组:

65, 66, 65, 65, 66, 65 in 8bit values 函数应该给我们 16, 20, 9, 1, 16, 20, 9, 1 in 6bit values.

我不知道如何从 8 位 table 构建 6 位 table。

我的解决方案的想法是例如从类型中一点一点地获取:

fill all bites in 6bit type to 0 (propably default)
if first bit (2**1) is 1 set bit (2**1) in 6bit type to 1;
and do some iterations

但我不知道该怎么做,类型总是有问题。这是个好主意还是我可以用更简单的方法做到这一点?我花了最后一晚尝试写这个但没有成功。

编辑: 我写了一些代码,它的工作,但我有数组初始化的问题。

function ChangeDataType (D: in Data_Type) return Changed_Data_Type
 is
  length: Natural := (4*(D'Length / 3));
  ER: Changed_Data_type(length);
  Temp: Ebit;     
  Temp1: Ebit;
  Temp2: Ebit;
  Actual: Ebit;
  n: Natural;
  k: Natural;
begin
  n := 0;
  k := 0;
  Temp := 2#00000000#;
  Temp1 := 2#00000000#;
  Temp2 := 2#00000000#;

  Array_loop:
  for k in D'Range loop
     case n is
        when 0 =>
           Actual := D(k);
           Temp1 := Actual / 2**2;
           ER(k) := Sbit(Temp1);
           Temp := Actual * ( 2**4);
           n := 2; 
        when 2 =>
           Actual := D(k);
           Temp1 := Actual / 2**4;
           Temp2 := Temp1 or Temp;
           ER(k) := Sbit(Temp2);
           Temp := Actual * ( 2**2); 
           n := 4;
        when 4 =>
           Actual := D(k);
           Temp1 := Actual / 2**6;
           Temp2 := Temp1 or Temp;
           ER(k) := Sbit(Temp2);
           n := 6;
        when 6 =>
           Temp1 := Actual * ( 2**2);
           Temp2 := Actual / 2**2;
           ER(k) := Sbit(Temp2);
           n := 0;
        when others =>
           n := 0;
     end case;
  end loop Array_Loop;


  return ER;
 end;

如果我明白你在问什么......你想将相同的 8 位数据重新打包成 6 位值,以便第一个 [=13] 的 "leftover" 位=] 成为第二个 Sbit.

的第一位(最高位还是最低位?)

您可以这样做的一种方法 - 至少对于固定大小的数组,例如您的 6 字 * 8 位、8 字 * 6 位示例是通过使用打包和 representation aspects (or pragmas, before Ada-2012) which are nicely described here.

为每种数组类型指定内存中的确切布局

我还没有测试以下内容,但它可以作为一个起点。

 type Ebit is mod 2**8;
 type Sbit is mod 2**6;
 for Ebit'Size use 8;
 for Sbit'Size use 6;
 type Data_Type is array (1 .. 6) of Ebit
    with Alignment => 0;  -- this should pack tightly
 type Changed_Data_Type is array (1 .. 8) of Sbit
    with Alignment => 0;

然后您可以使用两种数组类型实例化通用 Unchecked_Conversion 函数,并使用该函数将一个数组转换为另一个数组。

 with Ada.Unchecked_Conversion;
 function Change_Type is new Ada.Unchecked_Conversion(Data_Type, Changed_Data_Type);

 declare
   Packed_Bytes : Changed_Data_Type := Change_Type(Original_Bytes);
 begin ...

就生成的代码而言,它并不慢,因为 Unchecked_Conversion 什么都不做,只是告诉编译时类型检查从另一个角度看。

我认为 Unchecked_Conversion 就像我的猫从窗台上掉下来后给我的 "I meant to do that" 样子。再次...

或者,如果您希望避免复制,您可以将 Original_Bytes 声明为别名,并使用访问类型和 Unchecked_Access 的类似技巧将两个数组覆盖在同一内存上(如C) 中的联合。我认为这就是 DarkestKhan 在下面的评论中所说的 "array overlays"。另请参阅 this rather dated page which describes the technique further. It notes the overlaid variable must not only be declared aliased but also volatile so that accesses to one view aren't optimised into registers, but reflect any changes made via the other view. Another approach to overlays is in the Ada Wikibook here.

的第 3 节

现在这可能容易受到字节顺序考虑的影响,即它可能适用于某些平台但不适用于其他平台。上面的第二个参考给出了一个记录的例子,它的成员是精确的位对齐的:我们至少可以采用 Bit_Order 方面,如上面数组的 with Alignment => 0, Bit_Order => Low_Order_First;...

-- code stolen from "Rationale" ... see link above p.11
type RR is record
      Code: Opcode;
      R1: Register;
      R2: Register;
end record
with Alignment => 2, Bit_Order => High_Order_First;

for RR use record
      Code at 0 range 0 .. 7;
      R1 at 1 range 0 .. 3;
      R2 at 1 range 4 .. 7;
end record;

我不清楚的一件事是,是否有一种公式化的方法来指定数组中每个元素的确切布局,就像在此处的记录中所做的那样——或者即使有潜在的需要。如有必要,一种解决方法是用记录替换上面的数组。但如果有更好的答案,我很乐意看到。