1 /* 2 * Memory quantity calculation utilities. 3 * 4 * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include "memory_utils.h" 23 24 25 26 /* Return page 'n' for the configured page size. */ 27 28 offset_t page(unsigned int n) 29 { 30 return PAGE_SIZE * n; 31 } 32 33 /* Return the order of 'size', where 2 ** order yields the size. */ 34 35 unsigned int page_order(offset_t size) 36 { 37 /* Count zeros from the left, stopping at the first set bit, using the width 38 of the size value (in bits, starting with the width in bytes) to 39 calculate the position of this bit and thus the order of the value. */ 40 41 return sizeof(unsigned long) * 8 - 1 - __builtin_clzl(size); 42 } 43 44 /* Return 'value' rounded up to the nearest 'increment'. */ 45 46 offset_t round(offset_t value, offset_t increment) 47 { 48 return trunc(value + increment - 1, increment); 49 } 50 51 /* Return 'value' rounded up to the nearest multiple of 'increment'. */ 52 53 offset_t round_multiple(offset_t value, offset_t increment) 54 { 55 offset_t last = increment; 56 57 while (1) 58 { 59 if (value < increment) 60 return round(value, last); 61 62 last = increment; 63 increment *= 2; 64 } 65 } 66 67 /* Return 'value' rounded down (or truncated) to the nearest 'increment'. */ 68 69 offset_t trunc(offset_t value, offset_t increment) 70 { 71 return (value / increment) * increment; 72 } 73 74 /* Return 'value' rounded down (or truncated) to the nearest multiple of 75 'increment'. */ 76 77 offset_t trunc_multiple(offset_t value, offset_t increment) 78 { 79 offset_t last = increment; 80 81 while (1) 82 { 83 if (value < increment) 84 return trunc(value, last); 85 86 last = increment; 87 increment *= 2; 88 } 89 } 90 91 /* Find the maximum size aligned region within the region from 'start' to (but 92 not including) 'end', with the given initial 'increment'. */ 93 94 offset_t max_multiple(offset_t start, offset_t end, offset_t increment) 95 { 96 /* The largest possible aligned region is derived from the region size. */ 97 98 offset_t size = trunc_multiple(end - start, increment); 99 100 /* Apply the alignment to the start. */ 101 102 offset_t aligned = round(start, size); 103 104 /* If the region is aligned, return the size. */ 105 106 if (aligned == start) 107 return size; 108 109 /* If the region is not aligned to the current size, recalculate the aligned 110 size. */ 111 112 offset_t aligned_size; 113 114 do 115 { 116 aligned_size = trunc_multiple(end - aligned, increment); 117 size /= 2; 118 119 /* Determine whether a smaller alignment could yield a larger aligned 120 size. */ 121 122 if (aligned_size >= size) 123 return aligned_size; 124 125 aligned = round(start, size); 126 } 127 while (aligned > start); 128 129 return aligned_size; 130 } 131 132 // vim: tabstop=4 expandtab shiftwidth=4