I'm trying to read from a byte buffer in Ada, such as a file or via buffer for a network connection. The messages are variable in size with a common header, in C++ it'd look something like this:

```cpp
enum class MessageType : uint16_t {
    Foo = 0, Bar = 1
};

// Force the layout.
#pragma pack(push,1)

// 4 byte message header
struct MessageHeader {
    uint16_t checksum;
    uint16_t type;
};

// 4 byte header + 4 byte message
struct FooMessage {
    MessageHeader header;
    uint32_t tomatoes;
};

// 4 byte header + 8 byte message
struct BarMessage {
    MessageHeader header;
    uint32_t oranges;
    uint32_t apples;
};
#pragma pack(pop)

// For simplicity, assume the buffer is complete and only holds full messages.
void read(char* buffer, uint32_t bytesLeft) {
    while (bytesLeft > 0) {
        MessageHeader* header = reinterpret_cast<MessageHeader*>(buffer);
        switch (header->type) {
            case FooType: {
                FooMessage* foo = reinterpret_case<FooMessage*>(buffer);
                // process as const FooMessage&
                processFoo(*foo);
            }
            case BarType: {
                BarMessage* bar = reinterpret_cast<BarMessage*>(buffer);
                // process as const BarMessage&
                processBar(*bar);
            }
        }
        const auto size = (header->type == Foo ? sizeof(FooMessage) : sizeof(BarMessage));
        buffer += size;
        bytesLeft -= size;
    }
}

```

I'm not sure of the idiomatic way of doing it. Note that in some formats, the message type might not be the leading data member in the header, as well. Should you be writing to and reading off an array of `Character` or something from `Interfaces.C.char_array`, or an address of memory from `System.Address` or something else? Or should this be an address to an array elsewhere here, or just an array with "Convention => C" to prevent the leading size from being included?

This is what I have so far in Ada:

```
type Message_Type is (Foo, Bar) with Size => 16;
for Message_Type use (Foo => 0, Bar => 1);

-- Assume these work correctly and I don't need to do bit layout directly.
type Message_Header is record
    Checksum : Interfaces.Integer_16;
    Msg_Type : Message_Type;
end record
    with Convention => C, Size => 32;

type Foo_Message is record
    Header   : Message_Header;
    Tomatoes : Interfaces.Integer_32;
end record
    with Convention => C, Size => 64;

type Bar_Message is record
    Header  : Message_Header;
    Oranges : Interfaces.Integer_32;
    Apples  : Interfaces.Integer_32;
end record
    with Convention => C, Size => 96;

procedure Read(
    -- System.Address seems really weird here
    Buffer     : in out System.Address;
    Bytes_Left : in out Interfaces.Integer_64)
is
    use type Interfaces.Integer_64;
    use type System.Address;
    function To_Address is new Ada.Unchecked_Conversion (Interfaces.Integer_64, System.Address);
    function To_Integer is new Ada.Unchecked_Conversion (System.Address, Interfaces.Integer_64);

    procedure Process_Bar (B : aliased Bar_Message) is null;
    procedure Process_Foo (F : aliased Foo_Message) is null;
begin
    while Bytes_Left > 0 loop
        declare
            -- I'm really lost here.
            --
            -- Do you use access types to access the buffer or
            -- setting the address with "for Foo'Address use Buffer"??
            --
            Header : Message_Header;
            for Header'Address use Buffer;
    enter code here
            -- I'm assuming this doesn't initialize Foo and Bar here?    
            Foo_Msg : aliased Foo_Message;
            Bar_Msg : aliased Bar_Message;
            for Foo_Msg'Address use Buffer;
            for Bar_Msg'Address use Buffer;
            -- I'm assuming this doesn't initialize Foo and Bar here?    
            Size : System.Address := To_Address(0);
        begin
            case Header.Msg_Type is
                when Foo => Process_Foo (Foo_Msg);
                when Bar => Process_Bar (Bar_Msg);
            end case;
            Size := To_Address (if Header.Msg_Type = Foo then Foo'Size else Bar'Size);

            -- There's probably a better way to do this.
            Buffer := To_Address(To_Integer (Buffer) + To_Integer (Size));
            Bytes_Left := Bytes_Left - To_Integer (Size);
        end;
    end loop;
end Read;

```

What's the idiomatic way to march in a variable way across bytes in buffers and read the data in place?