Ada Endianness Float / Integer - PullRequest
       15

Ada Endianness Float / Integer

0 голосов
/ 09 октября 2018

Я новый разработчик в Аде, так что прости меня, если я не достаточно ясно.Я столкнулся с проблемой, и я не знаю, откуда может прийти ошибка.Я объясняю прежде всего контекст: у меня есть набор тестов, которые работают на QEMU (BE).Я хотел выполнить их на ПК нативный (x86) с прагмой Default_Scalar_Storage_Order (High_Order_First).Я заметил, что некоторые из моих тестов работали отлично, но это не относится к тестам, включая float.Для простоты я написал тест, включающий FLOAT и INT.

with AUNIT.ASSERTIONS; use AUNIT.ASSERTIONS;
with BASIC_TYPES;
with BASIC_TYPES.STREAM;
with INTERFACES;
with ADA.INTEGER_TEXT_IO;
with ADA.FLOAT_TEXT_IO;
with ADA.TEXT_IO;
with STREAMS;
with SYSTEM;

package body TEST.TEST is

   function Integer2Hexa(Hex_Int : Integer; Bits_Nbr : Integer) return String is
   Hexa : String(1..Bits_Nbr);
   begin
      Ada.Integer_Text_IO.Put(Hexa,Hex_Int,16);
      return Hexa;
   end Integer2Hexa;

   function NAME (T : TEST) return AUNIT.MESSAGE_STRING is
      pragma UNREFERENCED (T);
   begin
      return AUNIT.FORMAT ("Test package");
   end NAME;

   IntegerNbr : BASIC_TYPES.INT32_T;
   FloatNbr : INTERFACES.IEEE_Float_32;

   procedure RUN_TEST (T : in out TEST) is

      PACKED_ARRAY : BASIC_TYPES.UINT8_ARRAY_NC_T (1 .. 8) := (others => 0);
      MY_STREAM    : STREAMS.STREAM_T;

      use type BASIC_TYPES.UINT8_ARRAY_NC_T;

   begin

      IntegerNbr := 479037433;
      FloatNbr := 2.0012151e+09;

      ADA.TEXT_IO.PUT_LINE ("Default bit order: " & SYSTEM.Default_Bit_Order'IMG);

      ADA.TEXT_IO.PUT_LINE ("Integer size : " & INTEGER'IMAGE (INTEGER'SIZE));

      ADA.TEXT_IO.PUT ("16#4EEE903D#"); -- 2.0012151e+09 in FLOAT BIG ENDIAN
      ADA.TEXT_IO.PUT (Integer2Hexa(Integer(IntegerNbr),32)); -- 16#1C8D87F9# in INT BIG ENDIAN
      ADA.TEXT_IO.NEW_LINE;

      -- Init the stream
      STREAMS.INIT (MY_STREAM      => MY_STREAM,
                    STREAM_ADDRESS => PACKED_ARRAY (PACKED_ARRAY'FIRST)'ADDRESS,
                    STREAM_SIZE    => PACKED_ARRAY'LENGTH);

      BASIC_TYPES.STREAM.WRITE_FLOAT_T (MY_STREAM  => MY_STREAM,
                                      ITEM       => FloatNbr,
                                      ALIGN_MODE => STREAMS.PACK);

      BASIC_TYPES.STREAM.WRITE_INT32_T (MY_STREAM  => MY_STREAM,
                                      ITEM       => IntegerNbr,
                                      ALIGN_MODE => STREAMS.PACK);


      if (not ASSERT(PACKED_ARRAY = (16#4e#,  16#ee#,  16#90#,  16#3d#,  16#1c#,  16#8d#,  16#87#,  16#f9#), "PACKED_ARRAY incorrect")) then
         for I in PACKED_ARRAY'RANGE loop
            ADA.TEXT_IO.PUT (Integer2Hexa(Integer(PACKED_ARRAY (I)),8));
         end loop;
         ADA.TEXT_IO.NEW_LINE;

      end if;

   end RUN_TEST;

end TEST.TEST;

Я заметил, что запись INT выполнена правильно, но это не относится к FLOAT (он написан на LittleEndian).Действительно, на выходе у меня должно быть

16#4e#, 16#ee#, 16#90#, 16#3d#, 16#1c#, 16#8d#, 16#87#, 16#f9#

, но я получаю

16#3d#, 16#90#, 16#ee#, 16#4e#, 16#1c#, 16#8d#, 16#87#, 16#f9#

Я использовал этот сайт, чтобы подтвердить свои результаты: https://www.scadacore.com/tools/programming-calculators/online-hex-converter/

Я не знаю, правильно ли используется преобразование благодаря прагме для FLOAT.Я называю это в моем файле gpr в пакете Compiler с этим текстом в PRAGMA.txt: pragma Default_Scalar_Storage_Order (High_Order_First);

   package Compiler is
      for Local_Configuration_Pragmas use "PRAGMAS.txt";
      for Switches ("ada") use ("-g");
   end Compiler;

Приходит ли проблема к моему использованию прагмы?

Вот так называемые процедуры:

       procedure WRITE_FLOAT_T
         (MY_STREAM  : in out STREAMS.STREAM_T;
          ITEM       : in BASIC_TYPES.FLOAT_T;
          ALIGN_MODE : in STREAMS.ALIGN_MODE_T)
       is

          pragma UNREFERENCED (ALIGN_MODE);

          -- Temporary types for non pack case
          type TMP_TYPE_T is new STANDARD.FLOAT;
          for TMP_TYPE_T'VALUE_SIZE use FLOAT_T_SIZE_C;
          TMP_TYPE : TMP_TYPE_T;
          subtype BITS_FIELD_T is STREAMS.BIT_FIELD_ARR_NC_T (1 .. STREAMS.SIZE_T (FLOAT_T_SIZE_C));

          function TO_BITS_ARRAY is new UNCHECKED_CONVERSION (TMP_TYPE_T,
                                                              BITS_FIELD_T);


       begin

          -- Convert item to a temporary type
          TMP_TYPE := TMP_TYPE_T(ITEM);
          STREAMS.WRITE (MY_STREAM  => MY_STREAM,
                         DATA       => TO_BITS_ARRAY(TMP_TYPE));

       end WRITE_FLOAT_T;

   procedure WRITE (MY_STREAM : in out STREAM_T;
                    DATA      : in BIT_FIELD_ARR_NC_T) is

   begin

      if (MY_STREAM.ERROR_CODE = NO_ERROR)
        and then (MY_STREAM.WRITE_OFFSET + DATA'LENGTH - 1 <= MY_STREAM.STREAM_SIZE * 8) then

         if (MY_STREAM.WRITE_OFFSET mod 8 = 1) and then (DATA'LENGTH mod 8 = 0) then

            -- Byte mode
            WRITE_BYTES(MY_STREAM => MY_STREAM,
                        DATA      => DATA);

         else

            -- Bit mode
            WRITE_BITS(MY_STREAM => MY_STREAM,
                       DATA      => DATA);

         end if;

      elsif (MY_STREAM.ERROR_CODE = NO_ERROR) then

         -- Update ERROR_CODE on first error
         MY_STREAM.ERROR_CODE := END_ERROR;

      end if;

   end WRITE;

   procedure WRITE_BYTES (MY_STREAM : in out STREAM_T;
                          DATA      : in BIT_FIELD_ARR_NC_T) is

      BYTE_FIELD_ARR : BYTE_FIELD_ARR_NC_T (1 .. MY_STREAM.STREAM_SIZE);
      for BYTE_FIELD_ARR'ADDRESS use MY_STREAM.STREAM_ADDRESS;

      TMP_BYTE_FIELD_ARR : BYTE_FIELD_ARR_NC_T (1 .. DATA'LENGTH / 8);
      for TMP_BYTE_FIELD_ARR'ADDRESS use DATA'ADDRESS;

   begin

      -- Write byte field
      BYTE_FIELD_ARR ((MY_STREAM.WRITE_OFFSET + 7) / 8 .. (MY_STREAM.WRITE_OFFSET + 7) / 8 + (DATA'LENGTH / 8) - 1) := TMP_BYTE_FIELD_ARR;
      MY_STREAM.WRITE_OFFSET := MY_STREAM.WRITE_OFFSET + DATA'LENGTH;

   end WRITE_BYTES;

Заранее спасибо!

Q.Dherb

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

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

Проблема с вашей процедурой WRITE_FLOAT_T заключается в том, что ITEM является простым плавающим числом, поэтому Scalar_Storage_Order не имеет никакого эффекта.

Я использовал Scalar_Storage_Order, чтобы объявить запись, которую я хотел отправить,

type SNTP_Packet is record
   -- contents
end record
  with
    Bit_Order => System.High_Order_First,
    Scalar_Storage_Order => System.High_Order_First,
    Size => 48 * 8;
for SNTP_Packet use record
   -- placement of content
end record;

subtype Net_Packet is Ada.Streams.Stream_Element_Array (1 .. 48);
--  This is what actually gets streamed

function To_Net_Packet
is new Ada.Unchecked_Conversion (SNTP_Packet, Net_Packet);
function To_SNTP_Packet
is new Ada.Unchecked_Conversion (Net_Packet, SNTP_Packet);

Вы можете использовать pragma Default_Scalar_Storage_Order, но тогда я не уверен, что произойдет с необходимостьючтобы сделать Bit_Order совпадение.

В качестве альтернативы, если вы хотите иметь возможность использовать, например, Float'Write, вы можете изменить способ, которым потоки GNAT фундаментальных типов.

Среда выполнения Ada обрабатывает потоковую передачудля фундаментальных типов используется пакет System.Stream_Attributes, в файлах s-stratt.ads, s-stratt.adb и альтернативная реализация в s-stratt__xdr.adb (в последних компиляторах; старые компиляторы могут использовать другое имя файла, но будетxdr там).

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

  1. скопировать s-stratt__xdr.adb в s-stratt.adb в вашем рабочем каталоге

  2. используйте gnatmake -a для локальной компиляции необходимых частей среды выполнения (-gnatpg говорит "скомпилировать для среды выполнения"):

    gnatmake -a -f s-stratt.adb -gnatpg

  3. сборка вашей программы:

    gprbuild main.adb

Примечание. gprbuild не поддерживает -a.Может быть возможно использовать файл проекта, чтобы позволить вам создать библиотеку, содержащую измененные компоненты времени выполнения.

0 голосов
/ 10 октября 2018

Согласно документации Scalar_Storage_Order :

Этот атрибут, определенный реализацией, применяется только к массиву и записи.Это означает, что это не влияет на структуру памяти скалярного типа, такого как Float или Integer.Каким бы ни было значение атрибута Default_Scalar_Storage_Order, на машине с прямым порядком байтов целое число 16 # 12345678 # будет представлено как 12 34 56 78, а на машине с низким порядком байтов оно будет представлено как 78 56 34 12.

Forмассив определяет порядок хранения_элемента (обычно это байты) каждого скалярного компонента.В вашем случае, все ваши компоненты массива имеют размер, который ниже или равен элементу хранения, что означает, что предложение Scalar_Storage_Order не имеет никакого эффекта.

Вот пример, который показывает эффект этого предложения для массива:

with Ada.Text_IO;
with System;
with Interfaces;
with Ada.Streams;
with Ada.Integer_Text_IO;

procedure Scalar_Storage_Element_Exemple is

   type T_U16_Arr_Le is array (Positive range <>) of Interfaces.Unsigned_16
     with Component_Size => 16, Scalar_Storage_Order => System.Low_Order_First;
   type T_U16_Arr_Be is array (Positive range <>) of Interfaces.Unsigned_16
     with Component_Size => 16, Scalar_Storage_Order => System.High_Order_First;

   type T_U8_Arr_Le is array (Positive range <>) of Interfaces.Unsigned_8
     with Component_Size => 8, Scalar_Storage_Order => System.Low_Order_First;
   type T_U8_Arr_Be is array (Positive range <>) of Interfaces.Unsigned_8
     with Component_Size => 8, Scalar_Storage_Order => System.High_Order_First;

   Arr_16_LE : T_U16_Arr_Le (1 .. 2) := (16#1234#, 16#5678#);
   Arr_16_BE : T_U16_Arr_Be (1 .. 2) := (16#1234#, 16#5678#);

   Arr_8_LE : T_U8_Arr_Le (1 .. 4) := (16#12#, 16#34#, 16#56#, 16#78#);
   Arr_8_BE : T_U8_Arr_Be (1 .. 4) := (16#12#, 16#34#, 16#56#, 16#78#);

   Sea_16_LE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_16_LE'Address;
   Sea_16_BE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_16_BE'Address;

   Sea_8_LE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_8_LE'Address;
   Sea_8_BE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_8_BE'Address;

   function byte2Hexa(byte : Integer) return String is
      Hexa : String(1..8);
   begin
      Ada.Integer_Text_IO.Put(Hexa,byte,16);
      return Hexa;
   end byte2Hexa;

begin

   for byte of Sea_16_LE loop
      Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
   end loop;
   -- display 16#34#  16#12#  16#78#  16#56#
   -- each item of the array is in LE

   Ada.Text_IO.New_Line;

   for byte of Sea_16_BE loop
      Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
   end loop;
   -- 16#12#  16#34#  16#56#  16#78#
   -- each item of the array is in BE

   Ada.Text_IO.New_Line;

   for byte of Sea_8_LE loop
      Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
   end loop;
   -- 16#12#  16#34#  16#56#  16#78#
   -- no effect as size of component is inferior or equal to storage_element size

   Ada.Text_IO.New_Line;

   for byte of Sea_8_BE loop
      Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
   end loop;
   -- 16#12#  16#34#  16#56#  16#78#
   -- no effect as size of component is inferior or equal to storage_element size

end Scalar_Storage_Element_Exemple;

Ваша сериализация с плавающей точкой работает на вашем QEMU, потому что вы уже находитесь на BE.Поэтому Scalar_Storage_Order является только подтверждающим и не имеет никакого эффекта.

Он не работает на x86, потому что исходным порядком байтов является LE, и, как объяснялось ранее, предложение BE Scalar_Storage_Order не влияет на задействованные типы.Таким образом, конечный результат - LE float.

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

...