------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUN-TIME COMPONENTS                         --
--                                                                          --
--                 S Y S T E M . B I T F I E L D _ U T I L S                --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--               Copyright (C) 2019, Free Software Foundation, Inc.         --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

package System.Bitfield_Utils is

   --  This package provides a procedure for copying arbitrarily large and
   --  arbitrarily bit-aligned bit fields.

   --  Type Val is used to represent small bit fields. Val_2 represents a
   --  contiguous pair of Vals. Val_2'Alignment is half of its size in bytes,
   --  which is likely not the natural alignment. This is done to ensure that
   --  any bit field that fits in a Val can fit in an aligned Val_2, starting
   --  somewhere in the first half, and possibly crossing over into the second
   --  half. This allows us to isolate a Val value by shifting and masking the
   --  Val_2.
   --
   --  Val can be 8, 16, or 32 bits; larger values are more efficient. It can't
   --  be 64 bits, because we need Val_2 to be a double-wide shiftable type,
   --  and 128 bits is not supported. Instantiating with an 8-bit Val is useful
   --  for testing and debugging; 32 bits should be used for production.
   --
   --  We use modular types here, not because we want modular arithmetic, but
   --  so we can do shifting and masking. The actual for Val_2 should have
   --  pragma Provide_Shift_Operators, so that the Shift_Left and Shift_Right
   --  intrinsics can be passed in. It is impossible to put that pragma on a
   --  generic formal, or on a type derived from a generic formal, so they have
   --  to be passed in.
   --
   --  Endian indicates whether we're on little-endian or big-endian machine.

   pragma Elaborate_Body;

   Little : constant Bit_Order := Low_Order_First;
   Big : constant Bit_Order := High_Order_First;

   generic
      type Val is mod <>;
      type Val_2 is mod <>;

     with function Shift_Left
       (Value  : Val_2;
        Amount : Natural) return Val_2 is <>;

     with function Shift_Right
       (Value  : Val_2;
        Amount : Natural) return Val_2 is <>;

      Endian : Bit_Order := Default_Bit_Order;

   package G is
      --  Assert that Val has one of the allowed sizes, and that Val_2 is twice
      --  that.

      pragma Assert (Val'Size in 8 | 16 | 32);
      pragma Assert (Val_2'Size = Val'Size * 2);

      --  Assert that both are aligned the same, to the size in bytes of Val
      --  (not Val_2).

      pragma Assert (Val'Alignment = Val'Size / Storage_Unit);
      pragma Assert (Val_2'Alignment = Val'Alignment);

      type Val_Array is array (Positive range <>) of Val;

      --  It might make more sense to have:
      --    subtype Val is Val_2 range 0 .. 2**Val'Size - 1;
      --  But then GNAT gets the component size of Val_Array wrong.

      pragma Assert (Val_Array'Alignment = Val'Alignment);
      pragma Assert (Val_Array'Component_Size = Val'Size);

      subtype Bit_Size is Natural; -- Size in bits of a bit field
      subtype Small_Size is Bit_Size range 0 .. Val'Size;
      --  Size of a small one
      subtype Bit_Offset is Small_Size range 0 .. Val'Size - 1;
      --  Starting offset
      subtype Bit_Offset_In_Byte is Bit_Offset range 0 .. Storage_Unit - 1;

      procedure Copy_Bitfield
        (Src_Address  : Address;
         Src_Offset   : Bit_Offset_In_Byte;
         Dest_Address : Address;
         Dest_Offset  : Bit_Offset_In_Byte;
         Size         : Bit_Size);
      --  An Address and a Bit_Offset together form a "bit address". This
      --  copies the source bit field to the destination. Size is the size in
      --  bits of the bit field. The bit fields can be arbitrarily large, but
      --  the starting offsets must be within the first byte that the Addresses
      --  point to. The Address values need not be aligned.
      --
      --  For example, a slice assignment of a packed bit field:
      --
      --     D (D_First .. D_Last) := S (S_First .. S_Last);
      --
      --  can be implemented using:
      --
      --     Copy_Bitfield
      --       (S (S_First)'Address, S (S_First)'Bit,
      --        D (D_First)'Address, D (D_First)'Bit,
      --        Size);

   end G;

end System.Bitfield_Utils;
