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:
Ingo Weinhold
2007-03-07 06:22:18 +00:00
parent 4b997aa15a
commit 224e7c4269
73 changed files with 15977 additions and 0 deletions
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