3 #include "AllocationInfo.h"
4 #include "Attribute.h" // for debugging only
6 #include "DataContainer.h"
9 #include "Node.h" // for debugging only
13 DataContainer::DataContainer(Volume
*volume
)
20 DataContainer::~DataContainer()
27 DataContainer::InitCheck() const
29 return (fVolume
? B_OK
: B_ERROR
);
34 DataContainer::Resize(off_t newSize
)
36 status_t error
= B_OK
;
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
)
52 DataContainer::ReadAt(off_t offset
, void *_buffer
, size_t size
,
55 uint8
*buffer
= (uint8
*)_buffer
;
56 status_t error
= (buffer
&& offset
>= 0 && bytesRead
? B_OK
: B_BAD_VALUE
);
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
63 size_t blockSize
= fVolume
->GetBlockSize();
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
);
73 if (Attribute
*attribute
= dynamic_cast<Attribute
*>(this)) {
74 FATAL(("attribute `%s' of\n", attribute
->GetName()));
75 node
= attribute
->GetNode();
77 node
= dynamic_cast<Node
*>(this);
80 // FATAL(("node `%s'\n", node->GetName()));
81 FATAL(("container size: %Ld, offset: %Ld, buffer size: %lu\n",
82 fSize
, offset
, size
));
86 memcpy(buffer
, blockData
, toRead
);
98 DataContainer::WriteAt(off_t offset
, const void *_buffer
, size_t size
,
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
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
);
117 // iterate through the blocks, writing as long as there's something
119 size_t blockSize
= fVolume
->GetBlockSize();
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
);
131 *bytesWritten
+= toWrite
;
134 //PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize));
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());
148 *data
= fSmallBuffer
;
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());
169 // _RequiresBlockMode
172 DataContainer::_RequiresBlockMode(size_t size
)
174 return (size
> kSmallDataContainerSize
);
180 DataContainer::_IsBlockMode() const
182 return (fSize
> kSmallDataContainerSize
);
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
)) {
199 error
= _ResizeLastBlock((newSize
- 1) % blockSize
+ 1);
200 } else if (!_IsBlockMode() && !_RequiresBlockMode(newSize
)) {
201 // keep small buffer mode
203 } else if (fSize
< newSize
) {
204 // switch to block mode
205 _SwitchToBlockMode(newSize
);
207 // switch to small buffer mode
208 _SwitchToSmallBufferMode(newSize
);
210 } else if (newBlockCount
< blockCount
) {
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);
226 _SwitchToSmallBufferMode(newSize
);
228 // small buffer mode: just set the new size
233 if (_RequiresBlockMode(newSize
)) {
234 // resize the first block to the correct size, respectively
235 // switch to block mode
237 error
= _ResizeLastBlock(blockSize
);
239 error
= _SwitchToBlockMode(min((size_t)newSize
,
243 BlockList
*blocks
= _GetBlockList();
244 while (error
== B_OK
&& fSize
< newSize
) {
245 size_t newBlockSize
= min(size_t(newSize
- fSize
),
247 BlockReference
*block
= NULL
;
248 error
= fVolume
->AllocateBlock(newBlockSize
, &block
);
250 if (blocks
->AddItem(block
))
251 fSize
+= newBlockSize
;
253 SET_ERROR(error
, B_NO_MEMORY
);
254 fVolume
->FreeBlock(block
);
259 // no need to switch to block mode: just set the new size
264 //PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize));
270 DataContainer::BlockList
*
271 DataContainer::_GetBlockList()
273 return (BlockList
*)fBlocks
;
278 const DataContainer::BlockList
*
279 DataContainer::_GetBlockList() const
281 return (BlockList
*)fBlocks
;
287 DataContainer::_CountBlocks() const
290 return _GetBlockList()->CountItems();
291 else if (fSize
== 0) // small buffer mode, empty buffer
293 return 1; // small buffer mode, non-empty buffer
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
);
307 if (offset
+ size
> kSmallDataContainerSize
) {
308 FATAL(("DataContainer: Data access exceeds small buffer.\n"));
309 PANIC("DataContainer: Data access exceeds small buffer.");
313 return fSmallBuffer
+ offset
;
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
326 size_t blockSize
= fVolume
->GetBlockSize();
328 size_t inBlockOffset
= offset
% blockSize
;
329 size_t toClear
= min(size_t(size
), blockSize
- inBlockOffset
);
330 void *blockData
= _GetBlockDataAt(offset
/ blockSize
, inBlockOffset
,
332 D(if (!blockData
) return;);
333 memset(blockData
, 0, toClear
);
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.");
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
);
362 if (newBlock
!= block
)
363 blocks
->ReplaceItem(blockCount
- 1, newBlock
);
364 fSize
+= off_t(newSize
) - oldSize
;
366 SET_ERROR(error
, B_NO_MEMORY
);
369 //PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize));
373 // _SwitchToBlockMode
375 DataContainer::_SwitchToBlockMode(size_t newBlockSize
)
377 // allocate a new block
378 BlockReference
*block
= NULL
;
379 status_t error
= fVolume
->AllocateBlock(newBlockSize
, &block
);
381 // copy the data from the small buffer into the block
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
;
390 // error: destroy the block list and free the block
391 SET_ERROR(error
, B_NO_MEMORY
);
392 blocks
->~BlockList();
394 memcpy(fSmallBuffer
, block
->GetData(), fSize
);
395 fVolume
->FreeBlock(block
);
401 // _SwitchToSmallBufferMode
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();
412 memcpy(fSmallBuffer
, block
->GetData(), newSize
);
413 // free the block and set the new size
414 fVolume
->FreeBlock(block
);