aboutsummaryrefslogtreecommitdiffstats
path: root/src/dyn_tables.adb
blob: cf292d4fb88e62b0bee836e8443b4f8008ef35d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
--  Efficient expandable one dimensional array.
--  Copyright (C) 2015 - 2016 Tristan Gingold
--
--  This program 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 program 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 program.  If not, see <gnu.org/licenses>.

with Interfaces.C; use Interfaces.C;
with System;

package body Dyn_Tables is
   --  Size of an element in storage units (bytes).
   El_Size : constant size_t :=
     size_t (Table_Type'Component_Size / System.Storage_Unit);

   --  Expand the table by doubling its size.  The table must have been
   --  initialized.  Memory space for the extra elements are allocated but the
   --  length of the table is not increased.
   procedure Reserve (T : in out Instance; Num : Unsigned)
   is
      --  For efficiency, directly call realloc.
      function Crealloc (Ptr : Table_Thin_Ptr; Size : size_t)
                        return Table_Thin_Ptr;
      pragma Import (C, Crealloc, "realloc");

      New_Len : Unsigned;
      New_Last : Unsigned;
   begin
      pragma Assert (T.Priv.Length /= 0);
      pragma Assert (T.Table /= null);

      --  Expand the bound.
      New_Last := T.Priv.Last_Pos + Num;
      if New_Last < T.Priv.Last_Pos then
         raise Constraint_Error;
      end if;

      --  Check if need to reallocate.
      if New_Last < T.Priv.Length then
         return;
      end if;

      --  Double the length.
      loop
         New_Len := T.Priv.Length * 2;

         --  Check overflow.
         if New_Len < T.Priv.Length then
            raise Constraint_Error;
         end if;

         T.Priv.Length := New_Len;
         exit when New_Len > New_Last;
      end loop;

      --  Realloc and check result.
      if size_t (T.Priv.Length) > size_t'Last / El_Size then
         raise Constraint_Error;
      end if;
      T.Table := Crealloc (T.Table, size_t (T.Priv.Length) * El_Size);
      if T.Table = null then
         raise Storage_Error;
      end if;
   end Reserve;

   --  Expand the table (allocate and increase the length).
   procedure Expand (T : in out Instance; Num : Unsigned) is
   begin
      Reserve (T, Num);
      T.Priv.Last_Pos := T.Priv.Last_Pos + Num;
   end Expand;

   procedure Reserve (T : in out Instance; Num : Natural) is
   begin
      Reserve (T, Unsigned (Num));
   end Reserve;

   procedure Allocate (T : in out Instance; Num : Natural := 1) is
   begin
      Expand (T, Unsigned (Num));
   end Allocate;

   procedure Increment_Last (T : in out Instance) is
   begin
      --  Increase by 1.
      Expand (T, 1);
   end Increment_Last;

   procedure Decrement_Last (T : in out Instance) is
   begin
      T.Priv.Last_Pos := T.Priv.Last_Pos - 1;
   end Decrement_Last;

   procedure Set_Last (T : in out Instance; Index : Table_Index_Type)
   is
      New_Last : constant Unsigned :=
        (Table_Index_Type'Pos (Index)
           - Table_Index_Type'Pos (Table_Low_Bound) + 1);
   begin
      if New_Last < T.Priv.Last_Pos then
         --  Decrease length.
         T.Priv.Last_Pos := New_Last;
      else
         --  Increase length.
         Expand (T, New_Last - T.Priv.Last_Pos);
      end if;
   end Set_Last;

   procedure Init (T : in out Instance; Table_Initial : Positive)
   is
      --  Direct interface to malloc.
      function Cmalloc (Size : size_t) return Table_Thin_Ptr;
      pragma Import (C, Cmalloc, "malloc");
   begin
      if T.Table = null then
         --  Allocate memory if not already allocated.
         T.Priv.Length := Unsigned (Table_Initial);
         T.Table := Cmalloc (size_t (T.Priv.Length) * El_Size);
      end if;

      --  Table is initially empty.
      T.Priv.Last_Pos := 0;
   end Init;

   function Last (T : Instance) return Table_Index_Type is
   begin
      return Table_Index_Type'Val
        (Table_Index_Type'Pos (Table_Low_Bound)
           + Unsigned'Pos (T.Priv.Last_Pos) - 1);
   end Last;

   function Next (T : Instance) return Table_Index_Type is
   begin
      return Table_Index_Type'Val
        (Table_Index_Type'Pos (Table_Low_Bound) + T.Priv.Last_Pos);
   end Next;

   procedure Free (T : in out Instance) is
      --  Direct interface to free.
      procedure Cfree (Ptr : Table_Thin_Ptr);
      pragma Import (C, Cfree, "free");
   begin
      Cfree (T.Table);
      T := (Table => null,
            Priv => (Length => 0,
                     Last_Pos => 0));
   end Free;

   procedure Append (T : in out Instance; Val : Table_Component_Type) is
   begin
      Increment_Last (T);
      T.Table (Last (T)) := Val;
   end Append;
end Dyn_Tables;