1 // DataContainer.cpp 2 3 #include "AllocationInfo.h" 4 #include "Attribute.h" // for debugging only 5 #include "Block.h" 6 #include "DataContainer.h" 7 #include "Debug.h" 8 #include "Misc.h" 9 #include "Node.h" // for debugging only 10 #include "Volume.h" 11 12 // constructor 13 DataContainer::DataContainer(Volume *volume) 14 : fVolume(volume), 15 fSize(0) 16 { 17 } 18 19 // destructor 20 DataContainer::~DataContainer() 21 { 22 Resize(0); 23 } 24 25 // InitCheck 26 status_t 27 DataContainer::InitCheck() const 28 { 29 return (fVolume ? B_OK : B_ERROR); 30 } 31 32 // Resize 33 status_t 34 DataContainer::Resize(off_t newSize) 35 { 36 status_t error = B_OK; 37 if (newSize < 0) 38 newSize = 0; 39 if (newSize != fSize) { 40 // Shrinking should never fail. Growing can fail, if we run out of 41 // memory. Then we try to shrink back to the original size. 42 off_t oldSize = fSize; 43 error = _Resize(newSize); 44 if (error == B_NO_MEMORY && newSize > fSize) 45 _Resize(oldSize); 46 } 47 return error; 48 } 49 50 // ReadAt 51 status_t 52 DataContainer::ReadAt(off_t offset, void *_buffer, size_t size, 53 size_t *bytesRead) 54 { 55 uint8 *buffer = (uint8*)_buffer; 56 status_t error = (buffer && offset >= 0 && bytesRead ? B_OK : B_BAD_VALUE); 57 if (error == B_OK) { 58 // read not more than we have to offer 59 offset = min(offset, fSize); 60 size = min(size, size_t(fSize - offset)); 61 // iterate through the blocks, reading as long as there's something 62 // left to read 63 size_t blockSize = fVolume->GetBlockSize(); 64 *bytesRead = 0; 65 while (size > 0) { 66 size_t inBlockOffset = offset % blockSize; 67 size_t toRead = min(size, size_t(blockSize - inBlockOffset)); 68 void *blockData = _GetBlockDataAt(offset / blockSize, 69 inBlockOffset, toRead); 70 D( 71 if (!blockData) { 72 Node *node = NULL; 73 if (Attribute *attribute = dynamic_cast<Attribute*>(this)) { 74 FATAL(("attribute `%s' of\n", attribute->GetName())); 75 node = attribute->GetNode(); 76 } else { 77 node = dynamic_cast<Node*>(this); 78 } 79 if (node) 80 // FATAL(("node `%s'\n", node->GetName())); 81 FATAL(("container size: %Ld, offset: %Ld, buffer size: %lu\n", 82 fSize, offset, size)); 83 return B_ERROR; 84 } 85 ); 86 memcpy(buffer, blockData, toRead); 87 buffer += toRead; 88 size -= toRead; 89 offset += toRead; 90 *bytesRead += toRead; 91 } 92 } 93 return error; 94 } 95 96 // WriteAt 97 status_t 98 DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size, 99 size_t *bytesWritten) 100 { 101 //PRINT(("DataContainer::WriteAt(%Ld, %p, %lu, %p), fSize: %Ld\n", offset, _buffer, size, bytesWritten, fSize)); 102 const uint8 *buffer = (const uint8*)_buffer; 103 status_t error = (buffer && offset >= 0 && bytesWritten 104 ? B_OK : B_BAD_VALUE); 105 // resize the container, if necessary 106 if (error == B_OK) { 107 off_t newSize = offset + size; 108 off_t oldSize = fSize; 109 if (newSize > fSize) { 110 error = Resize(newSize); 111 // pad with zero, if necessary 112 if (error == B_OK && offset > oldSize) 113 _ClearArea(offset, oldSize - offset); 114 } 115 } 116 if (error == B_OK) { 117 // iterate through the blocks, writing as long as there's something 118 // left to write 119 size_t blockSize = fVolume->GetBlockSize(); 120 *bytesWritten = 0; 121 while (size > 0) { 122 size_t inBlockOffset = offset % blockSize; 123 size_t toWrite = min(size, size_t(blockSize - inBlockOffset)); 124 void *blockData = _GetBlockDataAt(offset / blockSize, 125 inBlockOffset, toWrite); 126 D(if (!blockData) return B_ERROR;); 127 memcpy(blockData, buffer, toWrite); 128 buffer += toWrite; 129 size -= toWrite; 130 offset += toWrite; 131 *bytesWritten += toWrite; 132 } 133 } 134 //PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize)); 135 return error; 136 } 137 138 // GetFirstDataBlock 139 void 140 DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length) 141 { 142 if (data && length) { 143 if (_IsBlockMode()) { 144 BlockReference *block = _GetBlockList()->ItemAt(0); 145 *data = (const uint8*)block->GetData(); 146 *length = min(fSize, fVolume->GetBlockSize()); 147 } else { 148 *data = fSmallBuffer; 149 *length = fSize; 150 } 151 } 152 } 153 154 // GetAllocationInfo 155 void 156 DataContainer::GetAllocationInfo(AllocationInfo &info) 157 { 158 if (_IsBlockMode()) { 159 BlockList *blocks = _GetBlockList(); 160 info.AddListAllocation(blocks->GetCapacity(), sizeof(BlockReference*)); 161 int32 blockCount = blocks->CountItems(); 162 for (int32 i = 0; i < blockCount; i++) 163 info.AddBlockAllocation(blocks->ItemAt(i)->GetBlock()->GetSize()); 164 } else { 165 // ... 166 } 167 } 168 169 // _RequiresBlockMode 170 inline 171 bool 172 DataContainer::_RequiresBlockMode(size_t size) 173 { 174 return (size > kSmallDataContainerSize); 175 } 176 177 // _IsBlockMode 178 inline 179 bool 180 DataContainer::_IsBlockMode() const 181 { 182 return (fSize > kSmallDataContainerSize); 183 } 184 185 // _Resize 186 status_t 187 DataContainer::_Resize(off_t newSize) 188 { 189 //PRINT(("DataContainer::_Resize(%Ld), fSize: %Ld\n", newSize, fSize)); 190 status_t error = B_OK; 191 if (newSize != fSize) { 192 size_t blockSize = fVolume->GetBlockSize(); 193 int32 blockCount = _CountBlocks(); 194 int32 newBlockCount = (newSize + blockSize - 1) / blockSize; 195 if (newBlockCount == blockCount) { 196 // only the last block needs to be resized 197 if (_IsBlockMode() && _RequiresBlockMode(newSize)) { 198 // keep block mode 199 error = _ResizeLastBlock((newSize - 1) % blockSize + 1); 200 } else if (!_IsBlockMode() && !_RequiresBlockMode(newSize)) { 201 // keep small buffer mode 202 fSize = newSize; 203 } else if (fSize < newSize) { 204 // switch to block mode 205 _SwitchToBlockMode(newSize); 206 } else { 207 // switch to small buffer mode 208 _SwitchToSmallBufferMode(newSize); 209 } 210 } else if (newBlockCount < blockCount) { 211 // shrink 212 if (_IsBlockMode()) { 213 // remove the last blocks 214 BlockList *blocks = _GetBlockList(); 215 for (int32 i = blockCount - 1; i >= newBlockCount; i--) { 216 BlockReference *block = blocks->ItemAt(i); 217 blocks->RemoveItem(i); 218 fVolume->FreeBlock(block); 219 fSize = (fSize - 1) / blockSize * blockSize; 220 } 221 // resize the last block to the correct size, respectively 222 // switch to small buffer mode 223 if (_RequiresBlockMode(newSize)) 224 error = _ResizeLastBlock((newSize - 1) % blockSize + 1); 225 else 226 _SwitchToSmallBufferMode(newSize); 227 } else { 228 // small buffer mode: just set the new size 229 fSize = newSize; 230 } 231 } else { 232 // grow 233 if (_RequiresBlockMode(newSize)) { 234 // resize the first block to the correct size, respectively 235 // switch to block mode 236 if (_IsBlockMode()) 237 error = _ResizeLastBlock(blockSize); 238 else { 239 error = _SwitchToBlockMode(min((size_t)newSize, 240 blockSize)); 241 } 242 // add new blocks 243 BlockList *blocks = _GetBlockList(); 244 while (error == B_OK && fSize < newSize) { 245 size_t newBlockSize = min(size_t(newSize - fSize), 246 blockSize); 247 BlockReference *block = NULL; 248 error = fVolume->AllocateBlock(newBlockSize, &block); 249 if (error == B_OK) { 250 if (blocks->AddItem(block)) 251 fSize += newBlockSize; 252 else { 253 SET_ERROR(error, B_NO_MEMORY); 254 fVolume->FreeBlock(block); 255 } 256 } 257 } 258 } else { 259 // no need to switch to block mode: just set the new size 260 fSize = newSize; 261 } 262 } 263 } 264 //PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize)); 265 return error; 266 } 267 268 // _GetBlockList 269 inline 270 DataContainer::BlockList * 271 DataContainer::_GetBlockList() 272 { 273 return (BlockList*)fBlocks; 274 } 275 276 // _GetBlockList 277 inline 278 const DataContainer::BlockList * 279 DataContainer::_GetBlockList() const 280 { 281 return (BlockList*)fBlocks; 282 } 283 284 // _CountBlocks 285 inline 286 int32 287 DataContainer::_CountBlocks() const 288 { 289 if (_IsBlockMode()) 290 return _GetBlockList()->CountItems(); 291 else if (fSize == 0) // small buffer mode, empty buffer 292 return 0; 293 return 1; // small buffer mode, non-empty buffer 294 } 295 296 // _GetBlockDataAt 297 inline 298 void * 299 DataContainer::_GetBlockDataAt(int32 index, size_t offset, size_t DARG(size)) 300 { 301 if (_IsBlockMode()) { 302 BlockReference *block = _GetBlockList()->ItemAt(index); 303 D(if (!fVolume->CheckBlock(block, offset + size)) return NULL;); 304 return block->GetDataAt(offset); 305 } else { 306 D( 307 if (offset + size > kSmallDataContainerSize) { 308 FATAL(("DataContainer: Data access exceeds small buffer.\n")); 309 PANIC("DataContainer: Data access exceeds small buffer."); 310 return NULL; 311 } 312 ); 313 return fSmallBuffer + offset; 314 } 315 } 316 317 // _ClearArea 318 void 319 DataContainer::_ClearArea(off_t offset, off_t size) 320 { 321 // constrain the area to the data area 322 offset = min(offset, fSize); 323 size = min(size, fSize - offset); 324 // iterate through the blocks, clearing as long as there's something 325 // left to clear 326 size_t blockSize = fVolume->GetBlockSize(); 327 while (size > 0) { 328 size_t inBlockOffset = offset % blockSize; 329 size_t toClear = min(size_t(size), blockSize - inBlockOffset); 330 void *blockData = _GetBlockDataAt(offset / blockSize, inBlockOffset, 331 toClear); 332 D(if (!blockData) return;); 333 memset(blockData, 0, toClear); 334 size -= toClear; 335 offset += toClear; 336 } 337 } 338 339 // _ResizeLastBlock 340 status_t 341 DataContainer::_ResizeLastBlock(size_t newSize) 342 { 343 //PRINT(("DataContainer::_ResizeLastBlock(%lu), fSize: %Ld\n", newSize, fSize)); 344 int32 blockCount = _CountBlocks(); 345 status_t error = (fSize > 0 && blockCount > 0 && newSize > 0 346 ? B_OK : B_BAD_VALUE); 347 D( 348 if (!_IsBlockMode()) { 349 FATAL(("Call of _ResizeLastBlock() in small buffer mode.\n")); 350 PANIC("Call of _ResizeLastBlock() in small buffer mode."); 351 return B_ERROR; 352 } 353 ); 354 if (error == B_OK) { 355 size_t blockSize = fVolume->GetBlockSize(); 356 size_t oldSize = (fSize - 1) % blockSize + 1; 357 if (newSize != oldSize) { 358 BlockList *blocks = _GetBlockList(); 359 BlockReference *block = blocks->ItemAt(blockCount - 1); 360 BlockReference *newBlock = fVolume->ResizeBlock(block, newSize); 361 if (newBlock) { 362 if (newBlock != block) 363 blocks->ReplaceItem(blockCount - 1, newBlock); 364 fSize += off_t(newSize) - oldSize; 365 } else 366 SET_ERROR(error, B_NO_MEMORY); 367 } 368 } 369 //PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize)); 370 return error; 371 } 372 373 // _SwitchToBlockMode 374 status_t 375 DataContainer::_SwitchToBlockMode(size_t newBlockSize) 376 { 377 // allocate a new block 378 BlockReference *block = NULL; 379 status_t error = fVolume->AllocateBlock(newBlockSize, &block); 380 if (error == B_OK) { 381 // copy the data from the small buffer into the block 382 if (fSize > 0) 383 memcpy(block->GetData(), fSmallBuffer, fSize); 384 // construct the block list and add the block 385 new (fBlocks) BlockList(10); 386 BlockList *blocks = _GetBlockList(); 387 if (blocks->AddItem(block)) { 388 fSize = newBlockSize; 389 } else { 390 // error: destroy the block list and free the block 391 SET_ERROR(error, B_NO_MEMORY); 392 blocks->~BlockList(); 393 if (fSize > 0) 394 memcpy(fSmallBuffer, block->GetData(), fSize); 395 fVolume->FreeBlock(block); 396 } 397 } 398 return error; 399 } 400 401 // _SwitchToSmallBufferMode 402 void 403 DataContainer::_SwitchToSmallBufferMode(size_t newSize) 404 { 405 // remove the first (and only) block 406 BlockList *blocks = _GetBlockList(); 407 BlockReference *block = blocks->ItemAt(0); 408 blocks->RemoveItem(0L); 409 // destroy the block list and copy the data into the small buffer 410 blocks->~BlockList(); 411 if (newSize > 0) 412 memcpy(fSmallBuffer, block->GetData(), newSize); 413 // free the block and set the new size 414 fVolume->FreeBlock(block); 415 fSize = newSize; 416 } 417 418