bit_array.c revision 299742
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 */ 54struct 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. */ 69static apr_size_t 70select_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 85svn_bit_array__t * 86svn_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 99void 100svn_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 167svn_boolean_t 168svn_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