| 1136 | } |
| 1137 | |
| 1138 | bool BTreeDatabase::tryFlatten() { |
| 1139 | if (m_headFreeIndexBlock == InvalidBlockIndex || m_rootIsLeaf || !m_device->isWritable()) |
| 1140 | return false; |
| 1141 | |
| 1142 | BlockIndex freeBlockCount = 0; |
| 1143 | BlockIndex indexBlockIndex = m_headFreeIndexBlock; |
| 1144 | while (indexBlockIndex != InvalidBlockIndex) { |
| 1145 | FreeIndexBlock indexBlock = readFreeIndexBlock(indexBlockIndex); |
| 1146 | freeBlockCount += 1 + indexBlock.freeBlocks.size(); |
| 1147 | indexBlockIndex = indexBlock.nextFreeBlock; |
| 1148 | } |
| 1149 | |
| 1150 | BlockIndex expectedBlockCount = (m_deviceSize - HeaderSize) / m_blockSize; |
| 1151 | float free = float(freeBlockCount) / float(expectedBlockCount); |
| 1152 | if (free < 0.05f) |
| 1153 | return false; |
| 1154 | |
| 1155 | Logger::info("[BTreeDatabase] File '{}' is {:.2f}% free space, flattening", m_device->deviceName(), free * 100.f); |
| 1156 | |
| 1157 | indexBlockIndex = m_headFreeIndexBlock; |
| 1158 | { |
| 1159 | List<BlockIndex> availableBlocksList; |
| 1160 | do { |
| 1161 | FreeIndexBlock indexBlock = readFreeIndexBlock(indexBlockIndex); |
| 1162 | availableBlocksList.appendAll(indexBlock.freeBlocks); |
| 1163 | availableBlocksList.append(indexBlockIndex); |
| 1164 | indexBlockIndex = indexBlock.nextFreeBlock; |
| 1165 | } while (indexBlockIndex != InvalidBlockIndex); |
| 1166 | m_headFreeIndexBlock = InvalidBlockIndex; |
| 1167 | |
| 1168 | sort(availableBlocksList); |
| 1169 | for (auto& availableBlock : availableBlocksList) |
| 1170 | m_availableBlocks.insert(m_availableBlocks.end(), availableBlock); |
| 1171 | } |
| 1172 | |
| 1173 | BlockIndex count = 1; // 1 to include root index |
| 1174 | |
| 1175 | double start = Time::monotonicTime(); |
| 1176 | auto index = m_impl.loadIndex(m_impl.rootPointer()); |
| 1177 | if (flattenVisitor(index, count)) { |
| 1178 | m_impl.deleteIndex(index); |
| 1179 | index->self = InvalidBlockIndex; |
| 1180 | m_root = m_impl.storeIndex(index); |
| 1181 | } |
| 1182 | |
| 1183 | m_availableBlocks.clear(); |
| 1184 | m_device->resize(m_deviceSize = HeaderSize + (StreamOffset)m_blockSize * count); |
| 1185 | |
| 1186 | m_indexCache.clear(); |
| 1187 | commitWrites(); |
| 1188 | writeRoot(); |
| 1189 | m_uncommitted.clear(); |
| 1190 | |
| 1191 | Logger::info("[BTreeDatabase] Finished flattening '{}' in {:.2f} milliseconds", m_device->deviceName(), (Time::monotonicTime() - start) * 1000.f); |
| 1192 | return true; |
| 1193 | } |
| 1194 | |
| 1195 | bool BTreeDatabase::flattenVisitor(BTreeImpl::Index& index, BlockIndex& count) { |
nothing calls this directly
no test coverage detected