| 1 | /* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. |
| 2 | |
| 3 | This program is free software; you can redistribute it and/or modify |
| 4 | it under the terms of the GNU General Public License as published by |
| 5 | the Free Software Foundation; version 2 of the License. |
| 6 | |
| 7 | This program is distributed in the hope that it will be useful, |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | GNU General Public License for more details. |
| 11 | |
| 12 | You should have received a copy of the GNU General Public License |
| 13 | along with this program; if not, write to the Free Software |
| 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 15 | |
| 16 | /* functions on blocks; Keys and records are saved in blocks */ |
| 17 | |
| 18 | #include "heapdef.h" |
| 19 | |
| 20 | /* |
| 21 | Find record according to record-position. |
| 22 | |
| 23 | The record is located by factoring position number pos into (p_0, p_1, ...) |
| 24 | such that |
| 25 | pos = SUM_i(block->level_info[i].records_under_level * p_i) |
| 26 | {p_0, p_1, ...} serve as indexes to descend the blocks tree. |
| 27 | */ |
| 28 | |
| 29 | uchar *hp_find_block(HP_BLOCK *block, ulong pos) |
| 30 | { |
| 31 | reg1 int i; |
| 32 | reg3 HP_PTRS *ptr; /* block base ptr */ |
| 33 | |
| 34 | for (i=block->levels-1, ptr=block->root ; i > 0 ; i--) |
| 35 | { |
| 36 | ptr=(HP_PTRS*)ptr->blocks[pos/block->level_info[i].records_under_level]; |
| 37 | pos%=block->level_info[i].records_under_level; |
| 38 | } |
| 39 | return (uchar*) ptr+ pos*block->recbuffer; |
| 40 | } |
| 41 | |
| 42 | |
| 43 | /* |
| 44 | Get one new block-of-records. Alloc ptr to block if needed |
| 45 | |
| 46 | SYNOPSIS |
| 47 | hp_get_new_block() |
| 48 | info heap handle |
| 49 | block HP_BLOCK tree-like block |
| 50 | alloc_length OUT Amount of memory allocated from the heap |
| 51 | |
| 52 | Interrupts are stopped to allow ha_panic in interrupts |
| 53 | RETURN |
| 54 | 0 OK |
| 55 | 1 Out of memory |
| 56 | */ |
| 57 | |
| 58 | int hp_get_new_block(HP_SHARE *info, HP_BLOCK *block, size_t *alloc_length) |
| 59 | { |
| 60 | reg1 uint i,j; |
| 61 | HP_PTRS *root; |
| 62 | |
| 63 | for (i=0 ; i < block->levels ; i++) |
| 64 | if (block->level_info[i].free_ptrs_in_block) |
| 65 | break; |
| 66 | |
| 67 | /* |
| 68 | Allocate space for leaf block (data) plus space for upper level blocks |
| 69 | up to first level that has a free slot to put the pointer. |
| 70 | If this is a new level, we have to allocate pointers to all future |
| 71 | lower levels. |
| 72 | |
| 73 | For example, for level 0, we allocate data for X rows. |
| 74 | When level 0 is full, we allocate data for HP_PTRS_IN_NOD + X rows. |
| 75 | Next time we allocate data for X rows. |
| 76 | When level 1 is full, we allocate data for HP_PTRS_IN_NOD at level 2 and 1 |
| 77 | + X rows at level 0. |
| 78 | */ |
| 79 | *alloc_length= (sizeof(HP_PTRS) * ((i == block->levels) ? i : i - 1) + |
| 80 | (ulonglong)block->records_in_block * block->recbuffer); |
| 81 | if (!(root=(HP_PTRS*) my_malloc(*alloc_length, |
| 82 | MYF(MY_WME | |
| 83 | (info->internal ? |
| 84 | MY_THREAD_SPECIFIC : 0))))) |
| 85 | return 1; |
| 86 | |
| 87 | if (i == 0) |
| 88 | { |
| 89 | block->levels=1; |
| 90 | block->root=block->level_info[0].last_blocks=root; |
| 91 | } |
| 92 | else |
| 93 | { |
| 94 | if ((uint) i == block->levels) |
| 95 | { |
| 96 | /* Adding a new level on top of the existing ones. */ |
| 97 | block->levels=i+1; |
| 98 | /* |
| 99 | Use first allocated HP_PTRS as a top-level block. Put the current |
| 100 | block tree into the first slot of a new top-level block. |
| 101 | */ |
| 102 | block->level_info[i].free_ptrs_in_block=HP_PTRS_IN_NOD-1; |
| 103 | ((HP_PTRS**) root)[0]= block->root; |
| 104 | block->root=block->level_info[i].last_blocks= root++; |
| 105 | } |
| 106 | /* Occupy the free slot we've found at level i */ |
| 107 | block->level_info[i].last_blocks-> |
| 108 | blocks[HP_PTRS_IN_NOD - block->level_info[i].free_ptrs_in_block--]= |
| 109 | (uchar*) root; |
| 110 | |
| 111 | /* Add a block subtree with each node having one left-most child */ |
| 112 | for (j=i-1 ; j >0 ; j--) |
| 113 | { |
| 114 | block->level_info[j].last_blocks= root++; |
| 115 | block->level_info[j].last_blocks->blocks[0]=(uchar*) root; |
| 116 | block->level_info[j].free_ptrs_in_block=HP_PTRS_IN_NOD-1; |
| 117 | } |
| 118 | |
| 119 | /* |
| 120 | root now points to last (block->records_in_block* block->recbuffer) |
| 121 | allocated bytes. Use it as a leaf block. |
| 122 | */ |
| 123 | block->level_info[0].last_blocks= root; |
| 124 | } |
| 125 | return 0; |
| 126 | } |
| 127 | |
| 128 | |
| 129 | /* free all blocks under level */ |
| 130 | |
| 131 | uchar *hp_free_level(HP_BLOCK *block, uint level, HP_PTRS *pos, uchar *last_pos) |
| 132 | { |
| 133 | int i,max_pos; |
| 134 | uchar *next_ptr; |
| 135 | |
| 136 | if (level == 1) |
| 137 | next_ptr=(uchar*) pos+block->recbuffer; |
| 138 | else |
| 139 | { |
| 140 | max_pos= (block->level_info[level-1].last_blocks == pos) ? |
| 141 | HP_PTRS_IN_NOD - block->level_info[level-1].free_ptrs_in_block : |
| 142 | HP_PTRS_IN_NOD; |
| 143 | |
| 144 | next_ptr=(uchar*) (pos+1); |
| 145 | for (i=0 ; i < max_pos ; i++) |
| 146 | next_ptr=hp_free_level(block,level-1, |
| 147 | (HP_PTRS*) pos->blocks[i],next_ptr); |
| 148 | } |
| 149 | if ((uchar*) pos != last_pos) |
| 150 | { |
| 151 | my_free(pos); |
| 152 | return last_pos; |
| 153 | } |
| 154 | return next_ptr; /* next memory position */ |
| 155 | } |
| 156 | |