mirror of
https://github.com/AdaCore/aws.git
synced 2026-02-12 12:29:46 -08:00
485 lines
14 KiB
Ada
485 lines
14 KiB
Ada
------------------------------------------------------------------------------
|
|
-- Generic memory stream --
|
|
-- --
|
|
-- Copyright (C) 2003-2015, Dmitriy Anisimkov --
|
|
-- --
|
|
-- This library is free software; you can redistribute it and/or modify --
|
|
-- it under the terms of the GNU General Public License as published by --
|
|
-- the Free Software Foundation; either version 2 of the License, or (at --
|
|
-- your option) any later version. --
|
|
-- --
|
|
-- This library is distributed in the hope that it will be useful, but --
|
|
-- WITHOUT ANY WARRANTY; without even the implied warranty of --
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU --
|
|
-- General Public License for more details. --
|
|
-- --
|
|
-- You should have received a copy of the GNU General Public License --
|
|
-- along with this library; if not, write to the Free Software Foundation, --
|
|
-- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. --
|
|
-- --
|
|
-- As a special exception, if other files instantiate generics from this --
|
|
-- unit, or you link this unit with other files to produce an executable, --
|
|
-- this unit does not by itself cause the resulting executable to be --
|
|
-- covered by the GNU General Public License. This exception does not --
|
|
-- however invalidate any other reasons why the executable file might be --
|
|
-- covered by the GNU Public License. --
|
|
------------------------------------------------------------------------------
|
|
|
|
pragma Ada_2012;
|
|
|
|
with Ada.Unchecked_Deallocation;
|
|
|
|
package body Memory_Streams is
|
|
|
|
procedure Free (Item : in out Buffer_Access);
|
|
|
|
procedure Free is
|
|
new Ada.Unchecked_Deallocation (Element_Array, Element_Access);
|
|
|
|
procedure Trim_Last_Block (Stream : in out Stream_Type);
|
|
-- Remove unused space from last buffer
|
|
|
|
function First (Item : in Buffer_Access) return Element_Index with Inline;
|
|
|
|
function Last (Item : in Buffer_Access) return Element_Index with Inline;
|
|
|
|
function Length (Item : in Buffer_Access) return Element_Index is
|
|
(if Item.Steady then Item.Const'Length else Item.Data'Length);
|
|
|
|
------------
|
|
-- Append --
|
|
------------
|
|
|
|
procedure Append
|
|
(Stream : in out Stream_Type;
|
|
Value : in Element_Array;
|
|
Trim : in Boolean := False) is
|
|
begin
|
|
if Value'Length = 0 then
|
|
if Trim then
|
|
Trim_Last_Block (Stream);
|
|
end if;
|
|
|
|
return;
|
|
end if;
|
|
|
|
if Stream.First = null then
|
|
Stream.First := new Buffer_Type (False);
|
|
|
|
if Value'Length >= First_Block_Length or else Trim then
|
|
Stream.First.Data := new Element_Array (1 .. Value'Length);
|
|
else
|
|
Stream.First.Data := new Element_Array (1 .. First_Block_Length);
|
|
end if;
|
|
|
|
Stream.First.Data (1 .. Value'Length) := Value;
|
|
|
|
Stream.Current := Stream.First;
|
|
Stream.Last := Stream.First;
|
|
Stream.Last_Length := Value'Length;
|
|
|
|
elsif Stream.Last.Steady
|
|
or else Stream.Last_Length = Stream.Last.Data'Length
|
|
then
|
|
Stream.Last.Next := new Buffer_Type (False);
|
|
Stream.Last := Stream.Last.Next;
|
|
|
|
if Value'Length >= Next_Block_Length or else Trim then
|
|
Stream.Last.Data := new Element_Array (1 .. Value'Length);
|
|
else
|
|
Stream.Last.Data := new Element_Array (1 .. Next_Block_Length);
|
|
end if;
|
|
|
|
Stream.Last.Data (1 .. Value'Length) := Value;
|
|
|
|
Stream.Last_Length := Value'Length;
|
|
|
|
else
|
|
declare
|
|
Block_Length : constant Element_Offset :=
|
|
Stream.Last_Length + Value'Length;
|
|
begin
|
|
if Block_Length <= Stream.Last.Data'Length then
|
|
Stream.Last.Data
|
|
(Stream.Last_Length + 1 .. Block_Length) := Value;
|
|
Stream.Last_Length := Block_Length;
|
|
|
|
if Trim then
|
|
Trim_Last_Block (Stream);
|
|
end if;
|
|
|
|
else
|
|
declare
|
|
Split_Value : constant Element_Index :=
|
|
Value'First
|
|
+ Stream.Last.Data'Length
|
|
- Stream.Last_Length;
|
|
Next_Length : constant Element_Index :=
|
|
Value'Last - Split_Value + 1;
|
|
begin
|
|
Stream.Last.Data
|
|
(Stream.Last_Length + 1 .. Stream.Last.Data'Last) :=
|
|
Value (Value'First .. Split_Value - 1);
|
|
|
|
Stream.Last.Next := new Buffer_Type (False);
|
|
Stream.Last := Stream.Last.Next;
|
|
|
|
if Next_Length >= Next_Block_Length or else Trim then
|
|
Stream.Last.Data := new Element_Array (1 .. Next_Length);
|
|
else
|
|
Stream.Last.Data :=
|
|
new Element_Array (1 .. Next_Block_Length);
|
|
end if;
|
|
|
|
Stream.Last.Data (1 .. Next_Length) :=
|
|
Value (Split_Value .. Value'Last);
|
|
|
|
Stream.Last_Length := Next_Length;
|
|
end;
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
Stream.Length := Stream.Length + Value'Length;
|
|
end Append;
|
|
|
|
------------
|
|
-- Append --
|
|
------------
|
|
|
|
procedure Append
|
|
(Stream : in out Stream_Type;
|
|
Data : in Constant_Access) is
|
|
begin
|
|
if Data'Length = 0 then
|
|
return;
|
|
end if;
|
|
|
|
if Stream.First = null then
|
|
Stream.First := new Buffer_Type (True);
|
|
Stream.First.Const := Data;
|
|
Stream.Current := Stream.First;
|
|
Stream.Last := Stream.First;
|
|
Stream.Current_Offset := Data'First;
|
|
|
|
else
|
|
Trim_Last_Block (Stream);
|
|
|
|
Stream.Last.Next := new Buffer_Type (True);
|
|
Stream.Last := Stream.Last.Next;
|
|
Stream.Last.Const := Data;
|
|
end if;
|
|
|
|
Stream.Last_Length := Data'Length;
|
|
Stream.Length := Stream.Length + Data'Length;
|
|
end Append;
|
|
|
|
procedure Append
|
|
(Stream : in out Stream_Type;
|
|
Data : in Element_Access) is
|
|
begin
|
|
if Data'Length = 0 then
|
|
return;
|
|
end if;
|
|
|
|
if Stream.First = null then
|
|
Stream.First := new Buffer_Type (False);
|
|
Stream.First.Data := Data;
|
|
Stream.Current := Stream.First;
|
|
Stream.Last := Stream.First;
|
|
Stream.Current_Offset := Data'First;
|
|
|
|
else
|
|
Trim_Last_Block (Stream);
|
|
|
|
Stream.Last.Next := new Buffer_Type (False);
|
|
Stream.Last := Stream.Last.Next;
|
|
Stream.Last.Data := Data;
|
|
end if;
|
|
|
|
Stream.Last_Length := Data'Length;
|
|
Stream.Length := Stream.Length + Data'Length;
|
|
end Append;
|
|
|
|
-----------
|
|
-- Close --
|
|
-----------
|
|
|
|
procedure Close (Stream : in out Stream_Type) is
|
|
First : Buffer_Access;
|
|
Length : Element_Offset := 0;
|
|
begin
|
|
while Stream.First /= null loop
|
|
First := Stream.First;
|
|
|
|
Length := Length +
|
|
(if First.Next = null then Stream.Last_Length
|
|
else Memory_Streams.Length (First));
|
|
|
|
Stream.First := First.Next;
|
|
Free (First);
|
|
end loop;
|
|
|
|
Stream.Current := null;
|
|
Stream.Last := null;
|
|
|
|
Reset (Stream);
|
|
|
|
if Stream.Length /= Length then
|
|
raise Program_Error;
|
|
end if;
|
|
|
|
Stream.Length := 0;
|
|
end Close;
|
|
|
|
-----------------
|
|
-- End_Of_File --
|
|
-----------------
|
|
|
|
function End_Of_File (Stream : in Stream_Type) return Boolean is
|
|
begin
|
|
return Stream.Current = null
|
|
or else (Stream.Current.Next = null
|
|
and then Stream.Current_Offset > Last (Stream.Current));
|
|
end End_Of_File;
|
|
|
|
-----------
|
|
-- First --
|
|
-----------
|
|
|
|
function First (Item : in Buffer_Access) return Element_Index is
|
|
begin
|
|
if Item.Steady then
|
|
return Item.Const'First;
|
|
else
|
|
return Item.Data'First;
|
|
end if;
|
|
end First;
|
|
|
|
----------
|
|
-- Free --
|
|
----------
|
|
|
|
procedure Free (Item : in out Buffer_Access) is
|
|
procedure Deallocate is
|
|
new Ada.Unchecked_Deallocation (Buffer_Type, Buffer_Access);
|
|
begin
|
|
if not Item.Steady then
|
|
Free (Item.Data);
|
|
end if;
|
|
|
|
Deallocate (Item);
|
|
end Free;
|
|
|
|
----------
|
|
-- Last --
|
|
----------
|
|
|
|
function Last (Item : in Buffer_Access) return Element_Index is
|
|
begin
|
|
if Item.Steady then
|
|
return Item.Const'Last;
|
|
else
|
|
return Item.Data'Last;
|
|
end if;
|
|
end Last;
|
|
|
|
-------------
|
|
-- Pending --
|
|
-------------
|
|
|
|
function Pending (Stream : in Stream_Type) return Element_Offset is
|
|
B : Buffer_Access := Stream.Current;
|
|
S : Element_Index'Base;
|
|
begin
|
|
if B = null then
|
|
return 0;
|
|
end if;
|
|
|
|
S := First (Stream.Current) - Stream.Current_Offset;
|
|
|
|
loop
|
|
if B.Next = null then
|
|
S := S + Stream.Last_Length;
|
|
exit;
|
|
else
|
|
S := S + Length (B);
|
|
end if;
|
|
|
|
B := B.Next;
|
|
end loop;
|
|
|
|
return S;
|
|
end Pending;
|
|
|
|
----------
|
|
-- Read --
|
|
----------
|
|
|
|
procedure Read
|
|
(Stream : in out Stream_Type;
|
|
Buffer : out Element_Array;
|
|
Last : out Element_Offset)
|
|
is
|
|
Buffer_Offset : Element_Offset := Buffer'First;
|
|
Block_Over : Boolean;
|
|
|
|
procedure Append (Data : in Element_Array);
|
|
-- Add the Data to the Buffer till necessary from the
|
|
-- Resource.Current_Offset and move it.
|
|
|
|
------------
|
|
-- Append --
|
|
------------
|
|
|
|
procedure Append (Data : in Element_Array) is
|
|
Buffer_Len_1 : constant Element_Offset :=
|
|
Buffer'Last - Buffer_Offset;
|
|
-- Buffer remain length minus 1
|
|
|
|
Current_Len_1 : constant Element_Offset :=
|
|
Data'Last - Stream.Current_Offset;
|
|
-- Data remain length minus 1
|
|
|
|
Current_Last : Element_Index;
|
|
begin
|
|
Block_Over := Buffer_Len_1 >= Current_Len_1;
|
|
|
|
if Block_Over then
|
|
Last := Buffer_Offset + Current_Len_1;
|
|
|
|
Buffer (Buffer_Offset .. Last) :=
|
|
Data (Stream.Current_Offset .. Data'Last);
|
|
|
|
Buffer_Offset := Last + 1;
|
|
|
|
Stream.Current_Offset := Data'Last + 1;
|
|
|
|
else
|
|
Last := Buffer'Last;
|
|
|
|
Current_Last := Stream.Current_Offset + Buffer_Len_1;
|
|
|
|
Buffer (Buffer_Offset .. Last) :=
|
|
Data (Stream.Current_Offset .. Current_Last);
|
|
|
|
Stream.Current_Offset := Current_Last + 1;
|
|
end if;
|
|
end Append;
|
|
|
|
begin
|
|
Last := Buffer'First - 1;
|
|
|
|
if Stream.Current = null then
|
|
return;
|
|
end if;
|
|
|
|
loop
|
|
if Stream.Current.Next = null then
|
|
-- Last block
|
|
|
|
if Stream.Current.Steady then
|
|
Append (Stream.Current.Const.all);
|
|
else
|
|
Append (Stream.Current.Data
|
|
(Stream.Current.Data'First
|
|
.. Stream.Last_Length + Stream.Current.Data'First
|
|
- 1));
|
|
end if;
|
|
|
|
if Block_Over then
|
|
Stream.Current := null;
|
|
exit;
|
|
end if;
|
|
|
|
else
|
|
if Stream.Current.Steady then
|
|
Append (Stream.Current.Const.all);
|
|
else
|
|
Append (Stream.Current.Data.all);
|
|
end if;
|
|
|
|
if Block_Over then
|
|
Stream.Current := Stream.Current.Next;
|
|
|
|
Stream.Current_Offset := First (Stream.Current);
|
|
end if;
|
|
end if;
|
|
|
|
exit when Last = Buffer'Last;
|
|
end loop;
|
|
end Read;
|
|
|
|
-----------
|
|
-- Reset --
|
|
-----------
|
|
|
|
procedure Reset (Stream : in out Stream_Type) is
|
|
begin
|
|
Stream.Current := Stream.First;
|
|
Stream.Current_Offset := (if Stream.Current = null then 1
|
|
else First (Stream.Current));
|
|
end Reset;
|
|
|
|
---------------
|
|
-- Set_Index --
|
|
---------------
|
|
|
|
procedure Set_Index
|
|
(Stream : in out Stream_Type;
|
|
To : in Element_Offset)
|
|
is
|
|
Idx : Element_Offset;
|
|
begin
|
|
if To < 1 or else To > Size (Stream) then
|
|
Stream.Current := Stream.Last;
|
|
Stream.Current_Offset := Last (Stream.Current) + 1;
|
|
|
|
else
|
|
Idx := Length (Stream.First);
|
|
|
|
Stream.Current := Stream.First;
|
|
|
|
while Idx < To loop
|
|
Stream.Current := Stream.Current.Next;
|
|
Idx := Idx + Length (Stream.Current);
|
|
end loop;
|
|
|
|
Stream.Current_Offset := Last (Stream.Current) + To - Idx;
|
|
end if;
|
|
end Set_Index;
|
|
|
|
----------
|
|
-- Size --
|
|
----------
|
|
|
|
function Size (Stream : in Stream_Type) return Element_Offset is
|
|
begin
|
|
return Stream.Length;
|
|
end Size;
|
|
|
|
----------------------
|
|
-- Trim_Last_Buffer --
|
|
----------------------
|
|
|
|
procedure Trim_Last_Block (Stream : in out Stream_Type) is
|
|
begin
|
|
if Stream.Last = null
|
|
or else Stream.Last.Steady
|
|
or else Stream.Last.Data'Length = Stream.Last_Length
|
|
then
|
|
return;
|
|
end if;
|
|
|
|
declare
|
|
Ptr : constant Element_Access :=
|
|
new Element_Array'
|
|
(Stream.Last.Data (1 .. Stream.Last_Length));
|
|
begin
|
|
Free (Stream.Last.Data);
|
|
Stream.Last.Data := Ptr;
|
|
end;
|
|
end Trim_Last_Block;
|
|
|
|
end Memory_Streams;
|