File : circular_stream.adb


------------------------------------------------------------------------------

--                         ADAGIO - ADALID - AENEA.                         --

--                                                                          --

--                            Copyright (C) 2003                            --

--                                 A. Mosteo.                               --

--                                                                          --

--  Authors: A. Mosteo. (adagio@mosteo.com)                                 --

--                                                                          --

--  If you have any questions in regard to this software, please address    --

--  them to the above email.                                                --

--                                                                          --

--  This program 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 program 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.          --

--                                                                          --

--  You are not allowed to use any part of this code to develop a program   --

--  whose output would be used to harass or prosecute other users of the    --

--  networks Adagio connects with. All data collected with Adagio or a tool --

--  containing Adagio code about other network users must remain            --

--  confidential and cannot be made public by any mean, nor be used to      --

--  harass or legally prosecute these users.                                --

------------------------------------------------------------------------------

--  $Id: circular_stream.adb,v 1.4 2004/01/21 21:05:43 Jano Exp $


--  Circular stream. This is a buffering stream where the written data

--    can be read afterwards in typical producer/consumer fashion.


with Ada.Unchecked_deallocation;

package body Circular_stream is

   Procedure Free is new Unchecked_deallocation
     (Buffer_type, Buffer_type_access);

   ------------------------------------------------------------------------

   -- Overriden primitives                                               --

   ------------------------------------------------------------------------

   procedure Read(
      Stream : in out Stream_type;
      Item   : out Stream_Element_Array;
      Last   : out Stream_Element_Offset) 
   is
      To_read : Stream_element_count := Stream_element_count'Min (
         Item'Length, Available_read (Stream));
      -- Aux is the amount of data read before the split (see lower):

      Aux     : Stream_element_count;
   begin
      -- No circular split:

      if Stream.Buffer.Data'Last - Stream.Pos_read + 1 >= To_read then
         Item (Item'First .. Item'First + To_read - 1) :=
         Stream.Buffer.Data (Stream.Pos_read .. Stream.Pos_read + To_read -1);
         Stream.Pos_read := Stream.Pos_read + To_read;
      else
         -- Circular splitting:

         Aux := Stream.Buffer.Data'Last - Stream.Pos_read + 1;
         Item (Item'First .. Item'First + Aux - 1) :=
            Stream.Buffer.Data (Stream.Pos_read .. Stream.Buffer.Data'Last);
         Item (Item'First + Aux .. Item'First + Aux + (To_read - Aux) - 1) :=
            Stream.Buffer.Data (Stream.Buffer.Data'First ..
               Stream.Buffer.Data'First + (To_read - Aux) - 1);
         Stream.Pos_read := Stream.Buffer.Data'First + To_read - Aux;
      end if;
      if Stream.Pos_read > Stream.Buffer.Data'Last then
         Stream.Pos_read := Stream.Buffer.Data'First;
      end if;
      Stream.Available_read := Stream.available_read - To_read;
      Last := Item'First + To_read - 1;
   end Read;

        procedure Write(
      Stream : in out Stream_type;
      Item   : in Stream_Element_Array) 
   is
      -- Aux is the amount written before the split:

      Aux : Stream_element_count;
   begin
      -- Enough room?

      if Available_write (Stream) < Natural'(Item'Length) then
         raise Constraint_error;
      end if;
      -- Allocation on first use:

      if Stream.Buffer.Data = null then
         Stream.Buffer.Data := new Buffer_type (1 .. Stream.Buffer.Size);
      end if;
      -- No circular split:

      if Item'Length <= Stream.Buffer.Data'Last - Stream.Pos_write + 1 then
         Stream.Buffer.Data (Stream.Pos_write .. 
            Stream.Pos_write + Item'Length - 1) := Item;
         Stream.Pos_write := Stream.Pos_write + Item'Length;
      else
         -- Circular split:

         Aux := Stream.Buffer.Data'Last - Stream.Pos_write + 1;
         Stream.Buffer.Data (Stream.Pos_write .. Stream.Buffer.Data'Last) :=
            Item (Item'First .. Item'First + Aux - 1);
         Stream.Buffer.Data (Stream.Buffer.Data'First ..
            Stream.Buffer.Data'First + (Item'Length - Aux) - 1) :=
               Item (Item'First + Aux .. Item'First + Aux + 
                  (Item'Length - Aux) - 1);
         Stream.Pos_write := Stream.Buffer.Data'First + Item'Length - Aux;
      end if;
      if Stream.Pos_write > Stream.Buffer.Data'Last then
         Stream.Pos_write := Stream.Buffer.Data'First;
      end if;
      Stream.Available_read := Stream.Available_read + Item'Length;
   end Write;

   ------------------------------------------------------------------------

   -- Available_read                                                     --

   ------------------------------------------------------------------------

   -- Says how many data has been written but not read:

   function Available_read (Stream : in Stream_type) 
      return Stream_element_count
   is
   begin
      return Stream.Available_read;
   end Available_read;

   function Available_read (Stream : in Stream_type) return Natural is
   begin
      return Natural (Stream.Available_read);
   end Available_read;

   ------------------------------------------------------------------------

   -- Available_write                                                    --

   ------------------------------------------------------------------------

   -- Says how many data has been written but not read:

   function Available_write (Stream : in Stream_type) 
      return Stream_element_count
   is
   begin
      return Stream.Size - Stream.Available_read;
   end Available_write;

   function Available_write (Stream : in Stream_type) return Natural is
   begin
      return Natural (Stream.Size - Stream.Available_read);
   end Available_write;

   ------------------------------------------------------------------------

   -- Reset                                                              --

   ------------------------------------------------------------------------

   -- Resets everything to the starting point

   procedure Reset (Stream : in out Stream_type) is
   begin
      Free (Stream.Buffer.Data);
      Stream.Pos_write := 1;
      Stream.Pos_read  := 1;
      Stream.Available_read := 0;
   end Reset;

   procedure Finalize   (This: in out Controlled_buffer_type) is
   begin
      Free (This.Data);
   end Finalize;

end Circular_stream;