hcl/lib2/h3-strings.adb
2021-09-30 23:54:50 +00:00

236 lines
6.9 KiB
Ada

with Ada.Unchecked_Deallocation;
package body H3.Strings is
BUFFER_ALIGN: constant := 16;
function To_Character_Array (Str: in Elastic_String) return Character_Array is
begin
return Str.Buffer.Slot(Str.Buffer.Slot'First .. Str.Buffer.Last);
end To_Character_Array;
-- return the buffer capacity excluding the terminator
function Get_Capacity (Str: in Elastic_String) return System_Size is
begin
return Str.Buffer.Slot'Length - 1;
end Get_Capacity;
-- private. return the buffer capacity including the terminator
function Get_Hard_Capacity (Str: in Elastic_String) return System_Size is
begin
return Str.Buffer.Slot'Length;
end Get_Hard_Capacity;
pragma Inline (Get_Hard_Capacity);
function Get_Length (Str: in Elastic_String) return System_Size is
begin
return 1 + Str.Buffer.Last - Str.Buffer.Slot'First;
end Get_Length;
function Get_First_Index (Str: in Elastic_String) return System_Size is
begin
return Str.Buffer.Slot'First;
end Get_First_Index;
function Get_Last_Index (Str: in Elastic_String) return System_Size is
begin
return Str.Buffer.Last;
end Get_Last_Index;
function Get_Item (Str: in Elastic_String; Pos: in System_Index) return Character_Type is
begin
return Str.Buffer.Slot(Pos);
end Get_Item;
-- unsafe as it exposes the internal buffer which can go away.
-- assume the system address is equal to the thin pointer in size.
function Get_Slot_Pointer (Str: in Elastic_String) return Thin_Character_Array_Pointer is
A: System.Address := Str.Buffer.Slot(Str.Buffer.Slot'First)'Address;
P: Thin_Character_Array_Pointer;
for P'Address use A'Address;
pragma Import (Ada, P);
begin
return P;
end Get_Slot_Pointer;
function Is_Shared(Str: in Elastic_String) return Standard.Boolean is
begin
return Str.Buffer /= Empty_Buffer'Access and then Str.Buffer.Refs > 1;
end Is_Shared;
procedure Free_Buffer (Str: in out Elastic_String) is
begin
if Str.Buffer /= Empty_Buffer'Access then
declare
procedure Free is new Ada.Unchecked_Deallocation(Buffer_Record, Buffer_Pointer);
begin
Free (Str.Buffer);
end;
end if;
end Free_Buffer;
procedure Ref_Buffer (Buf: in out Buffer_Pointer) is
begin
if Buf /= Empty_Buffer'Access then
Buf.Refs := Buf.Refs + 1;
end if;
end Ref_Buffer;
procedure Unref_Buffer (Buf: in out Buffer_Pointer) is
begin
if Buf /= Empty_Buffer'Access then
if Buf.Refs = 1 then
declare
procedure Free is new Ada.Unchecked_Deallocation(Buffer_Record, Buffer_Pointer);
begin
Free (Buf);
end;
Buf := Empty_Buffer'Access;
else
Buf.Refs := Buf.Refs - 1;
end if;
end if;
end Unref_Buffer;
function New_Buffer_Container (Hard_Capa: in System_Size) return Elastic_String is
Tmp: Elastic_String;
begin
Tmp.Buffer := new Buffer_Record(Hard_Capa);
Tmp.Buffer.Refs := 1;
return Tmp;
end New_Buffer_Container;
-- prepare the buffer for writing
procedure Prepare_Buffer (Str: in out Elastic_String) is
Tmp: Elastic_String;
begin
if Str.Buffer /= Empty_Buffer'Access then
if Is_Shared(Str) then
-- The code like this doesn't work correctly in terms of finalization.
-- The buffer pointer held inside a finalization controlled record must be
-- manipluated through the record itself. otherwise, the Adjust and Finalize
-- calls goes incompatible with the reference counting implementation.
-- It is because finalization is set on the record rather than the buffer pointer.
--Tmp: Buffer_Pointer;
--Tmp := new Buffer_Record(Get_Hard_Capacity(Str));
--Tmp.Slot := Str.Buffer.Slot;
--Tmp.Last := Str.Buffer.Last;
--Tmp.Refs := 1;
--Unref_Buffer (Str.Buffer);
--Str.Buffer := Tmp;
Tmp := Str;
Str := New_Buffer_Container(Get_Hard_Capacity(Str));
Str.Buffer.Slot := Tmp.Buffer.Slot;
Str.Buffer.Last := Tmp.Buffer.Last;
end if;
end if;
end Prepare_Buffer;
-- prepare the buffer for writing
procedure Prepare_Buffer (Str: in out Elastic_String; Req_Hard_Capa: in System_Size) is
Tmp: Elastic_String;
First, Last: System_Size;
Hard_Capa: System_Size;
begin
First := Get_First_Index(Str);
Last := Get_Last_Index(Str);
if Str.Buffer /= Empty_Buffer'Access and then Is_Shared(Str) then
if Req_Hard_Capa < Get_Hard_Capacity(Str) then
Hard_Capa := Get_Hard_Capacity(Str);
else
Hard_Capa := Req_Hard_Capa;
end if;
Tmp := New_Buffer_Container(Hard_Capa);
Tmp.Buffer.Slot(First .. Last + 1) := Str.Buffer.Slot(First .. Last + 1);
Tmp.Buffer.Last := Last;
Str := Tmp;
else
if Req_Hard_Capa > Get_Hard_Capacity(Str) then
Tmp := Str;
Str := New_Buffer_Container(Req_Hard_Capa);
Str.Buffer.Slot(First .. Last + 1) := Tmp.Buffer.Slot(First .. Last + 1);
Str.Buffer.Last := Last;
end if;
end if;
end Prepare_Buffer;
procedure Clear (Str: in out Elastic_String) is
begin
Prepare_Buffer (Str);
Str.Buffer.Last := Get_First_Index(Str) - 1;
end Clear;
procedure Purge (Str: in out Elastic_String) is
begin
Unref_Buffer (Str.Buffer);
Str.Buffer := Empty_Buffer'Access;
end Purge;
-- TODO: operator "&"
procedure Append (Str: in out Elastic_String; V: in Character_Array) is
begin
if V'Length > 0 then
Prepare_Buffer (Str, H3.Align(Get_Length(Str) + V'Length + 1, BUFFER_ALIGN));
Str.Buffer.Slot(Str.Buffer.Last + 1 .. Str.Buffer.Last + V'Length) := V;
Str.Buffer.Last := Str.Buffer.Last + V'Length;
Str.Buffer.Slot(Str.Buffer.Last + 1) := Null_Character;
end if;
end Append;
procedure Append (Str: in out Elastic_String; V: in Character_Type) is
Tmp: Character_Array(1 .. 1) := (1 => V);
begin
Append (Str, Tmp);
end Append;
procedure Delete (Str: in out Elastic_String; Pos: in System_Index; Length: in System_Size) is
begin
if Pos <= Str.Buffer.Last then
Prepare_Buffer (Str);
else
raise Standard.Constraint_Error;
end if;
end Delete;
procedure Insert (Str: in out Elastic_String; Pos: in System_Index; Char: in Character_Type) is
begin
if Pos <= Str.Buffer.Last then
Prepare_Buffer (Str, H3.Align(Get_Length(Str) + V'Length + 1, BUFFER_ALIGN));
Str.Buffer.Slot(Pos) := New_Char;
end if;
end Insert;
procedure Replace (Str: in out Elastic_String; Pos: in System_Index; New_Char: in Character_Type) is
begin
if Pos <= Str.Buffer.Last then
Prepare_Buffer (Str);
Str.Buffer.Slot(Pos) := New_Char;
else
-- raise Index_Error;
raise Standard.Constraint_Error;
end if;
end Replace;
-- ---------------------------------------------------------------------
-- Controlled Management
-- ---------------------------------------------------------------------
procedure Initialize (Str: in out Elastic_String) is
begin
-- the string is initialized to the empty buffer all the time.
-- there is no need to reference the buffer.
null;
end Initialize;
procedure Adjust (Str: in out Elastic_String) is
begin
Ref_Buffer (Str.Buffer);
end Adjust;
procedure Finalize (Str: in out Elastic_String) is
begin
Unref_Buffer (Str.Buffer);
end Finalize;
end H3.Strings;