--  with Interfaces.C;            use Interfaces.C;
--  with Interfaces.C.Strings;    use Interfaces.C.Strings;

--  with System;                  use System;
--  with System.Storage_Pools;    use System.Storage_Pools;
--  with System.Storage_Elements; use System.Storage_Elements;

-- O_Strings, native targets implementation

with Ada.Unchecked_Deallocation;

with Ada.Strings;
with Ada.Strings.Fixed;


package body Jwa.Strings is

   procedure Free is new Ada.Unchecked_Deallocation
     (Object => String_8_Internal,
      Name => String_8_Internal_Ptr);

   --------------
   -- Capacity --
   --------------

   function Capacity (Length : Natural) return Natural is
   begin
      if Length <= 256 then
         return Length / 8 * 8 + 8 + 4;
      elsif Length <= 512 then
         return Length / 32 * 32 + 32 + 4;
      elsif Length <= 1024 then
         return Length / 128 * 128 + 128 + 4;
      elsif Length <= 2048 then
         return Length / 256 * 256 + 256 + 4;
      else
         return Length / 1024 * 1024 + 1024 + 4;
      end if;
   end Capacity;

   -------------------
   -- Good_Capacity --
   -------------------

   function Good_Capacity (Old_Capacity, New_Length : Natural) return Boolean is
   begin
      return Capacity (New_Length) = Old_Capacity; -- stub implementation
   end Good_Capacity;

   ---------------------
   -- String_8.Adjust --
   ---------------------

   procedure Adjust (Object : in out String_8) is
   begin
      if Object.Ptr /= null then
         declare
            RC : Reference_Counter_Type renames Object.Ptr.Counter;
         begin
            RC.rc := RC.rc + 1;
         end;
      end if;
   end Adjust;

   -----------------------
   -- String_8.Finalize --
   -----------------------

   procedure Finalize (Object : in out String_8) is
      Delete : Boolean := False;
   begin
      if Object.Ptr /= null then
         declare
            RC : Reference_Counter_Type renames Object.Ptr.Counter;
         begin
            RC.rc := RC.rc - 1;
            Delete := RC.rc <= 0;
         end;
         if Delete then
            Free (Object.Ptr);
         end if;
      end if;
      Object.Ptr := null;
   end Finalize;

   ---------------------------------------
   -- String_8.Create ( : String_8_Ptr) --
   ---------------------------------------

   function Create (Ptr : String_8_Ptr) return String_8 is
      Length : Natural;
   begin
      return Result : String_8 do
         if Ptr /= null and then Ptr.Length > 0 then
            Length := Ptr.Length;
            Result.Ptr := new String_8_Internal (Capacity (Length));
            Result.Ptr.Data.Length := Length;
            Result.Ptr.Data.chr (Length + 1) := NUL_8;
            Result.Ptr.Data.chr (1 .. Length) := Ptr.chr (1 .. Length);
         end if;
      end return;
   end Create;

   ---------------------------------
   -- String_8.Create ( : String) --
   ---------------------------------

   function Create (Source : String) return String_8 is
      Length : constant Natural := Source'Length;
   begin
      return Result : String_8 do
         if Length > 0 then
            Result.Ptr := new String_8_Internal (Capacity (Length));
            Result.Ptr.Data.Length := Length;
            Result.Ptr.Data.chr (Length + 1) := NUL_8;
            Result.Ptr.Data.chr (1 .. Length) := Source;
         end if;
      end return;
   end Create;

   ----------------------------------
   -- String_8.Create ( : Natural) --
   ----------------------------------

   function Create (Length : Natural) return String_8 is
   begin
      return Result : String_8 do
         if Length > 0 then
            Result.Ptr := new String_8_Internal (Capacity (Length));
            Result.Ptr.Data.Length := Length;
            Result.Ptr.Data.chr (Length + 1) := NUL_8;
         end if;
      end return;
   end Create;

   --------------------------
   -- String_8.Make_Buffer --
   --------------------------

   procedure Make_Buffer (Target : in out String_8; Length : Natural) is
   begin
      if Length <= 0 then
         Target.Set_Empty;
         return;
      end if;

      if Target.Ptr /= null then
         -- Reusing allocated memory is desired
         -- What can prevent it?
         if Target.Ptr.Counter.rc > 1 then
            Target.Set_Empty;
         elsif not Good_Capacity (Target.Ptr.Capacity, Length) then
            Target.Set_Empty;
         end if;
      end if;

      if Target.Ptr = null then
         Target.Ptr := new String_8_Internal (Capacity (Length));
      end if;

      Target.Ptr.Data.Length := Length;
      Target.Ptr.Data.chr (Length + 1) := NUL_8;
   end Make_Buffer;

   ------------------------------------
   -- String_8.Set ( : String_8_Ptr) --
   ------------------------------------

   procedure Set (Target : in out String_8; Ptr : String_8_Ptr) is
      Length : Natural;
   begin
      if Ptr = null or else Ptr.Length <= 0 then
         Target.Set_Empty;
      end if;

      Length := Ptr.Length;

      Target.Make_Buffer (Length);
      Target.Ptr.Data.chr (1 .. Length) := Ptr.chr (1 .. Length);
   end Set;

   -------------------------------
   -- String_8.Set ( : String) --
   -------------------------------

   procedure Set (Target : in out String_8; Source : String) is
   begin
      if Source'Length > 0 then
         Target.Make_Buffer (Source'Length);
         Target.Ptr.Data.chr (1 .. Source'Length) := Source;
      else
         Target.Set_Empty;
      end if;
   end Set;

   -------------------------
   -- String_8.Set_Length --
   -------------------------

   procedure Set_Length (Target : in out String_8; Length : Natural) is
      Target_Length : constant Natural := Target.Length;
   begin
      if Length = Target_Length then
         return;
      elsif Length <= 0 then
         Target.Set_Empty;
         return;
      elsif Target.Ptr = null then
         Target.Ptr := new String_8_Internal (Capacity (Length));
         Target.Ptr.Data.Length := Length;
         Target.Ptr.Data.chr (Length + 1) := NUL_8;
         return;
      end if;

      declare
         Target_Ptr : String_8_Internal_Ptr := Target.Ptr;
         Target_Data : String_8_Ptr := Target_Ptr.Data'Access;
         Target_Copy : String_8;
         Copy_Length : Natural := Natural'Min (Target_Length, Length);
         Reuse : Boolean := True;
      begin
         -- Reusing allocated memory is desired
         -- What can prevent it?
         if Target_Ptr.Counter.rc > 1 or else
           not Good_Capacity (Target_Data.Capacity, Length) then
            -- Target_Copy := Target;
            -- Target.Set_Empty;

            -- don't touch reference counter

            Target_Copy.Ptr := Target_Ptr;
            Target.Ptr := null;
            Reuse := False;
         end if;

         if Target.Ptr = null then
            Target.Ptr := new String_8_Internal (Capacity (Length));
         end if;

         Target.Ptr.Data.Length := Length;
         Target.Ptr.Data.chr (Length + 1) := NUL_8;

         if not Reuse then
            Target.Ptr.Data.chr (1 .. Copy_Length) :=
              Target_Data.chr (1 .. Copy_Length);
         end if;
      end;
   end Set_Length;

   ---------------------
   -- String_8.To_Ada --
   ---------------------

   function To_Ada (Source : String_8) return String is
   begin
      if Source.Ptr = null then
         return (1 .. 0 => <>);
      else
         declare
            Data : String_8_Data renames Source.Ptr.Data;
         begin
            return Data.chr (1 .. Data.Length);
         end;
      end if;
   end To_Ada;

   ---------------------
   -- String_8.Unique --
   ---------------------

   procedure Unique (Object : in out String_8) is
      Is_Shared : Boolean := True;
      Object_Copy : String_8; -- don't remove
   begin
      if Object.Ptr /= null then
         declare
            RC : Reference_Counter_Type renames Object.Ptr.Counter;
         begin
            Is_Shared := RC.rc > 1;
         end;
         if Is_Shared then
            Object_Copy := String_8 (Object);
            Object.Set (Object_Copy.Ptr.Data'Access);
         end if;
      end if;
   end Unique;

   ----------------
   -- String_8.p --
   ----------------

   function p (Item : String_8) return String_8_Ptr is
   begin
      if Item.Ptr /= null then
         return Item.Ptr.Data'Access;
      else
         return Null_String_8_Ptr;
      end if;
   end p;

   ----------------
   -- String_8.a --
   ----------------

   function a (Item : String_8) return Char_8_Ptr is
   begin
      if Item.Ptr /= null then
         return Char_8_Ptr (Item.Ptr.Data.chr'Address);
      else
         return Null_String_8_Address;
      end if;
   end a;

   ---------------------------------------------
   -- String_8.Create ( : Char_8_Ptr, Length) --
   ---------------------------------------------

   function Create
     (Ptr : Char_8_Ptr;
      Length : Natural) return String_8 is
   begin
      if Length > 0 then
         return Result : String_8 do
            declare
               Src : String (1 .. Length);
               pragma Import (C, Src);
               for Src'Address use Address (Ptr);
            begin
               Result.Make_Buffer (Length);
               Result.Ptr.Data.chr (1 .. Length) := Src;
            end;
         end return;
      else
         return Null_String_8;
      end if;
   end Create;

   ---------------------------------------------------
   -- String_8.Create_Z ( : Char_8_Ptr, Max_Length) --
   ---------------------------------------------------

   function Create_Z
     (Ptr : Char_8_Ptr;
      Max_Length : Natural := Natural'Last) return String_8
   is
      Addr : constant Address := Address (Ptr);
   begin
      for i in 1 .. Max_Length loop
         declare
            S : String (1 .. i);
            pragma Import (C, S);
            for S'Address use Addr;
         begin
            if S (i) = NUL_8 then
               return Create (Ptr, i - 1);
            end if;
         end;
      end loop;
      return Create (Ptr, Max_Length);
   end Create_Z;

   --------------------------------------------
   -- String_8.Create_Z ( : String_8_Buffer) --
   --------------------------------------------

   function Create_Z (Buffer : String_8_Buffer) return String_8 is
      Ptr : constant Char_8_Ptr := Char_8_Ptr (Buffer'Address);
   begin
      for i in Buffer'Range loop
         if Buffer (i) = NUL_8 then
            return Create (Ptr, i - Buffer'First);
         end if;
      end loop;
      return Create (Ptr, Buffer'Length);
   end Create_Z;

   ------------------------------------------
   -- String_8.Set ( : Char_8_Ptr, Length) --
   ------------------------------------------

   procedure Set
     (Target : in out String_8;
      Ptr : Char_8_Ptr;
      Length : Natural) is
   begin
      if Length > 0 then
         declare
            Src : String (1 .. Length);
            pragma Import (C, Src);
            for Src'Address use Address (Ptr);
         begin
            Target.Make_Buffer (Length);
            Target.Ptr.Data.chr (1 .. Length) := Src;
         end;
      else
         Target.Set_Empty;
      end if;
   end Set;

   ------------------------------------------------
   -- String_8.Set_Z ( : Char_8_Ptr, Max_Length) --
   ------------------------------------------------

   procedure Set_Z
     (Target : in out String_8;
      Ptr : Char_8_Ptr;
      Max_Length : Natural := Natural'Last)
   is
      Addr : constant Address :=
        Address (Ptr);
   begin
      for i in 1 .. Max_Length loop
         declare
            S : String (1 .. i);
            pragma Import (C, S);
            for S'Address use Addr;
         begin
            if S (i) = NUL_8 then
               Target.Set (Ptr, i - 1);
            end if;
         end;
      end loop;
      Target.Set (Ptr, Max_Length);
   end Set_Z;

   ---------------------------------------
   -- String_8.Set_Z ( : String_8_Data) --
   ---------------------------------------

   procedure Set_Z
     (Target : in out String_8;
      Buffer : String_8_Buffer)
   is
      Ptr : constant Char_8_Ptr :=
        Char_8_Ptr (Buffer'Address);
   begin
      for i in Buffer'Range loop
         if Buffer (i) = NUL_8 then
            Target.Set (Ptr, i - Buffer'First);
         end if;
      end loop;
      Target.Set (Ptr, Buffer'Length);
   end Set_Z;

   --------------------------------------
   --                                  --
   --      Mere string facilities      --
   --                                  --
   --------------------------------------

   ---------------------
   -- String_8.Length --
   ---------------------

   function Length (Source : String_8) return Natural is
   begin
      if Source.Ptr = null then
         return 0;
      else
         return Source.Ptr.Data.Length;
      end if;
   end Length;

   -----------------------
   -- String_8.Is_Empty --
   -----------------------

   function Is_Empty (Source : String_8) return Boolean is
   begin
      return Source.Ptr = null;
   end Is_Empty;

   -----------------------------------
   -- String_8.Append ( : String_8) --
   -----------------------------------

   procedure Append (Source : in out String_8; New_Item : String_8) is
      Length_1 : constant Natural := Source.Length;
      Length_2 : constant Natural := New_Item.Length;
   begin
      if Length_2 <= 0 then
         return;
      elsif Length_1 <= 0 then
         Source := New_Item;
         return;
      end if;

      Source.Set_Length (Length_1 + Length_2);
      Source.Ptr.Data.chr (Length_1 + 1 .. Length_1 + Length_2) :=
        New_Item.Ptr.Data.chr (1 .. Length_2);
   end Append;

   ---------------------------------
   -- String_8.Append ( : String) --
   ---------------------------------

   procedure Append (Source : in out String_8; New_Item : String) is
      Length_1 : constant Natural := Source.Length;
      Length_2 : constant Natural := New_Item'Length;
   begin
      if Length_2 <= 0 then
         return;
      elsif Length_1 <= 0 then
         Source.Set (New_Item);
         return;
      end if;

      Source.Set_Length (Length_1 + Length_2);
      Source.Ptr.Data.chr (Length_1 + 1 .. Length_1 + Length_2) := New_Item;
   end Append;

   ---------------------------------
   -- String_8.Append ( : Char_8) --
   ---------------------------------

   procedure Append (Source : in out String_8; New_Item : Char_8) is
      Length : constant Natural := Source.Length;
   begin
      if Length <= 0 then
         Source.Set ((1 => New_Item));
         return;
      end if;

      Source.Set_Length (Length + 1);
      Source.Ptr.Data.chr (Length + 1) := New_Item;
   end Append;

   ------------------
   -- String_8.Get --
   ------------------

   function Get (Source : String_8; Index : Positive) return Char_8 is
      Length : constant Natural := Source.Length;
   begin
      if Index > Length then
         raise Ada.Strings.Index_Error;
      end if;

      return Source.Ptr.Data.chr (Index);
   end Get;

   ---------------------------------------
   -- String_8.Set ( : Index, : Char_8) --
   ---------------------------------------

   procedure Set (Source : in out String_8; Index : Positive; By : Char_8) is
      Length : constant Natural := Source.Length;
   begin
      if Index > Length then
         raise Ada.Strings.Index_Error;
      end if;

      if Source.Ptr.Data.chr (Index) /= By then
         Source.Unique;
         Source.Ptr.Data.chr (Index) := By;
      end if;
   end Set;

   --------------------
   -- String_8.Slice --
   --------------------

   function Slice (Source : String_8; Low : Positive; High : Natural)
                   return String
   is
      Length : constant Natural := Source.Length;
   begin
      if Low > Length + 1 or High > Length then
         raise Ada.Strings.Index_Error;
      end if;

      if High < Low then
         return (Low .. High => <>);
      else
         return Source.Ptr.Data.chr (Low .. High);
      end if;
   end Slice;

   ----------------------
   -- String_8.Slice_8 --
   ----------------------

   function Slice_8 (Source : String_8; Low : Positive; High : Natural)
                     return String_8
   is
      Length : constant Natural := Source.Length;
   begin
      if Low > Length + 1 or High > Length then
         raise Ada.Strings.Index_Error;
      end if;

      if High < Low then
         return Null_String_8;
      elsif Low = 1 and High = Length then
         return Source;
      else
         return Result : String_8 := String_8'(Create (High - Low + 1)) do
            Result.Ptr.Data.chr (1 .. High - Low + 1) :=
              Source.Ptr.Data.chr (Low .. High);
         end return;
      end if;
   end Slice_8;

   ----------------------
   -- String_8.Slice_8 --
   ----------------------

   procedure Slice_8 (Source : String_8; Target : in out String_8;
                      Low : Positive; High : Natural)
   is
      Length : constant Natural := Source.Length;
   begin
      if Low > Length + 1 or High > Length then
         raise Ada.Strings.Index_Error;
      end if;

      if High < Low then
         Target.Set_Empty;
      elsif Low = 1 and High = Length then
         Target := Source;
      else
         Target.Make_Buffer (High - Low + 1);
         Target.Ptr.Data.chr (1 .. High - Low + 1) :=
           Source.Ptr.Data.chr (Low .. High);
      end if;

   end Slice_8;

   -----------------------------------
   -- "&" ( : String_8, : String_8) --
   -----------------------------------

   function "&" (Left : String_8; Right : String_8) return String_8 is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return Left;
      elsif Length_1 <= 0 then
         return Right;
      end if;

      return Result : String_8 := String_8'(Create (Length_1 + Length_2)) do
         Result.Ptr.Data.chr (1 .. Length_1) :=
           Left.Ptr.Data.chr (1 .. Length_1);
         Result.Ptr.Data.chr (Length_1 + 1 .. Length_1 + Length_2) :=
           Right.Ptr.Data.chr (1 .. Length_2);
      end return;
   end "&";

   ---------------------------------
   -- "&" ( : String_8, : String) --
   ---------------------------------

   function "&" (Left : String_8; Right : String) return String_8 is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right'Length;
   begin
      if Length_2 <= 0 then
         return Left;
      elsif Length_1 <= 0 then
         return String_8'(Create (Right));
      end if;

      return Result : String_8 := String_8'(Create (Length_1 + Length_2)) do
         Result.Ptr.Data.chr (1 .. Length_1) :=
           Left.Ptr.Data.chr (1 .. Length_1);
         Result.Ptr.Data.chr (Length_1 + 1 .. Length_1 + Length_2) := Right;
      end return;
   end "&";

   ---------------------------------
   -- "&" ( : String, : String_8) --
   ---------------------------------

   function "&" (Left : String; Right : String_8) return String_8 is
      Length_1 : constant Natural := Left'Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return String_8'(Create (Left));
      elsif Length_1 <= 0 then
         return Right;
      end if;

      return Result : String_8 := String_8'(Create (Length_1 + Length_2)) do
         Result.Ptr.Data.chr (1 .. Length_1) := Left;
         Result.Ptr.Data.chr (Length_1 + 1 .. Length_1 + Length_2) :=
           Right.Ptr.Data.chr (1 .. Length_2);
      end return;
   end "&";

   ---------------------------------
   -- "&" ( : String_8, : Char_8) --
   ---------------------------------

   function "&" (Left : String_8; Right : Char_8) return String_8 is
      Length_1 : constant Natural := Left.Length;
   begin
      if Length_1 <= 0 then
         return String_8'(Create ((1 => Right)));
      end if;

      return Result : String_8 := String_8'(Create (Length_1 + 1)) do
         Result.Ptr.Data.chr (1 .. Length_1) :=
           Left.Ptr.Data.chr (1 .. Length_1);
         Result.Ptr.Data.chr (Length_1 + 1) := Right;
      end return;
   end "&";

   ---------------------------------
   -- "&" ( : Char_8, : String_8) --
   ---------------------------------

   function "&" (Left : Char_8; Right : String_8) return String_8 is
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return String_8'(Create ((1 => Left)));
      end if;

      return Result : String_8 := String_8'(Create (1 + Length_2)) do
         Result.Ptr.Data.chr (1) := Left;
         Result.Ptr.Data.chr (2 .. 1 + Length_2) :=
           Right.Ptr.Data.chr (1 .. Length_2);
      end return;
   end "&";

   --------------------------------
   -- "*" ( : Natural, : Char_8) --
   --------------------------------

   function "*" (Left : Natural; Right : Char_8) return String_8 is
   begin
      if Left <= 0 then
         return Null_String_8;
      end if;

      return Result : String_8 := String_8'(Create (Left)) do
         Result.Ptr.Data.chr (1 .. Left) := (1 .. Left => Right);
      end return;
   end "*";

   --------------------------------
   -- "*" ( : Natural, : String) --
   --------------------------------

   function "*" (Left : Natural; Right : String) return String_8 is
      R_Length : constant Natural := Right'Length;
      Length : constant Natural := Left * R_Length;
   begin
      if Length <= 0 then
         return Null_String_8;
      end if;

      if R_Length = 1 then
         return Left * Right (Right'First);
      end if;

      return Result : String_8 := String_8'(Create (Length)) do
         declare
            Result_Data : String_8_Data renames Result.Ptr.Data;
         begin
            for i in 0 .. Left - 1 loop
               Result_Data.chr (1 + i * R_Length .. (1 + i) * R_Length) :=
                 Right;
            end loop;
         end;
      end return;
   end "*";

   ----------------------------------
   -- "*" ( : Natural, : String_8) --
   ----------------------------------

   function "*" (Left : Natural; Right : String_8) return String_8 is
      R_Length : constant Natural := Right.Length;
      Length : constant Natural := Left * R_Length;
   begin
      if Length <= 0 then
         return Null_String_8;
      end if;

      if R_Length = 1 then
         return Left * Right.Ptr.Data.chr (1);
      end if;

      return Result : String_8 := String_8'(Create (Length)) do
         declare
            Right_Data : String_8_Data renames Right.Ptr.Data;
            Result_Data : String_8_Data renames Result.Ptr.Data;
         begin
            for i in 0 .. Left - 1 loop
               Result_Data.chr (1 + i * R_Length .. (1 + i) * R_Length) :=
                 Right_Data.chr (1 .. R_Length);
            end loop;
         end;
      end return;
   end "*";

   -----------------------------------
   -- "=" ( : String_8, : String_8) --
   -----------------------------------

   function "=" (Left : String_8; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_1 /= Length_2 then
         return False;
      elsif Length_1 <= 0 then
         return True;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) =
        Right.Ptr.Data.chr (1 .. Length_2);
   end "=";

   ---------------------------------
   -- "=" ( : String_8, : String) --
   ---------------------------------

   function "=" (Left : String_8; Right : String) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right'Length;
   begin
      if Length_1 /= Length_2 then
         return False;
      elsif Length_1 <= 0 then
         return True;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) = Right;
   end "=";

   ---------------------------------
   -- "=" ( : String, : String_8) --
   ---------------------------------

   function "=" (Left : String; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left'Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_1 /= Length_2 then
         return False;
      elsif Length_1 <= 0 then
         return True;
      end if;

      return Left = Right.Ptr.Data.chr (1 .. Length_2);
   end "=";

   -----------------------------------
   -- "<" ( : String_8, : String_8) --
   -----------------------------------

   function "<" (Left : String_8; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return False;
      elsif Length_1 <= 0 then
         return True;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) <
        Right.Ptr.Data.chr (1 .. Length_2);
   end "<";

   ---------------------------------
   -- "<" ( : String_8, : String) --
   ---------------------------------

   function "<" (Left : String_8; Right : String) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right'Length;
   begin
      if Length_2 <= 0 then
         return False;
      elsif Length_1 <= 0 then
         return True;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) < Right;
   end "<";

   ---------------------------------
   -- "<" ( : String, : String_8) --
   ---------------------------------

   function "<" (Left : String; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left'Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return False;
      elsif Length_1 <= 0 then
         return True;
      end if;

      return Left < Right.Ptr.Data.chr (1 .. Length_2);
   end "<";

   ------------------------------------
   -- "<=" ( : String_8, : String_8) --
   ------------------------------------

   function "<=" (Left : String_8; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_1 <= 0 then
         return True;
      elsif Length_2 <= 0 then
         return False;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) <=
        Right.Ptr.Data.chr (1 .. Length_2);
   end "<=";

   ----------------------------------
   -- "<=" ( : String_8, : String) --
   ----------------------------------

   function "<=" (Left : String_8; Right : String) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right'Length;
   begin
      if Length_1 <= 0 then
         return True;
      elsif Length_2 <= 0 then
         return False;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) <= Right;
   end "<=";

   ----------------------------------
   -- "<=" ( : String, : String_8) --
   ----------------------------------

   function "<=" (Left : String; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left'Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_1 <= 0 then
         return True;
      elsif Length_2 <= 0 then
         return False;
      end if;

      return Left <= Right.Ptr.Data.chr (1 .. Length_2);
   end "<=";

   -----------------------------------
   -- ">" ( : String_8, : String_8) --
   -----------------------------------

   function ">" (Left : String_8; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_1 <= 0 then
         return False;
      elsif Length_2 <= 0 then
         return True;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) >
        Right.Ptr.Data.chr (1 .. Length_2);
   end ">";

   ---------------------------------
   -- ">" ( : String_8, : String) --
   ---------------------------------

   function ">" (Left : String_8; Right : String) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right'Length;
   begin
      if Length_1 <= 0 then
         return False;
      elsif Length_2 <= 0 then
         return True;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) > Right;
   end ">";

   ---------------------------------
   -- ">" ( : String, : String_8) --
   ---------------------------------

   function ">" (Left : String; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left'Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_1 <= 0 then
         return False;
      elsif Length_2 <= 0 then
         return True;
      end if;

      return Left > Right.Ptr.Data.chr (1 .. Length_2);
   end ">";

   ------------------------------------
   -- ">=" ( : String_8, : String_8) --
   ------------------------------------

   function ">=" (Left : String_8; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return True;
      elsif Length_1 <= 0 then
         return False;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) >=
        Right.Ptr.Data.chr (1 .. Length_2);
   end ">=";

   ----------------------------------
   -- ">=" ( : String_8, : String) --
   ----------------------------------

   function ">=" (Left : String_8; Right : String) return Boolean is
      Length_1 : constant Natural := Left.Length;
      Length_2 : constant Natural := Right'Length;
   begin
      if Length_2 <= 0 then
         return True;
      elsif Length_1 <= 0 then
         return False;
      end if;

      return Left.Ptr.Data.chr (1 .. Length_1) >= Right;
   end ">=";

   ----------------------------------
   -- ">=" ( : String, : String_8) --
   ----------------------------------

   function ">=" (Left : String; Right : String_8) return Boolean is
      Length_1 : constant Natural := Left'Length;
      Length_2 : constant Natural := Right.Length;
   begin
      if Length_2 <= 0 then
         return True;
      elsif Length_1 <= 0 then
         return False;
      end if;

      return Left >= Right.Ptr.Data.chr (1 .. Length_2);
   end ">=";

   -------------------------------------------------------
   -- String_8.Index ( : String, , : Character_Mapping) --
   -------------------------------------------------------

   function Index
     (Source  : String_8;
      Pattern : String;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping := Maps.Identity) return Natural is
   begin
      if Pattern'Length <= 0 then
         raise Ada.Strings.Pattern_Error;
      end if;
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index
           (Source => S,
            Pattern => Pattern,
            Going => Going,
            Mapping => Mapping);
      end;
   end Index;

   ----------------------------------------------------------------
   -- String_8.Index ( : String, , : Character_Mapping_Function) --
   ----------------------------------------------------------------

   function Index
     (Source  : String_8;
      Pattern : String;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping_Function) return Natural is
   begin
      if Pattern'Length <= 0 then
         raise Ada.Strings.Pattern_Error;
      end if;
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index
           (Source => S,
            Pattern => Pattern,
            Going => Going,
            Mapping => Mapping);
      end;
   end Index;

   -------------------------------------------
   -- String_8.Index ( : Character_Set, , ) --
   -------------------------------------------

   function Index
     (Source : String_8;
      Set    : Maps.Character_Set;
      Test   : Membership := Inside;
      Going  : Direction  := Forward) return Natural is
   begin
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index
           (Source => S,
            Set => Set,
            Test => Test,
            Going => Going);
      end;
   end Index;

   -------------------------------------------------------------
   -- String_8.Index ( : String, From, , : Character_Mapping) --
   -------------------------------------------------------------

   function Index
     (Source  : String_8;
      Pattern : String;
      From    : Positive;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping := Maps.Identity) return Natural is
   begin
      if Pattern'Length <= 0 then
         raise Ada.Strings.Pattern_Error;
      end if;
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index
           (Source => S,
            Pattern => Pattern,
            From => From,
            Going => Going,
            Mapping => Mapping);
      end;
   end Index;

   ----------------------------------------------------------------------
   -- String_8.Index ( : String, From, , : Character_Mapping_Function) --
   ----------------------------------------------------------------------

   function Index
     (Source  : String_8;
      Pattern : String;
      From    : Positive;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping_Function) return Natural is
   begin
      if Pattern'Length <= 0 then
         raise Ada.Strings.Pattern_Error;
      end if;
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index
           (Source => S,
            Pattern => Pattern,
            From => From,
            Going => Going,
            Mapping => Mapping);
      end;
   end Index;

   -------------------------------------------------
   -- String_8.Index ( : Character_Set, From, , ) --
   -------------------------------------------------

   function Index
     (Source  : String_8;
      Set     : Maps.Character_Set;
      From    : Positive;
      Test    : Membership := Inside;
      Going   : Direction := Forward) return Natural is
   begin
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index
           (Source => S,
            Set => Set,
            From => From,
            Test => Test,
            Going => Going);
      end;
   end Index;

   ----------------------------------
   -- String_8.Index_Non_Blank ( ) --
   ----------------------------------

   function Index_Non_Blank
     (Source : String_8;
      Going  : Direction := Forward) return Natural is
   begin
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index_Non_Blank
           (Source => S,
            Going => Going);
      end;
   end Index_Non_Blank;

   ---------------------------------------
   -- String_8.Index_Non_Blank (From, ) --
   ---------------------------------------

   function Index_Non_Blank
     (Source : String_8;
      From   : Positive;
      Going  : Direction := Forward) return Natural is
   begin
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Index_Non_Blank
           (Source => S,
            From => From,
            Going => Going);
      end;
   end Index_Non_Blank;

   -----------------------------------------------------
   -- String_8.Count ( : String, : Character_Mapping) --
   -----------------------------------------------------

   function Count
     (Source  : String_8;
      Pattern : String;
      Mapping : Maps.Character_Mapping := Maps.Identity) return Natural is
   begin
      if Pattern'Length <= 0 then
         raise Ada.Strings.Pattern_Error;
      end if;
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Count
           (Source => S,
            Pattern => Pattern,
            Mapping => Mapping);
      end;
   end Count;

   --------------------------------------------------------------
   -- String_8.Count ( : String, : Character_Mapping_Function) --
   --------------------------------------------------------------

   function Count
     (Source  : String_8;
      Pattern : String;
      Mapping : Maps.Character_Mapping_Function) return Natural is
   begin
      if Pattern'Length <= 0 then
         raise Ada.Strings.Pattern_Error;
      end if;
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Count
           (Source => S,
            Pattern => Pattern,
            Mapping => Mapping);
      end;
   end Count;

   ---------------------------------------
   -- String_8.Count ( : Character_Set) --
   ---------------------------------------

   function Count
     (Source : String_8;
      Set    : Maps.Character_Set) return Natural is
   begin
      if Source.Ptr = null then
         return 0;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         return Ada.Strings.Fixed.Count
           (Source => S,
            Set => Set);
      end;
   end Count;

   -------------------------
   -- String_8.Find_Token --
   -------------------------

   procedure Find_Token
     (Source : String_8;
      Set    : Maps.Character_Set;
      Test   : Membership;
      First  : out Positive;
      Last   : out Natural) is
   begin
      if Source.Ptr = null then
         First := 1;
         Last := 0;
         return;
      end if;
      declare
         Length : Natural := Source.Length;
         S_Address : Address :=
           Source.Ptr.Data.chr'Address;
         S : constant String (1 .. Length);
         for S'Address use S_Address;
         pragma Import(Ada, S);
      begin
         Ada.Strings.Fixed.Find_Token
           (Source => S,
            Set => Set,
            Test => Test,
            First => First,
            Last => Last);
      end;
   end Find_Token;

   -----------------------------------------------
   -- String_8.Translate ( : Character_Mapping) --
   -----------------------------------------------

   function Translate
     (Source  : String_8;
      Mapping : Maps.Character_Mapping) return String_8 is
   begin
      if Source.Ptr = null then
         return Null_String_8;
      end if;
      return Result : String_8 do
         Result.Make_Buffer (Source.Length);
         declare
            Length : constant Natural := Source.Length;
            Source_Data : String_8_Data renames
              Source.Ptr.Data;
            Result_Data : String_8_Data renames
              Result.Ptr.Data;
         begin
            for i in 1 .. Length loop
               Result_Data.chr (i) :=
                 Maps.Value (Mapping, Source_Data.chr (i));
            end loop;
         end;
      end return;
   end Translate;

   -----------------------------------------------
   -- Source_8.Translate ( : Character_Mapping) --
   -----------------------------------------------

   procedure Translate
     (Source  : in out String_8;
      Mapping : Maps.Character_Mapping) is
   begin
      if Source.Ptr = null then
         return;
      end if;

      Source.Unique;
      declare
         Length : constant Natural := Source.Length;
         Source_Data : String_8_Data renames
           Source.Ptr.Data;
      begin
         for i in 1 .. Length loop
            Source_Data.chr (i) := Maps.Value
              (Mapping, Source_Data.chr (i));
         end loop;
      end;
   end Translate;

   --------------------------------------------------------
   -- String_8.Translate ( : Character_Mapping_Function) --
   --------------------------------------------------------

   function Translate
     (Source  : String_8;
      Mapping : Maps.Character_Mapping_Function) return String_8 is
   begin
      if Source.Ptr = null then
         return Null_String_8;
      end if;
      return Result : String_8 do
         Result.Make_Buffer (Source.Length);
         declare
            Length : constant Natural := Source.Length;
            Source_Data : String_8_Data renames
              Source.Ptr.Data;
            Result_Data : String_8_Data renames
              Result.Ptr.Data;
         begin
            for i in 1 .. Length loop
               Result_Data.chr (i) :=
                 Mapping (Source_Data.chr (i));
            end loop;
         end;
      end return;
   end Translate;

   --------------------------------------------------------
   -- String_8.Translate ( : Character_Mapping_Function) --
   --------------------------------------------------------

   procedure Translate
     (Source  : in out String_8;
      Mapping : Maps.Character_Mapping_Function) is
   begin
      if Source.Ptr = null then
         return;
      end if;

      Source.Unique;
      declare
         Length : constant Natural := Source.Length;
         Source_Data : String_8_Data renames
           Source.Ptr.Data;
      begin
         for i in 1 .. Length loop
            Source_Data.chr (i) :=
              Mapping (Source_Data.chr (i));
         end loop;
      end;
   end Translate;

   ----------------------------
   -- String_8.Replace_Slice --
   ----------------------------

   function Replace_Slice
     (Source : String_8;
      Low    : Positive;
      High   : Natural;
      By     : String) return String_8
   is
      Length : constant Natural := Source.Length;
   begin
      if Low > Length + 1 or High < 0 then
         raise Ada.Strings.Index_Error;
      end if;

      if Length <= 0 then
         return To_String_8 (By);
      end if;

      declare
         Source_Data : String_8_Data renames
           Source.Ptr.Data;
      begin
         if High >= Low then
            return Result : String_8 do
               Result.Make_Buffer
                 (Length + Low + By'Length - High - 1);
               declare
                  Result_Data : String_8_Data renames
                    Result.Ptr.Data;
               begin
                  Result_Data.chr (1 .. Low - 1) :=
                    Source_Data.chr (1 .. Low - 1);
                  Result_Data.chr (Low .. Low + By'Length - 1) := By;
                  Result_Data.chr (Low + By'Length ..
                                     Length + Low + By'Length - High - 1) :=
                      Source_Data.chr (High + 1 .. Length);
               end;
            end return;
         else -- High < Low, insertion only
            if By'Length = 0 then
               return Source;
            end if;
            return Result : String_8 do
               Result.Make_Buffer (Length + By'Length);
               declare
                  Result_Data : String_8_Data renames
                    Result.Ptr.Data;
               begin
                  Result_Data.chr (1 .. Low - 1) :=
                    Source_Data.chr (1 .. Low - 1);
                  Result_Data.chr (Low .. Low + By'Length - 1) := By;
                  Result_Data.chr (Low + By'Length .. Length + By'Length) :=
                    Source_Data.chr (Low .. Length);
               end;
            end return;
         end if;
      end;
   end Replace_Slice;

   ----------------------------
   -- String_8.Replace_Slice --
   ----------------------------

   procedure Replace_Slice
     (Source : in out String_8;
      Low    : Positive;
      High   : Natural;
      By     : String)
   is
      Length : constant Natural := Source.Length;
      By_Length : constant Natural := By'Length;
      New_Length : Natural;
   begin
      if Low > Length + 1 or High < 0 then
         raise Ada.Strings.Index_Error;
      end if;

      if Length <= 0 then
         Source.Set_String_8 (By);
         return;
      end if;

      if High >= Low then
         New_Length := Length + Low + By_Length - High - 1;
         if Source.Ptr.Counter.rc > 1 or else
           not Good_Capacity (Source.Ptr.Capacity, New_Length) then
            -- can't reuse allocated buffer
            declare
               Source_Copy : String_8 := Source;
            begin
               Source.Make_Buffer (New_Length);
               declare
                  Old_Data : String_8_Data renames Source_Copy.Ptr.Data;
                  New_Data : String_8_Data renames Source.Ptr.Data;
               begin
                  New_Data.chr (1 .. Low - 1) :=
                    Old_Data.chr (1 .. Low - 1);
                  New_Data.chr (Low .. Low + By_Length - 1) := By;
                  New_Data.chr (Low + By_Length .. New_Length) :=
                    Old_Data.chr (High + 1 .. Length);
               end;
            end;
         else -- can reuse buffer
            declare
               Source_Data : String_8_Data renames Source.Ptr.Data;
            begin
               Source_Data.Length := New_Length;
               if New_Length /= Length then
                  Source_Data.chr (Low + By_Length .. New_Length) :=
                    Source_Data.chr (High + 1 .. Length);
                  -- Is it legal?
                  -- Assigning string slice to a string slice of the
                  -- same string
                  -- Should Ada compiler be robust in these cases?
                  -- Delphi's Move() implementation checks the sign of
                  -- pointer's difference. Depending on whether Ptr1
                  -- or Ptr2 was greater, it moves memory from left to
                  -- right or vice versa.
               end if;

               Source_Data.chr (Low .. Low + By_Length - 1) := By;
            end;
         end if;
      else -- High < Low, insertion only
         if By_Length = 0 then
            return;
         end if;
         New_Length := Length + By_Length;
         if Source.Ptr.Counter.rc > 1 or else
           not Good_Capacity (Source.Ptr.Capacity, New_Length) then
            -- can't reuse allocated buffer
            declare
               Source_Copy : String_8 := Source;
            begin
               Source.Make_Buffer (New_Length);
               declare
                  Old_Data : String_8_Data renames Source_Copy.Ptr.Data;
                  New_Data : String_8_Data renames Source.Ptr.Data;
               begin
                  New_Data.chr (1 .. Low - 1) :=
                    Old_Data.chr (1 .. Low - 1);
                  New_Data.chr (Low .. Low + By_Length - 1) := By;
                  New_Data.chr (Low + By_Length .. New_Length) :=
                    Old_Data.chr (Low .. Length);
               end;
            end;
         else -- can reuse buffer
            declare
               Source_Data : String_8_Data renames Source.Ptr.Data;
            begin
               Source_Data.Length := New_Length;
               Source_Data.chr (Low + By_Length .. New_Length) :=
                 Source_Data.chr (Low .. Length);
               Source_Data.chr (Low .. Low + By_Length - 1) := By;
            end;
         end if;
      end if;
   end Replace_Slice;

   ---------------------
   -- String_8.Insert --
   ---------------------

   function Insert
     (Source   : String_8;
      Before   : Positive;
      New_Item : String) return String_8
   is
      Length : constant Natural := Source.Length;
      NI_Length : constant Natural := New_Item'Length;
      New_Length : constant Natural := Length + NI_Length;
   begin
      if Before not in 1 .. Length + 1 then
         raise Index_Error;
      end if;

      if Length <= 0 then
         return To_String_8 (New_Item);
      end if;

      if NI_Length <= 0 then
         return Source;
      end if;

      return Result : String_8 do
         Result.Make_Buffer (New_Length);
         declare
            Source_Data : String_8_Data renames Source.Ptr.Data;
            Result_Data : String_8_Data renames Result.Ptr.Data;
         begin
            Result_Data.chr (1 .. Before - 1) :=
              Source_Data.chr (1 .. Before - 1);
            Result_Data.chr (Before .. Before + NI_Length - 1) :=
              New_Item;
            Result_Data.chr (Before + NI_Length .. New_Length) :=
              Source_Data.chr (Before .. Length);
         end;
      end return;
   end Insert;

   ---------------------
   -- String_8.Insert --
   ---------------------

   procedure Insert
     (Source   : in out String_8;
      Before   : Positive;
      New_Item : String)
   is
      Length : constant Natural := Source.Length;
      NI_Length : constant Natural := New_Item'Length;
      New_Length : constant Natural := Length + NI_Length;
   begin
      if Before not in 1 .. Length + 1 then
         raise Index_Error;
      end if;

      if Length <= 0 then
         Source.Set_String_8 (New_Item);
         return;
      end if;

      if NI_Length <= 0 then
         return;
      end if;

      if Source.Ptr.Counter.rc > 1 or else
        not Good_Capacity (Source.Ptr.Capacity, New_Length) then
         -- can't reuse allocated buffer
         declare
            Source_Copy : String_8 := Source;
         begin
            Source.Make_Buffer (New_Length);
            declare
               Old_Data : String_8_Data renames Source_Copy.Ptr.Data;
               New_Data : String_8_Data renames Source.Ptr.Data;
            begin
               New_Data.chr (1 .. Before - 1) :=
                 Old_Data.chr (1 .. Before - 1);
               New_Data.chr (Before .. Before + NI_Length - 1) :=
                 New_Item;
               New_Data.chr (Before + NI_Length .. New_Length) :=
                 Old_Data.chr (Before .. Length);
            end;
         end;
      else -- can reuse buffer
         declare
            Source_Data : String_8_Data renames Source.Ptr.Data;
         begin
            Source_Data.Length := New_Length;
            Source_Data.chr (Before + NI_Length .. New_Length) :=
              Source_Data.chr (Before .. Length);
            Source_Data.chr (Before .. Before + NI_Length - 1) :=
              New_Item;
         end;
      end if;
   end Insert;

   ------------------------
   -- String_8.Overwrite --
   ------------------------

   function Overwrite
     (Source   : String_8;
      Position : Positive;
      New_Item : String) return String_8
   is
      Length : constant Natural := Source.Length;
      NI_Length : constant Natural := New_Item'Length;
      New_Length : Natural;
   begin
      if Position not in 1 .. Length + 1 then
         raise Index_Error;
      end if;

      if Position = 1 and Length <= NI_Length then
         return To_String_8 (New_Item);
      end if;

      if NI_Length <= 0 then
         return Source;
      end if;

      if Position + NI_Length - 1 <= Length then
         -- Length remains the same
         New_Length := Length;
         return Result : String_8 do
            Result.Make_Buffer (New_Length);
            declare
               Source_Data : String_8_Data renames Source.Ptr.Data;
               Result_Data : String_8_Data renames Result.Ptr.Data;
            begin
               Result_Data.chr (1 .. Position - 1) :=
                 Source_Data.chr (1 .. Position - 1);
               Result_Data.chr (Position .. Position + NI_Length - 1) :=
                 New_Item;
               Result_Data.chr (Position + NI_Length .. New_Length) :=
                 Source_Data.chr (Position + NI_Length .. Length);
            end;
         end return;
      else -- Length is increased
         New_Length := Position + NI_Length - 1;
         return Result : String_8 do
            Result.Make_Buffer (New_Length);
            declare
               Source_Data : String_8_Data renames Source.Ptr.Data;
               Result_Data : String_8_Data renames Result.Ptr.Data;
            begin
               Result_Data.chr (1 .. Position - 1) :=
                 Source_Data.chr (1 .. Position - 1);
               Result_Data.chr (Position .. New_Length) :=
                 New_Item;
            end;
         end return;
      end if;
   end Overwrite;

   ------------------------
   -- String_8.Overwrite --
   ------------------------

   procedure Overwrite
     (Source   : in out String_8;
      Position : Positive;
      New_Item : String)
   is
      Length : constant Natural := Source.Length;
      NI_Length : constant Natural := New_Item'Length;
      New_Length : Natural;
   begin
      if Position not in 1 .. Length + 1 then
         raise Index_Error;
      end if;

      if Position = 1 and Length <= NI_Length then
         Source.Set_String_8 (New_Item);
         return;
      end if;

      if NI_Length <= 0 then
         return;
      end if;

      if Position + NI_Length - 1 <= Length then
         -- Length remains the same
         New_Length := Length;
         if Source.Ptr.Counter.rc > 1 then
            -- can't reuse allocated buffer
            declare
               Source_Copy : String_8 := Source;
            begin
               Source.Make_Buffer (New_Length);
               declare
                  Old_Data : String_8_Data renames Source_Copy.Ptr.Data;
                  New_Data : String_8_Data renames Source.Ptr.Data;
               begin
                  New_Data.chr (1 .. Position - 1) :=
                    Old_Data.chr (1 .. Position - 1);
                  New_Data.chr (Position .. Position + NI_Length - 1) :=
                    New_Item;
                  New_Data.chr (Position + NI_Length .. New_Length) :=
                    Old_Data.chr (Position + NI_Length .. Length);
               end;
            end;
         else -- can reuse buffer
            declare
               Source_Data : String_8_Data renames Source.Ptr.Data;
            begin
               Source_Data.chr (Position .. Position + NI_Length - 1) :=
                 New_Item;
            end;
         end if;
      else -- Length is increased
         New_Length := Position + NI_Length - 1;
         if Source.Ptr.Counter.rc > 1 or else
           not Good_Capacity (Source.Ptr.Capacity, New_Length) then
            -- can't reuse allocated buffer
            declare
               Source_Copy : String_8 := Source;
            begin
               Source.Make_Buffer (New_Length);
               declare
                  Old_Data : String_8_Data renames Source_Copy.Ptr.Data;
                  New_Data : String_8_Data renames Source.Ptr.Data;
               begin
                  New_Data.chr (1 .. Position - 1) :=
                    Old_Data.chr (1 .. Position - 1);
                  New_Data.chr (Position .. New_Length) :=
                    New_Item;
               end;
            end;
         else -- can reuse buffer
            declare
               Source_Data : String_8_Data renames Source.Ptr.Data;
            begin
               Source_Data.Length := New_Length;
               Source_Data.chr (Position .. New_Length) :=
                 New_Item;
            end;
         end if;
      end if;
   end Overwrite;

   ---------------------
   -- String_8.Delete --
   ---------------------

   function Delete
     (Source  : String_8;
      From    : Positive;
      Through : Natural) return String_8
   is
      Length : constant Natural := Source.Length;
      New_Length : Natural;
   begin
      if From > Through then
         return Source;
      end if;

      if From > Length + 1 or Through < 0 then
         raise Index_Error;
      end if;

      if From = 1 and Through = Length then
         return Null_String_8;
      end if;

      New_Length := Length + From - Through - 1;

      return Result : String_8 do
         Result.Make_Buffer (New_Length);
         declare
            Source_Data : String_8_Data renames Source.Ptr.Data;
            Result_Data : String_8_Data renames Result.Ptr.Data;
         begin
            Result_Data.chr (1 .. From - 1) :=
              Source_Data.chr (1 .. From - 1);
            Result_Data.chr (From .. New_Length) :=
              Source_Data.chr (Through + 1 .. Length);
         end;
      end return;
   end Delete;

   ---------------------
   -- String_8.Delete --
   ---------------------

   procedure Delete
     (Source  : in out String_8;
      From    : Positive;
      Through : Natural)
   is
      Length : constant Natural := Source.Length;
      New_Length : Natural;
   begin
      if From > Through then
         return;
      end if;

      if From > Length + 1 or Through < 0 then
         raise Index_Error;
      end if;

      if From = 1 and Through = Length then
         Source.Set_Empty;
         return;
      end if;

      New_Length := Length + From - Through - 1;

      if Source.Ptr.Counter.rc > 1 or else
        not Good_Capacity (Source.Ptr.Capacity, New_Length) then
         -- can't reuse allocated buffer
         declare
            Source_Copy : String_8 := Source;
         begin
            Source.Make_Buffer (New_Length);
            declare
               Old_Data : String_8_Data renames Source_Copy.Ptr.Data;
               New_Data : String_8_Data renames Source.Ptr.Data;
            begin
               New_Data.chr (1 .. From - 1) :=
                 Old_Data.chr (1 .. From - 1);
               New_Data.chr (From .. New_Length) :=
                 Old_Data.chr (Through + 1 .. Length);
            end;
         end;
      else -- can reuse buffer
         declare
            Source_Data : String_8_Data renames Source.Ptr.Data;
         begin
            Source_Data.Length := New_Length;
            Source_Data.chr (From .. New_Length) :=
              Source_Data.chr (Through + 1 .. Length);
         end;
      end if;
   end Delete;

   --------------------------
   -- String_8.Trim (Side) --
   --------------------------

   function Trim
     (Source : String_8;
      Side   : Trim_End) return String_8 is
   begin
      return +Ada.Strings.Fixed.Trim (+Source, Side);
   end Trim;

   --------------------------
   -- String_8.Trim (Side) --
   --------------------------

   procedure Trim
     (Source : in out String_8;
      Side   : Trim_End) is
   begin
      Source.Set_String_8 (Ada.Strings.Fixed.Trim (+Source, Side));
   end Trim;

   ---------------------------------
   -- String_8.Trim (Left, Right) --
   ---------------------------------

   function Trim
     (Source : String_8;
      Left   : Maps.Character_Set;
      Right  : Maps.Character_Set) return String_8 is
   begin
      return +Ada.Strings.Fixed.Trim (+Source, Left, Right);
   end Trim;

   ---------------------------------
   -- String_8.Trim (Left, Right) --
   ---------------------------------

   procedure Trim
     (Source : in out String_8;
      Left   : Maps.Character_Set;
      Right  : Maps.Character_Set) is
   begin
      Source.Set_String_8 (Ada.Strings.Fixed.Trim (+Source, Left, Right));
   end Trim;

   -------------------
   -- String_8.Head --
   -------------------

   function Head
     (Source : String_8;
      Count  : Natural;
      Pad    : Character := Space) return String_8 is
   begin
      return +Ada.Strings.Fixed.Head (+Source, Count, Pad);
   end Head;

   -------------------
   -- String_8.Head --
   -------------------

   procedure Head
     (Source : in out String_8;
      Count  : Natural;
      Pad    : Character := Space) is
   begin
      Source.Set_String_8 (Ada.Strings.Fixed.Head (+Source, Count, Pad));
   end Head;

   -------------------
   -- String_8.Tail --
   -------------------

   function Tail
     (Source : String_8;
      Count  : Natural;
      Pad    : Character := Space) return String_8 is
   begin
      return +Ada.Strings.Fixed.Tail (+Source, Count, Pad);
   end Tail;

   -------------------
   -- String_8.Tail --
   -------------------

   procedure Tail
     (Source : in out String_8;
      Count  : Natural;
      Pad    : Character := Space) is
   begin
      Source.Set_String_8 (Ada.Strings.Fixed.Tail (+Source, Count, Pad));
   end Tail;

end Jwa.Strings;
