| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- I N T E R F A C E S . C . S T R I N G S -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. -- |
| -- -- |
| -- As a special exception under Section 7 of GPL version 3, you are granted -- |
| -- additional permissions described in the GCC Runtime Library Exception, -- |
| -- version 3.1, as published by the Free Software Foundation. -- |
| -- -- |
| -- You should have received a copy of the GNU General Public License and -- |
| -- a copy of the GCC Runtime Library Exception along with this program; -- |
| -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- |
| -- <http://www.gnu.org/licenses/>. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with System; use System; |
| with System.Storage_Elements; use System.Storage_Elements; |
| |
| with Ada.Unchecked_Conversion; |
| |
| package body Interfaces.C.Strings is |
| |
| -- Note that the type chars_ptr has a pragma No_Strict_Aliasing in the |
| -- spec, to prevent any assumptions about aliasing for values of this type, |
| -- since arbitrary addresses can be converted, and it is quite likely that |
| -- this type will in fact be used for aliasing values of other types. |
| |
| function To_chars_ptr is |
| new Ada.Unchecked_Conversion (System.Parameters.C_Address, chars_ptr); |
| |
| function To_Address is |
| new Ada.Unchecked_Conversion (chars_ptr, System.Parameters.C_Address); |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function Peek (From : chars_ptr) return char; |
| pragma Inline (Peek); |
| -- Given a chars_ptr value, obtain referenced character |
| |
| procedure Poke (Value : char; Into : chars_ptr); |
| pragma Inline (Poke); |
| -- Given a chars_ptr, modify referenced Character value |
| |
| function "+" (Left : chars_ptr; Right : size_t) return chars_ptr; |
| pragma Inline ("+"); |
| -- Address arithmetic on chars_ptr value |
| |
| function Position_Of_Nul (Into : char_array) return size_t; |
| -- Returns position of the first Nul in Into or Into'Last + 1 if none |
| |
| -- We can't use directly System.Memory because the categorization is not |
| -- compatible, so we directly import here the malloc and free routines. |
| |
| function Memory_Alloc (Size : size_t) return chars_ptr; |
| pragma Import (C, Memory_Alloc, System.Parameters.C_Malloc_Linkname); |
| |
| procedure Memory_Free (Address : chars_ptr); |
| pragma Import (C, Memory_Free, "__gnat_free"); |
| |
| --------- |
| -- "+" -- |
| --------- |
| |
| function "+" (Left : chars_ptr; Right : size_t) return chars_ptr is |
| begin |
| return To_chars_ptr (To_Address (Left) + Storage_Offset (Right)); |
| end "+"; |
| |
| ---------- |
| -- Free -- |
| ---------- |
| |
| procedure Free (Item : in out chars_ptr) is |
| begin |
| if Item = Null_Ptr then |
| return; |
| end if; |
| |
| Memory_Free (Item); |
| Item := Null_Ptr; |
| end Free; |
| |
| -------------------- |
| -- New_Char_Array -- |
| -------------------- |
| |
| function New_Char_Array (Chars : char_array) return chars_ptr is |
| Index : size_t; |
| Pointer : chars_ptr; |
| |
| begin |
| -- Get index of position of null. If Index > Chars'Last, |
| -- nul is absent and must be added explicitly. |
| |
| Index := Position_Of_Nul (Into => Chars); |
| Pointer := Memory_Alloc ((Index - Chars'First + 1)); |
| |
| -- If nul is present, transfer string up to and including nul |
| |
| if Index <= Chars'Last then |
| Update (Item => Pointer, |
| Offset => 0, |
| Chars => Chars (Chars'First .. Index), |
| Check => False); |
| else |
| -- If original string has no nul, transfer whole string and add |
| -- terminator explicitly. |
| |
| Update (Item => Pointer, |
| Offset => 0, |
| Chars => Chars, |
| Check => False); |
| Poke (nul, Into => Pointer + size_t'(Chars'Length)); |
| end if; |
| |
| return Pointer; |
| end New_Char_Array; |
| |
| ---------------- |
| -- New_String -- |
| ---------------- |
| |
| function New_String (Str : String) return chars_ptr is |
| |
| -- It's important that this subprogram uses the heap directly to compute |
| -- the result, and doesn't copy the string on the stack, otherwise its |
| -- use is limited when used from tasks on large strings. |
| |
| Result : constant chars_ptr := Memory_Alloc (Str'Length + 1); |
| |
| Result_Array : char_array (1 .. Str'Length + 1); |
| for Result_Array'Address use To_Address (Result); |
| pragma Import (Ada, Result_Array); |
| |
| Count : size_t; |
| |
| begin |
| To_C |
| (Item => Str, |
| Target => Result_Array, |
| Count => Count, |
| Append_Nul => True); |
| return Result; |
| end New_String; |
| |
| ---------- |
| -- Peek -- |
| ---------- |
| |
| function Peek (From : chars_ptr) return char is |
| begin |
| return char (From.all); |
| end Peek; |
| |
| ---------- |
| -- Poke -- |
| ---------- |
| |
| procedure Poke (Value : char; Into : chars_ptr) is |
| begin |
| Into.all := Character (Value); |
| end Poke; |
| |
| --------------------- |
| -- Position_Of_Nul -- |
| --------------------- |
| |
| function Position_Of_Nul (Into : char_array) return size_t is |
| begin |
| for J in Into'Range loop |
| if Into (J) = nul then |
| return J; |
| end if; |
| end loop; |
| |
| return Into'Last + 1; |
| end Position_Of_Nul; |
| |
| ------------ |
| -- Strlen -- |
| ------------ |
| |
| function Strlen (Item : chars_ptr) return size_t is |
| Item_Index : size_t := 0; |
| |
| begin |
| if Item = Null_Ptr then |
| raise Dereference_Error; |
| end if; |
| |
| loop |
| if Peek (Item + Item_Index) = nul then |
| return Item_Index; |
| end if; |
| |
| Item_Index := Item_Index + 1; |
| end loop; |
| end Strlen; |
| |
| ------------------ |
| -- To_Chars_Ptr -- |
| ------------------ |
| |
| function To_Chars_Ptr |
| (Item : char_array_access; |
| Nul_Check : Boolean := False) return chars_ptr |
| is |
| begin |
| if Item = null then |
| return Null_Ptr; |
| elsif Nul_Check |
| and then Position_Of_Nul (Into => Item.all) > Item'Last |
| then |
| raise Terminator_Error; |
| else |
| return To_chars_ptr (Item (Item'First)'Address); |
| end if; |
| end To_Chars_Ptr; |
| |
| ------------ |
| -- Update -- |
| ------------ |
| |
| procedure Update |
| (Item : chars_ptr; |
| Offset : size_t; |
| Chars : char_array; |
| Check : Boolean := True) |
| is |
| Index : chars_ptr := Item + Offset; |
| |
| begin |
| if Check and then Offset + Chars'Length > Strlen (Item) then |
| raise Update_Error; |
| end if; |
| |
| for J in Chars'Range loop |
| Poke (Chars (J), Into => Index); |
| Index := Index + size_t'(1); |
| end loop; |
| end Update; |
| |
| procedure Update |
| (Item : chars_ptr; |
| Offset : size_t; |
| Str : String; |
| Check : Boolean := True) |
| is |
| begin |
| -- Note: in RM 95, the Append_Nul => False parameter is omitted. But |
| -- this has the unintended consequence of truncating the string after |
| -- an update. As discussed in Ada 2005 AI-242, this was unintended, |
| -- and should be corrected. Since this is a clear error, it seems |
| -- appropriate to apply the correction in Ada 95 mode as well. |
| |
| Update (Item, Offset, To_C (Str, Append_Nul => False), Check); |
| end Update; |
| |
| ----------- |
| -- Value -- |
| ----------- |
| |
| function Value (Item : chars_ptr) return char_array is |
| Result : char_array (0 .. Strlen (Item)); |
| |
| begin |
| if Item = Null_Ptr then |
| raise Dereference_Error; |
| end if; |
| |
| -- Note that the following loop will also copy the terminating Nul |
| |
| for J in Result'Range loop |
| Result (J) := Peek (Item + J); |
| end loop; |
| |
| return Result; |
| end Value; |
| |
| function Value |
| (Item : chars_ptr; |
| Length : size_t) return char_array |
| is |
| begin |
| if Item = Null_Ptr then |
| raise Dereference_Error; |
| end if; |
| |
| -- ACATS cxb3010 checks that Constraint_Error gets raised when Length |
| -- is 0. Seems better to check that Length is not null before declaring |
| -- an array with size_t bounds of 0 .. Length - 1 anyway. |
| |
| if Length = 0 then |
| raise Constraint_Error; |
| end if; |
| |
| declare |
| Result : char_array (0 .. Length - 1); |
| |
| begin |
| for J in Result'Range loop |
| Result (J) := Peek (Item + J); |
| |
| if Result (J) = nul then |
| return Result (0 .. J); |
| end if; |
| end loop; |
| |
| return Result; |
| end; |
| end Value; |
| |
| function Value (Item : chars_ptr) return String is |
| begin |
| return To_Ada (Value (Item)); |
| end Value; |
| |
| function Value (Item : chars_ptr; Length : size_t) return String is |
| Result : char_array (0 .. Length); |
| |
| begin |
| -- As per AI-00177, this is equivalent to: |
| |
| -- To_Ada (Value (Item, Length) & nul); |
| |
| if Item = Null_Ptr then |
| raise Dereference_Error; |
| end if; |
| |
| for J in 0 .. Length - 1 loop |
| Result (J) := Peek (Item + J); |
| |
| if Result (J) = nul then |
| return To_Ada (Result (0 .. J)); |
| end if; |
| end loop; |
| |
| Result (Length) := nul; |
| return To_Ada (Result); |
| end Value; |
| |
| end Interfaces.C.Strings; |