Mmh, apparently I have to check the copied directory itself in first.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20345 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,236 @@
|
||||
// AllocationInfo.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Debug.h"
|
||||
|
||||
#include "Attribute.h"
|
||||
#include "Directory.h"
|
||||
#include "Entry.h"
|
||||
#include "File.h"
|
||||
#include "SymLink.h"
|
||||
|
||||
// constructor
|
||||
AllocationInfo::AllocationInfo()
|
||||
: fNodeTableArraySize(0),
|
||||
fNodeTableVectorSize(0),
|
||||
fNodeTableElementCount(0),
|
||||
fDirectoryEntryTableArraySize(0),
|
||||
fDirectoryEntryTableVectorSize(0),
|
||||
fDirectoryEntryTableElementCount(0),
|
||||
fNodeAttributeTableArraySize(0),
|
||||
fNodeAttributeTableVectorSize(0),
|
||||
fNodeAttributeTableElementCount(0),
|
||||
|
||||
fAttributeCount(0),
|
||||
fAttributeSize(0),
|
||||
fDirectoryCount(0),
|
||||
fEntryCount(0),
|
||||
fFileCount(0),
|
||||
fFileSize(0),
|
||||
fSymLinkCount(0),
|
||||
fSymLinkSize(0),
|
||||
|
||||
fAreaCount(0),
|
||||
fAreaSize(0),
|
||||
fBlockCount(0),
|
||||
fBlockSize(0),
|
||||
fListCount(0),
|
||||
fListSize(0),
|
||||
fOtherCount(0),
|
||||
fOtherSize(0),
|
||||
fStringCount(0),
|
||||
fStringSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
AllocationInfo::~AllocationInfo()
|
||||
{
|
||||
}
|
||||
|
||||
// AddNodeTableAllocation
|
||||
void
|
||||
AllocationInfo::AddNodeTableAllocation(size_t arraySize, size_t vectorSize,
|
||||
size_t elementSize, size_t elementCount)
|
||||
{
|
||||
fNodeTableArraySize += arraySize;
|
||||
fNodeTableVectorSize += vectorSize * elementSize;
|
||||
fNodeTableElementCount += elementCount;
|
||||
}
|
||||
|
||||
// AddDirectoryEntryTableAllocation
|
||||
void
|
||||
AllocationInfo::AddDirectoryEntryTableAllocation(size_t arraySize,
|
||||
size_t vectorSize,
|
||||
size_t elementSize,
|
||||
size_t elementCount)
|
||||
{
|
||||
fDirectoryEntryTableArraySize += arraySize;
|
||||
fDirectoryEntryTableVectorSize += vectorSize * elementSize;
|
||||
fDirectoryEntryTableElementCount += elementCount;
|
||||
}
|
||||
|
||||
// AddNodeAttributeTableAllocation
|
||||
void
|
||||
AllocationInfo::AddNodeAttributeTableAllocation(size_t arraySize,
|
||||
size_t vectorSize,
|
||||
size_t elementSize,
|
||||
size_t elementCount)
|
||||
{
|
||||
fNodeAttributeTableArraySize += arraySize;
|
||||
fNodeAttributeTableVectorSize += vectorSize * elementSize;
|
||||
fNodeAttributeTableElementCount += elementCount;
|
||||
}
|
||||
|
||||
// AddAttributeAllocation
|
||||
void
|
||||
AllocationInfo::AddAttributeAllocation(size_t size)
|
||||
{
|
||||
fAttributeCount++;
|
||||
fAttributeSize += size;
|
||||
}
|
||||
|
||||
// AddDirectoryAllocation
|
||||
void
|
||||
AllocationInfo::AddDirectoryAllocation()
|
||||
{
|
||||
fDirectoryCount++;
|
||||
}
|
||||
|
||||
// AddEntryAllocation
|
||||
void
|
||||
AllocationInfo::AddEntryAllocation()
|
||||
{
|
||||
fEntryCount++;
|
||||
}
|
||||
|
||||
// AddFileAllocation
|
||||
void
|
||||
AllocationInfo::AddFileAllocation(size_t size)
|
||||
{
|
||||
fFileCount++;
|
||||
fFileSize += size;
|
||||
}
|
||||
|
||||
// AddSymLinkAllocation
|
||||
void
|
||||
AllocationInfo::AddSymLinkAllocation(size_t size)
|
||||
{
|
||||
fSymLinkCount++;
|
||||
fSymLinkSize += size;
|
||||
}
|
||||
|
||||
// AddAreaAllocation
|
||||
void
|
||||
AllocationInfo::AddAreaAllocation(size_t size, size_t count)
|
||||
{
|
||||
fAreaCount += count;
|
||||
fAreaSize += count * size;
|
||||
}
|
||||
|
||||
// AddBlockAllocation
|
||||
void
|
||||
AllocationInfo::AddBlockAllocation(size_t size)
|
||||
{
|
||||
fBlockCount++;
|
||||
fBlockSize += size;
|
||||
}
|
||||
|
||||
// AddListAllocation
|
||||
void
|
||||
AllocationInfo::AddListAllocation(size_t capacity, size_t elementSize)
|
||||
{
|
||||
fListCount += 1;
|
||||
fListSize += capacity * elementSize;
|
||||
}
|
||||
|
||||
// AddOtherAllocation
|
||||
void
|
||||
AllocationInfo::AddOtherAllocation(size_t size, size_t count)
|
||||
{
|
||||
fOtherCount += count;
|
||||
fOtherSize += size * count;
|
||||
}
|
||||
|
||||
// AddStringAllocation
|
||||
void
|
||||
AllocationInfo::AddStringAllocation(size_t size)
|
||||
{
|
||||
fStringCount++;
|
||||
fStringSize += size;
|
||||
}
|
||||
|
||||
// Dump
|
||||
void
|
||||
AllocationInfo::Dump() const
|
||||
{
|
||||
size_t heapCount = 0;
|
||||
size_t heapSize = 0;
|
||||
size_t areaCount = 0;
|
||||
size_t areaSize = 0;
|
||||
|
||||
PRINT((" node table:\n"));
|
||||
PRINT((" array size: %9lu\n", fNodeTableArraySize));
|
||||
PRINT((" vector size: %9lu\n", fNodeTableVectorSize));
|
||||
PRINT((" elements: %9lu\n", fNodeTableElementCount));
|
||||
areaCount += 2;
|
||||
areaSize += fNodeTableArraySize * sizeof(int32) + fNodeTableVectorSize;
|
||||
|
||||
PRINT((" entry table:\n"));
|
||||
PRINT((" array size: %9lu\n", fDirectoryEntryTableArraySize));
|
||||
PRINT((" vector size: %9lu\n", fDirectoryEntryTableVectorSize));
|
||||
PRINT((" elements: %9lu\n", fDirectoryEntryTableElementCount));
|
||||
areaCount += 2;
|
||||
areaSize += fDirectoryEntryTableArraySize * sizeof(int32)
|
||||
+ fDirectoryEntryTableVectorSize;
|
||||
|
||||
PRINT((" attribute table:\n"));
|
||||
PRINT((" array size: %9lu\n", fNodeAttributeTableArraySize));
|
||||
PRINT((" vector size: %9lu\n", fNodeAttributeTableVectorSize));
|
||||
PRINT((" elements: %9lu\n", fNodeAttributeTableElementCount));
|
||||
areaCount += 2;
|
||||
areaSize += fNodeAttributeTableArraySize * sizeof(int32)
|
||||
+ fNodeAttributeTableVectorSize;
|
||||
|
||||
PRINT((" attributes: %9lu, size: %9lu\n", fAttributeCount, fAttributeSize));
|
||||
heapCount += fAttributeCount;
|
||||
heapSize += fAttributeCount * sizeof(Attribute);
|
||||
|
||||
PRINT((" directories: %9lu\n", fDirectoryCount));
|
||||
heapCount += fDirectoryCount;
|
||||
heapSize += fDirectoryCount * sizeof(Directory);
|
||||
|
||||
PRINT((" entries: %9lu\n", fEntryCount));
|
||||
heapCount += fEntryCount;
|
||||
heapSize += fEntryCount * sizeof(Entry);
|
||||
|
||||
PRINT((" files: %9lu, size: %9lu\n", fFileCount, fFileSize));
|
||||
heapCount += fFileCount;
|
||||
heapSize += fFileCount * sizeof(File);
|
||||
|
||||
PRINT((" symlinks: %9lu, size: %9lu\n", fSymLinkCount, fSymLinkSize));
|
||||
heapCount += fSymLinkCount;
|
||||
heapSize += fSymLinkCount * sizeof(SymLink);
|
||||
|
||||
PRINT((" areas: %9lu, size: %9lu\n", fAreaCount, fAreaSize));
|
||||
areaCount += fAreaCount;
|
||||
areaSize += fAreaSize;
|
||||
|
||||
PRINT((" blocks: %9lu, size: %9lu\n", fBlockCount, fBlockSize));
|
||||
|
||||
PRINT((" lists: %9lu, size: %9lu\n", fListCount, fListSize));
|
||||
heapCount += fListCount;
|
||||
heapSize += fListSize;
|
||||
|
||||
PRINT((" other: %9lu, size: %9lu\n", fOtherCount, fOtherSize));
|
||||
heapCount += fOtherCount;
|
||||
heapSize += fOtherSize;
|
||||
|
||||
PRINT((" strings: %9lu, size: %9lu\n", fStringCount, fStringSize));
|
||||
heapCount += fStringCount;
|
||||
heapSize += fStringSize;
|
||||
|
||||
PRINT(("heap: %9lu allocations, size: %9lu\n", heapCount, heapSize));
|
||||
PRINT(("areas: %9lu allocations, size: %9lu\n", areaCount, areaSize));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// AllocationInfo.h
|
||||
|
||||
#ifndef ALLOCATION_INFO_H
|
||||
#define ALLOCATION_INFO_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
class AllocationInfo {
|
||||
public:
|
||||
AllocationInfo();
|
||||
~AllocationInfo();
|
||||
|
||||
void AddNodeTableAllocation(size_t arraySize, size_t vectorSize,
|
||||
size_t elementSize, size_t elementCount);
|
||||
void AddDirectoryEntryTableAllocation(size_t arraySize, size_t vectorSize,
|
||||
size_t elementSize,
|
||||
size_t elementCount);
|
||||
void AddNodeAttributeTableAllocation(size_t arraySize, size_t vectorSize,
|
||||
size_t elementSize,
|
||||
size_t elementCount);
|
||||
|
||||
void AddAttributeAllocation(size_t size);
|
||||
void AddDirectoryAllocation();
|
||||
void AddEntryAllocation();
|
||||
void AddFileAllocation(size_t size);
|
||||
void AddSymLinkAllocation(size_t size);
|
||||
|
||||
void AddAreaAllocation(size_t size, size_t count = 1);
|
||||
void AddBlockAllocation(size_t size);
|
||||
void AddListAllocation(size_t capacity, size_t elementSize);
|
||||
void AddOtherAllocation(size_t size, size_t count = 1);
|
||||
void AddStringAllocation(size_t size);
|
||||
|
||||
void Dump() const;
|
||||
|
||||
private:
|
||||
size_t fNodeTableArraySize;
|
||||
size_t fNodeTableVectorSize;
|
||||
size_t fNodeTableElementCount;
|
||||
size_t fDirectoryEntryTableArraySize;
|
||||
size_t fDirectoryEntryTableVectorSize;
|
||||
size_t fDirectoryEntryTableElementCount;
|
||||
size_t fNodeAttributeTableArraySize;
|
||||
size_t fNodeAttributeTableVectorSize;
|
||||
size_t fNodeAttributeTableElementCount;
|
||||
|
||||
size_t fAttributeCount;
|
||||
size_t fAttributeSize;
|
||||
size_t fDirectoryCount;
|
||||
size_t fEntryCount;
|
||||
size_t fFileCount;
|
||||
size_t fFileSize;
|
||||
size_t fSymLinkCount;
|
||||
size_t fSymLinkSize;
|
||||
|
||||
size_t fAreaCount;
|
||||
size_t fAreaSize;
|
||||
size_t fBlockCount;
|
||||
size_t fBlockSize;
|
||||
size_t fListCount;
|
||||
size_t fListSize;
|
||||
size_t fOtherCount;
|
||||
size_t fOtherSize;
|
||||
size_t fStringCount;
|
||||
size_t fStringSize;
|
||||
};
|
||||
|
||||
#endif // ALLOCATION_INFO_H
|
||||
@@ -0,0 +1,160 @@
|
||||
// AreaUtils.cpp
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name of a copyright holder shall
|
||||
// not be used in advertising or otherwise to promote the sale, use or other
|
||||
// dealings in this Software without prior written authorization of the
|
||||
// copyright holder.
|
||||
|
||||
#include <algobase.h>
|
||||
#include <OS.h>
|
||||
|
||||
#include "AreaUtils.h"
|
||||
#include "Debug.h"
|
||||
|
||||
#ifndef USE_STANDARD_FUNCTIONS
|
||||
#define USE_STANDARD_FUNCTIONS 0
|
||||
#endif
|
||||
|
||||
|
||||
// area_info_for
|
||||
static
|
||||
status_t
|
||||
area_info_for(void *address, area_info *info)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
if (address) {
|
||||
// get the area ID for the ptr
|
||||
area_id area = area_for(address);
|
||||
// check if supplied pointer points to the beginning of the area
|
||||
if (area >= 0)
|
||||
error = get_area_info(area, info);
|
||||
else
|
||||
error = area;
|
||||
} else
|
||||
error = B_BAD_VALUE;
|
||||
return error;
|
||||
}
|
||||
|
||||
// calloc
|
||||
void *
|
||||
AreaUtils::calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
//PRINT(("AreaUtils::calloc(%lu, %lu)\n", nmemb, size));
|
||||
return AreaUtils::malloc(nmemb * size);
|
||||
}
|
||||
|
||||
// free
|
||||
void
|
||||
AreaUtils::free(void *ptr)
|
||||
{
|
||||
//PRINT(("AreaUtils::free(%p)\n", ptr));
|
||||
#if USE_STANDARD_FUNCTIONS
|
||||
return ::free(ptr);
|
||||
#else
|
||||
if (ptr) {
|
||||
// get the area for the pointer
|
||||
area_info info;
|
||||
if (area_info_for(ptr, &info) == B_OK) {
|
||||
if (ptr == info.address) {
|
||||
// everything is fine, delete the area
|
||||
delete_area(info.area);
|
||||
} else {
|
||||
INFORM(("WARNING: AreaUtils::free(%p): area begin is %p."
|
||||
"Ignored.\n", ptr, info.address));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// malloc
|
||||
void *
|
||||
AreaUtils::malloc(size_t size)
|
||||
{
|
||||
//PRINT(("AreaUtils::malloc(%lu)\n", size));
|
||||
#if USE_STANDARD_FUNCTIONS
|
||||
return ::malloc(size);
|
||||
#else
|
||||
void *address = NULL;
|
||||
if (size > 0) {
|
||||
// round to multiple of page size
|
||||
size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
|
||||
// create an area
|
||||
#if USER
|
||||
area_id area = create_area("AreaUtils::malloc", &address,
|
||||
B_ANY_ADDRESS, size, B_NO_LOCK,
|
||||
B_WRITE_AREA | B_READ_AREA);
|
||||
#else
|
||||
area_id area = create_area("AreaUtils::malloc", &address,
|
||||
B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
|
||||
B_READ_AREA | B_WRITE_AREA);
|
||||
#endif
|
||||
if (area < 0)
|
||||
address = NULL;
|
||||
}
|
||||
return address;
|
||||
#endif
|
||||
}
|
||||
|
||||
// realloc
|
||||
void *
|
||||
AreaUtils::realloc(void * ptr, size_t size)
|
||||
{
|
||||
//PRINT(("AreaUtils::realloc(%p, %lu)\n", ptr, size))
|
||||
#if USE_STANDARD_FUNCTIONS
|
||||
return ::realloc(ptr, size);
|
||||
#else
|
||||
void *newAddress = NULL;
|
||||
if (size == 0) {
|
||||
AreaUtils::free(ptr);
|
||||
} else if (ptr) {
|
||||
// get the area for the pointer
|
||||
area_info info;
|
||||
if (area_info_for(ptr, &info) == B_OK) {
|
||||
if (ptr == info.address) {
|
||||
// round to multiple of page size
|
||||
size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
|
||||
if (size == info.size) {
|
||||
// nothing to do
|
||||
newAddress = ptr;
|
||||
} else if (resize_area(info.area, size) == B_OK) {
|
||||
// resizing the area went fine
|
||||
newAddress = ptr;
|
||||
} else {
|
||||
// resizing the area failed: we need to allocate a new one
|
||||
newAddress = AreaUtils::malloc(size);
|
||||
if (newAddress) {
|
||||
memcpy(newAddress, ptr, min(size, info.size));
|
||||
delete_area(info.area);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
INFORM(("WARNING: AreaUtils::realloc(%p): area begin is %p."
|
||||
"Ignored.\n", ptr, info.address));
|
||||
}
|
||||
}
|
||||
}
|
||||
return newAddress;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// AreaUtils.h
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name of a copyright holder shall
|
||||
// not be used in advertising or otherwise to promote the sale, use or other
|
||||
// dealings in this Software without prior written authorization of the
|
||||
// copyright holder.
|
||||
|
||||
#ifndef AREA_UTILS_H
|
||||
#define AREA_UTILS_H
|
||||
|
||||
namespace AreaUtils {
|
||||
|
||||
void *calloc(size_t nmemb, size_t size);
|
||||
void free(void *ptr);
|
||||
void *malloc(size_t size);
|
||||
void *realloc(void * ptr, size_t size);
|
||||
|
||||
};
|
||||
|
||||
#endif // AREA_UTILS_H
|
||||
@@ -0,0 +1,136 @@
|
||||
// Attribute.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Attribute.h"
|
||||
#include "Misc.h"
|
||||
#include "Node.h"
|
||||
#include "ramfs.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
Attribute::Attribute(Volume *volume, Node *node, const char *name,
|
||||
uint32 type)
|
||||
: DataContainer(volume),
|
||||
fNode(node),
|
||||
fName(name),
|
||||
fType(type),
|
||||
fIndex(NULL),
|
||||
fInIndex(false),
|
||||
fIterators()
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
Attribute::~Attribute()
|
||||
{
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
Attribute::InitCheck() const
|
||||
{
|
||||
return (fName.GetString() ? B_OK : B_NO_INIT);
|
||||
}
|
||||
|
||||
// SetType
|
||||
void
|
||||
Attribute::SetType(uint32 type)
|
||||
{
|
||||
if (type != fType) {
|
||||
if (fIndex)
|
||||
fIndex->Removed(this);
|
||||
fType = type;
|
||||
if (AttributeIndex *index = GetVolume()->FindAttributeIndex(GetName(),
|
||||
fType)) {
|
||||
index->Added(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteAt
|
||||
status_t
|
||||
Attribute::WriteAt(off_t offset, const void *buffer, size_t size,
|
||||
size_t *bytesWritten)
|
||||
{
|
||||
// get the current key for the attribute
|
||||
uint8 oldKey[kMaxIndexKeyLength];
|
||||
size_t oldLength;
|
||||
GetKey(oldKey, &oldLength);
|
||||
|
||||
// write the new value
|
||||
status_t error = DataContainer::WriteAt(offset, buffer, size, bytesWritten);
|
||||
|
||||
// If there is an index and a change has been made within the key, notify
|
||||
// the index.
|
||||
if (offset < kMaxIndexKeyLength && size > 0 && fIndex)
|
||||
fIndex->Changed(this, oldKey, oldLength);
|
||||
|
||||
// update live queries
|
||||
const uint8* newKey;
|
||||
size_t newLength;
|
||||
GetKey(&newKey, &newLength);
|
||||
GetVolume()->UpdateLiveQueries(NULL, fNode, GetName(), fType, oldKey,
|
||||
oldLength, newKey, newLength);
|
||||
|
||||
// node has been changed
|
||||
if (fNode && size > 0)
|
||||
fNode->MarkModified();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetIndex
|
||||
void
|
||||
Attribute::SetIndex(AttributeIndex *index, bool inIndex)
|
||||
{
|
||||
fIndex = index;
|
||||
fInIndex = inIndex;
|
||||
}
|
||||
|
||||
// GetKey
|
||||
void
|
||||
Attribute::GetKey(const uint8 **key, size_t *length)
|
||||
{
|
||||
if (key && length) {
|
||||
GetFirstDataBlock(key, length);
|
||||
*length = min(*length, kMaxIndexKeyLength);
|
||||
}
|
||||
}
|
||||
|
||||
// GetKey
|
||||
void
|
||||
Attribute::GetKey(uint8 *key, size_t *length)
|
||||
{
|
||||
if (key && length) {
|
||||
const uint8 *originalKey = NULL;
|
||||
GetKey(&originalKey, length);
|
||||
if (length > 0)
|
||||
memcpy(key, originalKey, *length);
|
||||
}
|
||||
}
|
||||
|
||||
// AttachAttributeIterator
|
||||
void
|
||||
Attribute::AttachAttributeIterator(AttributeIterator *iterator)
|
||||
{
|
||||
if (iterator && iterator->GetCurrent() == this && !iterator->IsSuspended())
|
||||
fIterators.Insert(iterator);
|
||||
}
|
||||
|
||||
// DetachAttributeIterator
|
||||
void
|
||||
Attribute::DetachAttributeIterator(AttributeIterator *iterator)
|
||||
{
|
||||
if (iterator && iterator->GetCurrent() == this && iterator->IsSuspended())
|
||||
fIterators.Remove(iterator);
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
Attribute::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
DataContainer::GetAllocationInfo(info);
|
||||
info.AddAttributeAllocation(GetSize());
|
||||
info.AddStringAllocation(fName.GetLength());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Attribute.h
|
||||
|
||||
#ifndef ATTRIBUTE_H
|
||||
#define ATTRIBUTE_H
|
||||
|
||||
#include "AttributeIndex.h"
|
||||
#include "AttributeIterator.h"
|
||||
#include "DataContainer.h"
|
||||
#include "DLList.h"
|
||||
#include "String.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class Node;
|
||||
class Volume;
|
||||
|
||||
class Attribute : public DataContainer, public DLListLinkImpl<Attribute> {
|
||||
public:
|
||||
Attribute(Volume *volume, Node *node, const char *name, uint32 type = 0);
|
||||
~Attribute();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
void SetNode(Node *node) { fNode = node; }
|
||||
Node *GetNode() const { return fNode; }
|
||||
|
||||
const char *GetName() { return fName.GetString(); }
|
||||
|
||||
void SetType(uint32 type);
|
||||
uint32 GetType() const { return fType; }
|
||||
|
||||
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||
size_t *bytesWritten);
|
||||
|
||||
// index support
|
||||
void SetIndex(AttributeIndex *index, bool inIndex);
|
||||
AttributeIndex *GetIndex() const { return fIndex; }
|
||||
bool IsInIndex() const { return fInIndex; }
|
||||
void GetKey(const uint8 **key, size_t *length);
|
||||
void GetKey(uint8 *key, size_t *length);
|
||||
|
||||
// iterator management
|
||||
void AttachAttributeIterator(AttributeIterator *iterator);
|
||||
void DetachAttributeIterator(AttributeIterator *iterator);
|
||||
inline DLList<AttributeIterator> *GetAttributeIteratorList()
|
||||
{ return &fIterators; }
|
||||
|
||||
// debugging
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
Node *fNode;
|
||||
String fName;
|
||||
uint32 fType;
|
||||
AttributeIndex *fIndex;
|
||||
bool fInIndex;
|
||||
|
||||
// iterator management
|
||||
DLList<AttributeIterator> fIterators;
|
||||
};
|
||||
|
||||
#endif // ATTRIBUTE_H
|
||||
@@ -0,0 +1,16 @@
|
||||
// AttributeIndex.cpp
|
||||
|
||||
#include "AttributeIndex.h"
|
||||
|
||||
// constructor
|
||||
AttributeIndex::AttributeIndex(Volume *volume, const char *name, uint32 type,
|
||||
bool fixedKeyLength, size_t keyLength)
|
||||
: Index(volume, name, type, fixedKeyLength, keyLength)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
AttributeIndex::~AttributeIndex()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// AttributeIndex.h
|
||||
|
||||
#ifndef ATTRIBUTE_INDEX_H
|
||||
#define ATTRIBUTE_INDEX_H
|
||||
|
||||
#include "Index.h"
|
||||
|
||||
class Attribute;
|
||||
|
||||
class AttributeIndex : public Index {
|
||||
public:
|
||||
AttributeIndex(Volume *volume, const char *name, uint32 type,
|
||||
bool fixedKeyLength, size_t keyLength = 0);
|
||||
virtual ~AttributeIndex();
|
||||
|
||||
virtual status_t Added(Attribute *attribute) = 0;
|
||||
virtual bool Removed(Attribute *attribute) = 0;
|
||||
virtual status_t Changed(Attribute *attribute,
|
||||
const uint8 *oldKey, size_t length) = 0;
|
||||
};
|
||||
|
||||
#endif // ATTRIBUTE_INDEX_H
|
||||
@@ -0,0 +1,514 @@
|
||||
// AttributeIndexImpl.cpp
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include "AttributeIndexImpl.h"
|
||||
#include "Debug.h"
|
||||
#include "Entry.h"
|
||||
#include "EntryListener.h"
|
||||
#include "IndexImpl.h"
|
||||
#include "Misc.h"
|
||||
#include "Node.h"
|
||||
#include "NodeListener.h"
|
||||
#include "ramfs.h"
|
||||
#include "TwoKeyAVLTree.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// compare_integral
|
||||
template<typename Key>
|
||||
static inline
|
||||
int
|
||||
compare_integral(const Key &a, const Key &b)
|
||||
{
|
||||
if (a < b)
|
||||
return -1;
|
||||
else if (a > b)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// compare_keys
|
||||
static
|
||||
int
|
||||
compare_keys(const uint8 *key1, size_t length1, const uint8 *key2,
|
||||
size_t length2, uint32 type)
|
||||
{
|
||||
switch (type) {
|
||||
case B_INT32_TYPE:
|
||||
return compare_integral(*(int32*)key1, *(int32*)key2);
|
||||
case B_UINT32_TYPE:
|
||||
return compare_integral(*(uint32*)key1, *(uint32*)key2);
|
||||
case B_INT64_TYPE:
|
||||
return compare_integral(*(int64*)key1, *(int64*)key2);
|
||||
case B_UINT64_TYPE:
|
||||
return compare_integral(*(uint64*)key1, *(uint64*)key2);
|
||||
case B_FLOAT_TYPE:
|
||||
return compare_integral(*(float*)key1, *(float*)key2);
|
||||
case B_DOUBLE_TYPE:
|
||||
return compare_integral(*(double*)key1, *(double*)key2);
|
||||
case B_STRING_TYPE:
|
||||
{
|
||||
int result = strncmp((const char*)key1, (const char*)key2,
|
||||
min(length1, length2));
|
||||
if (result == 0) {
|
||||
result = compare_integral(strnlen((const char*)key1, length1),
|
||||
strnlen((const char*)key2, length2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// PrimaryKey
|
||||
class AttributeIndexImpl::PrimaryKey {
|
||||
public:
|
||||
PrimaryKey(Attribute *attribute, const uint8 *key,
|
||||
size_t length)
|
||||
: attribute(attribute), key(key), length(length) {}
|
||||
PrimaryKey(Attribute *attribute)
|
||||
: attribute(attribute) { attribute->GetKey(&key, &length); }
|
||||
PrimaryKey(const uint8 *key, size_t length)
|
||||
: attribute(NULL), key(key), length(length) {}
|
||||
|
||||
Attribute *attribute;
|
||||
const uint8 *key;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
// GetPrimaryKey
|
||||
class AttributeIndexImpl::GetPrimaryKey {
|
||||
public:
|
||||
inline PrimaryKey operator()(Attribute *a)
|
||||
{
|
||||
return PrimaryKey(a);
|
||||
}
|
||||
|
||||
inline PrimaryKey operator()(Attribute *a) const
|
||||
{
|
||||
return PrimaryKey(a);
|
||||
}
|
||||
};
|
||||
|
||||
// PrimaryKeyCompare
|
||||
class AttributeIndexImpl::PrimaryKeyCompare
|
||||
{
|
||||
public:
|
||||
PrimaryKeyCompare(uint32 type) : fType(type) {}
|
||||
|
||||
inline int operator()(const PrimaryKey &a,
|
||||
const PrimaryKey &b) const
|
||||
{
|
||||
if (a.attribute != NULL && a.attribute == b.attribute)
|
||||
return 0;
|
||||
return compare_keys(a.key, a.length, b.key, b.length, fType);
|
||||
}
|
||||
|
||||
uint32 fType;
|
||||
};
|
||||
|
||||
// AttributeNodeIterator
|
||||
template<typename AttributeIterator>
|
||||
class AttributeNodeIterator {
|
||||
public:
|
||||
inline Node **GetCurrent()
|
||||
{
|
||||
if (Attribute **attribute = fIterator.GetCurrent()) {
|
||||
fNode = (*attribute)->GetNode();
|
||||
return &fNode;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline Node **GetNext()
|
||||
{
|
||||
if (Attribute **attribute = fIterator.GetNext()) {
|
||||
fNode = (*attribute)->GetNode();
|
||||
return &fNode;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AttributeIterator fIterator;
|
||||
Node *fNode;
|
||||
};
|
||||
|
||||
|
||||
// AttributeTree
|
||||
class AttributeIndexImpl::AttributeTree
|
||||
: public TwoKeyAVLTree<Attribute*, PrimaryKey, PrimaryKeyCompare,
|
||||
GetPrimaryKey> {
|
||||
public:
|
||||
AttributeTree(uint32 type)
|
||||
: TwoKeyAVLTree<Attribute*, PrimaryKey, PrimaryKeyCompare,
|
||||
GetPrimaryKey>(PrimaryKeyCompare(type),
|
||||
GetPrimaryKey(), AVLTreeStandardCompare<Attribute*>(),
|
||||
AVLTreeStandardGetKey<Attribute*, Attribute*>(),
|
||||
AVLTreeStandardNodeAllocator<Attribute*,
|
||||
AVLTreeStandardNode<Attribute*> >(),
|
||||
AVLTreeStandardGetValue<Attribute*,
|
||||
AVLTreeStandardNode<Attribute*> >())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Iterator
|
||||
class AttributeIndexImpl::Iterator
|
||||
: public NodeEntryIterator<
|
||||
AttributeNodeIterator<AttributeTree::Iterator> >,
|
||||
public DLListLinkImpl<Iterator>, public EntryListener,
|
||||
public NodeListener {
|
||||
public:
|
||||
Iterator();
|
||||
virtual ~Iterator();
|
||||
|
||||
virtual Entry *GetCurrent();
|
||||
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||
|
||||
virtual status_t Suspend();
|
||||
virtual status_t Resume();
|
||||
|
||||
bool SetTo(AttributeIndexImpl *index, const uint8 *key, size_t length,
|
||||
bool ignoreValue = false);
|
||||
void Unset();
|
||||
|
||||
virtual void EntryRemoved(Entry *entry);
|
||||
virtual void NodeRemoved(Node *node);
|
||||
|
||||
private:
|
||||
typedef NodeEntryIterator<
|
||||
AttributeNodeIterator<AttributeTree::Iterator> > BaseClass;
|
||||
|
||||
private:
|
||||
AttributeIndexImpl *fIndex;
|
||||
};
|
||||
|
||||
|
||||
// IteratorList
|
||||
class AttributeIndexImpl::IteratorList : public DLList<Iterator> {};
|
||||
|
||||
|
||||
// AttributeIndexImpl
|
||||
|
||||
// constructor
|
||||
AttributeIndexImpl::AttributeIndexImpl(Volume *volume, const char *name,
|
||||
uint32 type, size_t keyLength)
|
||||
: AttributeIndex(volume, name, type, (keyLength > 0), keyLength),
|
||||
fAttributes(new(nothrow) AttributeTree(type)),
|
||||
fIterators(new(nothrow) IteratorList)
|
||||
{
|
||||
if (fInitStatus == B_OK && (!fAttributes || !fIterators))
|
||||
fInitStatus = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
// destructor
|
||||
AttributeIndexImpl::~AttributeIndexImpl()
|
||||
{
|
||||
if (fIterators) {
|
||||
// unset the iterators
|
||||
for (Iterator *iterator = fIterators->GetFirst();
|
||||
iterator;
|
||||
iterator = fIterators->GetNext(iterator)) {
|
||||
iterator->SetTo(NULL, NULL, 0);
|
||||
}
|
||||
delete fIterators;
|
||||
}
|
||||
// unset all attributes and delete the tree
|
||||
if (fAttributes) {
|
||||
AttributeTree::Iterator it;
|
||||
fAttributes->GetIterator(&it);
|
||||
for (Attribute **attribute = it.GetCurrent(); attribute; it.GetNext())
|
||||
(*attribute)->SetIndex(NULL, false);
|
||||
delete fAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
// CountEntries
|
||||
int32
|
||||
AttributeIndexImpl::CountEntries() const
|
||||
{
|
||||
return fAttributes->CountItems();
|
||||
}
|
||||
|
||||
// Changed
|
||||
status_t
|
||||
AttributeIndexImpl::Changed(Attribute *attribute, const uint8 *oldKey,
|
||||
size_t oldLength)
|
||||
{
|
||||
status_t error = B_BAD_VALUE;
|
||||
if (attribute && attribute->GetIndex() == this) {
|
||||
// update the iterators and remove the attribute from the tree
|
||||
error = B_OK;
|
||||
if (attribute->IsInIndex()) {
|
||||
AttributeTree::Iterator it;
|
||||
Attribute **foundAttribute = fAttributes->Find(
|
||||
PrimaryKey(attribute, oldKey, oldLength), attribute, &it);
|
||||
if (foundAttribute && *foundAttribute == attribute) {
|
||||
Node *node = attribute->GetNode();
|
||||
// update the iterators
|
||||
for (Iterator *iterator = fIterators->GetFirst();
|
||||
iterator;
|
||||
iterator = fIterators->GetNext(iterator)) {
|
||||
if (iterator->GetCurrentNode() == node)
|
||||
iterator->NodeRemoved(node);
|
||||
}
|
||||
// remove and re-insert the attribute
|
||||
fAttributes->Remove(it);
|
||||
}
|
||||
}
|
||||
// re-insert the attribute
|
||||
if (fKeyLength > 0 && attribute->GetSize() != fKeyLength) {
|
||||
attribute->SetIndex(this, false);
|
||||
} else {
|
||||
error = fAttributes->Insert(attribute);
|
||||
if (error == B_OK)
|
||||
attribute->SetIndex(this, true);
|
||||
else
|
||||
attribute->SetIndex(NULL, false);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Added
|
||||
status_t
|
||||
AttributeIndexImpl::Added(Attribute *attribute)
|
||||
{
|
||||
PRINT(("AttributeIndex::Add(%p)\n", attribute));
|
||||
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
size_t size = attribute->GetSize();
|
||||
if (fKeyLength > 0 && size != fKeyLength) {
|
||||
attribute->SetIndex(this, false);
|
||||
} else {
|
||||
error = fAttributes->Insert(attribute);
|
||||
if (error == B_OK)
|
||||
attribute->SetIndex(this, true);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Removed
|
||||
bool
|
||||
AttributeIndexImpl::Removed(Attribute *attribute)
|
||||
{
|
||||
PRINT(("AttributeIndex::Removed(%p)\n", attribute));
|
||||
bool result = (attribute && attribute->GetIndex() == this);
|
||||
if (result) {
|
||||
if (attribute->IsInIndex())
|
||||
fAttributes->Remove(attribute, attribute);
|
||||
attribute->SetIndex(NULL, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// InternalGetIterator
|
||||
AbstractIndexEntryIterator *
|
||||
AttributeIndexImpl::InternalGetIterator()
|
||||
{
|
||||
Iterator *iterator = new(nothrow) Iterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, NULL, 0, true)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// InternalFind
|
||||
AbstractIndexEntryIterator *
|
||||
AttributeIndexImpl::InternalFind(const uint8 *key, size_t length)
|
||||
{
|
||||
if (!key || (fKeyLength > 0 && length != fKeyLength))
|
||||
return NULL;
|
||||
Iterator *iterator = new(nothrow) Iterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, key, length)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// _AddIterator
|
||||
void
|
||||
AttributeIndexImpl::_AddIterator(Iterator *iterator)
|
||||
{
|
||||
fIterators->Insert(iterator);
|
||||
}
|
||||
|
||||
// _RemoveIterator
|
||||
void
|
||||
AttributeIndexImpl::_RemoveIterator(Iterator *iterator)
|
||||
{
|
||||
fIterators->Remove(iterator);
|
||||
}
|
||||
|
||||
|
||||
// Iterator
|
||||
|
||||
// constructor
|
||||
AttributeIndexImpl::Iterator::Iterator()
|
||||
: BaseClass(),
|
||||
fIndex(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
AttributeIndexImpl::Iterator::~Iterator()
|
||||
{
|
||||
SetTo(NULL, NULL, 0);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
AttributeIndexImpl::Iterator::GetCurrent()
|
||||
{
|
||||
return BaseClass::GetCurrent();
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
AttributeIndexImpl::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||
{
|
||||
Entry *entry = GetCurrent();
|
||||
if (entry) {
|
||||
if (Attribute **attribute = fIterator.fIterator.GetCurrent()) {
|
||||
if ((*attribute)->GetNode() == entry->GetNode()) {
|
||||
(*attribute)->GetKey(buffer, keyLength);
|
||||
} else {
|
||||
FATAL(("Node of current attribute and node of current entry "
|
||||
"differ: %Ld vs. %Ld\n",
|
||||
(*attribute)->GetNode()->GetID(),
|
||||
entry->GetNode()->GetID()));
|
||||
entry = NULL;
|
||||
}
|
||||
} else {
|
||||
FATAL(("We have a current entry (`%s', node: %Ld), but no current "
|
||||
"attribute.\n", entry->GetName(),
|
||||
entry->GetNode()->GetID()));
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
AttributeIndexImpl::Iterator::Suspend()
|
||||
{
|
||||
status_t error = BaseClass::Suspend();
|
||||
if (error == B_OK) {
|
||||
if (fNode) {
|
||||
error = fIndex->GetVolume()->AddNodeListener(this, fNode,
|
||||
NODE_LISTEN_REMOVED);
|
||||
if (error == B_OK && fEntry) {
|
||||
error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
|
||||
ENTRY_LISTEN_REMOVED);
|
||||
if (error != B_OK)
|
||||
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
}
|
||||
if (error != B_OK)
|
||||
BaseClass::Resume();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
AttributeIndexImpl::Iterator::Resume()
|
||||
{
|
||||
status_t error = BaseClass::Resume();
|
||||
if (error == B_OK) {
|
||||
if (fEntry)
|
||||
error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
|
||||
if (fNode) {
|
||||
if (error == B_OK)
|
||||
error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
else
|
||||
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetTo
|
||||
bool
|
||||
AttributeIndexImpl::Iterator::SetTo(AttributeIndexImpl *index,
|
||||
const uint8 *key, size_t length, bool ignoreValue)
|
||||
{
|
||||
Resume();
|
||||
Unset();
|
||||
// set the new values
|
||||
fIndex = index;
|
||||
if (fIndex)
|
||||
fIndex->_AddIterator(this);
|
||||
fInitialized = fIndex;
|
||||
// get the attribute node's first entry
|
||||
if (fIndex) {
|
||||
// get the first node
|
||||
bool found = true;
|
||||
if (ignoreValue)
|
||||
fIndex->fAttributes->GetIterator(&fIterator.fIterator);
|
||||
else {
|
||||
found = fIndex->fAttributes->FindFirst(PrimaryKey(key, length),
|
||||
&(fIterator.fIterator));
|
||||
}
|
||||
// get the first entry
|
||||
if (found) {
|
||||
if (Node **nodeP = fIterator.GetCurrent()) {
|
||||
fNode = *nodeP;
|
||||
fEntry = fNode->GetFirstReferrer();
|
||||
if (!fEntry)
|
||||
BaseClass::GetNext();
|
||||
if (Attribute **attribute = fIterator.fIterator.GetCurrent()) {
|
||||
const uint8 *attrKey;
|
||||
size_t attrKeyLength;
|
||||
(*attribute)->GetKey(&attrKey, &attrKeyLength);
|
||||
if (!ignoreValue
|
||||
&& compare_keys(attrKey, attrKeyLength, key, length,
|
||||
fIndex->GetType()) != 0) {
|
||||
Unset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fEntry;
|
||||
}
|
||||
|
||||
// Unset
|
||||
void
|
||||
AttributeIndexImpl::Iterator::Unset()
|
||||
{
|
||||
if (fIndex) {
|
||||
fIndex->_RemoveIterator(this);
|
||||
fIndex = NULL;
|
||||
}
|
||||
BaseClass::Unset();
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
void
|
||||
AttributeIndexImpl::Iterator::EntryRemoved(Entry */*entry*/)
|
||||
{
|
||||
Resume();
|
||||
fIsNext = BaseClass::GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
void
|
||||
AttributeIndexImpl::Iterator::NodeRemoved(Node */*node*/)
|
||||
{
|
||||
Resume();
|
||||
fEntry = NULL;
|
||||
fIsNext = BaseClass::GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// AttributeIndexImpl.h
|
||||
|
||||
#ifndef ATTRIBUTE_INDEX_IMPL_H
|
||||
#define ATTRIBUTE_INDEX_IMPL_H
|
||||
|
||||
#include "AttributeIndex.h"
|
||||
|
||||
// AttributeIndexImpl
|
||||
class AttributeIndexImpl : public AttributeIndex {
|
||||
public:
|
||||
AttributeIndexImpl(Volume *volume, const char *name, uint32 type,
|
||||
size_t keyLength);
|
||||
virtual ~AttributeIndexImpl();
|
||||
|
||||
virtual int32 CountEntries() const;
|
||||
|
||||
virtual status_t Changed(Attribute *attribute,
|
||||
const uint8 *oldKey, size_t oldLength);
|
||||
|
||||
private:
|
||||
virtual status_t Added(Attribute *attribute);
|
||||
virtual bool Removed(Attribute *attribute);
|
||||
|
||||
protected:
|
||||
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||
size_t length);
|
||||
|
||||
private:
|
||||
class Iterator;
|
||||
class IteratorList;
|
||||
class AttributeTree;
|
||||
|
||||
class PrimaryKey;
|
||||
class GetPrimaryKey;
|
||||
class PrimaryKeyCompare;
|
||||
|
||||
friend class Iterator;
|
||||
|
||||
private:
|
||||
void _AddIterator(Iterator *iterator);
|
||||
void _RemoveIterator(Iterator *iterator);
|
||||
|
||||
private:
|
||||
AttributeTree *fAttributes;
|
||||
IteratorList *fIterators;
|
||||
};
|
||||
|
||||
#endif // ATTRIBUTE_INDEX_IMPL_H
|
||||
@@ -0,0 +1,136 @@
|
||||
// AttributeIterator.cpp
|
||||
|
||||
#include "AttributeIterator.h"
|
||||
#include "Node.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
AttributeIterator::AttributeIterator(Node *node)
|
||||
: fNode(node),
|
||||
fAttribute(NULL),
|
||||
fSuspended(false),
|
||||
fIsNext(false),
|
||||
fDone(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
AttributeIterator::~AttributeIterator()
|
||||
{
|
||||
Unset();
|
||||
}
|
||||
|
||||
// SetTo
|
||||
status_t
|
||||
AttributeIterator::SetTo(Node *node)
|
||||
{
|
||||
Unset();
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
fNode = node;
|
||||
fAttribute = NULL;
|
||||
fSuspended = false;
|
||||
fIsNext = false;
|
||||
fDone = false;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Unset
|
||||
void
|
||||
AttributeIterator::Unset()
|
||||
{
|
||||
if (fNode && fSuspended)
|
||||
Resume();
|
||||
fNode = NULL;
|
||||
fAttribute = NULL;
|
||||
fSuspended = false;
|
||||
fIsNext = false;
|
||||
fDone = false;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
AttributeIterator::Suspend()
|
||||
{
|
||||
status_t error = (fNode ? B_OK : B_ERROR);
|
||||
if (error == B_OK) {
|
||||
if (fNode->GetVolume()->IteratorLock()) {
|
||||
if (!fSuspended) {
|
||||
if (fAttribute)
|
||||
fAttribute->AttachAttributeIterator(this);
|
||||
fNode->GetVolume()->IteratorUnlock();
|
||||
fSuspended = true;
|
||||
} else
|
||||
error = B_ERROR;
|
||||
} else
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
AttributeIterator::Resume()
|
||||
{
|
||||
status_t error = (fNode ? B_OK : B_ERROR);
|
||||
if (error == B_OK) {
|
||||
if (fNode->GetVolume()->IteratorLock()) {
|
||||
if (fSuspended) {
|
||||
if (fAttribute)
|
||||
fAttribute->DetachAttributeIterator(this);
|
||||
fSuspended = false;
|
||||
}
|
||||
fNode->GetVolume()->IteratorUnlock();
|
||||
} else
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNext
|
||||
status_t
|
||||
AttributeIterator::GetNext(Attribute **attribute)
|
||||
{
|
||||
status_t error = B_ENTRY_NOT_FOUND;
|
||||
if (!fDone && fNode && attribute) {
|
||||
if (fIsNext) {
|
||||
fIsNext = false;
|
||||
if (fAttribute)
|
||||
error = B_OK;
|
||||
} else
|
||||
error = fNode->GetNextAttribute(&fAttribute);
|
||||
*attribute = fAttribute;
|
||||
}
|
||||
fDone = (error != B_OK);
|
||||
return error;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
status_t
|
||||
AttributeIterator::Rewind()
|
||||
{
|
||||
status_t error = (fNode ? B_OK : B_ERROR);
|
||||
if (error == B_OK) {
|
||||
if (fNode->GetVolume()->IteratorLock()) {
|
||||
if (fSuspended && fAttribute)
|
||||
fAttribute->DetachAttributeIterator(this);
|
||||
fAttribute = NULL;
|
||||
fIsNext = false;
|
||||
fDone = false;
|
||||
fNode->GetVolume()->IteratorUnlock();
|
||||
} else
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetCurrent
|
||||
void
|
||||
AttributeIterator::SetCurrent(Attribute *attribute, bool isNext)
|
||||
{
|
||||
fIsNext = isNext;
|
||||
fAttribute = attribute;
|
||||
fDone = !fAttribute;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// AttributeIterator.h
|
||||
|
||||
#ifndef ATTRIBUTE_ITERATOR_H
|
||||
#define ATTRIBUTE_ITERATOR_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "DLList.h"
|
||||
|
||||
class Attribute;
|
||||
class Node;
|
||||
|
||||
class AttributeIterator : public DLListLinkImpl<AttributeIterator> {
|
||||
public:
|
||||
AttributeIterator(Node *node = NULL);
|
||||
~AttributeIterator();
|
||||
|
||||
status_t SetTo(Node *node);
|
||||
void Unset();
|
||||
|
||||
Node *GetNode() const { return fNode; }
|
||||
|
||||
status_t Suspend();
|
||||
status_t Resume();
|
||||
bool IsSuspended() const { return fSuspended; }
|
||||
|
||||
status_t GetNext(Attribute **attribute);
|
||||
Attribute *GetCurrent() const { return fAttribute; }
|
||||
|
||||
status_t Rewind();
|
||||
|
||||
private:
|
||||
void SetCurrent(Attribute *attribute, bool isNext);
|
||||
|
||||
private:
|
||||
friend class Node;
|
||||
|
||||
private:
|
||||
Node *fNode;
|
||||
Attribute *fAttribute;
|
||||
bool fSuspended;
|
||||
bool fIsNext;
|
||||
bool fDone;
|
||||
};
|
||||
|
||||
#endif // ATTRIBUTE_ITERATOR_H
|
||||
@@ -0,0 +1,317 @@
|
||||
// Block.h
|
||||
|
||||
#ifndef BLOCK_H
|
||||
#define BLOCK_H
|
||||
|
||||
class Block;
|
||||
class BlockHeader;
|
||||
class BlockReference;
|
||||
class TFreeBlock;
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
// debugging
|
||||
//#define inline
|
||||
#define BA_DEFINE_INLINES 1
|
||||
|
||||
// BlockHeader
|
||||
class BlockHeader {
|
||||
public:
|
||||
inline Block *ToBlock() { return (Block*)this; }
|
||||
inline TFreeBlock *ToFreeBlock() { return (TFreeBlock*)this; }
|
||||
|
||||
inline void SetPreviousBlock(Block *block);
|
||||
inline Block *GetPreviousBlock();
|
||||
|
||||
inline void SetNextBlock(Block *block);
|
||||
inline Block *GetNextBlock();
|
||||
inline bool HasNextBlock() { return (fSize & HAS_NEXT_FLAG); }
|
||||
|
||||
inline void SetSize(size_t size, bool hasNext = false);
|
||||
inline size_t GetSize() const;
|
||||
static inline size_t GetUsableSizeFor(size_t size);
|
||||
inline size_t GetUsableSize() const;
|
||||
|
||||
inline void *GetData();
|
||||
|
||||
inline void SetFree(bool flag);
|
||||
inline bool IsFree() const;
|
||||
|
||||
inline void SetReference(BlockReference *ref);
|
||||
inline BlockReference *GetReference() const { return fReference; }
|
||||
inline void FixReference();
|
||||
|
||||
inline void SetTo(Block *previous, size_t size, bool isFree, bool hasNext,
|
||||
BlockReference *reference = NULL);
|
||||
|
||||
private:
|
||||
enum {
|
||||
FREE_FLAG = 0x80000000,
|
||||
BACK_SKIP_MASK = 0x7fffffff,
|
||||
};
|
||||
|
||||
enum {
|
||||
HAS_NEXT_FLAG = 0x80000000,
|
||||
SIZE_MASK = 0x7fffffff,
|
||||
};
|
||||
|
||||
private:
|
||||
BlockHeader();
|
||||
~BlockHeader();
|
||||
|
||||
protected:
|
||||
size_t fBackSkip;
|
||||
size_t fSize;
|
||||
BlockReference *fReference;
|
||||
};
|
||||
|
||||
// Block
|
||||
class Block : public BlockHeader {
|
||||
public:
|
||||
static inline Block *MakeBlock(void *address, ssize_t offset,
|
||||
Block *previous, size_t size, bool isFree,
|
||||
bool hasNext,
|
||||
BlockReference *reference = NULL);
|
||||
|
||||
private:
|
||||
Block();
|
||||
~Block();
|
||||
};
|
||||
|
||||
// TFreeBlock
|
||||
class TFreeBlock : public Block {
|
||||
public:
|
||||
|
||||
inline void SetPreviousFreeBlock(TFreeBlock *block) { fPrevious = block; }
|
||||
inline void SetNextFreeBlock(TFreeBlock *block) { fNext = block; }
|
||||
inline TFreeBlock *GetPreviousFreeBlock() { return fPrevious; }
|
||||
inline TFreeBlock *GetNextFreeBlock() { return fNext; }
|
||||
|
||||
inline void SetTo(Block *previous, size_t size, bool hasNext,
|
||||
TFreeBlock *previousFree, TFreeBlock *nextFree);
|
||||
|
||||
// static inline TFreeBlock *MakeFreeBlock(void *address, ssize_t offset,
|
||||
// Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree,
|
||||
// TFreeBlock *nextFree);
|
||||
|
||||
private:
|
||||
TFreeBlock();
|
||||
~TFreeBlock();
|
||||
|
||||
private:
|
||||
TFreeBlock *fPrevious;
|
||||
TFreeBlock *fNext;
|
||||
};
|
||||
|
||||
// BlockReference
|
||||
class BlockReference {
|
||||
public:
|
||||
inline BlockReference() : fBlock(NULL) {}
|
||||
inline BlockReference(Block *block) : fBlock(block) {}
|
||||
|
||||
inline void SetBlock(Block *block) { fBlock = block; }
|
||||
inline Block *GetBlock() const { return fBlock; }
|
||||
|
||||
inline void *GetData() const { return fBlock->GetData(); }
|
||||
inline void *GetDataAt(ssize_t offset) const;
|
||||
|
||||
private:
|
||||
Block *fBlock;
|
||||
};
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// inline methods
|
||||
|
||||
// debugging
|
||||
#if BA_DEFINE_INLINES
|
||||
|
||||
// BlockHeader
|
||||
|
||||
// SetPreviousBlock
|
||||
inline
|
||||
void
|
||||
BlockHeader::SetPreviousBlock(Block *block)
|
||||
{
|
||||
size_t offset = (block ? (char*)this - (char*)block : 0);
|
||||
fBackSkip = fBackSkip & FREE_FLAG | offset;
|
||||
}
|
||||
|
||||
// GetPreviousBlock
|
||||
inline
|
||||
Block *
|
||||
BlockHeader::GetPreviousBlock()
|
||||
{
|
||||
if (fBackSkip & BACK_SKIP_MASK)
|
||||
return (Block*)((char*)this - (fBackSkip & BACK_SKIP_MASK));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// SetNextBlock
|
||||
inline
|
||||
void
|
||||
BlockHeader::SetNextBlock(Block *block)
|
||||
{
|
||||
if (block)
|
||||
fSize = ((char*)block - (char*)this) | HAS_NEXT_FLAG;
|
||||
else
|
||||
fSize &= SIZE_MASK;
|
||||
}
|
||||
|
||||
// GetNextBlock
|
||||
inline
|
||||
Block *
|
||||
BlockHeader::GetNextBlock()
|
||||
{
|
||||
if (fSize & HAS_NEXT_FLAG)
|
||||
return (Block*)((char*)this + (SIZE_MASK & fSize));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// SetSize
|
||||
inline
|
||||
void
|
||||
BlockHeader::SetSize(size_t size, bool hasNext)
|
||||
{
|
||||
fSize = size;
|
||||
if (hasNext)
|
||||
fSize |= HAS_NEXT_FLAG;
|
||||
}
|
||||
|
||||
// GetSize
|
||||
inline
|
||||
size_t
|
||||
BlockHeader::GetSize() const
|
||||
{
|
||||
return (fSize & SIZE_MASK);
|
||||
}
|
||||
|
||||
// GetUsableSizeFor
|
||||
inline
|
||||
size_t
|
||||
BlockHeader::GetUsableSizeFor(size_t size)
|
||||
{
|
||||
return (size - sizeof(BlockHeader));
|
||||
}
|
||||
|
||||
// GetUsableSize
|
||||
inline
|
||||
size_t
|
||||
BlockHeader::GetUsableSize() const
|
||||
{
|
||||
return GetUsableSizeFor(GetSize());
|
||||
}
|
||||
|
||||
// GetData
|
||||
inline
|
||||
void *
|
||||
BlockHeader::GetData()
|
||||
{
|
||||
return (char*)this + sizeof(BlockHeader);
|
||||
}
|
||||
|
||||
// SetFree
|
||||
inline
|
||||
void
|
||||
BlockHeader::SetFree(bool flag)
|
||||
{
|
||||
if (flag)
|
||||
fBackSkip |= FREE_FLAG;
|
||||
else
|
||||
fBackSkip &= ~FREE_FLAG;
|
||||
}
|
||||
|
||||
// IsFree
|
||||
inline
|
||||
bool
|
||||
BlockHeader::IsFree() const
|
||||
{
|
||||
return (fBackSkip & FREE_FLAG);
|
||||
}
|
||||
|
||||
// SetTo
|
||||
inline
|
||||
void
|
||||
BlockHeader::SetTo(Block *previous, size_t size, bool isFree, bool hasNext,
|
||||
BlockReference *reference)
|
||||
{
|
||||
SetPreviousBlock(previous);
|
||||
SetSize(size, hasNext);
|
||||
SetFree(isFree);
|
||||
SetReference(reference);
|
||||
}
|
||||
|
||||
// SetReference
|
||||
inline
|
||||
void
|
||||
BlockHeader::SetReference(BlockReference *ref)
|
||||
{
|
||||
fReference = ref;
|
||||
FixReference();
|
||||
}
|
||||
|
||||
// FixReference
|
||||
inline
|
||||
void
|
||||
BlockHeader::FixReference()
|
||||
{
|
||||
if (fReference)
|
||||
fReference->SetBlock(ToBlock());
|
||||
}
|
||||
|
||||
|
||||
// Block
|
||||
|
||||
// MakeBlock
|
||||
/*inline
|
||||
Block *
|
||||
Block::MakeBlock(void *address, ssize_t offset, Block *previous, size_t size,
|
||||
bool isFree, bool hasNext, BlockReference *reference)
|
||||
{
|
||||
Block *block = (Block*)((char*)address + offset);
|
||||
block->SetTo(previous, size, isFree, hasNext, reference);
|
||||
return block;
|
||||
}*/
|
||||
|
||||
|
||||
// TFreeBlock
|
||||
|
||||
// SetTo
|
||||
inline
|
||||
void
|
||||
TFreeBlock::SetTo(Block *previous, size_t size, bool hasNext,
|
||||
TFreeBlock *previousFree, TFreeBlock *nextFree)
|
||||
{
|
||||
Block::SetTo(previous, size, true, hasNext, NULL);
|
||||
SetPreviousFreeBlock(previousFree);
|
||||
SetNextFreeBlock(nextFree);
|
||||
}
|
||||
|
||||
// MakeFreeBlock
|
||||
/*inline
|
||||
TFreeBlock *
|
||||
TFreeBlock::MakeFreeBlock(void *address, ssize_t offset, Block *previous,
|
||||
size_t size, bool hasNext, TFreeBlock *previousFree,
|
||||
TFreeBlock *nextFree)
|
||||
{
|
||||
TFreeBlock *block = (TFreeBlock*)((char*)address + offset);
|
||||
block->SetTo(previous, size, hasNext, previousFree, nextFree);
|
||||
if (hasNext)
|
||||
block->GetNextBlock()->SetPreviousBlock(block);
|
||||
return block;
|
||||
}*/
|
||||
|
||||
|
||||
// BlockReference
|
||||
|
||||
// GetDataAt
|
||||
inline
|
||||
void *
|
||||
BlockReference::GetDataAt(ssize_t offset) const
|
||||
{
|
||||
return (char*)fBlock->GetData() + offset;
|
||||
}
|
||||
|
||||
#endif // BA_DEFINE_INLINES
|
||||
|
||||
#endif // BLOCK_H
|
||||
@@ -0,0 +1,426 @@
|
||||
// BlockAllocator.cpp
|
||||
|
||||
// debugging
|
||||
#define BA_DEFINE_INLINES 1
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "BlockAllocator.h"
|
||||
#include "BlockAllocatorArea.h"
|
||||
#include "BlockAllocatorAreaBucket.h"
|
||||
#include "Debug.h"
|
||||
#include "DLList.h"
|
||||
|
||||
// BlockAllocator
|
||||
|
||||
// constructor
|
||||
BlockAllocator::BlockAllocator(size_t areaSize)
|
||||
: fReferenceManager(),
|
||||
fBuckets(NULL),
|
||||
fBucketCount(0),
|
||||
fAreaSize(areaSize),
|
||||
fAreaCount(0),
|
||||
fFreeBytes(0)
|
||||
{
|
||||
// create and init buckets
|
||||
fBucketCount = bucket_containing_size(areaSize) + 1;
|
||||
fBuckets = new(nothrow) AreaBucket[fBucketCount];
|
||||
size_t minSize = 0;
|
||||
for (int32 i = 0; i < fBucketCount; i++) {
|
||||
size_t maxSize = (1 << i) * kMinNetBlockSize;
|
||||
fBuckets[i].SetIndex(i);
|
||||
fBuckets[i].SetSizeLimits(minSize, maxSize);
|
||||
minSize = maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
BlockAllocator::~BlockAllocator()
|
||||
{
|
||||
if (fBuckets)
|
||||
delete[] fBuckets;
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
BlockAllocator::InitCheck() const
|
||||
{
|
||||
RETURN_ERROR(fBuckets ? B_OK : B_NO_MEMORY);
|
||||
}
|
||||
|
||||
// AllocateBlock
|
||||
BlockReference *
|
||||
BlockAllocator::AllocateBlock(size_t usableSize)
|
||||
{
|
||||
#if ENABLE_BA_PANIC
|
||||
if (fPanic)
|
||||
return NULL;
|
||||
#endif
|
||||
//PRINT(("BlockAllocator::AllocateBlock(%lu)\n", usableSize));
|
||||
Block *block = NULL;
|
||||
if (usableSize > 0 && usableSize <= Area::GetMaxFreeBytesFor(fAreaSize)) {
|
||||
// get a block reference
|
||||
BlockReference *reference = fReferenceManager.AllocateReference();
|
||||
if (reference) {
|
||||
block = _AllocateBlock(usableSize);
|
||||
// set reference / cleanup on failure
|
||||
if (block)
|
||||
block->SetReference(reference);
|
||||
else
|
||||
fReferenceManager.FreeReference(reference);
|
||||
}
|
||||
D(SanityCheck(false));
|
||||
}
|
||||
//PRINT(("BlockAllocator::AllocateBlock() done: %p\n", block));
|
||||
return (block ? block->GetReference() : NULL);
|
||||
}
|
||||
|
||||
// FreeBlock
|
||||
void
|
||||
BlockAllocator::FreeBlock(BlockReference *blockReference)
|
||||
{
|
||||
#if ENABLE_BA_PANIC
|
||||
if (fPanic)
|
||||
return;
|
||||
#endif
|
||||
D(if (!CheckBlock(blockReference)) return;);
|
||||
Block *block = (blockReference ? blockReference->GetBlock() : NULL);
|
||||
//PRINT(("BlockAllocator::FreeBlock(%p)\n", block));
|
||||
Area *area = NULL;
|
||||
if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) {
|
||||
_FreeBlock(area, block, true);
|
||||
D(SanityCheck(false));
|
||||
if (_DefragmentingRecommended())
|
||||
_Defragment();
|
||||
}
|
||||
//PRINT(("BlockAllocator::FreeBlock() done\n"));
|
||||
}
|
||||
|
||||
// ResizeBlock
|
||||
BlockReference *
|
||||
BlockAllocator::ResizeBlock(BlockReference *blockReference, size_t usableSize)
|
||||
{
|
||||
#if ENABLE_BA_PANIC
|
||||
if (fPanic)
|
||||
return NULL;
|
||||
#endif
|
||||
D(if (!CheckBlock(blockReference)) return NULL;);
|
||||
//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", blockReference, usableSize));
|
||||
Block *block = (blockReference ? blockReference->GetBlock() : NULL);
|
||||
Block *resultBlock = NULL;
|
||||
Area *area = NULL;
|
||||
if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) {
|
||||
//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", block, usableSize));
|
||||
if (usableSize) {
|
||||
// try to let the area resize the block
|
||||
size_t blockSize = block->GetSize();
|
||||
size_t areaFreeBytes = area->GetFreeBytes();
|
||||
bool needsDefragmenting = area->NeedsDefragmenting();
|
||||
//PRINT((" block reference: %p / %p\n", blockReference, block->GetReference()));
|
||||
resultBlock = area->ResizeBlock(block, usableSize);
|
||||
block = blockReference->GetBlock();
|
||||
if (resultBlock) {
|
||||
//PRINT((" area succeeded in resizing the block\n"));
|
||||
//PRINT((" block reference now: %p\n", resultBlock->GetReference()));
|
||||
// the area was able to resize the block
|
||||
_RethinkAreaBucket(area, area->GetBucket(),
|
||||
needsDefragmenting);
|
||||
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
||||
// Defragment only, if the area was able to resize the block,
|
||||
// the new block is smaller than the old one and defragmenting
|
||||
// is recommended.
|
||||
if (blockSize > resultBlock->GetSize()
|
||||
&& _DefragmentingRecommended()) {
|
||||
_Defragment();
|
||||
}
|
||||
} else {
|
||||
//PRINT((" area failed to resize the block\n"));
|
||||
// the area failed: allocate a new block, copy the data, and
|
||||
// free the old one
|
||||
resultBlock = _AllocateBlock(usableSize);
|
||||
block = blockReference->GetBlock();
|
||||
if (resultBlock) {
|
||||
memcpy(resultBlock->GetData(), block->GetData(),
|
||||
block->GetUsableSize());
|
||||
resultBlock->SetReference(block->GetReference());
|
||||
_FreeBlock(area, block, false);
|
||||
}
|
||||
}
|
||||
} else
|
||||
FreeBlock(blockReference);
|
||||
D(SanityCheck(false));
|
||||
//PRINT(("BlockAllocator::ResizeBlock() done: %p\n", resultBlock));
|
||||
}
|
||||
return (resultBlock ? resultBlock->GetReference() : NULL);
|
||||
}
|
||||
|
||||
// SanityCheck
|
||||
bool
|
||||
BlockAllocator::SanityCheck(bool deep) const
|
||||
{
|
||||
// iterate through all areas of all buckets
|
||||
int32 areaCount = 0;
|
||||
size_t freeBytes = 0;
|
||||
for (int32 i = 0; i < fBucketCount; i++) {
|
||||
AreaBucket *bucket = fBuckets + i;
|
||||
if (deep) {
|
||||
if (!bucket->SanityCheck(deep))
|
||||
return false;
|
||||
}
|
||||
for (Area *area = bucket->GetFirstArea();
|
||||
area;
|
||||
area = bucket->GetNextArea(area)) {
|
||||
areaCount++;
|
||||
freeBytes += area->GetFreeBytes();
|
||||
}
|
||||
}
|
||||
// area count
|
||||
if (areaCount != fAreaCount) {
|
||||
FATAL(("fAreaCount is %ld, but should be %ld\n", fAreaCount,
|
||||
areaCount));
|
||||
BA_PANIC("BlockAllocator: Bad free bytes.");
|
||||
return false;
|
||||
}
|
||||
// free bytes
|
||||
if (fFreeBytes != freeBytes) {
|
||||
FATAL(("fFreeBytes is %lu, but should be %lu\n", fFreeBytes,
|
||||
freeBytes));
|
||||
BA_PANIC("BlockAllocator: Bad free bytes.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// CheckArea
|
||||
bool
|
||||
BlockAllocator::CheckArea(Area *checkArea)
|
||||
{
|
||||
for (int32 i = 0; i < fBucketCount; i++) {
|
||||
AreaBucket *bucket = fBuckets + i;
|
||||
for (Area *area = bucket->GetFirstArea();
|
||||
area;
|
||||
area = bucket->GetNextArea(area)) {
|
||||
if (area == checkArea)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FATAL(("Area %p is not a valid Area!\n", checkArea));
|
||||
BA_PANIC("Invalid Area.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// CheckBlock
|
||||
bool
|
||||
BlockAllocator::CheckBlock(Block *block, size_t minSize)
|
||||
{
|
||||
Area *area = _AreaForBlock(block);
|
||||
return (area/* && CheckArea(area)*/ && area->CheckBlock(block, minSize));
|
||||
}
|
||||
|
||||
// CheckBlock
|
||||
bool
|
||||
BlockAllocator::CheckBlock(BlockReference *reference, size_t minSize)
|
||||
{
|
||||
return (fReferenceManager.CheckReference(reference)
|
||||
&& CheckBlock(reference->GetBlock(), minSize));
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
BlockAllocator::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
fReferenceManager.GetAllocationInfo(info);
|
||||
info.AddOtherAllocation(sizeof(AreaBucket), fBucketCount);
|
||||
info.AddAreaAllocation(fAreaSize, fAreaCount);
|
||||
}
|
||||
|
||||
// _AreaForBlock
|
||||
inline
|
||||
BlockAllocator::Area *
|
||||
BlockAllocator::_AreaForBlock(Block *block)
|
||||
{
|
||||
Area *area = NULL;
|
||||
area_id id = area_for(block);
|
||||
area_info info;
|
||||
if (id >= 0 && get_area_info(id, &info) == B_OK)
|
||||
area = (Area*)info.address;
|
||||
D(if (!CheckArea(area)) return NULL;);
|
||||
return area;
|
||||
}
|
||||
|
||||
// _AllocateBlock
|
||||
Block *
|
||||
BlockAllocator::_AllocateBlock(size_t usableSize, bool dontCreateArea)
|
||||
{
|
||||
Block *block = NULL;
|
||||
// Get the last area (the one with the most free space) and try
|
||||
// to let it allocate a block. If that fails, allocate a new area.
|
||||
// find a bucket for the allocation
|
||||
// TODO: optimize
|
||||
AreaBucket *bucket = NULL;
|
||||
int32 index = bucket_containing_min_size(usableSize);
|
||||
for (; index < fBucketCount; index++) {
|
||||
if (!fBuckets[index].IsEmpty()) {
|
||||
bucket = fBuckets + index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// get an area: if we have one, from the bucket, else create a new
|
||||
// area
|
||||
Area *area = NULL;
|
||||
if (bucket)
|
||||
area = bucket->GetFirstArea();
|
||||
else if (!dontCreateArea) {
|
||||
area = Area::Create(fAreaSize);
|
||||
if (area) {
|
||||
fAreaCount++;
|
||||
fFreeBytes += area->GetFreeBytes();
|
||||
bucket = fBuckets + area->GetBucketIndex();
|
||||
bucket->AddArea(area);
|
||||
PRINT(("New area allocated. area count now: %ld, free bytes: %lu\n",
|
||||
fAreaCount, fFreeBytes));
|
||||
}
|
||||
}
|
||||
// allocate a block
|
||||
if (area) {
|
||||
size_t areaFreeBytes = area->GetFreeBytes();
|
||||
bool needsDefragmenting = area->NeedsDefragmenting();
|
||||
block = area->AllocateBlock(usableSize);
|
||||
// move the area into another bucket, if necessary
|
||||
if (block) {
|
||||
_RethinkAreaBucket(area, bucket, needsDefragmenting);
|
||||
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
||||
}
|
||||
#if ENABLE_BA_PANIC
|
||||
else if (!fPanic) {
|
||||
FATAL(("Block allocation failed unexpectedly.\n"));
|
||||
PRINT((" usableSize: %lu, areaFreeBytes: %lu\n", usableSize, areaFreeBytes));
|
||||
BA_PANIC("Block allocation failed unexpectedly.");
|
||||
//block = area->AllocateBlock(usableSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
// _FreeBlock
|
||||
void
|
||||
BlockAllocator::_FreeBlock(Area *area, Block *block, bool freeReference)
|
||||
{
|
||||
size_t areaFreeBytes = area->GetFreeBytes();
|
||||
AreaBucket *bucket = area->GetBucket();
|
||||
bool needsDefragmenting = area->NeedsDefragmenting();
|
||||
// free the block and the block reference
|
||||
BlockReference *reference = block->GetReference();
|
||||
area->FreeBlock(block);
|
||||
if (reference && freeReference)
|
||||
fReferenceManager.FreeReference(reference);
|
||||
// move the area into another bucket, if necessary
|
||||
_RethinkAreaBucket(area, bucket, needsDefragmenting);
|
||||
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
||||
}
|
||||
|
||||
// _RethinkAreaBucket
|
||||
inline
|
||||
void
|
||||
BlockAllocator::_RethinkAreaBucket(Area *area, AreaBucket *bucket,
|
||||
bool needsDefragmenting)
|
||||
{
|
||||
AreaBucket *newBucket = fBuckets + area->GetBucketIndex();
|
||||
if (newBucket != bucket
|
||||
|| needsDefragmenting != area->NeedsDefragmenting()) {
|
||||
bucket->RemoveArea(area);
|
||||
newBucket->AddArea(area);
|
||||
}
|
||||
}
|
||||
|
||||
// _DefragmentingRecommended
|
||||
inline
|
||||
bool
|
||||
BlockAllocator::_DefragmentingRecommended()
|
||||
{
|
||||
// Don't know, whether this makes much sense: We don't try to defragment,
|
||||
// when not at least a complete area could be deleted, and some tolerance
|
||||
// being left (a fixed value plus 1/32 of the used bytes).
|
||||
size_t usedBytes = fAreaCount * Area::GetMaxFreeBytesFor(fAreaSize)
|
||||
- fFreeBytes;
|
||||
return (fFreeBytes > fAreaSize + kDefragmentingTolerance + usedBytes / 32);
|
||||
}
|
||||
|
||||
// _Defragment
|
||||
bool
|
||||
BlockAllocator::_Defragment()
|
||||
{
|
||||
bool success = false;
|
||||
// We try to empty the least populated area by moving its blocks to other
|
||||
// areas.
|
||||
if (fFreeBytes > fAreaSize) {
|
||||
// find the least populated area
|
||||
// find the bucket with the least populated areas
|
||||
AreaBucket *bucket = NULL;
|
||||
for (int32 i = fBucketCount - 1; i >= 0; i--) {
|
||||
if (!fBuckets[i].IsEmpty()) {
|
||||
bucket = fBuckets + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// find the area in the bucket
|
||||
Area *area = NULL;
|
||||
if (bucket) {
|
||||
area = bucket->GetFirstArea();
|
||||
Area *bucketArea = area;
|
||||
while ((bucketArea = bucket->GetNextArea(bucketArea)) != NULL) {
|
||||
if (bucketArea->GetFreeBytes() > area->GetFreeBytes())
|
||||
area = bucketArea;
|
||||
}
|
||||
}
|
||||
if (area) {
|
||||
// remove the area from the bucket
|
||||
bucket->RemoveArea(area);
|
||||
fFreeBytes -= area->GetFreeBytes();
|
||||
// iterate through the blocks in the area and try to find a new
|
||||
// home for them
|
||||
success = true;
|
||||
while (Block *block = area->GetFirstUsedBlock()) {
|
||||
Block *newBlock = _AllocateBlock(block->GetUsableSize(), true);
|
||||
if (newBlock) {
|
||||
// got a new block: copy the data to it and free the old
|
||||
// one
|
||||
memcpy(newBlock->GetData(), block->GetData(),
|
||||
block->GetUsableSize());
|
||||
newBlock->SetReference(block->GetReference());
|
||||
block->SetReference(NULL);
|
||||
area->FreeBlock(block, true);
|
||||
#if ENABLE_BA_PANIC
|
||||
if (fPanic) {
|
||||
PRINT(("Panicked while trying to free block %p\n",
|
||||
block));
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// delete the area
|
||||
if (success && area->IsEmpty()) {
|
||||
area->Delete();
|
||||
fAreaCount--;
|
||||
PRINT(("defragmenting: area deleted\n"));
|
||||
} else {
|
||||
PRINT(("defragmenting: failed to empty area\n"));
|
||||
// failed: re-add the area
|
||||
fFreeBytes += area->GetFreeBytes();
|
||||
AreaBucket *newBucket = fBuckets + area->GetBucketIndex();
|
||||
newBucket->AddArea(area);
|
||||
}
|
||||
}
|
||||
D(SanityCheck(false));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#if ENABLE_BA_PANIC
|
||||
bool BlockAllocator::fPanic = false;
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
// BlockAllocator.h
|
||||
|
||||
#ifndef BLOCK_ALLOCATOR_H
|
||||
#define BLOCK_ALLOCATOR_H
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include "Block.h"
|
||||
#include "BlockReferenceManager.h"
|
||||
#include "Debug.h"
|
||||
#include "List.h"
|
||||
|
||||
#define ENABLE_BA_PANIC 1
|
||||
#if ENABLE_BA_PANIC
|
||||
#define BA_PANIC(x) { PANIC(x); BlockAllocator::fPanic = true; }
|
||||
#endif
|
||||
|
||||
class AllocationInfo;
|
||||
|
||||
// BlockAllocator
|
||||
class BlockAllocator {
|
||||
public:
|
||||
BlockAllocator(size_t areaSize);
|
||||
~BlockAllocator();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
BlockReference *AllocateBlock(size_t usableSize);
|
||||
void FreeBlock(BlockReference *block);
|
||||
BlockReference *ResizeBlock(BlockReference *block, size_t usableSize);
|
||||
|
||||
size_t GetAvailableBytes() const { return fAreaCount * fAreaSize; }
|
||||
size_t GetFreeBytes() const { return fFreeBytes; }
|
||||
size_t GetUsedBytes() const { return fAreaCount * fAreaSize
|
||||
- fFreeBytes; }
|
||||
|
||||
public:
|
||||
class Area;
|
||||
class AreaBucket;
|
||||
|
||||
// debugging only
|
||||
bool SanityCheck(bool deep = false) const;
|
||||
bool CheckArea(Area *area);
|
||||
bool CheckBlock(Block *block, size_t minSize = 0);
|
||||
bool CheckBlock(BlockReference *reference, size_t minSize = 0);
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
inline Area *_AreaForBlock(Block *block);
|
||||
Block *_AllocateBlock(size_t usableSize, bool dontCreateArea = false);
|
||||
void _FreeBlock(Area *area, Block *block, bool freeReference);
|
||||
inline void _RethinkAreaBucket(Area *area, AreaBucket *bucket,
|
||||
bool needsDefragmenting);
|
||||
inline bool _DefragmentingRecommended();
|
||||
bool _Defragment();
|
||||
|
||||
private:
|
||||
BlockReferenceManager fReferenceManager;
|
||||
AreaBucket *fBuckets;
|
||||
int32 fBucketCount;
|
||||
size_t fAreaSize;
|
||||
int32 fAreaCount;
|
||||
size_t fFreeBytes;
|
||||
#if ENABLE_BA_PANIC
|
||||
public:
|
||||
static bool fPanic;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // BLOCK_ALLOCATOR_H
|
||||
@@ -0,0 +1,596 @@
|
||||
// BlockAllocatorArea.cpp
|
||||
|
||||
#include "BlockAllocatorArea.h"
|
||||
#include "Debug.h"
|
||||
|
||||
// constructor
|
||||
BlockAllocator::Area::Area(area_id id, size_t size)
|
||||
: fBucket(NULL),
|
||||
fID(id),
|
||||
fSize(size),
|
||||
fFreeBytes(0),
|
||||
fFreeBlockCount(1),
|
||||
fUsedBlockCount(0),
|
||||
fFirstBlock(NULL),
|
||||
fLastBlock(NULL),
|
||||
fFirstFree(NULL),
|
||||
fLastFree(NULL)
|
||||
{
|
||||
size_t headerSize = block_align_ceil(sizeof(Area));
|
||||
fFirstFree = (TFreeBlock*)((char*)this + headerSize);
|
||||
fFirstFree->SetTo(NULL, block_align_floor(fSize - headerSize), false, NULL,
|
||||
NULL);
|
||||
fFirstBlock = fLastBlock = fLastFree = fFirstFree;
|
||||
fFreeBytes = fFirstFree->GetUsableSize();
|
||||
}
|
||||
|
||||
// Create
|
||||
BlockAllocator::Area *
|
||||
BlockAllocator::Area::Create(size_t size)
|
||||
{
|
||||
Area *area = NULL;
|
||||
void *base = NULL;
|
||||
#if USER
|
||||
area_id id = create_area("block alloc", &base, B_ANY_ADDRESS,
|
||||
size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
||||
#else
|
||||
area_id id = create_area("block alloc", &base, B_ANY_KERNEL_ADDRESS,
|
||||
size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
|
||||
#endif
|
||||
if (id >= 0) {
|
||||
area = new(base) Area(id, size);
|
||||
} else {
|
||||
ERROR(("BlockAllocator::Area::Create(%lu): Failed to create area: %s\n",
|
||||
size, strerror(id)));
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
// Delete
|
||||
void
|
||||
BlockAllocator::Area::Delete()
|
||||
{
|
||||
delete_area(fID);
|
||||
}
|
||||
|
||||
// AllocateBlock
|
||||
Block *
|
||||
BlockAllocator::Area::AllocateBlock(size_t usableSize, bool dontDefragment)
|
||||
{
|
||||
if (kMinBlockSize != block_align_ceil(sizeof(TFreeBlock))) {
|
||||
FATAL(("kMinBlockSize is not correctly initialized! Is %lu, but should be: "
|
||||
"%lu\n", kMinBlockSize, block_align_ceil(sizeof(TFreeBlock))));
|
||||
BA_PANIC("kMinBlockSize not correctly initialized.");
|
||||
return NULL;
|
||||
}
|
||||
if (usableSize == 0)
|
||||
return NULL;
|
||||
Block *newBlock = NULL;
|
||||
size_t size = max(usableSize + sizeof(BlockHeader), kMinBlockSize);
|
||||
size = block_align_ceil(size);
|
||||
if (size <= _GetBlockFreeBytes()) {
|
||||
// find first fit
|
||||
TFreeBlock *block = _FindFreeBlock(size);
|
||||
if (!block && !dontDefragment) {
|
||||
// defragmenting is necessary
|
||||
_Defragment();
|
||||
block = _FindFreeBlock(size);
|
||||
if (!block) {
|
||||
// no free block
|
||||
// Our data structures seem to be corrupted, since
|
||||
// _GetBlockFreeBytes() promised that we would have enough
|
||||
// free space.
|
||||
FATAL(("Couldn't find free block of min size %lu after "
|
||||
"defragmenting, although we should have %lu usable free "
|
||||
"bytes!\n", size, _GetBlockFreeBytes()));
|
||||
BA_PANIC("Bad area free bytes.");
|
||||
}
|
||||
}
|
||||
if (block) {
|
||||
// found a free block
|
||||
size_t remainder = block->GetSize() - size;
|
||||
if (remainder >= kMinBlockSize) {
|
||||
// enough space left for a free block
|
||||
Block *freePrev = block->GetPreviousBlock();
|
||||
// TFreeBlock *prevFree = block->GetPreviousFreeBlock();
|
||||
// TFreeBlock *nextFree = block->GetNextFreeBlock();
|
||||
// newBlock = block;
|
||||
_MoveResizeFreeBlock(block, size, remainder);
|
||||
// setup the new block
|
||||
// newBlock->SetSize(size, true);
|
||||
// newBlock->SetFree(false);
|
||||
newBlock = _MakeUsedBlock(block, 0, freePrev, size, true);
|
||||
} else {
|
||||
// not enough space left: take the free block over completely
|
||||
// remove the block from the free list
|
||||
_RemoveFreeBlock(block);
|
||||
newBlock = block;
|
||||
newBlock->SetFree(false);
|
||||
}
|
||||
if (fFreeBlockCount)
|
||||
fFreeBytes -= newBlock->GetSize();
|
||||
else
|
||||
fFreeBytes = 0;
|
||||
fUsedBlockCount++;
|
||||
}
|
||||
}
|
||||
D(SanityCheck());
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
// FreeBlock
|
||||
void
|
||||
BlockAllocator::Area::FreeBlock(Block *block, bool dontDefragment)
|
||||
{
|
||||
if (block) {
|
||||
// mark the block free and insert it into the free list
|
||||
block->SetFree(true);
|
||||
TFreeBlock *freeBlock = (TFreeBlock*)block;
|
||||
_InsertFreeBlock(freeBlock);
|
||||
fUsedBlockCount--;
|
||||
if (fFreeBlockCount == 1)
|
||||
fFreeBytes += freeBlock->GetUsableSize();
|
||||
else
|
||||
fFreeBytes += freeBlock->GetSize();
|
||||
// try coalescing with the next and the previous free block
|
||||
D(SanityCheck());
|
||||
_CoalesceWithNext(freeBlock);
|
||||
D(SanityCheck());
|
||||
_CoalesceWithNext(freeBlock->GetPreviousFreeBlock());
|
||||
// defragment, if sensible
|
||||
if (!dontDefragment && _DefragmentingRecommended())
|
||||
_Defragment();
|
||||
D(SanityCheck());
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeBlock
|
||||
Block *
|
||||
BlockAllocator::Area::ResizeBlock(Block *block, size_t newUsableSize,
|
||||
bool dontDefragment)
|
||||
{
|
||||
//PRINT(("Area::ResizeBlock(%p, %lu)\n", block, newUsableSize));
|
||||
// newUsableSize must be >0 !
|
||||
if (newUsableSize == 0)
|
||||
return NULL;
|
||||
Block *resultBlock = NULL;
|
||||
if (block) {
|
||||
size_t size = block->GetSize();
|
||||
size_t newSize = max(newUsableSize + sizeof(BlockHeader),
|
||||
kMinBlockSize);
|
||||
newSize = block_align_ceil(newSize);
|
||||
if (newSize == size) {
|
||||
// size doesn't change: nothing to do
|
||||
resultBlock = block;
|
||||
} else if (newSize < size) {
|
||||
// shrink the block
|
||||
size_t sizeDiff = size - newSize;
|
||||
Block *nextBlock = block->GetNextBlock();
|
||||
if (nextBlock && nextBlock->IsFree()) {
|
||||
// join the space with the adjoining free block
|
||||
TFreeBlock *freeBlock = nextBlock->ToFreeBlock();
|
||||
_MoveResizeFreeBlock(freeBlock, -sizeDiff,
|
||||
freeBlock->GetSize() + sizeDiff);
|
||||
// resize the block and we're done
|
||||
block->SetSize(newSize, true);
|
||||
fFreeBytes += sizeDiff;
|
||||
} else if (sizeDiff >= sizeof(TFreeBlock)) {
|
||||
// the freed space is large enough for a free block
|
||||
TFreeBlock *newFree = _MakeFreeBlock(block, newSize, block,
|
||||
sizeDiff, nextBlock, NULL, NULL);
|
||||
_InsertFreeBlock(newFree);
|
||||
block->SetSize(newSize, true);
|
||||
if (fFreeBlockCount == 1)
|
||||
fFreeBytes += newFree->GetUsableSize();
|
||||
else
|
||||
fFreeBytes += newFree->GetSize();
|
||||
if (!dontDefragment && _DefragmentingRecommended())
|
||||
_Defragment();
|
||||
} // else: insufficient space for a free block: no changes
|
||||
resultBlock = block;
|
||||
} else {
|
||||
//PRINT((" grow...\n"));
|
||||
// grow the block
|
||||
size_t sizeDiff = newSize - size;
|
||||
Block *nextBlock = block->GetNextBlock();
|
||||
if (nextBlock && nextBlock->IsFree()
|
||||
&& nextBlock->GetSize() >= sizeDiff) {
|
||||
//PRINT((" adjoining free block\n"));
|
||||
// there is a adjoining free block and it is large enough
|
||||
TFreeBlock *freeBlock = nextBlock->ToFreeBlock();
|
||||
size_t freeSize = freeBlock->GetSize();
|
||||
if (freeSize - sizeDiff >= sizeof(TFreeBlock)) {
|
||||
// the remaining space is still large enough for a free
|
||||
// block
|
||||
_MoveResizeFreeBlock(freeBlock, sizeDiff,
|
||||
freeSize - sizeDiff);
|
||||
block->SetSize(newSize, true);
|
||||
fFreeBytes -= sizeDiff;
|
||||
} else {
|
||||
// the remaining free space wouldn't be large enough for
|
||||
// a free block: consume the free block completely
|
||||
Block *freeNext = freeBlock->GetNextBlock();
|
||||
_RemoveFreeBlock(freeBlock);
|
||||
block->SetSize(size + freeSize, freeNext);
|
||||
_FixBlockList(block, block->GetPreviousBlock(), freeNext);
|
||||
if (fFreeBlockCount == 0)
|
||||
fFreeBytes = 0;
|
||||
else
|
||||
fFreeBytes -= freeSize;
|
||||
}
|
||||
resultBlock = block;
|
||||
} else {
|
||||
//PRINT((" no adjoining free block\n"));
|
||||
// no (large enough) adjoining free block: allocate
|
||||
// a new block and copy the data to it
|
||||
BlockReference *reference = block->GetReference();
|
||||
resultBlock = AllocateBlock(newUsableSize, dontDefragment);
|
||||
block = reference->GetBlock();
|
||||
if (resultBlock) {
|
||||
resultBlock->SetReference(reference);
|
||||
memcpy(resultBlock->GetData(), block->GetData(),
|
||||
block->GetUsableSize());
|
||||
FreeBlock(block, dontDefragment);
|
||||
resultBlock = reference->GetBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
D(SanityCheck());
|
||||
//PRINT(("Area::ResizeBlock() done: %p\n", resultBlock));
|
||||
return resultBlock;
|
||||
}
|
||||
|
||||
// SanityCheck
|
||||
bool
|
||||
BlockAllocator::Area::SanityCheck() const
|
||||
{
|
||||
// area ID
|
||||
if (fID < 0) {
|
||||
FATAL(("Area ID < 0: %lx\n", fID));
|
||||
BA_PANIC("Bad area ID.");
|
||||
return false;
|
||||
}
|
||||
// size
|
||||
size_t areaHeaderSize = block_align_ceil(sizeof(Area));
|
||||
if (fSize < areaHeaderSize + sizeof(TFreeBlock)) {
|
||||
FATAL(("Area too small to contain area header and at least one free "
|
||||
"block: %lu bytes\n", fSize));
|
||||
BA_PANIC("Bad area size.");
|
||||
return false;
|
||||
}
|
||||
// free bytes
|
||||
if (fFreeBytes > fSize) {
|
||||
FATAL(("Free size greater than area size: %lu vs %lu\n", fFreeBytes,
|
||||
fSize));
|
||||
BA_PANIC("Bad area free bytes.");
|
||||
return false;
|
||||
}
|
||||
// block count
|
||||
if (fFreeBlockCount + fUsedBlockCount == 0) {
|
||||
FATAL(("Area contains no blocks at all.\n"));
|
||||
BA_PANIC("Bad area block count.");
|
||||
return false;
|
||||
}
|
||||
// block list
|
||||
uint32 usedBlockCount = 0;
|
||||
uint32 freeBlockCount = 0;
|
||||
size_t freeBytes = 0;
|
||||
if (!fFirstBlock || !fLastBlock) {
|
||||
FATAL(("Invalid block list: first or last block NULL: first: %p, "
|
||||
"last: %p\n", fFirstBlock, fLastBlock));
|
||||
BA_PANIC("Bad area block list.");
|
||||
return false;
|
||||
} else {
|
||||
// iterate through block list and also check free list
|
||||
int32 blockCount = fFreeBlockCount + fUsedBlockCount;
|
||||
Block *block = fFirstBlock;
|
||||
Block *prevBlock = NULL;
|
||||
Block *prevFree = NULL;
|
||||
Block *nextFree = fFirstFree;
|
||||
bool blockListOK = true;
|
||||
for (int32 i = 0; i < blockCount; i++) {
|
||||
blockListOK = false;
|
||||
if (!block) {
|
||||
FATAL(("Encountered NULL in block list at index %ld, although "
|
||||
"list should have %ld blocks\n", i, blockCount));
|
||||
BA_PANIC("Bad area block list.");
|
||||
return false;
|
||||
}
|
||||
uint64 address = (uint32)block;
|
||||
// block within area?
|
||||
if (address < (uint32)this + areaHeaderSize
|
||||
|| address + sizeof(TFreeBlock) > (uint32)this + fSize) {
|
||||
FATAL(("Utterly mislocated block: %p, area: %p, "
|
||||
"size: %lu\n", block, this, fSize));
|
||||
BA_PANIC("Bad area block.");
|
||||
return false;
|
||||
}
|
||||
// block too large for area?
|
||||
size_t blockSize = block->GetSize();
|
||||
if (blockSize < sizeof(TFreeBlock)
|
||||
|| address + blockSize > (uint32)this + fSize) {
|
||||
FATAL(("Mislocated block: %p, size: %lu, area: %p, "
|
||||
"size: %lu\n", block, blockSize, this, fSize));
|
||||
BA_PANIC("Bad area block.");
|
||||
return false;
|
||||
}
|
||||
// alignment
|
||||
if (block_align_floor(address) != address
|
||||
|| block_align_floor(blockSize) != blockSize) {
|
||||
FATAL(("Block %ld not properly aligned: %p, size: %lu\n",
|
||||
i, block, blockSize));
|
||||
BA_PANIC("Bad area block.");
|
||||
return false;
|
||||
}
|
||||
// previous block
|
||||
if (block->GetPreviousBlock() != prevBlock) {
|
||||
FATAL(("Previous block of block %ld was not the previous "
|
||||
"block in list: %p vs %p\n", i,
|
||||
block->GetPreviousBlock(), prevBlock));
|
||||
BA_PANIC("Bad area block list.");
|
||||
return false;
|
||||
}
|
||||
// additional checks for free block list
|
||||
if (block->IsFree()) {
|
||||
freeBlockCount++;
|
||||
TFreeBlock *freeBlock = block->ToFreeBlock();
|
||||
if (prevFree)
|
||||
freeBytes += freeBlock->GetSize();
|
||||
else
|
||||
freeBytes += freeBlock->GetUsableSize();
|
||||
// block == next free block of previous free block
|
||||
if (freeBlock != nextFree) {
|
||||
FATAL(("Free block %ld is not the next block in free "
|
||||
"list: %p vs %p\n", i, freeBlock, nextFree));
|
||||
BA_PANIC("Bad area free list.");
|
||||
return false;
|
||||
}
|
||||
// previous free block
|
||||
if (freeBlock->GetPreviousFreeBlock() != prevFree) {
|
||||
FATAL(("Previous free block of block %ld was not the "
|
||||
" previous block in free list: %p vs %p\n", i,
|
||||
freeBlock->GetPreviousFreeBlock(), prevFree));
|
||||
BA_PANIC("Bad area free list.");
|
||||
return false;
|
||||
}
|
||||
prevFree = freeBlock;
|
||||
nextFree = freeBlock->GetNextFreeBlock();
|
||||
} else
|
||||
usedBlockCount++;
|
||||
prevBlock = block;
|
||||
block = block->GetNextBlock();
|
||||
blockListOK = true;
|
||||
}
|
||||
// final checks on block list
|
||||
if (blockListOK) {
|
||||
if (block) {
|
||||
FATAL(("More blocks in block list than expected\n"));
|
||||
BA_PANIC("Bad area block count.");
|
||||
return false;
|
||||
} else if (fLastBlock != prevBlock) {
|
||||
FATAL(("last block in block list was %p, but should be "
|
||||
"%p\n", fLastBlock, prevBlock));
|
||||
BA_PANIC("Bad area last block.");
|
||||
return false;
|
||||
} else if (prevFree != fLastFree) {
|
||||
FATAL(("last block in free list was %p, but should be %p\n",
|
||||
fLastFree, prevFree));
|
||||
BA_PANIC("Bad area last free block.");
|
||||
return false;
|
||||
}
|
||||
// block counts (a bit reduntant)
|
||||
if (freeBlockCount != fFreeBlockCount) {
|
||||
FATAL(("Free block count is %ld, but should be %ld\n",
|
||||
fFreeBlockCount, freeBlockCount));
|
||||
BA_PANIC("Bad area free block count.");
|
||||
return false;
|
||||
}
|
||||
if (usedBlockCount != fUsedBlockCount) {
|
||||
FATAL(("Used block count is %ld, but should be %ld\n",
|
||||
fUsedBlockCount, usedBlockCount));
|
||||
BA_PANIC("Bad area used block count.");
|
||||
return false;
|
||||
}
|
||||
// free bytes
|
||||
if (fFreeBytes != freeBytes) {
|
||||
FATAL(("Free bytes is %lu, but should be %lu\n",
|
||||
fFreeBytes, freeBytes));
|
||||
BA_PANIC("Bad area free bytes.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// CheckBlock
|
||||
bool
|
||||
BlockAllocator::Area::CheckBlock(Block *checkBlock, size_t minSize)
|
||||
{
|
||||
for (Block *block = fFirstBlock; block; block = block->GetNextBlock()) {
|
||||
if (block == checkBlock)
|
||||
return (block->GetUsableSize() >= minSize);
|
||||
}
|
||||
FATAL(("Block %p is not in area %p!\n", checkBlock, this));
|
||||
BA_PANIC("Invalid Block.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// _FindFreeBlock
|
||||
TFreeBlock *
|
||||
BlockAllocator::Area::_FindFreeBlock(size_t minSize)
|
||||
{
|
||||
// first fit
|
||||
for (TFreeBlock *block = GetFirstFreeBlock();
|
||||
block;
|
||||
block = block->GetNextFreeBlock()) {
|
||||
if (block->GetSize() >= minSize)
|
||||
return block;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// _InsertFreeBlock
|
||||
void
|
||||
BlockAllocator::Area::_InsertFreeBlock(TFreeBlock *block)
|
||||
{
|
||||
if (block) {
|
||||
// find the free block before which this one has to be inserted
|
||||
TFreeBlock *nextFree = NULL;
|
||||
for (nextFree = GetFirstFreeBlock();
|
||||
nextFree;
|
||||
nextFree = nextFree->GetNextFreeBlock()) {
|
||||
if ((uint32)nextFree > (uint32)block)
|
||||
break;
|
||||
}
|
||||
// get the previous block and insert the block between the two
|
||||
TFreeBlock *prevFree
|
||||
= (nextFree ? nextFree->GetPreviousFreeBlock() : fLastFree);
|
||||
_FixFreeList(block, prevFree, nextFree);
|
||||
fFreeBlockCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// _RemoveFreeBlock
|
||||
void
|
||||
BlockAllocator::Area::_RemoveFreeBlock(TFreeBlock *block)
|
||||
{
|
||||
if (block) {
|
||||
TFreeBlock *prevFree = block->GetPreviousFreeBlock();
|
||||
TFreeBlock *nextFree = block->GetNextFreeBlock();
|
||||
if (prevFree)
|
||||
prevFree->SetNextFreeBlock(nextFree);
|
||||
else
|
||||
fFirstFree = nextFree;
|
||||
if (nextFree)
|
||||
nextFree->SetPreviousFreeBlock(prevFree);
|
||||
else
|
||||
fLastFree = prevFree;
|
||||
}
|
||||
fFreeBlockCount--;
|
||||
}
|
||||
|
||||
// _MoveResizeFreeBlock
|
||||
TFreeBlock *
|
||||
BlockAllocator::Area::_MoveResizeFreeBlock(TFreeBlock *freeBlock,
|
||||
ssize_t offset, size_t newSize)
|
||||
{
|
||||
TFreeBlock *movedFree = NULL;
|
||||
if (freeBlock && offset) {
|
||||
// move the header of the free block
|
||||
Block *freePrev = freeBlock->GetPreviousBlock();
|
||||
TFreeBlock *prevFree = freeBlock->GetPreviousFreeBlock();
|
||||
TFreeBlock *nextFree = freeBlock->GetNextFreeBlock();
|
||||
movedFree = _MakeFreeBlock(freeBlock, offset, freePrev, newSize,
|
||||
freeBlock->HasNextBlock(), prevFree, nextFree);
|
||||
// update the free list
|
||||
_FixFreeList(movedFree, prevFree, nextFree);
|
||||
}
|
||||
return movedFree;
|
||||
}
|
||||
|
||||
// _MakeFreeBlock
|
||||
inline
|
||||
TFreeBlock *
|
||||
BlockAllocator::Area::_MakeFreeBlock(void *address, ssize_t offset,
|
||||
Block *previous, size_t size,
|
||||
bool hasNext, TFreeBlock *previousFree,
|
||||
TFreeBlock *nextFree)
|
||||
{
|
||||
TFreeBlock *block = (TFreeBlock*)((char*)address + offset);
|
||||
block->SetTo(previous, size, hasNext, previousFree, nextFree);
|
||||
if (hasNext)
|
||||
block->GetNextBlock()->SetPreviousBlock(block);
|
||||
else
|
||||
fLastBlock = block;
|
||||
return block;
|
||||
}
|
||||
|
||||
// _CoalesceWithNext
|
||||
bool
|
||||
BlockAllocator::Area::_CoalesceWithNext(TFreeBlock *block)
|
||||
{
|
||||
bool result = false;
|
||||
TFreeBlock *nextFree = NULL;
|
||||
if (block && (nextFree = block->GetNextFreeBlock()) != NULL
|
||||
&& block->GetNextBlock() == nextFree) {
|
||||
_RemoveFreeBlock(nextFree);
|
||||
Block *nextBlock = nextFree->GetNextBlock();
|
||||
block->SetSize(block->GetSize() + nextFree->GetSize(), nextBlock);
|
||||
if (nextBlock)
|
||||
nextBlock->SetPreviousBlock(block);
|
||||
else
|
||||
fLastBlock = block;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// _MakeUsedBlock
|
||||
inline
|
||||
Block *
|
||||
BlockAllocator::Area::_MakeUsedBlock(void *address, ssize_t offset,
|
||||
Block *previous, size_t size,
|
||||
bool hasNext)
|
||||
{
|
||||
Block *block = (Block*)((char*)address + offset);
|
||||
block->SetTo(previous, size, false, hasNext, NULL);
|
||||
if (hasNext)
|
||||
block->GetNextBlock()->SetPreviousBlock(block);
|
||||
else
|
||||
fLastBlock = block;
|
||||
return block;
|
||||
}
|
||||
|
||||
// _Defragment
|
||||
void
|
||||
BlockAllocator::Area::_Defragment()
|
||||
{
|
||||
D(SanityCheck());
|
||||
//PRINT(("BlockAllocator::Area::_Defragment()\n"));
|
||||
// A trivial strategy for now: Keep the last free block and move the
|
||||
// others so that they can be joined with it. This is done iteratively
|
||||
// by moving the first free block to adjoin to the second one and
|
||||
// coalescing them. A free block is moved by moving the data blocks in
|
||||
// between.
|
||||
TFreeBlock *nextFree = NULL;
|
||||
while (fFirstFree && (nextFree = fFirstFree->GetNextFreeBlock()) != NULL) {
|
||||
Block *prevBlock = fFirstFree->GetPreviousBlock();
|
||||
Block *nextBlock = fFirstFree->GetNextBlock();
|
||||
size_t size = fFirstFree->GetSize();
|
||||
// Used blocks are relatively position independed. We can move them
|
||||
// en bloc and only need to adjust the previous pointer of the first
|
||||
// one.
|
||||
if (!nextBlock->IsFree()) {
|
||||
// move the used blocks
|
||||
size_t chunkSize = (char*)nextFree - (char*)nextBlock;
|
||||
Block *nextFreePrev = nextFree->GetPreviousBlock();
|
||||
Block *movedBlock = fFirstFree;
|
||||
memmove(movedBlock, nextBlock, chunkSize);
|
||||
movedBlock->SetPreviousBlock(prevBlock);
|
||||
// init the first free block
|
||||
Block *movedNextFreePrev = (Block*)((char*)nextFreePrev - size);
|
||||
fFirstFree = _MakeFreeBlock(movedBlock, chunkSize,
|
||||
movedNextFreePrev, size, true, NULL, nextFree);
|
||||
nextFree->SetPreviousFreeBlock(fFirstFree);
|
||||
// fix the references of the moved blocks
|
||||
for (Block *block = movedBlock;
|
||||
block != fFirstFree;
|
||||
block = block->GetNextBlock()) {
|
||||
block->FixReference();
|
||||
}
|
||||
} else {
|
||||
// uncoalesced adjoining free block: That should never happen,
|
||||
// since we always coalesce as early as possible.
|
||||
INFORM(("Warning: Found uncoalesced adjoining free blocks!\n"));
|
||||
}
|
||||
// coalesce the first two blocks
|
||||
D(SanityCheck());
|
||||
_CoalesceWithNext(fFirstFree);
|
||||
D(SanityCheck());
|
||||
}
|
||||
//D(SanityCheck());
|
||||
//PRINT(("BlockAllocator::Area::_Defragment() done\n"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// BlockAllocatorArea.h
|
||||
|
||||
#ifndef BLOCK_ALLOCATOR_AREA_H
|
||||
#define BLOCK_ALLOCATOR_AREA_H
|
||||
|
||||
#include "BlockAllocator.h"
|
||||
#include "BlockAllocatorMisc.h"
|
||||
#include "DLList.h"
|
||||
|
||||
class BlockAllocator::Area : public DLListLinkImpl<Area> {
|
||||
public:
|
||||
static Area *Create(size_t size);
|
||||
void Delete();
|
||||
|
||||
inline void SetBucket(AreaBucket *bucket) { fBucket = bucket; }
|
||||
inline AreaBucket *GetBucket() const { return fBucket; }
|
||||
|
||||
inline Block *GetFirstBlock() const { return fFirstBlock; }
|
||||
inline Block *GetLastBlock() const { return fLastBlock; }
|
||||
|
||||
inline TFreeBlock *GetFirstFreeBlock() const { return fFirstFree; }
|
||||
inline TFreeBlock *GetLastFreeBlock() const { return fLastFree; }
|
||||
|
||||
inline bool IsEmpty() const { return (fUsedBlockCount == 0); }
|
||||
inline Block *GetFirstUsedBlock() const;
|
||||
|
||||
static inline size_t GetMaxFreeBytesFor(size_t areaSize);
|
||||
|
||||
inline size_t GetFreeBytes() const { return fFreeBytes; }
|
||||
inline bool NeedsDefragmenting() const { return (fFreeBlockCount > 1); }
|
||||
|
||||
inline int32 GetBucketIndex();
|
||||
|
||||
Block *AllocateBlock(size_t usableSize, bool dontDefragment = false);
|
||||
void FreeBlock(Block *block, bool dontDefragment = false);
|
||||
Block *ResizeBlock(Block *block, size_t newSize,
|
||||
bool dontDefragment = false);
|
||||
|
||||
// debugging only
|
||||
bool SanityCheck() const;
|
||||
bool CheckBlock(Block *block, size_t minSize = 0);
|
||||
|
||||
private:
|
||||
inline size_t _GetBlockFreeBytes()
|
||||
{ return fFreeBytes + sizeof(BlockHeader); }
|
||||
|
||||
Area(area_id id, size_t size);
|
||||
~Area();
|
||||
|
||||
inline void _FixBlockList(Block *block, Block *prevBlock,
|
||||
Block *nextBlock);
|
||||
inline void _FixFreeList(TFreeBlock *block, TFreeBlock *prevFree,
|
||||
TFreeBlock *nextFree);
|
||||
|
||||
TFreeBlock *_FindFreeBlock(size_t minSize);
|
||||
void _InsertFreeBlock(TFreeBlock *block);
|
||||
void _RemoveFreeBlock(TFreeBlock *block);
|
||||
TFreeBlock * _MoveResizeFreeBlock(TFreeBlock *freeBlock, ssize_t offset,
|
||||
size_t newSize);
|
||||
inline TFreeBlock *_MakeFreeBlock(void *address, ssize_t offset,
|
||||
Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree,
|
||||
TFreeBlock *nextFree);
|
||||
bool _CoalesceWithNext(TFreeBlock *block);
|
||||
|
||||
inline Block *_MakeUsedBlock(void *address, ssize_t offset,
|
||||
Block *previous, size_t size, bool hasNext);
|
||||
|
||||
inline bool _DefragmentingRecommended();
|
||||
void _Defragment();
|
||||
|
||||
private:
|
||||
AreaBucket *fBucket;
|
||||
area_id fID;
|
||||
size_t fSize;
|
||||
size_t fFreeBytes;
|
||||
size_t fFreeBlockCount;
|
||||
size_t fUsedBlockCount;
|
||||
Block *fFirstBlock;
|
||||
Block *fLastBlock;
|
||||
TFreeBlock *fFirstFree;
|
||||
TFreeBlock *fLastFree;
|
||||
};
|
||||
|
||||
typedef BlockAllocator::Area Area;
|
||||
|
||||
|
||||
// inline methods
|
||||
|
||||
// debugging
|
||||
#if BA_DEFINE_INLINES
|
||||
|
||||
// GetFirstUsedBlock
|
||||
inline
|
||||
Block *
|
||||
BlockAllocator::Area::GetFirstUsedBlock() const
|
||||
{
|
||||
// Two assumptions:
|
||||
// 1) There is always a first block. If that isn't so, our structure are
|
||||
// corrupt.
|
||||
// 2) If the first block is free, the second (if any) is not. Otherwise
|
||||
// there were adjoining free blocks, which our coalescing strategy
|
||||
// prevents.
|
||||
return (fFirstBlock->IsFree() ? fFirstBlock->GetNextBlock() : fFirstBlock);
|
||||
}
|
||||
|
||||
// GetMaxFreeBytesFor
|
||||
inline
|
||||
size_t
|
||||
BlockAllocator::Area::GetMaxFreeBytesFor(size_t areaSize)
|
||||
{
|
||||
size_t headerSize = block_align_ceil(sizeof(Area));
|
||||
return Block::GetUsableSizeFor(block_align_floor(areaSize - headerSize));
|
||||
}
|
||||
|
||||
// GetBucketIndex
|
||||
inline
|
||||
int32
|
||||
BlockAllocator::Area::GetBucketIndex()
|
||||
{
|
||||
return bucket_containing_size(GetFreeBytes());
|
||||
}
|
||||
|
||||
// _FixBlockList
|
||||
inline
|
||||
void
|
||||
BlockAllocator::Area::_FixBlockList(Block *block, Block *prevBlock,
|
||||
Block *nextBlock)
|
||||
{
|
||||
if (block) {
|
||||
if (prevBlock)
|
||||
prevBlock->SetNextBlock(block);
|
||||
else
|
||||
fFirstBlock = block;
|
||||
if (nextBlock)
|
||||
nextBlock->SetPreviousBlock(block);
|
||||
else
|
||||
fLastBlock = block;
|
||||
}
|
||||
}
|
||||
|
||||
// _FixFreeList
|
||||
inline
|
||||
void
|
||||
BlockAllocator::Area::_FixFreeList(TFreeBlock *block, TFreeBlock *prevFree,
|
||||
TFreeBlock *nextFree)
|
||||
{
|
||||
if (block) {
|
||||
if (prevFree)
|
||||
prevFree->SetNextFreeBlock(block);
|
||||
else
|
||||
fFirstFree = block;
|
||||
if (nextFree)
|
||||
nextFree->SetPreviousFreeBlock(block);
|
||||
else
|
||||
fLastFree = block;
|
||||
block->SetPreviousFreeBlock(prevFree);
|
||||
block->SetNextFreeBlock(nextFree);
|
||||
}
|
||||
}
|
||||
|
||||
// _DefragmentingRecommended
|
||||
inline
|
||||
bool
|
||||
BlockAllocator::Area::_DefragmentingRecommended()
|
||||
{
|
||||
// Defragmenting Condition: At least more than 5 free blocks and
|
||||
// free / block ratio greater 1 / 10. Don't know, if that makes any
|
||||
// sense. ;-)
|
||||
return (fFreeBlockCount > 5 && fUsedBlockCount / fFreeBlockCount < 10);
|
||||
}
|
||||
|
||||
#endif // BA_DEFINE_INLINES
|
||||
|
||||
|
||||
#endif // BLOCK_ALLOCATOR_AREA_H
|
||||
@@ -0,0 +1,51 @@
|
||||
// BlockAllocatorAreaBucket.cpp
|
||||
|
||||
#include "BlockAllocatorAreaBucket.h"
|
||||
|
||||
// constructor
|
||||
BlockAllocator::AreaBucket::AreaBucket()
|
||||
: fAreas(),
|
||||
fIndex(-1),
|
||||
fMinSize(0),
|
||||
fMaxSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
BlockAllocator::AreaBucket::~AreaBucket()
|
||||
{
|
||||
while (Area *area = fAreas.GetFirst()) {
|
||||
RemoveArea(area);
|
||||
area->Delete();
|
||||
}
|
||||
}
|
||||
|
||||
// SanityCheck
|
||||
bool
|
||||
BlockAllocator::AreaBucket::SanityCheck(bool deep) const
|
||||
{
|
||||
// check area list
|
||||
for (Area *area = GetFirstArea(); area; area = GetNextArea(area)) {
|
||||
if (deep) {
|
||||
if (!area->SanityCheck())
|
||||
return false;
|
||||
}
|
||||
// bucket
|
||||
if (area->GetBucket() != this) {
|
||||
FATAL(("Area %p is in bucket %p, but thinks it is in bucket %p\n",
|
||||
area, this, area->GetBucket()));
|
||||
BA_PANIC("Wrong area bucket.");
|
||||
return false;
|
||||
}
|
||||
// size
|
||||
size_t areaSize = area->GetFreeBytes();
|
||||
if (areaSize < fMinSize || areaSize >= fMaxSize) {
|
||||
FATAL(("Area is in wrong bucket: free: %lu, min: %lu, max: %lu\n",
|
||||
areaSize, fMinSize, fMaxSize));
|
||||
BA_PANIC("Area in wrong bucket.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// BlockAllocatorAreaBucket.h
|
||||
|
||||
#ifndef BLOCK_ALLOCATOR_AREA_BUCKET_H
|
||||
#define BLOCK_ALLOCATOR_AREA_BUCKET_H
|
||||
|
||||
#include "BlockAllocator.h"
|
||||
#include "BlockAllocatorArea.h"
|
||||
#include "Debug.h"
|
||||
#include "DLList.h"
|
||||
|
||||
class BlockAllocator::AreaBucket {
|
||||
public:
|
||||
AreaBucket();
|
||||
~AreaBucket();
|
||||
|
||||
inline void SetIndex(int32 index) { fIndex = index; }
|
||||
inline int32 GetIndex() const { return fIndex; }
|
||||
|
||||
inline void SetSizeLimits(size_t minSize, size_t maxSize);
|
||||
inline size_t GetMinSize() const { return fMinSize; } // incl.
|
||||
inline size_t GetMaxSize() const { return fMaxSize; } // excl.
|
||||
|
||||
inline void AddArea(Area *area);
|
||||
inline void RemoveArea(Area *area);
|
||||
|
||||
inline Area *GetFirstArea() const { return fAreas.GetFirst(); }
|
||||
inline Area *GetLastArea() const { return fAreas.GetLast(); }
|
||||
inline Area *GetNextArea(Area* area) const;
|
||||
|
||||
inline bool IsEmpty() const { return fAreas.IsEmpty(); }
|
||||
|
||||
// debugging only
|
||||
bool SanityCheck(bool deep = false) const;
|
||||
|
||||
private:
|
||||
DLList<Area> fAreas;
|
||||
int32 fIndex;
|
||||
size_t fMinSize;
|
||||
size_t fMaxSize;
|
||||
};
|
||||
|
||||
typedef BlockAllocator::AreaBucket AreaBucket;
|
||||
|
||||
|
||||
// inline methods
|
||||
|
||||
// debugging
|
||||
#if BA_DEFINE_INLINES
|
||||
|
||||
// SetSizeLimits
|
||||
/*! \brief Sets the size limits for areas this bucket may contain.
|
||||
\param minSize Minimal area size. Inclusively.
|
||||
\param maxSize Maximal area size. Exlusively.
|
||||
*/
|
||||
inline
|
||||
void
|
||||
BlockAllocator::AreaBucket::SetSizeLimits(size_t minSize, size_t maxSize)
|
||||
{
|
||||
fMinSize = minSize;
|
||||
fMaxSize = maxSize;
|
||||
}
|
||||
|
||||
// AddArea
|
||||
inline
|
||||
void
|
||||
BlockAllocator::AreaBucket::AddArea(Area *area)
|
||||
{
|
||||
if (area) {
|
||||
fAreas.Insert(area, area->NeedsDefragmenting());
|
||||
area->SetBucket(this);
|
||||
D(SanityCheck(false));
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveArea
|
||||
inline
|
||||
void
|
||||
BlockAllocator::AreaBucket::RemoveArea(Area *area)
|
||||
{
|
||||
if (area) {
|
||||
fAreas.Remove(area);
|
||||
area->SetBucket(NULL);
|
||||
D(SanityCheck(false));
|
||||
}
|
||||
}
|
||||
|
||||
// GetNextArea
|
||||
inline
|
||||
Area *
|
||||
BlockAllocator::AreaBucket::GetNextArea(Area* area) const
|
||||
{
|
||||
return fAreas.GetNext(area);
|
||||
}
|
||||
|
||||
#endif // BA_DEFINE_INLINES
|
||||
|
||||
#endif // BLOCK_ALLOCATOR_AREA_BUCKET_H
|
||||
@@ -0,0 +1,36 @@
|
||||
// BlockAllocatorMisc.h
|
||||
|
||||
#ifndef BLOCK_ALLOCATOR_MISC_H
|
||||
#define BLOCK_ALLOCATOR_MISC_H
|
||||
|
||||
#include "Block.h"
|
||||
#include "Misc.h"
|
||||
|
||||
// block alignment -- start offsets and size
|
||||
static const size_t kBlockAlignment = 4; // must be a power of 2
|
||||
|
||||
// block_align_{floor,ceil}
|
||||
static inline size_t block_align_floor(size_t value)
|
||||
{ return value & ~(kBlockAlignment - 1); }
|
||||
static inline size_t block_align_ceil(size_t value)
|
||||
{ return (value + kBlockAlignment - 1) & ~(kBlockAlignment - 1); }
|
||||
|
||||
// minimal size of a gross/net block
|
||||
// BAD DOG: No initializers in the kernel!
|
||||
//static const size_t kMinBlockSize = block_align_ceil(sizeof(TFreeBlock));
|
||||
#define kMinBlockSize (block_align_ceil(sizeof(TFreeBlock)))
|
||||
static const size_t kMinNetBlockSize = 8;
|
||||
|
||||
static const size_t kDefragmentingTolerance = 10240;
|
||||
|
||||
// bucket_containing_size -- bucket for to contain an area with size
|
||||
static inline int bucket_containing_size(size_t size)
|
||||
{ return fls(size / kMinNetBlockSize) + 1; }
|
||||
|
||||
// bucket_containing_min_size -- bucket containing areas >= size
|
||||
static inline int
|
||||
bucket_containing_min_size(size_t size)
|
||||
{ return (size ? bucket_containing_size(size - 1) + 1 : 0); }
|
||||
|
||||
|
||||
#endif BLOCK_ALLOCATOR_MISC_H
|
||||
@@ -0,0 +1,130 @@
|
||||
// BlockReferenceManager.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Block.h"
|
||||
#include "BlockAllocator.h" // only for BA_PANIC
|
||||
#include "BlockReferenceManager.h"
|
||||
#include "Debug.h"
|
||||
|
||||
static const int kBlockReferenceTableSize = 128;
|
||||
|
||||
// constructor
|
||||
BlockReferenceManager::BlockReferenceManager()
|
||||
: fTables(10),
|
||||
fFreeList(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
BlockReferenceManager::~BlockReferenceManager()
|
||||
{
|
||||
}
|
||||
|
||||
// AllocateReference
|
||||
BlockReference *
|
||||
BlockReferenceManager::AllocateReference()
|
||||
{
|
||||
BlockReference *reference = NULL;
|
||||
if (!fFreeList)
|
||||
_AddTable();
|
||||
if (fFreeList) {
|
||||
reference = fFreeList;
|
||||
fFreeList = *(BlockReference**)fFreeList;
|
||||
}
|
||||
return reference;
|
||||
}
|
||||
|
||||
// FreeReference
|
||||
void
|
||||
BlockReferenceManager::FreeReference(BlockReference *reference)
|
||||
{
|
||||
if (reference) {
|
||||
*(BlockReference**)reference = fFreeList;
|
||||
fFreeList = reference;
|
||||
}
|
||||
}
|
||||
|
||||
// CheckReference
|
||||
bool
|
||||
BlockReferenceManager::CheckReference(BlockReference *reference)
|
||||
{
|
||||
if (reference) {
|
||||
uint32 address = (uint32)reference;
|
||||
int32 tableCount = fTables.CountItems();
|
||||
for (int32 i = 0; i < tableCount; i++) {
|
||||
Table *table = &fTables.ItemAt(i);
|
||||
uint32 first = (uint32)table->GetReferences();
|
||||
uint32 last = (uint32)(table->GetReferences() + table->GetSize());
|
||||
if (first <= address && address < last)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FATAL(("BlockReference %p does not exist!\n", reference));
|
||||
BA_PANIC("BlockReference doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
BlockReferenceManager::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddListAllocation(fTables.GetCapacity(), sizeof(Table));
|
||||
int32 count = fTables.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
Table &table = fTables.ItemAt(i);
|
||||
info.AddOtherAllocation(table.GetSize() * sizeof(BlockReference));
|
||||
}
|
||||
}
|
||||
|
||||
// _AddTable
|
||||
status_t
|
||||
BlockReferenceManager::_AddTable()
|
||||
{
|
||||
status_t error = B_OK;
|
||||
// add a new table
|
||||
Table dummy;
|
||||
if (fTables.AddItem(dummy)) {
|
||||
int32 index = fTables.CountItems() - 1;
|
||||
Table &table = fTables.ItemAt(index);
|
||||
error = table.Init(kBlockReferenceTableSize);
|
||||
if (error == B_OK) {
|
||||
// add the references to the free list
|
||||
uint32 count = table.GetSize();
|
||||
BlockReference *references = table.GetReferences();
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
BlockReference *reference = references + i;
|
||||
*(BlockReference**)reference = fFreeList;
|
||||
fFreeList = reference;
|
||||
}
|
||||
} else
|
||||
fTables.RemoveItem(index);
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
// Table
|
||||
|
||||
// destructor
|
||||
BlockReferenceManager::Table::~Table()
|
||||
{
|
||||
if (fReferences)
|
||||
delete[] fReferences;
|
||||
}
|
||||
|
||||
// Init
|
||||
status_t
|
||||
BlockReferenceManager::Table::Init(int32 size)
|
||||
{
|
||||
status_t error = (size > 0 ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
fReferences = new(nothrow) BlockReference[size];
|
||||
if (fReferences)
|
||||
fSize = size;
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// BlockReferenceManager.h
|
||||
|
||||
#ifndef BLOCK_REFERENCE_MANAGER_H
|
||||
#define BLOCK_REFERENCE_MANAGER_H
|
||||
|
||||
#include "List.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class BlockReference;
|
||||
|
||||
class BlockReferenceManager {
|
||||
public:
|
||||
BlockReferenceManager();
|
||||
~BlockReferenceManager();
|
||||
|
||||
BlockReference *AllocateReference();
|
||||
void FreeReference(BlockReference *reference);
|
||||
|
||||
// debugging only
|
||||
bool CheckReference(BlockReference *reference);
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
status_t _AddTable();
|
||||
|
||||
private:
|
||||
class Table {
|
||||
public:
|
||||
Table() : fSize(0), fReferences(NULL) {}
|
||||
Table(int) : fSize(0), fReferences(NULL) {}
|
||||
~Table();
|
||||
|
||||
status_t Init(int32 size);
|
||||
|
||||
BlockReference *GetReferences() { return fReferences; }
|
||||
|
||||
int32 GetSize() const { return fSize; }
|
||||
|
||||
private:
|
||||
uint32 fSize;
|
||||
BlockReference *fReferences;
|
||||
};
|
||||
|
||||
List<Table> fTables;
|
||||
BlockReference *fFreeList;
|
||||
};
|
||||
|
||||
#endif // BLOCK_REFERENCE_MANAGER_H
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
// DataContainer.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Attribute.h" // for debugging only
|
||||
#include "Block.h"
|
||||
#include "DataContainer.h"
|
||||
#include "Debug.h"
|
||||
#include "Misc.h"
|
||||
#include "Node.h" // for debugging only
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
DataContainer::DataContainer(Volume *volume)
|
||||
: fVolume(volume),
|
||||
fSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
DataContainer::~DataContainer()
|
||||
{
|
||||
Resize(0);
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
DataContainer::InitCheck() const
|
||||
{
|
||||
return (fVolume ? B_OK : B_ERROR);
|
||||
}
|
||||
|
||||
// Resize
|
||||
status_t
|
||||
DataContainer::Resize(off_t newSize)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
if (newSize < 0)
|
||||
newSize = 0;
|
||||
if (newSize != fSize) {
|
||||
// Shrinking should never fail. Growing can fail, if we run out of
|
||||
// memory. Then we try to shrink back to the original size.
|
||||
off_t oldSize = fSize;
|
||||
error = _Resize(newSize);
|
||||
if (error == B_NO_MEMORY && newSize > fSize)
|
||||
_Resize(oldSize);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// ReadAt
|
||||
status_t
|
||||
DataContainer::ReadAt(off_t offset, void *_buffer, size_t size,
|
||||
size_t *bytesRead)
|
||||
{
|
||||
uint8 *buffer = (uint8*)_buffer;
|
||||
status_t error = (buffer && offset >= 0 && bytesRead ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// read not more than we have to offer
|
||||
offset = min(offset, fSize);
|
||||
size = min(size, size_t(fSize - offset));
|
||||
// iterate through the blocks, reading as long as there's something
|
||||
// left to read
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
*bytesRead = 0;
|
||||
while (size > 0) {
|
||||
size_t inBlockOffset = offset % blockSize;
|
||||
size_t toRead = min(size, size_t(blockSize - inBlockOffset));
|
||||
void *blockData = _GetBlockDataAt(offset / blockSize,
|
||||
inBlockOffset, toRead);
|
||||
D(
|
||||
if (!blockData) {
|
||||
Node *node = NULL;
|
||||
if (Attribute *attribute = dynamic_cast<Attribute*>(this)) {
|
||||
FATAL(("attribute `%s' of\n", attribute->GetName()));
|
||||
node = attribute->GetNode();
|
||||
} else {
|
||||
node = dynamic_cast<Node*>(this);
|
||||
}
|
||||
if (node)
|
||||
// FATAL(("node `%s'\n", node->GetName()));
|
||||
FATAL(("container size: %Ld, offset: %Ld, buffer size: %lu\n",
|
||||
fSize, offset, size));
|
||||
return B_ERROR;
|
||||
}
|
||||
);
|
||||
memcpy(buffer, blockData, toRead);
|
||||
buffer += toRead;
|
||||
size -= toRead;
|
||||
offset += toRead;
|
||||
*bytesRead += toRead;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// WriteAt
|
||||
status_t
|
||||
DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size,
|
||||
size_t *bytesWritten)
|
||||
{
|
||||
//PRINT(("DataContainer::WriteAt(%Ld, %p, %lu, %p), fSize: %Ld\n", offset, _buffer, size, bytesWritten, fSize));
|
||||
const uint8 *buffer = (const uint8*)_buffer;
|
||||
status_t error = (buffer && offset >= 0 && bytesWritten
|
||||
? B_OK : B_BAD_VALUE);
|
||||
// resize the container, if necessary
|
||||
if (error == B_OK) {
|
||||
off_t newSize = offset + size;
|
||||
off_t oldSize = fSize;
|
||||
if (newSize > fSize) {
|
||||
error = Resize(newSize);
|
||||
// pad with zero, if necessary
|
||||
if (error == B_OK && offset > oldSize)
|
||||
_ClearArea(offset, oldSize - offset);
|
||||
}
|
||||
}
|
||||
if (error == B_OK) {
|
||||
// iterate through the blocks, writing as long as there's something
|
||||
// left to write
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
*bytesWritten = 0;
|
||||
while (size > 0) {
|
||||
size_t inBlockOffset = offset % blockSize;
|
||||
size_t toWrite = min(size, size_t(blockSize - inBlockOffset));
|
||||
void *blockData = _GetBlockDataAt(offset / blockSize,
|
||||
inBlockOffset, toWrite);
|
||||
D(if (!blockData) return B_ERROR;);
|
||||
memcpy(blockData, buffer, toWrite);
|
||||
buffer += toWrite;
|
||||
size -= toWrite;
|
||||
offset += toWrite;
|
||||
*bytesWritten += toWrite;
|
||||
}
|
||||
}
|
||||
//PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize));
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetFirstDataBlock
|
||||
void
|
||||
DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length)
|
||||
{
|
||||
if (data && length) {
|
||||
if (_IsBlockMode()) {
|
||||
BlockReference *block = _GetBlockList()->ItemAt(0);
|
||||
*data = (const uint8*)block->GetData();
|
||||
*length = min(fSize, fVolume->GetBlockSize());
|
||||
} else {
|
||||
*data = fSmallBuffer;
|
||||
*length = fSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
DataContainer::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
if (_IsBlockMode()) {
|
||||
BlockList *blocks = _GetBlockList();
|
||||
info.AddListAllocation(blocks->GetCapacity(), sizeof(BlockReference*));
|
||||
int32 blockCount = blocks->CountItems();
|
||||
for (int32 i = 0; i < blockCount; i++)
|
||||
info.AddBlockAllocation(blocks->ItemAt(i)->GetBlock()->GetSize());
|
||||
} else {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// _RequiresBlockMode
|
||||
inline
|
||||
bool
|
||||
DataContainer::_RequiresBlockMode(size_t size)
|
||||
{
|
||||
return (size > kSmallDataContainerSize);
|
||||
}
|
||||
|
||||
// _IsBlockMode
|
||||
inline
|
||||
bool
|
||||
DataContainer::_IsBlockMode() const
|
||||
{
|
||||
return (fSize > kSmallDataContainerSize);
|
||||
}
|
||||
|
||||
// _Resize
|
||||
status_t
|
||||
DataContainer::_Resize(off_t newSize)
|
||||
{
|
||||
//PRINT(("DataContainer::_Resize(%Ld), fSize: %Ld\n", newSize, fSize));
|
||||
status_t error = B_OK;
|
||||
if (newSize != fSize) {
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
int32 blockCount = _CountBlocks();
|
||||
int32 newBlockCount = (newSize + blockSize - 1) / blockSize;
|
||||
if (newBlockCount == blockCount) {
|
||||
// only the last block needs to be resized
|
||||
if (_IsBlockMode() && _RequiresBlockMode(newSize)) {
|
||||
// keep block mode
|
||||
error = _ResizeLastBlock((newSize - 1) % blockSize + 1);
|
||||
} else if (!_IsBlockMode() && !_RequiresBlockMode(newSize)) {
|
||||
// keep small buffer mode
|
||||
fSize = newSize;
|
||||
} else if (fSize < newSize) {
|
||||
// switch to block mode
|
||||
_SwitchToBlockMode(newSize);
|
||||
} else {
|
||||
// switch to small buffer mode
|
||||
_SwitchToSmallBufferMode(newSize);
|
||||
}
|
||||
} else if (newBlockCount < blockCount) {
|
||||
// shrink
|
||||
if (_IsBlockMode()) {
|
||||
// remove the last blocks
|
||||
BlockList *blocks = _GetBlockList();
|
||||
for (int32 i = blockCount - 1; i >= newBlockCount; i--) {
|
||||
BlockReference *block = blocks->ItemAt(i);
|
||||
blocks->RemoveItem(i);
|
||||
fVolume->FreeBlock(block);
|
||||
fSize = (fSize - 1) / blockSize * blockSize;
|
||||
}
|
||||
// resize the last block to the correct size, respectively
|
||||
// switch to small buffer mode
|
||||
if (_RequiresBlockMode(newSize))
|
||||
error = _ResizeLastBlock((newSize - 1) % blockSize + 1);
|
||||
else
|
||||
_SwitchToSmallBufferMode(newSize);
|
||||
} else {
|
||||
// small buffer mode: just set the new size
|
||||
fSize = newSize;
|
||||
}
|
||||
} else {
|
||||
// grow
|
||||
if (_RequiresBlockMode(newSize)) {
|
||||
// resize the first block to the correct size, respectively
|
||||
// switch to block mode
|
||||
if (_IsBlockMode())
|
||||
error = _ResizeLastBlock(blockSize);
|
||||
else {
|
||||
error = _SwitchToBlockMode(min((size_t)newSize,
|
||||
blockSize));
|
||||
}
|
||||
// add new blocks
|
||||
BlockList *blocks = _GetBlockList();
|
||||
while (error == B_OK && fSize < newSize) {
|
||||
size_t newBlockSize = min(size_t(newSize - fSize),
|
||||
blockSize);
|
||||
BlockReference *block = NULL;
|
||||
error = fVolume->AllocateBlock(newBlockSize, &block);
|
||||
if (error == B_OK) {
|
||||
if (blocks->AddItem(block))
|
||||
fSize += newBlockSize;
|
||||
else {
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
fVolume->FreeBlock(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no need to switch to block mode: just set the new size
|
||||
fSize = newSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
//PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize));
|
||||
return error;
|
||||
}
|
||||
|
||||
// _GetBlockList
|
||||
inline
|
||||
DataContainer::BlockList *
|
||||
DataContainer::_GetBlockList()
|
||||
{
|
||||
return (BlockList*)fBlocks;
|
||||
}
|
||||
|
||||
// _GetBlockList
|
||||
inline
|
||||
const DataContainer::BlockList *
|
||||
DataContainer::_GetBlockList() const
|
||||
{
|
||||
return (BlockList*)fBlocks;
|
||||
}
|
||||
|
||||
// _CountBlocks
|
||||
inline
|
||||
int32
|
||||
DataContainer::_CountBlocks() const
|
||||
{
|
||||
if (_IsBlockMode())
|
||||
return _GetBlockList()->CountItems();
|
||||
else if (fSize == 0) // small buffer mode, empty buffer
|
||||
return 0;
|
||||
return 1; // small buffer mode, non-empty buffer
|
||||
}
|
||||
|
||||
// _GetBlockDataAt
|
||||
inline
|
||||
void *
|
||||
DataContainer::_GetBlockDataAt(int32 index, size_t offset, size_t DARG(size))
|
||||
{
|
||||
if (_IsBlockMode()) {
|
||||
BlockReference *block = _GetBlockList()->ItemAt(index);
|
||||
D(if (!fVolume->CheckBlock(block, offset + size)) return NULL;);
|
||||
return block->GetDataAt(offset);
|
||||
} else {
|
||||
D(
|
||||
if (offset + size > kSmallDataContainerSize) {
|
||||
FATAL(("DataContainer: Data access exceeds small buffer.\n"));
|
||||
PANIC("DataContainer: Data access exceeds small buffer.");
|
||||
return NULL;
|
||||
}
|
||||
);
|
||||
return fSmallBuffer + offset;
|
||||
}
|
||||
}
|
||||
|
||||
// _ClearArea
|
||||
void
|
||||
DataContainer::_ClearArea(off_t offset, off_t size)
|
||||
{
|
||||
// constrain the area to the data area
|
||||
offset = min(offset, fSize);
|
||||
size = min(size, fSize - offset);
|
||||
// iterate through the blocks, clearing as long as there's something
|
||||
// left to clear
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
while (size > 0) {
|
||||
size_t inBlockOffset = offset % blockSize;
|
||||
size_t toClear = min(size_t(size), blockSize - inBlockOffset);
|
||||
void *blockData = _GetBlockDataAt(offset / blockSize, inBlockOffset,
|
||||
toClear);
|
||||
D(if (!blockData) return;);
|
||||
memset(blockData, 0, toClear);
|
||||
size -= toClear;
|
||||
offset += toClear;
|
||||
}
|
||||
}
|
||||
|
||||
// _ResizeLastBlock
|
||||
status_t
|
||||
DataContainer::_ResizeLastBlock(size_t newSize)
|
||||
{
|
||||
//PRINT(("DataContainer::_ResizeLastBlock(%lu), fSize: %Ld\n", newSize, fSize));
|
||||
int32 blockCount = _CountBlocks();
|
||||
status_t error = (fSize > 0 && blockCount > 0 && newSize > 0
|
||||
? B_OK : B_BAD_VALUE);
|
||||
D(
|
||||
if (!_IsBlockMode()) {
|
||||
FATAL(("Call of _ResizeLastBlock() in small buffer mode.\n"));
|
||||
PANIC("Call of _ResizeLastBlock() in small buffer mode.");
|
||||
return B_ERROR;
|
||||
}
|
||||
);
|
||||
if (error == B_OK) {
|
||||
size_t blockSize = fVolume->GetBlockSize();
|
||||
size_t oldSize = (fSize - 1) % blockSize + 1;
|
||||
if (newSize != oldSize) {
|
||||
BlockList *blocks = _GetBlockList();
|
||||
BlockReference *block = blocks->ItemAt(blockCount - 1);
|
||||
BlockReference *newBlock = fVolume->ResizeBlock(block, newSize);
|
||||
if (newBlock) {
|
||||
if (newBlock != block)
|
||||
blocks->ReplaceItem(blockCount - 1, newBlock);
|
||||
fSize += off_t(newSize) - oldSize;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
}
|
||||
//PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize));
|
||||
return error;
|
||||
}
|
||||
|
||||
// _SwitchToBlockMode
|
||||
status_t
|
||||
DataContainer::_SwitchToBlockMode(size_t newBlockSize)
|
||||
{
|
||||
// allocate a new block
|
||||
BlockReference *block = NULL;
|
||||
status_t error = fVolume->AllocateBlock(newBlockSize, &block);
|
||||
if (error == B_OK) {
|
||||
// copy the data from the small buffer into the block
|
||||
if (fSize > 0)
|
||||
memcpy(block->GetData(), fSmallBuffer, fSize);
|
||||
// construct the block list and add the block
|
||||
new (fBlocks) BlockList(10);
|
||||
BlockList *blocks = _GetBlockList();
|
||||
if (blocks->AddItem(block)) {
|
||||
fSize = newBlockSize;
|
||||
} else {
|
||||
// error: destroy the block list and free the block
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
blocks->~BlockList();
|
||||
if (fSize > 0)
|
||||
memcpy(fSmallBuffer, block->GetData(), fSize);
|
||||
fVolume->FreeBlock(block);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// _SwitchToSmallBufferMode
|
||||
void
|
||||
DataContainer::_SwitchToSmallBufferMode(size_t newSize)
|
||||
{
|
||||
// remove the first (and only) block
|
||||
BlockList *blocks = _GetBlockList();
|
||||
BlockReference *block = blocks->ItemAt(0);
|
||||
blocks->RemoveItem(0L);
|
||||
// destroy the block list and copy the data into the small buffer
|
||||
blocks->~BlockList();
|
||||
if (newSize > 0)
|
||||
memcpy(fSmallBuffer, block->GetData(), newSize);
|
||||
// free the block and set the new size
|
||||
fVolume->FreeBlock(block);
|
||||
fSize = newSize;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// DataContainer.h
|
||||
|
||||
#ifndef DATA_CONTAINER_H
|
||||
#define DATA_CONTAINER_H
|
||||
|
||||
#include "List.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class BlockReference;
|
||||
class Volume;
|
||||
|
||||
// Size of the DataContainer's small buffer. If it contains data up to this
|
||||
// size, no blocks are allocated, but the small buffer is used instead.
|
||||
// 16 bytes are for free, since they are shared with the block list.
|
||||
// (actually even more, since the list has an initial size).
|
||||
// I ran a test analyzing what sizes the attributes in my system have:
|
||||
// size percentage bytes used in average
|
||||
// <= 0 0.00 93.45
|
||||
// <= 4 25.46 75.48
|
||||
// <= 8 30.54 73.02
|
||||
// <= 16 52.98 60.37
|
||||
// <= 32 80.19 51.74
|
||||
// <= 64 94.38 70.54
|
||||
// <= 126 96.90 128.23
|
||||
//
|
||||
// For average memory usage it is assumed, that attributes larger than 126
|
||||
// bytes have size 127, that the list has an initial capacity of 10 entries
|
||||
// (40 bytes), that the block reference consumes 4 bytes and the block header
|
||||
// 12 bytes. The optimal length is actually 35, with 51.05 bytes per
|
||||
// attribute, but I conservatively rounded to 32.
|
||||
static const size_t kSmallDataContainerSize = 32;
|
||||
|
||||
class DataContainer {
|
||||
public:
|
||||
DataContainer(Volume *volume);
|
||||
virtual ~DataContainer();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
Volume *GetVolume() const { return fVolume; }
|
||||
|
||||
status_t Resize(off_t newSize);
|
||||
off_t GetSize() const { return fSize; }
|
||||
|
||||
virtual status_t ReadAt(off_t offset, void *buffer, size_t size,
|
||||
size_t *bytesRead);
|
||||
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||
size_t *bytesWritten);
|
||||
|
||||
void GetFirstDataBlock(const uint8 **data, size_t *length);
|
||||
|
||||
// debugging
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
typedef List<BlockReference*> BlockList;
|
||||
|
||||
private:
|
||||
static inline bool _RequiresBlockMode(size_t size);
|
||||
inline bool _IsBlockMode() const;
|
||||
|
||||
inline BlockList *_GetBlockList();
|
||||
inline const BlockList *_GetBlockList() const;
|
||||
inline int32 _CountBlocks() const;
|
||||
inline void *_GetBlockDataAt(int32 index, size_t offset, size_t size);
|
||||
|
||||
void _ClearArea(off_t offset, off_t size);
|
||||
|
||||
status_t _Resize(off_t newSize);
|
||||
status_t _ResizeLastBlock(size_t newSize);
|
||||
|
||||
status_t _SwitchToBlockMode(size_t newBlockSize);
|
||||
void _SwitchToSmallBufferMode(size_t newSize);
|
||||
|
||||
private:
|
||||
Volume *fVolume;
|
||||
off_t fSize;
|
||||
union {
|
||||
uint8 fBlocks[sizeof(BlockList)];
|
||||
uint8 fSmallBuffer[kSmallDataContainerSize];
|
||||
};
|
||||
};
|
||||
|
||||
#endif // DATA_CONTAINER_H
|
||||
@@ -0,0 +1,348 @@
|
||||
// Directory.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "Directory.h"
|
||||
#include "Entry.h"
|
||||
#include "EntryIterator.h"
|
||||
#include "File.h"
|
||||
#include "SymLink.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
Directory::Directory(Volume *volume)
|
||||
: Node(volume, NODE_TYPE_DIRECTORY),
|
||||
fEntries()
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
Directory::~Directory()
|
||||
{
|
||||
// delete all entries
|
||||
while (Entry *entry = fEntries.GetFirst()) {
|
||||
if (DeleteEntry(entry) != B_OK) {
|
||||
FATAL(("Could not delete all entries in directory.\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Link
|
||||
status_t
|
||||
Directory::Link(Entry *entry)
|
||||
{
|
||||
if (fReferrers.IsEmpty())
|
||||
return Node::Link(entry);
|
||||
return B_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
// Unlink
|
||||
status_t
|
||||
Directory::Unlink(Entry *entry)
|
||||
{
|
||||
if (entry == fReferrers.GetFirst())
|
||||
return Node::Unlink(entry);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// SetSize
|
||||
status_t
|
||||
Directory::SetSize(off_t /*newSize*/)
|
||||
{
|
||||
return B_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
// GetSize
|
||||
off_t
|
||||
Directory::GetSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// GetParent
|
||||
Directory *
|
||||
Directory::GetParent() const
|
||||
{
|
||||
Entry *entry = fReferrers.GetFirst();
|
||||
return (entry ? entry->GetParent() : NULL);
|
||||
}
|
||||
|
||||
// CreateDirectory
|
||||
status_t
|
||||
Directory::CreateDirectory(const char *name, Directory **directory)
|
||||
{
|
||||
status_t error = (name && directory ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// create directory
|
||||
if (Directory *node = new(nothrow) Directory(GetVolume())) {
|
||||
error = _CreateCommon(node, name);
|
||||
// deletes the node on failure
|
||||
if (error == B_OK)
|
||||
*directory = node;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// CreateFile
|
||||
status_t
|
||||
Directory::CreateFile(const char *name, File **file)
|
||||
{
|
||||
status_t error = (name && file ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// create file
|
||||
if (File *node = new(nothrow) File(GetVolume())) {
|
||||
error = _CreateCommon(node, name);
|
||||
// deletes the node on failure
|
||||
if (error == B_OK)
|
||||
*file = node;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// CreateSymLink
|
||||
status_t
|
||||
Directory::CreateSymLink(const char *name, const char *path, SymLink **symLink)
|
||||
{
|
||||
status_t error = (name && symlink ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// create symlink
|
||||
if (SymLink *node = new(nothrow) SymLink(GetVolume())) {
|
||||
error = node->SetLinkedPath(path);
|
||||
if (error == B_OK) {
|
||||
error = _CreateCommon(node, name);
|
||||
// deletes the node on failure
|
||||
if (error == B_OK)
|
||||
*symLink = node;
|
||||
} else
|
||||
delete node;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// AddEntry
|
||||
status_t
|
||||
Directory::AddEntry(Entry *entry)
|
||||
{
|
||||
status_t error = (entry && !entry->GetParent() ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
fEntries.Insert(entry);
|
||||
entry->SetParent(this);
|
||||
error = GetVolume()->EntryAdded(GetID(), entry);
|
||||
if (error == B_OK) {
|
||||
MarkModified();
|
||||
} else {
|
||||
fEntries.Remove(entry);
|
||||
entry->SetParent(NULL);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// CreateEntry
|
||||
status_t
|
||||
Directory::CreateEntry(Node *node, const char *name, Entry **_entry)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// create an entry
|
||||
Entry *entry = new(nothrow) Entry(name);
|
||||
if (entry) {
|
||||
error = entry->InitCheck();
|
||||
if (error == B_OK) {
|
||||
// link to the node
|
||||
error = entry->Link(node);
|
||||
if (error == B_OK) {
|
||||
// add the entry
|
||||
error = AddEntry(entry);
|
||||
if (error == B_OK) {
|
||||
if (_entry)
|
||||
*_entry = entry;
|
||||
} else {
|
||||
// failure: unlink the node
|
||||
entry->Unlink();
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete the entry on failure
|
||||
if (error != B_OK)
|
||||
delete entry;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveEntry
|
||||
status_t
|
||||
Directory::RemoveEntry(Entry *entry)
|
||||
{
|
||||
status_t error = (entry && entry->GetParent() == this ? B_OK
|
||||
: B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// move all iterators pointing to the entry to the next entry
|
||||
if (GetVolume()->IteratorLock()) {
|
||||
// set the iterators' current entry
|
||||
Entry *nextEntry = fEntries.GetNext(entry);
|
||||
DLList<EntryIterator> *iterators = entry->GetEntryIteratorList();
|
||||
for (EntryIterator *iterator = iterators->GetFirst();
|
||||
iterator;
|
||||
iterator = iterators->GetNext(iterator)) {
|
||||
iterator->SetCurrent(nextEntry, true);
|
||||
}
|
||||
// Move the iterators from one list to the other, or just remove
|
||||
// them, if there is no next entry.
|
||||
if (nextEntry) {
|
||||
DLList<EntryIterator> *nextIterators
|
||||
= nextEntry->GetEntryIteratorList();
|
||||
nextIterators->MoveFrom(iterators);
|
||||
} else
|
||||
iterators->RemoveAll();
|
||||
GetVolume()->IteratorUnlock();
|
||||
} else
|
||||
error = B_ERROR;
|
||||
// remove the entry
|
||||
if (error == B_OK) {
|
||||
error = GetVolume()->EntryRemoved(GetID(), entry);
|
||||
if (error == B_OK) {
|
||||
fEntries.Remove(entry);
|
||||
entry->SetParent(NULL);
|
||||
MarkModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// DeleteEntry
|
||||
status_t
|
||||
Directory::DeleteEntry(Entry *entry)
|
||||
{
|
||||
status_t error = RemoveEntry(entry);
|
||||
if (error == B_OK) {
|
||||
error = entry->Unlink();
|
||||
if (error == B_OK)
|
||||
delete entry;
|
||||
else {
|
||||
FATAL(("Failed to Unlink() entry %p from node %Ld!\n", entry,
|
||||
entry->GetNode()->GetID()));
|
||||
AddEntry(entry);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindEntry
|
||||
status_t
|
||||
Directory::FindEntry(const char *name, Entry **_entry) const
|
||||
{
|
||||
status_t error = (name && _entry ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
/*
|
||||
Entry *entry = NULL;
|
||||
while (GetNextEntry(&entry) == B_OK) {
|
||||
if (!strcmp(entry->GetName(), name)) {
|
||||
*_entry = entry;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
*/
|
||||
error = GetVolume()->FindEntry(GetID(), name, _entry);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindNode
|
||||
status_t
|
||||
Directory::FindNode(const char *name, Node **node) const
|
||||
{
|
||||
status_t error = (name && node ? B_OK : B_BAD_VALUE);
|
||||
Entry *entry = NULL;
|
||||
if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK)
|
||||
*node = entry->GetNode();
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindAndGetNode
|
||||
status_t
|
||||
Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const
|
||||
{
|
||||
status_t error = (name && node ? B_OK : B_BAD_VALUE);
|
||||
Entry *entry = NULL;
|
||||
if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) {
|
||||
*node = entry->GetNode();
|
||||
if (_entry)
|
||||
*_entry = entry;
|
||||
error = GetVolume()->GetVNode(*node);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetPreviousEntry
|
||||
status_t
|
||||
Directory::GetPreviousEntry(Entry **entry) const
|
||||
{
|
||||
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (!*entry)
|
||||
*entry = fEntries.GetLast();
|
||||
else if ((*entry)->GetParent() == this)
|
||||
*entry = fEntries.GetPrevious(*entry);
|
||||
else
|
||||
error = B_BAD_VALUE;
|
||||
if (error == B_OK && !*entry)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNextEntry
|
||||
status_t
|
||||
Directory::GetNextEntry(Entry **entry) const
|
||||
{
|
||||
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (!*entry)
|
||||
*entry = fEntries.GetFirst();
|
||||
else if ((*entry)->GetParent() == this)
|
||||
*entry = fEntries.GetNext(*entry);
|
||||
else
|
||||
error = B_BAD_VALUE;
|
||||
if (error == B_OK && !*entry)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
Directory::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
Node::GetAllocationInfo(info);
|
||||
info.AddDirectoryAllocation();
|
||||
Entry *entry = NULL;
|
||||
while (GetNextEntry(&entry) == B_OK)
|
||||
entry->GetAllocationInfo(info);
|
||||
}
|
||||
|
||||
// _CreateCommon
|
||||
status_t
|
||||
Directory::_CreateCommon(Node *node, const char *name)
|
||||
{
|
||||
status_t error = node->InitCheck();
|
||||
if (error == B_OK) {
|
||||
// add node to directory
|
||||
error = CreateEntry(node, name);
|
||||
}
|
||||
if (error != B_OK)
|
||||
delete node;
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Directory.h
|
||||
|
||||
#ifndef DIRECTORY_H
|
||||
#define DIRECTORY_H
|
||||
|
||||
#include "DLList.h"
|
||||
#include "Node.h"
|
||||
|
||||
class Entry;
|
||||
class File;
|
||||
class SymLink;
|
||||
|
||||
class Directory : public Node {
|
||||
public:
|
||||
Directory(Volume *volume);
|
||||
virtual ~Directory();
|
||||
|
||||
virtual status_t Link(Entry *entry);
|
||||
virtual status_t Unlink(Entry *entry);
|
||||
|
||||
virtual status_t SetSize(off_t newSize);
|
||||
virtual off_t GetSize() const;
|
||||
|
||||
Directory *GetParent() const;
|
||||
|
||||
status_t CreateDirectory(const char *name, Directory **directory);
|
||||
status_t CreateFile(const char *name, File **file);
|
||||
status_t CreateSymLink(const char *name, const char *path,
|
||||
SymLink **symLink);
|
||||
|
||||
bool IsEmpty() const { return fEntries.IsEmpty(); }
|
||||
|
||||
status_t AddEntry(Entry *entry);
|
||||
status_t CreateEntry(Node *node, const char *name, Entry **entry = NULL);
|
||||
status_t RemoveEntry(Entry *entry);
|
||||
status_t DeleteEntry(Entry *entry);
|
||||
|
||||
status_t FindEntry(const char *name, Entry **entry) const;
|
||||
status_t FindNode(const char *name, Node **node) const;
|
||||
status_t FindAndGetNode(const char *name, Node **node,
|
||||
Entry **entry = NULL) const;
|
||||
|
||||
status_t GetPreviousEntry(Entry **entry) const;
|
||||
status_t GetNextEntry(Entry **entry) const;
|
||||
|
||||
// debugging
|
||||
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
status_t _CreateCommon(Node *node, const char *name);
|
||||
|
||||
private:
|
||||
DLList<Entry> fEntries;
|
||||
};
|
||||
|
||||
#endif // DIRECTORY_H
|
||||
@@ -0,0 +1,104 @@
|
||||
// Entry.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "Entry.h"
|
||||
#include "EntryIterator.h"
|
||||
#include "Node.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
Entry::Entry(const char *name, Node *node, Directory *parent)
|
||||
: fParent(parent),
|
||||
fNode(node),
|
||||
fName(name),
|
||||
fReferrerLink(),
|
||||
fIterators()
|
||||
{
|
||||
if (node)
|
||||
Link(node);
|
||||
}
|
||||
|
||||
// destructor
|
||||
Entry::~Entry()
|
||||
{
|
||||
if (fNode)
|
||||
Unlink();
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
Entry::InitCheck() const
|
||||
{
|
||||
return (fName.GetString() ? B_OK : B_NO_INIT);
|
||||
}
|
||||
|
||||
// Link
|
||||
status_t
|
||||
Entry::Link(Node *node)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// We first link to the new node and then unlink the old one. So, no
|
||||
// harm is done, if both are the same.
|
||||
Node *oldNode = fNode;
|
||||
error = node->Link(this);
|
||||
if (error == B_OK) {
|
||||
fNode = node;
|
||||
if (oldNode)
|
||||
oldNode->Unlink(this);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Unlink
|
||||
status_t
|
||||
Entry::Unlink()
|
||||
{
|
||||
status_t error = (fNode ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK && (error = fNode->Unlink(this)) == B_OK)
|
||||
fNode = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetName
|
||||
status_t
|
||||
Entry::SetName(const char *newName)
|
||||
{
|
||||
status_t error = (newName ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (fName.SetTo(newName)) {
|
||||
// if (fNode)
|
||||
// fNode->MarkModified();
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// AttachEntryIterator
|
||||
void
|
||||
Entry::AttachEntryIterator(EntryIterator *iterator)
|
||||
{
|
||||
if (iterator && iterator->GetCurrent() == this && !iterator->IsSuspended())
|
||||
fIterators.Insert(iterator);
|
||||
}
|
||||
|
||||
// DetachEntryIterator
|
||||
void
|
||||
Entry::DetachEntryIterator(EntryIterator *iterator)
|
||||
{
|
||||
if (iterator && iterator->GetCurrent() == this && iterator->IsSuspended())
|
||||
fIterators.Remove(iterator);
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
Entry::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddEntryAllocation();
|
||||
info.AddStringAllocation(fName.GetLength());
|
||||
fNode->GetAllocationInfo(info);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Entry.h
|
||||
|
||||
#ifndef ENTRY_H
|
||||
#define ENTRY_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "DLList.h"
|
||||
#include "fsproto.h"
|
||||
#include "String.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class Directory;
|
||||
class EntryIterator;
|
||||
class Node;
|
||||
|
||||
class Entry : public DLListLinkImpl<Entry> {
|
||||
public:
|
||||
Entry(const char *name, Node *node = NULL, Directory *parent = NULL);
|
||||
~Entry();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
inline void SetParent(Directory *parent) { fParent = parent; }
|
||||
Directory *GetParent() const { return fParent; }
|
||||
|
||||
// inline void SetNode(Node *node) { fNode = node; }
|
||||
status_t Link(Node *node);
|
||||
status_t Unlink();
|
||||
Node *GetNode() const { return fNode; }
|
||||
|
||||
status_t SetName(const char *newName);
|
||||
inline const char *GetName() const { return fName.GetString(); }
|
||||
|
||||
// inline Volume *GetVolume() const { return fVolume; }
|
||||
|
||||
inline DLListLink<Entry> *GetReferrerLink() { return &fReferrerLink; }
|
||||
|
||||
// entry iterator management
|
||||
void AttachEntryIterator(EntryIterator *iterator);
|
||||
void DetachEntryIterator(EntryIterator *iterator);
|
||||
inline DLList<EntryIterator> *GetEntryIteratorList()
|
||||
{ return &fIterators; }
|
||||
|
||||
// debugging
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
Directory *fParent;
|
||||
Node *fNode;
|
||||
String fName;
|
||||
DLListLink<Entry> fReferrerLink;
|
||||
// iterator management
|
||||
DLList<EntryIterator> fIterators;
|
||||
};
|
||||
|
||||
// GetNodeReferrerLink
|
||||
class GetNodeReferrerLink {
|
||||
private:
|
||||
typedef DLListLink<Entry> Link;
|
||||
|
||||
public:
|
||||
inline Link *operator()(Entry *entry) const
|
||||
{
|
||||
return entry->GetReferrerLink();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ENTRY_H
|
||||
@@ -0,0 +1,137 @@
|
||||
// EntryIterator.cpp
|
||||
|
||||
#include "Directory.h"
|
||||
#include "Entry.h"
|
||||
#include "EntryIterator.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
EntryIterator::EntryIterator(Directory *directory)
|
||||
: fDirectory(directory),
|
||||
fEntry(NULL),
|
||||
fSuspended(false),
|
||||
fIsNext(false),
|
||||
fDone(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
EntryIterator::~EntryIterator()
|
||||
{
|
||||
Unset();
|
||||
}
|
||||
|
||||
// SetTo
|
||||
status_t
|
||||
EntryIterator::SetTo(Directory *directory)
|
||||
{
|
||||
Unset();
|
||||
status_t error = (directory ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
fDirectory = directory;
|
||||
fEntry = NULL;
|
||||
fSuspended = false;
|
||||
fIsNext = false;
|
||||
fDone = false;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Unset
|
||||
void
|
||||
EntryIterator::Unset()
|
||||
{
|
||||
if (fDirectory && fSuspended)
|
||||
Resume();
|
||||
fDirectory = NULL;
|
||||
fEntry = NULL;
|
||||
fSuspended = false;
|
||||
fIsNext = false;
|
||||
fDone = false;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
EntryIterator::Suspend()
|
||||
{
|
||||
status_t error = (fDirectory ? B_OK : B_ERROR);
|
||||
if (error == B_OK) {
|
||||
if (fDirectory->GetVolume()->IteratorLock()) {
|
||||
if (!fSuspended) {
|
||||
if (fEntry)
|
||||
fEntry->AttachEntryIterator(this);
|
||||
fDirectory->GetVolume()->IteratorUnlock();
|
||||
fSuspended = true;
|
||||
} else
|
||||
error = B_ERROR;
|
||||
} else
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
EntryIterator::Resume()
|
||||
{
|
||||
status_t error = (fDirectory ? B_OK : B_ERROR);
|
||||
if (error == B_OK) {
|
||||
if (fDirectory->GetVolume()->IteratorLock()) {
|
||||
if (fSuspended) {
|
||||
if (fEntry)
|
||||
fEntry->DetachEntryIterator(this);
|
||||
fSuspended = false;
|
||||
}
|
||||
fDirectory->GetVolume()->IteratorUnlock();
|
||||
} else
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNext
|
||||
status_t
|
||||
EntryIterator::GetNext(Entry **entry)
|
||||
{
|
||||
status_t error = B_ENTRY_NOT_FOUND;
|
||||
if (!fDone && fDirectory && entry) {
|
||||
if (fIsNext) {
|
||||
fIsNext = false;
|
||||
if (fEntry)
|
||||
error = B_OK;
|
||||
} else
|
||||
error = fDirectory->GetNextEntry(&fEntry);
|
||||
*entry = fEntry;
|
||||
}
|
||||
fDone = (error != B_OK);
|
||||
return error;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
status_t
|
||||
EntryIterator::Rewind()
|
||||
{
|
||||
status_t error = (fDirectory ? B_OK : B_ERROR);
|
||||
if (error == B_OK) {
|
||||
if (fDirectory->GetVolume()->IteratorLock()) {
|
||||
if (fSuspended && fEntry)
|
||||
fEntry->DetachEntryIterator(this);
|
||||
fEntry = NULL;
|
||||
fIsNext = false;
|
||||
fDone = false;
|
||||
fDirectory->GetVolume()->IteratorUnlock();
|
||||
} else
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetCurrent
|
||||
void
|
||||
EntryIterator::SetCurrent(Entry *entry, bool isNext)
|
||||
{
|
||||
fIsNext = isNext;
|
||||
fEntry = entry;
|
||||
fDone = !fEntry;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// EntryIterator.h
|
||||
|
||||
#ifndef ENTRY_ITERATOR_H
|
||||
#define ENTRY_ITERATOR_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "DLList.h"
|
||||
|
||||
class Directory;
|
||||
class Entry;
|
||||
|
||||
class EntryIterator : public DLListLinkImpl<EntryIterator> {
|
||||
public:
|
||||
EntryIterator(Directory *directory = NULL);
|
||||
~EntryIterator();
|
||||
|
||||
status_t SetTo(Directory *directory);
|
||||
void Unset();
|
||||
|
||||
Directory *GetDirectory() const { return fDirectory; }
|
||||
|
||||
status_t Suspend();
|
||||
status_t Resume();
|
||||
bool IsSuspended() const { return fSuspended; }
|
||||
|
||||
status_t GetNext(Entry **entry);
|
||||
Entry *GetCurrent() const { return fEntry; }
|
||||
|
||||
status_t Rewind();
|
||||
|
||||
private:
|
||||
void SetCurrent(Entry *entry, bool isNext);
|
||||
|
||||
private:
|
||||
friend class Directory;
|
||||
|
||||
private:
|
||||
Directory *fDirectory;
|
||||
Entry *fEntry;
|
||||
bool fSuspended;
|
||||
bool fIsNext;
|
||||
bool fDone;
|
||||
};
|
||||
|
||||
#endif // ENTRY_ITERATOR_H
|
||||
@@ -0,0 +1,26 @@
|
||||
// EntryListener.cpp
|
||||
|
||||
#include "EntryListener.h"
|
||||
|
||||
// constructor
|
||||
EntryListener::EntryListener()
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
EntryListener::~EntryListener()
|
||||
{
|
||||
}
|
||||
|
||||
// EntryAdded
|
||||
void
|
||||
EntryListener::EntryAdded(Entry */*entry*/)
|
||||
{
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
void
|
||||
EntryListener::EntryRemoved(Entry */*entry*/)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// EntryListener.h
|
||||
|
||||
#ifndef ENTRY_LISTENER_H
|
||||
#define ENTRY_LISTENER_H
|
||||
|
||||
class Entry;
|
||||
|
||||
// listening flags
|
||||
enum {
|
||||
ENTRY_LISTEN_ANY_ENTRY = 0x01,
|
||||
ENTRY_LISTEN_ADDED = 0x02,
|
||||
ENTRY_LISTEN_REMOVED = 0x04,
|
||||
ENTRY_LISTEN_ALL = ENTRY_LISTEN_ADDED | ENTRY_LISTEN_REMOVED,
|
||||
};
|
||||
|
||||
class EntryListener {
|
||||
public:
|
||||
EntryListener();
|
||||
virtual ~EntryListener();
|
||||
|
||||
virtual void EntryAdded(Entry *entry);
|
||||
virtual void EntryRemoved(Entry *entry);
|
||||
};
|
||||
|
||||
#endif // ENTRY_LISTENER_H
|
||||
@@ -0,0 +1,75 @@
|
||||
// File.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "File.h"
|
||||
#include "SizeIndex.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
File::File(Volume *volume)
|
||||
: Node(volume, NODE_TYPE_FILE),
|
||||
DataContainer(volume)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
File::~File()
|
||||
{
|
||||
}
|
||||
|
||||
// ReadAt
|
||||
status_t
|
||||
File::ReadAt(off_t offset, void *buffer, size_t size, size_t *bytesRead)
|
||||
{
|
||||
status_t error = DataContainer::ReadAt(offset, buffer, size, bytesRead);
|
||||
// TODO: update access time?
|
||||
return error;
|
||||
}
|
||||
|
||||
// WriteAt
|
||||
status_t
|
||||
File::WriteAt(off_t offset, const void *buffer, size_t size,
|
||||
size_t *bytesWritten)
|
||||
{
|
||||
off_t oldSize = DataContainer::GetSize();
|
||||
status_t error = DataContainer::WriteAt(offset, buffer, size,
|
||||
bytesWritten);
|
||||
MarkModified();
|
||||
// update the size index, if our size has changed
|
||||
if (oldSize != DataContainer::GetSize()) {
|
||||
if (SizeIndex *index = GetVolume()->GetSizeIndex())
|
||||
index->Changed(this, oldSize);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetSize
|
||||
status_t
|
||||
File::SetSize(off_t newSize)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
off_t oldSize = DataContainer::GetSize();
|
||||
if (newSize != oldSize) {
|
||||
error = DataContainer::Resize(newSize);
|
||||
MarkModified();
|
||||
// update the size index
|
||||
if (SizeIndex *index = GetVolume()->GetSizeIndex())
|
||||
index->Changed(this, oldSize);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetSize
|
||||
off_t
|
||||
File::GetSize() const
|
||||
{
|
||||
return DataContainer::GetSize();
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
File::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddFileAllocation(GetSize());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// File.h
|
||||
|
||||
#ifndef FILE_H
|
||||
#define FILE_H
|
||||
|
||||
#include "DataContainer.h"
|
||||
#include "Node.h"
|
||||
|
||||
class File : public Node, public DataContainer {
|
||||
public:
|
||||
File(Volume *volume);
|
||||
virtual ~File();
|
||||
|
||||
Volume *GetVolume() const { return Node::GetVolume(); }
|
||||
|
||||
virtual status_t ReadAt(off_t offset, void *buffer, size_t size,
|
||||
size_t *bytesRead);
|
||||
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||
size_t *bytesWritten);
|
||||
|
||||
virtual status_t SetSize(off_t newSize);
|
||||
virtual off_t GetSize() const;
|
||||
|
||||
// debugging
|
||||
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||
};
|
||||
|
||||
#endif // FILE_H
|
||||
@@ -0,0 +1,192 @@
|
||||
// Index.cpp
|
||||
|
||||
#include "Debug.h"
|
||||
#include "Directory.h"
|
||||
#include "Entry.h"
|
||||
#include "Index.h"
|
||||
#include "IndexImpl.h"
|
||||
|
||||
// Index
|
||||
|
||||
// constructor
|
||||
Index::Index(Volume *volume, const char *name, uint32 type,
|
||||
bool fixedKeyLength, size_t keyLength)
|
||||
: fVolume(volume),
|
||||
fInitStatus(B_OK),
|
||||
fName(name),
|
||||
fType(type),
|
||||
fKeyLength(keyLength),
|
||||
fFixedKeyLength(fixedKeyLength)
|
||||
{
|
||||
if (!fVolume)
|
||||
fInitStatus = B_BAD_VALUE;
|
||||
else if (!fName.GetString())
|
||||
fInitStatus = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
// destructor
|
||||
Index::~Index()
|
||||
{
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
Index::InitCheck() const
|
||||
{
|
||||
return fInitStatus;
|
||||
}
|
||||
|
||||
// GetIterator
|
||||
bool
|
||||
Index::GetIterator(IndexEntryIterator *iterator)
|
||||
{
|
||||
bool result = false;
|
||||
if (iterator) {
|
||||
AbstractIndexEntryIterator *actualIterator = InternalGetIterator();
|
||||
if (actualIterator) {
|
||||
iterator->SetIterator(actualIterator);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Find
|
||||
bool
|
||||
Index::Find(const uint8 *key, size_t length, IndexEntryIterator *iterator)
|
||||
{
|
||||
bool result = false;
|
||||
if (key && iterator) {
|
||||
AbstractIndexEntryIterator *actualIterator
|
||||
= InternalFind(key, length);
|
||||
if (actualIterator) {
|
||||
iterator->SetIterator(actualIterator);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Dump
|
||||
void
|
||||
Index::Dump()
|
||||
{
|
||||
PRINT(("Index: `%s', type: %lx\n", GetName(), GetType()));
|
||||
for (IndexEntryIterator it(this); it.GetCurrent(); it.GetNext()) {
|
||||
Entry *entry = it.GetCurrent();
|
||||
PRINT((" entry: `%s', dir: %Ld\n", entry->GetName(),
|
||||
entry->GetParent()->GetID()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// IndexEntryIterator
|
||||
|
||||
// constructor
|
||||
IndexEntryIterator::IndexEntryIterator()
|
||||
: fIterator(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// constructor
|
||||
IndexEntryIterator::IndexEntryIterator(Index *index)
|
||||
: fIterator(NULL)
|
||||
{
|
||||
if (index)
|
||||
index->GetIterator(this);
|
||||
}
|
||||
|
||||
// destructor
|
||||
IndexEntryIterator::~IndexEntryIterator()
|
||||
{
|
||||
SetIterator(NULL);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
IndexEntryIterator::GetCurrent()
|
||||
{
|
||||
return (fIterator ? fIterator->GetCurrent() : NULL);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
IndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||
{
|
||||
return (fIterator ? fIterator->GetCurrent(buffer, keyLength) : NULL);
|
||||
}
|
||||
|
||||
// GetPrevious
|
||||
Entry *
|
||||
IndexEntryIterator::GetPrevious()
|
||||
{
|
||||
return (fIterator ? fIterator->GetPrevious() : NULL);
|
||||
}
|
||||
|
||||
// GetNext
|
||||
Entry *
|
||||
IndexEntryIterator::GetNext()
|
||||
{
|
||||
return (fIterator ? fIterator->GetNext() : NULL);
|
||||
}
|
||||
|
||||
// GetNext
|
||||
Entry *
|
||||
IndexEntryIterator::GetNext(uint8 *buffer, size_t *keyLength)
|
||||
{
|
||||
Entry *entry = NULL;
|
||||
if (fIterator && fIterator->GetNext())
|
||||
entry = GetCurrent(buffer, keyLength);
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
IndexEntryIterator::Suspend()
|
||||
{
|
||||
return (fIterator ? fIterator->Suspend() : B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
IndexEntryIterator::Resume()
|
||||
{
|
||||
return (fIterator ? fIterator->Resume() : B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// SetIterator
|
||||
void
|
||||
IndexEntryIterator::SetIterator(AbstractIndexEntryIterator *iterator)
|
||||
{
|
||||
if (fIterator)
|
||||
delete fIterator;
|
||||
fIterator = iterator;
|
||||
}
|
||||
|
||||
|
||||
// AbstractIndexEntryIterator
|
||||
|
||||
// constructor
|
||||
AbstractIndexEntryIterator::AbstractIndexEntryIterator()
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
AbstractIndexEntryIterator::~AbstractIndexEntryIterator()
|
||||
{
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
AbstractIndexEntryIterator::Suspend()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
AbstractIndexEntryIterator::Resume()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Index.h
|
||||
|
||||
#ifndef INDEX_H
|
||||
#define INDEX_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "String.h"
|
||||
|
||||
class AbstractIndexEntryIterator;
|
||||
class Entry;
|
||||
class IndexEntryIterator;
|
||||
class Node;
|
||||
class Volume;
|
||||
|
||||
// Index
|
||||
class Index {
|
||||
public:
|
||||
Index(Volume *volume, const char *name, uint32 type,
|
||||
bool fixedKeyLength, size_t keyLength = 0);
|
||||
virtual ~Index();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
Volume *GetVolume() const { return fVolume; }
|
||||
void GetVolume(Volume *volume) { fVolume = volume; }
|
||||
|
||||
const char *GetName() const { return fName.GetString(); }
|
||||
uint32 GetType() const { return fType; }
|
||||
bool HasFixedKeyLength() const { return fFixedKeyLength; }
|
||||
size_t GetKeyLength() const { return fKeyLength; }
|
||||
|
||||
virtual int32 CountEntries() const = 0;
|
||||
|
||||
bool GetIterator(IndexEntryIterator *iterator);
|
||||
bool Find(const uint8 *key, size_t length,
|
||||
IndexEntryIterator *iterator);
|
||||
|
||||
// debugging
|
||||
void Dump();
|
||||
|
||||
protected:
|
||||
virtual AbstractIndexEntryIterator *InternalGetIterator() = 0;
|
||||
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||
size_t length) = 0;
|
||||
|
||||
protected:
|
||||
Volume *fVolume;
|
||||
status_t fInitStatus;
|
||||
String fName;
|
||||
uint32 fType;
|
||||
size_t fKeyLength;
|
||||
bool fFixedKeyLength;
|
||||
};
|
||||
|
||||
// IndexEntryIterator
|
||||
class IndexEntryIterator {
|
||||
public:
|
||||
IndexEntryIterator();
|
||||
IndexEntryIterator(Index *index);
|
||||
~IndexEntryIterator();
|
||||
|
||||
Entry *GetCurrent();
|
||||
Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||
Entry *GetPrevious();
|
||||
Entry *GetNext();
|
||||
Entry *GetNext(uint8 *buffer, size_t *keyLength);
|
||||
|
||||
status_t Suspend();
|
||||
status_t Resume();
|
||||
|
||||
private:
|
||||
void SetIterator(AbstractIndexEntryIterator *iterator);
|
||||
|
||||
private:
|
||||
friend class Index;
|
||||
|
||||
AbstractIndexEntryIterator *fIterator;
|
||||
};
|
||||
|
||||
#endif // INDEX_H
|
||||
@@ -0,0 +1,204 @@
|
||||
// IndexDirectory.cpp
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include "AttributeIndexImpl.h"
|
||||
#include "Debug.h"
|
||||
#include "IndexDirectory.h"
|
||||
#include "LastModifiedIndex.h"
|
||||
#include "NameIndex.h"
|
||||
#include "SizeIndex.h"
|
||||
|
||||
// constructor
|
||||
IndexDirectory::IndexDirectory(Volume *volume)
|
||||
: fVolume(volume),
|
||||
fNameIndex(NULL),
|
||||
fLastModifiedIndex(NULL),
|
||||
fSizeIndex(NULL),
|
||||
fIndices()
|
||||
{
|
||||
fNameIndex = new(nothrow) NameIndex(volume);
|
||||
fLastModifiedIndex = new(nothrow) LastModifiedIndex(volume);
|
||||
fSizeIndex = new(nothrow) SizeIndex(volume);
|
||||
if (fNameIndex && fLastModifiedIndex && fSizeIndex) {
|
||||
if (!fIndices.AddItem(fNameIndex)
|
||||
|| !fIndices.AddItem(fLastModifiedIndex)
|
||||
|| !fIndices.AddItem(fSizeIndex)) {
|
||||
fIndices.MakeEmpty();
|
||||
delete fNameIndex;
|
||||
delete fLastModifiedIndex;
|
||||
delete fSizeIndex;
|
||||
fNameIndex = NULL;
|
||||
fLastModifiedIndex = NULL;
|
||||
fSizeIndex = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
IndexDirectory::~IndexDirectory()
|
||||
{
|
||||
// delete the default indices
|
||||
if (fNameIndex) {
|
||||
fIndices.RemoveItem(fNameIndex);
|
||||
delete fNameIndex;
|
||||
}
|
||||
if (fLastModifiedIndex) {
|
||||
fIndices.RemoveItem(fLastModifiedIndex);
|
||||
delete fLastModifiedIndex;
|
||||
}
|
||||
if (fSizeIndex) {
|
||||
fIndices.RemoveItem(fSizeIndex);
|
||||
delete fSizeIndex;
|
||||
}
|
||||
// delete the attribute indices
|
||||
int32 count = fIndices.CountItems();
|
||||
for (int i = 0; i < count; i++)
|
||||
delete fIndices.ItemAt(i);
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
IndexDirectory::InitCheck() const
|
||||
{
|
||||
return (fNameIndex && fLastModifiedIndex && fSizeIndex ? B_OK
|
||||
: B_NO_MEMORY);
|
||||
}
|
||||
|
||||
// CreateIndex
|
||||
status_t
|
||||
IndexDirectory::CreateIndex(const char *name, uint32 type,
|
||||
AttributeIndex **_index)
|
||||
{
|
||||
status_t error = (name ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (!FindIndex(name)) {
|
||||
// create the index
|
||||
AttributeIndex *index = NULL;
|
||||
switch (type) {
|
||||
case B_INT32_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, sizeof(int32));
|
||||
break;
|
||||
case B_UINT32_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, sizeof(uint32));
|
||||
break;
|
||||
case B_INT64_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, sizeof(int64));
|
||||
break;
|
||||
case B_UINT64_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, sizeof(uint64));
|
||||
break;
|
||||
case B_FLOAT_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, sizeof(float));
|
||||
break;
|
||||
case B_DOUBLE_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, sizeof(double));
|
||||
break;
|
||||
case B_STRING_TYPE:
|
||||
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||
name, type, 0);
|
||||
break;
|
||||
default:
|
||||
error = B_BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
if (error == B_OK && !index)
|
||||
error = B_NO_MEMORY;
|
||||
// add the index
|
||||
if (error == B_OK) {
|
||||
if (fIndices.AddItem(index)) {
|
||||
if (_index)
|
||||
*_index = index;
|
||||
} else {
|
||||
delete index;
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
} else
|
||||
error = B_FILE_EXISTS;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// DeleteIndex
|
||||
bool
|
||||
IndexDirectory::DeleteIndex(const char *name, uint32 type)
|
||||
{
|
||||
return DeleteIndex(FindIndex(name, type));
|
||||
}
|
||||
|
||||
// DeleteIndex
|
||||
bool
|
||||
IndexDirectory::DeleteIndex(Index *index)
|
||||
{
|
||||
bool result = false;
|
||||
if (index && !IsSpecialIndex(index)) {
|
||||
int32 i = fIndices.IndexOf(index);
|
||||
if (i >= 0) {
|
||||
fIndices.RemoveItem(i);
|
||||
delete index;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FindIndex
|
||||
Index *
|
||||
IndexDirectory::FindIndex(const char *name)
|
||||
{
|
||||
if (name) {
|
||||
int32 count = fIndices.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
Index *index = fIndices.ItemAt(i);
|
||||
if (!strcmp(index->GetName(), name))
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// FindIndex
|
||||
Index *
|
||||
IndexDirectory::FindIndex(const char *name, uint32 type)
|
||||
{
|
||||
Index *index = FindIndex(name);
|
||||
if (index && index->GetType() != type)
|
||||
index = NULL;
|
||||
return index;
|
||||
}
|
||||
|
||||
// FindAttributeIndex
|
||||
AttributeIndex *
|
||||
IndexDirectory::FindAttributeIndex(const char *name)
|
||||
{
|
||||
AttributeIndex *attrIndex = NULL;
|
||||
if (Index *index = FindIndex(name))
|
||||
attrIndex = dynamic_cast<AttributeIndex*>(index);
|
||||
return attrIndex;
|
||||
}
|
||||
|
||||
// FindAttributeIndex
|
||||
AttributeIndex *
|
||||
IndexDirectory::FindAttributeIndex(const char *name, uint32 type)
|
||||
{
|
||||
AttributeIndex *attrIndex = NULL;
|
||||
if (Index *index = FindIndex(name, type))
|
||||
attrIndex = dynamic_cast<AttributeIndex*>(index);
|
||||
return attrIndex;
|
||||
}
|
||||
|
||||
// IsSpecialIndex
|
||||
bool
|
||||
IndexDirectory::IsSpecialIndex(Index *index) const
|
||||
{
|
||||
return (index == fNameIndex || index == fLastModifiedIndex
|
||||
|| index == fSizeIndex);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// IndexDirectory.h
|
||||
|
||||
#ifndef INDEX_DIRECTORY_H
|
||||
#define INDEX_DIRECTORY_H
|
||||
|
||||
#include "List.h"
|
||||
|
||||
class AttributeIndex;
|
||||
class Index;
|
||||
class LastModifiedIndex;
|
||||
class NameIndex;
|
||||
class SizeIndex;
|
||||
class Volume;
|
||||
|
||||
class IndexDirectory {
|
||||
public:
|
||||
IndexDirectory(Volume *volume);
|
||||
~IndexDirectory();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
status_t CreateIndex(const char *name, uint32 type,
|
||||
AttributeIndex **index = NULL);
|
||||
bool DeleteIndex(const char *name, uint32 type);
|
||||
bool DeleteIndex(Index *index);
|
||||
|
||||
Index *FindIndex(const char *name);
|
||||
Index *FindIndex(const char *name, uint32 type);
|
||||
AttributeIndex *FindAttributeIndex(const char *name);
|
||||
AttributeIndex *FindAttributeIndex(const char *name, uint32 type);
|
||||
|
||||
bool IsSpecialIndex(Index *index) const;
|
||||
NameIndex *GetNameIndex() const { return fNameIndex; }
|
||||
LastModifiedIndex *GetLastModifiedIndex() const
|
||||
{ return fLastModifiedIndex; }
|
||||
SizeIndex *GetSizeIndex() const { return fSizeIndex; }
|
||||
|
||||
Index *IndexAt(int32 index) const { return fIndices.ItemAt(index); }
|
||||
|
||||
private:
|
||||
Volume *fVolume;
|
||||
NameIndex *fNameIndex;
|
||||
LastModifiedIndex *fLastModifiedIndex;
|
||||
SizeIndex *fSizeIndex;
|
||||
List<Index*> fIndices;
|
||||
};
|
||||
|
||||
#endif // INDEX_DIRECTORY_H
|
||||
@@ -0,0 +1,148 @@
|
||||
// IndexImpl.h
|
||||
|
||||
#ifndef INDEX_IMPL_H
|
||||
#define INDEX_IMPL_H
|
||||
|
||||
#include "Index.h"
|
||||
#include "Node.h"
|
||||
|
||||
// AbstractIndexEntryIterator
|
||||
class AbstractIndexEntryIterator {
|
||||
public:
|
||||
AbstractIndexEntryIterator();
|
||||
virtual ~AbstractIndexEntryIterator();
|
||||
|
||||
virtual Entry *GetCurrent() = 0;
|
||||
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength) = 0;
|
||||
virtual Entry *GetPrevious() = 0;
|
||||
virtual Entry *GetNext() = 0;
|
||||
|
||||
virtual status_t Suspend();
|
||||
virtual status_t Resume();
|
||||
};
|
||||
|
||||
|
||||
// NodeEntryIterator
|
||||
template<typename NodeIterator>
|
||||
class NodeEntryIterator : public AbstractIndexEntryIterator {
|
||||
public:
|
||||
NodeEntryIterator();
|
||||
virtual ~NodeEntryIterator();
|
||||
|
||||
void Unset();
|
||||
|
||||
virtual Entry *GetCurrent();
|
||||
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength) = 0;
|
||||
virtual Entry *GetPrevious();
|
||||
virtual Entry *GetNext();
|
||||
|
||||
virtual status_t Suspend();
|
||||
virtual status_t Resume();
|
||||
|
||||
Node *GetCurrentNode() const { return fNode; }
|
||||
|
||||
protected:
|
||||
NodeIterator fIterator;
|
||||
Node *fNode;
|
||||
Entry *fEntry;
|
||||
bool fInitialized;
|
||||
bool fIsNext;
|
||||
bool fSuspended;
|
||||
};
|
||||
|
||||
// constructor
|
||||
template<typename NodeIterator>
|
||||
NodeEntryIterator<NodeIterator>::NodeEntryIterator()
|
||||
: AbstractIndexEntryIterator(),
|
||||
fIterator(),
|
||||
fNode(NULL),
|
||||
fEntry(NULL),
|
||||
fInitialized(false),
|
||||
fIsNext(false),
|
||||
fSuspended(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
template<typename NodeIterator>
|
||||
NodeEntryIterator<NodeIterator>::~NodeEntryIterator()
|
||||
{
|
||||
}
|
||||
|
||||
// Unset
|
||||
template<typename NodeIterator>
|
||||
void
|
||||
NodeEntryIterator<NodeIterator>::Unset()
|
||||
{
|
||||
fNode = NULL;
|
||||
fEntry = NULL;
|
||||
fInitialized = false;
|
||||
fIsNext = false;
|
||||
fSuspended = false;
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
template<typename NodeIterator>
|
||||
Entry *
|
||||
NodeEntryIterator<NodeIterator>::GetCurrent()
|
||||
{
|
||||
return fEntry;
|
||||
}
|
||||
|
||||
// GetPrevious
|
||||
template<typename NodeIterator>
|
||||
Entry *
|
||||
NodeEntryIterator<NodeIterator>::GetPrevious()
|
||||
{
|
||||
return NULL; // backwards iteration not implemented
|
||||
}
|
||||
|
||||
// GetNext
|
||||
template<typename NodeIterator>
|
||||
Entry *
|
||||
NodeEntryIterator<NodeIterator>::GetNext()
|
||||
{
|
||||
if (!fInitialized || !fNode || fSuspended)
|
||||
return NULL;
|
||||
if (!(fEntry && fIsNext)) {
|
||||
while (fNode) {
|
||||
if (fEntry)
|
||||
fEntry = fNode->GetNextReferrer(fEntry);
|
||||
while (fNode && !fEntry) {
|
||||
fNode = NULL;
|
||||
if (Node **nodeP = fIterator.GetNext()) {
|
||||
fNode = *nodeP;
|
||||
fEntry = fNode->GetFirstReferrer();
|
||||
}
|
||||
}
|
||||
if (fEntry)
|
||||
break;
|
||||
}
|
||||
}
|
||||
fIsNext = false;
|
||||
return fEntry;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
template<typename NodeIterator>
|
||||
status_t
|
||||
NodeEntryIterator<NodeIterator>::Suspend()
|
||||
{
|
||||
status_t error = (fInitialized && !fSuspended ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK)
|
||||
fSuspended = true;
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
template<typename NodeIterator>
|
||||
status_t
|
||||
NodeEntryIterator<NodeIterator>::Resume()
|
||||
{
|
||||
status_t error = (fInitialized && fSuspended ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK)
|
||||
fSuspended = false;
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif // INDEX_IMPL_H
|
||||
@@ -0,0 +1,62 @@
|
||||
SubDir HAIKU_TOP src tests add-ons kernel file_systems userlandfs r5 src test
|
||||
ramfs ;
|
||||
|
||||
SetSubDirSupportedPlatforms r5 bone dano ;
|
||||
|
||||
local userlandFSTop = [ FDirName $(HAIKU_TOP) src tests add-ons kernel
|
||||
file_systems userlandfs r5 ] ;
|
||||
local userlandFSIncludes = [ FDirName $(userlandFSTop) headers ] ;
|
||||
|
||||
DEFINES += USER=1 ;
|
||||
|
||||
SubDirC++Flags -include
|
||||
[ FDirName $(userlandFSIncludes) shared Compatibility.h ] ;
|
||||
|
||||
SubDirSysHdrs [ FDirName $(userlandFSIncludes) public ] ;
|
||||
SubDirHdrs [ FDirName $(userlandFSIncludes) shared ] ;
|
||||
|
||||
if $(OSPLAT) = X86 {
|
||||
# SubDirC++Flags -include [ FDirName $(UFS_TOP) src kernel_add_on
|
||||
# kernel-cpp.h ] ;
|
||||
SubDirC++Flags -include [ FDirName $(SUBDIR) cpp.h ] ;
|
||||
}
|
||||
|
||||
SEARCH_SOURCE += [ FDirName $(userlandFSTop) src shared ] ;
|
||||
|
||||
Addon <test>ramfs
|
||||
: # relpath - obsolete
|
||||
: Debug.cpp
|
||||
Locker.cpp
|
||||
String.cpp
|
||||
|
||||
AllocationInfo.cpp
|
||||
AreaUtils.cpp
|
||||
Attribute.cpp
|
||||
AttributeIndex.cpp
|
||||
AttributeIndexImpl.cpp
|
||||
AttributeIterator.cpp
|
||||
BlockAllocator.cpp
|
||||
BlockAllocatorArea.cpp
|
||||
BlockAllocatorAreaBucket.cpp
|
||||
BlockReferenceManager.cpp
|
||||
DataContainer.cpp
|
||||
Directory.cpp
|
||||
Entry.cpp
|
||||
EntryIterator.cpp
|
||||
EntryListener.cpp
|
||||
File.cpp
|
||||
Index.cpp
|
||||
IndexDirectory.cpp
|
||||
kernel_interface.cpp
|
||||
LastModifiedIndex.cpp
|
||||
NameIndex.cpp
|
||||
Node.cpp
|
||||
NodeListener.cpp
|
||||
NodeTable.cpp
|
||||
Query.cpp
|
||||
SizeIndex.cpp
|
||||
SymLink.cpp
|
||||
Volume.cpp
|
||||
: false # is executable
|
||||
: <test>UserlandFSServer
|
||||
;
|
||||
@@ -0,0 +1,373 @@
|
||||
// LastModifiedIndex.cpp
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "Entry.h"
|
||||
#include "EntryListener.h"
|
||||
#include "IndexImpl.h"
|
||||
#include "LastModifiedIndex.h"
|
||||
#include "Node.h"
|
||||
#include "NodeListener.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// LastModifiedIndexPrimaryKey
|
||||
class LastModifiedIndexPrimaryKey {
|
||||
public:
|
||||
LastModifiedIndexPrimaryKey(Node *node, time_t modified)
|
||||
: node(node), modified(modified) {}
|
||||
LastModifiedIndexPrimaryKey(Node *node)
|
||||
: node(node), modified(node->GetMTime()) {}
|
||||
LastModifiedIndexPrimaryKey(time_t modified)
|
||||
: node(NULL), modified(modified) {}
|
||||
|
||||
Node *node;
|
||||
time_t modified;
|
||||
};
|
||||
|
||||
// LastModifiedIndexGetPrimaryKey
|
||||
class LastModifiedIndexGetPrimaryKey {
|
||||
public:
|
||||
inline LastModifiedIndexPrimaryKey operator()(Node *a)
|
||||
{
|
||||
return LastModifiedIndexPrimaryKey(a);
|
||||
}
|
||||
|
||||
inline LastModifiedIndexPrimaryKey operator()(Node *a) const
|
||||
{
|
||||
return LastModifiedIndexPrimaryKey(a);
|
||||
}
|
||||
};
|
||||
|
||||
// LastModifiedIndexPrimaryKeyCompare
|
||||
class LastModifiedIndexPrimaryKeyCompare
|
||||
{
|
||||
public:
|
||||
inline int operator()(const LastModifiedIndexPrimaryKey &a,
|
||||
const LastModifiedIndexPrimaryKey &b) const
|
||||
{
|
||||
if (a.node != NULL && a.node == b.node)
|
||||
return 0;
|
||||
if (a.modified < b.modified)
|
||||
return -1;
|
||||
if (a.modified > b.modified)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// NodeTree
|
||||
typedef TwoKeyAVLTree<Node*, LastModifiedIndexPrimaryKey,
|
||||
LastModifiedIndexPrimaryKeyCompare,
|
||||
LastModifiedIndexGetPrimaryKey>
|
||||
_NodeTree;
|
||||
class LastModifiedIndex::NodeTree : public _NodeTree {};
|
||||
|
||||
|
||||
// IteratorList
|
||||
class LastModifiedIndex::IteratorList : public DLList<Iterator> {};
|
||||
|
||||
|
||||
// Iterator
|
||||
class LastModifiedIndex::Iterator
|
||||
: public NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator>,
|
||||
public DLListLinkImpl<Iterator>, public EntryListener,
|
||||
public NodeListener {
|
||||
public:
|
||||
Iterator();
|
||||
virtual ~Iterator();
|
||||
|
||||
virtual Entry *GetCurrent();
|
||||
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||
|
||||
virtual status_t Suspend();
|
||||
virtual status_t Resume();
|
||||
|
||||
bool SetTo(LastModifiedIndex *index, time_t modified,
|
||||
bool ignoreValue = false);
|
||||
void Unset();
|
||||
|
||||
virtual void EntryRemoved(Entry *entry);
|
||||
virtual void NodeRemoved(Node *node);
|
||||
|
||||
private:
|
||||
typedef NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator> BaseClass;
|
||||
|
||||
private:
|
||||
LastModifiedIndex *fIndex;
|
||||
};
|
||||
|
||||
|
||||
// LastModifiedIndex
|
||||
|
||||
// constructor
|
||||
LastModifiedIndex::LastModifiedIndex(Volume *volume)
|
||||
: Index(volume, "last_modified", B_INT32_TYPE, true, sizeof(time_t)),
|
||||
fNodes(new(nothrow) NodeTree),
|
||||
fIterators(new(nothrow) IteratorList)
|
||||
{
|
||||
if (fInitStatus == B_OK && (!fNodes || !fIterators))
|
||||
fInitStatus = B_NO_MEMORY;
|
||||
if (fInitStatus == B_OK) {
|
||||
fInitStatus = fVolume->AddNodeListener(this,
|
||||
NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
LastModifiedIndex::~LastModifiedIndex()
|
||||
{
|
||||
if (fVolume)
|
||||
fVolume->RemoveNodeListener(this, NULL);
|
||||
if (fIterators) {
|
||||
// unset the iterators
|
||||
for (Iterator *iterator = fIterators->GetFirst();
|
||||
iterator;
|
||||
iterator = fIterators->GetNext(iterator)) {
|
||||
iterator->SetTo(NULL, 0);
|
||||
}
|
||||
delete fIterators;
|
||||
}
|
||||
if (fNodes)
|
||||
delete fNodes;
|
||||
}
|
||||
|
||||
// CountEntries
|
||||
int32
|
||||
LastModifiedIndex::CountEntries() const
|
||||
{
|
||||
return fNodes->CountItems();
|
||||
}
|
||||
|
||||
// Changed
|
||||
status_t
|
||||
LastModifiedIndex::Changed(Node *node, time_t oldModified)
|
||||
{
|
||||
status_t error = B_BAD_VALUE;
|
||||
if (node) {
|
||||
NodeTree::Iterator it;
|
||||
Node **foundNode = fNodes->Find(LastModifiedIndexPrimaryKey(node,
|
||||
oldModified), node, &it);
|
||||
if (foundNode && *foundNode == node) {
|
||||
// update the iterators
|
||||
for (Iterator *iterator = fIterators->GetFirst();
|
||||
iterator;
|
||||
iterator = fIterators->GetNext(iterator)) {
|
||||
if (iterator->GetCurrentNode() == node)
|
||||
iterator->NodeRemoved(node);
|
||||
}
|
||||
// remove and re-insert the node
|
||||
fNodes->Remove(it);
|
||||
error = fNodes->Insert(node);
|
||||
|
||||
// udpate live queries
|
||||
time_t newModified = node->GetMTime();
|
||||
fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
|
||||
(const uint8*)&oldModified, sizeof(oldModified),
|
||||
(const uint8*)&newModified, sizeof(newModified));
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// NodeAdded
|
||||
void
|
||||
LastModifiedIndex::NodeAdded(Node *node)
|
||||
{
|
||||
if (node)
|
||||
fNodes->Insert(node);
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
void
|
||||
LastModifiedIndex::NodeRemoved(Node *node)
|
||||
{
|
||||
if (node)
|
||||
fNodes->Remove(node, node);
|
||||
}
|
||||
|
||||
// InternalGetIterator
|
||||
AbstractIndexEntryIterator *
|
||||
LastModifiedIndex::InternalGetIterator()
|
||||
{
|
||||
Iterator *iterator = new(nothrow) Iterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, 0, true)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// InternalFind
|
||||
AbstractIndexEntryIterator *
|
||||
LastModifiedIndex::InternalFind(const uint8 *key, size_t length)
|
||||
{
|
||||
if (!key || length != sizeof(time_t))
|
||||
return NULL;
|
||||
Iterator *iterator = new(nothrow) Iterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, *(const time_t*)key)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// _AddIterator
|
||||
void
|
||||
LastModifiedIndex::_AddIterator(Iterator *iterator)
|
||||
{
|
||||
fIterators->Insert(iterator);
|
||||
}
|
||||
|
||||
// _RemoveIterator
|
||||
void
|
||||
LastModifiedIndex::_RemoveIterator(Iterator *iterator)
|
||||
{
|
||||
fIterators->Remove(iterator);
|
||||
}
|
||||
|
||||
|
||||
// Iterator
|
||||
|
||||
// constructor
|
||||
LastModifiedIndex::Iterator::Iterator()
|
||||
: BaseClass(),
|
||||
fIndex(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
LastModifiedIndex::Iterator::~Iterator()
|
||||
{
|
||||
SetTo(NULL, 0);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
LastModifiedIndex::Iterator::GetCurrent()
|
||||
{
|
||||
return BaseClass::GetCurrent();
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
LastModifiedIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||
{
|
||||
Entry *entry = GetCurrent();
|
||||
if (entry) {
|
||||
*(time_t*)buffer = entry->GetNode()->GetMTime();
|
||||
*keyLength = sizeof(time_t);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
LastModifiedIndex::Iterator::Suspend()
|
||||
{
|
||||
status_t error = BaseClass::Suspend();
|
||||
if (error == B_OK) {
|
||||
if (fNode) {
|
||||
error = fIndex->GetVolume()->AddNodeListener(this, fNode,
|
||||
NODE_LISTEN_REMOVED);
|
||||
if (error == B_OK && fEntry) {
|
||||
error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
|
||||
ENTRY_LISTEN_REMOVED);
|
||||
if (error != B_OK)
|
||||
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
}
|
||||
if (error != B_OK)
|
||||
BaseClass::Resume();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
LastModifiedIndex::Iterator::Resume()
|
||||
{
|
||||
status_t error = BaseClass::Resume();
|
||||
if (error == B_OK) {
|
||||
if (fEntry)
|
||||
error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
|
||||
if (fNode) {
|
||||
if (error == B_OK)
|
||||
error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
else
|
||||
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetTo
|
||||
bool
|
||||
LastModifiedIndex::Iterator::SetTo(LastModifiedIndex *index, time_t modified,
|
||||
bool ignoreValue)
|
||||
{
|
||||
Resume();
|
||||
Unset();
|
||||
// set the new values
|
||||
fIndex = index;
|
||||
if (fIndex)
|
||||
fIndex->_AddIterator(this);
|
||||
fInitialized = fIndex;
|
||||
// get the node's first entry
|
||||
if (fIndex) {
|
||||
// get the first node
|
||||
bool found = true;
|
||||
if (ignoreValue)
|
||||
fIndex->fNodes->GetIterator(&fIterator);
|
||||
else
|
||||
found = fIndex->fNodes->FindFirst(modified, &fIterator);
|
||||
// get the first entry
|
||||
if (found) {
|
||||
if (Node **nodeP = fIterator.GetCurrent()) {
|
||||
fNode = *nodeP;
|
||||
fEntry = fNode->GetFirstReferrer();
|
||||
if (!fEntry)
|
||||
BaseClass::GetNext();
|
||||
if (!ignoreValue && fNode && fNode->GetMTime() != modified)
|
||||
Unset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fEntry;
|
||||
}
|
||||
|
||||
// Unset
|
||||
void
|
||||
LastModifiedIndex::Iterator::Unset()
|
||||
{
|
||||
if (fIndex) {
|
||||
fIndex->_RemoveIterator(this);
|
||||
fIndex = NULL;
|
||||
}
|
||||
BaseClass::Unset();
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
void
|
||||
LastModifiedIndex::Iterator::EntryRemoved(Entry */*entry*/)
|
||||
{
|
||||
Resume();
|
||||
fIsNext = BaseClass::GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
void
|
||||
LastModifiedIndex::Iterator::NodeRemoved(Node */*node*/)
|
||||
{
|
||||
Resume();
|
||||
fEntry = NULL;
|
||||
fIsNext = BaseClass::GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// LastModifiedIndex.h
|
||||
|
||||
#ifndef LAST_MODIFIED_INDEX_H
|
||||
#define LAST_MODIFIED_INDEX_H
|
||||
|
||||
#include "Index.h"
|
||||
#include "NodeListener.h"
|
||||
#include "TwoKeyAVLTree.h"
|
||||
|
||||
// LastModifiedIndex
|
||||
class LastModifiedIndex : public Index, private NodeListener {
|
||||
public:
|
||||
LastModifiedIndex(Volume *volume);
|
||||
virtual ~LastModifiedIndex();
|
||||
|
||||
virtual int32 CountEntries() const;
|
||||
|
||||
virtual status_t Changed(Node *node, time_t oldModified);
|
||||
|
||||
private:
|
||||
virtual void NodeAdded(Node *node);
|
||||
virtual void NodeRemoved(Node *node);
|
||||
|
||||
protected:
|
||||
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||
size_t length);
|
||||
|
||||
private:
|
||||
class Iterator;
|
||||
class IteratorList;
|
||||
class NodeTree;
|
||||
friend class Iterator;
|
||||
|
||||
private:
|
||||
void _AddIterator(Iterator *iterator);
|
||||
void _RemoveIterator(Iterator *iterator);
|
||||
|
||||
private:
|
||||
NodeTree *fNodes;
|
||||
IteratorList *fIterators;
|
||||
};
|
||||
|
||||
#endif // LAST_MODIFIED_INDEX_H
|
||||
@@ -0,0 +1,385 @@
|
||||
// List.h
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name of a copyright holder shall
|
||||
// not be used in advertising or otherwise to promote the sale, use or other
|
||||
// dealings in this Software without prior written authorization of the
|
||||
// copyright holder.
|
||||
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#include <new.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
template<typename ITEM>
|
||||
class DefaultDefaultItemCreator {
|
||||
public:
|
||||
static inline ITEM GetItem() { return ITEM(0); }
|
||||
};
|
||||
|
||||
/*!
|
||||
\class List
|
||||
\brief A generic list implementation.
|
||||
*/
|
||||
template<typename ITEM,
|
||||
typename DEFAULT_ITEM_SUPPLIER = DefaultDefaultItemCreator<ITEM> >
|
||||
class List {
|
||||
public:
|
||||
typedef ITEM item_t;
|
||||
typedef List list_t;
|
||||
|
||||
private:
|
||||
static item_t sDefaultItem;
|
||||
static const size_t kDefaultChunkSize = 10;
|
||||
static const size_t kMaximalChunkSize = 1024 * 1024;
|
||||
|
||||
public:
|
||||
List(size_t chunkSize = kDefaultChunkSize);
|
||||
~List();
|
||||
|
||||
inline const item_t &GetDefaultItem() const;
|
||||
inline item_t &GetDefaultItem();
|
||||
|
||||
bool AddItem(const item_t &item, int32 index);
|
||||
bool AddItem(const item_t &item);
|
||||
// bool AddList(list_t *list, int32 index);
|
||||
// bool AddList(list_t *list);
|
||||
|
||||
bool RemoveItem(const item_t &item);
|
||||
bool RemoveItem(int32 index);
|
||||
|
||||
bool ReplaceItem(int32 index, const item_t &item);
|
||||
|
||||
bool MoveItem(int32 oldIndex, int32 newIndex);
|
||||
|
||||
void MakeEmpty();
|
||||
|
||||
int32 CountItems() const;
|
||||
bool IsEmpty() const;
|
||||
const item_t &ItemAt(int32 index) const;
|
||||
item_t &ItemAt(int32 index);
|
||||
const item_t *Items() const;
|
||||
int32 IndexOf(const item_t &item) const;
|
||||
bool HasItem(const item_t &item) const;
|
||||
|
||||
// debugging
|
||||
int32 GetCapacity() const { return fCapacity; }
|
||||
|
||||
private:
|
||||
inline static void _MoveItems(item_t* items, int32 offset, int32 count);
|
||||
bool _Resize(size_t count);
|
||||
|
||||
private:
|
||||
size_t fCapacity;
|
||||
size_t fChunkSize;
|
||||
int32 fItemCount;
|
||||
item_t *fItems;
|
||||
};
|
||||
|
||||
// sDefaultItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::sDefaultItem(
|
||||
DEFAULT_ITEM_SUPPLIER::GetItem());
|
||||
|
||||
// constructor
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::List(size_t chunkSize)
|
||||
: fCapacity(0),
|
||||
fChunkSize(chunkSize),
|
||||
fItemCount(0),
|
||||
fItems(NULL)
|
||||
{
|
||||
if (fChunkSize == 0 || fChunkSize > kMaximalChunkSize)
|
||||
fChunkSize = kDefaultChunkSize;
|
||||
_Resize(0);
|
||||
}
|
||||
|
||||
// destructor
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::~List()
|
||||
{
|
||||
MakeEmpty();
|
||||
free(fItems);
|
||||
}
|
||||
|
||||
// GetDefaultItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
inline
|
||||
const List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::GetDefaultItem() const
|
||||
{
|
||||
return sDefaultItem;
|
||||
}
|
||||
|
||||
// GetDefaultItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
inline
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::GetDefaultItem()
|
||||
{
|
||||
return sDefaultItem;
|
||||
}
|
||||
|
||||
// _MoveItems
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
inline
|
||||
void
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::_MoveItems(item_t* items, int32 offset, int32 count)
|
||||
{
|
||||
if (count > 0 && offset != 0)
|
||||
memmove(items + offset, items, count * sizeof(item_t));
|
||||
}
|
||||
|
||||
// AddItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddItem(const item_t &item, int32 index)
|
||||
{
|
||||
bool result = (index >= 0 && index <= fItemCount
|
||||
&& _Resize(fItemCount + 1));
|
||||
if (result) {
|
||||
_MoveItems(fItems + index, 1, fItemCount - index - 1);
|
||||
new(fItems + index) item_t(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddItem(const item_t &item)
|
||||
{
|
||||
bool result = true;
|
||||
if ((int32)fCapacity > fItemCount) {
|
||||
new(fItems + fItemCount) item_t(item);
|
||||
fItemCount++;
|
||||
} else {
|
||||
if ((result = _Resize(fItemCount + 1)))
|
||||
new(fItems + (fItemCount - 1)) item_t(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// These don't use the copy constructor!
|
||||
/*
|
||||
// AddList
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddList(list_t *list, int32 index)
|
||||
{
|
||||
bool result = (list && index >= 0 && index <= fItemCount);
|
||||
if (result && list->fItemCount > 0) {
|
||||
int32 count = list->fItemCount;
|
||||
result = _Resize(fItemCount + count);
|
||||
if (result) {
|
||||
_MoveItems(fItems + index, count, fItemCount - index - count);
|
||||
memcpy(fItems + index, list->fItems,
|
||||
list->fItemCount * sizeof(item_t));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddList
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddList(list_t *list)
|
||||
{
|
||||
bool result = (list);
|
||||
if (result && list->fItemCount > 0) {
|
||||
int32 index = fItemCount;
|
||||
int32 count = list->fItemCount;
|
||||
result = _Resize(fItemCount + count);
|
||||
if (result) {
|
||||
memcpy(fItems + index, list->fItems,
|
||||
list->fItemCount * sizeof(item_t));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
// RemoveItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::RemoveItem(const item_t &item)
|
||||
{
|
||||
int32 index = IndexOf(item);
|
||||
bool result = (index >= 0);
|
||||
if (result)
|
||||
RemoveItem(index);
|
||||
return result;
|
||||
}
|
||||
|
||||
// RemoveItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::RemoveItem(int32 index)
|
||||
{
|
||||
if (index >= 0 && index < fItemCount) {
|
||||
fItems[index].~item_t();
|
||||
_MoveItems(fItems + index + 1, -1, fItemCount - index - 1);
|
||||
_Resize(fItemCount - 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReplaceItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::ReplaceItem(int32 index, const item_t &item)
|
||||
{
|
||||
if (index >= 0 && index < fItemCount) {
|
||||
fItems[index] = item;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// MoveItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::MoveItem(int32 oldIndex, int32 newIndex)
|
||||
{
|
||||
if (oldIndex >= 0 && oldIndex < fItemCount
|
||||
&& newIndex >= 0 && newIndex <= fItemCount) {
|
||||
if (oldIndex < newIndex - 1) {
|
||||
item_t item = fItems[oldIndex];
|
||||
_MoveItems(fItems + oldIndex + 1, -1, newIndex - oldIndex - 1);
|
||||
fItems[newIndex] = item;
|
||||
} else if (oldIndex > newIndex) {
|
||||
item_t item = fItems[oldIndex];
|
||||
_MoveItems(fItems + newIndex, 1, oldIndex - newIndex);
|
||||
fItems[newIndex] = item;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// MakeEmpty
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
void
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::MakeEmpty()
|
||||
{
|
||||
for (int32 i = 0; i < fItemCount; i++)
|
||||
fItems[i].~item_t();
|
||||
_Resize(0);
|
||||
}
|
||||
|
||||
// CountItems
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
int32
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::CountItems() const
|
||||
{
|
||||
return fItemCount;
|
||||
}
|
||||
|
||||
// IsEmpty
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::IsEmpty() const
|
||||
{
|
||||
return (fItemCount == 0);
|
||||
}
|
||||
|
||||
// ItemAt
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
const List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::ItemAt(int32 index) const
|
||||
{
|
||||
if (index >= 0 && index < fItemCount)
|
||||
return fItems[index];
|
||||
return sDefaultItem;
|
||||
}
|
||||
|
||||
// ItemAt
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::ItemAt(int32 index)
|
||||
{
|
||||
if (index >= 0 && index < fItemCount)
|
||||
return fItems[index];
|
||||
return sDefaultItem;
|
||||
}
|
||||
|
||||
// Items
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
const List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t *
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::Items() const
|
||||
{
|
||||
return fItems;
|
||||
}
|
||||
|
||||
// IndexOf
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
int32
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::IndexOf(const item_t &item) const
|
||||
{
|
||||
for (int32 i = 0; i < fItemCount; i++) {
|
||||
if (fItems[i] == item)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// HasItem
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::HasItem(const item_t &item) const
|
||||
{
|
||||
return (IndexOf(item) >= 0);
|
||||
}
|
||||
|
||||
// _Resize
|
||||
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||
bool
|
||||
List<ITEM, DEFAULT_ITEM_SUPPLIER>::_Resize(size_t count)
|
||||
{
|
||||
bool result = true;
|
||||
// calculate the new capacity
|
||||
int32 newSize = count;
|
||||
if (newSize <= 0)
|
||||
newSize = 1;
|
||||
newSize = ((newSize - 1) / fChunkSize + 1) * fChunkSize;
|
||||
// resize if necessary
|
||||
if ((size_t)newSize != fCapacity) {
|
||||
item_t* newItems
|
||||
= (item_t*)realloc(fItems, newSize * sizeof(item_t));
|
||||
if (newItems) {
|
||||
fItems = newItems;
|
||||
fCapacity = newSize;
|
||||
} else
|
||||
result = false;
|
||||
}
|
||||
if (result)
|
||||
fItemCount = count;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // LIST_H
|
||||
@@ -0,0 +1,14 @@
|
||||
// Locking.h
|
||||
|
||||
#ifndef LOCKING_H
|
||||
#define LOCKING_H
|
||||
|
||||
#include "AutoLocker.h"
|
||||
|
||||
class Volume;
|
||||
|
||||
// instantiations
|
||||
typedef AutoLocker<Volume, AutoLockerReadLocking<Volume> > VolumeReadLocker;
|
||||
typedef AutoLocker<Volume, AutoLockerWriteLocking<Volume> > VolumeWriteLocker;
|
||||
|
||||
#endif LOCKING_H
|
||||
@@ -0,0 +1,107 @@
|
||||
// Misc.h
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name of a copyright holder shall
|
||||
// not be used in advertising or otherwise to promote the sale, use or other
|
||||
// dealings in this Software without prior written authorization of the
|
||||
// copyright holder.
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "String.h"
|
||||
|
||||
// min and max
|
||||
// We don't want to include <algobase.h> otherwise we also get <iostream.h>
|
||||
// and other undesired things.
|
||||
template<typename C>
|
||||
static inline C min(const C &a, const C &b) { return (a < b ? a : b); }
|
||||
template<typename C>
|
||||
static inline C max(const C &a, const C &b) { return (a > b ? a : b); }
|
||||
|
||||
// find last (most significant) set bit
|
||||
static inline
|
||||
int
|
||||
fls(uint32 value)
|
||||
{
|
||||
if (!value)
|
||||
return -1;
|
||||
int index = 0;
|
||||
#define HAND_OPTIMIZED_FLS 1
|
||||
#if !HAND_OPTIMIZED_FLS
|
||||
// This is the algorithm in its pure form.
|
||||
const uint32 masks[] = {
|
||||
0xffff0000,
|
||||
0xff00ff00,
|
||||
0xf0f0f0f0,
|
||||
0xcccccccc,
|
||||
0xaaaaaaaa,
|
||||
};
|
||||
int range = 16;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (value & masks[i]) {
|
||||
index += range;
|
||||
value &= masks[i];
|
||||
}
|
||||
range /= 2;
|
||||
}
|
||||
#else // HAND_OPTIMIZED_FLS
|
||||
// This is how the compiler should optimize it for us: Unroll the loop and
|
||||
// inline the masks.
|
||||
// 0: 0xffff0000
|
||||
if (value & 0xffff0000) {
|
||||
index += 16;
|
||||
value &= 0xffff0000;
|
||||
}
|
||||
// 1: 0xff00ff00
|
||||
if (value & 0xff00ff00) {
|
||||
index += 8;
|
||||
value &= 0xff00ff00;
|
||||
}
|
||||
// 2: 0xf0f0f0f0
|
||||
if (value & 0xf0f0f0f0) {
|
||||
index += 4;
|
||||
value &= 0xf0f0f0f0;
|
||||
}
|
||||
// 3: 0xcccccccc
|
||||
if (value & 0xcccccccc) {
|
||||
index += 2;
|
||||
value &= 0xcccccccc;
|
||||
}
|
||||
// 4: 0xaaaaaaaa
|
||||
if (value & 0xaaaaaaaa)
|
||||
index++;
|
||||
#endif // HAND_OPTIMIZED_FLS
|
||||
return index;
|
||||
}
|
||||
|
||||
// node_child_hash
|
||||
static inline
|
||||
uint32
|
||||
node_child_hash(uint64 id, const char *name)
|
||||
{
|
||||
return uint32(id & 0xffffffff) ^ string_hash(name);
|
||||
}
|
||||
|
||||
#endif // MISC_H
|
||||
@@ -0,0 +1,348 @@
|
||||
// NameIndex.cpp
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "Entry.h"
|
||||
#include "IndexImpl.h"
|
||||
#include "NameIndex.h"
|
||||
#include "ramfs.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// NameIndexPrimaryKey
|
||||
class NameIndexPrimaryKey {
|
||||
public:
|
||||
NameIndexPrimaryKey(const Entry *entry,
|
||||
const char *name = NULL)
|
||||
: entry(entry), name(name ? name : entry->GetName()) {}
|
||||
NameIndexPrimaryKey(const char *name)
|
||||
: entry(NULL), name(name) {}
|
||||
|
||||
const Entry *entry;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
// NameIndexGetPrimaryKey
|
||||
class NameIndexGetPrimaryKey {
|
||||
public:
|
||||
inline NameIndexPrimaryKey operator()(const Entry *a)
|
||||
{
|
||||
return NameIndexPrimaryKey(a);
|
||||
}
|
||||
|
||||
inline NameIndexPrimaryKey operator()(const Entry *a) const
|
||||
{
|
||||
return NameIndexPrimaryKey(a);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// NameIndexPrimaryKeyCompare
|
||||
class NameIndexPrimaryKeyCompare
|
||||
{
|
||||
public:
|
||||
inline int operator()(const NameIndexPrimaryKey &a,
|
||||
const NameIndexPrimaryKey &b) const
|
||||
{
|
||||
if (a.entry != NULL && a.entry == b.entry)
|
||||
return 0;
|
||||
return strcmp(a.name, b.name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// EntryTree
|
||||
|
||||
typedef TwoKeyAVLTree<Entry*, NameIndexPrimaryKey, NameIndexPrimaryKeyCompare,
|
||||
NameIndexGetPrimaryKey>
|
||||
_EntryTree;
|
||||
|
||||
class NameIndex::EntryTree : public _EntryTree {};
|
||||
|
||||
|
||||
// NameIndexEntryIterator
|
||||
class NameIndexEntryIterator : public AbstractIndexEntryIterator,
|
||||
public EntryListener {
|
||||
public:
|
||||
NameIndexEntryIterator();
|
||||
virtual ~NameIndexEntryIterator();
|
||||
|
||||
virtual Entry *GetCurrent();
|
||||
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||
virtual Entry *GetPrevious();
|
||||
virtual Entry *GetNext();
|
||||
|
||||
virtual status_t Suspend();
|
||||
virtual status_t Resume();
|
||||
|
||||
bool SetTo(NameIndex *index, const char *name, bool ignoreValue = false);
|
||||
|
||||
virtual void EntryRemoved(Entry *entry);
|
||||
|
||||
private:
|
||||
friend class NameIndex;
|
||||
|
||||
typedef AbstractIndexEntryIterator BaseClass;
|
||||
|
||||
private:
|
||||
NameIndex *fIndex;
|
||||
NameIndex::EntryTree::Iterator fIterator;
|
||||
bool fSuspended;
|
||||
bool fIsNext;
|
||||
};
|
||||
|
||||
|
||||
// NameIndex
|
||||
|
||||
// constructor
|
||||
NameIndex::NameIndex(Volume *volume)
|
||||
: Index(volume, "name", B_STRING_TYPE, false),
|
||||
fEntries(new(nothrow) EntryTree)
|
||||
{
|
||||
if (fInitStatus == B_OK && !fEntries)
|
||||
fInitStatus = B_NO_MEMORY;
|
||||
if (fInitStatus == B_OK) {
|
||||
fInitStatus = fVolume->AddEntryListener(this,
|
||||
NULL, ENTRY_LISTEN_ANY_ENTRY | ENTRY_LISTEN_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
NameIndex::~NameIndex()
|
||||
{
|
||||
if (fVolume)
|
||||
fVolume->RemoveEntryListener(this, NULL);
|
||||
if (fEntries)
|
||||
delete fEntries;
|
||||
// Actually we would need to maintain a list of iterators and unset the
|
||||
// still existing iterators here. But since the name index is deleted
|
||||
// when the volume is unmounted, there shouldn't be any iterators left
|
||||
// anymore.
|
||||
}
|
||||
|
||||
// CountEntries
|
||||
int32
|
||||
NameIndex::CountEntries() const
|
||||
{
|
||||
return fEntries->CountItems();
|
||||
}
|
||||
|
||||
// Changed
|
||||
status_t
|
||||
NameIndex::Changed(Entry *entry, const char *oldName)
|
||||
{
|
||||
status_t error = B_BAD_VALUE;
|
||||
if (entry && oldName) {
|
||||
EntryTree::Iterator it;
|
||||
Entry **foundEntry
|
||||
= fEntries->Find(NameIndexPrimaryKey(entry, oldName), entry, &it);
|
||||
if (foundEntry && *foundEntry == entry) {
|
||||
fEntries->Remove(it);
|
||||
error = fEntries->Insert(entry);
|
||||
|
||||
// udpate live queries
|
||||
_UpdateLiveQueries(entry, oldName, entry->GetName());
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// EntryAdded
|
||||
void
|
||||
NameIndex::EntryAdded(Entry *entry)
|
||||
{
|
||||
if (entry) {
|
||||
fEntries->Insert(entry);
|
||||
|
||||
// udpate live queries
|
||||
_UpdateLiveQueries(entry, NULL, entry->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
void
|
||||
NameIndex::EntryRemoved(Entry *entry)
|
||||
{
|
||||
if (entry) {
|
||||
fEntries->Remove(entry, entry);
|
||||
|
||||
// udpate live queries
|
||||
_UpdateLiveQueries(entry, entry->GetName(), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// InternalGetIterator
|
||||
AbstractIndexEntryIterator *
|
||||
NameIndex::InternalGetIterator()
|
||||
{
|
||||
NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, NULL, true)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// InternalFind
|
||||
AbstractIndexEntryIterator *
|
||||
NameIndex::InternalFind(const uint8 *key, size_t length)
|
||||
{
|
||||
if (!key || length == 0)
|
||||
return NULL;
|
||||
|
||||
// if the key is not null-terminated, copy it
|
||||
uint8 clonedKey[kMaxIndexKeyLength];
|
||||
if (key[length - 1] != '\0') {
|
||||
if (length >= kMaxIndexKeyLength)
|
||||
length = kMaxIndexKeyLength - 1;
|
||||
|
||||
memcpy(clonedKey, key, length);
|
||||
clonedKey[length] = '\0';
|
||||
length++;
|
||||
key = clonedKey;
|
||||
}
|
||||
|
||||
NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, (const char *)key)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// _UpdateLiveQueries
|
||||
void
|
||||
NameIndex::_UpdateLiveQueries(Entry* entry, const char* oldName,
|
||||
const char* newName)
|
||||
{
|
||||
fVolume->UpdateLiveQueries(entry, entry->GetNode(), GetName(),
|
||||
GetType(), (const uint8*)oldName, (oldName ? strlen(oldName) : 0),
|
||||
(const uint8*)newName, (newName ? strlen(newName) : 0));
|
||||
}
|
||||
|
||||
|
||||
// NameIndexEntryIterator
|
||||
|
||||
// constructor
|
||||
NameIndexEntryIterator::NameIndexEntryIterator()
|
||||
: AbstractIndexEntryIterator(),
|
||||
fIndex(NULL),
|
||||
fIterator(),
|
||||
fSuspended(false),
|
||||
fIsNext(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
NameIndexEntryIterator::~NameIndexEntryIterator()
|
||||
{
|
||||
SetTo(NULL, NULL);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
NameIndexEntryIterator::GetCurrent()
|
||||
{
|
||||
return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
NameIndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||
{
|
||||
Entry *entry = GetCurrent();
|
||||
if (entry) {
|
||||
strncpy((char*)buffer, entry->GetName(), kMaxIndexKeyLength);
|
||||
*keyLength = strlen(entry->GetName());
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
// GetPrevious
|
||||
Entry *
|
||||
NameIndexEntryIterator::GetPrevious()
|
||||
{
|
||||
if (fSuspended)
|
||||
return NULL;
|
||||
if (!(fIterator.GetCurrent() && fIsNext))
|
||||
fIterator.GetPrevious();
|
||||
fIsNext = false;
|
||||
return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
|
||||
}
|
||||
|
||||
// GetNext
|
||||
Entry *
|
||||
NameIndexEntryIterator::GetNext()
|
||||
{
|
||||
if (fSuspended)
|
||||
return NULL;
|
||||
if (!(fIterator.GetCurrent() && fIsNext))
|
||||
fIterator.GetNext();
|
||||
fIsNext = false;
|
||||
return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
NameIndexEntryIterator::Suspend()
|
||||
{
|
||||
status_t error = (!fSuspended ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (fIterator.GetCurrent()) {
|
||||
error = fIndex->GetVolume()->AddEntryListener(this,
|
||||
*fIterator.GetCurrent(), ENTRY_LISTEN_REMOVED);
|
||||
}
|
||||
if (error == B_OK)
|
||||
fSuspended = true;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
NameIndexEntryIterator::Resume()
|
||||
{
|
||||
status_t error = (fSuspended ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (fIterator.GetCurrent()) {
|
||||
error = fIndex->GetVolume()->RemoveEntryListener(this,
|
||||
*fIterator.GetCurrent());
|
||||
}
|
||||
if (error == B_OK)
|
||||
fSuspended = false;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetTo
|
||||
bool
|
||||
NameIndexEntryIterator::SetTo(NameIndex *index, const char *name,
|
||||
bool ignoreValue)
|
||||
{
|
||||
Resume();
|
||||
fIndex = index;
|
||||
fSuspended = false;
|
||||
fIsNext = false;
|
||||
if (fIndex) {
|
||||
if (ignoreValue) {
|
||||
fIndex->fEntries->GetIterator(&fIterator);
|
||||
return fIterator.GetCurrent();
|
||||
}
|
||||
return fIndex->fEntries->FindFirst(name, &fIterator);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
void
|
||||
NameIndexEntryIterator::EntryRemoved(Entry */*entry*/)
|
||||
{
|
||||
Resume();
|
||||
fIsNext = GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// NameIndex.h
|
||||
|
||||
#ifndef NAME_INDEX_H
|
||||
#define NAME_INDEX_H
|
||||
|
||||
#include "EntryListener.h"
|
||||
#include "Index.h"
|
||||
#include "TwoKeyAVLTree.h"
|
||||
|
||||
class NameIndexEntryIterator;
|
||||
|
||||
// NameIndex
|
||||
class NameIndex : public Index, private EntryListener {
|
||||
public:
|
||||
NameIndex(Volume *volume);
|
||||
virtual ~NameIndex();
|
||||
|
||||
virtual int32 CountEntries() const;
|
||||
|
||||
virtual status_t Changed(Entry *entry, const char *oldName);
|
||||
|
||||
private:
|
||||
virtual void EntryAdded(Entry *entry);
|
||||
virtual void EntryRemoved(Entry *entry);
|
||||
|
||||
protected:
|
||||
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||
size_t length);
|
||||
|
||||
private:
|
||||
class EntryTree;
|
||||
friend class NameIndexEntryIterator;
|
||||
|
||||
void _UpdateLiveQueries(Entry* entry, const char* oldName,
|
||||
const char* newName);
|
||||
|
||||
private:
|
||||
EntryTree *fEntries;
|
||||
};
|
||||
|
||||
#endif // NAME_INDEX_H
|
||||
@@ -0,0 +1,361 @@
|
||||
// Node.cpp
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "EntryIterator.h"
|
||||
#include "LastModifiedIndex.h"
|
||||
#include "Node.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// is_user_in_group
|
||||
inline static
|
||||
bool
|
||||
is_user_in_group(gid_t gid)
|
||||
{
|
||||
// Either I miss something, or we don't have getgroups() in the kernel. :-(
|
||||
/*
|
||||
gid_t groups[NGROUPS_MAX];
|
||||
int groupCount = getgroups(NGROUPS_MAX, groups);
|
||||
for (int i = 0; i < groupCount; i++) {
|
||||
if (gid == groups[i])
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
return (gid == getegid());
|
||||
}
|
||||
|
||||
|
||||
// constructor
|
||||
Node::Node(Volume *volume, uint8 type)
|
||||
: fVolume(volume),
|
||||
fID(fVolume->NextNodeID()),
|
||||
fRefCount(0),
|
||||
fMode(0),
|
||||
fUID(0),
|
||||
fGID(0),
|
||||
fATime(0),
|
||||
fMTime(0),
|
||||
fCTime(0),
|
||||
fCrTime(0),
|
||||
fModified(false),
|
||||
fIsKnownToVFS(false),
|
||||
// attribute management
|
||||
fAttributes(),
|
||||
// referrers
|
||||
fReferrers()
|
||||
{
|
||||
// set file type
|
||||
switch (type) {
|
||||
case NODE_TYPE_DIRECTORY:
|
||||
fMode = S_IFDIR;
|
||||
break;
|
||||
case NODE_TYPE_FILE:
|
||||
fMode = S_IFREG;
|
||||
break;
|
||||
case NODE_TYPE_SYMLINK:
|
||||
fMode = S_IFLNK;
|
||||
break;
|
||||
}
|
||||
// set defaults for time
|
||||
fATime = fMTime = fCTime = fCrTime = time(NULL);
|
||||
}
|
||||
|
||||
// destructor
|
||||
Node::~Node()
|
||||
{
|
||||
// delete all attributes
|
||||
while (Attribute *attribute = fAttributes.GetFirst()) {
|
||||
status_t error = DeleteAttribute(attribute);
|
||||
if (error != B_OK) {
|
||||
FATAL(("Node::~Node(): Failed to delete attribute!\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
Node::InitCheck() const
|
||||
{
|
||||
return (fVolume && fID >= 0 ? B_OK : B_NO_INIT);
|
||||
}
|
||||
|
||||
// AddReference
|
||||
status_t
|
||||
Node::AddReference()
|
||||
{
|
||||
if (++fRefCount == 1) {
|
||||
status_t error = GetVolume()->NewVNode(this);
|
||||
if (error != B_OK) {
|
||||
fRefCount--;
|
||||
return error;
|
||||
}
|
||||
|
||||
fIsKnownToVFS = true;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// RemoveReference
|
||||
void
|
||||
Node::RemoveReference()
|
||||
{
|
||||
if (--fRefCount == 0) {
|
||||
GetVolume()->RemoveVNode(this);
|
||||
fRefCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Link
|
||||
status_t
|
||||
Node::Link(Entry *entry)
|
||||
{
|
||||
PRINT(("Node[%Ld]::Link(): %ld ->...\n", fID, fRefCount));
|
||||
fReferrers.Insert(entry);
|
||||
|
||||
status_t error = AddReference();
|
||||
if (error != B_OK)
|
||||
fReferrers.Remove(entry);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// Unlink
|
||||
status_t
|
||||
Node::Unlink(Entry *entry)
|
||||
{
|
||||
PRINT(("Node[%Ld]::Unlink(): %ld ->...\n", fID, fRefCount));
|
||||
RemoveReference();
|
||||
fReferrers.Remove(entry);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// SetMTime
|
||||
void
|
||||
Node::SetMTime(time_t mTime)
|
||||
{
|
||||
time_t oldMTime = fMTime;
|
||||
fATime = fMTime = mTime;
|
||||
if (oldMTime != fMTime) {
|
||||
if (LastModifiedIndex *index = fVolume->GetLastModifiedIndex())
|
||||
index->Changed(this, oldMTime);
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPermissions
|
||||
status_t
|
||||
Node::CheckPermissions(int mode) const
|
||||
{
|
||||
int userPermissions = (fMode & S_IRWXU) >> 6;
|
||||
int groupPermissions = (fMode & S_IRWXG) >> 3;
|
||||
int otherPermissions = fMode & S_IRWXO;
|
||||
// get the permissions for this uid/gid
|
||||
int permissions = 0;
|
||||
uid_t uid = geteuid();
|
||||
// user is root
|
||||
if (uid == 0) {
|
||||
// root has always read/write permission, but at least one of the
|
||||
// X bits must be set for execute permission
|
||||
permissions = userPermissions | groupPermissions | otherPermissions
|
||||
| ACCESS_R | ACCESS_W;
|
||||
// user is node owner
|
||||
} else if (uid == fUID)
|
||||
permissions = userPermissions;
|
||||
// user is in owning group
|
||||
else if (is_user_in_group(fGID))
|
||||
permissions = groupPermissions;
|
||||
// user is one of the others
|
||||
else
|
||||
permissions = otherPermissions;
|
||||
// do the check
|
||||
return ((mode & ~permissions) ? B_NOT_ALLOWED : B_OK);
|
||||
}
|
||||
|
||||
// CreateAttribute
|
||||
status_t
|
||||
Node::CreateAttribute(const char *name, Attribute **_attribute)
|
||||
{
|
||||
status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// create attribute
|
||||
Attribute *attribute = new(nothrow) Attribute(fVolume, NULL, name);
|
||||
if (attribute) {
|
||||
error = attribute->InitCheck();
|
||||
if (error == B_OK) {
|
||||
// add attribute to node
|
||||
error = AddAttribute(attribute);
|
||||
if (error == B_OK)
|
||||
*_attribute = attribute;
|
||||
}
|
||||
if (error != B_OK)
|
||||
delete attribute;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// DeleteAttribute
|
||||
status_t
|
||||
Node::DeleteAttribute(Attribute *attribute)
|
||||
{
|
||||
status_t error = RemoveAttribute(attribute);
|
||||
if (error == B_OK)
|
||||
delete attribute;
|
||||
return error;
|
||||
}
|
||||
|
||||
// AddAttribute
|
||||
status_t
|
||||
Node::AddAttribute(Attribute *attribute)
|
||||
{
|
||||
status_t error = (attribute && !attribute->GetNode() ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = GetVolume()->NodeAttributeAdded(GetID(), attribute);
|
||||
if (error == B_OK) {
|
||||
fAttributes.Insert(attribute);
|
||||
attribute->SetNode(this);
|
||||
MarkModified();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveAttribute
|
||||
status_t
|
||||
Node::RemoveAttribute(Attribute *attribute)
|
||||
{
|
||||
status_t error = (attribute && attribute->GetNode() == this
|
||||
? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
// move all iterators pointing to the attribute to the next attribute
|
||||
if (GetVolume()->IteratorLock()) {
|
||||
// set the iterators' current entry
|
||||
Attribute *nextAttr = fAttributes.GetNext(attribute);
|
||||
DLList<AttributeIterator> *iterators
|
||||
= attribute->GetAttributeIteratorList();
|
||||
for (AttributeIterator *iterator = iterators->GetFirst();
|
||||
iterator;
|
||||
iterator = iterators->GetNext(iterator)) {
|
||||
iterator->SetCurrent(nextAttr, true);
|
||||
}
|
||||
// Move the iterators from one list to the other, or just remove
|
||||
// them, if there is no next attribute.
|
||||
if (nextAttr) {
|
||||
DLList<AttributeIterator> *nextIterators
|
||||
= nextAttr->GetAttributeIteratorList();
|
||||
nextIterators->MoveFrom(iterators);
|
||||
} else
|
||||
iterators->RemoveAll();
|
||||
GetVolume()->IteratorUnlock();
|
||||
} else
|
||||
error = B_ERROR;
|
||||
// remove the attribute
|
||||
if (error == B_OK) {
|
||||
error = GetVolume()->NodeAttributeRemoved(GetID(), attribute);
|
||||
if (error == B_OK) {
|
||||
fAttributes.Remove(attribute);
|
||||
attribute->SetNode(NULL);
|
||||
MarkModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindAttribute
|
||||
status_t
|
||||
Node::FindAttribute(const char *name, Attribute **_attribute) const
|
||||
{
|
||||
status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
/*
|
||||
Attribute *attribute = NULL;
|
||||
while (GetNextAttribute(&attribute) == B_OK) {
|
||||
if (!strcmp(attribute->GetName(), name)) {
|
||||
*_attribute = attribute;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
*/
|
||||
error = GetVolume()->FindNodeAttribute(GetID(), name, _attribute);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetPreviousAttribute
|
||||
status_t
|
||||
Node::GetPreviousAttribute(Attribute **attribute) const
|
||||
{
|
||||
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (!*attribute)
|
||||
*attribute = fAttributes.GetLast();
|
||||
else if ((*attribute)->GetNode() == this)
|
||||
*attribute = fAttributes.GetPrevious(*attribute);
|
||||
else
|
||||
error = B_BAD_VALUE;
|
||||
if (error == B_OK && !*attribute)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNextAttribute
|
||||
status_t
|
||||
Node::GetNextAttribute(Attribute **attribute) const
|
||||
{
|
||||
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (!*attribute)
|
||||
*attribute = fAttributes.GetFirst();
|
||||
else if ((*attribute)->GetNode() == this)
|
||||
*attribute = fAttributes.GetNext(*attribute);
|
||||
else
|
||||
error = B_BAD_VALUE;
|
||||
if (error == B_OK && !*attribute)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetFirstReferrer
|
||||
Entry *
|
||||
Node::GetFirstReferrer() const
|
||||
{
|
||||
return fReferrers.GetHead();
|
||||
}
|
||||
|
||||
// GetLastReferrer
|
||||
Entry *
|
||||
Node::GetLastReferrer() const
|
||||
{
|
||||
return fReferrers.GetTail();
|
||||
}
|
||||
|
||||
// GetPreviousReferrer
|
||||
Entry *
|
||||
Node::GetPreviousReferrer(Entry *entry) const
|
||||
{
|
||||
return (entry ? fReferrers.GetPrevious(entry) : NULL );
|
||||
}
|
||||
|
||||
// GetNextReferrer
|
||||
Entry *
|
||||
Node::GetNextReferrer(Entry *entry) const
|
||||
{
|
||||
return (entry ? fReferrers.GetNext(entry) : NULL );
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
Node::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
Attribute *attribute = NULL;
|
||||
while (GetNextAttribute(&attribute) == B_OK)
|
||||
attribute->GetAllocationInfo(info);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// Node.h
|
||||
|
||||
#ifndef NODE_H
|
||||
#define NODE_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "Attribute.h"
|
||||
#include "Entry.h"
|
||||
#include "fsproto.h"
|
||||
#include "String.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class AttributeIterator;
|
||||
class Directory;
|
||||
class Volume;
|
||||
|
||||
// node type
|
||||
enum {
|
||||
NODE_TYPE_DIRECTORY,
|
||||
NODE_TYPE_FILE,
|
||||
NODE_TYPE_SYMLINK,
|
||||
};
|
||||
|
||||
// access modes
|
||||
enum {
|
||||
ACCESS_R = S_IROTH,
|
||||
ACCESS_W = S_IWOTH,
|
||||
ACCESS_X = S_IXOTH,
|
||||
};
|
||||
|
||||
class Node : public DLListLinkImpl<Node> {
|
||||
public:
|
||||
Node(Volume *volume, uint8 type);
|
||||
virtual ~Node();
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
inline void SetVolume(Volume *volume) { fVolume = volume; }
|
||||
inline Volume *GetVolume() const { return fVolume; }
|
||||
|
||||
inline vnode_id GetID() const { return fID; }
|
||||
|
||||
status_t AddReference();
|
||||
void RemoveReference();
|
||||
int32 GetRefCount() { return fRefCount; }
|
||||
|
||||
virtual status_t Link(Entry *entry);
|
||||
virtual status_t Unlink(Entry *entry);
|
||||
|
||||
inline bool IsDirectory() const { return S_ISDIR(fMode); }
|
||||
inline bool IsFile() const { return S_ISREG(fMode); }
|
||||
inline bool IsSymLink() const { return S_ISLNK(fMode); }
|
||||
|
||||
virtual status_t SetSize(off_t newSize) = 0;
|
||||
virtual off_t GetSize() const = 0;
|
||||
|
||||
// stat data
|
||||
|
||||
inline void SetMode(mode_t mode)
|
||||
{ fMode = fMode & ~S_IUMSK | mode & S_IUMSK; }
|
||||
inline mode_t GetMode() const { return fMode; }
|
||||
|
||||
inline void SetUID(uid_t uid) { fUID = uid; }
|
||||
inline uid_t GetUID() const { return fUID; }
|
||||
|
||||
inline void SetGID(uid_t gid) { fGID = gid; }
|
||||
inline uid_t GetGID() const { return fGID; }
|
||||
|
||||
inline void SetATime(time_t aTime) { fATime = aTime; }
|
||||
inline time_t GetATime() const { return fATime; }
|
||||
|
||||
void SetMTime(time_t mTime);
|
||||
inline time_t GetMTime() const { return fMTime; }
|
||||
|
||||
inline void SetCTime(time_t cTime) { fCTime = cTime; }
|
||||
inline time_t GetCTime() const { return fCTime; }
|
||||
|
||||
inline void SetCrTime(time_t crTime) { fCrTime = crTime; }
|
||||
inline time_t GetCrTime() const { return fCrTime; }
|
||||
|
||||
inline void MarkModified() { fModified = true; }
|
||||
inline void MarkUnmodified();
|
||||
inline void SetModified(bool modified) { fModified = modified; }
|
||||
inline bool IsModified() const { return fModified; }
|
||||
|
||||
status_t CheckPermissions(int mode) const;
|
||||
|
||||
bool IsKnownToVFS() const { return fIsKnownToVFS; }
|
||||
|
||||
// attributes
|
||||
status_t CreateAttribute(const char *name, Attribute **attribute);
|
||||
status_t DeleteAttribute(Attribute *attribute);
|
||||
status_t AddAttribute(Attribute *attribute);
|
||||
status_t RemoveAttribute(Attribute *attribute);
|
||||
|
||||
status_t FindAttribute(const char *name, Attribute **attribute) const;
|
||||
|
||||
status_t GetPreviousAttribute(Attribute **attribute) const;
|
||||
status_t GetNextAttribute(Attribute **attribute) const;
|
||||
|
||||
Entry *GetFirstReferrer() const;
|
||||
Entry *GetLastReferrer() const;
|
||||
Entry *GetPreviousReferrer(Entry *entry) const;
|
||||
Entry *GetNextReferrer(Entry *entry) const;
|
||||
|
||||
// debugging
|
||||
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
Volume *fVolume;
|
||||
vnode_id fID;
|
||||
int32 fRefCount;
|
||||
mode_t fMode;
|
||||
uid_t fUID;
|
||||
uid_t fGID;
|
||||
time_t fATime;
|
||||
time_t fMTime;
|
||||
time_t fCTime;
|
||||
time_t fCrTime;
|
||||
bool fModified;
|
||||
bool fIsKnownToVFS;
|
||||
|
||||
// attribute management
|
||||
DLList<Attribute> fAttributes;
|
||||
|
||||
protected:
|
||||
// entries referring to this node
|
||||
DLList<Entry, GetNodeReferrerLink> fReferrers;
|
||||
};
|
||||
|
||||
// MarkUnmodified
|
||||
inline
|
||||
void
|
||||
Node::MarkUnmodified()
|
||||
{
|
||||
if (fModified) {
|
||||
fCTime = time(NULL);
|
||||
SetMTime(fCTime);
|
||||
fModified = false;
|
||||
}
|
||||
}
|
||||
|
||||
// open_mode_to_access
|
||||
inline static
|
||||
int
|
||||
open_mode_to_access(int openMode)
|
||||
{
|
||||
switch (openMode & O_RWMASK) {
|
||||
case O_RDONLY:
|
||||
return ACCESS_R;
|
||||
case O_WRONLY:
|
||||
return ACCESS_W;
|
||||
case O_RDWR:
|
||||
return ACCESS_R | ACCESS_W;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// NodeMTimeUpdater
|
||||
class NodeMTimeUpdater {
|
||||
public:
|
||||
NodeMTimeUpdater(Node *node) : fNode(node) {}
|
||||
~NodeMTimeUpdater()
|
||||
{
|
||||
if (fNode && fNode->IsModified())
|
||||
fNode->MarkUnmodified();
|
||||
}
|
||||
|
||||
private:
|
||||
Node *fNode;
|
||||
};
|
||||
|
||||
#endif // NODE_H
|
||||
@@ -0,0 +1,245 @@
|
||||
// NodeChildTable.h
|
||||
|
||||
#ifndef NODE_CHILD_TABLE_H
|
||||
#define NODE_CHILD_TABLE_H
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "Misc.h"
|
||||
#include "Node.h"
|
||||
#include "OpenHashTable.h"
|
||||
|
||||
// NodeChildHashElement
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
class NodeChildHashElement : public OpenHashElement {
|
||||
private:
|
||||
typedef NodeChildHashElement<ParentNode, NodeChild> Element;
|
||||
public:
|
||||
|
||||
NodeChildHashElement() : OpenHashElement(), fID(-1), fChild(NULL)
|
||||
{
|
||||
fNext = -1;
|
||||
}
|
||||
|
||||
static inline uint32 HashFor(vnode_id id, const char *name)
|
||||
{
|
||||
return node_child_hash(id, name);
|
||||
}
|
||||
|
||||
static inline uint32 HashFor(ParentNode *parent, NodeChild *child)
|
||||
{
|
||||
return node_child_hash(parent->GetID(), child->GetName());
|
||||
}
|
||||
|
||||
inline uint32 Hash() const
|
||||
{
|
||||
return HashFor(fID, fChild->GetName());
|
||||
}
|
||||
|
||||
inline bool Equals(vnode_id id, const char *name)
|
||||
{
|
||||
return (fID == id && !strcmp(fChild->GetName(), name));
|
||||
}
|
||||
|
||||
inline bool operator==(const OpenHashElement &_element) const
|
||||
{
|
||||
const Element &element = static_cast<const Element&>(_element);
|
||||
return Equals(element.fID, element.fChild->GetName());
|
||||
}
|
||||
|
||||
inline void Adopt(Element &element)
|
||||
{
|
||||
fID = element.fID;
|
||||
fChild = element.fChild;
|
||||
}
|
||||
|
||||
vnode_id fID;
|
||||
NodeChild *fChild;
|
||||
};
|
||||
|
||||
// NodeChildTable
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
class NodeChildTable {
|
||||
public:
|
||||
NodeChildTable();
|
||||
~NodeChildTable();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
status_t AddNodeChild(ParentNode *node, NodeChild *child);
|
||||
status_t AddNodeChild(vnode_id, NodeChild *child);
|
||||
status_t RemoveNodeChild(ParentNode *node, NodeChild *child);
|
||||
status_t RemoveNodeChild(vnode_id id, NodeChild *child);
|
||||
status_t RemoveNodeChild(vnode_id id, const char *name);
|
||||
NodeChild *GetNodeChild(vnode_id id, const char *name);
|
||||
|
||||
protected:
|
||||
typedef NodeChildHashElement<ParentNode, NodeChild> Element;
|
||||
|
||||
private:
|
||||
Element *_FindElement(vnode_id id, const char *name) const;
|
||||
|
||||
protected:
|
||||
OpenHashElementArray<Element> fElementArray;
|
||||
OpenHashTable<Element, OpenHashElementArray<Element> > fTable;
|
||||
};
|
||||
|
||||
// define convenient instantiation types
|
||||
|
||||
// DirectoryEntryTable
|
||||
class DirectoryEntryTable : public NodeChildTable<Directory, Entry> {
|
||||
public:
|
||||
DirectoryEntryTable() {}
|
||||
~DirectoryEntryTable() {}
|
||||
|
||||
void GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddDirectoryEntryTableAllocation(fTable.ArraySize(),
|
||||
fTable.VectorSize(),
|
||||
sizeof(Element),
|
||||
fTable.CountElements());
|
||||
}
|
||||
};
|
||||
|
||||
// NodeAttributeTable
|
||||
class NodeAttributeTable : public NodeChildTable<Node, Attribute> {
|
||||
public:
|
||||
NodeAttributeTable() {}
|
||||
~NodeAttributeTable() {}
|
||||
|
||||
void GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddNodeAttributeTableAllocation(fTable.ArraySize(),
|
||||
fTable.VectorSize(),
|
||||
sizeof(Element),
|
||||
fTable.CountElements());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// NodeChildTable implementation
|
||||
|
||||
// constructor
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
NodeChildTable<ParentNode, NodeChild>::NodeChildTable()
|
||||
: fElementArray(1000),
|
||||
fTable(1000, &fElementArray)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
NodeChildTable<ParentNode, NodeChild>::~NodeChildTable()
|
||||
{
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
status_t
|
||||
NodeChildTable<ParentNode, NodeChild>::InitCheck() const
|
||||
{
|
||||
RETURN_ERROR(fTable.InitCheck() && fElementArray.InitCheck()
|
||||
? B_OK : B_NO_MEMORY);
|
||||
}
|
||||
|
||||
// AddNodeChild
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
status_t
|
||||
NodeChildTable<ParentNode, NodeChild>::AddNodeChild(ParentNode *node,
|
||||
NodeChild *child)
|
||||
{
|
||||
status_t error = (node && child ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK)
|
||||
error = AddNodeChild(node->GetID(), child);
|
||||
return error;
|
||||
}
|
||||
|
||||
// AddNodeChild
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
status_t
|
||||
NodeChildTable<ParentNode, NodeChild>::AddNodeChild(vnode_id id,
|
||||
NodeChild *child)
|
||||
{
|
||||
status_t error = (child ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
Element *element = fTable.Add(Element::HashFor(id, child->GetName()));
|
||||
if (element) {
|
||||
element->fID = id;
|
||||
element->fChild = child;
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveNodeChild
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
status_t
|
||||
NodeChildTable<ParentNode, NodeChild>::RemoveNodeChild(ParentNode *node,
|
||||
NodeChild *child)
|
||||
{
|
||||
status_t error = (node && child ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK)
|
||||
error = RemoveNodeChild(node->GetID(), child->GetName());
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveNodeChild
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
status_t
|
||||
NodeChildTable<ParentNode, NodeChild>::RemoveNodeChild(vnode_id id,
|
||||
NodeChild *child)
|
||||
{
|
||||
status_t error = (child ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK)
|
||||
error = RemoveNodeChild(id, child->GetName());
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveNodeChild
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
status_t
|
||||
NodeChildTable<ParentNode, NodeChild>::RemoveNodeChild(vnode_id id,
|
||||
const char *name)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
if (Element *element = _FindElement(id, name))
|
||||
fTable.Remove(element);
|
||||
else
|
||||
error = B_ERROR;
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNodeChild
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
NodeChild *
|
||||
NodeChildTable<ParentNode, NodeChild>::GetNodeChild(vnode_id id,
|
||||
const char *name)
|
||||
{
|
||||
NodeChild *child = NULL;
|
||||
if (Element *element = _FindElement(id, name))
|
||||
child = element->fChild;
|
||||
return child;
|
||||
}
|
||||
|
||||
// _FindElement
|
||||
template<typename ParentNode, typename NodeChild>
|
||||
NodeChildTable<ParentNode, NodeChild>::Element *
|
||||
NodeChildTable<ParentNode, NodeChild>::_FindElement(vnode_id id,
|
||||
const char *name) const
|
||||
{
|
||||
Element *element = fTable.FindFirst(Element::HashFor(id, name));
|
||||
while (element && !element->Equals(id, name)) {
|
||||
if (element->fNext >= 0)
|
||||
element = fTable.ElementAt(element->fNext);
|
||||
else
|
||||
element = NULL;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
// undefine the PRINT from <Debug.h>
|
||||
//#undef PRINT
|
||||
|
||||
#endif // NODE_CHILD_TABLE_H
|
||||
@@ -0,0 +1,26 @@
|
||||
// NodeListener.cpp
|
||||
|
||||
#include "NodeListener.h"
|
||||
|
||||
// constructor
|
||||
NodeListener::NodeListener()
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
NodeListener::~NodeListener()
|
||||
{
|
||||
}
|
||||
|
||||
// NodeAdded
|
||||
void
|
||||
NodeListener::NodeAdded(Node */*node*/)
|
||||
{
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
void
|
||||
NodeListener::NodeRemoved(Node */*node*/)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// NodeListener.h
|
||||
|
||||
#ifndef NODE_LISTENER_H
|
||||
#define NODE_LISTENER_H
|
||||
|
||||
class Node;
|
||||
|
||||
// listening flags
|
||||
enum {
|
||||
NODE_LISTEN_ANY_NODE = 0x01,
|
||||
NODE_LISTEN_ADDED = 0x02,
|
||||
NODE_LISTEN_REMOVED = 0x04,
|
||||
NODE_LISTEN_ALL = NODE_LISTEN_ADDED | NODE_LISTEN_REMOVED,
|
||||
};
|
||||
|
||||
class NodeListener {
|
||||
public:
|
||||
NodeListener();
|
||||
virtual ~NodeListener();
|
||||
|
||||
virtual void NodeAdded(Node *node);
|
||||
virtual void NodeRemoved(Node *node);
|
||||
};
|
||||
|
||||
#endif // NODE_LISTENER_H
|
||||
@@ -0,0 +1,97 @@
|
||||
// NodeTable.cpp
|
||||
|
||||
#include "Debug.h"
|
||||
#include "NodeTable.h"
|
||||
|
||||
// constructor
|
||||
NodeTable::NodeTable()
|
||||
: fElementArray(1000),
|
||||
fNodes(1000, &fElementArray)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
NodeTable::~NodeTable()
|
||||
{
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
NodeTable::InitCheck() const
|
||||
{
|
||||
RETURN_ERROR(fNodes.InitCheck() && fElementArray.InitCheck()
|
||||
? B_OK : B_NO_MEMORY);
|
||||
}
|
||||
|
||||
// AddNode
|
||||
status_t
|
||||
NodeTable::AddNode(Node *node)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
NodeHashElement *element
|
||||
= fNodes.Add(NodeHashElement::HashForID(node));
|
||||
if (element)
|
||||
element->fNode = node;
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveNode
|
||||
status_t
|
||||
NodeTable::RemoveNode(Node *node)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK)
|
||||
error = RemoveNode(node->GetID());
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveNode
|
||||
status_t
|
||||
NodeTable::RemoveNode(vnode_id id)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
if (NodeHashElement *element = _FindElement(id))
|
||||
fNodes.Remove(element);
|
||||
else
|
||||
error = B_ERROR;
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNode
|
||||
Node *
|
||||
NodeTable::GetNode(vnode_id id)
|
||||
{
|
||||
Node *node = NULL;
|
||||
if (NodeHashElement *element = _FindElement(id))
|
||||
node = element->fNode;
|
||||
return node;
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
NodeTable::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddNodeTableAllocation(fNodes.ArraySize(), fNodes.VectorSize(),
|
||||
sizeof(NodeHashElement),
|
||||
fNodes.CountElements());
|
||||
}
|
||||
|
||||
// _FindElement
|
||||
NodeHashElement *
|
||||
NodeTable::_FindElement(vnode_id id) const
|
||||
{
|
||||
NodeHashElement *element
|
||||
= fNodes.FindFirst(NodeHashElement::HashForID(id));
|
||||
while (element && element->fNode->GetID() != id) {
|
||||
if (element->fNext >= 0)
|
||||
element = fNodes.ElementAt(element->fNext);
|
||||
else
|
||||
element = NULL;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// NodeTable.h
|
||||
|
||||
#ifndef NODE_TABLE_H
|
||||
#define NODE_TABLE_H
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Node.h"
|
||||
#include "OpenHashTable.h"
|
||||
|
||||
// NodeHashElement
|
||||
class NodeHashElement : public OpenHashElement {
|
||||
public:
|
||||
NodeHashElement() : OpenHashElement(), fNode(NULL)
|
||||
{
|
||||
fNext = -1;
|
||||
}
|
||||
|
||||
static inline uint32 HashForID(vnode_id id)
|
||||
{
|
||||
return uint32(id & 0xffffffff);
|
||||
}
|
||||
|
||||
static inline uint32 HashForID(Node *node)
|
||||
{
|
||||
return HashForID(node->GetID());
|
||||
}
|
||||
|
||||
inline uint32 Hash() const
|
||||
{
|
||||
return HashForID(fNode);
|
||||
}
|
||||
|
||||
inline bool operator==(const OpenHashElement &element) const
|
||||
{
|
||||
return (static_cast<const NodeHashElement&>(element).fNode == fNode);
|
||||
}
|
||||
|
||||
inline void Adopt(NodeHashElement &element)
|
||||
{
|
||||
fNode = element.fNode;
|
||||
}
|
||||
|
||||
Node *fNode;
|
||||
};
|
||||
|
||||
// NodeTable
|
||||
class NodeTable {
|
||||
public:
|
||||
NodeTable();
|
||||
~NodeTable();
|
||||
|
||||
status_t InitCheck() const;
|
||||
|
||||
status_t AddNode(Node *node);
|
||||
status_t RemoveNode(Node *node);
|
||||
status_t RemoveNode(vnode_id id);
|
||||
Node *GetNode(vnode_id id);
|
||||
|
||||
// debugging
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
NodeHashElement *_FindElement(vnode_id id) const;
|
||||
|
||||
private:
|
||||
OpenHashElementArray<NodeHashElement> fElementArray;
|
||||
OpenHashTable<NodeHashElement, OpenHashElementArray<NodeHashElement> >
|
||||
fNodes;
|
||||
};
|
||||
|
||||
// undefine the PRINT from <Debug.h>
|
||||
//#undef PRINT
|
||||
|
||||
#endif // NODE_TABLE_H
|
||||
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
Open Tracker License
|
||||
|
||||
Terms and Conditions
|
||||
|
||||
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice applies to all licensees
|
||||
and shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Be Incorporated shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings in
|
||||
this Software without prior written authorization from Be Incorporated.
|
||||
|
||||
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
|
||||
of Be Incorporated in the United States and other countries. Other brand product
|
||||
names are registered trademarks or trademarks of their respective holders.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
// bonefish:
|
||||
// * removed need for exceptions
|
||||
// * fixed warnings
|
||||
// * implemented rehashing
|
||||
// * hash array and element vector use areas for allocations
|
||||
// TODO:
|
||||
// * shrinking of element vectors
|
||||
|
||||
// Hash table with open addresssing
|
||||
|
||||
#ifndef __OPEN_HASH_TABLE__
|
||||
#define __OPEN_HASH_TABLE__
|
||||
|
||||
#include <malloc.h>
|
||||
#include <new.h>
|
||||
|
||||
#include "AreaUtils.h"
|
||||
#include "Misc.h"
|
||||
|
||||
// don't include <Debug.h>
|
||||
#define ASSERT(E) (void)0
|
||||
#define TRESPASS() (void)0
|
||||
|
||||
//namespace BPrivate {
|
||||
|
||||
template <class Element>
|
||||
class ElementVector {
|
||||
// element vector for OpenHashTable needs to implement this
|
||||
// interface
|
||||
public:
|
||||
Element &At(int32 index);
|
||||
Element *Add();
|
||||
int32 IndexOf(const Element &) const;
|
||||
void Remove(int32 index);
|
||||
};
|
||||
|
||||
class OpenHashElement {
|
||||
public:
|
||||
uint32 Hash() const;
|
||||
bool operator==(const OpenHashElement &) const;
|
||||
void Adopt(OpenHashElement &);
|
||||
// low overhead copy, original element is in undefined state
|
||||
// after call (calls Adopt on BString members, etc.)
|
||||
int32 fNext;
|
||||
};
|
||||
|
||||
const uint32 kPrimes [] = {
|
||||
509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
|
||||
524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
|
||||
134217689, 268435399, 536870909, 1073741789, 2147483647, 0
|
||||
};
|
||||
|
||||
template <class Element, class ElementVec = ElementVector<Element> >
|
||||
class OpenHashTable {
|
||||
public:
|
||||
OpenHashTable(int32 minSize, ElementVec *elementVector = 0,
|
||||
float maxLoadFactor = 0.8);
|
||||
// it is up to the subclass of OpenHashTable to supply
|
||||
// elementVector
|
||||
~OpenHashTable();
|
||||
|
||||
bool InitCheck() const;
|
||||
|
||||
void SetElementVector(ElementVec *elementVector);
|
||||
|
||||
Element *FindFirst(uint32 elementHash) const;
|
||||
Element *Add(uint32 elementHash);
|
||||
|
||||
void Remove(Element *);
|
||||
|
||||
// when calling Add, any outstanding element pointer may become
|
||||
// invalid; to deal with this, get the element index and restore
|
||||
// it after the add
|
||||
int32 ElementIndex(const Element *) const;
|
||||
Element *ElementAt(int32 index) const;
|
||||
|
||||
int32 ArraySize() const;
|
||||
int32 VectorSize() const;
|
||||
int32 CountElements() const;
|
||||
|
||||
protected:
|
||||
static int32 OptimalSize(int32 minSize);
|
||||
|
||||
private:
|
||||
bool _RehashIfNeeded();
|
||||
bool _Rehash();
|
||||
|
||||
int32 fArraySize;
|
||||
int32 fInitialSize;
|
||||
int32 fElementCount;
|
||||
int32 *fHashArray;
|
||||
ElementVec *fElementVector;
|
||||
float fMaxLoadFactor;
|
||||
};
|
||||
|
||||
template <class Element>
|
||||
class OpenHashElementArray : public ElementVector<Element> {
|
||||
// this is a straightforward implementation of an element vector
|
||||
// deleting is handled by linking deleted elements into a free list
|
||||
// the vector never shrinks
|
||||
public:
|
||||
OpenHashElementArray(int32 initialSize);
|
||||
~OpenHashElementArray();
|
||||
|
||||
bool InitCheck() const;
|
||||
|
||||
Element &At(int32 index);
|
||||
const Element &At(int32 index) const;
|
||||
Element *Add(const Element &);
|
||||
Element *Add();
|
||||
void Remove(int32 index);
|
||||
int32 IndexOf(const Element &) const;
|
||||
int32 Size() const;
|
||||
|
||||
private:
|
||||
Element *fData;
|
||||
int32 fSize;
|
||||
int32 fNextFree;
|
||||
int32 fNextDeleted;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
OpenHashTable<Element, ElementVec>::OpenHashTable(int32 minSize,
|
||||
ElementVec *elementVector, float maxLoadFactor)
|
||||
: fArraySize(OptimalSize(minSize)),
|
||||
fInitialSize(fArraySize),
|
||||
fElementCount(0),
|
||||
fElementVector(elementVector),
|
||||
fMaxLoadFactor(maxLoadFactor)
|
||||
{
|
||||
// sanity check the maximal load factor
|
||||
if (fMaxLoadFactor < 0.5)
|
||||
fMaxLoadFactor = 0.5;
|
||||
// allocate and init the array
|
||||
fHashArray = (int32*)AreaUtils::calloc(fArraySize, sizeof(int32));
|
||||
if (fHashArray) {
|
||||
for (int32 index = 0; index < fArraySize; index++)
|
||||
fHashArray[index] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
OpenHashTable<Element, ElementVec>::~OpenHashTable()
|
||||
{
|
||||
AreaUtils::free(fHashArray);
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
bool
|
||||
OpenHashTable<Element, ElementVec>::InitCheck() const
|
||||
{
|
||||
return (fHashArray && fElementVector);
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
int32
|
||||
OpenHashTable<Element, ElementVec>::OptimalSize(int32 minSize)
|
||||
{
|
||||
for (int32 index = 0; ; index++)
|
||||
if (!kPrimes[index] || kPrimes[index] >= (uint32)minSize)
|
||||
return (int32)kPrimes[index];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
Element *
|
||||
OpenHashTable<Element, ElementVec>::FindFirst(uint32 hash) const
|
||||
{
|
||||
ASSERT(fElementVector);
|
||||
hash %= fArraySize;
|
||||
if (fHashArray[hash] < 0)
|
||||
return 0;
|
||||
|
||||
return &fElementVector->At(fHashArray[hash]);
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
int32
|
||||
OpenHashTable<Element, ElementVec>::ElementIndex(const Element *element) const
|
||||
{
|
||||
return fElementVector->IndexOf(*element);
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
Element *
|
||||
OpenHashTable<Element, ElementVec>::ElementAt(int32 index) const
|
||||
{
|
||||
return &fElementVector->At(index);
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
int32
|
||||
OpenHashTable<Element, ElementVec>::ArraySize() const
|
||||
{
|
||||
return fArraySize;
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
int32
|
||||
OpenHashTable<Element, ElementVec>::VectorSize() const
|
||||
{
|
||||
return fElementVector->Size();
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
int32
|
||||
OpenHashTable<Element, ElementVec>::CountElements() const
|
||||
{
|
||||
return fElementCount;
|
||||
}
|
||||
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
Element *
|
||||
OpenHashTable<Element, ElementVec>::Add(uint32 hash)
|
||||
{
|
||||
ASSERT(fElementVector);
|
||||
_RehashIfNeeded();
|
||||
hash %= fArraySize;
|
||||
Element *result = fElementVector->Add();
|
||||
if (result) {
|
||||
result->fNext = fHashArray[hash];
|
||||
fHashArray[hash] = fElementVector->IndexOf(*result);
|
||||
fElementCount++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
void
|
||||
OpenHashTable<Element, ElementVec>::Remove(Element *element)
|
||||
{
|
||||
_RehashIfNeeded();
|
||||
uint32 hash = element->Hash() % fArraySize;
|
||||
int32 next = fHashArray[hash];
|
||||
ASSERT(next >= 0);
|
||||
|
||||
if (&fElementVector->At(next) == element) {
|
||||
fHashArray[hash] = element->fNext;
|
||||
fElementVector->Remove(next);
|
||||
fElementCount--;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 index = next; index >= 0; ) {
|
||||
// look for an existing match in table
|
||||
next = fElementVector->At(index).fNext;
|
||||
if (next < 0) {
|
||||
TRESPASS();
|
||||
return;
|
||||
}
|
||||
|
||||
if (&fElementVector->At(next) == element) {
|
||||
fElementVector->At(index).fNext = element->fNext;
|
||||
fElementVector->Remove(next);
|
||||
fElementCount--;
|
||||
return;
|
||||
}
|
||||
index = next;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Element, class ElementVec>
|
||||
void
|
||||
OpenHashTable<Element, ElementVec>::SetElementVector(ElementVec *elementVector)
|
||||
{
|
||||
fElementVector = elementVector;
|
||||
}
|
||||
|
||||
// _RehashIfNeeded
|
||||
template<class Element, class ElementVec>
|
||||
bool
|
||||
OpenHashTable<Element, ElementVec>::_RehashIfNeeded()
|
||||
{
|
||||
// The load factor range [fMaxLoadFactor / 3, fMaxLoadFactor] is fine,
|
||||
// I think. After rehashing the load factor will be about
|
||||
// fMaxLoadFactor * 2 / 3, respectively fMaxLoadFactor / 2.
|
||||
float loadFactor = (float)fElementCount / (float)fArraySize;
|
||||
if (loadFactor > fMaxLoadFactor
|
||||
|| (fArraySize > fInitialSize && loadFactor < fMaxLoadFactor / 3)) {
|
||||
return _Rehash();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// _Rehash
|
||||
template<class Element, class ElementVec>
|
||||
bool
|
||||
OpenHashTable<Element, ElementVec>::_Rehash()
|
||||
{
|
||||
bool result = true;
|
||||
int32 newSize = max(fInitialSize,
|
||||
int32(fElementCount * 1.73 * fMaxLoadFactor));
|
||||
newSize = OptimalSize(newSize);
|
||||
if (newSize != fArraySize) {
|
||||
PRINT(("_Rehash(): %lu -> %lu (currently %lu entries)\n", fArraySize, newSize,
|
||||
fElementCount));
|
||||
// allocate a new array
|
||||
int32 *newHashArray
|
||||
= (int32*)AreaUtils::calloc(newSize, sizeof(int32));
|
||||
if (newHashArray) {
|
||||
// init the new hash array
|
||||
for (int32 index = 0; index < newSize; index++)
|
||||
newHashArray[index] = -1;
|
||||
// iterate through all elements and put them into the new
|
||||
// hash array
|
||||
for (int i = 0; i < fArraySize; i++) {
|
||||
int32 index = fHashArray[i];
|
||||
while (index >= 0) {
|
||||
// insert the element in the new array
|
||||
Element &element = fElementVector->At(index);
|
||||
int32 next = element.fNext;
|
||||
uint32 hash = (element.Hash() % newSize);
|
||||
element.fNext = newHashArray[hash];
|
||||
newHashArray[hash] = index;
|
||||
// next element in old list
|
||||
index = next;
|
||||
}
|
||||
}
|
||||
// delete the old array and set the new one
|
||||
AreaUtils::free(fHashArray);
|
||||
fHashArray = newHashArray;
|
||||
fArraySize = newSize;
|
||||
} else
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template<class Element>
|
||||
OpenHashElementArray<Element>::OpenHashElementArray(int32 initialSize)
|
||||
: fSize(initialSize),
|
||||
fNextFree(0),
|
||||
fNextDeleted(-1)
|
||||
{
|
||||
fData = (Element*)AreaUtils::calloc((size_t)initialSize, sizeof(Element));
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
OpenHashElementArray<Element>::~OpenHashElementArray()
|
||||
{
|
||||
AreaUtils::free(fData);
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
bool
|
||||
OpenHashElementArray<Element>::InitCheck() const
|
||||
{
|
||||
return fData;
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
Element &
|
||||
OpenHashElementArray<Element>::At(int32 index)
|
||||
{
|
||||
ASSERT(index < fSize);
|
||||
return fData[index];
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
const Element &
|
||||
OpenHashElementArray<Element>::At(int32 index) const
|
||||
{
|
||||
ASSERT(index < fSize);
|
||||
return fData[index];
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
int32
|
||||
OpenHashElementArray<Element>::IndexOf(const Element &element) const
|
||||
{
|
||||
int32 result = &element - fData;
|
||||
if (result < 0 || result > fSize)
|
||||
return -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
int32
|
||||
OpenHashElementArray<Element>::Size() const
|
||||
{
|
||||
return fSize;
|
||||
}
|
||||
|
||||
|
||||
template<class Element>
|
||||
Element *
|
||||
OpenHashElementArray<Element>::Add(const Element &newElement)
|
||||
{
|
||||
Element *element = Add();
|
||||
if (element)
|
||||
element.Adopt(newElement);
|
||||
return element;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
const int32 kGrowChunk = 10;
|
||||
#else
|
||||
const int32 kGrowChunk = 1024;
|
||||
#endif
|
||||
|
||||
template<class Element>
|
||||
Element *
|
||||
OpenHashElementArray<Element>::Add()
|
||||
{
|
||||
int32 index = fNextFree;
|
||||
if (fNextDeleted >= 0) {
|
||||
index = fNextDeleted;
|
||||
fNextDeleted = At(index).fNext;
|
||||
} else if (fNextFree >= fSize - 1) {
|
||||
int32 newSize = fSize + kGrowChunk;
|
||||
/*
|
||||
Element *newData = (Element *)calloc((size_t)newSize , sizeof(Element));
|
||||
if (!newData)
|
||||
return NULL;
|
||||
memcpy(newData, fData, fSize * sizeof(Element));
|
||||
free(fData);
|
||||
*/
|
||||
Element *newData = (Element*)AreaUtils::realloc(fData,
|
||||
(size_t)newSize * sizeof(Element));
|
||||
if (!newData)
|
||||
return NULL;
|
||||
|
||||
fData = newData;
|
||||
fSize = newSize;
|
||||
index = fNextFree;
|
||||
fNextFree++;
|
||||
} else
|
||||
fNextFree++;
|
||||
|
||||
new (&At(index)) Element;
|
||||
// call placement new to initialize the element properly
|
||||
ASSERT(At(index).fNext == -1);
|
||||
|
||||
return &At(index);
|
||||
}
|
||||
|
||||
template<class Element>
|
||||
void
|
||||
OpenHashElementArray<Element>::Remove(int32 index)
|
||||
{
|
||||
// delete by chaining empty elements in a single linked
|
||||
// list, reusing the next field
|
||||
ASSERT(index < fSize);
|
||||
At(index).~Element();
|
||||
// call the destructor explicitly to destroy the element
|
||||
// properly
|
||||
At(index).fNext = fNextDeleted;
|
||||
fNextDeleted = index;
|
||||
}
|
||||
|
||||
//} // namespace BPrivate
|
||||
|
||||
//using namespace BPrivate;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
/* Query - query parsing and evaluation
|
||||
*
|
||||
* Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*
|
||||
* Adjusted by Ingo Weinhold <bonefish@cs.tu-berlin.de> for usage in RAM FS.
|
||||
*/
|
||||
#ifndef QUERY_H
|
||||
#define QUERY_H
|
||||
|
||||
|
||||
#include <OS.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "DLList.h"
|
||||
#include "Index.h"
|
||||
#include "Stack.h"
|
||||
#include "ramfs.h"
|
||||
|
||||
class Entry;
|
||||
class Equation;
|
||||
class IndexIterator;
|
||||
class Node;
|
||||
class Query;
|
||||
class Term;
|
||||
class Volume;
|
||||
|
||||
|
||||
#define B_QUERY_NON_INDEXED 0x00000002
|
||||
|
||||
|
||||
// Wraps the RAM FS Index to provide the interface required by the Query
|
||||
// implementation. At least most of it.
|
||||
//
|
||||
// IndexWrapper
|
||||
class IndexWrapper {
|
||||
public:
|
||||
IndexWrapper(Volume *volume);
|
||||
|
||||
status_t SetTo(const char *name);
|
||||
void Unset();
|
||||
|
||||
uint32 Type() const;
|
||||
off_t GetSize() const;
|
||||
int32 KeySize() const;
|
||||
|
||||
private:
|
||||
friend class IndexIterator;
|
||||
|
||||
Volume *fVolume;
|
||||
Index *fIndex;
|
||||
};
|
||||
|
||||
// IndexIterator
|
||||
class IndexIterator {
|
||||
public:
|
||||
IndexIterator(IndexWrapper *indexWrapper);
|
||||
|
||||
status_t Find(const uint8 *const key, size_t keyLength);
|
||||
status_t GetNextEntry(uint8 *buffer, uint16 *keyLength, size_t bufferSize,
|
||||
Entry **entry);
|
||||
|
||||
private:
|
||||
IndexWrapper *fIndexWrapper;
|
||||
IndexEntryIterator fIterator;
|
||||
bool fInitialized;
|
||||
};
|
||||
|
||||
|
||||
class Expression {
|
||||
public:
|
||||
Expression(char *expr);
|
||||
~Expression();
|
||||
|
||||
status_t InitCheck();
|
||||
const char *Position() const { return fPosition; }
|
||||
Term *Root() const { return fTerm; }
|
||||
|
||||
protected:
|
||||
Term *ParseOr(char **expr);
|
||||
Term *ParseAnd(char **expr);
|
||||
Term *ParseEquation(char **expr);
|
||||
|
||||
bool IsOperator(char **expr,char op);
|
||||
|
||||
private:
|
||||
Expression(const Expression &);
|
||||
Expression &operator=(const Expression &);
|
||||
// no implementation
|
||||
|
||||
char *fPosition;
|
||||
Term *fTerm;
|
||||
};
|
||||
|
||||
class Query : public DLListLinkImpl<Query> {
|
||||
public:
|
||||
Query(Volume *volume, Expression *expression, uint32 flags);
|
||||
~Query();
|
||||
|
||||
status_t Rewind();
|
||||
status_t GetNextEntry(struct dirent *, size_t size);
|
||||
|
||||
void SetLiveMode(port_id port, int32 token);
|
||||
void LiveUpdate(Entry *entry, Node* node, const char *attribute,
|
||||
int32 type, const uint8 *oldKey, size_t oldLength,
|
||||
const uint8 *newKey, size_t newLength);
|
||||
|
||||
Expression *GetExpression() const { return fExpression; }
|
||||
|
||||
private:
|
||||
// void SendNotification(Entry* entry)
|
||||
|
||||
private:
|
||||
Volume *fVolume;
|
||||
Expression *fExpression;
|
||||
Equation *fCurrent;
|
||||
IndexIterator *fIterator;
|
||||
IndexWrapper fIndex;
|
||||
Stack<Equation *> fStack;
|
||||
|
||||
uint32 fFlags;
|
||||
port_id fPort;
|
||||
int32 fToken;
|
||||
bool fNeedsEntry;
|
||||
};
|
||||
|
||||
#endif /* QUERY_H */
|
||||
@@ -0,0 +1,288 @@
|
||||
// SLList.h
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name of a copyright holder shall
|
||||
// not be used in advertising or otherwise to promote the sale, use or other
|
||||
// dealings in this Software without prior written authorization of the
|
||||
// copyright holder.
|
||||
|
||||
#ifndef SL_LIST_H
|
||||
#define SL_LIST_H
|
||||
|
||||
#include <new>
|
||||
|
||||
// SLListStandardNode
|
||||
template<typename Value>
|
||||
struct SLListStandardNode {
|
||||
SLListStandardNode(const Value &a)
|
||||
: value(a),
|
||||
next(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Value value;
|
||||
SLListStandardNode<Value> *next;
|
||||
};
|
||||
|
||||
// SLListStandardNodeAllocator
|
||||
template<typename Value, typename Node>
|
||||
class SLListStandardNodeAllocator
|
||||
{
|
||||
public:
|
||||
inline Node *Allocate(const Value &a) const
|
||||
{
|
||||
return new(nothrow) SLListStandardNode<Value>(a);
|
||||
}
|
||||
|
||||
inline void Free(Node *node) const
|
||||
{
|
||||
delete node;
|
||||
}
|
||||
};
|
||||
|
||||
// SLListValueNodeAllocator
|
||||
template<typename Value, typename Node>
|
||||
class SLListValueNodeAllocator
|
||||
{
|
||||
public:
|
||||
inline Node *Allocate(const Value &a) const
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
inline void Free(Node *node) const
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// SLListStandardGetValue
|
||||
template<typename Value, typename Node>
|
||||
class SLListStandardGetValue
|
||||
{
|
||||
public:
|
||||
inline Value &operator()(Node *node) const
|
||||
{
|
||||
return node->value;
|
||||
}
|
||||
};
|
||||
|
||||
// SLListValueNodeGetValue
|
||||
template<typename Value, typename Node>
|
||||
class SLListValueNodeGetValue
|
||||
{
|
||||
public:
|
||||
inline Value &operator()(Node *node) const
|
||||
{
|
||||
return *node;
|
||||
}
|
||||
};
|
||||
|
||||
// for convenience
|
||||
#define SL_LIST_TEMPLATE_LIST template<typename Value, typename Node, \
|
||||
typename NodeAllocator, typename GetValue>
|
||||
#define SL_LIST_CLASS_NAME SLList<Value, Node, NodeAllocator, GetValue>
|
||||
|
||||
// SLList
|
||||
template<typename Value, typename Node = SLListStandardNode<Value>,
|
||||
typename NodeAllocator = SLListStandardNodeAllocator<Value, Node>,
|
||||
typename GetValue = SLListStandardGetValue<Value, Node> >
|
||||
class SLList {
|
||||
public:
|
||||
class Iterator;
|
||||
|
||||
public:
|
||||
SLList();
|
||||
SLList(const NodeAllocator &nodeAllocator, const GetValue &getValue);
|
||||
~SLList();
|
||||
|
||||
bool Insert(const Value &value, Iterator *iterator = NULL);
|
||||
bool Remove(const Value &value);
|
||||
void Remove(Iterator &iterator);
|
||||
void RemoveAll();
|
||||
|
||||
bool Find(const Value &value, Iterator *iterator = NULL) const;
|
||||
void GetIterator(Iterator *iterator) const;
|
||||
|
||||
private:
|
||||
friend class Iterator;
|
||||
|
||||
Node *fHead;
|
||||
NodeAllocator fNodeAllocator;
|
||||
GetValue fGetValue;
|
||||
};
|
||||
|
||||
// Iterator
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
class SL_LIST_CLASS_NAME::Iterator {
|
||||
public:
|
||||
Iterator() : fList(NULL), fCurrent(NULL) {}
|
||||
~Iterator() {}
|
||||
|
||||
inline Value *GetCurrent()
|
||||
{
|
||||
return (fList && fCurrent ? &fList->fGetValue(fCurrent) : NULL);
|
||||
}
|
||||
|
||||
inline Value *GetNext()
|
||||
{
|
||||
if (fCurrent)
|
||||
fCurrent = fCurrent->next;
|
||||
return GetCurrent();
|
||||
}
|
||||
|
||||
inline void Remove()
|
||||
{
|
||||
if (fList)
|
||||
fList->Remove(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SL_LIST_CLASS_NAME;
|
||||
|
||||
inline void _SetTo(SL_LIST_CLASS_NAME *list, Node *previous, Node *node)
|
||||
{
|
||||
fList = list;
|
||||
fPrevious = previous;
|
||||
fCurrent = node;
|
||||
}
|
||||
|
||||
inline SL_LIST_CLASS_NAME *_GetList() const { return fList; }
|
||||
inline Node *_GetPreviousNode() const { return fPrevious; }
|
||||
inline Node *_GetCurrentNode() const { return fCurrent; }
|
||||
|
||||
private:
|
||||
SL_LIST_CLASS_NAME *fList;
|
||||
Node *fPrevious;
|
||||
Node *fCurrent;
|
||||
};
|
||||
|
||||
// constructor
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
SL_LIST_CLASS_NAME::SLList()
|
||||
: fHead(NULL)/*,
|
||||
fNodeAllocator(),
|
||||
fGetValue()*/
|
||||
{
|
||||
}
|
||||
|
||||
// constructor
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
SL_LIST_CLASS_NAME::SLList(const NodeAllocator &nodeAllocator,
|
||||
const GetValue &getValue)
|
||||
: fHead(NULL),
|
||||
fNodeAllocator(nodeAllocator),
|
||||
fGetValue(getValue)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
SL_LIST_CLASS_NAME::~SLList()
|
||||
{
|
||||
RemoveAll();
|
||||
}
|
||||
|
||||
// Insert
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
bool
|
||||
SL_LIST_CLASS_NAME::Insert(const Value &value, Iterator *iterator)
|
||||
{
|
||||
Node *node = fNodeAllocator.Allocate(value);
|
||||
if (node) {
|
||||
node->next = fHead;
|
||||
fHead = node;
|
||||
if (iterator)
|
||||
iterator->_SetTo(this, NULL, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// Remove
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
bool
|
||||
SL_LIST_CLASS_NAME::Remove(const Value &value)
|
||||
{
|
||||
Iterator iterator;
|
||||
bool result = Find(value, &iterator);
|
||||
if (result)
|
||||
iterator.Remove();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
void
|
||||
SL_LIST_CLASS_NAME::Remove(Iterator &iterator)
|
||||
{
|
||||
Node *node = iterator._GetCurrentNode();
|
||||
if (iterator._GetList() == this && node) {
|
||||
Node *previous = iterator._GetPreviousNode();
|
||||
iterator._SetTo(this, previous, node->next);
|
||||
if (previous)
|
||||
previous->next = node->next;
|
||||
else
|
||||
fHead = node->next;
|
||||
fNodeAllocator.Free(node);
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveAll
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
void
|
||||
SL_LIST_CLASS_NAME::RemoveAll()
|
||||
{
|
||||
for (Node *node = fHead; node; ) {
|
||||
Node *next = node->next;
|
||||
fNodeAllocator.Free(node);
|
||||
node = next;
|
||||
}
|
||||
fHead = NULL;
|
||||
}
|
||||
|
||||
// Find
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
bool
|
||||
SL_LIST_CLASS_NAME::Find(const Value &value, Iterator *iterator) const
|
||||
{
|
||||
Node *node = fHead;
|
||||
Node *previous = NULL;
|
||||
while (node && fGetValue(node) != value) {
|
||||
previous = node;
|
||||
node = node->next;
|
||||
}
|
||||
if (node && iterator) {
|
||||
iterator->_SetTo(const_cast<SL_LIST_CLASS_NAME*>(this), previous,
|
||||
node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// GetIterator
|
||||
SL_LIST_TEMPLATE_LIST
|
||||
void
|
||||
SL_LIST_CLASS_NAME::GetIterator(Iterator *iterator) const
|
||||
{
|
||||
if (iterator)
|
||||
iterator->_SetTo(const_cast<SL_LIST_CLASS_NAME*>(this), NULL, fHead);
|
||||
}
|
||||
|
||||
#endif // SL_LIST_H
|
||||
@@ -0,0 +1,371 @@
|
||||
// SizeIndex.cpp
|
||||
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include "Entry.h"
|
||||
#include "EntryListener.h"
|
||||
#include "IndexImpl.h"
|
||||
#include "Node.h"
|
||||
#include "NodeListener.h"
|
||||
#include "SizeIndex.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// SizeIndexPrimaryKey
|
||||
class SizeIndexPrimaryKey {
|
||||
public:
|
||||
SizeIndexPrimaryKey(Node *node, off_t size)
|
||||
: node(node), size(size) {}
|
||||
SizeIndexPrimaryKey(Node *node)
|
||||
: node(node), size(node->GetSize()) {}
|
||||
SizeIndexPrimaryKey(off_t size)
|
||||
: node(NULL), size(size) {}
|
||||
|
||||
Node *node;
|
||||
off_t size;
|
||||
};
|
||||
|
||||
// SizeIndexGetPrimaryKey
|
||||
class SizeIndexGetPrimaryKey {
|
||||
public:
|
||||
inline SizeIndexPrimaryKey operator()(Node *a)
|
||||
{
|
||||
return SizeIndexPrimaryKey(a);
|
||||
}
|
||||
|
||||
inline SizeIndexPrimaryKey operator()(Node *a) const
|
||||
{
|
||||
return SizeIndexPrimaryKey(a);
|
||||
}
|
||||
};
|
||||
|
||||
// SizeIndexPrimaryKeyCompare
|
||||
class SizeIndexPrimaryKeyCompare
|
||||
{
|
||||
public:
|
||||
inline int operator()(const SizeIndexPrimaryKey &a,
|
||||
const SizeIndexPrimaryKey &b) const
|
||||
{
|
||||
if (a.node != NULL && a.node == b.node)
|
||||
return 0;
|
||||
if (a.size < b.size)
|
||||
return -1;
|
||||
if (a.size > b.size)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// NodeTree
|
||||
typedef TwoKeyAVLTree<Node*, SizeIndexPrimaryKey,
|
||||
SizeIndexPrimaryKeyCompare,
|
||||
SizeIndexGetPrimaryKey>
|
||||
_NodeTree;
|
||||
class SizeIndex::NodeTree : public _NodeTree {};
|
||||
|
||||
|
||||
// IteratorList
|
||||
class SizeIndex::IteratorList : public DLList<Iterator> {};
|
||||
|
||||
|
||||
// Iterator
|
||||
class SizeIndex::Iterator
|
||||
: public NodeEntryIterator<SizeIndex::NodeTree::Iterator>,
|
||||
public DLListLinkImpl<Iterator>, public EntryListener,
|
||||
public NodeListener {
|
||||
public:
|
||||
Iterator();
|
||||
virtual ~Iterator();
|
||||
|
||||
virtual Entry *GetCurrent();
|
||||
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||
|
||||
virtual status_t Suspend();
|
||||
virtual status_t Resume();
|
||||
|
||||
bool SetTo(SizeIndex *index, off_t size, bool ignoreValue = false);
|
||||
void Unset();
|
||||
|
||||
virtual void EntryRemoved(Entry *entry);
|
||||
virtual void NodeRemoved(Node *node);
|
||||
|
||||
private:
|
||||
typedef NodeEntryIterator<SizeIndex::NodeTree::Iterator> BaseClass;
|
||||
|
||||
private:
|
||||
SizeIndex *fIndex;
|
||||
};
|
||||
|
||||
|
||||
// SizeIndex
|
||||
|
||||
// constructor
|
||||
SizeIndex::SizeIndex(Volume *volume)
|
||||
: Index(volume, "size", B_INT64_TYPE, true, sizeof(off_t)),
|
||||
fNodes(new(nothrow) NodeTree),
|
||||
fIterators(new(nothrow) IteratorList)
|
||||
{
|
||||
if (fInitStatus == B_OK && (!fNodes || !fIterators))
|
||||
fInitStatus = B_NO_MEMORY;
|
||||
if (fInitStatus == B_OK) {
|
||||
fInitStatus = fVolume->AddNodeListener(this,
|
||||
NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
SizeIndex::~SizeIndex()
|
||||
{
|
||||
if (fVolume)
|
||||
fVolume->RemoveNodeListener(this, NULL);
|
||||
if (fIterators) {
|
||||
// unset the iterators
|
||||
for (Iterator *iterator = fIterators->GetFirst();
|
||||
iterator;
|
||||
iterator = fIterators->GetNext(iterator)) {
|
||||
iterator->SetTo(NULL, 0);
|
||||
}
|
||||
delete fIterators;
|
||||
}
|
||||
if (fNodes)
|
||||
delete fNodes;
|
||||
}
|
||||
|
||||
// CountEntries
|
||||
int32
|
||||
SizeIndex::CountEntries() const
|
||||
{
|
||||
return fNodes->CountItems();
|
||||
}
|
||||
|
||||
// Changed
|
||||
status_t
|
||||
SizeIndex::Changed(Node *node, off_t oldSize)
|
||||
{
|
||||
status_t error = B_BAD_VALUE;
|
||||
if (node) {
|
||||
NodeTree::Iterator it;
|
||||
Node **foundNode = fNodes->Find(SizeIndexPrimaryKey(node, oldSize),
|
||||
node, &it);
|
||||
if (foundNode && *foundNode == node) {
|
||||
// update the iterators
|
||||
for (Iterator *iterator = fIterators->GetFirst();
|
||||
iterator;
|
||||
iterator = fIterators->GetNext(iterator)) {
|
||||
if (iterator->GetCurrentNode() == node)
|
||||
iterator->NodeRemoved(node);
|
||||
}
|
||||
|
||||
// remove and re-insert the node
|
||||
fNodes->Remove(it);
|
||||
error = fNodes->Insert(node);
|
||||
|
||||
// udpate live queries
|
||||
off_t newSize = node->GetSize();
|
||||
fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
|
||||
(const uint8*)&oldSize, sizeof(oldSize), (const uint8*)&newSize,
|
||||
sizeof(newSize));
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// NodeAdded
|
||||
void
|
||||
SizeIndex::NodeAdded(Node *node)
|
||||
{
|
||||
if (node)
|
||||
fNodes->Insert(node);
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
void
|
||||
SizeIndex::NodeRemoved(Node *node)
|
||||
{
|
||||
if (node)
|
||||
fNodes->Remove(node, node);
|
||||
}
|
||||
|
||||
// InternalGetIterator
|
||||
AbstractIndexEntryIterator *
|
||||
SizeIndex::InternalGetIterator()
|
||||
{
|
||||
Iterator *iterator = new(nothrow) Iterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, 0, true)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// InternalFind
|
||||
AbstractIndexEntryIterator *
|
||||
SizeIndex::InternalFind(const uint8 *key, size_t length)
|
||||
{
|
||||
if (!key || length != sizeof(off_t))
|
||||
return NULL;
|
||||
Iterator *iterator = new(nothrow) Iterator;
|
||||
if (iterator) {
|
||||
if (!iterator->SetTo(this, *(const off_t*)key)) {
|
||||
delete iterator;
|
||||
iterator = NULL;
|
||||
}
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// _AddIterator
|
||||
void
|
||||
SizeIndex::_AddIterator(Iterator *iterator)
|
||||
{
|
||||
fIterators->Insert(iterator);
|
||||
}
|
||||
|
||||
// _RemoveIterator
|
||||
void
|
||||
SizeIndex::_RemoveIterator(Iterator *iterator)
|
||||
{
|
||||
fIterators->Remove(iterator);
|
||||
}
|
||||
|
||||
|
||||
// Iterator
|
||||
|
||||
// constructor
|
||||
SizeIndex::Iterator::Iterator()
|
||||
: BaseClass(),
|
||||
fIndex(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
SizeIndex::Iterator::~Iterator()
|
||||
{
|
||||
SetTo(NULL, 0);
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
SizeIndex::Iterator::GetCurrent()
|
||||
{
|
||||
return BaseClass::GetCurrent();
|
||||
}
|
||||
|
||||
// GetCurrent
|
||||
Entry *
|
||||
SizeIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||
{
|
||||
Entry *entry = GetCurrent();
|
||||
if (entry) {
|
||||
*(off_t*)buffer = entry->GetNode()->GetSize();
|
||||
*keyLength = sizeof(size_t);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Suspend
|
||||
status_t
|
||||
SizeIndex::Iterator::Suspend()
|
||||
{
|
||||
status_t error = BaseClass::Suspend();
|
||||
if (error == B_OK) {
|
||||
if (fNode) {
|
||||
error = fIndex->GetVolume()->AddNodeListener(this, fNode,
|
||||
NODE_LISTEN_REMOVED);
|
||||
if (error == B_OK && fEntry) {
|
||||
error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
|
||||
ENTRY_LISTEN_REMOVED);
|
||||
if (error != B_OK)
|
||||
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
}
|
||||
if (error != B_OK)
|
||||
BaseClass::Resume();
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Resume
|
||||
status_t
|
||||
SizeIndex::Iterator::Resume()
|
||||
{
|
||||
status_t error = BaseClass::Resume();
|
||||
if (error == B_OK) {
|
||||
if (fEntry)
|
||||
error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
|
||||
if (fNode) {
|
||||
if (error == B_OK)
|
||||
error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
else
|
||||
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// SetTo
|
||||
bool
|
||||
SizeIndex::Iterator::SetTo(SizeIndex *index, off_t size, bool ignoreValue)
|
||||
{
|
||||
Resume();
|
||||
Unset();
|
||||
// set the new values
|
||||
fIndex = index;
|
||||
if (fIndex)
|
||||
fIndex->_AddIterator(this);
|
||||
fInitialized = fIndex;
|
||||
// get the node's first entry
|
||||
if (fIndex) {
|
||||
// get the first node
|
||||
bool found = true;
|
||||
if (ignoreValue)
|
||||
fIndex->fNodes->GetIterator(&fIterator);
|
||||
else
|
||||
found = fIndex->fNodes->FindFirst(size, &fIterator);
|
||||
// get the first entry
|
||||
if (found) {
|
||||
if (Node **nodeP = fIterator.GetCurrent()) {
|
||||
fNode = *nodeP;
|
||||
fEntry = fNode->GetFirstReferrer();
|
||||
if (!fEntry)
|
||||
BaseClass::GetNext();
|
||||
if (!ignoreValue && fNode && fNode->GetSize() != size)
|
||||
Unset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fEntry;
|
||||
}
|
||||
|
||||
// Unset
|
||||
void
|
||||
SizeIndex::Iterator::Unset()
|
||||
{
|
||||
if (fIndex) {
|
||||
fIndex->_RemoveIterator(this);
|
||||
fIndex = NULL;
|
||||
}
|
||||
BaseClass::Unset();
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
void
|
||||
SizeIndex::Iterator::EntryRemoved(Entry */*entry*/)
|
||||
{
|
||||
Resume();
|
||||
fIsNext = BaseClass::GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
void
|
||||
SizeIndex::Iterator::NodeRemoved(Node */*node*/)
|
||||
{
|
||||
Resume();
|
||||
fEntry = NULL;
|
||||
fIsNext = BaseClass::GetNext();
|
||||
Suspend();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// SizeIndex.h
|
||||
|
||||
#ifndef SIZE_INDEX_H
|
||||
#define SIZE_INDEX_H
|
||||
|
||||
#include "Index.h"
|
||||
#include "NodeListener.h"
|
||||
#include "TwoKeyAVLTree.h"
|
||||
|
||||
// SizeIndex
|
||||
class SizeIndex : public Index, private NodeListener {
|
||||
public:
|
||||
SizeIndex(Volume *volume);
|
||||
virtual ~SizeIndex();
|
||||
|
||||
virtual int32 CountEntries() const;
|
||||
|
||||
virtual status_t Changed(Node *node, off_t oldSize);
|
||||
|
||||
private:
|
||||
virtual void NodeAdded(Node *node);
|
||||
virtual void NodeRemoved(Node *node);
|
||||
|
||||
protected:
|
||||
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||
size_t length);
|
||||
|
||||
private:
|
||||
class Iterator;
|
||||
class IteratorList;
|
||||
class NodeTree;
|
||||
friend class Iterator;
|
||||
|
||||
private:
|
||||
void _AddIterator(Iterator *iterator);
|
||||
void _RemoveIterator(Iterator *iterator);
|
||||
|
||||
private:
|
||||
NodeTree *fNodes;
|
||||
IteratorList *fIterators;
|
||||
};
|
||||
|
||||
#endif // SIZE_INDEX_H
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Stack - a template stack class (plus some handy methods)
|
||||
*
|
||||
* Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KERNEL_UTIL_STACK_H
|
||||
#define KERNEL_UTIL_STACK_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
template<class T> class Stack {
|
||||
public:
|
||||
Stack()
|
||||
:
|
||||
fArray(NULL),
|
||||
fUsed(0),
|
||||
fMax(0)
|
||||
{
|
||||
}
|
||||
|
||||
~Stack()
|
||||
{
|
||||
free(fArray);
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return fUsed == 0;
|
||||
}
|
||||
|
||||
void MakeEmpty()
|
||||
{
|
||||
// could also free the memory
|
||||
fUsed = 0;
|
||||
}
|
||||
|
||||
status_t Push(T value)
|
||||
{
|
||||
if (fUsed >= fMax) {
|
||||
fMax += 16;
|
||||
T *newArray = (T *)realloc(fArray, fMax * sizeof(T));
|
||||
if (newArray == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fArray = newArray;
|
||||
}
|
||||
fArray[fUsed++] = value;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
bool Pop(T *value)
|
||||
{
|
||||
if (fUsed == 0)
|
||||
return false;
|
||||
|
||||
*value = fArray[--fUsed];
|
||||
return true;
|
||||
}
|
||||
|
||||
T *Array()
|
||||
{
|
||||
return fArray;
|
||||
}
|
||||
|
||||
int32 CountItems() const
|
||||
{
|
||||
return fUsed;
|
||||
}
|
||||
|
||||
private:
|
||||
T *fArray;
|
||||
int32 fUsed;
|
||||
int32 fMax;
|
||||
};
|
||||
|
||||
#endif /* KERNEL_UTIL_STACK_H */
|
||||
@@ -0,0 +1,70 @@
|
||||
// SymLink.cpp
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "AllocationInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "SizeIndex.h"
|
||||
#include "SymLink.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// constructor
|
||||
SymLink::SymLink(Volume *volume)
|
||||
: Node(volume, NODE_TYPE_SYMLINK),
|
||||
fLinkedPath()
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
SymLink::~SymLink()
|
||||
{
|
||||
}
|
||||
|
||||
// SetSize
|
||||
status_t
|
||||
SymLink::SetSize(off_t newSize)
|
||||
{
|
||||
status_t error = (newSize >= 0 && newSize < PATH_MAX ? B_OK : B_BAD_VALUE);
|
||||
int32 oldSize = GetLinkedPathLength();
|
||||
if (error == B_OK && newSize < oldSize) {
|
||||
fLinkedPath.Truncate(newSize);
|
||||
MarkModified();
|
||||
// update the size index
|
||||
if (SizeIndex *index = GetVolume()->GetSizeIndex())
|
||||
index->Changed(this, oldSize);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetSize
|
||||
off_t
|
||||
SymLink::GetSize() const
|
||||
{
|
||||
return GetLinkedPathLength();
|
||||
}
|
||||
|
||||
// SetLinkedPath
|
||||
status_t
|
||||
SymLink::SetLinkedPath(const char *path)
|
||||
{
|
||||
int32 oldLen = GetLinkedPathLength();
|
||||
int32 len = strnlen(path, PATH_MAX - 1);
|
||||
if (fLinkedPath.SetTo(path, len)) {
|
||||
MarkModified();
|
||||
// update the size index, if necessary
|
||||
if (len != oldLen) {
|
||||
if (SizeIndex *index = GetVolume()->GetSizeIndex())
|
||||
index->Changed(this, oldLen);
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
SymLink::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
info.AddSymLinkAllocation(GetSize());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// SymLink.h
|
||||
|
||||
#ifndef SYMLINK_H
|
||||
#define SYMLINK_H
|
||||
|
||||
#include "Node.h"
|
||||
#include "String.h"
|
||||
|
||||
class SymLink : public Node {
|
||||
public:
|
||||
SymLink(Volume *volume);
|
||||
virtual ~SymLink();
|
||||
|
||||
virtual status_t SetSize(off_t newSize);
|
||||
virtual off_t GetSize() const;
|
||||
|
||||
status_t SetLinkedPath(const char *path);
|
||||
const char *GetLinkedPath() const { return fLinkedPath.GetString(); }
|
||||
size_t GetLinkedPathLength() const { return fLinkedPath.GetLength(); }
|
||||
|
||||
// debugging
|
||||
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
private:
|
||||
String fLinkedPath;
|
||||
};
|
||||
|
||||
#endif // SYMLINK_H
|
||||
@@ -0,0 +1,304 @@
|
||||
// TwoKeyAVLTree.h
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// Except as contained in this notice, the name of a copyright holder shall
|
||||
// not be used in advertising or otherwise to promote the sale, use or other
|
||||
// dealings in this Software without prior written authorization of the
|
||||
// copyright holder.
|
||||
|
||||
#ifndef TWO_KEY_AVL_TREE_H
|
||||
#define TWO_KEY_AVL_TREE_H
|
||||
|
||||
#include "AVLTree.h"
|
||||
|
||||
// TwoKeyAVLTreeKey
|
||||
template<typename PrimaryKey, typename SecondaryKey>
|
||||
class TwoKeyAVLTreeKey {
|
||||
public:
|
||||
inline TwoKeyAVLTreeKey(const PrimaryKey &primary,
|
||||
const SecondaryKey &secondary)
|
||||
: primary(primary),
|
||||
secondary(secondary),
|
||||
use_secondary(true)
|
||||
{
|
||||
}
|
||||
|
||||
inline TwoKeyAVLTreeKey(const PrimaryKey *primary)
|
||||
: primary(primary),
|
||||
secondary(NULL),
|
||||
use_secondary(false)
|
||||
{
|
||||
}
|
||||
|
||||
PrimaryKey primary;
|
||||
SecondaryKey secondary;
|
||||
bool use_secondary;
|
||||
};
|
||||
|
||||
// TwoKeyAVLTreeKeyCompare
|
||||
template<typename PrimaryKey, typename SecondaryKey,
|
||||
typename PrimaryKeyCompare, typename SecondaryKeyCompare>
|
||||
class TwoKeyAVLTreeKeyCompare {
|
||||
private:
|
||||
typedef TwoKeyAVLTreeKey<PrimaryKey, SecondaryKey> Key;
|
||||
|
||||
public:
|
||||
inline TwoKeyAVLTreeKeyCompare(const PrimaryKeyCompare &primary,
|
||||
const SecondaryKeyCompare &secondary)
|
||||
: fPrimaryKeyCompare(primary), fSecondaryKeyCompare(secondary) {}
|
||||
|
||||
inline int operator()(const Key &a, const Key &b) const
|
||||
{
|
||||
int result = fPrimaryKeyCompare(a.primary, b.primary);
|
||||
if (result == 0 && a.use_secondary && b.use_secondary)
|
||||
result = fSecondaryKeyCompare(a.secondary, b.secondary);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
PrimaryKeyCompare fPrimaryKeyCompare;
|
||||
SecondaryKeyCompare fSecondaryKeyCompare;
|
||||
};
|
||||
|
||||
// TwoKeyAVLTreeGetKey
|
||||
template<typename Value, typename PrimaryKey, typename SecondaryKey,
|
||||
typename GetPrimaryKey, typename GetSecondaryKey>
|
||||
class TwoKeyAVLTreeGetKey
|
||||
{
|
||||
private:
|
||||
typedef TwoKeyAVLTreeKey<PrimaryKey, SecondaryKey> Key;
|
||||
|
||||
public:
|
||||
TwoKeyAVLTreeGetKey(const GetPrimaryKey &getPrimary,
|
||||
const GetSecondaryKey &getSecondary)
|
||||
: fGetPrimaryKey(getPrimary),
|
||||
fGetSecondaryKey(getSecondary)
|
||||
{
|
||||
}
|
||||
|
||||
inline Key operator()(const Value &a) const
|
||||
{
|
||||
return Key(fGetPrimaryKey(a), fGetSecondaryKey(a));
|
||||
}
|
||||
|
||||
private:
|
||||
GetPrimaryKey fGetPrimaryKey;
|
||||
GetSecondaryKey fGetSecondaryKey;
|
||||
};
|
||||
|
||||
// for convenience
|
||||
#define TWO_KEY_AVL_TREE_TEMPLATE_LIST template<typename Value, \
|
||||
typename PrimaryKey, typename PrimaryKeyCompare, \
|
||||
typename GetPrimaryKey, typename SecondaryKey, typename Node, \
|
||||
typename SecondaryKeyCompare, typename GetSecondaryKey, \
|
||||
typename NodeAllocator, typename GetValue>
|
||||
#define TWO_KEY_AVL_TREE_CLASS_NAME TwoKeyAVLTree<Value, PrimaryKey, \
|
||||
PrimaryKeyCompare, GetPrimaryKey, SecondaryKey, Node, \
|
||||
SecondaryKeyCompare, GetSecondaryKey, NodeAllocator, GetValue>
|
||||
|
||||
// TwoKeyAVLTree
|
||||
template<typename Value, typename PrimaryKey,
|
||||
typename PrimaryKeyCompare, typename GetPrimaryKey,
|
||||
typename SecondaryKey = Value,
|
||||
typename Node = AVLTreeStandardNode<Value>,
|
||||
typename SecondaryKeyCompare = AVLTreeStandardCompare<SecondaryKey>,
|
||||
typename GetSecondaryKey = AVLTreeStandardGetKey<Value, SecondaryKey>,
|
||||
typename NodeAllocator = AVLTreeStandardNodeAllocator<Value, Node>,
|
||||
typename GetValue = AVLTreeStandardGetValue<Value, Node> >
|
||||
class TwoKeyAVLTree : private AVLTree<Value,
|
||||
TwoKeyAVLTreeKey<PrimaryKey, SecondaryKey>, Node,
|
||||
TwoKeyAVLTreeKeyCompare<PrimaryKey, SecondaryKey, PrimaryKeyCompare,
|
||||
SecondaryKeyCompare>,
|
||||
TwoKeyAVLTreeGetKey<Value, PrimaryKey, SecondaryKey, GetPrimaryKey,
|
||||
GetSecondaryKey>,
|
||||
NodeAllocator, GetValue> {
|
||||
private:
|
||||
typedef TwoKeyAVLTreeKey<PrimaryKey, SecondaryKey> Key;
|
||||
typedef TwoKeyAVLTreeKeyCompare<PrimaryKey, SecondaryKey,
|
||||
PrimaryKeyCompare, SecondaryKeyCompare>
|
||||
KeyCompare;
|
||||
typedef TwoKeyAVLTreeGetKey<Value, PrimaryKey, SecondaryKey, GetPrimaryKey,
|
||||
GetSecondaryKey>
|
||||
GetKey;
|
||||
typedef AVLTree<Value, Key, Node, KeyCompare, GetKey, NodeAllocator,
|
||||
GetValue> BaseClass;
|
||||
|
||||
public:
|
||||
TwoKeyAVLTree();
|
||||
TwoKeyAVLTree(const PrimaryKeyCompare &primaryCompare,
|
||||
const GetPrimaryKey &getPrimary,
|
||||
const SecondaryKeyCompare &secondaryCompare,
|
||||
const GetSecondaryKey &getSecondary,
|
||||
const NodeAllocator &allocator,
|
||||
const GetValue &getValue);
|
||||
~TwoKeyAVLTree();
|
||||
|
||||
inline int CountItems() const { return BaseClass::CountItems(); }
|
||||
|
||||
Value *FindFirst(const PrimaryKey &key, Iterator *iterator = NULL);
|
||||
Value *FindLast(const PrimaryKey &key, Iterator *iterator = NULL);
|
||||
inline Value *Find(const PrimaryKey &primaryKey,
|
||||
const SecondaryKey &secondaryKey,
|
||||
Iterator *iterator = NULL);
|
||||
|
||||
inline void GetIterator(Iterator *iterator, bool reverse = false);
|
||||
|
||||
inline status_t Insert(const Value &value, Iterator *iterator = NULL);
|
||||
inline status_t Remove(const PrimaryKey &primaryKey,
|
||||
const SecondaryKey &secondaryKey);
|
||||
inline void Remove(Iterator &iterator);
|
||||
|
||||
private:
|
||||
PrimaryKeyCompare fPrimaryKeyCompare;
|
||||
GetPrimaryKey fGetPrimaryKey;
|
||||
};
|
||||
|
||||
|
||||
// constructor
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::TwoKeyAVLTree()
|
||||
: BaseClass(KeyCompare(PrimaryKeyCompare(), SecondaryKeyCompare()),
|
||||
GetKey(GetPrimaryKey(), GetSecondaryKey()),
|
||||
NodeAllocator(), GetValue())
|
||||
{
|
||||
}
|
||||
|
||||
// constructor
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::TwoKeyAVLTree(
|
||||
const PrimaryKeyCompare &primaryCompare, const GetPrimaryKey &getPrimary,
|
||||
const SecondaryKeyCompare &secondaryCompare,
|
||||
const GetSecondaryKey &getSecondary, const NodeAllocator &allocator,
|
||||
const GetValue &getValue)
|
||||
: BaseClass(KeyCompare(primaryCompare, secondaryCompare),
|
||||
GetKey(getPrimary, getSecondary),
|
||||
allocator, getValue),
|
||||
fPrimaryKeyCompare(primaryCompare),
|
||||
fGetPrimaryKey(getPrimary)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::~TwoKeyAVLTree()
|
||||
{
|
||||
}
|
||||
|
||||
// FindFirst
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
Value *
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::FindFirst(const PrimaryKey &key,
|
||||
Iterator *iterator)
|
||||
{
|
||||
Node *node = fRoot;
|
||||
while (node) {
|
||||
int cmp = fPrimaryKeyCompare(key, fGetPrimaryKey(fGetValue(node)));
|
||||
if (cmp == 0) {
|
||||
// found a matching node, now get the left-most node with that key
|
||||
while (node->left && fPrimaryKeyCompare(key,
|
||||
fGetPrimaryKey(fGetValue(node->left))) == 0) {
|
||||
node = node->left;
|
||||
}
|
||||
if (iterator)
|
||||
_InitIterator(iterator, node);
|
||||
return &fGetValue(node);
|
||||
}
|
||||
if (cmp < 0)
|
||||
node = node->left;
|
||||
else
|
||||
node = node->right;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// FindLast
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
Value *
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::FindLast(const PrimaryKey &key,
|
||||
Iterator *iterator)
|
||||
{
|
||||
Node *node = fRoot;
|
||||
while (node) {
|
||||
int cmp = fPrimaryKeyCompare(key, fGetPrimaryKey(fGetValue(node)));
|
||||
if (cmp == 0) {
|
||||
// found a matching node, now get the right-most node with that key
|
||||
while (node->right && fPrimaryKeyCompare(key,
|
||||
fGetPrimaryKey(fGetValue(node->right))) == 0) {
|
||||
node = node->right;
|
||||
}
|
||||
if (iterator)
|
||||
_InitIterator(iterator, node);
|
||||
return &fGetValue(node);
|
||||
}
|
||||
if (cmp < 0)
|
||||
node = node->left;
|
||||
else
|
||||
node = node->right;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
Value *
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::Find(const PrimaryKey &primaryKey,
|
||||
const SecondaryKey &secondaryKey,
|
||||
Iterator *iterator)
|
||||
{
|
||||
return BaseClass::Find(Key(primaryKey, secondaryKey), iterator);
|
||||
}
|
||||
|
||||
// GetIterator
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
void
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::GetIterator(Iterator *iterator, bool reverse)
|
||||
{
|
||||
BaseClass::GetIterator(iterator, reverse);
|
||||
}
|
||||
|
||||
// Insert
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
status_t
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::Insert(const Value &value, Iterator *iterator)
|
||||
{
|
||||
return BaseClass::Insert(value, iterator);
|
||||
}
|
||||
|
||||
// Remove
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
status_t
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::Remove(const PrimaryKey &primaryKey,
|
||||
const SecondaryKey &secondaryKey)
|
||||
{
|
||||
return BaseClass::Remove(Key(primaryKey, secondaryKey));
|
||||
}
|
||||
|
||||
// Remove
|
||||
TWO_KEY_AVL_TREE_TEMPLATE_LIST
|
||||
void
|
||||
TWO_KEY_AVL_TREE_CLASS_NAME::Remove(Iterator &iterator)
|
||||
{
|
||||
BaseClass::Remove(iterator);
|
||||
}
|
||||
|
||||
#endif // TWO_KEY_AVL_TREE_H
|
||||
@@ -0,0 +1,895 @@
|
||||
// Volume.cpp
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// You can alternatively use *this file* under the terms of the the MIT
|
||||
// license included in this package.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Block.h"
|
||||
#include "BlockAllocator.h"
|
||||
#include "Debug.h"
|
||||
#include "Directory.h"
|
||||
#include "Entry.h"
|
||||
#include "EntryListener.h"
|
||||
#include "IndexDirectory.h"
|
||||
#include "Locking.h"
|
||||
#include "Misc.h"
|
||||
#include "NameIndex.h"
|
||||
#include "Node.h"
|
||||
#include "NodeChildTable.h"
|
||||
#include "NodeListener.h"
|
||||
#include "NodeTable.h"
|
||||
#include "TwoKeyAVLTree.h"
|
||||
#include "Volume.h"
|
||||
|
||||
// default block size
|
||||
static const off_t kDefaultBlockSize = 4096;
|
||||
|
||||
static const size_t kDefaultAreaSize = kDefaultBlockSize * 128;
|
||||
|
||||
// default volume name
|
||||
static const char *kDefaultVolumeName = "RAM FS";
|
||||
|
||||
// NodeListenerGetPrimaryKey
|
||||
class NodeListenerGetPrimaryKey {
|
||||
public:
|
||||
inline Node *operator()(const NodeListenerValue &a)
|
||||
{
|
||||
return a.node;
|
||||
}
|
||||
|
||||
inline Node *operator()(const NodeListenerValue &a) const
|
||||
{
|
||||
return a.node;
|
||||
}
|
||||
};
|
||||
|
||||
// NodeListenerGetSecondaryKey
|
||||
class NodeListenerGetSecondaryKey {
|
||||
public:
|
||||
inline NodeListener *operator()(const NodeListenerValue &a)
|
||||
{
|
||||
return a.listener;
|
||||
}
|
||||
|
||||
inline NodeListener *operator()(const NodeListenerValue &a) const
|
||||
{
|
||||
return a.listener;
|
||||
}
|
||||
};
|
||||
|
||||
// NodeListenerTree
|
||||
typedef TwoKeyAVLTree<NodeListenerValue, Node*,
|
||||
AVLTreeStandardCompare<Node*>,
|
||||
NodeListenerGetPrimaryKey, NodeListener*,
|
||||
AVLTreeStandardNode<NodeListenerValue>,
|
||||
AVLTreeStandardCompare<NodeListener*>,
|
||||
NodeListenerGetSecondaryKey > _NodeListenerTree;
|
||||
class NodeListenerTree : public _NodeListenerTree {};
|
||||
|
||||
// EntryListenerGetPrimaryKey
|
||||
class EntryListenerGetPrimaryKey {
|
||||
public:
|
||||
inline Entry *operator()(const EntryListenerValue &a)
|
||||
{
|
||||
return a.entry;
|
||||
}
|
||||
|
||||
inline Entry *operator()(const EntryListenerValue &a) const
|
||||
{
|
||||
return a.entry;
|
||||
}
|
||||
};
|
||||
|
||||
// EntryListenerGetSecondaryKey
|
||||
class EntryListenerGetSecondaryKey {
|
||||
public:
|
||||
inline EntryListener *operator()(const EntryListenerValue &a)
|
||||
{
|
||||
return a.listener;
|
||||
}
|
||||
|
||||
inline EntryListener *operator()(const EntryListenerValue &a) const
|
||||
{
|
||||
return a.listener;
|
||||
}
|
||||
};
|
||||
|
||||
// EntryListenerTree
|
||||
typedef TwoKeyAVLTree<EntryListenerValue, Entry*,
|
||||
AVLTreeStandardCompare<Entry*>,
|
||||
EntryListenerGetPrimaryKey, EntryListener*,
|
||||
AVLTreeStandardNode<EntryListenerValue>,
|
||||
AVLTreeStandardCompare<EntryListener*>,
|
||||
EntryListenerGetSecondaryKey > _EntryListenerTree;
|
||||
class EntryListenerTree : public _EntryListenerTree {};
|
||||
|
||||
|
||||
/*!
|
||||
\class Volume
|
||||
\brief Represents a volume.
|
||||
*/
|
||||
|
||||
// constructor
|
||||
Volume::Volume()
|
||||
: fID(0),
|
||||
fNextNodeID(kRootParentID + 1),
|
||||
fNodeTable(NULL),
|
||||
fDirectoryEntryTable(NULL),
|
||||
fNodeAttributeTable(NULL),
|
||||
fIndexDirectory(NULL),
|
||||
fRootDirectory(NULL),
|
||||
fName(kDefaultVolumeName),
|
||||
fLocker("volume"),
|
||||
fIteratorLocker("iterators"),
|
||||
fQueryLocker("queries"),
|
||||
fNodeListeners(NULL),
|
||||
fAnyNodeListeners(),
|
||||
fEntryListeners(NULL),
|
||||
fAnyEntryListeners(),
|
||||
fBlockAllocator(NULL),
|
||||
fBlockSize(kDefaultBlockSize),
|
||||
fAllocatedBlocks(0),
|
||||
fAccessTime(0),
|
||||
fMounted(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
Volume::~Volume()
|
||||
{
|
||||
Unmount();
|
||||
}
|
||||
|
||||
// Mount
|
||||
status_t
|
||||
Volume::Mount(nspace_id id)
|
||||
{
|
||||
Unmount();
|
||||
|
||||
// check the locker's semaphores
|
||||
if (fLocker.Sem() < 0)
|
||||
return fLocker.Sem();
|
||||
if (fIteratorLocker.Sem() < 0)
|
||||
return fIteratorLocker.Sem();
|
||||
if (fQueryLocker.Sem() < 0)
|
||||
return fQueryLocker.Sem();
|
||||
|
||||
status_t error = B_OK;
|
||||
fID = id;
|
||||
// create a block allocator
|
||||
if (error == B_OK) {
|
||||
fBlockAllocator = new(nothrow) BlockAllocator(kDefaultAreaSize);
|
||||
if (fBlockAllocator)
|
||||
error = fBlockAllocator->InitCheck();
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// create the listener trees
|
||||
if (error == B_OK) {
|
||||
fNodeListeners = new(nothrow) NodeListenerTree;
|
||||
if (!fNodeListeners)
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
if (error == B_OK) {
|
||||
fEntryListeners = new(nothrow) EntryListenerTree;
|
||||
if (!fEntryListeners)
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
// create the node table
|
||||
if (error == B_OK) {
|
||||
fNodeTable = new(nothrow) NodeTable;
|
||||
if (fNodeTable)
|
||||
error = fNodeTable->InitCheck();
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// create the directory entry table
|
||||
if (error == B_OK) {
|
||||
fDirectoryEntryTable = new(nothrow) DirectoryEntryTable;
|
||||
if (fDirectoryEntryTable)
|
||||
error = fDirectoryEntryTable->InitCheck();
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// create the node attribute table
|
||||
if (error == B_OK) {
|
||||
fNodeAttributeTable = new(nothrow) NodeAttributeTable;
|
||||
if (fNodeAttributeTable)
|
||||
error = fNodeAttributeTable->InitCheck();
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// create the index directory
|
||||
if (error == B_OK) {
|
||||
fIndexDirectory = new(nothrow) IndexDirectory(this);
|
||||
if (!fIndexDirectory)
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// create the root dir
|
||||
if (error == B_OK) {
|
||||
fRootDirectory = new(nothrow) Directory(this);
|
||||
if (fRootDirectory) {
|
||||
// set permissions: -rwxr-xr-x
|
||||
fRootDirectory->SetMode(
|
||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
error = fRootDirectory->Link(NULL);
|
||||
} else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
// set mounted flag / cleanup on error
|
||||
if (error == B_OK)
|
||||
fMounted = true;
|
||||
else
|
||||
Unmount();
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// Unmount
|
||||
status_t
|
||||
Volume::Unmount()
|
||||
{
|
||||
fMounted = false;
|
||||
// delete the root directory
|
||||
if (fRootDirectory) {
|
||||
// deleting the root directory destroys the complete hierarchy
|
||||
delete fRootDirectory;
|
||||
fRootDirectory = NULL;
|
||||
}
|
||||
// delete the index directory
|
||||
if (fIndexDirectory) {
|
||||
delete fIndexDirectory;
|
||||
fIndexDirectory = NULL;
|
||||
}
|
||||
// delete the listener trees
|
||||
if (fEntryListeners) {
|
||||
delete fEntryListeners;
|
||||
fEntryListeners = NULL;
|
||||
}
|
||||
if (fNodeListeners) {
|
||||
delete fNodeListeners;
|
||||
fNodeListeners = NULL;
|
||||
}
|
||||
// delete the tables
|
||||
if (fNodeAttributeTable) {
|
||||
delete fNodeAttributeTable;
|
||||
fNodeAttributeTable = NULL;
|
||||
}
|
||||
if (fDirectoryEntryTable) {
|
||||
delete fDirectoryEntryTable;
|
||||
fDirectoryEntryTable = NULL;
|
||||
}
|
||||
if (fNodeTable) {
|
||||
delete fNodeTable;
|
||||
fNodeTable = NULL;
|
||||
}
|
||||
// delete the block allocator
|
||||
if (fBlockAllocator) {
|
||||
delete fBlockAllocator;
|
||||
fBlockAllocator = NULL;
|
||||
}
|
||||
fID = 0;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// GetBlockSize
|
||||
off_t
|
||||
Volume::GetBlockSize() const
|
||||
{
|
||||
return fBlockSize;
|
||||
}
|
||||
|
||||
// CountBlocks
|
||||
off_t
|
||||
Volume::CountBlocks() const
|
||||
{
|
||||
size_t bytes = 0;
|
||||
system_info sysInfo;
|
||||
if (get_system_info(&sysInfo) == B_OK) {
|
||||
int32 freePages = sysInfo.max_pages - sysInfo.used_pages;
|
||||
bytes = (uint32)freePages * B_PAGE_SIZE
|
||||
+ fBlockAllocator->GetAvailableBytes();
|
||||
}
|
||||
return bytes / kDefaultBlockSize;
|
||||
}
|
||||
|
||||
// CountFreeBlocks
|
||||
off_t
|
||||
Volume::CountFreeBlocks() const
|
||||
{
|
||||
// TODO:...
|
||||
return CountBlocks() - fBlockAllocator->GetUsedBytes() / kDefaultBlockSize;
|
||||
}
|
||||
|
||||
// SetName
|
||||
status_t
|
||||
Volume::SetName(const char *name)
|
||||
{
|
||||
status_t error = (name ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
if (!fName.SetTo(name))
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetName
|
||||
const char *
|
||||
Volume::GetName() const
|
||||
{
|
||||
return fName.GetString();
|
||||
}
|
||||
|
||||
// NewVNode
|
||||
status_t
|
||||
Volume::NewVNode(Node *node)
|
||||
{
|
||||
status_t error = NodeAdded(node);
|
||||
if (error == B_OK) {
|
||||
error = new_vnode(GetID(), node->GetID(), node);
|
||||
if (error != B_OK)
|
||||
NodeRemoved(node);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetVNode
|
||||
status_t
|
||||
Volume::GetVNode(vnode_id id, Node **node)
|
||||
{
|
||||
return (fMounted ? get_vnode(GetID(), id, (void**)node) : B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// GetVNode
|
||||
status_t
|
||||
Volume::GetVNode(Node *node)
|
||||
{
|
||||
Node *dummy = NULL;
|
||||
status_t error = (fMounted ? GetVNode(node->GetID(), &dummy)
|
||||
: B_BAD_VALUE );
|
||||
if (error == B_OK && dummy != node) {
|
||||
FATAL(("Two Nodes have the same ID: %Ld!\n", node->GetID()));
|
||||
PutVNode(dummy);
|
||||
error = B_ERROR;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// PutVNode
|
||||
status_t
|
||||
Volume::PutVNode(vnode_id id)
|
||||
{
|
||||
return (fMounted ? put_vnode(GetID(), id) : B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// PutVNode
|
||||
status_t
|
||||
Volume::PutVNode(Node *node)
|
||||
{
|
||||
return (fMounted ? put_vnode(GetID(), node->GetID()) : B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// RemoveVNode
|
||||
status_t
|
||||
Volume::RemoveVNode(Node *node)
|
||||
{
|
||||
if (fMounted)
|
||||
return remove_vnode(GetID(), node->GetID());
|
||||
status_t error = NodeRemoved(node);
|
||||
if (error == B_OK)
|
||||
delete node;
|
||||
return error;
|
||||
}
|
||||
|
||||
// UnremoveVNode
|
||||
status_t
|
||||
Volume::UnremoveVNode(Node *node)
|
||||
{
|
||||
return (fMounted ? unremove_vnode(GetID(), node->GetID()) : B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// NodeAdded
|
||||
status_t
|
||||
Volume::NodeAdded(Node *node)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = fNodeTable->AddNode(node);
|
||||
// notify listeners
|
||||
if (error == B_OK) {
|
||||
// listeners interested in that node
|
||||
NodeListenerTree::Iterator it;
|
||||
if (fNodeListeners->FindFirst(node, &it)) {
|
||||
for (NodeListenerValue *value = it.GetCurrent();
|
||||
value && value->node == node;
|
||||
value = it.GetNext()) {
|
||||
if (value->flags & NODE_LISTEN_ADDED)
|
||||
value->listener->NodeAdded(node);
|
||||
}
|
||||
}
|
||||
// listeners interested in any node
|
||||
int32 count = fAnyNodeListeners.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
|
||||
if (value.flags & NODE_LISTEN_ADDED)
|
||||
value.listener->NodeAdded(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// NodeRemoved
|
||||
status_t
|
||||
Volume::NodeRemoved(Node *node)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = fNodeTable->RemoveNode(node);
|
||||
// notify listeners
|
||||
if (error == B_OK) {
|
||||
// listeners interested in that node
|
||||
NodeListenerTree::Iterator it;
|
||||
if (fNodeListeners->FindFirst(node, &it)) {
|
||||
for (NodeListenerValue *value = it.GetCurrent();
|
||||
value && value->node == node;
|
||||
value = it.GetNext()) {
|
||||
if (value->flags & NODE_LISTEN_REMOVED)
|
||||
value->listener->NodeRemoved(node);
|
||||
}
|
||||
}
|
||||
// listeners interested in any node
|
||||
int32 count = fAnyNodeListeners.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i);
|
||||
if (value.flags & NODE_LISTEN_REMOVED)
|
||||
value.listener->NodeRemoved(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindNode
|
||||
/*! \brief Finds the node identified by a vnode_id.
|
||||
|
||||
\note The method does not initialize the parent ID for non-directory nodes.
|
||||
|
||||
\param id ID of the node to be found.
|
||||
\param node pointer to a pre-allocated Node* to be set to the found node.
|
||||
\return \c B_OK, if everything went fine.
|
||||
*/
|
||||
status_t
|
||||
Volume::FindNode(vnode_id id, Node **node)
|
||||
{
|
||||
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
*node = fNodeTable->GetNode(id);
|
||||
if (!*node)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// AddNodeListener
|
||||
status_t
|
||||
Volume::AddNodeListener(NodeListener *listener, Node *node, uint32 flags)
|
||||
{
|
||||
// check parameters
|
||||
if (!listener || !node && !(flags & NODE_LISTEN_ANY_NODE)
|
||||
|| !(flags & NODE_LISTEN_ALL)) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
// add the listener to the right container
|
||||
status_t error = B_OK;
|
||||
NodeListenerValue value(listener, node, flags);
|
||||
if (flags & NODE_LISTEN_ANY_NODE) {
|
||||
if (!fAnyNodeListeners.AddItem(value))
|
||||
error = B_NO_MEMORY;
|
||||
} else
|
||||
error = fNodeListeners->Insert(value);
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveNodeListener
|
||||
status_t
|
||||
Volume::RemoveNodeListener(NodeListener *listener, Node *node)
|
||||
{
|
||||
if (!listener)
|
||||
return B_BAD_VALUE;
|
||||
status_t error = B_OK;
|
||||
if (node)
|
||||
error = fNodeListeners->Remove(node, listener);
|
||||
else {
|
||||
NodeListenerValue value(listener, node, 0);
|
||||
if (!fAnyNodeListeners.RemoveItem(value))
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// EntryAdded
|
||||
status_t
|
||||
Volume::EntryAdded(vnode_id id, Entry *entry)
|
||||
{
|
||||
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = fDirectoryEntryTable->AddNodeChild(id, entry);
|
||||
if (error == B_OK) {
|
||||
// notify listeners
|
||||
// listeners interested in that entry
|
||||
EntryListenerTree::Iterator it;
|
||||
if (fEntryListeners->FindFirst(entry, &it)) {
|
||||
for (EntryListenerValue *value = it.GetCurrent();
|
||||
value && value->entry == entry;
|
||||
value = it.GetNext()) {
|
||||
if (value->flags & ENTRY_LISTEN_ADDED)
|
||||
value->listener->EntryAdded(entry);
|
||||
}
|
||||
}
|
||||
// listeners interested in any entry
|
||||
int32 count = fAnyEntryListeners.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
|
||||
if (value.flags & ENTRY_LISTEN_ADDED)
|
||||
value.listener->EntryAdded(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// EntryRemoved
|
||||
status_t
|
||||
Volume::EntryRemoved(vnode_id id, Entry *entry)
|
||||
{
|
||||
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = fDirectoryEntryTable->RemoveNodeChild(id, entry);
|
||||
if (error == B_OK) {
|
||||
// notify listeners
|
||||
// listeners interested in that entry
|
||||
EntryListenerTree::Iterator it;
|
||||
if (fEntryListeners->FindFirst(entry, &it)) {
|
||||
for (EntryListenerValue *value = it.GetCurrent();
|
||||
value && value->entry == entry;
|
||||
value = it.GetNext()) {
|
||||
if (value->flags & ENTRY_LISTEN_REMOVED)
|
||||
value->listener->EntryRemoved(entry);
|
||||
}
|
||||
}
|
||||
// listeners interested in any entry
|
||||
int32 count = fAnyEntryListeners.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i);
|
||||
if (value.flags & ENTRY_LISTEN_REMOVED)
|
||||
value.listener->EntryRemoved(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindEntry
|
||||
status_t
|
||||
Volume::FindEntry(vnode_id id, const char *name, Entry **entry)
|
||||
{
|
||||
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
*entry = fDirectoryEntryTable->GetNodeChild(id, name);
|
||||
if (!*entry)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// AddEntryListener
|
||||
status_t
|
||||
Volume::AddEntryListener(EntryListener *listener, Entry *entry, uint32 flags)
|
||||
{
|
||||
// check parameters
|
||||
if (!listener || !entry && !(flags & ENTRY_LISTEN_ANY_ENTRY)
|
||||
|| !(flags & ENTRY_LISTEN_ALL)) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
// add the listener to the right container
|
||||
status_t error = B_OK;
|
||||
EntryListenerValue value(listener, entry, flags);
|
||||
if (flags & ENTRY_LISTEN_ANY_ENTRY) {
|
||||
if (!fAnyEntryListeners.AddItem(value))
|
||||
error = B_NO_MEMORY;
|
||||
} else
|
||||
error = fEntryListeners->Insert(value);
|
||||
return error;
|
||||
}
|
||||
|
||||
// RemoveEntryListener
|
||||
status_t
|
||||
Volume::RemoveEntryListener(EntryListener *listener, Entry *entry)
|
||||
{
|
||||
if (!listener)
|
||||
return B_BAD_VALUE;
|
||||
status_t error = B_OK;
|
||||
if (entry)
|
||||
error = fEntryListeners->Remove(entry, listener);
|
||||
else {
|
||||
EntryListenerValue value(listener, entry, 0);
|
||||
if (!fAnyEntryListeners.RemoveItem(value))
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// NodeAttributeAdded
|
||||
status_t
|
||||
Volume::NodeAttributeAdded(vnode_id id, Attribute *attribute)
|
||||
{
|
||||
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = fNodeAttributeTable->AddNodeChild(id, attribute);
|
||||
// notify the respective attribute index
|
||||
if (error == B_OK) {
|
||||
if (AttributeIndex *index = FindAttributeIndex(
|
||||
attribute->GetName(), attribute->GetType())) {
|
||||
index->Added(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// NodeAttributeRemoved
|
||||
status_t
|
||||
Volume::NodeAttributeRemoved(vnode_id id, Attribute *attribute)
|
||||
{
|
||||
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
error = fNodeAttributeTable->RemoveNodeChild(id, attribute);
|
||||
// notify the respective attribute index
|
||||
if (error == B_OK) {
|
||||
if (AttributeIndex *index = FindAttributeIndex(
|
||||
attribute->GetName(), attribute->GetType())) {
|
||||
index->Removed(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
// update live queries
|
||||
if (error == B_OK && attribute->GetNode()) {
|
||||
const uint8* oldKey;
|
||||
size_t oldLength;
|
||||
attribute->GetKey(&oldKey, &oldLength);
|
||||
UpdateLiveQueries(NULL, attribute->GetNode(), attribute->GetName(),
|
||||
attribute->GetType(), oldKey, oldLength, NULL, 0);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FindNodeAttribute
|
||||
status_t
|
||||
Volume::FindNodeAttribute(vnode_id id, const char *name, Attribute **attribute)
|
||||
{
|
||||
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
*attribute = fNodeAttributeTable->GetNodeChild(id, name);
|
||||
if (!*attribute)
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// GetNameIndex
|
||||
NameIndex *
|
||||
Volume::GetNameIndex() const
|
||||
{
|
||||
return (fIndexDirectory ? fIndexDirectory->GetNameIndex() : NULL);
|
||||
}
|
||||
|
||||
// GetLastModifiedIndex
|
||||
LastModifiedIndex *
|
||||
Volume::GetLastModifiedIndex() const
|
||||
{
|
||||
return (fIndexDirectory ? fIndexDirectory->GetLastModifiedIndex() : NULL);
|
||||
}
|
||||
|
||||
// GetSizeIndex
|
||||
SizeIndex *
|
||||
Volume::GetSizeIndex() const
|
||||
{
|
||||
return (fIndexDirectory ? fIndexDirectory->GetSizeIndex() : NULL);
|
||||
}
|
||||
|
||||
// FindIndex
|
||||
Index *
|
||||
Volume::FindIndex(const char *name)
|
||||
{
|
||||
return (fIndexDirectory ? fIndexDirectory->FindIndex(name) : NULL);
|
||||
}
|
||||
|
||||
// FindAttributeIndex
|
||||
AttributeIndex *
|
||||
Volume::FindAttributeIndex(const char *name, uint32 type)
|
||||
{
|
||||
return (fIndexDirectory
|
||||
? fIndexDirectory->FindAttributeIndex(name, type) : NULL);
|
||||
}
|
||||
|
||||
// AddQuery
|
||||
void
|
||||
Volume::AddQuery(Query *query)
|
||||
{
|
||||
AutoLocker<Locker> _(fQueryLocker);
|
||||
|
||||
if (query)
|
||||
fQueries.Insert(query);
|
||||
}
|
||||
|
||||
// RemoveQuery
|
||||
void
|
||||
Volume::RemoveQuery(Query *query)
|
||||
{
|
||||
AutoLocker<Locker> _(fQueryLocker);
|
||||
|
||||
if (query)
|
||||
fQueries.Remove(query);
|
||||
}
|
||||
|
||||
// UpdateLiveQueries
|
||||
void
|
||||
Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
|
||||
int32 type, const uint8 *oldKey, size_t oldLength, const uint8 *newKey,
|
||||
size_t newLength)
|
||||
{
|
||||
AutoLocker<Locker> _(fQueryLocker);
|
||||
|
||||
for (Query* query = fQueries.GetFirst();
|
||||
query;
|
||||
query = fQueries.GetNext(query)) {
|
||||
query->LiveUpdate(entry, node, attribute, type, oldKey, oldLength,
|
||||
newKey, newLength);
|
||||
}
|
||||
}
|
||||
|
||||
// AllocateBlock
|
||||
status_t
|
||||
Volume::AllocateBlock(size_t size, BlockReference **block)
|
||||
{
|
||||
status_t error = (size > 0 && size <= fBlockSize && block
|
||||
? B_OK : B_BAD_VALUE);
|
||||
if (error == B_OK) {
|
||||
*block = fBlockAllocator->AllocateBlock(size);
|
||||
if (*block)
|
||||
fAllocatedBlocks++;
|
||||
else
|
||||
SET_ERROR(error, B_NO_MEMORY);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// FreeBlock
|
||||
void
|
||||
Volume::FreeBlock(BlockReference *block)
|
||||
{
|
||||
if (block) {
|
||||
fBlockAllocator->FreeBlock(block);
|
||||
fAllocatedBlocks--;
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeBlock
|
||||
BlockReference *
|
||||
Volume::ResizeBlock(BlockReference *block, size_t size)
|
||||
{
|
||||
BlockReference *newBlock = NULL;
|
||||
if (size <= fBlockSize && block) {
|
||||
if (size == 0) {
|
||||
fBlockAllocator->FreeBlock(block);
|
||||
fAllocatedBlocks--;
|
||||
} else
|
||||
newBlock = fBlockAllocator->ResizeBlock(block, size);
|
||||
}
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
// CheckBlock
|
||||
bool
|
||||
Volume::CheckBlock(BlockReference *block, size_t size)
|
||||
{
|
||||
return fBlockAllocator->CheckBlock(block, size);
|
||||
}
|
||||
|
||||
// GetAllocationInfo
|
||||
void
|
||||
Volume::GetAllocationInfo(AllocationInfo &info)
|
||||
{
|
||||
// tables
|
||||
info.AddOtherAllocation(sizeof(NodeTable));
|
||||
fNodeTable->GetAllocationInfo(info);
|
||||
info.AddOtherAllocation(sizeof(DirectoryEntryTable));
|
||||
fDirectoryEntryTable->GetAllocationInfo(info);
|
||||
info.AddOtherAllocation(sizeof(NodeAttributeTable));
|
||||
fNodeAttributeTable->GetAllocationInfo(info);
|
||||
// node hierarchy
|
||||
fRootDirectory->GetAllocationInfo(info);
|
||||
// name
|
||||
info.AddStringAllocation(fName.GetLength());
|
||||
// block allocator
|
||||
info.AddOtherAllocation(sizeof(BlockAllocator));
|
||||
fBlockAllocator->GetAllocationInfo(info);
|
||||
}
|
||||
|
||||
// ReadLock
|
||||
bool
|
||||
Volume::ReadLock()
|
||||
{
|
||||
bool alreadyLocked = fLocker.IsLocked();
|
||||
if (fLocker.Lock()) {
|
||||
if (!alreadyLocked)
|
||||
fAccessTime = system_time();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReadUnlock
|
||||
void
|
||||
Volume::ReadUnlock()
|
||||
{
|
||||
fLocker.Unlock();
|
||||
}
|
||||
|
||||
// WriteLock
|
||||
bool
|
||||
Volume::WriteLock()
|
||||
{
|
||||
bool alreadyLocked = fLocker.IsLocked();
|
||||
if (fLocker.Lock()) {
|
||||
if (!alreadyLocked)
|
||||
fAccessTime = system_time();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// WriteUnlock
|
||||
void
|
||||
Volume::WriteUnlock()
|
||||
{
|
||||
fLocker.Unlock();
|
||||
}
|
||||
|
||||
// IteratorLock
|
||||
bool
|
||||
Volume::IteratorLock()
|
||||
{
|
||||
return fIteratorLocker.Lock();
|
||||
}
|
||||
|
||||
// IteratorUnlock
|
||||
void
|
||||
Volume::IteratorUnlock()
|
||||
{
|
||||
fIteratorLocker.Unlock();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// Volume.h
|
||||
//
|
||||
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// You can alternatively use *this file* under the terms of the the MIT
|
||||
// license included in this package.
|
||||
|
||||
#ifndef VOLUME_H
|
||||
#define VOLUME_H
|
||||
|
||||
#include <fsproto.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "DLList.h"
|
||||
#include "Entry.h"
|
||||
#include "List.h"
|
||||
#include "Locker.h"
|
||||
#include "Query.h"
|
||||
#include "String.h"
|
||||
|
||||
class AllocationInfo;
|
||||
class Block;
|
||||
class BlockAllocator;
|
||||
class BlockReference;
|
||||
class Directory;
|
||||
class DirectoryEntryTable;
|
||||
class Entry;
|
||||
class EntryListener;
|
||||
class EntryListenerTree;
|
||||
class Index;
|
||||
class IndexDirectory;
|
||||
class LastModifiedIndex;
|
||||
class NameIndex;
|
||||
class Node;
|
||||
class NodeAttributeTable;
|
||||
class NodeListener;
|
||||
class NodeListenerTree;
|
||||
class NodeTable;
|
||||
class SizeIndex;
|
||||
|
||||
const vnode_id kRootParentID = 0;
|
||||
|
||||
// NodeListenerValue
|
||||
class NodeListenerValue {
|
||||
public:
|
||||
inline NodeListenerValue(int) {}
|
||||
inline NodeListenerValue(NodeListener *listener, Node *node, uint32 flags)
|
||||
: listener(listener), node(node), flags(flags) {}
|
||||
|
||||
inline bool operator==(const NodeListenerValue &other)
|
||||
{ return listener == other.listener; }
|
||||
|
||||
NodeListener *listener;
|
||||
Node *node;
|
||||
uint32 flags;
|
||||
};
|
||||
typedef List<NodeListenerValue> NodeListenerList;
|
||||
|
||||
// EntryListenerValue
|
||||
class EntryListenerValue {
|
||||
public:
|
||||
inline EntryListenerValue(int) {}
|
||||
inline EntryListenerValue(EntryListener *listener, Entry *entry,
|
||||
uint32 flags)
|
||||
: listener(listener), entry(entry), flags(flags) {}
|
||||
|
||||
inline bool operator==(const EntryListenerValue &other)
|
||||
{ return listener == other.listener; }
|
||||
|
||||
EntryListener *listener;
|
||||
Entry *entry;
|
||||
uint32 flags;
|
||||
};
|
||||
typedef List<EntryListenerValue> EntryListenerList;
|
||||
|
||||
// Volume
|
||||
class Volume {
|
||||
public:
|
||||
Volume();
|
||||
~Volume();
|
||||
|
||||
status_t Mount(nspace_id nsid);
|
||||
status_t Unmount();
|
||||
|
||||
nspace_id GetID() const { return fID; }
|
||||
|
||||
off_t GetBlockSize() const;
|
||||
off_t CountBlocks() const;
|
||||
off_t CountFreeBlocks() const;
|
||||
|
||||
status_t SetName(const char *name);
|
||||
const char *GetName() const;
|
||||
|
||||
Directory *GetRootDirectory() const { return fRootDirectory; }
|
||||
|
||||
status_t NewVNode(Node *node);
|
||||
status_t GetVNode(vnode_id id, Node **node);
|
||||
status_t GetVNode(Node *node);
|
||||
status_t PutVNode(vnode_id id);
|
||||
status_t PutVNode(Node *node);
|
||||
status_t RemoveVNode(Node *node);
|
||||
status_t UnremoveVNode(Node *node);
|
||||
|
||||
// node table and listeners
|
||||
status_t NodeAdded(Node *node);
|
||||
status_t NodeRemoved(Node *node);
|
||||
status_t FindNode(vnode_id id, Node **node);
|
||||
status_t AddNodeListener(NodeListener *listener, Node *node,
|
||||
uint32 flags);
|
||||
status_t RemoveNodeListener(NodeListener *listener, Node *node);
|
||||
|
||||
// entry table and listeners
|
||||
status_t EntryAdded(vnode_id id, Entry *entry);
|
||||
status_t EntryRemoved(vnode_id id, Entry *entry);
|
||||
status_t FindEntry(vnode_id id, const char *name, Entry **entry);
|
||||
status_t AddEntryListener(EntryListener *listener, Entry *entry,
|
||||
uint32 flags);
|
||||
status_t RemoveEntryListener(EntryListener *listener, Entry *entry);
|
||||
|
||||
// node attribute table
|
||||
status_t NodeAttributeAdded(vnode_id id, Attribute *attribute);
|
||||
status_t NodeAttributeRemoved(vnode_id id, Attribute *attribute);
|
||||
status_t FindNodeAttribute(vnode_id id, const char *name,
|
||||
Attribute **attribute);
|
||||
|
||||
// indices
|
||||
IndexDirectory *GetIndexDirectory() const { return fIndexDirectory; }
|
||||
NameIndex *GetNameIndex() const;
|
||||
LastModifiedIndex *GetLastModifiedIndex() const;
|
||||
SizeIndex *GetSizeIndex() const;
|
||||
Index *FindIndex(const char *name);
|
||||
AttributeIndex *FindAttributeIndex(const char *name, uint32 type);
|
||||
|
||||
// queries
|
||||
void AddQuery(Query *query);
|
||||
void RemoveQuery(Query *query);
|
||||
void UpdateLiveQueries(Entry *entry, Node* node, const char *attribute,
|
||||
int32 type, const uint8 *oldKey, size_t oldLength,
|
||||
const uint8 *newKey, size_t newLength);
|
||||
|
||||
vnode_id NextNodeID() { return fNextNodeID++; }
|
||||
|
||||
status_t AllocateBlock(size_t size, BlockReference **block);
|
||||
void FreeBlock(BlockReference *block);
|
||||
BlockReference *ResizeBlock(BlockReference *block, size_t size);
|
||||
// debugging only
|
||||
bool CheckBlock(BlockReference *block, size_t size = 0);
|
||||
void GetAllocationInfo(AllocationInfo &info);
|
||||
|
||||
bigtime_t GetAccessTime() const { return fAccessTime; }
|
||||
|
||||
// locking
|
||||
bool ReadLock();
|
||||
void ReadUnlock();
|
||||
bool WriteLock();
|
||||
void WriteUnlock();
|
||||
|
||||
bool IteratorLock();
|
||||
void IteratorUnlock();
|
||||
|
||||
private:
|
||||
typedef DLList<Query> QueryList;
|
||||
|
||||
nspace_id fID;
|
||||
vnode_id fNextNodeID;
|
||||
NodeTable *fNodeTable;
|
||||
DirectoryEntryTable *fDirectoryEntryTable;
|
||||
NodeAttributeTable *fNodeAttributeTable;
|
||||
IndexDirectory *fIndexDirectory;
|
||||
Directory *fRootDirectory;
|
||||
String fName;
|
||||
Locker fLocker;
|
||||
Locker fIteratorLocker;
|
||||
Locker fQueryLocker;
|
||||
NodeListenerTree *fNodeListeners;
|
||||
NodeListenerList fAnyNodeListeners;
|
||||
EntryListenerTree *fEntryListeners;
|
||||
EntryListenerList fAnyEntryListeners;
|
||||
QueryList fQueries;
|
||||
BlockAllocator *fBlockAllocator;
|
||||
off_t fBlockSize;
|
||||
off_t fAllocatedBlocks;
|
||||
bigtime_t fAccessTime;
|
||||
bool fMounted;
|
||||
};
|
||||
|
||||
#endif // VOLUME_H
|
||||
@@ -0,0 +1,21 @@
|
||||
/* cpp - C++ in the kernel
|
||||
**
|
||||
** Initial version by Axel Dörfler, axeld@pinc-software.de
|
||||
** This file may be used under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
|
||||
#include "cpp.h"
|
||||
|
||||
|
||||
//const struct nothrow_t nothrow = {};
|
||||
|
||||
//extern "C" void __pure_virtual()
|
||||
//{
|
||||
//printf("pure virtual function call");
|
||||
//}
|
||||
|
||||
int stderr;
|
||||
|
||||
extern "C" int fprintf() { return 0; }
|
||||
extern "C" void abort() {}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef CPP_H
|
||||
#define CPP_H
|
||||
/* cpp - C++ in the kernel
|
||||
**
|
||||
** Initial version by Axel Dörfler, axeld@pinc-software.de
|
||||
** This file may be used under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
// Oh no! C++ in the kernel! Are you nuts?
|
||||
//
|
||||
// - no exceptions
|
||||
// - (almost) no virtuals (well, the Query code now uses them)
|
||||
// - it's basically only the C++ syntax, and type checking
|
||||
// - since one tend to encapsulate everything in classes, it has a slightly
|
||||
// higher memory overhead
|
||||
// - nicer code
|
||||
// - easier to maintain
|
||||
|
||||
|
||||
inline void *operator new(size_t size, const nothrow_t&) throw()
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
inline void *operator new[](size_t size, const nothrow_t&) throw()
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
inline void operator delete(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
inline void operator delete[](void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
// now we're using virtuals
|
||||
extern "C" void __pure_virtual();
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif /* CPP_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
## BeOS Generic Makefile v2.2 ##
|
||||
|
||||
## Fill in this file to specify the project being created, and the referenced
|
||||
## makefile-engine will do all of the hard work for you. This handles both
|
||||
## Intel and PowerPC builds of the BeOS.
|
||||
|
||||
## Application Specific Settings ---------------------------------------------
|
||||
|
||||
# specify the name of the binary
|
||||
NAME= ../ramfs
|
||||
|
||||
# specify the type of binary
|
||||
# APP: Application
|
||||
# SHARED: Shared library or add-on
|
||||
# STATIC: Static library archive
|
||||
# DRIVER: Kernel Driver
|
||||
TYPE= DRIVER
|
||||
|
||||
# add support for new Pe and Eddie features
|
||||
# to fill in generic makefile
|
||||
|
||||
#%{
|
||||
# @src->@
|
||||
|
||||
# specify the source files to use
|
||||
# full paths or paths relative to the makefile can be included
|
||||
# all files, regardless of directory, will have their object
|
||||
# files created in the common object directory.
|
||||
# Note that this means this makefile will not work correctly
|
||||
# if two source files with the same name (source.c or source.cpp)
|
||||
# are included from different directories. Also note that spaces
|
||||
# in folder names do not work well with this makefile.
|
||||
SRCS= AllocationInfo.cpp AreaUtils.cpp Attribute.cpp AttributeIndex.cpp \
|
||||
AttributeIndexImpl.cpp AttributeIterator.cpp \
|
||||
BlockAllocator.cpp BlockAllocatorArea.cpp BlockAllocatorAreaBucket.cpp \
|
||||
BlockReferenceManager.cpp DataContainer.cpp Debug.cpp \
|
||||
Directory.cpp Entry.cpp EntryIterator.cpp EntryListener.cpp File.cpp \
|
||||
Index.cpp IndexDirectory.cpp LastModifiedIndex.cpp Locker.cpp \
|
||||
NameIndex.cpp Node.cpp \
|
||||
NodeListener.cpp \
|
||||
NodeTable.cpp Query.cpp SizeIndex.cpp String.cpp SymLink.cpp Volume.cpp \
|
||||
cpp.cpp kernel_interface.cpp
|
||||
|
||||
# specify the resource files to use
|
||||
# full path or a relative path to the resource file can be used.
|
||||
RSRCS=
|
||||
|
||||
# @<-src@
|
||||
#%}
|
||||
|
||||
# end support for Pe and Eddie
|
||||
|
||||
# specify additional libraries to link against
|
||||
# there are two acceptable forms of library specifications
|
||||
# - if your library follows the naming pattern of:
|
||||
# libXXX.so or libXXX.a you can simply specify XXX
|
||||
# library: libbe.so entry: be
|
||||
#
|
||||
# - if your library does not follow the standard library
|
||||
# naming scheme you need to specify the path to the library
|
||||
# and it's name
|
||||
# library: my_lib.a entry: my_lib.a or path/my_lib.a
|
||||
LIBS= /boot/develop/tools/gnupro/lib/gcc-lib/i586-beos/2.9-beos-000224/libgcc.a
|
||||
# specify additional paths to directories following the standard
|
||||
# libXXX.so or libXXX.a naming scheme. You can specify full paths
|
||||
# or paths relative to the makefile. The paths included may not
|
||||
# be recursive, so include all of the paths where libraries can
|
||||
# be found. Directories where source files are found are
|
||||
# automatically included.
|
||||
LIBPATHS=
|
||||
|
||||
# additional paths to look for system headers
|
||||
# thes use the form: #include <header>
|
||||
# source file directories are NOT auto-included here
|
||||
SYSTEM_INCLUDE_PATHS =
|
||||
|
||||
# additional paths to look for local headers
|
||||
# thes use the form: #include "header"
|
||||
# source file directories are automatically included
|
||||
LOCAL_INCLUDE_PATHS =
|
||||
|
||||
# specify the level of optimization that you desire
|
||||
# NONE, SOME, FULL
|
||||
OPTIMIZE= NONE
|
||||
|
||||
# specify any preprocessor symbols to be defined. The symbols will not
|
||||
# have their values set automatically; you must supply the value (if any)
|
||||
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
|
||||
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
|
||||
# would pass "-DDEBUG" on the compiler's command line.
|
||||
#
|
||||
# USER - [0/1] userland build
|
||||
# DEBUG - [0/1] Enable debugging.
|
||||
# DBG_PRINT - [0/1] Print debug output to file (only if DEBUG).
|
||||
# DBG_PRINT_FILE - [0/1] Name of debug output file
|
||||
# (only if DBG_PRINT).
|
||||
#
|
||||
DEFINES= USER=0 \
|
||||
DEBUG=1 \
|
||||
DEBUG_PRINT=1 \
|
||||
DEBUG_PRINT_FILE=\"/var/log/ramfs.log\" \
|
||||
B_BAD_DATA=B_ERROR
|
||||
|
||||
# specify special warning levels
|
||||
# if unspecified default warnings will be used
|
||||
# NONE = supress all warnings
|
||||
# ALL = enable all warnings
|
||||
WARNINGS = ALL
|
||||
|
||||
# specify whether image symbols will be created
|
||||
# so that stack crawls in the debugger are meaningful
|
||||
# if TRUE symbols will be created
|
||||
SYMBOLS = TRUE
|
||||
|
||||
# specify debug settings
|
||||
# if TRUE will allow application to be run from a source-level
|
||||
# debugger. Note that this will disable all optimzation.
|
||||
DEBUGGER = TRUE
|
||||
|
||||
# specify additional compiler flags for all files
|
||||
#COMPILER_FLAGS = -include "cpp.h" -fno-exceptions -fno-rtti \
|
||||
|
||||
COMPILER_FLAGS = -include "cpp.h" -fno-exceptions \
|
||||
-Wmissing-prototypes -Woverloaded-virtual \
|
||||
-Wpointer-arith -Wcast-align -Wsign-compare
|
||||
|
||||
# specify additional linker flags
|
||||
LINKER_FLAGS =
|
||||
|
||||
# specify the version of this particular item
|
||||
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
|
||||
# This may also be specified in a resource.
|
||||
APP_VERSION =
|
||||
|
||||
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
|
||||
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
|
||||
# instruct the driverinstall rule to place a symlink to your driver's binary in
|
||||
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
|
||||
# /dev/video/usb when loaded. Default is "misc".
|
||||
DRIVER_PATH =
|
||||
|
||||
## include the makefile-engine
|
||||
include $(BUILDHOME)/etc/makefile-engine
|
||||
@@ -0,0 +1,10 @@
|
||||
// ramfs.h
|
||||
|
||||
#ifndef RAM_FS_H
|
||||
#define RAM_FS_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
const size_t kMaxIndexKeyLength = 256;
|
||||
|
||||
#endif // RAM_FS_H
|
||||
@@ -0,0 +1,15 @@
|
||||
// ramfs_ioctl.h
|
||||
|
||||
#ifndef RAMFS_IOCTL_H
|
||||
#define RAMFS_IOCTL_H
|
||||
|
||||
#include <Drivers.h>
|
||||
|
||||
#define RAMFS_IOCTL_BASE (B_DEVICE_OP_CODES_END + 10001)
|
||||
|
||||
enum {
|
||||
RAMFS_IOCTL_GET_ALLOCATION_INFO = RAMFS_IOCTL_BASE, // AllocationInfo*
|
||||
RAMFS_IOCTL_DUMP_INDEX, // const char* name
|
||||
};
|
||||
|
||||
#endif // RAMFS_IOCTL_H
|
||||
Reference in New Issue
Block a user