-- Name table. -- Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold -- -- GHDL is free software; you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free -- Software Foundation; either version 2, or (at your option) any later -- version. -- -- GHDL is distributed in the hope that it will be useful, but WITHOUT ANY -- WARRANTY; without even the implied warranty of MERCHANTABILITY or -- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- for more details. -- -- You should have received a copy of the GNU General Public License -- along with GHDL; see the file COPYING. If not, write to the Free -- Software Foundation, 59 Temple Place - Suite 330, Boston, MA -- 02111-1307, USA. with Ada.Text_IO; use Ada.Text_IO; with GNAT.Table; package body Name_Table is -- A flag that creates verbosity. Debug_Name_Table: constant Boolean := False; First_Character_Name_Id : constant Name_Id := 1; type Hash_Value_Type is mod 2**32; -- An entry in the name table. type Identifier is record Hash: Hash_Value_Type; Next: Name_Id; -- FIXME: to be removed (compute from name of next identifier). Length: Natural; -- Index in strings_table. Name: Natural; -- User infos. Info: Int32; end record; -- Hash table. -- Number of entry points. Hash_Table_Size: constant Hash_Value_Type := 1024; Hash_Table: array (0 .. Hash_Table_Size - 1) of Name_Id; -- The table to store all the strings. package Strings_Table is new GNAT.Table (Table_Index_Type => Natural, Table_Component_Type => Character, Table_Low_Bound => Natural'First, Table_Initial => 4096, Table_Increment => 100); -- A NUL character is stored after each word in the strings_table. -- This is used for compatibility with C. NUL: constant Character := Character'Val (0); -- Allocate place in the strings_table, and store the name_buffer into it. -- Also append a NUL. function Store return Natural is Res: Natural; begin Res := Strings_Table.Allocate (Name_Length + 1); Strings_Table.Table (Res .. Res + Name_Length - 1) := Strings_Table.Table_Type (Name_Buffer (1 .. Name_Length)); Strings_Table.Table (Res + Name_Length) := NUL; return Res; end Store; package Names_Table is new GNAT.Table (Table_Index_Type => Name_Id, Table_Component_Type => Identifier, Table_Low_Bound => Name_Id'First, Table_Initial => 1024, Table_Increment => 100); -- Initialize this package -- This must be called once and only once before any use. procedure Initialize is Pos: Natural; Id: Name_Id; begin Strings_Table.Init; Names_Table.Init; -- Reserve entry 0. if Names_Table.Allocate /= Null_Identifier then raise Program_Error; end if; Strings_Table.Set_Last (1); Names_Table.Table (Null_Identifier) := (Length => 0, Hash => 0, Name => 1, Next => Null_Identifier, Info => 0); -- Store characters. for C in Character loop Pos := Strings_Table.Allocate; Strings_Table.Table (Pos) := C; Id := Names_Table.Allocate; Names_Table.Table (Id) := (Length => 1, Hash => 0, Name => Pos, Next => Null_Identifier, Info => 0); end loop; Hash_Table := (others => Null_Identifier); end Initialize; -- Compute the hash value of a string. function Hash return Hash_Value_Type is Res: Hash_Value_Type := 0; begin for I in 1 .. Name_Length loop Res := Res * 7 + Character'Pos(Name_Buffer(I)); Res := Res + Res / 2**28; end loop; return Res; end Hash; -- Get the string associed to an identifier. function Image (Id: Name_Id) return String is Name_Entry: Identifier renames Names_Table.Table(Id); subtype Result_Type is String (1 .. Name_Entry.Length); begin if Is_Character (Id) then return ''' & Strings_Table.Table (Name_Entry.Name) & '''; else return Result_Type (Strings_Table.Table (Name_Entry.Name .. Name_Entry.Name + Name_Entry.Length - 1)); end if; end Image; procedure Image (Id : Name_Id) is Name_Entry: Identifier renames Names_Table.Table(Id); begin if Is_Character (Id) then Name_Buffer (1) := Get_Character (Id); Name_Length := 1; else Name_Length := Name_Entry.Length; Name_Buffer (1 .. Name_Entry.Length) := String (Strings_Table.Table (Name_Entry.Name .. Name_Entry.Name + Name_Entry.Length - 1)); end if; end Image; -- Get the address of the first character of ID. -- The string is NUL-terminated (this is done by get_identifier). function Get_Address (Id: Name_Id) return System.Address is Name_Entry: Identifier renames Names_Table.Table(Id); begin return Strings_Table.Table (Name_Entry.Name)'Address; end Get_Address; function Get_Name_Length (Id: Name_Id) return Natural is begin return Names_Table.Table(Id).Length; end Get_Name_Length; function Is_Character (Id: Name_Id) return Boolean is begin return Id >= First_Character_Name_Id and then Id <= First_Character_Name_Id + Character'Pos (Character'Last); end Is_Character; -- Get the character associed to an identifier. function Get_Character (Id: Name_Id) return Character is begin pragma Assert (Is_Character (Id)); return Character'Val (Id - First_Character_Name_Id); end Get_Character; -- Get and set the info field associated with each identifier. -- Used to store interpretations of the name. function Get_Info (Id: Name_Id) return Int32 is begin return Names_Table.Table (Id).Info; end Get_Info; procedure Set_Info (Id: Name_Id; Info: Int32) is begin Names_Table.Table (Id).Info := Info; end Set_Info; function Compare_Name_Buffer_With_Name (Id : Name_Id) return Boolean is Ne: Identifier renames Names_Table.Table(Id); begin return String (Strings_Table.Table (Ne.Name .. Ne.Name + Ne.Length - 1)) = Name_Buffer (1 .. Name_Length); end Compare_Name_Buffer_With_Name; -- Get or create an entry in the name table. -- The string is taken from NAME_BUFFER and NAME_LENGTH. function Get_Identifier return Name_Id is Hash_Value, Hash_Index: Hash_Value_Type; Res: Name_Id; begin Hash_Value := Hash; Hash_Index := Hash_Value mod Hash_Table_Size; if Debug_Name_Table then Put_Line ("get_identifier " & Name_Buffer (1 .. Name_Length)); end if; Res := Hash_Table (Hash_Index); while Res /= Null_Identifier loop --Put_Line ("compare with " & Get_String (Res)); if Names_Table.Table (Res).Hash = Hash_Value and then Names_Table.Table (Res).Length = Name_Length and then Compare_Name_Buffer_With_Name (Res) then --Put_Line ("found"); return Res; end if; Res := Names_Table.Table (Res).Next; end loop; Res := Names_Table.Allocate; Names_Table.Table (Res) := (Length => Name_Length, Hash => Hash_Value, Name => Store, Next => Hash_Table (Hash_Index), Info => 0); Hash_Table (Hash_Index) := Res; --Put_Line ("created"); return Res; end Get_Identifier; function Get_Identifier_No_Create return Name_Id is Hash_Value, Hash_Index: Hash_Value_Type; Res: Name_Id; begin Hash_Value := Hash; Hash_Index := Hash_Value mod Hash_Table_Size; Res := Hash_Table (Hash_Index); while Res /= Null_Identifier loop if Names_Table.Table (Res).Hash = Hash_Value and then Names_Table.Table (Res).Length = Name_Length and then Compare_Name_Buffer_With_Name (Res) then return Res; end if; Res := Names_Table.Table (Res).Next; end loop; return Null_Identifier; end Get_Identifier_No_Create; -- Get or create an entry in the name table. function Get_Identifier (Str: String) return Name_Id is begin Name_Length := Str'Length; Name_Buffer (1 .. Name_Length) := Str; return Get_Identifier; end Get_Identifier; function Get_Identifier (Char: Character) return Name_Id is begin return First_Character_Name_Id + Character'Pos (Char); end Get_Identifier; -- Be sure all info fields have their default value. procedure Assert_No_Infos is Err: Boolean := False; begin for I in Names_Table.First .. Names_Table.Last loop if Get_Info (I) /= 0 then Err := True; Put_Line ("still infos in" & Name_Id'Image (I) & ", ie: " & Image (I) & ", info =" & Int32'Image (Names_Table.Table (I).Info)); end if; end loop; if Err then raise Program_Error; end if; end Assert_No_Infos; -- Return the latest name_id used. -- kludge, use only for debugging. function Last_Name_Id return Name_Id is begin return Names_Table.Last; end Last_Name_Id; -- Used to debug. -- Disp the strings table, one word per line. procedure Dump; pragma Unreferenced (Dump); procedure Dump is First: Natural; begin Put_Line ("strings_table:"); First := 0; for I in 0 .. Strings_Table.Last loop if Strings_Table.Table(I) = NUL then Put_Line (Natural'Image (First) & ": " & String (Strings_Table.Table (First .. I - 1))); First := I + 1; end if; end loop; end Dump; function Get_Hash_Entry_Length (H : Hash_Value_Type) return Natural is Res : Natural := 0; N : Name_Id; begin N := Hash_Table (H); while N /= Null_Identifier loop Res := Res + 1; N := Names_Table.Table (N).Next; end loop; return Res; end Get_Hash_Entry_Length; procedure Disp_Stats is Min : Natural; Max : Natural; N : Natural; begin Put_Line ("Name table statistics:"); Put_Line (" number of identifiers: " & Name_Id'Image (Last_Name_Id)); Put_Line (" size of strings: " & Natural'Image (Strings_Table.Last)); Put_Line (" hash distribution (number of entries per length):"); Min := Natural'Last; Max := Natural'First; for I in Hash_Table'Range loop N := Get_Hash_Entry_Length (I); Min := Natural'Min (Min, N); Max := Natural'Max (Max, N); end loop; declare type Nat_Array is array (Min .. Max) of Natural; S : Nat_Array := (others => 0); begin for I in Hash_Table'Range loop N := Get_Hash_Entry_Length (I); S (N) := S (N) + 1; end loop; for I in S'Range loop if S (I) /= 0 then Put_Line (" " & Natural'Image (I) & ":" & Natural'Image (S (I))); end if; end loop; end; end Disp_Stats; end Name_Table;