| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T . P E R F E C T _ H A S H _ G E N E R A T O R S -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2002-2022, AdaCore -- |
| -- -- |
| -- 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 Ada.IO_Exceptions; use Ada.IO_Exceptions; |
| with Ada.Characters.Handling; use Ada.Characters.Handling; |
| |
| with GNAT.OS_Lib; use GNAT.OS_Lib; |
| |
| package body GNAT.Perfect_Hash_Generators is |
| |
| use SPHG; |
| |
| function Image (Int : Integer; W : Natural := 0) return String; |
| function Image (Str : String; W : Natural := 0) return String; |
| -- Return a string which includes string Str or integer Int preceded by |
| -- leading spaces if required by width W. |
| |
| EOL : constant Character := ASCII.LF; |
| |
| Max : constant := 78; |
| Last : Natural := 0; |
| Line : String (1 .. Max); |
| -- Use this line to provide buffered IO |
| |
| NK : Natural := 0; |
| -- NK : Number of Keys |
| |
| Opt : Optimization; |
| -- Optimization mode (memory vs CPU) |
| |
| procedure Add (C : Character); |
| procedure Add (S : String); |
| -- Add a character or a string in Line and update Last |
| |
| procedure Put |
| (F : File_Descriptor; |
| S : String; |
| F1 : Natural; |
| L1 : Natural; |
| C1 : Natural; |
| F2 : Natural; |
| L2 : Natural; |
| C2 : Natural); |
| -- Write string S into file F as a element of an array of one or two |
| -- dimensions. Fk (resp. Lk and Ck) indicates the first (resp last and |
| -- current) index in the k-th dimension. If F1 = L1 the array is considered |
| -- as a one dimension array. This dimension is described by F2 and L2. This |
| -- routine takes care of all the parenthesis, spaces and commas needed to |
| -- format correctly the array. Moreover, the array is well indented and is |
| -- wrapped to fit in a 80 col line. When the line is full, the routine |
| -- writes it into file F. When the array is completed, the routine adds |
| -- semi-colon and writes the line into file F. |
| |
| procedure New_Line (File : File_Descriptor); |
| -- Simulate Ada.Text_IO.New_Line with GNAT.OS_Lib |
| |
| procedure Put (File : File_Descriptor; Str : String); |
| -- Simulate Ada.Text_IO.Put with GNAT.OS_Lib |
| |
| procedure Put_Int_Matrix |
| (File : File_Descriptor; |
| Title : String; |
| Table : Table_Name; |
| Len_1 : Natural; |
| Len_2 : Natural); |
| -- Output a title and a matrix. When the matrix has only one non-empty |
| -- dimension (Len_2 = 0), output a vector. |
| |
| function Ada_File_Base_Name (Pkg_Name : String) return String; |
| -- Return the base file name (i.e. without .ads/.adb extension) for an |
| -- Ada source file containing the named package, using the standard GNAT |
| -- file-naming convention. For example, if Pkg_Name is "Parent.Child", we |
| -- return "parent-child". |
| |
| ------------------------ |
| -- Ada_File_Base_Name -- |
| ------------------------ |
| |
| function Ada_File_Base_Name (Pkg_Name : String) return String is |
| begin |
| -- Convert to lower case, then replace '.' with '-' |
| |
| return Result : String := To_Lower (Pkg_Name) do |
| for J in Result'Range loop |
| if Result (J) = '.' then |
| Result (J) := '-'; |
| end if; |
| end loop; |
| end return; |
| end Ada_File_Base_Name; |
| |
| --------- |
| -- Add -- |
| --------- |
| |
| procedure Add (C : Character) is |
| pragma Assert (C /= ASCII.NUL); |
| begin |
| Line (Last + 1) := C; |
| Last := Last + 1; |
| end Add; |
| |
| --------- |
| -- Add -- |
| --------- |
| |
| procedure Add (S : String) is |
| Len : constant Natural := S'Length; |
| begin |
| for J in S'Range loop |
| pragma Assert (S (J) /= ASCII.NUL); |
| null; |
| end loop; |
| |
| Line (Last + 1 .. Last + Len) := S; |
| Last := Last + Len; |
| end Add; |
| |
| ------------- |
| -- Compute -- |
| ------------- |
| |
| procedure Compute (Position : String := Default_Position) is |
| begin |
| SPHG.Compute (Position); |
| end Compute; |
| |
| -------------- |
| -- Finalize -- |
| -------------- |
| |
| procedure Finalize is |
| begin |
| NK := 0; |
| SPHG.Finalize; |
| end Finalize; |
| |
| ----------- |
| -- Image -- |
| ----------- |
| |
| function Image (Int : Integer; W : Natural := 0) return String is |
| B : String (1 .. 32); |
| L : Natural := 0; |
| |
| procedure Img (V : Natural); |
| -- Compute image of V into B, starting at B (L), incrementing L |
| |
| --------- |
| -- Img -- |
| --------- |
| |
| procedure Img (V : Natural) is |
| begin |
| if V > 9 then |
| Img (V / 10); |
| end if; |
| |
| L := L + 1; |
| B (L) := Character'Val ((V mod 10) + Character'Pos ('0')); |
| end Img; |
| |
| -- Start of processing for Image |
| |
| begin |
| if Int < 0 then |
| L := L + 1; |
| B (L) := '-'; |
| Img (-Int); |
| else |
| Img (Int); |
| end if; |
| |
| return Image (B (1 .. L), W); |
| end Image; |
| |
| ----------- |
| -- Image -- |
| ----------- |
| |
| function Image (Str : String; W : Natural := 0) return String is |
| Len : constant Natural := Str'Length; |
| Max : Natural := Len; |
| |
| begin |
| if Max < W then |
| Max := W; |
| end if; |
| |
| declare |
| Buf : String (1 .. Max) := (1 .. Max => ' '); |
| |
| begin |
| for J in 0 .. Len - 1 loop |
| Buf (Max - Len + 1 + J) := Str (Str'First + J); |
| end loop; |
| |
| return Buf; |
| end; |
| end Image; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize |
| (Seed : Natural; |
| K_To_V : Float := Default_K_To_V; |
| Optim : Optimization := Memory_Space; |
| Tries : Positive := Default_Tries) |
| is |
| V : constant Positive := Positive (Float (NK) * K_To_V); |
| |
| begin |
| Opt := Optim; |
| SPHG.Initialize (Seed, V, SPHG.Optimization (Optim), Tries); |
| end Initialize; |
| |
| ------------ |
| -- Insert -- |
| ------------ |
| |
| procedure Insert (Value : String) is |
| begin |
| NK := NK + 1; |
| SPHG.Insert (Value); |
| end Insert; |
| |
| -------------- |
| -- New_Line -- |
| -------------- |
| |
| procedure New_Line (File : File_Descriptor) is |
| begin |
| if Write (File, EOL'Address, 1) /= 1 then |
| raise Program_Error; |
| end if; |
| end New_Line; |
| |
| ------------- |
| -- Produce -- |
| ------------- |
| |
| procedure Produce |
| (Pkg_Name : String := Default_Pkg_Name; |
| Use_Stdout : Boolean := False) |
| is |
| File : File_Descriptor := Standout; |
| |
| Siz, L1, L2 : Natural; |
| -- For calls to Define |
| |
| Status : Boolean; |
| -- For call to Close |
| |
| function Array_Img (N, T, R1 : String; R2 : String := "") return String; |
| -- Return string "N : constant array (R1[, R2]) of T;" |
| |
| function Range_Img (F, L : Natural; T : String := "") return String; |
| -- Return string "[T range ]F .. L" |
| |
| function Type_Img (Siz : Positive) return String; |
| -- Return the name of the unsigned type of size S |
| |
| --------------- |
| -- Array_Img -- |
| --------------- |
| |
| function Array_Img |
| (N, T, R1 : String; |
| R2 : String := "") return String |
| is |
| begin |
| Last := 0; |
| Add (" "); |
| Add (N); |
| Add (" : constant array ("); |
| Add (R1); |
| |
| if R2 /= "" then |
| Add (", "); |
| Add (R2); |
| end if; |
| |
| Add (") of "); |
| Add (T); |
| Add (" :="); |
| return Line (1 .. Last); |
| end Array_Img; |
| |
| --------------- |
| -- Range_Img -- |
| --------------- |
| |
| function Range_Img (F, L : Natural; T : String := "") return String is |
| FI : constant String := Image (F); |
| FL : constant Natural := FI'Length; |
| LI : constant String := Image (L); |
| LL : constant Natural := LI'Length; |
| TL : constant Natural := T'Length; |
| RI : String (1 .. TL + 7 + FL + 4 + LL); |
| Len : Natural := 0; |
| |
| begin |
| if TL /= 0 then |
| RI (Len + 1 .. Len + TL) := T; |
| Len := Len + TL; |
| RI (Len + 1 .. Len + 7) := " range "; |
| Len := Len + 7; |
| end if; |
| |
| RI (Len + 1 .. Len + FL) := FI; |
| Len := Len + FL; |
| RI (Len + 1 .. Len + 4) := " .. "; |
| Len := Len + 4; |
| RI (Len + 1 .. Len + LL) := LI; |
| Len := Len + LL; |
| return RI (1 .. Len); |
| end Range_Img; |
| |
| -------------- |
| -- Type_Img -- |
| -------------- |
| |
| function Type_Img (Siz : Positive) return String is |
| S : constant String := Image (Siz); |
| U : String := "Unsigned_ "; |
| N : Natural := 9; |
| |
| begin |
| for J in S'Range loop |
| N := N + 1; |
| U (N) := S (J); |
| end loop; |
| |
| return U (1 .. N); |
| end Type_Img; |
| |
| P : Natural; |
| |
| FName : String := Ada_File_Base_Name (Pkg_Name) & ".ads"; |
| -- Initially, the name of the spec file, then modified to be the name of |
| -- the body file. Not used if Use_Stdout is True. |
| |
| -- Start of processing for Produce |
| |
| begin |
| if not Use_Stdout then |
| File := Create_File (FName, Binary); |
| |
| if File = Invalid_FD then |
| raise Program_Error with "cannot create: " & FName; |
| end if; |
| end if; |
| |
| Put (File, "package "); |
| Put (File, Pkg_Name); |
| Put (File, " is"); |
| New_Line (File); |
| Put (File, " function Hash (S : String) return Natural;"); |
| New_Line (File); |
| Put (File, "end "); |
| Put (File, Pkg_Name); |
| Put (File, ";"); |
| New_Line (File); |
| |
| if not Use_Stdout then |
| Close (File, Status); |
| |
| if not Status then |
| raise Device_Error; |
| end if; |
| end if; |
| |
| if not Use_Stdout then |
| |
| -- Set to body file name |
| |
| FName (FName'Last) := 'b'; |
| |
| File := Create_File (FName, Binary); |
| |
| if File = Invalid_FD then |
| raise Program_Error with "cannot create: " & FName; |
| end if; |
| end if; |
| |
| Put (File, "with Interfaces; use Interfaces;"); |
| New_Line (File); |
| New_Line (File); |
| Put (File, "package body "); |
| Put (File, Pkg_Name); |
| Put (File, " is"); |
| New_Line (File); |
| New_Line (File); |
| |
| if Opt = CPU_Time then |
| -- The format of this table is fixed |
| |
| Define (Used_Character_Set, Siz, L1, L2); |
| pragma Assert (L1 = 256 and then L2 = 0); |
| |
| Put (File, Array_Img ("C", Type_Img (Siz), "Character")); |
| New_Line (File); |
| |
| for J in 0 .. 255 loop |
| P := Value (Used_Character_Set, J); |
| Put (File, Image (P), 1, 0, 1, 0, 255, J); |
| end loop; |
| |
| New_Line (File); |
| end if; |
| |
| Define (Character_Position, Siz, L1, L2); |
| pragma Assert (Siz = 31 and then L2 = 0); |
| |
| Put (File, Array_Img ("P", "Natural", Range_Img (0, L1 - 1))); |
| New_Line (File); |
| |
| for J in 0 .. L1 - 1 loop |
| P := Value (Character_Position, J); |
| Put (File, Image (P), 1, 0, 1, 0, L1 - 1, J); |
| end loop; |
| |
| New_Line (File); |
| |
| Define (Function_Table_1, Siz, L1, L2); |
| |
| case Opt is |
| when CPU_Time => |
| Put_Int_Matrix |
| (File, |
| Array_Img ("T1", Type_Img (Siz), |
| Range_Img (0, L1 - 1), |
| Range_Img (0, L2 - 1, Type_Img (8))), |
| Function_Table_1, L1, L2); |
| |
| when Memory_Space => |
| Put_Int_Matrix |
| (File, |
| Array_Img ("T1", Type_Img (Siz), |
| Range_Img (0, L1 - 1)), |
| Function_Table_1, L1, 0); |
| end case; |
| |
| New_Line (File); |
| |
| Define (Function_Table_2, Siz, L1, L2); |
| |
| case Opt is |
| when CPU_Time => |
| Put_Int_Matrix |
| (File, |
| Array_Img ("T2", Type_Img (Siz), |
| Range_Img (0, L1 - 1), |
| Range_Img (0, L2 - 1, Type_Img (8))), |
| Function_Table_2, L1, L2); |
| |
| when Memory_Space => |
| Put_Int_Matrix |
| (File, |
| Array_Img ("T2", Type_Img (Siz), |
| Range_Img (0, L1 - 1)), |
| Function_Table_2, L1, 0); |
| end case; |
| |
| New_Line (File); |
| |
| Define (Graph_Table, Siz, L1, L2); |
| pragma Assert (L2 = 0); |
| |
| Put (File, Array_Img ("G", Type_Img (Siz), |
| Range_Img (0, L1 - 1))); |
| New_Line (File); |
| |
| for J in 0 .. L1 - 1 loop |
| P := Value (Graph_Table, J); |
| Put (File, Image (P), 1, 0, 1, 0, L1 - 1, J); |
| end loop; |
| |
| New_Line (File); |
| |
| Put (File, " function Hash (S : String) return Natural is"); |
| New_Line (File); |
| Put (File, " F : constant Natural := S'First - 1;"); |
| New_Line (File); |
| Put (File, " L : constant Natural := S'Length;"); |
| New_Line (File); |
| Put (File, " F1, F2 : Natural := 0;"); |
| New_Line (File); |
| |
| Put (File, " J : "); |
| |
| case Opt is |
| when CPU_Time => |
| Put (File, Type_Img (8)); |
| |
| when Memory_Space => |
| Put (File, "Natural"); |
| end case; |
| |
| Put (File, ";"); |
| New_Line (File); |
| |
| Put (File, " begin"); |
| New_Line (File); |
| Put (File, " for K in P'Range loop"); |
| New_Line (File); |
| Put (File, " exit when L < P (K);"); |
| New_Line (File); |
| Put (File, " J := "); |
| |
| case Opt is |
| when CPU_Time => |
| Put (File, "C"); |
| |
| when Memory_Space => |
| Put (File, "Character'Pos"); |
| end case; |
| |
| Put (File, " (S (P (K) + F));"); |
| New_Line (File); |
| |
| Put (File, " F1 := (F1 + Natural (T1 (K"); |
| |
| if Opt = CPU_Time then |
| Put (File, ", J"); |
| end if; |
| |
| Put (File, "))"); |
| |
| if Opt = Memory_Space then |
| Put (File, " * J"); |
| end if; |
| |
| Put (File, ") mod "); |
| Put (File, Image (L1)); |
| Put (File, ";"); |
| New_Line (File); |
| |
| Put (File, " F2 := (F2 + Natural (T2 (K"); |
| |
| if Opt = CPU_Time then |
| Put (File, ", J"); |
| end if; |
| |
| Put (File, "))"); |
| |
| if Opt = Memory_Space then |
| Put (File, " * J"); |
| end if; |
| |
| Put (File, ") mod "); |
| Put (File, Image (L1)); |
| Put (File, ";"); |
| New_Line (File); |
| |
| Put (File, " end loop;"); |
| New_Line (File); |
| |
| Put (File, |
| " return (Natural (G (F1)) + Natural (G (F2))) mod "); |
| |
| Put (File, Image (NK)); |
| Put (File, ";"); |
| New_Line (File); |
| Put (File, " end Hash;"); |
| New_Line (File); |
| New_Line (File); |
| Put (File, "end "); |
| Put (File, Pkg_Name); |
| Put (File, ";"); |
| New_Line (File); |
| |
| if not Use_Stdout then |
| Close (File, Status); |
| |
| if not Status then |
| raise Device_Error; |
| end if; |
| end if; |
| end Produce; |
| |
| --------- |
| -- Put -- |
| --------- |
| |
| procedure Put (File : File_Descriptor; Str : String) is |
| Len : constant Natural := Str'Length; |
| begin |
| for J in Str'Range loop |
| pragma Assert (Str (J) /= ASCII.NUL); |
| null; |
| end loop; |
| |
| if Write (File, Str'Address, Len) /= Len then |
| raise Program_Error; |
| end if; |
| end Put; |
| |
| --------- |
| -- Put -- |
| --------- |
| |
| procedure Put |
| (F : File_Descriptor; |
| S : String; |
| F1 : Natural; |
| L1 : Natural; |
| C1 : Natural; |
| F2 : Natural; |
| L2 : Natural; |
| C2 : Natural) |
| is |
| Len : constant Natural := S'Length; |
| |
| procedure Flush; |
| -- Write current line, followed by LF |
| |
| ----------- |
| -- Flush -- |
| ----------- |
| |
| procedure Flush is |
| begin |
| Put (F, Line (1 .. Last)); |
| New_Line (F); |
| Last := 0; |
| end Flush; |
| |
| -- Start of processing for Put |
| |
| begin |
| if C1 = F1 and then C2 = F2 then |
| Last := 0; |
| end if; |
| |
| if Last + Len + 3 >= Max then |
| Flush; |
| end if; |
| |
| if Last = 0 then |
| Add (" "); |
| |
| if F1 <= L1 then |
| if C1 = F1 and then C2 = F2 then |
| Add ('('); |
| |
| if F1 = L1 then |
| Add ("0 .. 0 => "); |
| end if; |
| |
| else |
| Add (' '); |
| end if; |
| end if; |
| end if; |
| |
| if C2 = F2 then |
| Add ('('); |
| |
| if F2 = L2 then |
| Add ("0 .. 0 => "); |
| end if; |
| |
| else |
| Add (' '); |
| end if; |
| |
| Add (S); |
| |
| if C2 = L2 then |
| Add (')'); |
| |
| if F1 > L1 then |
| Add (';'); |
| Flush; |
| |
| elsif C1 /= L1 then |
| Add (','); |
| Flush; |
| |
| else |
| Add (')'); |
| Add (';'); |
| Flush; |
| end if; |
| |
| else |
| Add (','); |
| end if; |
| end Put; |
| |
| -------------------- |
| -- Put_Int_Matrix -- |
| -------------------- |
| |
| procedure Put_Int_Matrix |
| (File : File_Descriptor; |
| Title : String; |
| Table : Table_Name; |
| Len_1 : Natural; |
| Len_2 : Natural) |
| is |
| F1 : constant Integer := 0; |
| L1 : constant Integer := Len_1 - 1; |
| F2 : constant Integer := 0; |
| L2 : constant Integer := Len_2 - 1; |
| Ix : Natural; |
| |
| begin |
| Put (File, Title); |
| New_Line (File); |
| |
| if Len_2 = 0 then |
| for J in F1 .. L1 loop |
| Ix := Value (Table, J, 0); |
| Put (File, Image (Ix), 1, 0, 1, F1, L1, J); |
| end loop; |
| |
| else |
| for J in F1 .. L1 loop |
| for K in F2 .. L2 loop |
| Ix := Value (Table, J, K); |
| Put (File, Image (Ix), F1, L1, J, F2, L2, K); |
| end loop; |
| end loop; |
| end if; |
| end Put_Int_Matrix; |
| |
| end GNAT.Perfect_Hash_Generators; |