1 /*
2 * bit_array.c : implement a simple packed bit array
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25 #include "svn_sorts.h"
26 #include "private/svn_subr_private.h"
27
28 /* We allocate our data buffer in blocks of this size (in bytes).
29 * For performance reasons, this shall be a power of two.
30 * It should also not exceed 80kB (to prevent APR pool fragmentation) and
31 * not be too small (to keep the number of OS-side memory allocations low -
32 * avoiding hitting system-specific limits).
33 */
34 #define BLOCK_SIZE 0x10000
35
36 /* Number of bits in each block.
37 */
38 #define BLOCK_SIZE_BITS (8 * BLOCK_SIZE)
39
40 /* Initial array size (covers INITIAL_BLOCK_COUNT * BLOCK_SIZE_BITS bits).
41 * For performance reasons, this shall be a power of two.
42 */
43 #define INITIAL_BLOCK_COUNT 16
44
45 /* We store the bits in a lazily allocated two-dimensional array.
46 * For every BLOCK_SIZE_BITS range of indexes, there is one entry in the
47 * BLOCKS array. If index / BLOCK_SIZE_BITS exceeds BLOCK_COUNT-1, the
48 * blocks are implicitly empty. Only if a bit will be set to 1, will the
49 * BLOCKS array be auto-expanded.
50 *
51 * As long as no bit got set in a particular block, the respective entry in
52 * BLOCKS entry will be NULL, implying that all block contents is 0.
53 */
54 struct svn_bit_array__t
55 {
56 /* Data buffer of BLOCK_COUNT blocks, BLOCK_SIZE_BITS each. Never NULL.
57 * Every block may be NULL, though. */
58 unsigned char **blocks;
59
60 /* Number of bytes allocated to DATA. Never shrinks. */
61 apr_size_t block_count;
62
63 /* Reallocate DATA form this POOL when growing. */
64 apr_pool_t *pool;
65 };
66
67 /* Given that MAX shall be an actual bit index in a packed bit array,
68 * return the number of blocks entries to allocate for the data buffer. */
69 static apr_size_t
select_data_size(apr_size_t max)70 select_data_size(apr_size_t max)
71 {
72 /* We allocate a power of two of bytes but at least 16 blocks. */
73 apr_size_t size = INITIAL_BLOCK_COUNT;
74
75 /* Caution:
76 * MAX / BLOCK_SIZE_BITS == SIZE still means that MAX is out of bounds.
77 * OTOH, 2 * (MAX/BLOCK_SIZE_BITS) is always within the value range of
78 * APR_SIZE_T. */
79 while (size <= max / BLOCK_SIZE_BITS)
80 size *= 2;
81
82 return size;
83 }
84
85 svn_bit_array__t *
svn_bit_array__create(apr_size_t max,apr_pool_t * pool)86 svn_bit_array__create(apr_size_t max,
87 apr_pool_t *pool)
88 {
89 svn_bit_array__t *array = apr_pcalloc(pool, sizeof(*array));
90
91 array->block_count = select_data_size(max);
92 array->pool = pool;
93 array->blocks = apr_pcalloc(pool,
94 array->block_count * sizeof(*array->blocks));
95
96 return array;
97 }
98
99 void
svn_bit_array__set(svn_bit_array__t * array,apr_size_t idx,svn_boolean_t value)100 svn_bit_array__set(svn_bit_array__t *array,
101 apr_size_t idx,
102 svn_boolean_t value)
103 {
104 unsigned char *block;
105
106 /* Index within ARRAY->BLOCKS for the block containing bit IDX. */
107 apr_size_t block_idx = idx / BLOCK_SIZE_BITS;
108
109 /* Within that block, index of the byte containing IDX. */
110 apr_size_t byte_idx = (idx % BLOCK_SIZE_BITS) / 8;
111
112 /* Within that byte, index of the bit corresponding to IDX. */
113 apr_size_t bit_idx = (idx % BLOCK_SIZE_BITS) % 8;
114
115 /* If IDX is outside the allocated range, we _may_ have to grow it.
116 *
117 * Be sure to use division instead of multiplication as we need to cover
118 * the full value range of APR_SIZE_T for the bit indexes.
119 */
120 if (block_idx >= array->block_count)
121 {
122 apr_size_t new_count;
123 unsigned char **new_blocks;
124
125 /* Unallocated indexes are implicitly 0, so no actual allocation
126 * required in that case.
127 */
128 if (!value)
129 return;
130
131 /* Grow block list to cover IDX.
132 * Clear the new entries to guarantee our array[idx]==0 default.
133 */
134 new_count = select_data_size(idx);
135 new_blocks = apr_pcalloc(array->pool, new_count * sizeof(*new_blocks));
136 memcpy(new_blocks, array->blocks,
137 array->block_count * sizeof(*new_blocks));
138 array->blocks = new_blocks;
139 array->block_count = new_count;
140 }
141
142 /* IDX is covered by ARRAY->BLOCKS now. */
143
144 /* Get the block that contains IDX. Auto-allocate it if missing. */
145 block = array->blocks[block_idx];
146 if (block == NULL)
147 {
148 /* Unallocated indexes are implicitly 0, so no actual allocation
149 * required in that case.
150 */
151 if (!value)
152 return;
153
154 /* Allocate the previously missing block and clear it for our
155 * array[idx] == 0 default. */
156 block = apr_pcalloc(array->pool, BLOCK_SIZE);
157 array->blocks[block_idx] = block;
158 }
159
160 /* Set / reset one bit. Be sure to use unsigned shifts. */
161 if (value)
162 block[byte_idx] |= (unsigned char)(1u << bit_idx);
163 else
164 block[byte_idx] &= ~(unsigned char)(1u << bit_idx);
165 }
166
167 svn_boolean_t
svn_bit_array__get(svn_bit_array__t * array,apr_size_t idx)168 svn_bit_array__get(svn_bit_array__t *array,
169 apr_size_t idx)
170 {
171 unsigned char *block;
172
173 /* Index within ARRAY->BLOCKS for the block containing bit IDX. */
174 apr_size_t block_idx = idx / BLOCK_SIZE_BITS;
175
176 /* Within that block, index of the byte containing IDX. */
177 apr_size_t byte_idx = (idx % BLOCK_SIZE_BITS) / 8;
178
179 /* Within that byte, index of the bit corresponding to IDX. */
180 apr_size_t bit_idx = (idx % BLOCK_SIZE_BITS) % 8;
181
182 /* Indexes outside the allocated range are implicitly 0. */
183 if (block_idx >= array->block_count)
184 return 0;
185
186 /* Same if the respective block has not been allocated. */
187 block = array->blocks[block_idx];
188 if (block == NULL)
189 return 0;
190
191 /* Extract one bit (get the byte, shift bit to LSB, extract it). */
192 return (block[byte_idx] >> bit_idx) & 1;
193 }
194
195