* Added new interface BufferCache -- basically a memory allocator with the
option to cache a freed buffer -- and implementations
BlockBufferCache{NoLock,Kernel}.
* ZlibPackageDataReader does now dynamically get its read and uncompress
buffers from a provided BufferCache when needed.
* Allocating the buffers once and keeping them over the whole life time was a
bit too memory heavy, since we create a reader for every file for which a
vnode is created. A FS module global factory provides a buffer cache.
* Added a mutex to PackageFile::DataAccessor which guards the access to the
data reader which isn't thread safe.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34115 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "BlockBufferCacheKernel.h"
|
||||||
|
|
||||||
|
|
||||||
|
BlockBufferCacheKernel::BlockBufferCacheKernel(size_t blockSize,
|
||||||
|
uint32 maxCachedBlocks)
|
||||||
|
:
|
||||||
|
BlockBufferCache(blockSize, maxCachedBlocks)
|
||||||
|
{
|
||||||
|
mutex_init(&fLock, "BlockBufferCache");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockBufferCacheKernel::~BlockBufferCacheKernel()
|
||||||
|
{
|
||||||
|
mutex_destroy(&fLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BlockBufferCacheKernel::Lock()
|
||||||
|
{
|
||||||
|
mutex_lock(&fLock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BlockBufferCacheKernel::Unlock()
|
||||||
|
{
|
||||||
|
mutex_unlock(&fLock);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef BLOCK_BUFFER_CACHE_KERNEL_H
|
||||||
|
#define BLOCK_BUFFER_CACHE_KERNEL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include "BlockBufferCache.h"
|
||||||
|
|
||||||
|
|
||||||
|
class BlockBufferCacheKernel : public BlockBufferCache {
|
||||||
|
public:
|
||||||
|
BlockBufferCacheKernel(size_t blockSize,
|
||||||
|
uint32 maxCachedBlocks);
|
||||||
|
virtual ~BlockBufferCacheKernel();
|
||||||
|
|
||||||
|
virtual bool Lock();
|
||||||
|
virtual void Unlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutex fLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BLOCK_BUFFER_CACHE_KERNEL_H
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "GlobalFactory.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <haiku_package.h>
|
||||||
|
|
||||||
|
|
||||||
|
static const uint32 kMaxCachedBuffers = 32;
|
||||||
|
|
||||||
|
/*static*/ GlobalFactory* GlobalFactory::sDefaultInstance = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
GlobalFactory::GlobalFactory()
|
||||||
|
:
|
||||||
|
fBufferCache(B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, kMaxCachedBuffers),
|
||||||
|
fPackageDataReaderFactory(&fBufferCache)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GlobalFactory::~GlobalFactory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ status_t
|
||||||
|
GlobalFactory::CreateDefault()
|
||||||
|
{
|
||||||
|
if (sDefaultInstance != NULL)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
GlobalFactory* factory = new(std::nothrow) GlobalFactory;
|
||||||
|
if (factory == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t error = factory->_Init();
|
||||||
|
if (error != B_OK) {
|
||||||
|
delete factory;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
sDefaultInstance = factory;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
GlobalFactory::DeleteDefault()
|
||||||
|
{
|
||||||
|
delete sDefaultInstance;
|
||||||
|
sDefaultInstance = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ GlobalFactory*
|
||||||
|
GlobalFactory::Default()
|
||||||
|
{
|
||||||
|
return sDefaultInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
GlobalFactory::CreatePackageDataReader(DataReader* dataReader,
|
||||||
|
const PackageData& data, PackageDataReader*& _reader)
|
||||||
|
{
|
||||||
|
return fPackageDataReaderFactory.CreatePackageDataReader(dataReader, data,
|
||||||
|
_reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
GlobalFactory::_Init()
|
||||||
|
{
|
||||||
|
status_t error = fBufferCache.Init();
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef GLOBAL_FACTORY_H
|
||||||
|
#define GLOBAL_FACTORY_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "BlockBufferCacheKernel.h"
|
||||||
|
#include "PackageDataReader.h"
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalFactory {
|
||||||
|
private:
|
||||||
|
GlobalFactory();
|
||||||
|
~GlobalFactory();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static status_t CreateDefault();
|
||||||
|
static void DeleteDefault();
|
||||||
|
static GlobalFactory* Default();
|
||||||
|
|
||||||
|
status_t CreatePackageDataReader(DataReader* dataReader,
|
||||||
|
const PackageData& data,
|
||||||
|
PackageDataReader*& _reader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _Init();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static GlobalFactory* sDefaultInstance;
|
||||||
|
|
||||||
|
BlockBufferCacheKernel fBufferCache;
|
||||||
|
PackageDataReaderFactory fPackageDataReaderFactory;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLOBAL_FACTORY_H
|
||||||
@@ -10,8 +10,10 @@ DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ;
|
|||||||
|
|
||||||
|
|
||||||
HAIKU_PACKAGE_FS_SOURCES =
|
HAIKU_PACKAGE_FS_SOURCES =
|
||||||
|
BlockBufferCacheKernel.cpp
|
||||||
DebugSupport.cpp
|
DebugSupport.cpp
|
||||||
Directory.cpp
|
Directory.cpp
|
||||||
|
GlobalFactory.cpp
|
||||||
kernel_interface.cpp
|
kernel_interface.cpp
|
||||||
LeafNode.cpp
|
LeafNode.cpp
|
||||||
Node.cpp
|
Node.cpp
|
||||||
@@ -26,6 +28,8 @@ HAIKU_PACKAGE_FS_SOURCES =
|
|||||||
;
|
;
|
||||||
|
|
||||||
HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES =
|
HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES =
|
||||||
|
BlockBufferCache.cpp
|
||||||
|
BufferCache.cpp
|
||||||
DataOutput.cpp
|
DataOutput.cpp
|
||||||
DataReader.cpp
|
DataReader.cpp
|
||||||
ErrorOutput.cpp
|
ErrorOutput.cpp
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "PackageDataReader.h"
|
#include "PackageDataReader.h"
|
||||||
|
|
||||||
#include "DebugSupport.h"
|
#include "DebugSupport.h"
|
||||||
|
#include "GlobalFactory.h"
|
||||||
#include "Package.h"
|
#include "Package.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ struct PackageFile::DataAccessor {
|
|||||||
fReader(NULL),
|
fReader(NULL),
|
||||||
fFileCache(NULL)
|
fFileCache(NULL)
|
||||||
{
|
{
|
||||||
|
mutex_init(&fLock, "file data accessor");
|
||||||
}
|
}
|
||||||
|
|
||||||
~DataAccessor()
|
~DataAccessor()
|
||||||
@@ -36,6 +38,7 @@ struct PackageFile::DataAccessor {
|
|||||||
file_cache_delete(fFileCache);
|
file_cache_delete(fFileCache);
|
||||||
delete fReader;
|
delete fReader;
|
||||||
delete fDataReader;
|
delete fDataReader;
|
||||||
|
mutex_destroy(&fLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t Init(dev_t deviceID, ino_t nodeID, int fd)
|
status_t Init(dev_t deviceID, ino_t nodeID, int fd)
|
||||||
@@ -51,7 +54,7 @@ struct PackageFile::DataAccessor {
|
|||||||
RETURN_ERROR(B_NO_MEMORY);
|
RETURN_ERROR(B_NO_MEMORY);
|
||||||
|
|
||||||
// create a PackageDataReader
|
// create a PackageDataReader
|
||||||
status_t error = PackageDataReaderFactory::CreatePackageDataReader(
|
status_t error = GlobalFactory::Default()->CreatePackageDataReader(
|
||||||
fDataReader, *fData, fReader);
|
fDataReader, *fData, fReader);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
RETURN_ERROR(error);
|
RETURN_ERROR(error);
|
||||||
@@ -74,6 +77,7 @@ struct PackageFile::DataAccessor {
|
|||||||
fData->UncompressedSize() - offset);
|
fData->UncompressedSize() - offset);
|
||||||
|
|
||||||
if (toRead > 0) {
|
if (toRead > 0) {
|
||||||
|
MutexLocker locker(fLock);
|
||||||
status_t error = fReader->ReadData(offset, buffer, toRead);
|
status_t error = fReader->ReadData(offset, buffer, toRead);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
RETURN_ERROR(error);
|
RETURN_ERROR(error);
|
||||||
@@ -84,6 +88,7 @@ struct PackageFile::DataAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
mutex fLock;
|
||||||
PackageData* fData;
|
PackageData* fData;
|
||||||
DataReader* fDataReader;
|
DataReader* fDataReader;
|
||||||
PackageDataReader* fReader;
|
PackageDataReader* fReader;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "DebugSupport.h"
|
#include "DebugSupport.h"
|
||||||
#include "Directory.h"
|
#include "Directory.h"
|
||||||
|
#include "GlobalFactory.h"
|
||||||
#include "LeafNode.h"
|
#include "LeafNode.h"
|
||||||
#include "Volume.h"
|
#include "Volume.h"
|
||||||
|
|
||||||
@@ -651,14 +652,27 @@ packagefs_std_ops(int32 op, ...)
|
|||||||
{
|
{
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case B_MODULE_INIT:
|
case B_MODULE_INIT:
|
||||||
|
{
|
||||||
init_debugging();
|
init_debugging();
|
||||||
PRINT("package_std_ops(): B_MODULE_INIT\n");
|
PRINT("package_std_ops(): B_MODULE_INIT\n");
|
||||||
|
|
||||||
|
status_t error = GlobalFactory::CreateDefault();
|
||||||
|
if (error != B_OK) {
|
||||||
|
ERROR("Failed to init GlobalFactory\n");
|
||||||
|
exit_debugging();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
case B_MODULE_UNINIT:
|
case B_MODULE_UNINIT:
|
||||||
|
{
|
||||||
PRINT("package_std_ops(): B_MODULE_UNINIT\n");
|
PRINT("package_std_ops(): B_MODULE_UNINIT\n");
|
||||||
|
GlobalFactory::DeleteDefault();
|
||||||
exit_debugging();
|
exit_debugging();
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "BlockBufferCache.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <AutoLocker.h>
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - BlockBufferCache
|
||||||
|
|
||||||
|
|
||||||
|
BlockBufferCache::BlockBufferCache(size_t blockSize, uint32 maxCachedBlocks)
|
||||||
|
:
|
||||||
|
fBlockSize(blockSize),
|
||||||
|
fMaxCachedBlocks(maxCachedBlocks),
|
||||||
|
fAllocatedBlocks(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockBufferCache::~BlockBufferCache()
|
||||||
|
{
|
||||||
|
// delete all cached blocks
|
||||||
|
while (CachedBuffer* block = fCachedBuffers.RemoveHead())
|
||||||
|
delete block;
|
||||||
|
|
||||||
|
while (CachedBuffer* block = fUnusedBuffers.RemoveHead())
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BlockBufferCache::Init()
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CachedBuffer*
|
||||||
|
BlockBufferCache::GetBuffer(size_t size, CachedBuffer** owner, bool* _newBuffer)
|
||||||
|
{
|
||||||
|
// for sizes greater than the block size, we always allocate a new buffer
|
||||||
|
if (size > fBlockSize)
|
||||||
|
return _AllocateBuffer(size, owner, _newBuffer);
|
||||||
|
|
||||||
|
AutoLocker<BlockBufferCache> locker(this);
|
||||||
|
|
||||||
|
// if an owner is given and the buffer is still cached, return it
|
||||||
|
if (owner != NULL && *owner != NULL) {
|
||||||
|
CachedBuffer* buffer = *owner;
|
||||||
|
fCachedBuffers.Remove(buffer);
|
||||||
|
|
||||||
|
if (_newBuffer != NULL)
|
||||||
|
*_newBuffer = false;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need a new buffer -- try unused ones first
|
||||||
|
CachedBuffer* buffer = fUnusedBuffers.RemoveHead();
|
||||||
|
if (buffer != NULL) {
|
||||||
|
buffer->SetOwner(owner);
|
||||||
|
|
||||||
|
if (owner != NULL)
|
||||||
|
*owner = buffer;
|
||||||
|
if (_newBuffer != NULL)
|
||||||
|
*_newBuffer = true;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have already hit the max block limit, steal a cached block
|
||||||
|
if (fAllocatedBlocks >= fMaxCachedBlocks) {
|
||||||
|
buffer = fCachedBuffers.RemoveHead();
|
||||||
|
if (buffer != NULL) {
|
||||||
|
buffer->SetCached(false);
|
||||||
|
*buffer->Owner() = NULL;
|
||||||
|
buffer->SetOwner(owner);
|
||||||
|
|
||||||
|
if (owner != NULL)
|
||||||
|
*owner = buffer;
|
||||||
|
if (_newBuffer != NULL)
|
||||||
|
*_newBuffer = true;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate a new buffer
|
||||||
|
locker.Unlock();
|
||||||
|
return _AllocateBuffer(size, owner, _newBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BlockBufferCache::PutBufferAndCache(CachedBuffer** owner)
|
||||||
|
{
|
||||||
|
CachedBuffer* buffer = *owner;
|
||||||
|
|
||||||
|
// always delete buffers with non-standard size
|
||||||
|
if (buffer->Size() != fBlockSize) {
|
||||||
|
*owner = NULL;
|
||||||
|
delete buffer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoLocker<BlockBufferCache> locker(this);
|
||||||
|
|
||||||
|
// queue the cached buffer
|
||||||
|
buffer->SetOwner(owner);
|
||||||
|
fCachedBuffers.Add(buffer);
|
||||||
|
buffer->SetCached(true);
|
||||||
|
|
||||||
|
if (fAllocatedBlocks > fMaxCachedBlocks) {
|
||||||
|
// We have exceeded the limit -- we need to free a buffer.
|
||||||
|
CachedBuffer* otherBuffer = fUnusedBuffers.RemoveHead();
|
||||||
|
if (otherBuffer == NULL) {
|
||||||
|
otherBuffer = fCachedBuffers.RemoveHead();
|
||||||
|
*otherBuffer->Owner() = NULL;
|
||||||
|
otherBuffer->SetCached(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete otherBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BlockBufferCache::PutBuffer(CachedBuffer** owner)
|
||||||
|
{
|
||||||
|
AutoLocker<BlockBufferCache> locker(this);
|
||||||
|
|
||||||
|
CachedBuffer* buffer = *owner;
|
||||||
|
|
||||||
|
if (buffer == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (buffer->IsCached()) {
|
||||||
|
fCachedBuffers.Remove(buffer);
|
||||||
|
buffer->SetCached(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->SetOwner(NULL);
|
||||||
|
*owner = NULL;
|
||||||
|
|
||||||
|
if (buffer->Size() == fBlockSize && fAllocatedBlocks < fMaxCachedBlocks)
|
||||||
|
fUnusedBuffers.Add(buffer);
|
||||||
|
else
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CachedBuffer*
|
||||||
|
BlockBufferCache::_AllocateBuffer(size_t size, CachedBuffer** owner,
|
||||||
|
bool* _newBuffer)
|
||||||
|
{
|
||||||
|
CachedBuffer* buffer = new(std::nothrow) CachedBuffer(
|
||||||
|
std::max(size, fBlockSize));
|
||||||
|
if (buffer == NULL || buffer->Buffer() == NULL) {
|
||||||
|
delete buffer;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->SetOwner(owner);
|
||||||
|
|
||||||
|
if (_newBuffer != NULL)
|
||||||
|
*_newBuffer = true;
|
||||||
|
|
||||||
|
AutoLocker<BlockBufferCache> locker(this);
|
||||||
|
fAllocatedBlocks++;
|
||||||
|
|
||||||
|
if (owner != NULL)
|
||||||
|
*owner = buffer;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - BlockBufferCacheNoLock
|
||||||
|
|
||||||
|
|
||||||
|
BlockBufferCacheNoLock::BlockBufferCacheNoLock(size_t blockSize,
|
||||||
|
uint32 maxCachedBlocks)
|
||||||
|
:
|
||||||
|
BlockBufferCache(blockSize, maxCachedBlocks)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockBufferCacheNoLock::~BlockBufferCacheNoLock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
BlockBufferCacheNoLock::Lock()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BlockBufferCacheNoLock::Unlock()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef BLOCK_BUFFER_CACHE_H
|
||||||
|
#define BLOCK_BUFFER_CACHE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "BufferCache.h"
|
||||||
|
|
||||||
|
|
||||||
|
class BlockBufferCache : public BufferCache {
|
||||||
|
public:
|
||||||
|
BlockBufferCache(size_t blockSize,
|
||||||
|
uint32 maxCachedBlocks);
|
||||||
|
virtual ~BlockBufferCache();
|
||||||
|
|
||||||
|
virtual status_t Init();
|
||||||
|
|
||||||
|
virtual CachedBuffer* GetBuffer(size_t size,
|
||||||
|
CachedBuffer** owner = NULL,
|
||||||
|
bool* _newBuffer = NULL);
|
||||||
|
virtual void PutBufferAndCache(CachedBuffer** owner);
|
||||||
|
virtual void PutBuffer(CachedBuffer** owner);
|
||||||
|
|
||||||
|
virtual bool Lock() = 0;
|
||||||
|
virtual void Unlock() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef DoublyLinkedList<CachedBuffer> BufferList;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CachedBuffer* _AllocateBuffer(size_t size,
|
||||||
|
CachedBuffer** owner, bool* _newBuffer);
|
||||||
|
// object must not be locked
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t fBlockSize;
|
||||||
|
uint32 fMaxCachedBlocks;
|
||||||
|
uint32 fAllocatedBlocks;
|
||||||
|
BufferList fUnusedBuffers;
|
||||||
|
BufferList fCachedBuffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BlockBufferCacheNoLock : public BlockBufferCache {
|
||||||
|
public:
|
||||||
|
BlockBufferCacheNoLock(size_t blockSize,
|
||||||
|
uint32 maxCachedBlocks);
|
||||||
|
virtual ~BlockBufferCacheNoLock();
|
||||||
|
|
||||||
|
virtual bool Lock();
|
||||||
|
virtual void Unlock();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BLOCK_BUFFER_CACHE_H
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "BufferCache.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - CachedBuffer
|
||||||
|
|
||||||
|
|
||||||
|
CachedBuffer::CachedBuffer(size_t size)
|
||||||
|
:
|
||||||
|
fOwner(NULL),
|
||||||
|
fBuffer(malloc(size)),
|
||||||
|
fSize(size),
|
||||||
|
fCached(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CachedBuffer::~CachedBuffer()
|
||||||
|
{
|
||||||
|
free(fBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - BufferCache
|
||||||
|
|
||||||
|
|
||||||
|
BufferCache::~BufferCache()
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef BUFFER_CACHE_H
|
||||||
|
#define BUFFER_CACHE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
|
|
||||||
|
class CachedBuffer : public DoublyLinkedListLinkImpl<CachedBuffer> {
|
||||||
|
public:
|
||||||
|
CachedBuffer(size_t size);
|
||||||
|
~CachedBuffer();
|
||||||
|
|
||||||
|
void* Buffer() const { return fBuffer; }
|
||||||
|
size_t Size() const { return fSize; }
|
||||||
|
|
||||||
|
|
||||||
|
// implementation private
|
||||||
|
CachedBuffer** Owner() const { return fOwner; }
|
||||||
|
void SetOwner(CachedBuffer** owner)
|
||||||
|
{ fOwner = owner; }
|
||||||
|
|
||||||
|
void SetCached(bool cached) { fCached = cached; }
|
||||||
|
bool IsCached() const { return fCached; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
CachedBuffer** fOwner;
|
||||||
|
void* fBuffer;
|
||||||
|
size_t fSize;
|
||||||
|
bool fCached;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BufferCache {
|
||||||
|
public:
|
||||||
|
virtual ~BufferCache();
|
||||||
|
|
||||||
|
virtual CachedBuffer* GetBuffer(size_t size,
|
||||||
|
CachedBuffer** owner = NULL,
|
||||||
|
bool* _newBuffer = NULL) = 0;
|
||||||
|
virtual void PutBufferAndCache(CachedBuffer** owner) = 0;
|
||||||
|
// caller is buffer owner and wants the
|
||||||
|
// buffer cached, if possible
|
||||||
|
virtual void PutBuffer(CachedBuffer** owner) = 0;
|
||||||
|
// puts the buffer for good, owner might
|
||||||
|
// have called PutBufferAndCache() before
|
||||||
|
// and might not own a buffer anymore
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CachedBufferPutter {
|
||||||
|
public:
|
||||||
|
CachedBufferPutter(BufferCache* cache, CachedBuffer** owner)
|
||||||
|
:
|
||||||
|
fCache(cache),
|
||||||
|
fOwner(owner),
|
||||||
|
fBuffer(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedBufferPutter(BufferCache* cache, CachedBuffer* buffer)
|
||||||
|
:
|
||||||
|
fCache(cache),
|
||||||
|
fOwner(NULL),
|
||||||
|
fBuffer(buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CachedBufferPutter()
|
||||||
|
{
|
||||||
|
if (fCache != NULL) {
|
||||||
|
if (fOwner != NULL)
|
||||||
|
fCache->PutBufferAndCache(fOwner);
|
||||||
|
else if (fBuffer != NULL)
|
||||||
|
fCache->PutBuffer(&fBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BufferCache* fCache;
|
||||||
|
CachedBuffer** fOwner;
|
||||||
|
CachedBuffer* fBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BUFFER_CACHE_H
|
||||||
@@ -10,6 +10,8 @@ DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ;
|
|||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) compression ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) compression ] ;
|
||||||
|
|
||||||
BinCommand package :
|
BinCommand package :
|
||||||
|
BlockBufferCache.cpp
|
||||||
|
BufferCache.cpp
|
||||||
command_create.cpp
|
command_create.cpp
|
||||||
command_dump.cpp
|
command_dump.cpp
|
||||||
command_extract.cpp
|
command_extract.cpp
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <haiku_package.h>
|
#include <haiku_package.h>
|
||||||
|
|
||||||
|
#include "BufferCache.h"
|
||||||
#include "PackageData.h"
|
#include "PackageData.h"
|
||||||
#include "ZlibDecompressor.h"
|
#include "ZlibDecompressor.h"
|
||||||
|
|
||||||
@@ -94,18 +95,20 @@ private:
|
|||||||
|
|
||||||
class ZlibPackageDataReader : public PackageDataReader {
|
class ZlibPackageDataReader : public PackageDataReader {
|
||||||
public:
|
public:
|
||||||
ZlibPackageDataReader(DataReader* dataReader)
|
ZlibPackageDataReader(DataReader* dataReader, BufferCache* bufferCache)
|
||||||
:
|
:
|
||||||
PackageDataReader(dataReader),
|
PackageDataReader(dataReader),
|
||||||
fOffsetTable(NULL),
|
fBufferCache(bufferCache),
|
||||||
fReadBuffer(NULL)
|
fUncompressBuffer(NULL),
|
||||||
|
fOffsetTable(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~ZlibPackageDataReader()
|
~ZlibPackageDataReader()
|
||||||
{
|
{
|
||||||
delete[] fOffsetTable;
|
delete[] fOffsetTable;
|
||||||
free(fReadBuffer);
|
|
||||||
|
fBufferCache->PutBuffer(&fUncompressBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t Init(const PackageData& data)
|
status_t Init(const PackageData& data)
|
||||||
@@ -142,14 +145,8 @@ public:
|
|||||||
} else
|
} else
|
||||||
fChunkSize = fUncompressedSize;
|
fChunkSize = fUncompressedSize;
|
||||||
|
|
||||||
// allocate the read/uncompress buffer
|
// mark uncompressed content invalid
|
||||||
fReadBuffer = (uint8*)malloc(fChunkSize * 2);
|
|
||||||
if (fReadBuffer == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
fUncompressBuffer = fReadBuffer + fChunkSize;
|
|
||||||
fUncompressedChunk = -1;
|
fUncompressedChunk = -1;
|
||||||
// mark content invalid
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -180,6 +177,17 @@ public:
|
|||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get our uncompressed chunk buffer back, if possible
|
||||||
|
bool newBuffer;
|
||||||
|
if (fBufferCache->GetBuffer(fChunkSize, &fUncompressBuffer, &newBuffer)
|
||||||
|
== NULL) {
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
CachedBufferPutter uncompressBuffer(fBufferCache, &fUncompressBuffer);
|
||||||
|
|
||||||
|
if (newBuffer)
|
||||||
|
fUncompressedChunk = -1;
|
||||||
|
|
||||||
// uncompress
|
// uncompress
|
||||||
int64 chunkIndex = offset / fChunkSize;
|
int64 chunkIndex = offset / fChunkSize;
|
||||||
off_t chunkOffset = chunkIndex * fChunkSize;
|
off_t chunkOffset = chunkIndex * fChunkSize;
|
||||||
@@ -193,7 +201,8 @@ public:
|
|||||||
|
|
||||||
// copy data to buffer
|
// copy data to buffer
|
||||||
size_t toCopy = std::min(size, (size_t)fChunkSize - inChunkOffset);
|
size_t toCopy = std::min(size, (size_t)fChunkSize - inChunkOffset);
|
||||||
memcpy(buffer, fUncompressBuffer + inChunkOffset, toCopy);
|
memcpy(buffer, (uint8*)fUncompressBuffer->Buffer() + inChunkOffset,
|
||||||
|
toCopy);
|
||||||
|
|
||||||
buffer += toCopy;
|
buffer += toCopy;
|
||||||
size -= toCopy;
|
size -= toCopy;
|
||||||
@@ -227,17 +236,24 @@ private:
|
|||||||
if (compressedSize == uncompressedSize) {
|
if (compressedSize == uncompressedSize) {
|
||||||
// the chunk is not compressed -- read it directly into the
|
// the chunk is not compressed -- read it directly into the
|
||||||
// uncompressed buffer
|
// uncompressed buffer
|
||||||
error = fDataReader->ReadData(offset, fUncompressBuffer,
|
error = fDataReader->ReadData(offset, fUncompressBuffer->Buffer(),
|
||||||
compressedSize);
|
compressedSize);
|
||||||
} else {
|
} else {
|
||||||
// read to the read buffer and uncompress
|
// read to a read buffer and uncompress
|
||||||
error = fDataReader->ReadData(offset, fReadBuffer, compressedSize);
|
CachedBuffer* readBuffer = fBufferCache->GetBuffer(fChunkSize);
|
||||||
|
if (readBuffer == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
CachedBufferPutter readBufferPutter(fBufferCache, readBuffer);
|
||||||
|
|
||||||
|
error = fDataReader->ReadData(offset, readBuffer->Buffer(),
|
||||||
|
compressedSize);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
size_t actuallyUncompressedSize;
|
size_t actuallyUncompressedSize;
|
||||||
error = ZlibDecompressor::DecompressSingleBuffer(fReadBuffer,
|
error = ZlibDecompressor::DecompressSingleBuffer(
|
||||||
compressedSize, fUncompressBuffer, uncompressedSize,
|
readBuffer->Buffer(), compressedSize,
|
||||||
|
fUncompressBuffer->Buffer(), uncompressedSize,
|
||||||
actuallyUncompressedSize);
|
actuallyUncompressedSize);
|
||||||
if (error == B_OK && actuallyUncompressedSize != uncompressedSize)
|
if (error == B_OK && actuallyUncompressedSize != uncompressedSize)
|
||||||
error = B_BAD_DATA;
|
error = B_BAD_DATA;
|
||||||
@@ -321,25 +337,33 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64 fOffset;
|
BufferCache* fBufferCache;
|
||||||
uint64 fUncompressedSize;
|
CachedBuffer* fUncompressBuffer;
|
||||||
uint64 fCompressedSize;
|
int64 fUncompressedChunk;
|
||||||
uint64 fOffsetTableSize;
|
|
||||||
uint64 fChunkCount;
|
uint64 fOffset;
|
||||||
uint32 fChunkSize;
|
uint64 fUncompressedSize;
|
||||||
uint32 fOffsetTableBufferEntryCount;
|
uint64 fCompressedSize;
|
||||||
uint64* fOffsetTable;
|
uint64 fOffsetTableSize;
|
||||||
int32 fOffsetTableIndex;
|
uint64 fChunkCount;
|
||||||
uint8* fReadBuffer;
|
uint32 fChunkSize;
|
||||||
uint8* fUncompressBuffer;
|
uint32 fOffsetTableBufferEntryCount;
|
||||||
int64 fUncompressedChunk;
|
uint64* fOffsetTable;
|
||||||
|
int32 fOffsetTableIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - PackageDataReaderFactory
|
// #pragma mark - PackageDataReaderFactory
|
||||||
|
|
||||||
|
|
||||||
/*static*/ status_t
|
PackageDataReaderFactory::PackageDataReaderFactory(BufferCache* bufferCache)
|
||||||
|
:
|
||||||
|
fBufferCache(bufferCache)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
PackageDataReaderFactory::CreatePackageDataReader(DataReader* dataReader,
|
PackageDataReaderFactory::CreatePackageDataReader(DataReader* dataReader,
|
||||||
const PackageData& data, PackageDataReader*& _reader)
|
const PackageData& data, PackageDataReader*& _reader)
|
||||||
{
|
{
|
||||||
@@ -351,7 +375,8 @@ PackageDataReaderFactory::CreatePackageDataReader(DataReader* dataReader,
|
|||||||
dataReader);
|
dataReader);
|
||||||
break;
|
break;
|
||||||
case B_HPKG_COMPRESSION_ZLIB:
|
case B_HPKG_COMPRESSION_ZLIB:
|
||||||
reader = new(std::nothrow) ZlibPackageDataReader(dataReader);
|
reader = new(std::nothrow) ZlibPackageDataReader(dataReader,
|
||||||
|
fBufferCache);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "DataReader.h"
|
#include "DataReader.h"
|
||||||
|
|
||||||
|
|
||||||
|
class BufferCache;
|
||||||
class PackageData;
|
class PackageData;
|
||||||
|
|
||||||
|
|
||||||
@@ -29,9 +30,15 @@ protected:
|
|||||||
|
|
||||||
class PackageDataReaderFactory {
|
class PackageDataReaderFactory {
|
||||||
public:
|
public:
|
||||||
static status_t CreatePackageDataReader(DataReader* dataReader,
|
PackageDataReaderFactory(
|
||||||
|
BufferCache* bufferCache);
|
||||||
|
|
||||||
|
status_t CreatePackageDataReader(DataReader* dataReader,
|
||||||
const PackageData& data,
|
const PackageData& data,
|
||||||
PackageDataReader*& _reader);
|
PackageDataReader*& _reader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BufferCache* fBufferCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include <AutoDeleter.h>
|
#include <AutoDeleter.h>
|
||||||
|
|
||||||
|
#include "BlockBufferCache.h"
|
||||||
#include "FDCloser.h"
|
#include "FDCloser.h"
|
||||||
#include "package.h"
|
#include "package.h"
|
||||||
#include "PackageDataReader.h"
|
#include "PackageDataReader.h"
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
struct PackageContentExtractHandler : PackageContentHandler {
|
struct PackageContentExtractHandler : PackageContentHandler {
|
||||||
PackageContentExtractHandler(int packageFileFD)
|
PackageContentExtractHandler(int packageFileFD)
|
||||||
:
|
:
|
||||||
|
fBufferCache(B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, 2),
|
||||||
fPackageFileReader(packageFileFD),
|
fPackageFileReader(packageFileFD),
|
||||||
fDataBuffer(NULL),
|
fDataBuffer(NULL),
|
||||||
fDataBufferSize(0),
|
fDataBufferSize(0),
|
||||||
@@ -49,6 +51,10 @@ struct PackageContentExtractHandler : PackageContentHandler {
|
|||||||
|
|
||||||
status_t Init()
|
status_t Init()
|
||||||
{
|
{
|
||||||
|
status_t error = fBufferCache.Init();
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
fDataBufferSize = 64 * 1024;
|
fDataBufferSize = 64 * 1024;
|
||||||
fDataBuffer = malloc(fDataBufferSize);
|
fDataBuffer = malloc(fDataBufferSize);
|
||||||
if (fDataBuffer == NULL)
|
if (fDataBuffer == NULL)
|
||||||
@@ -245,8 +251,8 @@ private:
|
|||||||
{
|
{
|
||||||
// create a PackageDataReader
|
// create a PackageDataReader
|
||||||
PackageDataReader* reader;
|
PackageDataReader* reader;
|
||||||
status_t error = PackageDataReaderFactory::CreatePackageDataReader(
|
status_t error = PackageDataReaderFactory(&fBufferCache)
|
||||||
dataReader, data, reader);
|
.CreatePackageDataReader(dataReader, data, reader);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
ObjectDeleter<PackageDataReader> readerDeleter(reader);
|
ObjectDeleter<PackageDataReader> readerDeleter(reader);
|
||||||
@@ -284,10 +290,11 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FDDataReader fPackageFileReader;
|
BlockBufferCacheNoLock fBufferCache;
|
||||||
void* fDataBuffer;
|
FDDataReader fPackageFileReader;
|
||||||
size_t fDataBufferSize;
|
void* fDataBuffer;
|
||||||
bool fErrorOccurred;
|
size_t fDataBufferSize;
|
||||||
|
bool fErrorOccurred;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user