| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license | |
| 5 * that can be found in the LICENSE file in the root of the source | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | |
| 11 | |
| 12 /* This code is in the public domain. | |
| 13 ** Version: 1.1 Author: Walt Karas | |
| 14 */ | |
| 15 | |
| 16 #include "hmm_intrnl.h" | |
| 17 | |
| 18 void U(init)(U(descriptor) *desc) { | |
| 19 desc->avl_tree_root = 0; | |
| 20 desc->last_freed = 0; | |
| 21 } | |
| 22 | |
| 23 /* Remove a free block from a bin's doubly-linked list when it is not, | |
| 24 ** the first block in the bin. | |
| 25 */ | |
| 26 void U(dll_remove)( | |
| 27 /* Pointer to pointer record in the block to be removed. */ | |
| 28 ptr_record *to_remove) { | |
| 29 to_remove->prev->next = to_remove->next; | |
| 30 | |
| 31 if (to_remove->next) | |
| 32 to_remove->next->prev = to_remove->prev; | |
| 33 } | |
| 34 | |
| 35 /* Put a block into the free collection of a heap. | |
| 36 */ | |
| 37 void U(into_free_collection)( | |
| 38 /* Pointer to heap descriptor. */ | |
| 39 U(descriptor) *desc, | |
| 40 /* Pointer to head record of block. */ | |
| 41 head_record *head_ptr) { | |
| 42 ptr_record *ptr_rec_ptr = HEAD_TO_PTR_REC(head_ptr); | |
| 43 | |
| 44 ptr_record *bin_front_ptr = | |
| 45 U(avl_insert)((U(avl_avl) *) & (desc->avl_tree_root), ptr_rec_ptr); | |
| 46 | |
| 47 if (bin_front_ptr != ptr_rec_ptr) { | |
| 48 /* The block was not inserted into the AVL tree because there is | |
| 49 ** already a bin for the size of the block. */ | |
| 50 | |
| 51 MARK_SUCCESSIVE_BLOCK_IN_FREE_BIN(head_ptr) | |
| 52 ptr_rec_ptr->self = ptr_rec_ptr; | |
| 53 | |
| 54 /* Make the block the new second block in the bin's doubly-linked | |
| 55 ** list. */ | |
| 56 ptr_rec_ptr->prev = bin_front_ptr; | |
| 57 ptr_rec_ptr->next = bin_front_ptr->next; | |
| 58 bin_front_ptr->next = ptr_rec_ptr; | |
| 59 | |
| 60 if (ptr_rec_ptr->next) | |
| 61 ptr_rec_ptr->next->prev = ptr_rec_ptr; | |
| 62 } else | |
| 63 /* Block is first block in new bin. */ | |
| 64 ptr_rec_ptr->next = 0; | |
| 65 } | |
| 66 | |
| 67 /* Allocate a block from a given bin. Returns a pointer to the payload | |
| 68 ** of the removed block. The "last freed" pointer must be null prior | |
| 69 ** to calling this function. | |
| 70 */ | |
| 71 void *U(alloc_from_bin)( | |
| 72 /* Pointer to heap descriptor. */ | |
| 73 U(descriptor) *desc, | |
| 74 /* Pointer to pointer record of first block in bin. */ | |
| 75 ptr_record *bin_front_ptr, | |
| 76 /* Number of BAUs needed in the allocated block. If the block taken | |
| 77 ** from the bin is significantly larger than the number of BAUs needed, | |
| 78 ** the "extra" BAUs are split off to form a new free block. */ | |
| 79 U(size_bau) n_baus) { | |
| 80 head_record *head_ptr; | |
| 81 U(size_bau) rem_baus; | |
| 82 | |
| 83 if (bin_front_ptr->next) { | |
| 84 /* There are multiple blocks in this bin. Use the 2nd block in | |
| 85 ** the bin to avoid needless change to the AVL tree. | |
| 86 */ | |
| 87 | |
| 88 ptr_record *ptr_rec_ptr = bin_front_ptr->next; | |
| 89 head_ptr = PTR_REC_TO_HEAD(ptr_rec_ptr); | |
| 90 | |
| 91 #ifdef AUDIT_FAIL | |
| 92 AUDIT_BLOCK(head_ptr) | |
| 93 #endif | |
| 94 | |
| 95 U(dll_remove)(ptr_rec_ptr); | |
| 96 } else { | |
| 97 /* There is only one block in the bin, so it has to be removed | |
| 98 ** from the AVL tree. | |
| 99 */ | |
| 100 | |
| 101 head_ptr = PTR_REC_TO_HEAD(bin_front_ptr); | |
| 102 | |
| 103 U(avl_remove)( | |
| 104 (U(avl_avl) *) & (desc->avl_tree_root), BLOCK_BAUS(head_ptr)); | |
| 105 } | |
| 106 | |
| 107 MARK_BLOCK_ALLOCATED(head_ptr) | |
| 108 | |
| 109 rem_baus = BLOCK_BAUS(head_ptr) - n_baus; | |
| 110 | |
| 111 if (rem_baus >= MIN_BLOCK_BAUS) { | |
| 112 /* Since there are enough "extra" BAUs, split them off to form | |
| 113 ** a new free block. | |
| 114 */ | |
| 115 | |
| 116 head_record *rem_head_ptr = | |
| 117 (head_record *) BAUS_FORWARD(head_ptr, n_baus); | |
| 118 | |
| 119 /* Change the next block's header to reflect the fact that the | |
| 120 ** block preceeding it is now smaller. | |
| 121 */ | |
| 122 SET_PREV_BLOCK_BAUS( | |
| 123 BAUS_FORWARD(head_ptr, head_ptr->block_size), rem_baus) | |
| 124 | |
| 125 head_ptr->block_size = n_baus; | |
| 126 | |
| 127 rem_head_ptr->previous_block_size = n_baus; | |
| 128 rem_head_ptr->block_size = rem_baus; | |
| 129 | |
| 130 desc->last_freed = rem_head_ptr; | |
| 131 } | |
| 132 | |
| 133 return(HEAD_TO_PTR_REC(head_ptr)); | |
| 134 } | |
| 135 | |
| 136 /* Take a block out of the free collection. | |
| 137 */ | |
| 138 void U(out_of_free_collection)( | |
| 139 /* Descriptor of heap that block is in. */ | |
| 140 U(descriptor) *desc, | |
| 141 /* Pointer to head of block to take out of free collection. */ | |
| 142 head_record *head_ptr) { | |
| 143 ptr_record *ptr_rec_ptr = HEAD_TO_PTR_REC(head_ptr); | |
| 144 | |
| 145 if (ptr_rec_ptr->self == ptr_rec_ptr) | |
| 146 /* Block is not the front block in its bin, so all we have to | |
| 147 ** do is take it out of the bin's doubly-linked list. */ | |
| 148 U(dll_remove)(ptr_rec_ptr); | |
| 149 else { | |
| 150 ptr_record *next = ptr_rec_ptr->next; | |
| 151 | |
| 152 if (next) | |
| 153 /* Block is the front block in its bin, and there is at least | |
| 154 ** one other block in the bin. Substitute the next block for | |
| 155 ** the front block. */ | |
| 156 U(avl_subst)((U(avl_avl) *) & (desc->avl_tree_root), next); | |
| 157 else | |
| 158 /* Block is the front block in its bin, but there is no other | |
| 159 ** block in the bin. Eliminate the bin. */ | |
| 160 U(avl_remove)( | |
| 161 (U(avl_avl) *) & (desc->avl_tree_root), BLOCK_BAUS(head_ptr)); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 void U(free)(U(descriptor) *desc, void *payload_ptr) { | |
| 166 /* Flags if coalesce with adjacent block. */ | |
| 167 int coalesce; | |
| 168 | |
| 169 head_record *fwd_head_ptr; | |
| 170 head_record *free_head_ptr = PTR_REC_TO_HEAD(payload_ptr); | |
| 171 | |
| 172 desc->num_baus_can_shrink = 0; | |
| 173 | |
| 174 #ifdef HMM_AUDIT_FAIL | |
| 175 | |
| 176 AUDIT_BLOCK(free_head_ptr) | |
| 177 | |
| 178 /* Make sure not freeing an already free block. */ | |
| 179 if (!IS_BLOCK_ALLOCATED(free_head_ptr)) | |
| 180 HMM_AUDIT_FAIL | |
| 181 | |
| 182 if (desc->avl_tree_root) | |
| 183 /* Audit root block in AVL tree. */ | |
| 184 AUDIT_BLOCK(PTR_REC_TO_HEAD(desc->avl_tree_root)) | |
| 185 | |
| 186 #endif | |
| 187 | |
| 188 fwd_head_ptr = | |
| 189 (head_record *) BAUS_FORWARD(free_head_ptr, free_head_ptr->block_size); | |
| 190 | |
| 191 if (free_head_ptr->previous_block_size) { | |
| 192 /* Coalesce with backward block if possible. */ | |
| 193 | |
| 194 head_record *bkwd_head_ptr = | |
| 195 (head_record *) BAUS_BACKWARD( | |
| 196 free_head_ptr, free_head_ptr->previous_block_size); | |
| 197 | |
| 198 #ifdef HMM_AUDIT_FAIL | |
| 199 AUDIT_BLOCK(bkwd_head_ptr) | |
| 200 #endif | |
| 201 | |
| 202 if (bkwd_head_ptr == (head_record *)(desc->last_freed)) { | |
| 203 desc->last_freed = 0; | |
| 204 coalesce = 1; | |
| 205 } else if (IS_BLOCK_ALLOCATED(bkwd_head_ptr)) | |
| 206 coalesce = 0; | |
| 207 else { | |
| 208 U(out_of_free_collection)(desc, bkwd_head_ptr); | |
| 209 coalesce = 1; | |
| 210 } | |
| 211 | |
| 212 if (coalesce) { | |
| 213 bkwd_head_ptr->block_size += free_head_ptr->block_size; | |
| 214 SET_PREV_BLOCK_BAUS(fwd_head_ptr, BLOCK_BAUS(bkwd_head_ptr)) | |
| 215 free_head_ptr = bkwd_head_ptr; | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 if (fwd_head_ptr->block_size == 0) { | |
| 220 /* Block to be freed is last block before dummy end-of-chunk block. */ | |
| 221 desc->end_of_shrinkable_chunk = | |
| 222 BAUS_FORWARD(fwd_head_ptr, DUMMY_END_BLOCK_BAUS); | |
| 223 desc->num_baus_can_shrink = BLOCK_BAUS(free_head_ptr); | |
| 224 | |
| 225 if (PREV_BLOCK_BAUS(free_head_ptr) == 0) | |
| 226 /* Free block is the entire chunk, so shrinking can eliminate | |
| 227 ** entire chunk including dummy end block. */ | |
| 228 desc->num_baus_can_shrink += DUMMY_END_BLOCK_BAUS; | |
| 229 } else { | |
| 230 /* Coalesce with forward block if possible. */ | |
| 231 | |
| 232 #ifdef HMM_AUDIT_FAIL | |
| 233 AUDIT_BLOCK(fwd_head_ptr) | |
| 234 #endif | |
| 235 | |
| 236 if (fwd_head_ptr == (head_record *)(desc->last_freed)) { | |
| 237 desc->last_freed = 0; | |
| 238 coalesce = 1; | |
| 239 } else if (IS_BLOCK_ALLOCATED(fwd_head_ptr)) | |
| 240 coalesce = 0; | |
| 241 else { | |
| 242 U(out_of_free_collection)(desc, fwd_head_ptr); | |
| 243 coalesce = 1; | |
| 244 } | |
| 245 | |
| 246 if (coalesce) { | |
| 247 free_head_ptr->block_size += fwd_head_ptr->block_size; | |
| 248 | |
| 249 fwd_head_ptr = | |
| 250 (head_record *) BAUS_FORWARD( | |
| 251 fwd_head_ptr, BLOCK_BAUS(fwd_head_ptr)); | |
| 252 | |
| 253 SET_PREV_BLOCK_BAUS(fwd_head_ptr, BLOCK_BAUS(free_head_ptr)) | |
| 254 | |
| 255 if (fwd_head_ptr->block_size == 0) { | |
| 256 /* Coalesced block to be freed is last block before dummy | |
| 257 ** end-of-chunk block. */ | |
| 258 desc->end_of_shrinkable_chunk = | |
| 259 BAUS_FORWARD(fwd_head_ptr, DUMMY_END_BLOCK_BAUS); | |
| 260 desc->num_baus_can_shrink = BLOCK_BAUS(free_head_ptr); | |
| 261 | |
| 262 if (PREV_BLOCK_BAUS(free_head_ptr) == 0) | |
| 263 /* Free block is the entire chunk, so shrinking can | |
| 264 ** eliminate entire chunk including dummy end block. */ | |
| 265 desc->num_baus_can_shrink += DUMMY_END_BLOCK_BAUS; | |
| 266 } | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 if (desc->last_freed) { | |
| 271 /* There is a last freed block, but it is not adjacent to the | |
| 272 ** block being freed by this call to free, so put the last | |
| 273 ** freed block into the free collection. | |
| 274 */ | |
| 275 | |
| 276 #ifdef HMM_AUDIT_FAIL | |
| 277 AUDIT_BLOCK(desc->last_freed) | |
| 278 #endif | |
| 279 | |
| 280 U(into_free_collection)(desc, (head_record *)(desc->last_freed)); | |
| 281 } | |
| 282 | |
| 283 desc->last_freed = free_head_ptr; | |
| 284 } | |
| 285 | |
| 286 void U(new_chunk)(U(descriptor) *desc, void *start, U(size_bau) n_baus) { | |
| 287 #ifdef HMM_AUDIT_FAIL | |
| 288 | |
| 289 if (desc->avl_tree_root) | |
| 290 /* Audit root block in AVL tree. */ | |
| 291 AUDIT_BLOCK(PTR_REC_TO_HEAD(desc->avl_tree_root)) | |
| 292 #endif | |
| 293 | |
| 294 #undef HEAD_PTR | |
| 295 #define HEAD_PTR ((head_record *) start) | |
| 296 | |
| 297 /* Make the chunk one big free block followed by a dummy end block. | |
| 298 */ | |
| 299 | |
| 300 n_baus -= DUMMY_END_BLOCK_BAUS; | |
| 301 | |
| 302 HEAD_PTR->previous_block_size = 0; | |
| 303 HEAD_PTR->block_size = n_baus; | |
| 304 | |
| 305 U(into_free_collection)(desc, HEAD_PTR); | |
| 306 | |
| 307 /* Set up the dummy end block. */ | |
| 308 start = BAUS_FORWARD(start, n_baus); | |
| 309 HEAD_PTR->previous_block_size = n_baus; | |
| 310 HEAD_PTR->block_size = 0; | |
| 311 | |
| 312 #undef HEAD_PTR | |
| 313 } | |
| 314 | |
| 315 #ifdef HMM_AUDIT_FAIL | |
| 316 | |
| 317 /* Function that does audit fail actions defined my preprocessor symbol, | |
| 318 ** and returns a dummy integer value. | |
| 319 */ | |
| 320 int U(audit_block_fail_dummy_return)(void) { | |
| 321 HMM_AUDIT_FAIL | |
| 322 | |
| 323 /* Dummy return. */ | |
| 324 return(0); | |
| 325 } | |
| 326 | |
| 327 #endif | |
| 328 | |
| 329 /* AVL Tree instantiation. */ | |
| 330 | |
| 331 #ifdef HMM_AUDIT_FAIL | |
| 332 | |
| 333 /* The AVL tree generic package passes an ACCESS of 1 when it "touches" | |
| 334 ** a child node for the first time during a particular operation. I use | |
| 335 ** this feature to audit only one time (per operation) the free blocks | |
| 336 ** that are tree nodes. Since the root node is not a child node, it has | |
| 337 ** to be audited directly. | |
| 338 */ | |
| 339 | |
| 340 /* The pain you feel while reading these macros will not be in vain. It | |
| 341 ** will remove all doubt from you mind that C++ inline functions are | |
| 342 ** a very good thing. | |
| 343 */ | |
| 344 | |
| 345 #define AVL_GET_LESS(H, ACCESS) \ | |
| 346 (((ACCESS) ? AUDIT_BLOCK_AS_EXPR(PTR_REC_TO_HEAD(H)) : 0), (H)->self) | |
| 347 #define AVL_GET_GREATER(H, ACCESS) \ | |
| 348 (((ACCESS) ? AUDIT_BLOCK_AS_EXPR(PTR_REC_TO_HEAD(H)) : 0), (H)->prev) | |
| 349 | |
| 350 #else | |
| 351 | |
| 352 #define AVL_GET_LESS(H, ACCESS) ((H)->self) | |
| 353 #define AVL_GET_GREATER(H, ACCESS) ((H)->prev) | |
| 354 | |
| 355 #endif | |
| 356 | |
| 357 #define AVL_SET_LESS(H, LH) (H)->self = (LH); | |
| 358 #define AVL_SET_GREATER(H, GH) (H)->prev = (GH); | |
| 359 | |
| 360 /* high bit of high bit of | |
| 361 ** block_size previous_block_size balance factor | |
| 362 ** ----------- ------------------- -------------- | |
| 363 ** 0 0 n/a (block allocated) | |
| 364 ** 0 1 1 | |
| 365 ** 1 0 -1 | |
| 366 ** 1 1 0 | |
| 367 */ | |
| 368 | |
| 369 #define AVL_GET_BALANCE_FACTOR(H) \ | |
| 370 ((((head_record *) (PTR_REC_TO_HEAD(H)))->block_size & \ | |
| 371 HIGH_BIT_BAU_SIZE) ? \ | |
| 372 (((head_record *) (PTR_REC_TO_HEAD(H)))->previous_block_size & \ | |
| 373 HIGH_BIT_BAU_SIZE ? 0 : -1) : 1) | |
| 374 | |
| 375 #define AVL_SET_BALANCE_FACTOR(H, BF) \ | |
| 376 { \ | |
| 377 register head_record *p = \ | |
| 378 (head_record *) PTR_REC_TO_HEAD(H);
\ | |
| 379 register int bal_f = (BF); \ | |
| 380 \ | |
| 381 if (bal_f <= 0) \ | |
| 382 p->block_size |= HIGH_BIT_BAU_SIZE; \ | |
| 383 else \ | |
| 384 p->block_size &= ~HIGH_BIT_BAU_SIZE; \ | |
| 385 if (bal_f >= 0) \ | |
| 386 p->previous_block_size |= HIGH_BIT_BAU_SIZE; \ | |
| 387 else \ | |
| 388 p->previous_block_size &= ~HIGH_BIT_BAU_SIZE; \ | |
| 389 } | |
| 390 | |
| 391 #define COMPARE_KEY_KEY(K1, K2) ((K1) == (K2) ? 0 : ((K1) > (K2) ? 1 : -1)) | |
| 392 | |
| 393 #define AVL_COMPARE_KEY_NODE(K, H) \ | |
| 394 COMPARE_KEY_KEY(K, BLOCK_BAUS(PTR_REC_TO_HEAD(H))) | |
| 395 | |
| 396 #define AVL_COMPARE_NODE_NODE(H1, H2) \ | |
| 397 COMPARE_KEY_KEY(BLOCK_BAUS(PTR_REC_TO_HEAD(H1)), \ | |
| 398 BLOCK_BAUS(PTR_REC_TO_HEAD(H2))) | |
| 399 | |
| 400 #define AVL_NULL ((ptr_record *) 0) | |
| 401 | |
| 402 #define AVL_IMPL_MASK \ | |
| 403 ( AVL_IMPL_INSERT | AVL_IMPL_SEARCH | AVL_IMPL_REMOVE | AVL_IMPL_SUBST ) | |
| 404 | |
| 405 #include "cavl_impl.h" | |
| OLD | NEW |