Unchecked_Conversion, Bit Manipulation & Custom Streams

Reinterpret bits with Ada.Unchecked_Conversion (IEEE float bit patterns), manipulate bits with Interfaces modular types (Shift_Left, Shift_Right, Rotate_Left, and/or/xor), implement your own stream type derived from Root_Stream_Type, and redefine stream attributes ('Write, 'Read) to control serialization formats.

Low-level code sometimes needs to look at raw bits: a float's IEEE encoding, a checksum, a packet header. Ada's escape hatch is Ada.Unchecked_Conversion — explicit, searchable and localized. For serialization, Ada's stream architecture separates what is written (stream attributes, customizable per type) from where it goes (any type derived from Root_Stream_Type).

Unchecked_Conversion

'function Float_Bits is new Ada.Unchecked_Conversion (Float, Unsigned_32);' copies the bit pattern without any conversion or check. Source and target should have the same size (GNAT warns otherwise). Results may be invalid for the target subtype, so validate with 'Valid when converting to types with constraints. It is the clean replacement for C's pointer casts and unions: the generic instantiation documents exactly which reinterpretation happens where.

• Unchecked_Conversion (Source, Target) — bitwise reinterpretation. • Sizes should match; validate results with 'Valid. • Keep uses few and localized.

Bit Manipulation

Package Interfaces declares Unsigned_8/16/32/64 and Integer_8/16/32/64 with guaranteed sizes, and for the unsigned ones Shift_Left, Shift_Right, Shift_Right_Arithmetic, Rotate_Left and Rotate_Right. Modular types support and, or, xor and not bitwise, and wrap around on overflow. Rotating a 16-bit value by 8 swaps its bytes. For portable binary formats, prefer representation clauses or explicit shifts over overlays.

• Interfaces.Unsigned_N / Integer_N. • Shift_Left / Shift_Right / Rotate_Left / Rotate_Right. • and / or / xor / not on modular types.

Custom Streams and Stream Attributes

A stream is any type derived from Ada.Streams.Root_Stream_Type that overrides Read and Write on Stream_Element_Array — a memory buffer, a socket, a compressed file. All stream attributes ('Write, 'Output, 'Read, 'Input) then work on it. For each type you can replace the attribute implementation: 'for Temperature'Write use Write_Temp;' writes a compact 2-byte encoding instead of the default component-by-component layout. The default formats are implementation-defined (GNAT writes Integer as 4 bytes little-endian and String'Output as bounds then characters), so define your own attributes for data exchanged between systems.

• type My_Stream is new Root_Stream_Type with ...; override Read/Write. • for T'Write use Proc; — custom serialization. • Default formats are implementation-defined.

Code Examples

1.0 is 16#3F800000# in IEEE single precision and 16#40490FDB# is π. The memory stream holds 17 bytes: Integer 1000 (232 3 0 0), String'Output of "Ada" (bounds 1 and 3 as 4-byte integers, then 65 100 97), and the custom 2-byte Temperature 215 (215 0). Output: Float_Bits (1.0) = 16#3F800000# Float_Bits (-2.5) = 16#C0200000# Bits_Float (16#40490FDB#) = 3.14159E+00 Shift_Left (1, 10) = 1024, Rotate_Right (16#F0#, 4) = 15 Byte-swapped 16#ABCD# = 16#CDAB# 16#F0F0# and 16#0FF0# = 240, xor = 65280 Bytes written to memory stream: 17 Read back Integer: 1000, String: Ada Raw bytes: 232 3 0 0 1 0 0 0 3 0 0 0 65 100 97 215 0 function Conv is new Ada.Unchecked_Conversion (Unsigned_8, Unsigned_32);

-- use a normal type conversion Unsigned_32 (X) for value conversion

Unchecked_Conversion is for reinterpreting bits of equal size, not widening values.

-- default 'Write formats sent over a network to another platform -- define explicit stream attributes (or a documented encoding)

Default stream representations are compiler- and platform-specific.

overriding procedure Write... is begin... end;... overriding procedure Read...; -- declared after a body -- declare all overriding specs first, then the bodies

A body freezes the type; later overriding declarations are 'too late' (GNAT error).

What does Unchecked_Conversion (Float, Unsigned_32) (1.0) return? A. 1 B. 16#3F80_0000# C. 16#0000_0001# D. Constraint_Error

Which operation swaps the bytes of a 16-bit value? A. Shift_Left (X, 8) B. Rotate_Left (X, 8) C. not X D. X xor 16#FF#

How do you replace the default 'Write of a type? A. overriding 'Write B. for T'Write use My_Write; C. pragma Stream D. Impossible

What must a custom stream type derive from? A. Ada.Streams.Root_Stream_Type B. Ada.Text_IO.File_Type C. Ada.Finalization.Controlled D. System.Address

Trace the unchecked conversion and stream example.

Real output: Float_Bits (1.0) = 16#3F800000# Float_Bits (-2.5) = 16#C0200000# Bits_Float (16#40490FDB#) = 3.14159E+00 Shift_Left (1, 10) = 1024, Rotate_Right (16#F0#, 4) = 15 Byte-swapped 16#ABCD# = 16#CDAB# 16#F0F0# and 16#0FF0# = 240, xor = 65280 Bytes written to memory stream: 17 Read back Integer: 1000, String: Ada Raw bytes: 232 3 0 0 1 0 0 0 3 0 0 0 65 100 97 215 0

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Ada.Streams;  use Ada.Streams;
with Interfaces;   use Interfaces;

procedure L61_Unchecked_Streams is
   --  Reinterpret the bits of one type as another (sizes must match)
   function Float_Bits is new Ada.Unchecked_Conversion (Float, Unsigned_32);
   function Bits_Float is new Ada.Unchecked_Conversion (Unsigned_32, Float);

   function Hex (V : Unsigned_32) return String is
      Digs : constant String := "0123456789ABCDEF";
      R : String (1 .. 8);
      X : Unsigned_32 := V;
   begin
      for I in reverse R'Range loop
         R (I) := Digs (Integer (X and 16#F#) + 1);
         X := Shift_Right (X, 4);
      end loop;
      return "16#" & R & "#";
   end Hex;

   --  ---------- A user-defined stream: an in-memory buffer ----------
   type Memory_Stream is new Root_Stream_Type with record
      Data  : Stream_Element_Array (1 .. 64);
      Last  : Stream_Element_Offset := 0;       --  written so far
      Pos   : Stream_Element_Offset := 1;       --  next to read
   end record;

   --  Overriding specs must come before any body (bodies freeze the type)
   overriding procedure Write (S : in out Memory_Stream
…