1 /*
2  * This file is part of the Yices SMT Solver.
3  * Copyright (C) 2017 SRI International.
4  *
5  * Yices is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Yices is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Yices.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * SPARSE ARRAYS
21  */
22 
23 #include "utils/memalloc.h"
24 #include "utils/sparse_arrays.h"
25 
26 
27 /*
28  * Initialize a:
29  * - n = minimal size requested
30  * - if n is 0, the default size is used
31  * - all blocks are marked as clean
32  */
init_sparse_array(sparse_array_t * a,uint32_t n)33 void init_sparse_array(sparse_array_t *a, uint32_t n) {
34   uint32_t nblocks;
35 
36   if (n == 0) {
37     nblocks = DEF_SPARSE_ARRAY_NBLOCKS;
38   } else {
39     nblocks = (n + (BSIZE - 1)) >> BSIZE_NBITS;
40     assert(n <= nblocks * BSIZE);
41     if (nblocks > MAX_NBLOCKS) {
42       out_of_memory();
43     }
44   }
45 
46   // adjust n to a multiple of the block size
47   n = nblocks * BSIZE;
48   a->data = (uint32_t *) safe_malloc(n * sizeof(uint32_t));
49   a->clean = allocate_bitvector0(nblocks); // all dirty
50   a->nblocks = nblocks;
51   a->nelems = 0;
52 }
53 
54 
55 /*
56  * Delete: free all memory
57  */
delete_sparse_array(sparse_array_t * a)58 void delete_sparse_array(sparse_array_t *a) {
59   safe_free(a->data);
60   delete_bitvector(a->clean);
61   a->data = NULL;
62   a->clean = NULL;
63 }
64 
65 
66 /*
67  * Reset: mark all blocks as dirty
68  */
reset_sparse_array(sparse_array_t * a)69 void reset_sparse_array(sparse_array_t *a) {
70   clear_bitvector(a->clean, a->nblocks);
71   a->nelems = 0;
72 }
73 
74 
75 /*
76  * Initialize block i in array a
77  */
clean_block(uint32_t * a,uint32_t i)78 static void clean_block(uint32_t *a, uint32_t i) {
79   uint32_t n;
80 
81   n = BSIZE;
82   a += block_start(i);
83   do {
84     *a ++ = 0;
85     n --;
86   } while (n > 0);
87 }
88 
89 
90 /*
91  * Copy block i of a into b
92  */
copy_block(uint32_t * b,uint32_t * a,uint32_t i)93 static void copy_block(uint32_t *b, uint32_t *a, uint32_t i) {
94   uint32_t n;
95 
96   n = BSIZE;
97   a += block_start(i);
98   b += block_start(i);
99   do {
100     * b++ = *a ++;
101     n --;
102   } while (n > 0);
103 }
104 
105 
106 
107 /*
108  * Resize the array to at least nb blocks
109  * - nb must be more than a->nblocks
110  */
resize_sparse_array(sparse_array_t * a,uint32_t nb)111 static void resize_sparse_array(sparse_array_t *a, uint32_t nb) {
112   uint32_t i, nblocks, n;
113   uint32_t *tmp;
114 
115   if (nb > MAX_NBLOCKS) {
116     out_of_memory();
117   }
118 
119   n = a->nblocks;
120   nblocks = n;
121   nblocks += nblocks>>1; // try 50% larger
122   if (nb > nblocks) {
123     nblocks = nb;
124   } else if (nblocks > MAX_NBLOCKS) {
125     nblocks = MAX_NBLOCKS;
126   }
127 
128   // n = current size, nblocks = new size
129   // we avoid realloc here (to save the cost of copying the full array)
130   tmp = (uint32_t *) safe_malloc(nblocks * (BSIZE * sizeof(uint32_t)));
131   a->clean = extend_bitvector0(a->clean, n, nblocks);
132 
133   // copy all clean blocks from a->data to tmp
134   n = a->nblocks;
135   for (i=0; i<n; i++) {
136     if (tst_bit(a->clean, i)) {
137       copy_block(tmp, a->data, i);
138     }
139   }
140 
141   safe_free(a->data);
142   a->data = tmp;
143   a->nblocks = nblocks;
144 }
145 
146 
147 /*
148  * Read the value mapped to i:
149  * - return 0 if i is in a dirty block or outside the array
150  */
sparse_array_read(sparse_array_t * a,uint32_t i)151 uint32_t sparse_array_read(sparse_array_t *a, uint32_t i) {
152   uint32_t k, y;
153 
154   k = block_of_index(i);
155   y = 0;
156   if (k < a->nblocks && tst_bit(a->clean, k)) {
157     y = a->data[i];
158   }
159   return y;
160 }
161 
162 
163 /*
164  * Increment a[i]
165  */
sparse_array_incr(sparse_array_t * a,uint32_t i)166 void sparse_array_incr(sparse_array_t *a, uint32_t i) {
167   uint32_t k;
168 
169   k = block_of_index(i);
170   if (k >= a->nblocks) {
171     resize_sparse_array(a, k+1);
172   }
173   if (tst_bit(a->clean, k) && a->data[i] < UINT32_MAX) {
174     if (a->data[i] == 0) {
175       a->nelems ++;
176     }
177     a->data[i] ++;
178   } else {
179     set_bit(a->clean, k);
180     clean_block(a->data, k);
181     a->data[i] = 1;
182     a->nelems ++;
183   }
184 }
185 
186 
187 /*
188  * Decrement a[i]: the current count must be positive
189  */
sparse_array_decr(sparse_array_t * a,uint32_t i)190 void sparse_array_decr(sparse_array_t *a, uint32_t i) {
191 #ifndef NDEBUG
192   uint32_t k;
193 
194   k = block_of_index(i);
195   assert(k < a->nblocks && tst_bit(a->clean, k) && a->data[i] > 0);
196 #endif
197 
198   a->data[i] --;
199   if (a->data[i] == 0) {
200     assert(a->nelems > 0);
201     a->nelems --;
202   }
203 }
204 
205 
206 /*
207  * Apply f(aux, i) to all i that have positive count in block k
208  */
iterate_in_block(uint32_t * a,uint32_t k,void * aux,sparse_array_iterator_t f)209 static void iterate_in_block(uint32_t *a, uint32_t k, void *aux, sparse_array_iterator_t f) {
210   uint32_t n, i;
211 
212   n = block_start(k) + BSIZE;
213   for (i = block_start(k); i<n; i++) {
214     if (a[i] > 0) {
215       f(aux, i);
216     }
217   }
218 }
219 
220 
221 /*
222  * Go through all elements i with a positive ref counter and call f(aux, i)
223  */
sparse_array_iterate(sparse_array_t * a,void * aux,sparse_array_iterator_t f)224 void sparse_array_iterate(sparse_array_t *a, void *aux, sparse_array_iterator_t f) {
225   uint32_t i, n;
226 
227   n = a->nblocks;
228   for (i=0; i<n; i++) {
229     if (tst_bit(a->clean, i)) {
230       iterate_in_block(a->data, i, aux, f);
231     }
232   }
233 }
234 
235