TABLES

version 1.15

by Dmitry A. Kazakov

(mailbox@dmitry-kazakov.de)



This library 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 of the License, or (at your option) any later version. This library 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 this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As a special exception, if other files instantiate generics from this unit, or you link this unit with other files to produce an executable, this unit does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Public License.

Tables project is a part of the simple components for Ada project and can be obtained with it. Alternatively the latest version can be here.

ARM Intel Download Tables Platform: v7 64- 32bit Fedora packages precompiled and packaged using RPM CentOS packages precompiled and packaged using RPM Debian packages precompiled and packaged for dpkg Ubuntu packages precompiled and packaged for dpkg Source distribution (any platform) tables_1_15.tgz (tar + gzip, Windows users may use WinZip)

See also changes log.

1. Tables

The generic package Tables provides tables searched using string keys. The binary search is used for names of known length (see Find). It is also possible to search a table for names of unknown length, i.e. to parse a string using the table (Get). In this case the search time is near to logarithmic, but in the worst case can be linear (when the table contains tokens like "a", "aa", "aaa" and so on). The package is generic. The instantiation parameter is the type of the data tag associated with each table item. The table is initially empty. It is automatically enlarged as new items are added. Upon destruction the memory used by the table is reclaimed. Items in the table can be accessed either by their offsets (in alphabetical order) or by names. Text parsing is also supported. Table assignment makes deep copy.

generic

type Tag is private ;

package Tables is

type Table is new Ada.Finalization.Controlled with private ;

The following subroutines are provided by the package:

Add Insert item Delete Remove item Erase Remove all items Find Find item by name IsIn Membership test Get Parse string GetName Get name of an item GetSize Get number of items GetTag Get tag of an item Locate Find item or parse string returning an offset to Replace Replace item procedure Add

( Folder : in out Table;

Name : String;

Data : Tag

);

procedure Add

( Folder : in out Table;

Name : String;

Data : Tag;

Offset : out Positive

);

The procedure Add inserts an item into the table Folder. The value of the parameter Name is the item name. The parameter Data defines the data associated with the inserted item. The procedure raises the exception Name_Error if an item with the same name is already in the table. When the parameter Offset is specified it is set to the offset of the inserted item. All table items have offsets starting from 1. Items are alphabetically ordered. See also Replace.

procedure Delete (Folder : in out Table; Name : String);

The procedure Delete removes the item Name from the table Folder. Nothing happens when there is no item with the given name. See also Replace.

procedure Delete (Folder : in out Table; Offset : Positive);

This procedure removes an item by its offset. End_Error is propagated when there is no item with such offset.

procedure Erase (Folder : in out Table);

The procedure Erase removes all items from the table Folder.

function Find (Folder : Table; Name : String) return Tag;

The function Find returns the data associated with the item Name of the table Folder. The exception End_Error is propagated when there no such item in the table. See also Get.

procedure Get

( Source : String;

Pointer : in out Integer;

Folder : Table;

Data : out Tag;

[ Got_It : out Boolean ]

);

The procedure Get is used to parse the string specified by the parameter Source using the table Folder. The procedure tries to find the item with the longest name that matches the string Source starting from the position defined by the parameter Pointer. On success Pointer is advanced to the first position following the name of the found item. I.e. Source (OldPointer..Pointer - 1) is the name of the found item. Note that unlike Find that searches tables in logarithmic time, Get could require linear time in some pathological cases. The exception End_Error is propagated when no table item matches the string. The variant with Boolean output parameter Got_It does not propagate End_Error and sets Got_It to false when no table item was matched and true otherwise. The exception Layout_Error is propagated when the value of Pointer is not in the range Source'First..Source'Last+1.

function GetName (Folder : Table; Offset : Positive) return String;

The function GetName returns the name of a table item. The parameter Offset specifies the item offset. All items in the table are enumerated in alphabetical order. The first item has the offset 1. The exception End_Error is propagated when there is no item with such offset. See also GetSize and GetTag.

function GetSize (Folder : Table) return Natural;

The function GetSize returns the number of items in the table Folder.

function GetTag (Folder : Table; Offset : Positive) return Tag;

The function GetTag returns the data associated with a table item. The parameter Offset specifies the item offset. All items in the table are enumerated in alphabetical order. The first item has the offset 1. The exception End_Error is propagated when there is no item with such offset. See also GetSize and GetName.

function IsIn (Folder : Table; Name : String) return Boolean;

The function IsIn returns true if Folder contains an item under the name Name.

function Locate (Folder : Table; Name : String) return Natural;

The function Locate returns the positive offset to item specified by Name. The result 0 when no item is found. The offset can be used in operations GetName, GetTag. Note that the offset indicates the item no longer Folder is updated.

procedure Locate

( Source : String;

Pointer : in out Integer;

Folder : Table;

Offset : out Natural

);

This procedure is similar to Get except that it returns offset to the matched item or else 0. The exception Layout_Error is propagated when the value of Pointer is not in the range Source'First..Source'Last+1.

procedure Replace

( Folder : in out Table;

Name : String;

Data : Tag

procedure Replace

( Folder : in out Table;

Name : String;

Data : Tag;

Offset : out Positive

The procedure Replace inserts new or replaces an existing item of the table Folder. The value of the parameter Name is the item name. The parameter Data defines the data associated with the inserted item. When used, the parameter Offset will be set to the offset of the item. See also Add.

procedure Replace

( Folder : in out Table;

Offset : Positive;

Data : Tag

);

This procedure replaces the data of an existing item specified by its Offset. The offset shall be in 1..GetSize (Folder) range, otherwise End_Error is propagated. The parameter Data is the new data to be associated with the item.

2. Case-insensitive tables

2.1. Latin-1 case-insensitive tables

The child package Tables.Names

generic

with procedure Check_Spelling (Name : String) is <>;

with function

Check_Matched (Source : String; Pointer : Integer)

return Boolean is <>;

Blanks : Ada.Strings.Maps.Character_Set :=

Ada.Strings.Maps.To_Set (' ' & Ada.Characters.Latin_1.HT);

package Tables.Names is

type Dictionary is new Table with private ;

provides the type Dictionary, a descendant of Table. An instance of Dictionary has the same primitive operations (methods) as Table. The difference is that it is intended for dealing with case-insensitive names. So only one of "name", "Name", "namE" can be put into Dictionary. When matched by Find or Get the case of a token plays no role. However, the original case is preserved by the table, so GetName would return the name of a token exactly as it was given in Add or Replace. The parameter Blanks is the set of characters considered blank. All non-empty chains of characters from Blanks are considered equivalent when matched and compared. The original appearance of names containing blank characters is preserved by the table. By default Blanks contains space and horizontal tabulator.

The spelling of a name is checked before it is placed into a Dictionary. For this the procedure Check_Spelling is called. It may raise Constraint_Error to indicate a wrong spelling, which will then propagate out of Add or Replace. The procedure Replace does not change the spelling of the replaced name with regard to the case of its letters and blank characters.

The function Check_Matched is called by Get to ensure that the matched name ends correctly. For example, when the table contains a token for black then Get would call Check_Matched on the string "Blackbird" with the parameter Pointer pointing to bird. Check_Matched could then discard this matching returning false . Check_Matched is never called with Pointer outside Source'Range.

2.2. UTF-8 case-insensitive tables

Implementation nodes. The package described in this section is a part of the distribution of Simple Components for Ada, which is necessary in order to be able to use this package. Note that Simple Components for Ada contains the Tables software as an integral part, so you don't need to install Tables separately from Simple Components for Ada if you plan to use this package.

The child package Tables.UTF8_Names

generic

with procedure Check_Spelling (Name : String) is <>;

with function

Check_Matched (Source : String; Pointer : Integer)

return Boolean is <>;

Blanks : Unicode_Set := -- Tabulator and spaces

Strings_Edit.UTF8.Maps.Constants.Blanks_Set;

Ignored : Unicode_Set := -- Hyphen and others

Strings_Edit.UTF8.Maps.Constants.Other_Format_Set;

package Tables.UTF8_Names is

type Dictionary is new Table with private ;

provides the type Dictionary, a descendant of Table. An instance of Dictionary has the same primitive operations (methods) as Table. The difference is that it is intended for dealing with case-insensitive UTF-8 encoded names. The case as defined by the Unicode standard plays no role when upon matching (such as by Find or Get). However, the original case is preserved by the table, so GetName would return the name of a token exactly as it was given in Add or Replace. The parameter Blanks is the set of code points considered blank. All non-empty chains of characters from Blanks are considered equivalent when matched and compared. The original appearance of names containing blank characters is preserved by the table. By default Blanks contains space and horizontal tabulator. The parameter Ignored is the set of code points which are ignored upon matching. They are also removed when a name is stored in the table. The default value of Ignored is the set of Unicode other format code points, which contains soft hyphen, for example.

The spelling of a name is checked before it is placed into a Dictionary. For this the procedure Check_Spelling is called. It may raise Constraint_Error to indicate a wrong spelling, which will then propagate out of Add or Replace. The procedure Replace does not change the spelling of the replaced name with regard to the case of its letters and blank characters. When Name is an invalid UTF-8 string then it is recommended to propagate Constraint_Error out of Check_Spelling. Otherwise Add, Replace will propagate Data_Error.

When Name is not a valid UTF-8 string in Find, End_Error is propagated. For such a string IsIn returns false .When Soruce is not a valid UTF-8 string in Get its valid substring starting with Source (Pointer) is matched.

The function Check_Matched is called by Get to ensure that the matched name ends correctly. For example, when the table contains a token for black then Get would call Check_Matched on the string "Blackbird" with the parameter Pointer pointing to bird. Check_Matched could then discard this matching returning false . Check_Matched is never called with Pointer outside Source'Range.

Additionally the package provides:

function Canonize (Name : String) return String;

This function returns Name with all code points from the set Ignored removed. Constraint_Error is propagated when Name is an invalid UTF-8 string. All names put in a Dictionary go through this function after being checked using Check_Spelling.

3. Example

The following small example illustrates instantiation of the package Tables using a data type declared in another package. First we declare the type Employee, instances of which will be used as the data associated with the tokens of the table. File data.ads:

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

with Ada.Calendar; use Ada.Calendar;



package Data is

type Employee is record

HomeAddress : Unbounded_String;

DateOfBirth : Time;

end record ;

function Create

( Name : String;

Year : Year_Number;

Month : Month_Number;

Day : Day_Number

) return Employee;

procedure Put (Data : Employee);

end Data;

Implementation, file data.adb:

with Text_IO; use Text_IO;



package body Data is

function Create

( Name : String;

Year : Year_Number;

Month : Month_Number;

Day : Day_Number

) return Employee is

begin

return

( To_Unbounded_String (Name),

Time_Of (Year, Month, Day)

);

end Create;

procedure Put (Data : Employee) is

Year : Year_Number;

Month : Month_Number;

Day : Day_Number;

Sec : Day_Duration;

begin

Split (Data.DateOfBirth, Year, Month, Day, Sec);

Put (" Address : " & To_String (Data.HomeAddress));

New_Line;

Put (" Birth : ");

Put (Year_Number'Image (Year));

Put (Month_Number'Image (Month));

Put (Day_Number'Image (Day));

New_Line;

end Put;

end Data;

Now the package Tables is instantiated with the type Data.Employee as the parameter. Note that this shall be done at library level, so it is a separate file employee_list.ads:

with Tables;

with Data;



package Employee_List is new Tables (Data.Employee);

Here we build a small data base of employees using the package Employee_List to keep it. Then the TOC of the list is printed and the user is asked to enter a name. The list is searched for the name and if such an employee exists, his data are printed. File table_example.adb:

with Data; use Data;

with Employee_List; use Employee_List;

with Text_IO; use Text_IO;



procedure Table_Example is

List : Table;

Name : String ( 1 .. 80 );

Last : Natural;

Data : Employee;

begin

Add

( List,

" Lou Harris ",

Create

( " 10 Midway St., New Yourk, N.Y. 73371 ",

1960 , 12 , 1

) );

Add

( List,

" John M.Knight ",

Create

( " 12 West 42 Rd.A-844, Brooklin, N.Y. 27457 ",

1971 , 5 , 8

) );

Add

( List,

" Alice Clark ",

Create

( " 141 Penns. Avenue, Washington, D.C. 10399 ",

1965 , 10 , 24

) );

for Index in 1 ..GetSize (List) loop

Put (GetName (List, Index));

New_Line;

end loop ;

loop

Put (" Enter a name: ");

Get_Line (Name, Last);

exit when Last = 0 ;

begin

Data := Find (List, Name ( 1 ..Last));

Put (Data);

exception

when End_Error =>

Put (" Unknown ");

New_Line;

end;

end loop;

end Table_Example;

4. Installation

The software does not require special installation. The archive's content can be put in a directory and used as-is. For users of GNAT compiler the software provides gpr project files, which can be used in the Gnat Programming Studio (GPS).

For CentOS, Debian, Fedora, Ubuntu Linux distributions there are pre-compiled packages, see the links on the top of the page.

Project files Provides Use in custom project tables Tables for Ada with "tables.gpr";

5. Changes log

The following versions were tested with the compilers:

GNAT Community 2018 (20180523-73)

GNAT 8

Changes (16 September 2019) to the version 1.14:

A variant of the procedure Get was added. It uses a return code instead of raising exception when nothing has been matched.

Changes (5 Aug 2018) to the version 1.13:

Switched to GNAT Community 2018.

Changes (2 April 2015) to the version 1.12:

ARMv7 (AKA armhf) support.

Changes (1 June 2014) to the version 1.11:

Compiled with GNAT 4.9

Changes to the version 1.10:

Fedora and Debian packages are provided for both 32- and 64-bit architectures.

Changes to the version 1.9:

The library is packaged for Fedora and Debian.

Changes to the version 1.8:

Operations Locate were added to the package Tables and Tables.Names. Differently to Find and Get they return the offset to the found item;

Installation instructions added.

Changes to the version 1.7:

Case-insensitive tables of UTF-8 names were added;

Minor bug fixes.

Changes to the version 1.6:

For GNAT users GPS project files were added.

Changes to the version 1.5:

Procedure Replace was added;

A variant of the procedure Add now returns the offset of the inserted item.

Changes to the version 1.4:

The set of blank characters can be specified as the formal parameter of the package Tables.Names.

Changes to the version 1.3:

Function IsIn was added to provide membership test;

Bug fixes in the test programs.

Changes to the version 1.2:

Licensing wording was corrected to comply with GMGPL

Changes to the version 1.1:

Delete also removes items by their offsets;

The child package Tables.Names for dealing with case-insensitive names.

6. Table of Contents

1. Tables

2. Case-insensitive tables

2.1. Latin-1 case-insensitive tables

2.2. UTF-8 case-insensitive tables

3. Example

4. Installation

5. Changes log

6. Table of contents