BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / file_systems / ramfs / DataContainer.cpp
blob37e6f4d6ee1b857e44be29c1a61f621a2f3aae61
1 // DataContainer.cpp
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"
12 // constructor
13 DataContainer::DataContainer(Volume *volume)
14 : fVolume(volume),
15 fSize(0)
19 // destructor
20 DataContainer::~DataContainer()
22 Resize(0);
25 // InitCheck
26 status_t
27 DataContainer::InitCheck() const
29 return (fVolume ? B_OK : B_ERROR);
32 // Resize
33 status_t
34 DataContainer::Resize(off_t newSize)
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);
47 return error;
50 // ReadAt
51 status_t
52 DataContainer::ReadAt(off_t offset, void *_buffer, size_t size,
53 size_t *bytesRead)
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);
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);
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;
86 memcpy(buffer, blockData, toRead);
87 buffer += toRead;
88 size -= toRead;
89 offset += toRead;
90 *bytesRead += toRead;
93 return error;
96 // WriteAt
97 status_t
98 DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size,
99 size_t *bytesWritten)
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);
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;
134 //PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize));
135 return error;
138 // GetFirstDataBlock
139 void
140 DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length)
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;
154 // GetAllocationInfo
155 void
156 DataContainer::GetAllocationInfo(AllocationInfo &info)
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 // ...
169 // _RequiresBlockMode
170 inline
171 bool
172 DataContainer::_RequiresBlockMode(size_t size)
174 return (size > kSmallDataContainerSize);
177 // _IsBlockMode
178 inline
179 bool
180 DataContainer::_IsBlockMode() const
182 return (fSize > kSmallDataContainerSize);
185 // _Resize
186 status_t
187 DataContainer::_Resize(off_t newSize)
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);
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;
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;
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));
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);
258 } else {
259 // no need to switch to block mode: just set the new size
260 fSize = newSize;
264 //PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize));
265 return error;
268 // _GetBlockList
269 inline
270 DataContainer::BlockList *
271 DataContainer::_GetBlockList()
273 return (BlockList*)fBlocks;
276 // _GetBlockList
277 inline
278 const DataContainer::BlockList *
279 DataContainer::_GetBlockList() const
281 return (BlockList*)fBlocks;
284 // _CountBlocks
285 inline
286 int32
287 DataContainer::_CountBlocks() const
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
296 // _GetBlockDataAt
297 inline
298 void *
299 DataContainer::_GetBlockDataAt(int32 index, size_t offset, size_t DARG(size))
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 {
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;
313 return fSmallBuffer + offset;
317 // _ClearArea
318 void
319 DataContainer::_ClearArea(off_t offset, off_t size)
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;
339 // _ResizeLastBlock
340 status_t
341 DataContainer::_ResizeLastBlock(size_t newSize)
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);
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;
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);
369 //PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize));
370 return error;
373 // _SwitchToBlockMode
374 status_t
375 DataContainer::_SwitchToBlockMode(size_t newBlockSize)
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);
398 return error;
401 // _SwitchToSmallBufferMode
402 void
403 DataContainer::_SwitchToSmallBufferMode(size_t newSize)
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;