Mmh, apparently I have to check the copied directory itself in first.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20345 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,236 @@
|
|||||||
|
// AllocationInfo.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
|
#include "Attribute.h"
|
||||||
|
#include "Directory.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "File.h"
|
||||||
|
#include "SymLink.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AllocationInfo::AllocationInfo()
|
||||||
|
: fNodeTableArraySize(0),
|
||||||
|
fNodeTableVectorSize(0),
|
||||||
|
fNodeTableElementCount(0),
|
||||||
|
fDirectoryEntryTableArraySize(0),
|
||||||
|
fDirectoryEntryTableVectorSize(0),
|
||||||
|
fDirectoryEntryTableElementCount(0),
|
||||||
|
fNodeAttributeTableArraySize(0),
|
||||||
|
fNodeAttributeTableVectorSize(0),
|
||||||
|
fNodeAttributeTableElementCount(0),
|
||||||
|
|
||||||
|
fAttributeCount(0),
|
||||||
|
fAttributeSize(0),
|
||||||
|
fDirectoryCount(0),
|
||||||
|
fEntryCount(0),
|
||||||
|
fFileCount(0),
|
||||||
|
fFileSize(0),
|
||||||
|
fSymLinkCount(0),
|
||||||
|
fSymLinkSize(0),
|
||||||
|
|
||||||
|
fAreaCount(0),
|
||||||
|
fAreaSize(0),
|
||||||
|
fBlockCount(0),
|
||||||
|
fBlockSize(0),
|
||||||
|
fListCount(0),
|
||||||
|
fListSize(0),
|
||||||
|
fOtherCount(0),
|
||||||
|
fOtherSize(0),
|
||||||
|
fStringCount(0),
|
||||||
|
fStringSize(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
AllocationInfo::~AllocationInfo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodeTableAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddNodeTableAllocation(size_t arraySize, size_t vectorSize,
|
||||||
|
size_t elementSize, size_t elementCount)
|
||||||
|
{
|
||||||
|
fNodeTableArraySize += arraySize;
|
||||||
|
fNodeTableVectorSize += vectorSize * elementSize;
|
||||||
|
fNodeTableElementCount += elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDirectoryEntryTableAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddDirectoryEntryTableAllocation(size_t arraySize,
|
||||||
|
size_t vectorSize,
|
||||||
|
size_t elementSize,
|
||||||
|
size_t elementCount)
|
||||||
|
{
|
||||||
|
fDirectoryEntryTableArraySize += arraySize;
|
||||||
|
fDirectoryEntryTableVectorSize += vectorSize * elementSize;
|
||||||
|
fDirectoryEntryTableElementCount += elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodeAttributeTableAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddNodeAttributeTableAllocation(size_t arraySize,
|
||||||
|
size_t vectorSize,
|
||||||
|
size_t elementSize,
|
||||||
|
size_t elementCount)
|
||||||
|
{
|
||||||
|
fNodeAttributeTableArraySize += arraySize;
|
||||||
|
fNodeAttributeTableVectorSize += vectorSize * elementSize;
|
||||||
|
fNodeAttributeTableElementCount += elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAttributeAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddAttributeAllocation(size_t size)
|
||||||
|
{
|
||||||
|
fAttributeCount++;
|
||||||
|
fAttributeSize += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDirectoryAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddDirectoryAllocation()
|
||||||
|
{
|
||||||
|
fDirectoryCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEntryAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddEntryAllocation()
|
||||||
|
{
|
||||||
|
fEntryCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFileAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddFileAllocation(size_t size)
|
||||||
|
{
|
||||||
|
fFileCount++;
|
||||||
|
fFileSize += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSymLinkAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddSymLinkAllocation(size_t size)
|
||||||
|
{
|
||||||
|
fSymLinkCount++;
|
||||||
|
fSymLinkSize += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAreaAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddAreaAllocation(size_t size, size_t count)
|
||||||
|
{
|
||||||
|
fAreaCount += count;
|
||||||
|
fAreaSize += count * size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBlockAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddBlockAllocation(size_t size)
|
||||||
|
{
|
||||||
|
fBlockCount++;
|
||||||
|
fBlockSize += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddListAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddListAllocation(size_t capacity, size_t elementSize)
|
||||||
|
{
|
||||||
|
fListCount += 1;
|
||||||
|
fListSize += capacity * elementSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOtherAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddOtherAllocation(size_t size, size_t count)
|
||||||
|
{
|
||||||
|
fOtherCount += count;
|
||||||
|
fOtherSize += size * count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddStringAllocation
|
||||||
|
void
|
||||||
|
AllocationInfo::AddStringAllocation(size_t size)
|
||||||
|
{
|
||||||
|
fStringCount++;
|
||||||
|
fStringSize += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump
|
||||||
|
void
|
||||||
|
AllocationInfo::Dump() const
|
||||||
|
{
|
||||||
|
size_t heapCount = 0;
|
||||||
|
size_t heapSize = 0;
|
||||||
|
size_t areaCount = 0;
|
||||||
|
size_t areaSize = 0;
|
||||||
|
|
||||||
|
PRINT((" node table:\n"));
|
||||||
|
PRINT((" array size: %9lu\n", fNodeTableArraySize));
|
||||||
|
PRINT((" vector size: %9lu\n", fNodeTableVectorSize));
|
||||||
|
PRINT((" elements: %9lu\n", fNodeTableElementCount));
|
||||||
|
areaCount += 2;
|
||||||
|
areaSize += fNodeTableArraySize * sizeof(int32) + fNodeTableVectorSize;
|
||||||
|
|
||||||
|
PRINT((" entry table:\n"));
|
||||||
|
PRINT((" array size: %9lu\n", fDirectoryEntryTableArraySize));
|
||||||
|
PRINT((" vector size: %9lu\n", fDirectoryEntryTableVectorSize));
|
||||||
|
PRINT((" elements: %9lu\n", fDirectoryEntryTableElementCount));
|
||||||
|
areaCount += 2;
|
||||||
|
areaSize += fDirectoryEntryTableArraySize * sizeof(int32)
|
||||||
|
+ fDirectoryEntryTableVectorSize;
|
||||||
|
|
||||||
|
PRINT((" attribute table:\n"));
|
||||||
|
PRINT((" array size: %9lu\n", fNodeAttributeTableArraySize));
|
||||||
|
PRINT((" vector size: %9lu\n", fNodeAttributeTableVectorSize));
|
||||||
|
PRINT((" elements: %9lu\n", fNodeAttributeTableElementCount));
|
||||||
|
areaCount += 2;
|
||||||
|
areaSize += fNodeAttributeTableArraySize * sizeof(int32)
|
||||||
|
+ fNodeAttributeTableVectorSize;
|
||||||
|
|
||||||
|
PRINT((" attributes: %9lu, size: %9lu\n", fAttributeCount, fAttributeSize));
|
||||||
|
heapCount += fAttributeCount;
|
||||||
|
heapSize += fAttributeCount * sizeof(Attribute);
|
||||||
|
|
||||||
|
PRINT((" directories: %9lu\n", fDirectoryCount));
|
||||||
|
heapCount += fDirectoryCount;
|
||||||
|
heapSize += fDirectoryCount * sizeof(Directory);
|
||||||
|
|
||||||
|
PRINT((" entries: %9lu\n", fEntryCount));
|
||||||
|
heapCount += fEntryCount;
|
||||||
|
heapSize += fEntryCount * sizeof(Entry);
|
||||||
|
|
||||||
|
PRINT((" files: %9lu, size: %9lu\n", fFileCount, fFileSize));
|
||||||
|
heapCount += fFileCount;
|
||||||
|
heapSize += fFileCount * sizeof(File);
|
||||||
|
|
||||||
|
PRINT((" symlinks: %9lu, size: %9lu\n", fSymLinkCount, fSymLinkSize));
|
||||||
|
heapCount += fSymLinkCount;
|
||||||
|
heapSize += fSymLinkCount * sizeof(SymLink);
|
||||||
|
|
||||||
|
PRINT((" areas: %9lu, size: %9lu\n", fAreaCount, fAreaSize));
|
||||||
|
areaCount += fAreaCount;
|
||||||
|
areaSize += fAreaSize;
|
||||||
|
|
||||||
|
PRINT((" blocks: %9lu, size: %9lu\n", fBlockCount, fBlockSize));
|
||||||
|
|
||||||
|
PRINT((" lists: %9lu, size: %9lu\n", fListCount, fListSize));
|
||||||
|
heapCount += fListCount;
|
||||||
|
heapSize += fListSize;
|
||||||
|
|
||||||
|
PRINT((" other: %9lu, size: %9lu\n", fOtherCount, fOtherSize));
|
||||||
|
heapCount += fOtherCount;
|
||||||
|
heapSize += fOtherSize;
|
||||||
|
|
||||||
|
PRINT((" strings: %9lu, size: %9lu\n", fStringCount, fStringSize));
|
||||||
|
heapCount += fStringCount;
|
||||||
|
heapSize += fStringSize;
|
||||||
|
|
||||||
|
PRINT(("heap: %9lu allocations, size: %9lu\n", heapCount, heapSize));
|
||||||
|
PRINT(("areas: %9lu allocations, size: %9lu\n", areaCount, areaSize));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// AllocationInfo.h
|
||||||
|
|
||||||
|
#ifndef ALLOCATION_INFO_H
|
||||||
|
#define ALLOCATION_INFO_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
class AllocationInfo {
|
||||||
|
public:
|
||||||
|
AllocationInfo();
|
||||||
|
~AllocationInfo();
|
||||||
|
|
||||||
|
void AddNodeTableAllocation(size_t arraySize, size_t vectorSize,
|
||||||
|
size_t elementSize, size_t elementCount);
|
||||||
|
void AddDirectoryEntryTableAllocation(size_t arraySize, size_t vectorSize,
|
||||||
|
size_t elementSize,
|
||||||
|
size_t elementCount);
|
||||||
|
void AddNodeAttributeTableAllocation(size_t arraySize, size_t vectorSize,
|
||||||
|
size_t elementSize,
|
||||||
|
size_t elementCount);
|
||||||
|
|
||||||
|
void AddAttributeAllocation(size_t size);
|
||||||
|
void AddDirectoryAllocation();
|
||||||
|
void AddEntryAllocation();
|
||||||
|
void AddFileAllocation(size_t size);
|
||||||
|
void AddSymLinkAllocation(size_t size);
|
||||||
|
|
||||||
|
void AddAreaAllocation(size_t size, size_t count = 1);
|
||||||
|
void AddBlockAllocation(size_t size);
|
||||||
|
void AddListAllocation(size_t capacity, size_t elementSize);
|
||||||
|
void AddOtherAllocation(size_t size, size_t count = 1);
|
||||||
|
void AddStringAllocation(size_t size);
|
||||||
|
|
||||||
|
void Dump() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t fNodeTableArraySize;
|
||||||
|
size_t fNodeTableVectorSize;
|
||||||
|
size_t fNodeTableElementCount;
|
||||||
|
size_t fDirectoryEntryTableArraySize;
|
||||||
|
size_t fDirectoryEntryTableVectorSize;
|
||||||
|
size_t fDirectoryEntryTableElementCount;
|
||||||
|
size_t fNodeAttributeTableArraySize;
|
||||||
|
size_t fNodeAttributeTableVectorSize;
|
||||||
|
size_t fNodeAttributeTableElementCount;
|
||||||
|
|
||||||
|
size_t fAttributeCount;
|
||||||
|
size_t fAttributeSize;
|
||||||
|
size_t fDirectoryCount;
|
||||||
|
size_t fEntryCount;
|
||||||
|
size_t fFileCount;
|
||||||
|
size_t fFileSize;
|
||||||
|
size_t fSymLinkCount;
|
||||||
|
size_t fSymLinkSize;
|
||||||
|
|
||||||
|
size_t fAreaCount;
|
||||||
|
size_t fAreaSize;
|
||||||
|
size_t fBlockCount;
|
||||||
|
size_t fBlockSize;
|
||||||
|
size_t fListCount;
|
||||||
|
size_t fListSize;
|
||||||
|
size_t fOtherCount;
|
||||||
|
size_t fOtherSize;
|
||||||
|
size_t fStringCount;
|
||||||
|
size_t fStringSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALLOCATION_INFO_H
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// AreaUtils.cpp
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// Except as contained in this notice, the name of a copyright holder shall
|
||||||
|
// not be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
// dealings in this Software without prior written authorization of the
|
||||||
|
// copyright holder.
|
||||||
|
|
||||||
|
#include <algobase.h>
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
#include "AreaUtils.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
|
#ifndef USE_STANDARD_FUNCTIONS
|
||||||
|
#define USE_STANDARD_FUNCTIONS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// area_info_for
|
||||||
|
static
|
||||||
|
status_t
|
||||||
|
area_info_for(void *address, area_info *info)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (address) {
|
||||||
|
// get the area ID for the ptr
|
||||||
|
area_id area = area_for(address);
|
||||||
|
// check if supplied pointer points to the beginning of the area
|
||||||
|
if (area >= 0)
|
||||||
|
error = get_area_info(area, info);
|
||||||
|
else
|
||||||
|
error = area;
|
||||||
|
} else
|
||||||
|
error = B_BAD_VALUE;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calloc
|
||||||
|
void *
|
||||||
|
AreaUtils::calloc(size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
//PRINT(("AreaUtils::calloc(%lu, %lu)\n", nmemb, size));
|
||||||
|
return AreaUtils::malloc(nmemb * size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free
|
||||||
|
void
|
||||||
|
AreaUtils::free(void *ptr)
|
||||||
|
{
|
||||||
|
//PRINT(("AreaUtils::free(%p)\n", ptr));
|
||||||
|
#if USE_STANDARD_FUNCTIONS
|
||||||
|
return ::free(ptr);
|
||||||
|
#else
|
||||||
|
if (ptr) {
|
||||||
|
// get the area for the pointer
|
||||||
|
area_info info;
|
||||||
|
if (area_info_for(ptr, &info) == B_OK) {
|
||||||
|
if (ptr == info.address) {
|
||||||
|
// everything is fine, delete the area
|
||||||
|
delete_area(info.area);
|
||||||
|
} else {
|
||||||
|
INFORM(("WARNING: AreaUtils::free(%p): area begin is %p."
|
||||||
|
"Ignored.\n", ptr, info.address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// malloc
|
||||||
|
void *
|
||||||
|
AreaUtils::malloc(size_t size)
|
||||||
|
{
|
||||||
|
//PRINT(("AreaUtils::malloc(%lu)\n", size));
|
||||||
|
#if USE_STANDARD_FUNCTIONS
|
||||||
|
return ::malloc(size);
|
||||||
|
#else
|
||||||
|
void *address = NULL;
|
||||||
|
if (size > 0) {
|
||||||
|
// round to multiple of page size
|
||||||
|
size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
|
||||||
|
// create an area
|
||||||
|
#if USER
|
||||||
|
area_id area = create_area("AreaUtils::malloc", &address,
|
||||||
|
B_ANY_ADDRESS, size, B_NO_LOCK,
|
||||||
|
B_WRITE_AREA | B_READ_AREA);
|
||||||
|
#else
|
||||||
|
area_id area = create_area("AreaUtils::malloc", &address,
|
||||||
|
B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK,
|
||||||
|
B_READ_AREA | B_WRITE_AREA);
|
||||||
|
#endif
|
||||||
|
if (area < 0)
|
||||||
|
address = NULL;
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// realloc
|
||||||
|
void *
|
||||||
|
AreaUtils::realloc(void * ptr, size_t size)
|
||||||
|
{
|
||||||
|
//PRINT(("AreaUtils::realloc(%p, %lu)\n", ptr, size))
|
||||||
|
#if USE_STANDARD_FUNCTIONS
|
||||||
|
return ::realloc(ptr, size);
|
||||||
|
#else
|
||||||
|
void *newAddress = NULL;
|
||||||
|
if (size == 0) {
|
||||||
|
AreaUtils::free(ptr);
|
||||||
|
} else if (ptr) {
|
||||||
|
// get the area for the pointer
|
||||||
|
area_info info;
|
||||||
|
if (area_info_for(ptr, &info) == B_OK) {
|
||||||
|
if (ptr == info.address) {
|
||||||
|
// round to multiple of page size
|
||||||
|
size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
|
||||||
|
if (size == info.size) {
|
||||||
|
// nothing to do
|
||||||
|
newAddress = ptr;
|
||||||
|
} else if (resize_area(info.area, size) == B_OK) {
|
||||||
|
// resizing the area went fine
|
||||||
|
newAddress = ptr;
|
||||||
|
} else {
|
||||||
|
// resizing the area failed: we need to allocate a new one
|
||||||
|
newAddress = AreaUtils::malloc(size);
|
||||||
|
if (newAddress) {
|
||||||
|
memcpy(newAddress, ptr, min(size, info.size));
|
||||||
|
delete_area(info.area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
INFORM(("WARNING: AreaUtils::realloc(%p): area begin is %p."
|
||||||
|
"Ignored.\n", ptr, info.address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newAddress;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// AreaUtils.h
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// Except as contained in this notice, the name of a copyright holder shall
|
||||||
|
// not be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
// dealings in this Software without prior written authorization of the
|
||||||
|
// copyright holder.
|
||||||
|
|
||||||
|
#ifndef AREA_UTILS_H
|
||||||
|
#define AREA_UTILS_H
|
||||||
|
|
||||||
|
namespace AreaUtils {
|
||||||
|
|
||||||
|
void *calloc(size_t nmemb, size_t size);
|
||||||
|
void free(void *ptr);
|
||||||
|
void *malloc(size_t size);
|
||||||
|
void *realloc(void * ptr, size_t size);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AREA_UTILS_H
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
// Attribute.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Attribute.h"
|
||||||
|
#include "Misc.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "ramfs.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Attribute::Attribute(Volume *volume, Node *node, const char *name,
|
||||||
|
uint32 type)
|
||||||
|
: DataContainer(volume),
|
||||||
|
fNode(node),
|
||||||
|
fName(name),
|
||||||
|
fType(type),
|
||||||
|
fIndex(NULL),
|
||||||
|
fInIndex(false),
|
||||||
|
fIterators()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
Attribute::~Attribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
Attribute::InitCheck() const
|
||||||
|
{
|
||||||
|
return (fName.GetString() ? B_OK : B_NO_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetType
|
||||||
|
void
|
||||||
|
Attribute::SetType(uint32 type)
|
||||||
|
{
|
||||||
|
if (type != fType) {
|
||||||
|
if (fIndex)
|
||||||
|
fIndex->Removed(this);
|
||||||
|
fType = type;
|
||||||
|
if (AttributeIndex *index = GetVolume()->FindAttributeIndex(GetName(),
|
||||||
|
fType)) {
|
||||||
|
index->Added(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAt
|
||||||
|
status_t
|
||||||
|
Attribute::WriteAt(off_t offset, const void *buffer, size_t size,
|
||||||
|
size_t *bytesWritten)
|
||||||
|
{
|
||||||
|
// get the current key for the attribute
|
||||||
|
uint8 oldKey[kMaxIndexKeyLength];
|
||||||
|
size_t oldLength;
|
||||||
|
GetKey(oldKey, &oldLength);
|
||||||
|
|
||||||
|
// write the new value
|
||||||
|
status_t error = DataContainer::WriteAt(offset, buffer, size, bytesWritten);
|
||||||
|
|
||||||
|
// If there is an index and a change has been made within the key, notify
|
||||||
|
// the index.
|
||||||
|
if (offset < kMaxIndexKeyLength && size > 0 && fIndex)
|
||||||
|
fIndex->Changed(this, oldKey, oldLength);
|
||||||
|
|
||||||
|
// update live queries
|
||||||
|
const uint8* newKey;
|
||||||
|
size_t newLength;
|
||||||
|
GetKey(&newKey, &newLength);
|
||||||
|
GetVolume()->UpdateLiveQueries(NULL, fNode, GetName(), fType, oldKey,
|
||||||
|
oldLength, newKey, newLength);
|
||||||
|
|
||||||
|
// node has been changed
|
||||||
|
if (fNode && size > 0)
|
||||||
|
fNode->MarkModified();
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIndex
|
||||||
|
void
|
||||||
|
Attribute::SetIndex(AttributeIndex *index, bool inIndex)
|
||||||
|
{
|
||||||
|
fIndex = index;
|
||||||
|
fInIndex = inIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey
|
||||||
|
void
|
||||||
|
Attribute::GetKey(const uint8 **key, size_t *length)
|
||||||
|
{
|
||||||
|
if (key && length) {
|
||||||
|
GetFirstDataBlock(key, length);
|
||||||
|
*length = min(*length, kMaxIndexKeyLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey
|
||||||
|
void
|
||||||
|
Attribute::GetKey(uint8 *key, size_t *length)
|
||||||
|
{
|
||||||
|
if (key && length) {
|
||||||
|
const uint8 *originalKey = NULL;
|
||||||
|
GetKey(&originalKey, length);
|
||||||
|
if (length > 0)
|
||||||
|
memcpy(key, originalKey, *length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachAttributeIterator
|
||||||
|
void
|
||||||
|
Attribute::AttachAttributeIterator(AttributeIterator *iterator)
|
||||||
|
{
|
||||||
|
if (iterator && iterator->GetCurrent() == this && !iterator->IsSuspended())
|
||||||
|
fIterators.Insert(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachAttributeIterator
|
||||||
|
void
|
||||||
|
Attribute::DetachAttributeIterator(AttributeIterator *iterator)
|
||||||
|
{
|
||||||
|
if (iterator && iterator->GetCurrent() == this && iterator->IsSuspended())
|
||||||
|
fIterators.Remove(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
Attribute::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
DataContainer::GetAllocationInfo(info);
|
||||||
|
info.AddAttributeAllocation(GetSize());
|
||||||
|
info.AddStringAllocation(fName.GetLength());
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Attribute.h
|
||||||
|
|
||||||
|
#ifndef ATTRIBUTE_H
|
||||||
|
#define ATTRIBUTE_H
|
||||||
|
|
||||||
|
#include "AttributeIndex.h"
|
||||||
|
#include "AttributeIterator.h"
|
||||||
|
#include "DataContainer.h"
|
||||||
|
#include "DLList.h"
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
class AllocationInfo;
|
||||||
|
class Node;
|
||||||
|
class Volume;
|
||||||
|
|
||||||
|
class Attribute : public DataContainer, public DLListLinkImpl<Attribute> {
|
||||||
|
public:
|
||||||
|
Attribute(Volume *volume, Node *node, const char *name, uint32 type = 0);
|
||||||
|
~Attribute();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
void SetNode(Node *node) { fNode = node; }
|
||||||
|
Node *GetNode() const { return fNode; }
|
||||||
|
|
||||||
|
const char *GetName() { return fName.GetString(); }
|
||||||
|
|
||||||
|
void SetType(uint32 type);
|
||||||
|
uint32 GetType() const { return fType; }
|
||||||
|
|
||||||
|
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||||
|
size_t *bytesWritten);
|
||||||
|
|
||||||
|
// index support
|
||||||
|
void SetIndex(AttributeIndex *index, bool inIndex);
|
||||||
|
AttributeIndex *GetIndex() const { return fIndex; }
|
||||||
|
bool IsInIndex() const { return fInIndex; }
|
||||||
|
void GetKey(const uint8 **key, size_t *length);
|
||||||
|
void GetKey(uint8 *key, size_t *length);
|
||||||
|
|
||||||
|
// iterator management
|
||||||
|
void AttachAttributeIterator(AttributeIterator *iterator);
|
||||||
|
void DetachAttributeIterator(AttributeIterator *iterator);
|
||||||
|
inline DLList<AttributeIterator> *GetAttributeIteratorList()
|
||||||
|
{ return &fIterators; }
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *fNode;
|
||||||
|
String fName;
|
||||||
|
uint32 fType;
|
||||||
|
AttributeIndex *fIndex;
|
||||||
|
bool fInIndex;
|
||||||
|
|
||||||
|
// iterator management
|
||||||
|
DLList<AttributeIterator> fIterators;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ATTRIBUTE_H
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// AttributeIndex.cpp
|
||||||
|
|
||||||
|
#include "AttributeIndex.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AttributeIndex::AttributeIndex(Volume *volume, const char *name, uint32 type,
|
||||||
|
bool fixedKeyLength, size_t keyLength)
|
||||||
|
: Index(volume, name, type, fixedKeyLength, keyLength)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
AttributeIndex::~AttributeIndex()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// AttributeIndex.h
|
||||||
|
|
||||||
|
#ifndef ATTRIBUTE_INDEX_H
|
||||||
|
#define ATTRIBUTE_INDEX_H
|
||||||
|
|
||||||
|
#include "Index.h"
|
||||||
|
|
||||||
|
class Attribute;
|
||||||
|
|
||||||
|
class AttributeIndex : public Index {
|
||||||
|
public:
|
||||||
|
AttributeIndex(Volume *volume, const char *name, uint32 type,
|
||||||
|
bool fixedKeyLength, size_t keyLength = 0);
|
||||||
|
virtual ~AttributeIndex();
|
||||||
|
|
||||||
|
virtual status_t Added(Attribute *attribute) = 0;
|
||||||
|
virtual bool Removed(Attribute *attribute) = 0;
|
||||||
|
virtual status_t Changed(Attribute *attribute,
|
||||||
|
const uint8 *oldKey, size_t length) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ATTRIBUTE_INDEX_H
|
||||||
@@ -0,0 +1,514 @@
|
|||||||
|
// AttributeIndexImpl.cpp
|
||||||
|
|
||||||
|
#include <TypeConstants.h>
|
||||||
|
|
||||||
|
#include "AttributeIndexImpl.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "EntryListener.h"
|
||||||
|
#include "IndexImpl.h"
|
||||||
|
#include "Misc.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "NodeListener.h"
|
||||||
|
#include "ramfs.h"
|
||||||
|
#include "TwoKeyAVLTree.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// compare_integral
|
||||||
|
template<typename Key>
|
||||||
|
static inline
|
||||||
|
int
|
||||||
|
compare_integral(const Key &a, const Key &b)
|
||||||
|
{
|
||||||
|
if (a < b)
|
||||||
|
return -1;
|
||||||
|
else if (a > b)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare_keys
|
||||||
|
static
|
||||||
|
int
|
||||||
|
compare_keys(const uint8 *key1, size_t length1, const uint8 *key2,
|
||||||
|
size_t length2, uint32 type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case B_INT32_TYPE:
|
||||||
|
return compare_integral(*(int32*)key1, *(int32*)key2);
|
||||||
|
case B_UINT32_TYPE:
|
||||||
|
return compare_integral(*(uint32*)key1, *(uint32*)key2);
|
||||||
|
case B_INT64_TYPE:
|
||||||
|
return compare_integral(*(int64*)key1, *(int64*)key2);
|
||||||
|
case B_UINT64_TYPE:
|
||||||
|
return compare_integral(*(uint64*)key1, *(uint64*)key2);
|
||||||
|
case B_FLOAT_TYPE:
|
||||||
|
return compare_integral(*(float*)key1, *(float*)key2);
|
||||||
|
case B_DOUBLE_TYPE:
|
||||||
|
return compare_integral(*(double*)key1, *(double*)key2);
|
||||||
|
case B_STRING_TYPE:
|
||||||
|
{
|
||||||
|
int result = strncmp((const char*)key1, (const char*)key2,
|
||||||
|
min(length1, length2));
|
||||||
|
if (result == 0) {
|
||||||
|
result = compare_integral(strnlen((const char*)key1, length1),
|
||||||
|
strnlen((const char*)key2, length2));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrimaryKey
|
||||||
|
class AttributeIndexImpl::PrimaryKey {
|
||||||
|
public:
|
||||||
|
PrimaryKey(Attribute *attribute, const uint8 *key,
|
||||||
|
size_t length)
|
||||||
|
: attribute(attribute), key(key), length(length) {}
|
||||||
|
PrimaryKey(Attribute *attribute)
|
||||||
|
: attribute(attribute) { attribute->GetKey(&key, &length); }
|
||||||
|
PrimaryKey(const uint8 *key, size_t length)
|
||||||
|
: attribute(NULL), key(key), length(length) {}
|
||||||
|
|
||||||
|
Attribute *attribute;
|
||||||
|
const uint8 *key;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
// GetPrimaryKey
|
||||||
|
class AttributeIndexImpl::GetPrimaryKey {
|
||||||
|
public:
|
||||||
|
inline PrimaryKey operator()(Attribute *a)
|
||||||
|
{
|
||||||
|
return PrimaryKey(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PrimaryKey operator()(Attribute *a) const
|
||||||
|
{
|
||||||
|
return PrimaryKey(a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// PrimaryKeyCompare
|
||||||
|
class AttributeIndexImpl::PrimaryKeyCompare
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrimaryKeyCompare(uint32 type) : fType(type) {}
|
||||||
|
|
||||||
|
inline int operator()(const PrimaryKey &a,
|
||||||
|
const PrimaryKey &b) const
|
||||||
|
{
|
||||||
|
if (a.attribute != NULL && a.attribute == b.attribute)
|
||||||
|
return 0;
|
||||||
|
return compare_keys(a.key, a.length, b.key, b.length, fType);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 fType;
|
||||||
|
};
|
||||||
|
|
||||||
|
// AttributeNodeIterator
|
||||||
|
template<typename AttributeIterator>
|
||||||
|
class AttributeNodeIterator {
|
||||||
|
public:
|
||||||
|
inline Node **GetCurrent()
|
||||||
|
{
|
||||||
|
if (Attribute **attribute = fIterator.GetCurrent()) {
|
||||||
|
fNode = (*attribute)->GetNode();
|
||||||
|
return &fNode;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Node **GetNext()
|
||||||
|
{
|
||||||
|
if (Attribute **attribute = fIterator.GetNext()) {
|
||||||
|
fNode = (*attribute)->GetNode();
|
||||||
|
return &fNode;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeIterator fIterator;
|
||||||
|
Node *fNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// AttributeTree
|
||||||
|
class AttributeIndexImpl::AttributeTree
|
||||||
|
: public TwoKeyAVLTree<Attribute*, PrimaryKey, PrimaryKeyCompare,
|
||||||
|
GetPrimaryKey> {
|
||||||
|
public:
|
||||||
|
AttributeTree(uint32 type)
|
||||||
|
: TwoKeyAVLTree<Attribute*, PrimaryKey, PrimaryKeyCompare,
|
||||||
|
GetPrimaryKey>(PrimaryKeyCompare(type),
|
||||||
|
GetPrimaryKey(), AVLTreeStandardCompare<Attribute*>(),
|
||||||
|
AVLTreeStandardGetKey<Attribute*, Attribute*>(),
|
||||||
|
AVLTreeStandardNodeAllocator<Attribute*,
|
||||||
|
AVLTreeStandardNode<Attribute*> >(),
|
||||||
|
AVLTreeStandardGetValue<Attribute*,
|
||||||
|
AVLTreeStandardNode<Attribute*> >())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Iterator
|
||||||
|
class AttributeIndexImpl::Iterator
|
||||||
|
: public NodeEntryIterator<
|
||||||
|
AttributeNodeIterator<AttributeTree::Iterator> >,
|
||||||
|
public DLListLinkImpl<Iterator>, public EntryListener,
|
||||||
|
public NodeListener {
|
||||||
|
public:
|
||||||
|
Iterator();
|
||||||
|
virtual ~Iterator();
|
||||||
|
|
||||||
|
virtual Entry *GetCurrent();
|
||||||
|
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||||
|
|
||||||
|
virtual status_t Suspend();
|
||||||
|
virtual status_t Resume();
|
||||||
|
|
||||||
|
bool SetTo(AttributeIndexImpl *index, const uint8 *key, size_t length,
|
||||||
|
bool ignoreValue = false);
|
||||||
|
void Unset();
|
||||||
|
|
||||||
|
virtual void EntryRemoved(Entry *entry);
|
||||||
|
virtual void NodeRemoved(Node *node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef NodeEntryIterator<
|
||||||
|
AttributeNodeIterator<AttributeTree::Iterator> > BaseClass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AttributeIndexImpl *fIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// IteratorList
|
||||||
|
class AttributeIndexImpl::IteratorList : public DLList<Iterator> {};
|
||||||
|
|
||||||
|
|
||||||
|
// AttributeIndexImpl
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AttributeIndexImpl::AttributeIndexImpl(Volume *volume, const char *name,
|
||||||
|
uint32 type, size_t keyLength)
|
||||||
|
: AttributeIndex(volume, name, type, (keyLength > 0), keyLength),
|
||||||
|
fAttributes(new(nothrow) AttributeTree(type)),
|
||||||
|
fIterators(new(nothrow) IteratorList)
|
||||||
|
{
|
||||||
|
if (fInitStatus == B_OK && (!fAttributes || !fIterators))
|
||||||
|
fInitStatus = B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
AttributeIndexImpl::~AttributeIndexImpl()
|
||||||
|
{
|
||||||
|
if (fIterators) {
|
||||||
|
// unset the iterators
|
||||||
|
for (Iterator *iterator = fIterators->GetFirst();
|
||||||
|
iterator;
|
||||||
|
iterator = fIterators->GetNext(iterator)) {
|
||||||
|
iterator->SetTo(NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
delete fIterators;
|
||||||
|
}
|
||||||
|
// unset all attributes and delete the tree
|
||||||
|
if (fAttributes) {
|
||||||
|
AttributeTree::Iterator it;
|
||||||
|
fAttributes->GetIterator(&it);
|
||||||
|
for (Attribute **attribute = it.GetCurrent(); attribute; it.GetNext())
|
||||||
|
(*attribute)->SetIndex(NULL, false);
|
||||||
|
delete fAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountEntries
|
||||||
|
int32
|
||||||
|
AttributeIndexImpl::CountEntries() const
|
||||||
|
{
|
||||||
|
return fAttributes->CountItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed
|
||||||
|
status_t
|
||||||
|
AttributeIndexImpl::Changed(Attribute *attribute, const uint8 *oldKey,
|
||||||
|
size_t oldLength)
|
||||||
|
{
|
||||||
|
status_t error = B_BAD_VALUE;
|
||||||
|
if (attribute && attribute->GetIndex() == this) {
|
||||||
|
// update the iterators and remove the attribute from the tree
|
||||||
|
error = B_OK;
|
||||||
|
if (attribute->IsInIndex()) {
|
||||||
|
AttributeTree::Iterator it;
|
||||||
|
Attribute **foundAttribute = fAttributes->Find(
|
||||||
|
PrimaryKey(attribute, oldKey, oldLength), attribute, &it);
|
||||||
|
if (foundAttribute && *foundAttribute == attribute) {
|
||||||
|
Node *node = attribute->GetNode();
|
||||||
|
// update the iterators
|
||||||
|
for (Iterator *iterator = fIterators->GetFirst();
|
||||||
|
iterator;
|
||||||
|
iterator = fIterators->GetNext(iterator)) {
|
||||||
|
if (iterator->GetCurrentNode() == node)
|
||||||
|
iterator->NodeRemoved(node);
|
||||||
|
}
|
||||||
|
// remove and re-insert the attribute
|
||||||
|
fAttributes->Remove(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// re-insert the attribute
|
||||||
|
if (fKeyLength > 0 && attribute->GetSize() != fKeyLength) {
|
||||||
|
attribute->SetIndex(this, false);
|
||||||
|
} else {
|
||||||
|
error = fAttributes->Insert(attribute);
|
||||||
|
if (error == B_OK)
|
||||||
|
attribute->SetIndex(this, true);
|
||||||
|
else
|
||||||
|
attribute->SetIndex(NULL, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added
|
||||||
|
status_t
|
||||||
|
AttributeIndexImpl::Added(Attribute *attribute)
|
||||||
|
{
|
||||||
|
PRINT(("AttributeIndex::Add(%p)\n", attribute));
|
||||||
|
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
size_t size = attribute->GetSize();
|
||||||
|
if (fKeyLength > 0 && size != fKeyLength) {
|
||||||
|
attribute->SetIndex(this, false);
|
||||||
|
} else {
|
||||||
|
error = fAttributes->Insert(attribute);
|
||||||
|
if (error == B_OK)
|
||||||
|
attribute->SetIndex(this, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removed
|
||||||
|
bool
|
||||||
|
AttributeIndexImpl::Removed(Attribute *attribute)
|
||||||
|
{
|
||||||
|
PRINT(("AttributeIndex::Removed(%p)\n", attribute));
|
||||||
|
bool result = (attribute && attribute->GetIndex() == this);
|
||||||
|
if (result) {
|
||||||
|
if (attribute->IsInIndex())
|
||||||
|
fAttributes->Remove(attribute, attribute);
|
||||||
|
attribute->SetIndex(NULL, false);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalGetIterator
|
||||||
|
AbstractIndexEntryIterator *
|
||||||
|
AttributeIndexImpl::InternalGetIterator()
|
||||||
|
{
|
||||||
|
Iterator *iterator = new(nothrow) Iterator;
|
||||||
|
if (iterator) {
|
||||||
|
if (!iterator->SetTo(this, NULL, 0, true)) {
|
||||||
|
delete iterator;
|
||||||
|
iterator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalFind
|
||||||
|
AbstractIndexEntryIterator *
|
||||||
|
AttributeIndexImpl::InternalFind(const uint8 *key, size_t length)
|
||||||
|
{
|
||||||
|
if (!key || (fKeyLength > 0 && length != fKeyLength))
|
||||||
|
return NULL;
|
||||||
|
Iterator *iterator = new(nothrow) Iterator;
|
||||||
|
if (iterator) {
|
||||||
|
if (!iterator->SetTo(this, key, length)) {
|
||||||
|
delete iterator;
|
||||||
|
iterator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AddIterator
|
||||||
|
void
|
||||||
|
AttributeIndexImpl::_AddIterator(Iterator *iterator)
|
||||||
|
{
|
||||||
|
fIterators->Insert(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _RemoveIterator
|
||||||
|
void
|
||||||
|
AttributeIndexImpl::_RemoveIterator(Iterator *iterator)
|
||||||
|
{
|
||||||
|
fIterators->Remove(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Iterator
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AttributeIndexImpl::Iterator::Iterator()
|
||||||
|
: BaseClass(),
|
||||||
|
fIndex(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
AttributeIndexImpl::Iterator::~Iterator()
|
||||||
|
{
|
||||||
|
SetTo(NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
AttributeIndexImpl::Iterator::GetCurrent()
|
||||||
|
{
|
||||||
|
return BaseClass::GetCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
AttributeIndexImpl::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||||
|
{
|
||||||
|
Entry *entry = GetCurrent();
|
||||||
|
if (entry) {
|
||||||
|
if (Attribute **attribute = fIterator.fIterator.GetCurrent()) {
|
||||||
|
if ((*attribute)->GetNode() == entry->GetNode()) {
|
||||||
|
(*attribute)->GetKey(buffer, keyLength);
|
||||||
|
} else {
|
||||||
|
FATAL(("Node of current attribute and node of current entry "
|
||||||
|
"differ: %Ld vs. %Ld\n",
|
||||||
|
(*attribute)->GetNode()->GetID(),
|
||||||
|
entry->GetNode()->GetID()));
|
||||||
|
entry = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FATAL(("We have a current entry (`%s', node: %Ld), but no current "
|
||||||
|
"attribute.\n", entry->GetName(),
|
||||||
|
entry->GetNode()->GetID()));
|
||||||
|
entry = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
AttributeIndexImpl::Iterator::Suspend()
|
||||||
|
{
|
||||||
|
status_t error = BaseClass::Suspend();
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fNode) {
|
||||||
|
error = fIndex->GetVolume()->AddNodeListener(this, fNode,
|
||||||
|
NODE_LISTEN_REMOVED);
|
||||||
|
if (error == B_OK && fEntry) {
|
||||||
|
error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
|
||||||
|
ENTRY_LISTEN_REMOVED);
|
||||||
|
if (error != B_OK)
|
||||||
|
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||||
|
}
|
||||||
|
if (error != B_OK)
|
||||||
|
BaseClass::Resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
AttributeIndexImpl::Iterator::Resume()
|
||||||
|
{
|
||||||
|
status_t error = BaseClass::Resume();
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fEntry)
|
||||||
|
error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
|
||||||
|
if (fNode) {
|
||||||
|
if (error == B_OK)
|
||||||
|
error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||||
|
else
|
||||||
|
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
bool
|
||||||
|
AttributeIndexImpl::Iterator::SetTo(AttributeIndexImpl *index,
|
||||||
|
const uint8 *key, size_t length, bool ignoreValue)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
Unset();
|
||||||
|
// set the new values
|
||||||
|
fIndex = index;
|
||||||
|
if (fIndex)
|
||||||
|
fIndex->_AddIterator(this);
|
||||||
|
fInitialized = fIndex;
|
||||||
|
// get the attribute node's first entry
|
||||||
|
if (fIndex) {
|
||||||
|
// get the first node
|
||||||
|
bool found = true;
|
||||||
|
if (ignoreValue)
|
||||||
|
fIndex->fAttributes->GetIterator(&fIterator.fIterator);
|
||||||
|
else {
|
||||||
|
found = fIndex->fAttributes->FindFirst(PrimaryKey(key, length),
|
||||||
|
&(fIterator.fIterator));
|
||||||
|
}
|
||||||
|
// get the first entry
|
||||||
|
if (found) {
|
||||||
|
if (Node **nodeP = fIterator.GetCurrent()) {
|
||||||
|
fNode = *nodeP;
|
||||||
|
fEntry = fNode->GetFirstReferrer();
|
||||||
|
if (!fEntry)
|
||||||
|
BaseClass::GetNext();
|
||||||
|
if (Attribute **attribute = fIterator.fIterator.GetCurrent()) {
|
||||||
|
const uint8 *attrKey;
|
||||||
|
size_t attrKeyLength;
|
||||||
|
(*attribute)->GetKey(&attrKey, &attrKeyLength);
|
||||||
|
if (!ignoreValue
|
||||||
|
&& compare_keys(attrKey, attrKeyLength, key, length,
|
||||||
|
fIndex->GetType()) != 0) {
|
||||||
|
Unset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset
|
||||||
|
void
|
||||||
|
AttributeIndexImpl::Iterator::Unset()
|
||||||
|
{
|
||||||
|
if (fIndex) {
|
||||||
|
fIndex->_RemoveIterator(this);
|
||||||
|
fIndex = NULL;
|
||||||
|
}
|
||||||
|
BaseClass::Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryRemoved
|
||||||
|
void
|
||||||
|
AttributeIndexImpl::Iterator::EntryRemoved(Entry */*entry*/)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
fIsNext = BaseClass::GetNext();
|
||||||
|
Suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoved
|
||||||
|
void
|
||||||
|
AttributeIndexImpl::Iterator::NodeRemoved(Node */*node*/)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
fEntry = NULL;
|
||||||
|
fIsNext = BaseClass::GetNext();
|
||||||
|
Suspend();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// AttributeIndexImpl.h
|
||||||
|
|
||||||
|
#ifndef ATTRIBUTE_INDEX_IMPL_H
|
||||||
|
#define ATTRIBUTE_INDEX_IMPL_H
|
||||||
|
|
||||||
|
#include "AttributeIndex.h"
|
||||||
|
|
||||||
|
// AttributeIndexImpl
|
||||||
|
class AttributeIndexImpl : public AttributeIndex {
|
||||||
|
public:
|
||||||
|
AttributeIndexImpl(Volume *volume, const char *name, uint32 type,
|
||||||
|
size_t keyLength);
|
||||||
|
virtual ~AttributeIndexImpl();
|
||||||
|
|
||||||
|
virtual int32 CountEntries() const;
|
||||||
|
|
||||||
|
virtual status_t Changed(Attribute *attribute,
|
||||||
|
const uint8 *oldKey, size_t oldLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual status_t Added(Attribute *attribute);
|
||||||
|
virtual bool Removed(Attribute *attribute);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||||
|
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Iterator;
|
||||||
|
class IteratorList;
|
||||||
|
class AttributeTree;
|
||||||
|
|
||||||
|
class PrimaryKey;
|
||||||
|
class GetPrimaryKey;
|
||||||
|
class PrimaryKeyCompare;
|
||||||
|
|
||||||
|
friend class Iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _AddIterator(Iterator *iterator);
|
||||||
|
void _RemoveIterator(Iterator *iterator);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AttributeTree *fAttributes;
|
||||||
|
IteratorList *fIterators;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ATTRIBUTE_INDEX_IMPL_H
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
// AttributeIterator.cpp
|
||||||
|
|
||||||
|
#include "AttributeIterator.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AttributeIterator::AttributeIterator(Node *node)
|
||||||
|
: fNode(node),
|
||||||
|
fAttribute(NULL),
|
||||||
|
fSuspended(false),
|
||||||
|
fIsNext(false),
|
||||||
|
fDone(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
AttributeIterator::~AttributeIterator()
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
status_t
|
||||||
|
AttributeIterator::SetTo(Node *node)
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fNode = node;
|
||||||
|
fAttribute = NULL;
|
||||||
|
fSuspended = false;
|
||||||
|
fIsNext = false;
|
||||||
|
fDone = false;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset
|
||||||
|
void
|
||||||
|
AttributeIterator::Unset()
|
||||||
|
{
|
||||||
|
if (fNode && fSuspended)
|
||||||
|
Resume();
|
||||||
|
fNode = NULL;
|
||||||
|
fAttribute = NULL;
|
||||||
|
fSuspended = false;
|
||||||
|
fIsNext = false;
|
||||||
|
fDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
AttributeIterator::Suspend()
|
||||||
|
{
|
||||||
|
status_t error = (fNode ? B_OK : B_ERROR);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fNode->GetVolume()->IteratorLock()) {
|
||||||
|
if (!fSuspended) {
|
||||||
|
if (fAttribute)
|
||||||
|
fAttribute->AttachAttributeIterator(this);
|
||||||
|
fNode->GetVolume()->IteratorUnlock();
|
||||||
|
fSuspended = true;
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
AttributeIterator::Resume()
|
||||||
|
{
|
||||||
|
status_t error = (fNode ? B_OK : B_ERROR);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fNode->GetVolume()->IteratorLock()) {
|
||||||
|
if (fSuspended) {
|
||||||
|
if (fAttribute)
|
||||||
|
fAttribute->DetachAttributeIterator(this);
|
||||||
|
fSuspended = false;
|
||||||
|
}
|
||||||
|
fNode->GetVolume()->IteratorUnlock();
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext
|
||||||
|
status_t
|
||||||
|
AttributeIterator::GetNext(Attribute **attribute)
|
||||||
|
{
|
||||||
|
status_t error = B_ENTRY_NOT_FOUND;
|
||||||
|
if (!fDone && fNode && attribute) {
|
||||||
|
if (fIsNext) {
|
||||||
|
fIsNext = false;
|
||||||
|
if (fAttribute)
|
||||||
|
error = B_OK;
|
||||||
|
} else
|
||||||
|
error = fNode->GetNextAttribute(&fAttribute);
|
||||||
|
*attribute = fAttribute;
|
||||||
|
}
|
||||||
|
fDone = (error != B_OK);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind
|
||||||
|
status_t
|
||||||
|
AttributeIterator::Rewind()
|
||||||
|
{
|
||||||
|
status_t error = (fNode ? B_OK : B_ERROR);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fNode->GetVolume()->IteratorLock()) {
|
||||||
|
if (fSuspended && fAttribute)
|
||||||
|
fAttribute->DetachAttributeIterator(this);
|
||||||
|
fAttribute = NULL;
|
||||||
|
fIsNext = false;
|
||||||
|
fDone = false;
|
||||||
|
fNode->GetVolume()->IteratorUnlock();
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCurrent
|
||||||
|
void
|
||||||
|
AttributeIterator::SetCurrent(Attribute *attribute, bool isNext)
|
||||||
|
{
|
||||||
|
fIsNext = isNext;
|
||||||
|
fAttribute = attribute;
|
||||||
|
fDone = !fAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// AttributeIterator.h
|
||||||
|
|
||||||
|
#ifndef ATTRIBUTE_ITERATOR_H
|
||||||
|
#define ATTRIBUTE_ITERATOR_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include "DLList.h"
|
||||||
|
|
||||||
|
class Attribute;
|
||||||
|
class Node;
|
||||||
|
|
||||||
|
class AttributeIterator : public DLListLinkImpl<AttributeIterator> {
|
||||||
|
public:
|
||||||
|
AttributeIterator(Node *node = NULL);
|
||||||
|
~AttributeIterator();
|
||||||
|
|
||||||
|
status_t SetTo(Node *node);
|
||||||
|
void Unset();
|
||||||
|
|
||||||
|
Node *GetNode() const { return fNode; }
|
||||||
|
|
||||||
|
status_t Suspend();
|
||||||
|
status_t Resume();
|
||||||
|
bool IsSuspended() const { return fSuspended; }
|
||||||
|
|
||||||
|
status_t GetNext(Attribute **attribute);
|
||||||
|
Attribute *GetCurrent() const { return fAttribute; }
|
||||||
|
|
||||||
|
status_t Rewind();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetCurrent(Attribute *attribute, bool isNext);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Node;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *fNode;
|
||||||
|
Attribute *fAttribute;
|
||||||
|
bool fSuspended;
|
||||||
|
bool fIsNext;
|
||||||
|
bool fDone;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ATTRIBUTE_ITERATOR_H
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
// Block.h
|
||||||
|
|
||||||
|
#ifndef BLOCK_H
|
||||||
|
#define BLOCK_H
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
class BlockHeader;
|
||||||
|
class BlockReference;
|
||||||
|
class TFreeBlock;
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
//#define inline
|
||||||
|
#define BA_DEFINE_INLINES 1
|
||||||
|
|
||||||
|
// BlockHeader
|
||||||
|
class BlockHeader {
|
||||||
|
public:
|
||||||
|
inline Block *ToBlock() { return (Block*)this; }
|
||||||
|
inline TFreeBlock *ToFreeBlock() { return (TFreeBlock*)this; }
|
||||||
|
|
||||||
|
inline void SetPreviousBlock(Block *block);
|
||||||
|
inline Block *GetPreviousBlock();
|
||||||
|
|
||||||
|
inline void SetNextBlock(Block *block);
|
||||||
|
inline Block *GetNextBlock();
|
||||||
|
inline bool HasNextBlock() { return (fSize & HAS_NEXT_FLAG); }
|
||||||
|
|
||||||
|
inline void SetSize(size_t size, bool hasNext = false);
|
||||||
|
inline size_t GetSize() const;
|
||||||
|
static inline size_t GetUsableSizeFor(size_t size);
|
||||||
|
inline size_t GetUsableSize() const;
|
||||||
|
|
||||||
|
inline void *GetData();
|
||||||
|
|
||||||
|
inline void SetFree(bool flag);
|
||||||
|
inline bool IsFree() const;
|
||||||
|
|
||||||
|
inline void SetReference(BlockReference *ref);
|
||||||
|
inline BlockReference *GetReference() const { return fReference; }
|
||||||
|
inline void FixReference();
|
||||||
|
|
||||||
|
inline void SetTo(Block *previous, size_t size, bool isFree, bool hasNext,
|
||||||
|
BlockReference *reference = NULL);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
FREE_FLAG = 0x80000000,
|
||||||
|
BACK_SKIP_MASK = 0x7fffffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HAS_NEXT_FLAG = 0x80000000,
|
||||||
|
SIZE_MASK = 0x7fffffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockHeader();
|
||||||
|
~BlockHeader();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t fBackSkip;
|
||||||
|
size_t fSize;
|
||||||
|
BlockReference *fReference;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Block
|
||||||
|
class Block : public BlockHeader {
|
||||||
|
public:
|
||||||
|
static inline Block *MakeBlock(void *address, ssize_t offset,
|
||||||
|
Block *previous, size_t size, bool isFree,
|
||||||
|
bool hasNext,
|
||||||
|
BlockReference *reference = NULL);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Block();
|
||||||
|
~Block();
|
||||||
|
};
|
||||||
|
|
||||||
|
// TFreeBlock
|
||||||
|
class TFreeBlock : public Block {
|
||||||
|
public:
|
||||||
|
|
||||||
|
inline void SetPreviousFreeBlock(TFreeBlock *block) { fPrevious = block; }
|
||||||
|
inline void SetNextFreeBlock(TFreeBlock *block) { fNext = block; }
|
||||||
|
inline TFreeBlock *GetPreviousFreeBlock() { return fPrevious; }
|
||||||
|
inline TFreeBlock *GetNextFreeBlock() { return fNext; }
|
||||||
|
|
||||||
|
inline void SetTo(Block *previous, size_t size, bool hasNext,
|
||||||
|
TFreeBlock *previousFree, TFreeBlock *nextFree);
|
||||||
|
|
||||||
|
// static inline TFreeBlock *MakeFreeBlock(void *address, ssize_t offset,
|
||||||
|
// Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree,
|
||||||
|
// TFreeBlock *nextFree);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TFreeBlock();
|
||||||
|
~TFreeBlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TFreeBlock *fPrevious;
|
||||||
|
TFreeBlock *fNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BlockReference
|
||||||
|
class BlockReference {
|
||||||
|
public:
|
||||||
|
inline BlockReference() : fBlock(NULL) {}
|
||||||
|
inline BlockReference(Block *block) : fBlock(block) {}
|
||||||
|
|
||||||
|
inline void SetBlock(Block *block) { fBlock = block; }
|
||||||
|
inline Block *GetBlock() const { return fBlock; }
|
||||||
|
|
||||||
|
inline void *GetData() const { return fBlock->GetData(); }
|
||||||
|
inline void *GetDataAt(ssize_t offset) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Block *fBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// inline methods
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
#if BA_DEFINE_INLINES
|
||||||
|
|
||||||
|
// BlockHeader
|
||||||
|
|
||||||
|
// SetPreviousBlock
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::SetPreviousBlock(Block *block)
|
||||||
|
{
|
||||||
|
size_t offset = (block ? (char*)this - (char*)block : 0);
|
||||||
|
fBackSkip = fBackSkip & FREE_FLAG | offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreviousBlock
|
||||||
|
inline
|
||||||
|
Block *
|
||||||
|
BlockHeader::GetPreviousBlock()
|
||||||
|
{
|
||||||
|
if (fBackSkip & BACK_SKIP_MASK)
|
||||||
|
return (Block*)((char*)this - (fBackSkip & BACK_SKIP_MASK));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNextBlock
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::SetNextBlock(Block *block)
|
||||||
|
{
|
||||||
|
if (block)
|
||||||
|
fSize = ((char*)block - (char*)this) | HAS_NEXT_FLAG;
|
||||||
|
else
|
||||||
|
fSize &= SIZE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextBlock
|
||||||
|
inline
|
||||||
|
Block *
|
||||||
|
BlockHeader::GetNextBlock()
|
||||||
|
{
|
||||||
|
if (fSize & HAS_NEXT_FLAG)
|
||||||
|
return (Block*)((char*)this + (SIZE_MASK & fSize));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::SetSize(size_t size, bool hasNext)
|
||||||
|
{
|
||||||
|
fSize = size;
|
||||||
|
if (hasNext)
|
||||||
|
fSize |= HAS_NEXT_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize
|
||||||
|
inline
|
||||||
|
size_t
|
||||||
|
BlockHeader::GetSize() const
|
||||||
|
{
|
||||||
|
return (fSize & SIZE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsableSizeFor
|
||||||
|
inline
|
||||||
|
size_t
|
||||||
|
BlockHeader::GetUsableSizeFor(size_t size)
|
||||||
|
{
|
||||||
|
return (size - sizeof(BlockHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsableSize
|
||||||
|
inline
|
||||||
|
size_t
|
||||||
|
BlockHeader::GetUsableSize() const
|
||||||
|
{
|
||||||
|
return GetUsableSizeFor(GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetData
|
||||||
|
inline
|
||||||
|
void *
|
||||||
|
BlockHeader::GetData()
|
||||||
|
{
|
||||||
|
return (char*)this + sizeof(BlockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFree
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::SetFree(bool flag)
|
||||||
|
{
|
||||||
|
if (flag)
|
||||||
|
fBackSkip |= FREE_FLAG;
|
||||||
|
else
|
||||||
|
fBackSkip &= ~FREE_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFree
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
BlockHeader::IsFree() const
|
||||||
|
{
|
||||||
|
return (fBackSkip & FREE_FLAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::SetTo(Block *previous, size_t size, bool isFree, bool hasNext,
|
||||||
|
BlockReference *reference)
|
||||||
|
{
|
||||||
|
SetPreviousBlock(previous);
|
||||||
|
SetSize(size, hasNext);
|
||||||
|
SetFree(isFree);
|
||||||
|
SetReference(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReference
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::SetReference(BlockReference *ref)
|
||||||
|
{
|
||||||
|
fReference = ref;
|
||||||
|
FixReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixReference
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockHeader::FixReference()
|
||||||
|
{
|
||||||
|
if (fReference)
|
||||||
|
fReference->SetBlock(ToBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Block
|
||||||
|
|
||||||
|
// MakeBlock
|
||||||
|
/*inline
|
||||||
|
Block *
|
||||||
|
Block::MakeBlock(void *address, ssize_t offset, Block *previous, size_t size,
|
||||||
|
bool isFree, bool hasNext, BlockReference *reference)
|
||||||
|
{
|
||||||
|
Block *block = (Block*)((char*)address + offset);
|
||||||
|
block->SetTo(previous, size, isFree, hasNext, reference);
|
||||||
|
return block;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
// TFreeBlock
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
TFreeBlock::SetTo(Block *previous, size_t size, bool hasNext,
|
||||||
|
TFreeBlock *previousFree, TFreeBlock *nextFree)
|
||||||
|
{
|
||||||
|
Block::SetTo(previous, size, true, hasNext, NULL);
|
||||||
|
SetPreviousFreeBlock(previousFree);
|
||||||
|
SetNextFreeBlock(nextFree);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFreeBlock
|
||||||
|
/*inline
|
||||||
|
TFreeBlock *
|
||||||
|
TFreeBlock::MakeFreeBlock(void *address, ssize_t offset, Block *previous,
|
||||||
|
size_t size, bool hasNext, TFreeBlock *previousFree,
|
||||||
|
TFreeBlock *nextFree)
|
||||||
|
{
|
||||||
|
TFreeBlock *block = (TFreeBlock*)((char*)address + offset);
|
||||||
|
block->SetTo(previous, size, hasNext, previousFree, nextFree);
|
||||||
|
if (hasNext)
|
||||||
|
block->GetNextBlock()->SetPreviousBlock(block);
|
||||||
|
return block;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
// BlockReference
|
||||||
|
|
||||||
|
// GetDataAt
|
||||||
|
inline
|
||||||
|
void *
|
||||||
|
BlockReference::GetDataAt(ssize_t offset) const
|
||||||
|
{
|
||||||
|
return (char*)fBlock->GetData() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BA_DEFINE_INLINES
|
||||||
|
|
||||||
|
#endif // BLOCK_H
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
// BlockAllocator.cpp
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
#define BA_DEFINE_INLINES 1
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "BlockAllocator.h"
|
||||||
|
#include "BlockAllocatorArea.h"
|
||||||
|
#include "BlockAllocatorAreaBucket.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "DLList.h"
|
||||||
|
|
||||||
|
// BlockAllocator
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
BlockAllocator::BlockAllocator(size_t areaSize)
|
||||||
|
: fReferenceManager(),
|
||||||
|
fBuckets(NULL),
|
||||||
|
fBucketCount(0),
|
||||||
|
fAreaSize(areaSize),
|
||||||
|
fAreaCount(0),
|
||||||
|
fFreeBytes(0)
|
||||||
|
{
|
||||||
|
// create and init buckets
|
||||||
|
fBucketCount = bucket_containing_size(areaSize) + 1;
|
||||||
|
fBuckets = new(nothrow) AreaBucket[fBucketCount];
|
||||||
|
size_t minSize = 0;
|
||||||
|
for (int32 i = 0; i < fBucketCount; i++) {
|
||||||
|
size_t maxSize = (1 << i) * kMinNetBlockSize;
|
||||||
|
fBuckets[i].SetIndex(i);
|
||||||
|
fBuckets[i].SetSizeLimits(minSize, maxSize);
|
||||||
|
minSize = maxSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
BlockAllocator::~BlockAllocator()
|
||||||
|
{
|
||||||
|
if (fBuckets)
|
||||||
|
delete[] fBuckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
BlockAllocator::InitCheck() const
|
||||||
|
{
|
||||||
|
RETURN_ERROR(fBuckets ? B_OK : B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllocateBlock
|
||||||
|
BlockReference *
|
||||||
|
BlockAllocator::AllocateBlock(size_t usableSize)
|
||||||
|
{
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
if (fPanic)
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
//PRINT(("BlockAllocator::AllocateBlock(%lu)\n", usableSize));
|
||||||
|
Block *block = NULL;
|
||||||
|
if (usableSize > 0 && usableSize <= Area::GetMaxFreeBytesFor(fAreaSize)) {
|
||||||
|
// get a block reference
|
||||||
|
BlockReference *reference = fReferenceManager.AllocateReference();
|
||||||
|
if (reference) {
|
||||||
|
block = _AllocateBlock(usableSize);
|
||||||
|
// set reference / cleanup on failure
|
||||||
|
if (block)
|
||||||
|
block->SetReference(reference);
|
||||||
|
else
|
||||||
|
fReferenceManager.FreeReference(reference);
|
||||||
|
}
|
||||||
|
D(SanityCheck(false));
|
||||||
|
}
|
||||||
|
//PRINT(("BlockAllocator::AllocateBlock() done: %p\n", block));
|
||||||
|
return (block ? block->GetReference() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeBlock
|
||||||
|
void
|
||||||
|
BlockAllocator::FreeBlock(BlockReference *blockReference)
|
||||||
|
{
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
if (fPanic)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
D(if (!CheckBlock(blockReference)) return;);
|
||||||
|
Block *block = (blockReference ? blockReference->GetBlock() : NULL);
|
||||||
|
//PRINT(("BlockAllocator::FreeBlock(%p)\n", block));
|
||||||
|
Area *area = NULL;
|
||||||
|
if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) {
|
||||||
|
_FreeBlock(area, block, true);
|
||||||
|
D(SanityCheck(false));
|
||||||
|
if (_DefragmentingRecommended())
|
||||||
|
_Defragment();
|
||||||
|
}
|
||||||
|
//PRINT(("BlockAllocator::FreeBlock() done\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeBlock
|
||||||
|
BlockReference *
|
||||||
|
BlockAllocator::ResizeBlock(BlockReference *blockReference, size_t usableSize)
|
||||||
|
{
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
if (fPanic)
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
D(if (!CheckBlock(blockReference)) return NULL;);
|
||||||
|
//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", blockReference, usableSize));
|
||||||
|
Block *block = (blockReference ? blockReference->GetBlock() : NULL);
|
||||||
|
Block *resultBlock = NULL;
|
||||||
|
Area *area = NULL;
|
||||||
|
if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) {
|
||||||
|
//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", block, usableSize));
|
||||||
|
if (usableSize) {
|
||||||
|
// try to let the area resize the block
|
||||||
|
size_t blockSize = block->GetSize();
|
||||||
|
size_t areaFreeBytes = area->GetFreeBytes();
|
||||||
|
bool needsDefragmenting = area->NeedsDefragmenting();
|
||||||
|
//PRINT((" block reference: %p / %p\n", blockReference, block->GetReference()));
|
||||||
|
resultBlock = area->ResizeBlock(block, usableSize);
|
||||||
|
block = blockReference->GetBlock();
|
||||||
|
if (resultBlock) {
|
||||||
|
//PRINT((" area succeeded in resizing the block\n"));
|
||||||
|
//PRINT((" block reference now: %p\n", resultBlock->GetReference()));
|
||||||
|
// the area was able to resize the block
|
||||||
|
_RethinkAreaBucket(area, area->GetBucket(),
|
||||||
|
needsDefragmenting);
|
||||||
|
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
||||||
|
// Defragment only, if the area was able to resize the block,
|
||||||
|
// the new block is smaller than the old one and defragmenting
|
||||||
|
// is recommended.
|
||||||
|
if (blockSize > resultBlock->GetSize()
|
||||||
|
&& _DefragmentingRecommended()) {
|
||||||
|
_Defragment();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//PRINT((" area failed to resize the block\n"));
|
||||||
|
// the area failed: allocate a new block, copy the data, and
|
||||||
|
// free the old one
|
||||||
|
resultBlock = _AllocateBlock(usableSize);
|
||||||
|
block = blockReference->GetBlock();
|
||||||
|
if (resultBlock) {
|
||||||
|
memcpy(resultBlock->GetData(), block->GetData(),
|
||||||
|
block->GetUsableSize());
|
||||||
|
resultBlock->SetReference(block->GetReference());
|
||||||
|
_FreeBlock(area, block, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
FreeBlock(blockReference);
|
||||||
|
D(SanityCheck(false));
|
||||||
|
//PRINT(("BlockAllocator::ResizeBlock() done: %p\n", resultBlock));
|
||||||
|
}
|
||||||
|
return (resultBlock ? resultBlock->GetReference() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanityCheck
|
||||||
|
bool
|
||||||
|
BlockAllocator::SanityCheck(bool deep) const
|
||||||
|
{
|
||||||
|
// iterate through all areas of all buckets
|
||||||
|
int32 areaCount = 0;
|
||||||
|
size_t freeBytes = 0;
|
||||||
|
for (int32 i = 0; i < fBucketCount; i++) {
|
||||||
|
AreaBucket *bucket = fBuckets + i;
|
||||||
|
if (deep) {
|
||||||
|
if (!bucket->SanityCheck(deep))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Area *area = bucket->GetFirstArea();
|
||||||
|
area;
|
||||||
|
area = bucket->GetNextArea(area)) {
|
||||||
|
areaCount++;
|
||||||
|
freeBytes += area->GetFreeBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// area count
|
||||||
|
if (areaCount != fAreaCount) {
|
||||||
|
FATAL(("fAreaCount is %ld, but should be %ld\n", fAreaCount,
|
||||||
|
areaCount));
|
||||||
|
BA_PANIC("BlockAllocator: Bad free bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// free bytes
|
||||||
|
if (fFreeBytes != freeBytes) {
|
||||||
|
FATAL(("fFreeBytes is %lu, but should be %lu\n", fFreeBytes,
|
||||||
|
freeBytes));
|
||||||
|
BA_PANIC("BlockAllocator: Bad free bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckArea
|
||||||
|
bool
|
||||||
|
BlockAllocator::CheckArea(Area *checkArea)
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < fBucketCount; i++) {
|
||||||
|
AreaBucket *bucket = fBuckets + i;
|
||||||
|
for (Area *area = bucket->GetFirstArea();
|
||||||
|
area;
|
||||||
|
area = bucket->GetNextArea(area)) {
|
||||||
|
if (area == checkArea)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FATAL(("Area %p is not a valid Area!\n", checkArea));
|
||||||
|
BA_PANIC("Invalid Area.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBlock
|
||||||
|
bool
|
||||||
|
BlockAllocator::CheckBlock(Block *block, size_t minSize)
|
||||||
|
{
|
||||||
|
Area *area = _AreaForBlock(block);
|
||||||
|
return (area/* && CheckArea(area)*/ && area->CheckBlock(block, minSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBlock
|
||||||
|
bool
|
||||||
|
BlockAllocator::CheckBlock(BlockReference *reference, size_t minSize)
|
||||||
|
{
|
||||||
|
return (fReferenceManager.CheckReference(reference)
|
||||||
|
&& CheckBlock(reference->GetBlock(), minSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
BlockAllocator::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
fReferenceManager.GetAllocationInfo(info);
|
||||||
|
info.AddOtherAllocation(sizeof(AreaBucket), fBucketCount);
|
||||||
|
info.AddAreaAllocation(fAreaSize, fAreaCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AreaForBlock
|
||||||
|
inline
|
||||||
|
BlockAllocator::Area *
|
||||||
|
BlockAllocator::_AreaForBlock(Block *block)
|
||||||
|
{
|
||||||
|
Area *area = NULL;
|
||||||
|
area_id id = area_for(block);
|
||||||
|
area_info info;
|
||||||
|
if (id >= 0 && get_area_info(id, &info) == B_OK)
|
||||||
|
area = (Area*)info.address;
|
||||||
|
D(if (!CheckArea(area)) return NULL;);
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AllocateBlock
|
||||||
|
Block *
|
||||||
|
BlockAllocator::_AllocateBlock(size_t usableSize, bool dontCreateArea)
|
||||||
|
{
|
||||||
|
Block *block = NULL;
|
||||||
|
// Get the last area (the one with the most free space) and try
|
||||||
|
// to let it allocate a block. If that fails, allocate a new area.
|
||||||
|
// find a bucket for the allocation
|
||||||
|
// TODO: optimize
|
||||||
|
AreaBucket *bucket = NULL;
|
||||||
|
int32 index = bucket_containing_min_size(usableSize);
|
||||||
|
for (; index < fBucketCount; index++) {
|
||||||
|
if (!fBuckets[index].IsEmpty()) {
|
||||||
|
bucket = fBuckets + index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// get an area: if we have one, from the bucket, else create a new
|
||||||
|
// area
|
||||||
|
Area *area = NULL;
|
||||||
|
if (bucket)
|
||||||
|
area = bucket->GetFirstArea();
|
||||||
|
else if (!dontCreateArea) {
|
||||||
|
area = Area::Create(fAreaSize);
|
||||||
|
if (area) {
|
||||||
|
fAreaCount++;
|
||||||
|
fFreeBytes += area->GetFreeBytes();
|
||||||
|
bucket = fBuckets + area->GetBucketIndex();
|
||||||
|
bucket->AddArea(area);
|
||||||
|
PRINT(("New area allocated. area count now: %ld, free bytes: %lu\n",
|
||||||
|
fAreaCount, fFreeBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// allocate a block
|
||||||
|
if (area) {
|
||||||
|
size_t areaFreeBytes = area->GetFreeBytes();
|
||||||
|
bool needsDefragmenting = area->NeedsDefragmenting();
|
||||||
|
block = area->AllocateBlock(usableSize);
|
||||||
|
// move the area into another bucket, if necessary
|
||||||
|
if (block) {
|
||||||
|
_RethinkAreaBucket(area, bucket, needsDefragmenting);
|
||||||
|
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
||||||
|
}
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
else if (!fPanic) {
|
||||||
|
FATAL(("Block allocation failed unexpectedly.\n"));
|
||||||
|
PRINT((" usableSize: %lu, areaFreeBytes: %lu\n", usableSize, areaFreeBytes));
|
||||||
|
BA_PANIC("Block allocation failed unexpectedly.");
|
||||||
|
//block = area->AllocateBlock(usableSize);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FreeBlock
|
||||||
|
void
|
||||||
|
BlockAllocator::_FreeBlock(Area *area, Block *block, bool freeReference)
|
||||||
|
{
|
||||||
|
size_t areaFreeBytes = area->GetFreeBytes();
|
||||||
|
AreaBucket *bucket = area->GetBucket();
|
||||||
|
bool needsDefragmenting = area->NeedsDefragmenting();
|
||||||
|
// free the block and the block reference
|
||||||
|
BlockReference *reference = block->GetReference();
|
||||||
|
area->FreeBlock(block);
|
||||||
|
if (reference && freeReference)
|
||||||
|
fReferenceManager.FreeReference(reference);
|
||||||
|
// move the area into another bucket, if necessary
|
||||||
|
_RethinkAreaBucket(area, bucket, needsDefragmenting);
|
||||||
|
fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _RethinkAreaBucket
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockAllocator::_RethinkAreaBucket(Area *area, AreaBucket *bucket,
|
||||||
|
bool needsDefragmenting)
|
||||||
|
{
|
||||||
|
AreaBucket *newBucket = fBuckets + area->GetBucketIndex();
|
||||||
|
if (newBucket != bucket
|
||||||
|
|| needsDefragmenting != area->NeedsDefragmenting()) {
|
||||||
|
bucket->RemoveArea(area);
|
||||||
|
newBucket->AddArea(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _DefragmentingRecommended
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
BlockAllocator::_DefragmentingRecommended()
|
||||||
|
{
|
||||||
|
// Don't know, whether this makes much sense: We don't try to defragment,
|
||||||
|
// when not at least a complete area could be deleted, and some tolerance
|
||||||
|
// being left (a fixed value plus 1/32 of the used bytes).
|
||||||
|
size_t usedBytes = fAreaCount * Area::GetMaxFreeBytesFor(fAreaSize)
|
||||||
|
- fFreeBytes;
|
||||||
|
return (fFreeBytes > fAreaSize + kDefragmentingTolerance + usedBytes / 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Defragment
|
||||||
|
bool
|
||||||
|
BlockAllocator::_Defragment()
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
// We try to empty the least populated area by moving its blocks to other
|
||||||
|
// areas.
|
||||||
|
if (fFreeBytes > fAreaSize) {
|
||||||
|
// find the least populated area
|
||||||
|
// find the bucket with the least populated areas
|
||||||
|
AreaBucket *bucket = NULL;
|
||||||
|
for (int32 i = fBucketCount - 1; i >= 0; i--) {
|
||||||
|
if (!fBuckets[i].IsEmpty()) {
|
||||||
|
bucket = fBuckets + i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// find the area in the bucket
|
||||||
|
Area *area = NULL;
|
||||||
|
if (bucket) {
|
||||||
|
area = bucket->GetFirstArea();
|
||||||
|
Area *bucketArea = area;
|
||||||
|
while ((bucketArea = bucket->GetNextArea(bucketArea)) != NULL) {
|
||||||
|
if (bucketArea->GetFreeBytes() > area->GetFreeBytes())
|
||||||
|
area = bucketArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (area) {
|
||||||
|
// remove the area from the bucket
|
||||||
|
bucket->RemoveArea(area);
|
||||||
|
fFreeBytes -= area->GetFreeBytes();
|
||||||
|
// iterate through the blocks in the area and try to find a new
|
||||||
|
// home for them
|
||||||
|
success = true;
|
||||||
|
while (Block *block = area->GetFirstUsedBlock()) {
|
||||||
|
Block *newBlock = _AllocateBlock(block->GetUsableSize(), true);
|
||||||
|
if (newBlock) {
|
||||||
|
// got a new block: copy the data to it and free the old
|
||||||
|
// one
|
||||||
|
memcpy(newBlock->GetData(), block->GetData(),
|
||||||
|
block->GetUsableSize());
|
||||||
|
newBlock->SetReference(block->GetReference());
|
||||||
|
block->SetReference(NULL);
|
||||||
|
area->FreeBlock(block, true);
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
if (fPanic) {
|
||||||
|
PRINT(("Panicked while trying to free block %p\n",
|
||||||
|
block));
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete the area
|
||||||
|
if (success && area->IsEmpty()) {
|
||||||
|
area->Delete();
|
||||||
|
fAreaCount--;
|
||||||
|
PRINT(("defragmenting: area deleted\n"));
|
||||||
|
} else {
|
||||||
|
PRINT(("defragmenting: failed to empty area\n"));
|
||||||
|
// failed: re-add the area
|
||||||
|
fFreeBytes += area->GetFreeBytes();
|
||||||
|
AreaBucket *newBucket = fBuckets + area->GetBucketIndex();
|
||||||
|
newBucket->AddArea(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
D(SanityCheck(false));
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
bool BlockAllocator::fPanic = false;
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// BlockAllocator.h
|
||||||
|
|
||||||
|
#ifndef BLOCK_ALLOCATOR_H
|
||||||
|
#define BLOCK_ALLOCATOR_H
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
#include "Block.h"
|
||||||
|
#include "BlockReferenceManager.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "List.h"
|
||||||
|
|
||||||
|
#define ENABLE_BA_PANIC 1
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
#define BA_PANIC(x) { PANIC(x); BlockAllocator::fPanic = true; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class AllocationInfo;
|
||||||
|
|
||||||
|
// BlockAllocator
|
||||||
|
class BlockAllocator {
|
||||||
|
public:
|
||||||
|
BlockAllocator(size_t areaSize);
|
||||||
|
~BlockAllocator();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
BlockReference *AllocateBlock(size_t usableSize);
|
||||||
|
void FreeBlock(BlockReference *block);
|
||||||
|
BlockReference *ResizeBlock(BlockReference *block, size_t usableSize);
|
||||||
|
|
||||||
|
size_t GetAvailableBytes() const { return fAreaCount * fAreaSize; }
|
||||||
|
size_t GetFreeBytes() const { return fFreeBytes; }
|
||||||
|
size_t GetUsedBytes() const { return fAreaCount * fAreaSize
|
||||||
|
- fFreeBytes; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Area;
|
||||||
|
class AreaBucket;
|
||||||
|
|
||||||
|
// debugging only
|
||||||
|
bool SanityCheck(bool deep = false) const;
|
||||||
|
bool CheckArea(Area *area);
|
||||||
|
bool CheckBlock(Block *block, size_t minSize = 0);
|
||||||
|
bool CheckBlock(BlockReference *reference, size_t minSize = 0);
|
||||||
|
void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline Area *_AreaForBlock(Block *block);
|
||||||
|
Block *_AllocateBlock(size_t usableSize, bool dontCreateArea = false);
|
||||||
|
void _FreeBlock(Area *area, Block *block, bool freeReference);
|
||||||
|
inline void _RethinkAreaBucket(Area *area, AreaBucket *bucket,
|
||||||
|
bool needsDefragmenting);
|
||||||
|
inline bool _DefragmentingRecommended();
|
||||||
|
bool _Defragment();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockReferenceManager fReferenceManager;
|
||||||
|
AreaBucket *fBuckets;
|
||||||
|
int32 fBucketCount;
|
||||||
|
size_t fAreaSize;
|
||||||
|
int32 fAreaCount;
|
||||||
|
size_t fFreeBytes;
|
||||||
|
#if ENABLE_BA_PANIC
|
||||||
|
public:
|
||||||
|
static bool fPanic;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BLOCK_ALLOCATOR_H
|
||||||
@@ -0,0 +1,596 @@
|
|||||||
|
// BlockAllocatorArea.cpp
|
||||||
|
|
||||||
|
#include "BlockAllocatorArea.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
BlockAllocator::Area::Area(area_id id, size_t size)
|
||||||
|
: fBucket(NULL),
|
||||||
|
fID(id),
|
||||||
|
fSize(size),
|
||||||
|
fFreeBytes(0),
|
||||||
|
fFreeBlockCount(1),
|
||||||
|
fUsedBlockCount(0),
|
||||||
|
fFirstBlock(NULL),
|
||||||
|
fLastBlock(NULL),
|
||||||
|
fFirstFree(NULL),
|
||||||
|
fLastFree(NULL)
|
||||||
|
{
|
||||||
|
size_t headerSize = block_align_ceil(sizeof(Area));
|
||||||
|
fFirstFree = (TFreeBlock*)((char*)this + headerSize);
|
||||||
|
fFirstFree->SetTo(NULL, block_align_floor(fSize - headerSize), false, NULL,
|
||||||
|
NULL);
|
||||||
|
fFirstBlock = fLastBlock = fLastFree = fFirstFree;
|
||||||
|
fFreeBytes = fFirstFree->GetUsableSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create
|
||||||
|
BlockAllocator::Area *
|
||||||
|
BlockAllocator::Area::Create(size_t size)
|
||||||
|
{
|
||||||
|
Area *area = NULL;
|
||||||
|
void *base = NULL;
|
||||||
|
#if USER
|
||||||
|
area_id id = create_area("block alloc", &base, B_ANY_ADDRESS,
|
||||||
|
size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
||||||
|
#else
|
||||||
|
area_id id = create_area("block alloc", &base, B_ANY_KERNEL_ADDRESS,
|
||||||
|
size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
|
||||||
|
#endif
|
||||||
|
if (id >= 0) {
|
||||||
|
area = new(base) Area(id, size);
|
||||||
|
} else {
|
||||||
|
ERROR(("BlockAllocator::Area::Create(%lu): Failed to create area: %s\n",
|
||||||
|
size, strerror(id)));
|
||||||
|
}
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::Delete()
|
||||||
|
{
|
||||||
|
delete_area(fID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllocateBlock
|
||||||
|
Block *
|
||||||
|
BlockAllocator::Area::AllocateBlock(size_t usableSize, bool dontDefragment)
|
||||||
|
{
|
||||||
|
if (kMinBlockSize != block_align_ceil(sizeof(TFreeBlock))) {
|
||||||
|
FATAL(("kMinBlockSize is not correctly initialized! Is %lu, but should be: "
|
||||||
|
"%lu\n", kMinBlockSize, block_align_ceil(sizeof(TFreeBlock))));
|
||||||
|
BA_PANIC("kMinBlockSize not correctly initialized.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (usableSize == 0)
|
||||||
|
return NULL;
|
||||||
|
Block *newBlock = NULL;
|
||||||
|
size_t size = max(usableSize + sizeof(BlockHeader), kMinBlockSize);
|
||||||
|
size = block_align_ceil(size);
|
||||||
|
if (size <= _GetBlockFreeBytes()) {
|
||||||
|
// find first fit
|
||||||
|
TFreeBlock *block = _FindFreeBlock(size);
|
||||||
|
if (!block && !dontDefragment) {
|
||||||
|
// defragmenting is necessary
|
||||||
|
_Defragment();
|
||||||
|
block = _FindFreeBlock(size);
|
||||||
|
if (!block) {
|
||||||
|
// no free block
|
||||||
|
// Our data structures seem to be corrupted, since
|
||||||
|
// _GetBlockFreeBytes() promised that we would have enough
|
||||||
|
// free space.
|
||||||
|
FATAL(("Couldn't find free block of min size %lu after "
|
||||||
|
"defragmenting, although we should have %lu usable free "
|
||||||
|
"bytes!\n", size, _GetBlockFreeBytes()));
|
||||||
|
BA_PANIC("Bad area free bytes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block) {
|
||||||
|
// found a free block
|
||||||
|
size_t remainder = block->GetSize() - size;
|
||||||
|
if (remainder >= kMinBlockSize) {
|
||||||
|
// enough space left for a free block
|
||||||
|
Block *freePrev = block->GetPreviousBlock();
|
||||||
|
// TFreeBlock *prevFree = block->GetPreviousFreeBlock();
|
||||||
|
// TFreeBlock *nextFree = block->GetNextFreeBlock();
|
||||||
|
// newBlock = block;
|
||||||
|
_MoveResizeFreeBlock(block, size, remainder);
|
||||||
|
// setup the new block
|
||||||
|
// newBlock->SetSize(size, true);
|
||||||
|
// newBlock->SetFree(false);
|
||||||
|
newBlock = _MakeUsedBlock(block, 0, freePrev, size, true);
|
||||||
|
} else {
|
||||||
|
// not enough space left: take the free block over completely
|
||||||
|
// remove the block from the free list
|
||||||
|
_RemoveFreeBlock(block);
|
||||||
|
newBlock = block;
|
||||||
|
newBlock->SetFree(false);
|
||||||
|
}
|
||||||
|
if (fFreeBlockCount)
|
||||||
|
fFreeBytes -= newBlock->GetSize();
|
||||||
|
else
|
||||||
|
fFreeBytes = 0;
|
||||||
|
fUsedBlockCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
D(SanityCheck());
|
||||||
|
return newBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeBlock
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::FreeBlock(Block *block, bool dontDefragment)
|
||||||
|
{
|
||||||
|
if (block) {
|
||||||
|
// mark the block free and insert it into the free list
|
||||||
|
block->SetFree(true);
|
||||||
|
TFreeBlock *freeBlock = (TFreeBlock*)block;
|
||||||
|
_InsertFreeBlock(freeBlock);
|
||||||
|
fUsedBlockCount--;
|
||||||
|
if (fFreeBlockCount == 1)
|
||||||
|
fFreeBytes += freeBlock->GetUsableSize();
|
||||||
|
else
|
||||||
|
fFreeBytes += freeBlock->GetSize();
|
||||||
|
// try coalescing with the next and the previous free block
|
||||||
|
D(SanityCheck());
|
||||||
|
_CoalesceWithNext(freeBlock);
|
||||||
|
D(SanityCheck());
|
||||||
|
_CoalesceWithNext(freeBlock->GetPreviousFreeBlock());
|
||||||
|
// defragment, if sensible
|
||||||
|
if (!dontDefragment && _DefragmentingRecommended())
|
||||||
|
_Defragment();
|
||||||
|
D(SanityCheck());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeBlock
|
||||||
|
Block *
|
||||||
|
BlockAllocator::Area::ResizeBlock(Block *block, size_t newUsableSize,
|
||||||
|
bool dontDefragment)
|
||||||
|
{
|
||||||
|
//PRINT(("Area::ResizeBlock(%p, %lu)\n", block, newUsableSize));
|
||||||
|
// newUsableSize must be >0 !
|
||||||
|
if (newUsableSize == 0)
|
||||||
|
return NULL;
|
||||||
|
Block *resultBlock = NULL;
|
||||||
|
if (block) {
|
||||||
|
size_t size = block->GetSize();
|
||||||
|
size_t newSize = max(newUsableSize + sizeof(BlockHeader),
|
||||||
|
kMinBlockSize);
|
||||||
|
newSize = block_align_ceil(newSize);
|
||||||
|
if (newSize == size) {
|
||||||
|
// size doesn't change: nothing to do
|
||||||
|
resultBlock = block;
|
||||||
|
} else if (newSize < size) {
|
||||||
|
// shrink the block
|
||||||
|
size_t sizeDiff = size - newSize;
|
||||||
|
Block *nextBlock = block->GetNextBlock();
|
||||||
|
if (nextBlock && nextBlock->IsFree()) {
|
||||||
|
// join the space with the adjoining free block
|
||||||
|
TFreeBlock *freeBlock = nextBlock->ToFreeBlock();
|
||||||
|
_MoveResizeFreeBlock(freeBlock, -sizeDiff,
|
||||||
|
freeBlock->GetSize() + sizeDiff);
|
||||||
|
// resize the block and we're done
|
||||||
|
block->SetSize(newSize, true);
|
||||||
|
fFreeBytes += sizeDiff;
|
||||||
|
} else if (sizeDiff >= sizeof(TFreeBlock)) {
|
||||||
|
// the freed space is large enough for a free block
|
||||||
|
TFreeBlock *newFree = _MakeFreeBlock(block, newSize, block,
|
||||||
|
sizeDiff, nextBlock, NULL, NULL);
|
||||||
|
_InsertFreeBlock(newFree);
|
||||||
|
block->SetSize(newSize, true);
|
||||||
|
if (fFreeBlockCount == 1)
|
||||||
|
fFreeBytes += newFree->GetUsableSize();
|
||||||
|
else
|
||||||
|
fFreeBytes += newFree->GetSize();
|
||||||
|
if (!dontDefragment && _DefragmentingRecommended())
|
||||||
|
_Defragment();
|
||||||
|
} // else: insufficient space for a free block: no changes
|
||||||
|
resultBlock = block;
|
||||||
|
} else {
|
||||||
|
//PRINT((" grow...\n"));
|
||||||
|
// grow the block
|
||||||
|
size_t sizeDiff = newSize - size;
|
||||||
|
Block *nextBlock = block->GetNextBlock();
|
||||||
|
if (nextBlock && nextBlock->IsFree()
|
||||||
|
&& nextBlock->GetSize() >= sizeDiff) {
|
||||||
|
//PRINT((" adjoining free block\n"));
|
||||||
|
// there is a adjoining free block and it is large enough
|
||||||
|
TFreeBlock *freeBlock = nextBlock->ToFreeBlock();
|
||||||
|
size_t freeSize = freeBlock->GetSize();
|
||||||
|
if (freeSize - sizeDiff >= sizeof(TFreeBlock)) {
|
||||||
|
// the remaining space is still large enough for a free
|
||||||
|
// block
|
||||||
|
_MoveResizeFreeBlock(freeBlock, sizeDiff,
|
||||||
|
freeSize - sizeDiff);
|
||||||
|
block->SetSize(newSize, true);
|
||||||
|
fFreeBytes -= sizeDiff;
|
||||||
|
} else {
|
||||||
|
// the remaining free space wouldn't be large enough for
|
||||||
|
// a free block: consume the free block completely
|
||||||
|
Block *freeNext = freeBlock->GetNextBlock();
|
||||||
|
_RemoveFreeBlock(freeBlock);
|
||||||
|
block->SetSize(size + freeSize, freeNext);
|
||||||
|
_FixBlockList(block, block->GetPreviousBlock(), freeNext);
|
||||||
|
if (fFreeBlockCount == 0)
|
||||||
|
fFreeBytes = 0;
|
||||||
|
else
|
||||||
|
fFreeBytes -= freeSize;
|
||||||
|
}
|
||||||
|
resultBlock = block;
|
||||||
|
} else {
|
||||||
|
//PRINT((" no adjoining free block\n"));
|
||||||
|
// no (large enough) adjoining free block: allocate
|
||||||
|
// a new block and copy the data to it
|
||||||
|
BlockReference *reference = block->GetReference();
|
||||||
|
resultBlock = AllocateBlock(newUsableSize, dontDefragment);
|
||||||
|
block = reference->GetBlock();
|
||||||
|
if (resultBlock) {
|
||||||
|
resultBlock->SetReference(reference);
|
||||||
|
memcpy(resultBlock->GetData(), block->GetData(),
|
||||||
|
block->GetUsableSize());
|
||||||
|
FreeBlock(block, dontDefragment);
|
||||||
|
resultBlock = reference->GetBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
D(SanityCheck());
|
||||||
|
//PRINT(("Area::ResizeBlock() done: %p\n", resultBlock));
|
||||||
|
return resultBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanityCheck
|
||||||
|
bool
|
||||||
|
BlockAllocator::Area::SanityCheck() const
|
||||||
|
{
|
||||||
|
// area ID
|
||||||
|
if (fID < 0) {
|
||||||
|
FATAL(("Area ID < 0: %lx\n", fID));
|
||||||
|
BA_PANIC("Bad area ID.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// size
|
||||||
|
size_t areaHeaderSize = block_align_ceil(sizeof(Area));
|
||||||
|
if (fSize < areaHeaderSize + sizeof(TFreeBlock)) {
|
||||||
|
FATAL(("Area too small to contain area header and at least one free "
|
||||||
|
"block: %lu bytes\n", fSize));
|
||||||
|
BA_PANIC("Bad area size.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// free bytes
|
||||||
|
if (fFreeBytes > fSize) {
|
||||||
|
FATAL(("Free size greater than area size: %lu vs %lu\n", fFreeBytes,
|
||||||
|
fSize));
|
||||||
|
BA_PANIC("Bad area free bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// block count
|
||||||
|
if (fFreeBlockCount + fUsedBlockCount == 0) {
|
||||||
|
FATAL(("Area contains no blocks at all.\n"));
|
||||||
|
BA_PANIC("Bad area block count.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// block list
|
||||||
|
uint32 usedBlockCount = 0;
|
||||||
|
uint32 freeBlockCount = 0;
|
||||||
|
size_t freeBytes = 0;
|
||||||
|
if (!fFirstBlock || !fLastBlock) {
|
||||||
|
FATAL(("Invalid block list: first or last block NULL: first: %p, "
|
||||||
|
"last: %p\n", fFirstBlock, fLastBlock));
|
||||||
|
BA_PANIC("Bad area block list.");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// iterate through block list and also check free list
|
||||||
|
int32 blockCount = fFreeBlockCount + fUsedBlockCount;
|
||||||
|
Block *block = fFirstBlock;
|
||||||
|
Block *prevBlock = NULL;
|
||||||
|
Block *prevFree = NULL;
|
||||||
|
Block *nextFree = fFirstFree;
|
||||||
|
bool blockListOK = true;
|
||||||
|
for (int32 i = 0; i < blockCount; i++) {
|
||||||
|
blockListOK = false;
|
||||||
|
if (!block) {
|
||||||
|
FATAL(("Encountered NULL in block list at index %ld, although "
|
||||||
|
"list should have %ld blocks\n", i, blockCount));
|
||||||
|
BA_PANIC("Bad area block list.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint64 address = (uint32)block;
|
||||||
|
// block within area?
|
||||||
|
if (address < (uint32)this + areaHeaderSize
|
||||||
|
|| address + sizeof(TFreeBlock) > (uint32)this + fSize) {
|
||||||
|
FATAL(("Utterly mislocated block: %p, area: %p, "
|
||||||
|
"size: %lu\n", block, this, fSize));
|
||||||
|
BA_PANIC("Bad area block.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// block too large for area?
|
||||||
|
size_t blockSize = block->GetSize();
|
||||||
|
if (blockSize < sizeof(TFreeBlock)
|
||||||
|
|| address + blockSize > (uint32)this + fSize) {
|
||||||
|
FATAL(("Mislocated block: %p, size: %lu, area: %p, "
|
||||||
|
"size: %lu\n", block, blockSize, this, fSize));
|
||||||
|
BA_PANIC("Bad area block.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// alignment
|
||||||
|
if (block_align_floor(address) != address
|
||||||
|
|| block_align_floor(blockSize) != blockSize) {
|
||||||
|
FATAL(("Block %ld not properly aligned: %p, size: %lu\n",
|
||||||
|
i, block, blockSize));
|
||||||
|
BA_PANIC("Bad area block.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// previous block
|
||||||
|
if (block->GetPreviousBlock() != prevBlock) {
|
||||||
|
FATAL(("Previous block of block %ld was not the previous "
|
||||||
|
"block in list: %p vs %p\n", i,
|
||||||
|
block->GetPreviousBlock(), prevBlock));
|
||||||
|
BA_PANIC("Bad area block list.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// additional checks for free block list
|
||||||
|
if (block->IsFree()) {
|
||||||
|
freeBlockCount++;
|
||||||
|
TFreeBlock *freeBlock = block->ToFreeBlock();
|
||||||
|
if (prevFree)
|
||||||
|
freeBytes += freeBlock->GetSize();
|
||||||
|
else
|
||||||
|
freeBytes += freeBlock->GetUsableSize();
|
||||||
|
// block == next free block of previous free block
|
||||||
|
if (freeBlock != nextFree) {
|
||||||
|
FATAL(("Free block %ld is not the next block in free "
|
||||||
|
"list: %p vs %p\n", i, freeBlock, nextFree));
|
||||||
|
BA_PANIC("Bad area free list.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// previous free block
|
||||||
|
if (freeBlock->GetPreviousFreeBlock() != prevFree) {
|
||||||
|
FATAL(("Previous free block of block %ld was not the "
|
||||||
|
" previous block in free list: %p vs %p\n", i,
|
||||||
|
freeBlock->GetPreviousFreeBlock(), prevFree));
|
||||||
|
BA_PANIC("Bad area free list.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
prevFree = freeBlock;
|
||||||
|
nextFree = freeBlock->GetNextFreeBlock();
|
||||||
|
} else
|
||||||
|
usedBlockCount++;
|
||||||
|
prevBlock = block;
|
||||||
|
block = block->GetNextBlock();
|
||||||
|
blockListOK = true;
|
||||||
|
}
|
||||||
|
// final checks on block list
|
||||||
|
if (blockListOK) {
|
||||||
|
if (block) {
|
||||||
|
FATAL(("More blocks in block list than expected\n"));
|
||||||
|
BA_PANIC("Bad area block count.");
|
||||||
|
return false;
|
||||||
|
} else if (fLastBlock != prevBlock) {
|
||||||
|
FATAL(("last block in block list was %p, but should be "
|
||||||
|
"%p\n", fLastBlock, prevBlock));
|
||||||
|
BA_PANIC("Bad area last block.");
|
||||||
|
return false;
|
||||||
|
} else if (prevFree != fLastFree) {
|
||||||
|
FATAL(("last block in free list was %p, but should be %p\n",
|
||||||
|
fLastFree, prevFree));
|
||||||
|
BA_PANIC("Bad area last free block.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// block counts (a bit reduntant)
|
||||||
|
if (freeBlockCount != fFreeBlockCount) {
|
||||||
|
FATAL(("Free block count is %ld, but should be %ld\n",
|
||||||
|
fFreeBlockCount, freeBlockCount));
|
||||||
|
BA_PANIC("Bad area free block count.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (usedBlockCount != fUsedBlockCount) {
|
||||||
|
FATAL(("Used block count is %ld, but should be %ld\n",
|
||||||
|
fUsedBlockCount, usedBlockCount));
|
||||||
|
BA_PANIC("Bad area used block count.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// free bytes
|
||||||
|
if (fFreeBytes != freeBytes) {
|
||||||
|
FATAL(("Free bytes is %lu, but should be %lu\n",
|
||||||
|
fFreeBytes, freeBytes));
|
||||||
|
BA_PANIC("Bad area free bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBlock
|
||||||
|
bool
|
||||||
|
BlockAllocator::Area::CheckBlock(Block *checkBlock, size_t minSize)
|
||||||
|
{
|
||||||
|
for (Block *block = fFirstBlock; block; block = block->GetNextBlock()) {
|
||||||
|
if (block == checkBlock)
|
||||||
|
return (block->GetUsableSize() >= minSize);
|
||||||
|
}
|
||||||
|
FATAL(("Block %p is not in area %p!\n", checkBlock, this));
|
||||||
|
BA_PANIC("Invalid Block.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FindFreeBlock
|
||||||
|
TFreeBlock *
|
||||||
|
BlockAllocator::Area::_FindFreeBlock(size_t minSize)
|
||||||
|
{
|
||||||
|
// first fit
|
||||||
|
for (TFreeBlock *block = GetFirstFreeBlock();
|
||||||
|
block;
|
||||||
|
block = block->GetNextFreeBlock()) {
|
||||||
|
if (block->GetSize() >= minSize)
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _InsertFreeBlock
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::_InsertFreeBlock(TFreeBlock *block)
|
||||||
|
{
|
||||||
|
if (block) {
|
||||||
|
// find the free block before which this one has to be inserted
|
||||||
|
TFreeBlock *nextFree = NULL;
|
||||||
|
for (nextFree = GetFirstFreeBlock();
|
||||||
|
nextFree;
|
||||||
|
nextFree = nextFree->GetNextFreeBlock()) {
|
||||||
|
if ((uint32)nextFree > (uint32)block)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// get the previous block and insert the block between the two
|
||||||
|
TFreeBlock *prevFree
|
||||||
|
= (nextFree ? nextFree->GetPreviousFreeBlock() : fLastFree);
|
||||||
|
_FixFreeList(block, prevFree, nextFree);
|
||||||
|
fFreeBlockCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _RemoveFreeBlock
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::_RemoveFreeBlock(TFreeBlock *block)
|
||||||
|
{
|
||||||
|
if (block) {
|
||||||
|
TFreeBlock *prevFree = block->GetPreviousFreeBlock();
|
||||||
|
TFreeBlock *nextFree = block->GetNextFreeBlock();
|
||||||
|
if (prevFree)
|
||||||
|
prevFree->SetNextFreeBlock(nextFree);
|
||||||
|
else
|
||||||
|
fFirstFree = nextFree;
|
||||||
|
if (nextFree)
|
||||||
|
nextFree->SetPreviousFreeBlock(prevFree);
|
||||||
|
else
|
||||||
|
fLastFree = prevFree;
|
||||||
|
}
|
||||||
|
fFreeBlockCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _MoveResizeFreeBlock
|
||||||
|
TFreeBlock *
|
||||||
|
BlockAllocator::Area::_MoveResizeFreeBlock(TFreeBlock *freeBlock,
|
||||||
|
ssize_t offset, size_t newSize)
|
||||||
|
{
|
||||||
|
TFreeBlock *movedFree = NULL;
|
||||||
|
if (freeBlock && offset) {
|
||||||
|
// move the header of the free block
|
||||||
|
Block *freePrev = freeBlock->GetPreviousBlock();
|
||||||
|
TFreeBlock *prevFree = freeBlock->GetPreviousFreeBlock();
|
||||||
|
TFreeBlock *nextFree = freeBlock->GetNextFreeBlock();
|
||||||
|
movedFree = _MakeFreeBlock(freeBlock, offset, freePrev, newSize,
|
||||||
|
freeBlock->HasNextBlock(), prevFree, nextFree);
|
||||||
|
// update the free list
|
||||||
|
_FixFreeList(movedFree, prevFree, nextFree);
|
||||||
|
}
|
||||||
|
return movedFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _MakeFreeBlock
|
||||||
|
inline
|
||||||
|
TFreeBlock *
|
||||||
|
BlockAllocator::Area::_MakeFreeBlock(void *address, ssize_t offset,
|
||||||
|
Block *previous, size_t size,
|
||||||
|
bool hasNext, TFreeBlock *previousFree,
|
||||||
|
TFreeBlock *nextFree)
|
||||||
|
{
|
||||||
|
TFreeBlock *block = (TFreeBlock*)((char*)address + offset);
|
||||||
|
block->SetTo(previous, size, hasNext, previousFree, nextFree);
|
||||||
|
if (hasNext)
|
||||||
|
block->GetNextBlock()->SetPreviousBlock(block);
|
||||||
|
else
|
||||||
|
fLastBlock = block;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _CoalesceWithNext
|
||||||
|
bool
|
||||||
|
BlockAllocator::Area::_CoalesceWithNext(TFreeBlock *block)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
TFreeBlock *nextFree = NULL;
|
||||||
|
if (block && (nextFree = block->GetNextFreeBlock()) != NULL
|
||||||
|
&& block->GetNextBlock() == nextFree) {
|
||||||
|
_RemoveFreeBlock(nextFree);
|
||||||
|
Block *nextBlock = nextFree->GetNextBlock();
|
||||||
|
block->SetSize(block->GetSize() + nextFree->GetSize(), nextBlock);
|
||||||
|
if (nextBlock)
|
||||||
|
nextBlock->SetPreviousBlock(block);
|
||||||
|
else
|
||||||
|
fLastBlock = block;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _MakeUsedBlock
|
||||||
|
inline
|
||||||
|
Block *
|
||||||
|
BlockAllocator::Area::_MakeUsedBlock(void *address, ssize_t offset,
|
||||||
|
Block *previous, size_t size,
|
||||||
|
bool hasNext)
|
||||||
|
{
|
||||||
|
Block *block = (Block*)((char*)address + offset);
|
||||||
|
block->SetTo(previous, size, false, hasNext, NULL);
|
||||||
|
if (hasNext)
|
||||||
|
block->GetNextBlock()->SetPreviousBlock(block);
|
||||||
|
else
|
||||||
|
fLastBlock = block;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Defragment
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::_Defragment()
|
||||||
|
{
|
||||||
|
D(SanityCheck());
|
||||||
|
//PRINT(("BlockAllocator::Area::_Defragment()\n"));
|
||||||
|
// A trivial strategy for now: Keep the last free block and move the
|
||||||
|
// others so that they can be joined with it. This is done iteratively
|
||||||
|
// by moving the first free block to adjoin to the second one and
|
||||||
|
// coalescing them. A free block is moved by moving the data blocks in
|
||||||
|
// between.
|
||||||
|
TFreeBlock *nextFree = NULL;
|
||||||
|
while (fFirstFree && (nextFree = fFirstFree->GetNextFreeBlock()) != NULL) {
|
||||||
|
Block *prevBlock = fFirstFree->GetPreviousBlock();
|
||||||
|
Block *nextBlock = fFirstFree->GetNextBlock();
|
||||||
|
size_t size = fFirstFree->GetSize();
|
||||||
|
// Used blocks are relatively position independed. We can move them
|
||||||
|
// en bloc and only need to adjust the previous pointer of the first
|
||||||
|
// one.
|
||||||
|
if (!nextBlock->IsFree()) {
|
||||||
|
// move the used blocks
|
||||||
|
size_t chunkSize = (char*)nextFree - (char*)nextBlock;
|
||||||
|
Block *nextFreePrev = nextFree->GetPreviousBlock();
|
||||||
|
Block *movedBlock = fFirstFree;
|
||||||
|
memmove(movedBlock, nextBlock, chunkSize);
|
||||||
|
movedBlock->SetPreviousBlock(prevBlock);
|
||||||
|
// init the first free block
|
||||||
|
Block *movedNextFreePrev = (Block*)((char*)nextFreePrev - size);
|
||||||
|
fFirstFree = _MakeFreeBlock(movedBlock, chunkSize,
|
||||||
|
movedNextFreePrev, size, true, NULL, nextFree);
|
||||||
|
nextFree->SetPreviousFreeBlock(fFirstFree);
|
||||||
|
// fix the references of the moved blocks
|
||||||
|
for (Block *block = movedBlock;
|
||||||
|
block != fFirstFree;
|
||||||
|
block = block->GetNextBlock()) {
|
||||||
|
block->FixReference();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// uncoalesced adjoining free block: That should never happen,
|
||||||
|
// since we always coalesce as early as possible.
|
||||||
|
INFORM(("Warning: Found uncoalesced adjoining free blocks!\n"));
|
||||||
|
}
|
||||||
|
// coalesce the first two blocks
|
||||||
|
D(SanityCheck());
|
||||||
|
_CoalesceWithNext(fFirstFree);
|
||||||
|
D(SanityCheck());
|
||||||
|
}
|
||||||
|
//D(SanityCheck());
|
||||||
|
//PRINT(("BlockAllocator::Area::_Defragment() done\n"));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
// BlockAllocatorArea.h
|
||||||
|
|
||||||
|
#ifndef BLOCK_ALLOCATOR_AREA_H
|
||||||
|
#define BLOCK_ALLOCATOR_AREA_H
|
||||||
|
|
||||||
|
#include "BlockAllocator.h"
|
||||||
|
#include "BlockAllocatorMisc.h"
|
||||||
|
#include "DLList.h"
|
||||||
|
|
||||||
|
class BlockAllocator::Area : public DLListLinkImpl<Area> {
|
||||||
|
public:
|
||||||
|
static Area *Create(size_t size);
|
||||||
|
void Delete();
|
||||||
|
|
||||||
|
inline void SetBucket(AreaBucket *bucket) { fBucket = bucket; }
|
||||||
|
inline AreaBucket *GetBucket() const { return fBucket; }
|
||||||
|
|
||||||
|
inline Block *GetFirstBlock() const { return fFirstBlock; }
|
||||||
|
inline Block *GetLastBlock() const { return fLastBlock; }
|
||||||
|
|
||||||
|
inline TFreeBlock *GetFirstFreeBlock() const { return fFirstFree; }
|
||||||
|
inline TFreeBlock *GetLastFreeBlock() const { return fLastFree; }
|
||||||
|
|
||||||
|
inline bool IsEmpty() const { return (fUsedBlockCount == 0); }
|
||||||
|
inline Block *GetFirstUsedBlock() const;
|
||||||
|
|
||||||
|
static inline size_t GetMaxFreeBytesFor(size_t areaSize);
|
||||||
|
|
||||||
|
inline size_t GetFreeBytes() const { return fFreeBytes; }
|
||||||
|
inline bool NeedsDefragmenting() const { return (fFreeBlockCount > 1); }
|
||||||
|
|
||||||
|
inline int32 GetBucketIndex();
|
||||||
|
|
||||||
|
Block *AllocateBlock(size_t usableSize, bool dontDefragment = false);
|
||||||
|
void FreeBlock(Block *block, bool dontDefragment = false);
|
||||||
|
Block *ResizeBlock(Block *block, size_t newSize,
|
||||||
|
bool dontDefragment = false);
|
||||||
|
|
||||||
|
// debugging only
|
||||||
|
bool SanityCheck() const;
|
||||||
|
bool CheckBlock(Block *block, size_t minSize = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline size_t _GetBlockFreeBytes()
|
||||||
|
{ return fFreeBytes + sizeof(BlockHeader); }
|
||||||
|
|
||||||
|
Area(area_id id, size_t size);
|
||||||
|
~Area();
|
||||||
|
|
||||||
|
inline void _FixBlockList(Block *block, Block *prevBlock,
|
||||||
|
Block *nextBlock);
|
||||||
|
inline void _FixFreeList(TFreeBlock *block, TFreeBlock *prevFree,
|
||||||
|
TFreeBlock *nextFree);
|
||||||
|
|
||||||
|
TFreeBlock *_FindFreeBlock(size_t minSize);
|
||||||
|
void _InsertFreeBlock(TFreeBlock *block);
|
||||||
|
void _RemoveFreeBlock(TFreeBlock *block);
|
||||||
|
TFreeBlock * _MoveResizeFreeBlock(TFreeBlock *freeBlock, ssize_t offset,
|
||||||
|
size_t newSize);
|
||||||
|
inline TFreeBlock *_MakeFreeBlock(void *address, ssize_t offset,
|
||||||
|
Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree,
|
||||||
|
TFreeBlock *nextFree);
|
||||||
|
bool _CoalesceWithNext(TFreeBlock *block);
|
||||||
|
|
||||||
|
inline Block *_MakeUsedBlock(void *address, ssize_t offset,
|
||||||
|
Block *previous, size_t size, bool hasNext);
|
||||||
|
|
||||||
|
inline bool _DefragmentingRecommended();
|
||||||
|
void _Defragment();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AreaBucket *fBucket;
|
||||||
|
area_id fID;
|
||||||
|
size_t fSize;
|
||||||
|
size_t fFreeBytes;
|
||||||
|
size_t fFreeBlockCount;
|
||||||
|
size_t fUsedBlockCount;
|
||||||
|
Block *fFirstBlock;
|
||||||
|
Block *fLastBlock;
|
||||||
|
TFreeBlock *fFirstFree;
|
||||||
|
TFreeBlock *fLastFree;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BlockAllocator::Area Area;
|
||||||
|
|
||||||
|
|
||||||
|
// inline methods
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
#if BA_DEFINE_INLINES
|
||||||
|
|
||||||
|
// GetFirstUsedBlock
|
||||||
|
inline
|
||||||
|
Block *
|
||||||
|
BlockAllocator::Area::GetFirstUsedBlock() const
|
||||||
|
{
|
||||||
|
// Two assumptions:
|
||||||
|
// 1) There is always a first block. If that isn't so, our structure are
|
||||||
|
// corrupt.
|
||||||
|
// 2) If the first block is free, the second (if any) is not. Otherwise
|
||||||
|
// there were adjoining free blocks, which our coalescing strategy
|
||||||
|
// prevents.
|
||||||
|
return (fFirstBlock->IsFree() ? fFirstBlock->GetNextBlock() : fFirstBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxFreeBytesFor
|
||||||
|
inline
|
||||||
|
size_t
|
||||||
|
BlockAllocator::Area::GetMaxFreeBytesFor(size_t areaSize)
|
||||||
|
{
|
||||||
|
size_t headerSize = block_align_ceil(sizeof(Area));
|
||||||
|
return Block::GetUsableSizeFor(block_align_floor(areaSize - headerSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBucketIndex
|
||||||
|
inline
|
||||||
|
int32
|
||||||
|
BlockAllocator::Area::GetBucketIndex()
|
||||||
|
{
|
||||||
|
return bucket_containing_size(GetFreeBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FixBlockList
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::_FixBlockList(Block *block, Block *prevBlock,
|
||||||
|
Block *nextBlock)
|
||||||
|
{
|
||||||
|
if (block) {
|
||||||
|
if (prevBlock)
|
||||||
|
prevBlock->SetNextBlock(block);
|
||||||
|
else
|
||||||
|
fFirstBlock = block;
|
||||||
|
if (nextBlock)
|
||||||
|
nextBlock->SetPreviousBlock(block);
|
||||||
|
else
|
||||||
|
fLastBlock = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FixFreeList
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockAllocator::Area::_FixFreeList(TFreeBlock *block, TFreeBlock *prevFree,
|
||||||
|
TFreeBlock *nextFree)
|
||||||
|
{
|
||||||
|
if (block) {
|
||||||
|
if (prevFree)
|
||||||
|
prevFree->SetNextFreeBlock(block);
|
||||||
|
else
|
||||||
|
fFirstFree = block;
|
||||||
|
if (nextFree)
|
||||||
|
nextFree->SetPreviousFreeBlock(block);
|
||||||
|
else
|
||||||
|
fLastFree = block;
|
||||||
|
block->SetPreviousFreeBlock(prevFree);
|
||||||
|
block->SetNextFreeBlock(nextFree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _DefragmentingRecommended
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
BlockAllocator::Area::_DefragmentingRecommended()
|
||||||
|
{
|
||||||
|
// Defragmenting Condition: At least more than 5 free blocks and
|
||||||
|
// free / block ratio greater 1 / 10. Don't know, if that makes any
|
||||||
|
// sense. ;-)
|
||||||
|
return (fFreeBlockCount > 5 && fUsedBlockCount / fFreeBlockCount < 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BA_DEFINE_INLINES
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BLOCK_ALLOCATOR_AREA_H
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// BlockAllocatorAreaBucket.cpp
|
||||||
|
|
||||||
|
#include "BlockAllocatorAreaBucket.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
BlockAllocator::AreaBucket::AreaBucket()
|
||||||
|
: fAreas(),
|
||||||
|
fIndex(-1),
|
||||||
|
fMinSize(0),
|
||||||
|
fMaxSize(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
BlockAllocator::AreaBucket::~AreaBucket()
|
||||||
|
{
|
||||||
|
while (Area *area = fAreas.GetFirst()) {
|
||||||
|
RemoveArea(area);
|
||||||
|
area->Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanityCheck
|
||||||
|
bool
|
||||||
|
BlockAllocator::AreaBucket::SanityCheck(bool deep) const
|
||||||
|
{
|
||||||
|
// check area list
|
||||||
|
for (Area *area = GetFirstArea(); area; area = GetNextArea(area)) {
|
||||||
|
if (deep) {
|
||||||
|
if (!area->SanityCheck())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// bucket
|
||||||
|
if (area->GetBucket() != this) {
|
||||||
|
FATAL(("Area %p is in bucket %p, but thinks it is in bucket %p\n",
|
||||||
|
area, this, area->GetBucket()));
|
||||||
|
BA_PANIC("Wrong area bucket.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// size
|
||||||
|
size_t areaSize = area->GetFreeBytes();
|
||||||
|
if (areaSize < fMinSize || areaSize >= fMaxSize) {
|
||||||
|
FATAL(("Area is in wrong bucket: free: %lu, min: %lu, max: %lu\n",
|
||||||
|
areaSize, fMinSize, fMaxSize));
|
||||||
|
BA_PANIC("Area in wrong bucket.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// BlockAllocatorAreaBucket.h
|
||||||
|
|
||||||
|
#ifndef BLOCK_ALLOCATOR_AREA_BUCKET_H
|
||||||
|
#define BLOCK_ALLOCATOR_AREA_BUCKET_H
|
||||||
|
|
||||||
|
#include "BlockAllocator.h"
|
||||||
|
#include "BlockAllocatorArea.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "DLList.h"
|
||||||
|
|
||||||
|
class BlockAllocator::AreaBucket {
|
||||||
|
public:
|
||||||
|
AreaBucket();
|
||||||
|
~AreaBucket();
|
||||||
|
|
||||||
|
inline void SetIndex(int32 index) { fIndex = index; }
|
||||||
|
inline int32 GetIndex() const { return fIndex; }
|
||||||
|
|
||||||
|
inline void SetSizeLimits(size_t minSize, size_t maxSize);
|
||||||
|
inline size_t GetMinSize() const { return fMinSize; } // incl.
|
||||||
|
inline size_t GetMaxSize() const { return fMaxSize; } // excl.
|
||||||
|
|
||||||
|
inline void AddArea(Area *area);
|
||||||
|
inline void RemoveArea(Area *area);
|
||||||
|
|
||||||
|
inline Area *GetFirstArea() const { return fAreas.GetFirst(); }
|
||||||
|
inline Area *GetLastArea() const { return fAreas.GetLast(); }
|
||||||
|
inline Area *GetNextArea(Area* area) const;
|
||||||
|
|
||||||
|
inline bool IsEmpty() const { return fAreas.IsEmpty(); }
|
||||||
|
|
||||||
|
// debugging only
|
||||||
|
bool SanityCheck(bool deep = false) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DLList<Area> fAreas;
|
||||||
|
int32 fIndex;
|
||||||
|
size_t fMinSize;
|
||||||
|
size_t fMaxSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BlockAllocator::AreaBucket AreaBucket;
|
||||||
|
|
||||||
|
|
||||||
|
// inline methods
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
#if BA_DEFINE_INLINES
|
||||||
|
|
||||||
|
// SetSizeLimits
|
||||||
|
/*! \brief Sets the size limits for areas this bucket may contain.
|
||||||
|
\param minSize Minimal area size. Inclusively.
|
||||||
|
\param maxSize Maximal area size. Exlusively.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockAllocator::AreaBucket::SetSizeLimits(size_t minSize, size_t maxSize)
|
||||||
|
{
|
||||||
|
fMinSize = minSize;
|
||||||
|
fMaxSize = maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddArea
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockAllocator::AreaBucket::AddArea(Area *area)
|
||||||
|
{
|
||||||
|
if (area) {
|
||||||
|
fAreas.Insert(area, area->NeedsDefragmenting());
|
||||||
|
area->SetBucket(this);
|
||||||
|
D(SanityCheck(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveArea
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
BlockAllocator::AreaBucket::RemoveArea(Area *area)
|
||||||
|
{
|
||||||
|
if (area) {
|
||||||
|
fAreas.Remove(area);
|
||||||
|
area->SetBucket(NULL);
|
||||||
|
D(SanityCheck(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextArea
|
||||||
|
inline
|
||||||
|
Area *
|
||||||
|
BlockAllocator::AreaBucket::GetNextArea(Area* area) const
|
||||||
|
{
|
||||||
|
return fAreas.GetNext(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BA_DEFINE_INLINES
|
||||||
|
|
||||||
|
#endif // BLOCK_ALLOCATOR_AREA_BUCKET_H
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// BlockAllocatorMisc.h
|
||||||
|
|
||||||
|
#ifndef BLOCK_ALLOCATOR_MISC_H
|
||||||
|
#define BLOCK_ALLOCATOR_MISC_H
|
||||||
|
|
||||||
|
#include "Block.h"
|
||||||
|
#include "Misc.h"
|
||||||
|
|
||||||
|
// block alignment -- start offsets and size
|
||||||
|
static const size_t kBlockAlignment = 4; // must be a power of 2
|
||||||
|
|
||||||
|
// block_align_{floor,ceil}
|
||||||
|
static inline size_t block_align_floor(size_t value)
|
||||||
|
{ return value & ~(kBlockAlignment - 1); }
|
||||||
|
static inline size_t block_align_ceil(size_t value)
|
||||||
|
{ return (value + kBlockAlignment - 1) & ~(kBlockAlignment - 1); }
|
||||||
|
|
||||||
|
// minimal size of a gross/net block
|
||||||
|
// BAD DOG: No initializers in the kernel!
|
||||||
|
//static const size_t kMinBlockSize = block_align_ceil(sizeof(TFreeBlock));
|
||||||
|
#define kMinBlockSize (block_align_ceil(sizeof(TFreeBlock)))
|
||||||
|
static const size_t kMinNetBlockSize = 8;
|
||||||
|
|
||||||
|
static const size_t kDefragmentingTolerance = 10240;
|
||||||
|
|
||||||
|
// bucket_containing_size -- bucket for to contain an area with size
|
||||||
|
static inline int bucket_containing_size(size_t size)
|
||||||
|
{ return fls(size / kMinNetBlockSize) + 1; }
|
||||||
|
|
||||||
|
// bucket_containing_min_size -- bucket containing areas >= size
|
||||||
|
static inline int
|
||||||
|
bucket_containing_min_size(size_t size)
|
||||||
|
{ return (size ? bucket_containing_size(size - 1) + 1 : 0); }
|
||||||
|
|
||||||
|
|
||||||
|
#endif BLOCK_ALLOCATOR_MISC_H
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
// BlockReferenceManager.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Block.h"
|
||||||
|
#include "BlockAllocator.h" // only for BA_PANIC
|
||||||
|
#include "BlockReferenceManager.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
|
||||||
|
static const int kBlockReferenceTableSize = 128;
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
BlockReferenceManager::BlockReferenceManager()
|
||||||
|
: fTables(10),
|
||||||
|
fFreeList(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
BlockReferenceManager::~BlockReferenceManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllocateReference
|
||||||
|
BlockReference *
|
||||||
|
BlockReferenceManager::AllocateReference()
|
||||||
|
{
|
||||||
|
BlockReference *reference = NULL;
|
||||||
|
if (!fFreeList)
|
||||||
|
_AddTable();
|
||||||
|
if (fFreeList) {
|
||||||
|
reference = fFreeList;
|
||||||
|
fFreeList = *(BlockReference**)fFreeList;
|
||||||
|
}
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeReference
|
||||||
|
void
|
||||||
|
BlockReferenceManager::FreeReference(BlockReference *reference)
|
||||||
|
{
|
||||||
|
if (reference) {
|
||||||
|
*(BlockReference**)reference = fFreeList;
|
||||||
|
fFreeList = reference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckReference
|
||||||
|
bool
|
||||||
|
BlockReferenceManager::CheckReference(BlockReference *reference)
|
||||||
|
{
|
||||||
|
if (reference) {
|
||||||
|
uint32 address = (uint32)reference;
|
||||||
|
int32 tableCount = fTables.CountItems();
|
||||||
|
for (int32 i = 0; i < tableCount; i++) {
|
||||||
|
Table *table = &fTables.ItemAt(i);
|
||||||
|
uint32 first = (uint32)table->GetReferences();
|
||||||
|
uint32 last = (uint32)(table->GetReferences() + table->GetSize());
|
||||||
|
if (first <= address && address < last)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FATAL(("BlockReference %p does not exist!\n", reference));
|
||||||
|
BA_PANIC("BlockReference doesn't exist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
BlockReferenceManager::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
info.AddListAllocation(fTables.GetCapacity(), sizeof(Table));
|
||||||
|
int32 count = fTables.CountItems();
|
||||||
|
for (int32 i = 0; i < count; i++) {
|
||||||
|
Table &table = fTables.ItemAt(i);
|
||||||
|
info.AddOtherAllocation(table.GetSize() * sizeof(BlockReference));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AddTable
|
||||||
|
status_t
|
||||||
|
BlockReferenceManager::_AddTable()
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
// add a new table
|
||||||
|
Table dummy;
|
||||||
|
if (fTables.AddItem(dummy)) {
|
||||||
|
int32 index = fTables.CountItems() - 1;
|
||||||
|
Table &table = fTables.ItemAt(index);
|
||||||
|
error = table.Init(kBlockReferenceTableSize);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// add the references to the free list
|
||||||
|
uint32 count = table.GetSize();
|
||||||
|
BlockReference *references = table.GetReferences();
|
||||||
|
for (uint32 i = 0; i < count; i++) {
|
||||||
|
BlockReference *reference = references + i;
|
||||||
|
*(BlockReference**)reference = fFreeList;
|
||||||
|
fFreeList = reference;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
fTables.RemoveItem(index);
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Table
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
BlockReferenceManager::Table::~Table()
|
||||||
|
{
|
||||||
|
if (fReferences)
|
||||||
|
delete[] fReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
status_t
|
||||||
|
BlockReferenceManager::Table::Init(int32 size)
|
||||||
|
{
|
||||||
|
status_t error = (size > 0 ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fReferences = new(nothrow) BlockReference[size];
|
||||||
|
if (fReferences)
|
||||||
|
fSize = size;
|
||||||
|
else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// BlockReferenceManager.h
|
||||||
|
|
||||||
|
#ifndef BLOCK_REFERENCE_MANAGER_H
|
||||||
|
#define BLOCK_REFERENCE_MANAGER_H
|
||||||
|
|
||||||
|
#include "List.h"
|
||||||
|
|
||||||
|
class AllocationInfo;
|
||||||
|
class BlockReference;
|
||||||
|
|
||||||
|
class BlockReferenceManager {
|
||||||
|
public:
|
||||||
|
BlockReferenceManager();
|
||||||
|
~BlockReferenceManager();
|
||||||
|
|
||||||
|
BlockReference *AllocateReference();
|
||||||
|
void FreeReference(BlockReference *reference);
|
||||||
|
|
||||||
|
// debugging only
|
||||||
|
bool CheckReference(BlockReference *reference);
|
||||||
|
void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _AddTable();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Table {
|
||||||
|
public:
|
||||||
|
Table() : fSize(0), fReferences(NULL) {}
|
||||||
|
Table(int) : fSize(0), fReferences(NULL) {}
|
||||||
|
~Table();
|
||||||
|
|
||||||
|
status_t Init(int32 size);
|
||||||
|
|
||||||
|
BlockReference *GetReferences() { return fReferences; }
|
||||||
|
|
||||||
|
int32 GetSize() const { return fSize; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 fSize;
|
||||||
|
BlockReference *fReferences;
|
||||||
|
};
|
||||||
|
|
||||||
|
List<Table> fTables;
|
||||||
|
BlockReference *fFreeList;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BLOCK_REFERENCE_MANAGER_H
|
||||||
|
|
||||||
@@ -0,0 +1,417 @@
|
|||||||
|
// DataContainer.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Attribute.h" // for debugging only
|
||||||
|
#include "Block.h"
|
||||||
|
#include "DataContainer.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Misc.h"
|
||||||
|
#include "Node.h" // for debugging only
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
DataContainer::DataContainer(Volume *volume)
|
||||||
|
: fVolume(volume),
|
||||||
|
fSize(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
DataContainer::~DataContainer()
|
||||||
|
{
|
||||||
|
Resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
DataContainer::InitCheck() const
|
||||||
|
{
|
||||||
|
return (fVolume ? B_OK : B_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize
|
||||||
|
status_t
|
||||||
|
DataContainer::Resize(off_t newSize)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (newSize < 0)
|
||||||
|
newSize = 0;
|
||||||
|
if (newSize != fSize) {
|
||||||
|
// Shrinking should never fail. Growing can fail, if we run out of
|
||||||
|
// memory. Then we try to shrink back to the original size.
|
||||||
|
off_t oldSize = fSize;
|
||||||
|
error = _Resize(newSize);
|
||||||
|
if (error == B_NO_MEMORY && newSize > fSize)
|
||||||
|
_Resize(oldSize);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAt
|
||||||
|
status_t
|
||||||
|
DataContainer::ReadAt(off_t offset, void *_buffer, size_t size,
|
||||||
|
size_t *bytesRead)
|
||||||
|
{
|
||||||
|
uint8 *buffer = (uint8*)_buffer;
|
||||||
|
status_t error = (buffer && offset >= 0 && bytesRead ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// read not more than we have to offer
|
||||||
|
offset = min(offset, fSize);
|
||||||
|
size = min(size, size_t(fSize - offset));
|
||||||
|
// iterate through the blocks, reading as long as there's something
|
||||||
|
// left to read
|
||||||
|
size_t blockSize = fVolume->GetBlockSize();
|
||||||
|
*bytesRead = 0;
|
||||||
|
while (size > 0) {
|
||||||
|
size_t inBlockOffset = offset % blockSize;
|
||||||
|
size_t toRead = min(size, size_t(blockSize - inBlockOffset));
|
||||||
|
void *blockData = _GetBlockDataAt(offset / blockSize,
|
||||||
|
inBlockOffset, toRead);
|
||||||
|
D(
|
||||||
|
if (!blockData) {
|
||||||
|
Node *node = NULL;
|
||||||
|
if (Attribute *attribute = dynamic_cast<Attribute*>(this)) {
|
||||||
|
FATAL(("attribute `%s' of\n", attribute->GetName()));
|
||||||
|
node = attribute->GetNode();
|
||||||
|
} else {
|
||||||
|
node = dynamic_cast<Node*>(this);
|
||||||
|
}
|
||||||
|
if (node)
|
||||||
|
// FATAL(("node `%s'\n", node->GetName()));
|
||||||
|
FATAL(("container size: %Ld, offset: %Ld, buffer size: %lu\n",
|
||||||
|
fSize, offset, size));
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
memcpy(buffer, blockData, toRead);
|
||||||
|
buffer += toRead;
|
||||||
|
size -= toRead;
|
||||||
|
offset += toRead;
|
||||||
|
*bytesRead += toRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAt
|
||||||
|
status_t
|
||||||
|
DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size,
|
||||||
|
size_t *bytesWritten)
|
||||||
|
{
|
||||||
|
//PRINT(("DataContainer::WriteAt(%Ld, %p, %lu, %p), fSize: %Ld\n", offset, _buffer, size, bytesWritten, fSize));
|
||||||
|
const uint8 *buffer = (const uint8*)_buffer;
|
||||||
|
status_t error = (buffer && offset >= 0 && bytesWritten
|
||||||
|
? B_OK : B_BAD_VALUE);
|
||||||
|
// resize the container, if necessary
|
||||||
|
if (error == B_OK) {
|
||||||
|
off_t newSize = offset + size;
|
||||||
|
off_t oldSize = fSize;
|
||||||
|
if (newSize > fSize) {
|
||||||
|
error = Resize(newSize);
|
||||||
|
// pad with zero, if necessary
|
||||||
|
if (error == B_OK && offset > oldSize)
|
||||||
|
_ClearArea(offset, oldSize - offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error == B_OK) {
|
||||||
|
// iterate through the blocks, writing as long as there's something
|
||||||
|
// left to write
|
||||||
|
size_t blockSize = fVolume->GetBlockSize();
|
||||||
|
*bytesWritten = 0;
|
||||||
|
while (size > 0) {
|
||||||
|
size_t inBlockOffset = offset % blockSize;
|
||||||
|
size_t toWrite = min(size, size_t(blockSize - inBlockOffset));
|
||||||
|
void *blockData = _GetBlockDataAt(offset / blockSize,
|
||||||
|
inBlockOffset, toWrite);
|
||||||
|
D(if (!blockData) return B_ERROR;);
|
||||||
|
memcpy(blockData, buffer, toWrite);
|
||||||
|
buffer += toWrite;
|
||||||
|
size -= toWrite;
|
||||||
|
offset += toWrite;
|
||||||
|
*bytesWritten += toWrite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFirstDataBlock
|
||||||
|
void
|
||||||
|
DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length)
|
||||||
|
{
|
||||||
|
if (data && length) {
|
||||||
|
if (_IsBlockMode()) {
|
||||||
|
BlockReference *block = _GetBlockList()->ItemAt(0);
|
||||||
|
*data = (const uint8*)block->GetData();
|
||||||
|
*length = min(fSize, fVolume->GetBlockSize());
|
||||||
|
} else {
|
||||||
|
*data = fSmallBuffer;
|
||||||
|
*length = fSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
DataContainer::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
if (_IsBlockMode()) {
|
||||||
|
BlockList *blocks = _GetBlockList();
|
||||||
|
info.AddListAllocation(blocks->GetCapacity(), sizeof(BlockReference*));
|
||||||
|
int32 blockCount = blocks->CountItems();
|
||||||
|
for (int32 i = 0; i < blockCount; i++)
|
||||||
|
info.AddBlockAllocation(blocks->ItemAt(i)->GetBlock()->GetSize());
|
||||||
|
} else {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _RequiresBlockMode
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
DataContainer::_RequiresBlockMode(size_t size)
|
||||||
|
{
|
||||||
|
return (size > kSmallDataContainerSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _IsBlockMode
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
DataContainer::_IsBlockMode() const
|
||||||
|
{
|
||||||
|
return (fSize > kSmallDataContainerSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Resize
|
||||||
|
status_t
|
||||||
|
DataContainer::_Resize(off_t newSize)
|
||||||
|
{
|
||||||
|
//PRINT(("DataContainer::_Resize(%Ld), fSize: %Ld\n", newSize, fSize));
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (newSize != fSize) {
|
||||||
|
size_t blockSize = fVolume->GetBlockSize();
|
||||||
|
int32 blockCount = _CountBlocks();
|
||||||
|
int32 newBlockCount = (newSize + blockSize - 1) / blockSize;
|
||||||
|
if (newBlockCount == blockCount) {
|
||||||
|
// only the last block needs to be resized
|
||||||
|
if (_IsBlockMode() && _RequiresBlockMode(newSize)) {
|
||||||
|
// keep block mode
|
||||||
|
error = _ResizeLastBlock((newSize - 1) % blockSize + 1);
|
||||||
|
} else if (!_IsBlockMode() && !_RequiresBlockMode(newSize)) {
|
||||||
|
// keep small buffer mode
|
||||||
|
fSize = newSize;
|
||||||
|
} else if (fSize < newSize) {
|
||||||
|
// switch to block mode
|
||||||
|
_SwitchToBlockMode(newSize);
|
||||||
|
} else {
|
||||||
|
// switch to small buffer mode
|
||||||
|
_SwitchToSmallBufferMode(newSize);
|
||||||
|
}
|
||||||
|
} else if (newBlockCount < blockCount) {
|
||||||
|
// shrink
|
||||||
|
if (_IsBlockMode()) {
|
||||||
|
// remove the last blocks
|
||||||
|
BlockList *blocks = _GetBlockList();
|
||||||
|
for (int32 i = blockCount - 1; i >= newBlockCount; i--) {
|
||||||
|
BlockReference *block = blocks->ItemAt(i);
|
||||||
|
blocks->RemoveItem(i);
|
||||||
|
fVolume->FreeBlock(block);
|
||||||
|
fSize = (fSize - 1) / blockSize * blockSize;
|
||||||
|
}
|
||||||
|
// resize the last block to the correct size, respectively
|
||||||
|
// switch to small buffer mode
|
||||||
|
if (_RequiresBlockMode(newSize))
|
||||||
|
error = _ResizeLastBlock((newSize - 1) % blockSize + 1);
|
||||||
|
else
|
||||||
|
_SwitchToSmallBufferMode(newSize);
|
||||||
|
} else {
|
||||||
|
// small buffer mode: just set the new size
|
||||||
|
fSize = newSize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// grow
|
||||||
|
if (_RequiresBlockMode(newSize)) {
|
||||||
|
// resize the first block to the correct size, respectively
|
||||||
|
// switch to block mode
|
||||||
|
if (_IsBlockMode())
|
||||||
|
error = _ResizeLastBlock(blockSize);
|
||||||
|
else {
|
||||||
|
error = _SwitchToBlockMode(min((size_t)newSize,
|
||||||
|
blockSize));
|
||||||
|
}
|
||||||
|
// add new blocks
|
||||||
|
BlockList *blocks = _GetBlockList();
|
||||||
|
while (error == B_OK && fSize < newSize) {
|
||||||
|
size_t newBlockSize = min(size_t(newSize - fSize),
|
||||||
|
blockSize);
|
||||||
|
BlockReference *block = NULL;
|
||||||
|
error = fVolume->AllocateBlock(newBlockSize, &block);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (blocks->AddItem(block))
|
||||||
|
fSize += newBlockSize;
|
||||||
|
else {
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
fVolume->FreeBlock(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no need to switch to block mode: just set the new size
|
||||||
|
fSize = newSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _GetBlockList
|
||||||
|
inline
|
||||||
|
DataContainer::BlockList *
|
||||||
|
DataContainer::_GetBlockList()
|
||||||
|
{
|
||||||
|
return (BlockList*)fBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _GetBlockList
|
||||||
|
inline
|
||||||
|
const DataContainer::BlockList *
|
||||||
|
DataContainer::_GetBlockList() const
|
||||||
|
{
|
||||||
|
return (BlockList*)fBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _CountBlocks
|
||||||
|
inline
|
||||||
|
int32
|
||||||
|
DataContainer::_CountBlocks() const
|
||||||
|
{
|
||||||
|
if (_IsBlockMode())
|
||||||
|
return _GetBlockList()->CountItems();
|
||||||
|
else if (fSize == 0) // small buffer mode, empty buffer
|
||||||
|
return 0;
|
||||||
|
return 1; // small buffer mode, non-empty buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// _GetBlockDataAt
|
||||||
|
inline
|
||||||
|
void *
|
||||||
|
DataContainer::_GetBlockDataAt(int32 index, size_t offset, size_t DARG(size))
|
||||||
|
{
|
||||||
|
if (_IsBlockMode()) {
|
||||||
|
BlockReference *block = _GetBlockList()->ItemAt(index);
|
||||||
|
D(if (!fVolume->CheckBlock(block, offset + size)) return NULL;);
|
||||||
|
return block->GetDataAt(offset);
|
||||||
|
} else {
|
||||||
|
D(
|
||||||
|
if (offset + size > kSmallDataContainerSize) {
|
||||||
|
FATAL(("DataContainer: Data access exceeds small buffer.\n"));
|
||||||
|
PANIC("DataContainer: Data access exceeds small buffer.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return fSmallBuffer + offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ClearArea
|
||||||
|
void
|
||||||
|
DataContainer::_ClearArea(off_t offset, off_t size)
|
||||||
|
{
|
||||||
|
// constrain the area to the data area
|
||||||
|
offset = min(offset, fSize);
|
||||||
|
size = min(size, fSize - offset);
|
||||||
|
// iterate through the blocks, clearing as long as there's something
|
||||||
|
// left to clear
|
||||||
|
size_t blockSize = fVolume->GetBlockSize();
|
||||||
|
while (size > 0) {
|
||||||
|
size_t inBlockOffset = offset % blockSize;
|
||||||
|
size_t toClear = min(size_t(size), blockSize - inBlockOffset);
|
||||||
|
void *blockData = _GetBlockDataAt(offset / blockSize, inBlockOffset,
|
||||||
|
toClear);
|
||||||
|
D(if (!blockData) return;);
|
||||||
|
memset(blockData, 0, toClear);
|
||||||
|
size -= toClear;
|
||||||
|
offset += toClear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ResizeLastBlock
|
||||||
|
status_t
|
||||||
|
DataContainer::_ResizeLastBlock(size_t newSize)
|
||||||
|
{
|
||||||
|
//PRINT(("DataContainer::_ResizeLastBlock(%lu), fSize: %Ld\n", newSize, fSize));
|
||||||
|
int32 blockCount = _CountBlocks();
|
||||||
|
status_t error = (fSize > 0 && blockCount > 0 && newSize > 0
|
||||||
|
? B_OK : B_BAD_VALUE);
|
||||||
|
D(
|
||||||
|
if (!_IsBlockMode()) {
|
||||||
|
FATAL(("Call of _ResizeLastBlock() in small buffer mode.\n"));
|
||||||
|
PANIC("Call of _ResizeLastBlock() in small buffer mode.");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (error == B_OK) {
|
||||||
|
size_t blockSize = fVolume->GetBlockSize();
|
||||||
|
size_t oldSize = (fSize - 1) % blockSize + 1;
|
||||||
|
if (newSize != oldSize) {
|
||||||
|
BlockList *blocks = _GetBlockList();
|
||||||
|
BlockReference *block = blocks->ItemAt(blockCount - 1);
|
||||||
|
BlockReference *newBlock = fVolume->ResizeBlock(block, newSize);
|
||||||
|
if (newBlock) {
|
||||||
|
if (newBlock != block)
|
||||||
|
blocks->ReplaceItem(blockCount - 1, newBlock);
|
||||||
|
fSize += off_t(newSize) - oldSize;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _SwitchToBlockMode
|
||||||
|
status_t
|
||||||
|
DataContainer::_SwitchToBlockMode(size_t newBlockSize)
|
||||||
|
{
|
||||||
|
// allocate a new block
|
||||||
|
BlockReference *block = NULL;
|
||||||
|
status_t error = fVolume->AllocateBlock(newBlockSize, &block);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// copy the data from the small buffer into the block
|
||||||
|
if (fSize > 0)
|
||||||
|
memcpy(block->GetData(), fSmallBuffer, fSize);
|
||||||
|
// construct the block list and add the block
|
||||||
|
new (fBlocks) BlockList(10);
|
||||||
|
BlockList *blocks = _GetBlockList();
|
||||||
|
if (blocks->AddItem(block)) {
|
||||||
|
fSize = newBlockSize;
|
||||||
|
} else {
|
||||||
|
// error: destroy the block list and free the block
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
blocks->~BlockList();
|
||||||
|
if (fSize > 0)
|
||||||
|
memcpy(fSmallBuffer, block->GetData(), fSize);
|
||||||
|
fVolume->FreeBlock(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _SwitchToSmallBufferMode
|
||||||
|
void
|
||||||
|
DataContainer::_SwitchToSmallBufferMode(size_t newSize)
|
||||||
|
{
|
||||||
|
// remove the first (and only) block
|
||||||
|
BlockList *blocks = _GetBlockList();
|
||||||
|
BlockReference *block = blocks->ItemAt(0);
|
||||||
|
blocks->RemoveItem(0L);
|
||||||
|
// destroy the block list and copy the data into the small buffer
|
||||||
|
blocks->~BlockList();
|
||||||
|
if (newSize > 0)
|
||||||
|
memcpy(fSmallBuffer, block->GetData(), newSize);
|
||||||
|
// free the block and set the new size
|
||||||
|
fVolume->FreeBlock(block);
|
||||||
|
fSize = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// DataContainer.h
|
||||||
|
|
||||||
|
#ifndef DATA_CONTAINER_H
|
||||||
|
#define DATA_CONTAINER_H
|
||||||
|
|
||||||
|
#include "List.h"
|
||||||
|
|
||||||
|
class AllocationInfo;
|
||||||
|
class BlockReference;
|
||||||
|
class Volume;
|
||||||
|
|
||||||
|
// Size of the DataContainer's small buffer. If it contains data up to this
|
||||||
|
// size, no blocks are allocated, but the small buffer is used instead.
|
||||||
|
// 16 bytes are for free, since they are shared with the block list.
|
||||||
|
// (actually even more, since the list has an initial size).
|
||||||
|
// I ran a test analyzing what sizes the attributes in my system have:
|
||||||
|
// size percentage bytes used in average
|
||||||
|
// <= 0 0.00 93.45
|
||||||
|
// <= 4 25.46 75.48
|
||||||
|
// <= 8 30.54 73.02
|
||||||
|
// <= 16 52.98 60.37
|
||||||
|
// <= 32 80.19 51.74
|
||||||
|
// <= 64 94.38 70.54
|
||||||
|
// <= 126 96.90 128.23
|
||||||
|
//
|
||||||
|
// For average memory usage it is assumed, that attributes larger than 126
|
||||||
|
// bytes have size 127, that the list has an initial capacity of 10 entries
|
||||||
|
// (40 bytes), that the block reference consumes 4 bytes and the block header
|
||||||
|
// 12 bytes. The optimal length is actually 35, with 51.05 bytes per
|
||||||
|
// attribute, but I conservatively rounded to 32.
|
||||||
|
static const size_t kSmallDataContainerSize = 32;
|
||||||
|
|
||||||
|
class DataContainer {
|
||||||
|
public:
|
||||||
|
DataContainer(Volume *volume);
|
||||||
|
virtual ~DataContainer();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
Volume *GetVolume() const { return fVolume; }
|
||||||
|
|
||||||
|
status_t Resize(off_t newSize);
|
||||||
|
off_t GetSize() const { return fSize; }
|
||||||
|
|
||||||
|
virtual status_t ReadAt(off_t offset, void *buffer, size_t size,
|
||||||
|
size_t *bytesRead);
|
||||||
|
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||||
|
size_t *bytesWritten);
|
||||||
|
|
||||||
|
void GetFirstDataBlock(const uint8 **data, size_t *length);
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef List<BlockReference*> BlockList;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline bool _RequiresBlockMode(size_t size);
|
||||||
|
inline bool _IsBlockMode() const;
|
||||||
|
|
||||||
|
inline BlockList *_GetBlockList();
|
||||||
|
inline const BlockList *_GetBlockList() const;
|
||||||
|
inline int32 _CountBlocks() const;
|
||||||
|
inline void *_GetBlockDataAt(int32 index, size_t offset, size_t size);
|
||||||
|
|
||||||
|
void _ClearArea(off_t offset, off_t size);
|
||||||
|
|
||||||
|
status_t _Resize(off_t newSize);
|
||||||
|
status_t _ResizeLastBlock(size_t newSize);
|
||||||
|
|
||||||
|
status_t _SwitchToBlockMode(size_t newBlockSize);
|
||||||
|
void _SwitchToSmallBufferMode(size_t newSize);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Volume *fVolume;
|
||||||
|
off_t fSize;
|
||||||
|
union {
|
||||||
|
uint8 fBlocks[sizeof(BlockList)];
|
||||||
|
uint8 fSmallBuffer[kSmallDataContainerSize];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DATA_CONTAINER_H
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
// Directory.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Directory.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "EntryIterator.h"
|
||||||
|
#include "File.h"
|
||||||
|
#include "SymLink.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Directory::Directory(Volume *volume)
|
||||||
|
: Node(volume, NODE_TYPE_DIRECTORY),
|
||||||
|
fEntries()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
Directory::~Directory()
|
||||||
|
{
|
||||||
|
// delete all entries
|
||||||
|
while (Entry *entry = fEntries.GetFirst()) {
|
||||||
|
if (DeleteEntry(entry) != B_OK) {
|
||||||
|
FATAL(("Could not delete all entries in directory.\n"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link
|
||||||
|
status_t
|
||||||
|
Directory::Link(Entry *entry)
|
||||||
|
{
|
||||||
|
if (fReferrers.IsEmpty())
|
||||||
|
return Node::Link(entry);
|
||||||
|
return B_IS_A_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink
|
||||||
|
status_t
|
||||||
|
Directory::Unlink(Entry *entry)
|
||||||
|
{
|
||||||
|
if (entry == fReferrers.GetFirst())
|
||||||
|
return Node::Unlink(entry);
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize
|
||||||
|
status_t
|
||||||
|
Directory::SetSize(off_t /*newSize*/)
|
||||||
|
{
|
||||||
|
return B_IS_A_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize
|
||||||
|
off_t
|
||||||
|
Directory::GetSize() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParent
|
||||||
|
Directory *
|
||||||
|
Directory::GetParent() const
|
||||||
|
{
|
||||||
|
Entry *entry = fReferrers.GetFirst();
|
||||||
|
return (entry ? entry->GetParent() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDirectory
|
||||||
|
status_t
|
||||||
|
Directory::CreateDirectory(const char *name, Directory **directory)
|
||||||
|
{
|
||||||
|
status_t error = (name && directory ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// create directory
|
||||||
|
if (Directory *node = new(nothrow) Directory(GetVolume())) {
|
||||||
|
error = _CreateCommon(node, name);
|
||||||
|
// deletes the node on failure
|
||||||
|
if (error == B_OK)
|
||||||
|
*directory = node;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFile
|
||||||
|
status_t
|
||||||
|
Directory::CreateFile(const char *name, File **file)
|
||||||
|
{
|
||||||
|
status_t error = (name && file ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// create file
|
||||||
|
if (File *node = new(nothrow) File(GetVolume())) {
|
||||||
|
error = _CreateCommon(node, name);
|
||||||
|
// deletes the node on failure
|
||||||
|
if (error == B_OK)
|
||||||
|
*file = node;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSymLink
|
||||||
|
status_t
|
||||||
|
Directory::CreateSymLink(const char *name, const char *path, SymLink **symLink)
|
||||||
|
{
|
||||||
|
status_t error = (name && symlink ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// create symlink
|
||||||
|
if (SymLink *node = new(nothrow) SymLink(GetVolume())) {
|
||||||
|
error = node->SetLinkedPath(path);
|
||||||
|
if (error == B_OK) {
|
||||||
|
error = _CreateCommon(node, name);
|
||||||
|
// deletes the node on failure
|
||||||
|
if (error == B_OK)
|
||||||
|
*symLink = node;
|
||||||
|
} else
|
||||||
|
delete node;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEntry
|
||||||
|
status_t
|
||||||
|
Directory::AddEntry(Entry *entry)
|
||||||
|
{
|
||||||
|
status_t error = (entry && !entry->GetParent() ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fEntries.Insert(entry);
|
||||||
|
entry->SetParent(this);
|
||||||
|
error = GetVolume()->EntryAdded(GetID(), entry);
|
||||||
|
if (error == B_OK) {
|
||||||
|
MarkModified();
|
||||||
|
} else {
|
||||||
|
fEntries.Remove(entry);
|
||||||
|
entry->SetParent(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEntry
|
||||||
|
status_t
|
||||||
|
Directory::CreateEntry(Node *node, const char *name, Entry **_entry)
|
||||||
|
{
|
||||||
|
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// create an entry
|
||||||
|
Entry *entry = new(nothrow) Entry(name);
|
||||||
|
if (entry) {
|
||||||
|
error = entry->InitCheck();
|
||||||
|
if (error == B_OK) {
|
||||||
|
// link to the node
|
||||||
|
error = entry->Link(node);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// add the entry
|
||||||
|
error = AddEntry(entry);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (_entry)
|
||||||
|
*_entry = entry;
|
||||||
|
} else {
|
||||||
|
// failure: unlink the node
|
||||||
|
entry->Unlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete the entry on failure
|
||||||
|
if (error != B_OK)
|
||||||
|
delete entry;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEntry
|
||||||
|
status_t
|
||||||
|
Directory::RemoveEntry(Entry *entry)
|
||||||
|
{
|
||||||
|
status_t error = (entry && entry->GetParent() == this ? B_OK
|
||||||
|
: B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// move all iterators pointing to the entry to the next entry
|
||||||
|
if (GetVolume()->IteratorLock()) {
|
||||||
|
// set the iterators' current entry
|
||||||
|
Entry *nextEntry = fEntries.GetNext(entry);
|
||||||
|
DLList<EntryIterator> *iterators = entry->GetEntryIteratorList();
|
||||||
|
for (EntryIterator *iterator = iterators->GetFirst();
|
||||||
|
iterator;
|
||||||
|
iterator = iterators->GetNext(iterator)) {
|
||||||
|
iterator->SetCurrent(nextEntry, true);
|
||||||
|
}
|
||||||
|
// Move the iterators from one list to the other, or just remove
|
||||||
|
// them, if there is no next entry.
|
||||||
|
if (nextEntry) {
|
||||||
|
DLList<EntryIterator> *nextIterators
|
||||||
|
= nextEntry->GetEntryIteratorList();
|
||||||
|
nextIterators->MoveFrom(iterators);
|
||||||
|
} else
|
||||||
|
iterators->RemoveAll();
|
||||||
|
GetVolume()->IteratorUnlock();
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
// remove the entry
|
||||||
|
if (error == B_OK) {
|
||||||
|
error = GetVolume()->EntryRemoved(GetID(), entry);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fEntries.Remove(entry);
|
||||||
|
entry->SetParent(NULL);
|
||||||
|
MarkModified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEntry
|
||||||
|
status_t
|
||||||
|
Directory::DeleteEntry(Entry *entry)
|
||||||
|
{
|
||||||
|
status_t error = RemoveEntry(entry);
|
||||||
|
if (error == B_OK) {
|
||||||
|
error = entry->Unlink();
|
||||||
|
if (error == B_OK)
|
||||||
|
delete entry;
|
||||||
|
else {
|
||||||
|
FATAL(("Failed to Unlink() entry %p from node %Ld!\n", entry,
|
||||||
|
entry->GetNode()->GetID()));
|
||||||
|
AddEntry(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindEntry
|
||||||
|
status_t
|
||||||
|
Directory::FindEntry(const char *name, Entry **_entry) const
|
||||||
|
{
|
||||||
|
status_t error = (name && _entry ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
/*
|
||||||
|
Entry *entry = NULL;
|
||||||
|
while (GetNextEntry(&entry) == B_OK) {
|
||||||
|
if (!strcmp(entry->GetName(), name)) {
|
||||||
|
*_entry = entry;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = B_ENTRY_NOT_FOUND;
|
||||||
|
*/
|
||||||
|
error = GetVolume()->FindEntry(GetID(), name, _entry);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindNode
|
||||||
|
status_t
|
||||||
|
Directory::FindNode(const char *name, Node **node) const
|
||||||
|
{
|
||||||
|
status_t error = (name && node ? B_OK : B_BAD_VALUE);
|
||||||
|
Entry *entry = NULL;
|
||||||
|
if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK)
|
||||||
|
*node = entry->GetNode();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindAndGetNode
|
||||||
|
status_t
|
||||||
|
Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const
|
||||||
|
{
|
||||||
|
status_t error = (name && node ? B_OK : B_BAD_VALUE);
|
||||||
|
Entry *entry = NULL;
|
||||||
|
if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) {
|
||||||
|
*node = entry->GetNode();
|
||||||
|
if (_entry)
|
||||||
|
*_entry = entry;
|
||||||
|
error = GetVolume()->GetVNode(*node);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreviousEntry
|
||||||
|
status_t
|
||||||
|
Directory::GetPreviousEntry(Entry **entry) const
|
||||||
|
{
|
||||||
|
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (!*entry)
|
||||||
|
*entry = fEntries.GetLast();
|
||||||
|
else if ((*entry)->GetParent() == this)
|
||||||
|
*entry = fEntries.GetPrevious(*entry);
|
||||||
|
else
|
||||||
|
error = B_BAD_VALUE;
|
||||||
|
if (error == B_OK && !*entry)
|
||||||
|
error = B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextEntry
|
||||||
|
status_t
|
||||||
|
Directory::GetNextEntry(Entry **entry) const
|
||||||
|
{
|
||||||
|
status_t error = (entry ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (!*entry)
|
||||||
|
*entry = fEntries.GetFirst();
|
||||||
|
else if ((*entry)->GetParent() == this)
|
||||||
|
*entry = fEntries.GetNext(*entry);
|
||||||
|
else
|
||||||
|
error = B_BAD_VALUE;
|
||||||
|
if (error == B_OK && !*entry)
|
||||||
|
error = B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
Directory::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
Node::GetAllocationInfo(info);
|
||||||
|
info.AddDirectoryAllocation();
|
||||||
|
Entry *entry = NULL;
|
||||||
|
while (GetNextEntry(&entry) == B_OK)
|
||||||
|
entry->GetAllocationInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _CreateCommon
|
||||||
|
status_t
|
||||||
|
Directory::_CreateCommon(Node *node, const char *name)
|
||||||
|
{
|
||||||
|
status_t error = node->InitCheck();
|
||||||
|
if (error == B_OK) {
|
||||||
|
// add node to directory
|
||||||
|
error = CreateEntry(node, name);
|
||||||
|
}
|
||||||
|
if (error != B_OK)
|
||||||
|
delete node;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Directory.h
|
||||||
|
|
||||||
|
#ifndef DIRECTORY_H
|
||||||
|
#define DIRECTORY_H
|
||||||
|
|
||||||
|
#include "DLList.h"
|
||||||
|
#include "Node.h"
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
class File;
|
||||||
|
class SymLink;
|
||||||
|
|
||||||
|
class Directory : public Node {
|
||||||
|
public:
|
||||||
|
Directory(Volume *volume);
|
||||||
|
virtual ~Directory();
|
||||||
|
|
||||||
|
virtual status_t Link(Entry *entry);
|
||||||
|
virtual status_t Unlink(Entry *entry);
|
||||||
|
|
||||||
|
virtual status_t SetSize(off_t newSize);
|
||||||
|
virtual off_t GetSize() const;
|
||||||
|
|
||||||
|
Directory *GetParent() const;
|
||||||
|
|
||||||
|
status_t CreateDirectory(const char *name, Directory **directory);
|
||||||
|
status_t CreateFile(const char *name, File **file);
|
||||||
|
status_t CreateSymLink(const char *name, const char *path,
|
||||||
|
SymLink **symLink);
|
||||||
|
|
||||||
|
bool IsEmpty() const { return fEntries.IsEmpty(); }
|
||||||
|
|
||||||
|
status_t AddEntry(Entry *entry);
|
||||||
|
status_t CreateEntry(Node *node, const char *name, Entry **entry = NULL);
|
||||||
|
status_t RemoveEntry(Entry *entry);
|
||||||
|
status_t DeleteEntry(Entry *entry);
|
||||||
|
|
||||||
|
status_t FindEntry(const char *name, Entry **entry) const;
|
||||||
|
status_t FindNode(const char *name, Node **node) const;
|
||||||
|
status_t FindAndGetNode(const char *name, Node **node,
|
||||||
|
Entry **entry = NULL) const;
|
||||||
|
|
||||||
|
status_t GetPreviousEntry(Entry **entry) const;
|
||||||
|
status_t GetNextEntry(Entry **entry) const;
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _CreateCommon(Node *node, const char *name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DLList<Entry> fEntries;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIRECTORY_H
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// Entry.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "EntryIterator.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Entry::Entry(const char *name, Node *node, Directory *parent)
|
||||||
|
: fParent(parent),
|
||||||
|
fNode(node),
|
||||||
|
fName(name),
|
||||||
|
fReferrerLink(),
|
||||||
|
fIterators()
|
||||||
|
{
|
||||||
|
if (node)
|
||||||
|
Link(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
Entry::~Entry()
|
||||||
|
{
|
||||||
|
if (fNode)
|
||||||
|
Unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
Entry::InitCheck() const
|
||||||
|
{
|
||||||
|
return (fName.GetString() ? B_OK : B_NO_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link
|
||||||
|
status_t
|
||||||
|
Entry::Link(Node *node)
|
||||||
|
{
|
||||||
|
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// We first link to the new node and then unlink the old one. So, no
|
||||||
|
// harm is done, if both are the same.
|
||||||
|
Node *oldNode = fNode;
|
||||||
|
error = node->Link(this);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fNode = node;
|
||||||
|
if (oldNode)
|
||||||
|
oldNode->Unlink(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink
|
||||||
|
status_t
|
||||||
|
Entry::Unlink()
|
||||||
|
{
|
||||||
|
status_t error = (fNode ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK && (error = fNode->Unlink(this)) == B_OK)
|
||||||
|
fNode = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName
|
||||||
|
status_t
|
||||||
|
Entry::SetName(const char *newName)
|
||||||
|
{
|
||||||
|
status_t error = (newName ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fName.SetTo(newName)) {
|
||||||
|
// if (fNode)
|
||||||
|
// fNode->MarkModified();
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttachEntryIterator
|
||||||
|
void
|
||||||
|
Entry::AttachEntryIterator(EntryIterator *iterator)
|
||||||
|
{
|
||||||
|
if (iterator && iterator->GetCurrent() == this && !iterator->IsSuspended())
|
||||||
|
fIterators.Insert(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachEntryIterator
|
||||||
|
void
|
||||||
|
Entry::DetachEntryIterator(EntryIterator *iterator)
|
||||||
|
{
|
||||||
|
if (iterator && iterator->GetCurrent() == this && iterator->IsSuspended())
|
||||||
|
fIterators.Remove(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
Entry::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
info.AddEntryAllocation();
|
||||||
|
info.AddStringAllocation(fName.GetLength());
|
||||||
|
fNode->GetAllocationInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Entry.h
|
||||||
|
|
||||||
|
#ifndef ENTRY_H
|
||||||
|
#define ENTRY_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include "DLList.h"
|
||||||
|
#include "fsproto.h"
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
class AllocationInfo;
|
||||||
|
class Directory;
|
||||||
|
class EntryIterator;
|
||||||
|
class Node;
|
||||||
|
|
||||||
|
class Entry : public DLListLinkImpl<Entry> {
|
||||||
|
public:
|
||||||
|
Entry(const char *name, Node *node = NULL, Directory *parent = NULL);
|
||||||
|
~Entry();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
inline void SetParent(Directory *parent) { fParent = parent; }
|
||||||
|
Directory *GetParent() const { return fParent; }
|
||||||
|
|
||||||
|
// inline void SetNode(Node *node) { fNode = node; }
|
||||||
|
status_t Link(Node *node);
|
||||||
|
status_t Unlink();
|
||||||
|
Node *GetNode() const { return fNode; }
|
||||||
|
|
||||||
|
status_t SetName(const char *newName);
|
||||||
|
inline const char *GetName() const { return fName.GetString(); }
|
||||||
|
|
||||||
|
// inline Volume *GetVolume() const { return fVolume; }
|
||||||
|
|
||||||
|
inline DLListLink<Entry> *GetReferrerLink() { return &fReferrerLink; }
|
||||||
|
|
||||||
|
// entry iterator management
|
||||||
|
void AttachEntryIterator(EntryIterator *iterator);
|
||||||
|
void DetachEntryIterator(EntryIterator *iterator);
|
||||||
|
inline DLList<EntryIterator> *GetEntryIteratorList()
|
||||||
|
{ return &fIterators; }
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Directory *fParent;
|
||||||
|
Node *fNode;
|
||||||
|
String fName;
|
||||||
|
DLListLink<Entry> fReferrerLink;
|
||||||
|
// iterator management
|
||||||
|
DLList<EntryIterator> fIterators;
|
||||||
|
};
|
||||||
|
|
||||||
|
// GetNodeReferrerLink
|
||||||
|
class GetNodeReferrerLink {
|
||||||
|
private:
|
||||||
|
typedef DLListLink<Entry> Link;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Link *operator()(Entry *entry) const
|
||||||
|
{
|
||||||
|
return entry->GetReferrerLink();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENTRY_H
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
// EntryIterator.cpp
|
||||||
|
|
||||||
|
#include "Directory.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "EntryIterator.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
EntryIterator::EntryIterator(Directory *directory)
|
||||||
|
: fDirectory(directory),
|
||||||
|
fEntry(NULL),
|
||||||
|
fSuspended(false),
|
||||||
|
fIsNext(false),
|
||||||
|
fDone(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
EntryIterator::~EntryIterator()
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
status_t
|
||||||
|
EntryIterator::SetTo(Directory *directory)
|
||||||
|
{
|
||||||
|
Unset();
|
||||||
|
status_t error = (directory ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fDirectory = directory;
|
||||||
|
fEntry = NULL;
|
||||||
|
fSuspended = false;
|
||||||
|
fIsNext = false;
|
||||||
|
fDone = false;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset
|
||||||
|
void
|
||||||
|
EntryIterator::Unset()
|
||||||
|
{
|
||||||
|
if (fDirectory && fSuspended)
|
||||||
|
Resume();
|
||||||
|
fDirectory = NULL;
|
||||||
|
fEntry = NULL;
|
||||||
|
fSuspended = false;
|
||||||
|
fIsNext = false;
|
||||||
|
fDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
EntryIterator::Suspend()
|
||||||
|
{
|
||||||
|
status_t error = (fDirectory ? B_OK : B_ERROR);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fDirectory->GetVolume()->IteratorLock()) {
|
||||||
|
if (!fSuspended) {
|
||||||
|
if (fEntry)
|
||||||
|
fEntry->AttachEntryIterator(this);
|
||||||
|
fDirectory->GetVolume()->IteratorUnlock();
|
||||||
|
fSuspended = true;
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
EntryIterator::Resume()
|
||||||
|
{
|
||||||
|
status_t error = (fDirectory ? B_OK : B_ERROR);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fDirectory->GetVolume()->IteratorLock()) {
|
||||||
|
if (fSuspended) {
|
||||||
|
if (fEntry)
|
||||||
|
fEntry->DetachEntryIterator(this);
|
||||||
|
fSuspended = false;
|
||||||
|
}
|
||||||
|
fDirectory->GetVolume()->IteratorUnlock();
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext
|
||||||
|
status_t
|
||||||
|
EntryIterator::GetNext(Entry **entry)
|
||||||
|
{
|
||||||
|
status_t error = B_ENTRY_NOT_FOUND;
|
||||||
|
if (!fDone && fDirectory && entry) {
|
||||||
|
if (fIsNext) {
|
||||||
|
fIsNext = false;
|
||||||
|
if (fEntry)
|
||||||
|
error = B_OK;
|
||||||
|
} else
|
||||||
|
error = fDirectory->GetNextEntry(&fEntry);
|
||||||
|
*entry = fEntry;
|
||||||
|
}
|
||||||
|
fDone = (error != B_OK);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind
|
||||||
|
status_t
|
||||||
|
EntryIterator::Rewind()
|
||||||
|
{
|
||||||
|
status_t error = (fDirectory ? B_OK : B_ERROR);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fDirectory->GetVolume()->IteratorLock()) {
|
||||||
|
if (fSuspended && fEntry)
|
||||||
|
fEntry->DetachEntryIterator(this);
|
||||||
|
fEntry = NULL;
|
||||||
|
fIsNext = false;
|
||||||
|
fDone = false;
|
||||||
|
fDirectory->GetVolume()->IteratorUnlock();
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCurrent
|
||||||
|
void
|
||||||
|
EntryIterator::SetCurrent(Entry *entry, bool isNext)
|
||||||
|
{
|
||||||
|
fIsNext = isNext;
|
||||||
|
fEntry = entry;
|
||||||
|
fDone = !fEntry;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// EntryIterator.h
|
||||||
|
|
||||||
|
#ifndef ENTRY_ITERATOR_H
|
||||||
|
#define ENTRY_ITERATOR_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include "DLList.h"
|
||||||
|
|
||||||
|
class Directory;
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
class EntryIterator : public DLListLinkImpl<EntryIterator> {
|
||||||
|
public:
|
||||||
|
EntryIterator(Directory *directory = NULL);
|
||||||
|
~EntryIterator();
|
||||||
|
|
||||||
|
status_t SetTo(Directory *directory);
|
||||||
|
void Unset();
|
||||||
|
|
||||||
|
Directory *GetDirectory() const { return fDirectory; }
|
||||||
|
|
||||||
|
status_t Suspend();
|
||||||
|
status_t Resume();
|
||||||
|
bool IsSuspended() const { return fSuspended; }
|
||||||
|
|
||||||
|
status_t GetNext(Entry **entry);
|
||||||
|
Entry *GetCurrent() const { return fEntry; }
|
||||||
|
|
||||||
|
status_t Rewind();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetCurrent(Entry *entry, bool isNext);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Directory;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Directory *fDirectory;
|
||||||
|
Entry *fEntry;
|
||||||
|
bool fSuspended;
|
||||||
|
bool fIsNext;
|
||||||
|
bool fDone;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENTRY_ITERATOR_H
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// EntryListener.cpp
|
||||||
|
|
||||||
|
#include "EntryListener.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
EntryListener::EntryListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
EntryListener::~EntryListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryAdded
|
||||||
|
void
|
||||||
|
EntryListener::EntryAdded(Entry */*entry*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryRemoved
|
||||||
|
void
|
||||||
|
EntryListener::EntryRemoved(Entry */*entry*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// EntryListener.h
|
||||||
|
|
||||||
|
#ifndef ENTRY_LISTENER_H
|
||||||
|
#define ENTRY_LISTENER_H
|
||||||
|
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
// listening flags
|
||||||
|
enum {
|
||||||
|
ENTRY_LISTEN_ANY_ENTRY = 0x01,
|
||||||
|
ENTRY_LISTEN_ADDED = 0x02,
|
||||||
|
ENTRY_LISTEN_REMOVED = 0x04,
|
||||||
|
ENTRY_LISTEN_ALL = ENTRY_LISTEN_ADDED | ENTRY_LISTEN_REMOVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
class EntryListener {
|
||||||
|
public:
|
||||||
|
EntryListener();
|
||||||
|
virtual ~EntryListener();
|
||||||
|
|
||||||
|
virtual void EntryAdded(Entry *entry);
|
||||||
|
virtual void EntryRemoved(Entry *entry);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENTRY_LISTENER_H
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// File.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "File.h"
|
||||||
|
#include "SizeIndex.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
File::File(Volume *volume)
|
||||||
|
: Node(volume, NODE_TYPE_FILE),
|
||||||
|
DataContainer(volume)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
File::~File()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAt
|
||||||
|
status_t
|
||||||
|
File::ReadAt(off_t offset, void *buffer, size_t size, size_t *bytesRead)
|
||||||
|
{
|
||||||
|
status_t error = DataContainer::ReadAt(offset, buffer, size, bytesRead);
|
||||||
|
// TODO: update access time?
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAt
|
||||||
|
status_t
|
||||||
|
File::WriteAt(off_t offset, const void *buffer, size_t size,
|
||||||
|
size_t *bytesWritten)
|
||||||
|
{
|
||||||
|
off_t oldSize = DataContainer::GetSize();
|
||||||
|
status_t error = DataContainer::WriteAt(offset, buffer, size,
|
||||||
|
bytesWritten);
|
||||||
|
MarkModified();
|
||||||
|
// update the size index, if our size has changed
|
||||||
|
if (oldSize != DataContainer::GetSize()) {
|
||||||
|
if (SizeIndex *index = GetVolume()->GetSizeIndex())
|
||||||
|
index->Changed(this, oldSize);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize
|
||||||
|
status_t
|
||||||
|
File::SetSize(off_t newSize)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
off_t oldSize = DataContainer::GetSize();
|
||||||
|
if (newSize != oldSize) {
|
||||||
|
error = DataContainer::Resize(newSize);
|
||||||
|
MarkModified();
|
||||||
|
// update the size index
|
||||||
|
if (SizeIndex *index = GetVolume()->GetSizeIndex())
|
||||||
|
index->Changed(this, oldSize);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize
|
||||||
|
off_t
|
||||||
|
File::GetSize() const
|
||||||
|
{
|
||||||
|
return DataContainer::GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
File::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
info.AddFileAllocation(GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// File.h
|
||||||
|
|
||||||
|
#ifndef FILE_H
|
||||||
|
#define FILE_H
|
||||||
|
|
||||||
|
#include "DataContainer.h"
|
||||||
|
#include "Node.h"
|
||||||
|
|
||||||
|
class File : public Node, public DataContainer {
|
||||||
|
public:
|
||||||
|
File(Volume *volume);
|
||||||
|
virtual ~File();
|
||||||
|
|
||||||
|
Volume *GetVolume() const { return Node::GetVolume(); }
|
||||||
|
|
||||||
|
virtual status_t ReadAt(off_t offset, void *buffer, size_t size,
|
||||||
|
size_t *bytesRead);
|
||||||
|
virtual status_t WriteAt(off_t offset, const void *buffer, size_t size,
|
||||||
|
size_t *bytesWritten);
|
||||||
|
|
||||||
|
virtual status_t SetSize(off_t newSize);
|
||||||
|
virtual off_t GetSize() const;
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILE_H
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
// Index.cpp
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Directory.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "Index.h"
|
||||||
|
#include "IndexImpl.h"
|
||||||
|
|
||||||
|
// Index
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Index::Index(Volume *volume, const char *name, uint32 type,
|
||||||
|
bool fixedKeyLength, size_t keyLength)
|
||||||
|
: fVolume(volume),
|
||||||
|
fInitStatus(B_OK),
|
||||||
|
fName(name),
|
||||||
|
fType(type),
|
||||||
|
fKeyLength(keyLength),
|
||||||
|
fFixedKeyLength(fixedKeyLength)
|
||||||
|
{
|
||||||
|
if (!fVolume)
|
||||||
|
fInitStatus = B_BAD_VALUE;
|
||||||
|
else if (!fName.GetString())
|
||||||
|
fInitStatus = B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
Index::~Index()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
Index::InitCheck() const
|
||||||
|
{
|
||||||
|
return fInitStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIterator
|
||||||
|
bool
|
||||||
|
Index::GetIterator(IndexEntryIterator *iterator)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (iterator) {
|
||||||
|
AbstractIndexEntryIterator *actualIterator = InternalGetIterator();
|
||||||
|
if (actualIterator) {
|
||||||
|
iterator->SetIterator(actualIterator);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find
|
||||||
|
bool
|
||||||
|
Index::Find(const uint8 *key, size_t length, IndexEntryIterator *iterator)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (key && iterator) {
|
||||||
|
AbstractIndexEntryIterator *actualIterator
|
||||||
|
= InternalFind(key, length);
|
||||||
|
if (actualIterator) {
|
||||||
|
iterator->SetIterator(actualIterator);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump
|
||||||
|
void
|
||||||
|
Index::Dump()
|
||||||
|
{
|
||||||
|
PRINT(("Index: `%s', type: %lx\n", GetName(), GetType()));
|
||||||
|
for (IndexEntryIterator it(this); it.GetCurrent(); it.GetNext()) {
|
||||||
|
Entry *entry = it.GetCurrent();
|
||||||
|
PRINT((" entry: `%s', dir: %Ld\n", entry->GetName(),
|
||||||
|
entry->GetParent()->GetID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IndexEntryIterator
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
IndexEntryIterator::IndexEntryIterator()
|
||||||
|
: fIterator(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
IndexEntryIterator::IndexEntryIterator(Index *index)
|
||||||
|
: fIterator(NULL)
|
||||||
|
{
|
||||||
|
if (index)
|
||||||
|
index->GetIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
IndexEntryIterator::~IndexEntryIterator()
|
||||||
|
{
|
||||||
|
SetIterator(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
IndexEntryIterator::GetCurrent()
|
||||||
|
{
|
||||||
|
return (fIterator ? fIterator->GetCurrent() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
IndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||||
|
{
|
||||||
|
return (fIterator ? fIterator->GetCurrent(buffer, keyLength) : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrevious
|
||||||
|
Entry *
|
||||||
|
IndexEntryIterator::GetPrevious()
|
||||||
|
{
|
||||||
|
return (fIterator ? fIterator->GetPrevious() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext
|
||||||
|
Entry *
|
||||||
|
IndexEntryIterator::GetNext()
|
||||||
|
{
|
||||||
|
return (fIterator ? fIterator->GetNext() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext
|
||||||
|
Entry *
|
||||||
|
IndexEntryIterator::GetNext(uint8 *buffer, size_t *keyLength)
|
||||||
|
{
|
||||||
|
Entry *entry = NULL;
|
||||||
|
if (fIterator && fIterator->GetNext())
|
||||||
|
entry = GetCurrent(buffer, keyLength);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
IndexEntryIterator::Suspend()
|
||||||
|
{
|
||||||
|
return (fIterator ? fIterator->Suspend() : B_BAD_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
IndexEntryIterator::Resume()
|
||||||
|
{
|
||||||
|
return (fIterator ? fIterator->Resume() : B_BAD_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIterator
|
||||||
|
void
|
||||||
|
IndexEntryIterator::SetIterator(AbstractIndexEntryIterator *iterator)
|
||||||
|
{
|
||||||
|
if (fIterator)
|
||||||
|
delete fIterator;
|
||||||
|
fIterator = iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// AbstractIndexEntryIterator
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AbstractIndexEntryIterator::AbstractIndexEntryIterator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
AbstractIndexEntryIterator::~AbstractIndexEntryIterator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
AbstractIndexEntryIterator::Suspend()
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
AbstractIndexEntryIterator::Resume()
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Index.h
|
||||||
|
|
||||||
|
#ifndef INDEX_H
|
||||||
|
#define INDEX_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
class AbstractIndexEntryIterator;
|
||||||
|
class Entry;
|
||||||
|
class IndexEntryIterator;
|
||||||
|
class Node;
|
||||||
|
class Volume;
|
||||||
|
|
||||||
|
// Index
|
||||||
|
class Index {
|
||||||
|
public:
|
||||||
|
Index(Volume *volume, const char *name, uint32 type,
|
||||||
|
bool fixedKeyLength, size_t keyLength = 0);
|
||||||
|
virtual ~Index();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
Volume *GetVolume() const { return fVolume; }
|
||||||
|
void GetVolume(Volume *volume) { fVolume = volume; }
|
||||||
|
|
||||||
|
const char *GetName() const { return fName.GetString(); }
|
||||||
|
uint32 GetType() const { return fType; }
|
||||||
|
bool HasFixedKeyLength() const { return fFixedKeyLength; }
|
||||||
|
size_t GetKeyLength() const { return fKeyLength; }
|
||||||
|
|
||||||
|
virtual int32 CountEntries() const = 0;
|
||||||
|
|
||||||
|
bool GetIterator(IndexEntryIterator *iterator);
|
||||||
|
bool Find(const uint8 *key, size_t length,
|
||||||
|
IndexEntryIterator *iterator);
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
void Dump();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual AbstractIndexEntryIterator *InternalGetIterator() = 0;
|
||||||
|
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||||
|
size_t length) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Volume *fVolume;
|
||||||
|
status_t fInitStatus;
|
||||||
|
String fName;
|
||||||
|
uint32 fType;
|
||||||
|
size_t fKeyLength;
|
||||||
|
bool fFixedKeyLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
// IndexEntryIterator
|
||||||
|
class IndexEntryIterator {
|
||||||
|
public:
|
||||||
|
IndexEntryIterator();
|
||||||
|
IndexEntryIterator(Index *index);
|
||||||
|
~IndexEntryIterator();
|
||||||
|
|
||||||
|
Entry *GetCurrent();
|
||||||
|
Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||||
|
Entry *GetPrevious();
|
||||||
|
Entry *GetNext();
|
||||||
|
Entry *GetNext(uint8 *buffer, size_t *keyLength);
|
||||||
|
|
||||||
|
status_t Suspend();
|
||||||
|
status_t Resume();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetIterator(AbstractIndexEntryIterator *iterator);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Index;
|
||||||
|
|
||||||
|
AbstractIndexEntryIterator *fIterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INDEX_H
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
// IndexDirectory.cpp
|
||||||
|
|
||||||
|
#include <TypeConstants.h>
|
||||||
|
|
||||||
|
#include "AttributeIndexImpl.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "IndexDirectory.h"
|
||||||
|
#include "LastModifiedIndex.h"
|
||||||
|
#include "NameIndex.h"
|
||||||
|
#include "SizeIndex.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
IndexDirectory::IndexDirectory(Volume *volume)
|
||||||
|
: fVolume(volume),
|
||||||
|
fNameIndex(NULL),
|
||||||
|
fLastModifiedIndex(NULL),
|
||||||
|
fSizeIndex(NULL),
|
||||||
|
fIndices()
|
||||||
|
{
|
||||||
|
fNameIndex = new(nothrow) NameIndex(volume);
|
||||||
|
fLastModifiedIndex = new(nothrow) LastModifiedIndex(volume);
|
||||||
|
fSizeIndex = new(nothrow) SizeIndex(volume);
|
||||||
|
if (fNameIndex && fLastModifiedIndex && fSizeIndex) {
|
||||||
|
if (!fIndices.AddItem(fNameIndex)
|
||||||
|
|| !fIndices.AddItem(fLastModifiedIndex)
|
||||||
|
|| !fIndices.AddItem(fSizeIndex)) {
|
||||||
|
fIndices.MakeEmpty();
|
||||||
|
delete fNameIndex;
|
||||||
|
delete fLastModifiedIndex;
|
||||||
|
delete fSizeIndex;
|
||||||
|
fNameIndex = NULL;
|
||||||
|
fLastModifiedIndex = NULL;
|
||||||
|
fSizeIndex = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
IndexDirectory::~IndexDirectory()
|
||||||
|
{
|
||||||
|
// delete the default indices
|
||||||
|
if (fNameIndex) {
|
||||||
|
fIndices.RemoveItem(fNameIndex);
|
||||||
|
delete fNameIndex;
|
||||||
|
}
|
||||||
|
if (fLastModifiedIndex) {
|
||||||
|
fIndices.RemoveItem(fLastModifiedIndex);
|
||||||
|
delete fLastModifiedIndex;
|
||||||
|
}
|
||||||
|
if (fSizeIndex) {
|
||||||
|
fIndices.RemoveItem(fSizeIndex);
|
||||||
|
delete fSizeIndex;
|
||||||
|
}
|
||||||
|
// delete the attribute indices
|
||||||
|
int32 count = fIndices.CountItems();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
delete fIndices.ItemAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
IndexDirectory::InitCheck() const
|
||||||
|
{
|
||||||
|
return (fNameIndex && fLastModifiedIndex && fSizeIndex ? B_OK
|
||||||
|
: B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIndex
|
||||||
|
status_t
|
||||||
|
IndexDirectory::CreateIndex(const char *name, uint32 type,
|
||||||
|
AttributeIndex **_index)
|
||||||
|
{
|
||||||
|
status_t error = (name ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (!FindIndex(name)) {
|
||||||
|
// create the index
|
||||||
|
AttributeIndex *index = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case B_INT32_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, sizeof(int32));
|
||||||
|
break;
|
||||||
|
case B_UINT32_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, sizeof(uint32));
|
||||||
|
break;
|
||||||
|
case B_INT64_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, sizeof(int64));
|
||||||
|
break;
|
||||||
|
case B_UINT64_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, sizeof(uint64));
|
||||||
|
break;
|
||||||
|
case B_FLOAT_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, sizeof(float));
|
||||||
|
break;
|
||||||
|
case B_DOUBLE_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, sizeof(double));
|
||||||
|
break;
|
||||||
|
case B_STRING_TYPE:
|
||||||
|
index = new(nothrow) AttributeIndexImpl(fVolume,
|
||||||
|
name, type, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = B_BAD_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (error == B_OK && !index)
|
||||||
|
error = B_NO_MEMORY;
|
||||||
|
// add the index
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fIndices.AddItem(index)) {
|
||||||
|
if (_index)
|
||||||
|
*_index = index;
|
||||||
|
} else {
|
||||||
|
delete index;
|
||||||
|
error = B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
error = B_FILE_EXISTS;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIndex
|
||||||
|
bool
|
||||||
|
IndexDirectory::DeleteIndex(const char *name, uint32 type)
|
||||||
|
{
|
||||||
|
return DeleteIndex(FindIndex(name, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteIndex
|
||||||
|
bool
|
||||||
|
IndexDirectory::DeleteIndex(Index *index)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (index && !IsSpecialIndex(index)) {
|
||||||
|
int32 i = fIndices.IndexOf(index);
|
||||||
|
if (i >= 0) {
|
||||||
|
fIndices.RemoveItem(i);
|
||||||
|
delete index;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndex
|
||||||
|
Index *
|
||||||
|
IndexDirectory::FindIndex(const char *name)
|
||||||
|
{
|
||||||
|
if (name) {
|
||||||
|
int32 count = fIndices.CountItems();
|
||||||
|
for (int32 i = 0; i < count; i++) {
|
||||||
|
Index *index = fIndices.ItemAt(i);
|
||||||
|
if (!strcmp(index->GetName(), name))
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndex
|
||||||
|
Index *
|
||||||
|
IndexDirectory::FindIndex(const char *name, uint32 type)
|
||||||
|
{
|
||||||
|
Index *index = FindIndex(name);
|
||||||
|
if (index && index->GetType() != type)
|
||||||
|
index = NULL;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindAttributeIndex
|
||||||
|
AttributeIndex *
|
||||||
|
IndexDirectory::FindAttributeIndex(const char *name)
|
||||||
|
{
|
||||||
|
AttributeIndex *attrIndex = NULL;
|
||||||
|
if (Index *index = FindIndex(name))
|
||||||
|
attrIndex = dynamic_cast<AttributeIndex*>(index);
|
||||||
|
return attrIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindAttributeIndex
|
||||||
|
AttributeIndex *
|
||||||
|
IndexDirectory::FindAttributeIndex(const char *name, uint32 type)
|
||||||
|
{
|
||||||
|
AttributeIndex *attrIndex = NULL;
|
||||||
|
if (Index *index = FindIndex(name, type))
|
||||||
|
attrIndex = dynamic_cast<AttributeIndex*>(index);
|
||||||
|
return attrIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSpecialIndex
|
||||||
|
bool
|
||||||
|
IndexDirectory::IsSpecialIndex(Index *index) const
|
||||||
|
{
|
||||||
|
return (index == fNameIndex || index == fLastModifiedIndex
|
||||||
|
|| index == fSizeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// IndexDirectory.h
|
||||||
|
|
||||||
|
#ifndef INDEX_DIRECTORY_H
|
||||||
|
#define INDEX_DIRECTORY_H
|
||||||
|
|
||||||
|
#include "List.h"
|
||||||
|
|
||||||
|
class AttributeIndex;
|
||||||
|
class Index;
|
||||||
|
class LastModifiedIndex;
|
||||||
|
class NameIndex;
|
||||||
|
class SizeIndex;
|
||||||
|
class Volume;
|
||||||
|
|
||||||
|
class IndexDirectory {
|
||||||
|
public:
|
||||||
|
IndexDirectory(Volume *volume);
|
||||||
|
~IndexDirectory();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
status_t CreateIndex(const char *name, uint32 type,
|
||||||
|
AttributeIndex **index = NULL);
|
||||||
|
bool DeleteIndex(const char *name, uint32 type);
|
||||||
|
bool DeleteIndex(Index *index);
|
||||||
|
|
||||||
|
Index *FindIndex(const char *name);
|
||||||
|
Index *FindIndex(const char *name, uint32 type);
|
||||||
|
AttributeIndex *FindAttributeIndex(const char *name);
|
||||||
|
AttributeIndex *FindAttributeIndex(const char *name, uint32 type);
|
||||||
|
|
||||||
|
bool IsSpecialIndex(Index *index) const;
|
||||||
|
NameIndex *GetNameIndex() const { return fNameIndex; }
|
||||||
|
LastModifiedIndex *GetLastModifiedIndex() const
|
||||||
|
{ return fLastModifiedIndex; }
|
||||||
|
SizeIndex *GetSizeIndex() const { return fSizeIndex; }
|
||||||
|
|
||||||
|
Index *IndexAt(int32 index) const { return fIndices.ItemAt(index); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Volume *fVolume;
|
||||||
|
NameIndex *fNameIndex;
|
||||||
|
LastModifiedIndex *fLastModifiedIndex;
|
||||||
|
SizeIndex *fSizeIndex;
|
||||||
|
List<Index*> fIndices;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INDEX_DIRECTORY_H
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
// IndexImpl.h
|
||||||
|
|
||||||
|
#ifndef INDEX_IMPL_H
|
||||||
|
#define INDEX_IMPL_H
|
||||||
|
|
||||||
|
#include "Index.h"
|
||||||
|
#include "Node.h"
|
||||||
|
|
||||||
|
// AbstractIndexEntryIterator
|
||||||
|
class AbstractIndexEntryIterator {
|
||||||
|
public:
|
||||||
|
AbstractIndexEntryIterator();
|
||||||
|
virtual ~AbstractIndexEntryIterator();
|
||||||
|
|
||||||
|
virtual Entry *GetCurrent() = 0;
|
||||||
|
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength) = 0;
|
||||||
|
virtual Entry *GetPrevious() = 0;
|
||||||
|
virtual Entry *GetNext() = 0;
|
||||||
|
|
||||||
|
virtual status_t Suspend();
|
||||||
|
virtual status_t Resume();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// NodeEntryIterator
|
||||||
|
template<typename NodeIterator>
|
||||||
|
class NodeEntryIterator : public AbstractIndexEntryIterator {
|
||||||
|
public:
|
||||||
|
NodeEntryIterator();
|
||||||
|
virtual ~NodeEntryIterator();
|
||||||
|
|
||||||
|
void Unset();
|
||||||
|
|
||||||
|
virtual Entry *GetCurrent();
|
||||||
|
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength) = 0;
|
||||||
|
virtual Entry *GetPrevious();
|
||||||
|
virtual Entry *GetNext();
|
||||||
|
|
||||||
|
virtual status_t Suspend();
|
||||||
|
virtual status_t Resume();
|
||||||
|
|
||||||
|
Node *GetCurrentNode() const { return fNode; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
NodeIterator fIterator;
|
||||||
|
Node *fNode;
|
||||||
|
Entry *fEntry;
|
||||||
|
bool fInitialized;
|
||||||
|
bool fIsNext;
|
||||||
|
bool fSuspended;
|
||||||
|
};
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
template<typename NodeIterator>
|
||||||
|
NodeEntryIterator<NodeIterator>::NodeEntryIterator()
|
||||||
|
: AbstractIndexEntryIterator(),
|
||||||
|
fIterator(),
|
||||||
|
fNode(NULL),
|
||||||
|
fEntry(NULL),
|
||||||
|
fInitialized(false),
|
||||||
|
fIsNext(false),
|
||||||
|
fSuspended(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
template<typename NodeIterator>
|
||||||
|
NodeEntryIterator<NodeIterator>::~NodeEntryIterator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset
|
||||||
|
template<typename NodeIterator>
|
||||||
|
void
|
||||||
|
NodeEntryIterator<NodeIterator>::Unset()
|
||||||
|
{
|
||||||
|
fNode = NULL;
|
||||||
|
fEntry = NULL;
|
||||||
|
fInitialized = false;
|
||||||
|
fIsNext = false;
|
||||||
|
fSuspended = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
template<typename NodeIterator>
|
||||||
|
Entry *
|
||||||
|
NodeEntryIterator<NodeIterator>::GetCurrent()
|
||||||
|
{
|
||||||
|
return fEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrevious
|
||||||
|
template<typename NodeIterator>
|
||||||
|
Entry *
|
||||||
|
NodeEntryIterator<NodeIterator>::GetPrevious()
|
||||||
|
{
|
||||||
|
return NULL; // backwards iteration not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext
|
||||||
|
template<typename NodeIterator>
|
||||||
|
Entry *
|
||||||
|
NodeEntryIterator<NodeIterator>::GetNext()
|
||||||
|
{
|
||||||
|
if (!fInitialized || !fNode || fSuspended)
|
||||||
|
return NULL;
|
||||||
|
if (!(fEntry && fIsNext)) {
|
||||||
|
while (fNode) {
|
||||||
|
if (fEntry)
|
||||||
|
fEntry = fNode->GetNextReferrer(fEntry);
|
||||||
|
while (fNode && !fEntry) {
|
||||||
|
fNode = NULL;
|
||||||
|
if (Node **nodeP = fIterator.GetNext()) {
|
||||||
|
fNode = *nodeP;
|
||||||
|
fEntry = fNode->GetFirstReferrer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fEntry)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fIsNext = false;
|
||||||
|
return fEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
template<typename NodeIterator>
|
||||||
|
status_t
|
||||||
|
NodeEntryIterator<NodeIterator>::Suspend()
|
||||||
|
{
|
||||||
|
status_t error = (fInitialized && !fSuspended ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK)
|
||||||
|
fSuspended = true;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
template<typename NodeIterator>
|
||||||
|
status_t
|
||||||
|
NodeEntryIterator<NodeIterator>::Resume()
|
||||||
|
{
|
||||||
|
status_t error = (fInitialized && fSuspended ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK)
|
||||||
|
fSuspended = false;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // INDEX_IMPL_H
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
SubDir HAIKU_TOP src tests add-ons kernel file_systems userlandfs r5 src test
|
||||||
|
ramfs ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatforms r5 bone dano ;
|
||||||
|
|
||||||
|
local userlandFSTop = [ FDirName $(HAIKU_TOP) src tests add-ons kernel
|
||||||
|
file_systems userlandfs r5 ] ;
|
||||||
|
local userlandFSIncludes = [ FDirName $(userlandFSTop) headers ] ;
|
||||||
|
|
||||||
|
DEFINES += USER=1 ;
|
||||||
|
|
||||||
|
SubDirC++Flags -include
|
||||||
|
[ FDirName $(userlandFSIncludes) shared Compatibility.h ] ;
|
||||||
|
|
||||||
|
SubDirSysHdrs [ FDirName $(userlandFSIncludes) public ] ;
|
||||||
|
SubDirHdrs [ FDirName $(userlandFSIncludes) shared ] ;
|
||||||
|
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
# SubDirC++Flags -include [ FDirName $(UFS_TOP) src kernel_add_on
|
||||||
|
# kernel-cpp.h ] ;
|
||||||
|
SubDirC++Flags -include [ FDirName $(SUBDIR) cpp.h ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEARCH_SOURCE += [ FDirName $(userlandFSTop) src shared ] ;
|
||||||
|
|
||||||
|
Addon <test>ramfs
|
||||||
|
: # relpath - obsolete
|
||||||
|
: Debug.cpp
|
||||||
|
Locker.cpp
|
||||||
|
String.cpp
|
||||||
|
|
||||||
|
AllocationInfo.cpp
|
||||||
|
AreaUtils.cpp
|
||||||
|
Attribute.cpp
|
||||||
|
AttributeIndex.cpp
|
||||||
|
AttributeIndexImpl.cpp
|
||||||
|
AttributeIterator.cpp
|
||||||
|
BlockAllocator.cpp
|
||||||
|
BlockAllocatorArea.cpp
|
||||||
|
BlockAllocatorAreaBucket.cpp
|
||||||
|
BlockReferenceManager.cpp
|
||||||
|
DataContainer.cpp
|
||||||
|
Directory.cpp
|
||||||
|
Entry.cpp
|
||||||
|
EntryIterator.cpp
|
||||||
|
EntryListener.cpp
|
||||||
|
File.cpp
|
||||||
|
Index.cpp
|
||||||
|
IndexDirectory.cpp
|
||||||
|
kernel_interface.cpp
|
||||||
|
LastModifiedIndex.cpp
|
||||||
|
NameIndex.cpp
|
||||||
|
Node.cpp
|
||||||
|
NodeListener.cpp
|
||||||
|
NodeTable.cpp
|
||||||
|
Query.cpp
|
||||||
|
SizeIndex.cpp
|
||||||
|
SymLink.cpp
|
||||||
|
Volume.cpp
|
||||||
|
: false # is executable
|
||||||
|
: <test>UserlandFSServer
|
||||||
|
;
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
// LastModifiedIndex.cpp
|
||||||
|
|
||||||
|
#include <TypeConstants.h>
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "EntryListener.h"
|
||||||
|
#include "IndexImpl.h"
|
||||||
|
#include "LastModifiedIndex.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "NodeListener.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// LastModifiedIndexPrimaryKey
|
||||||
|
class LastModifiedIndexPrimaryKey {
|
||||||
|
public:
|
||||||
|
LastModifiedIndexPrimaryKey(Node *node, time_t modified)
|
||||||
|
: node(node), modified(modified) {}
|
||||||
|
LastModifiedIndexPrimaryKey(Node *node)
|
||||||
|
: node(node), modified(node->GetMTime()) {}
|
||||||
|
LastModifiedIndexPrimaryKey(time_t modified)
|
||||||
|
: node(NULL), modified(modified) {}
|
||||||
|
|
||||||
|
Node *node;
|
||||||
|
time_t modified;
|
||||||
|
};
|
||||||
|
|
||||||
|
// LastModifiedIndexGetPrimaryKey
|
||||||
|
class LastModifiedIndexGetPrimaryKey {
|
||||||
|
public:
|
||||||
|
inline LastModifiedIndexPrimaryKey operator()(Node *a)
|
||||||
|
{
|
||||||
|
return LastModifiedIndexPrimaryKey(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LastModifiedIndexPrimaryKey operator()(Node *a) const
|
||||||
|
{
|
||||||
|
return LastModifiedIndexPrimaryKey(a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LastModifiedIndexPrimaryKeyCompare
|
||||||
|
class LastModifiedIndexPrimaryKeyCompare
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline int operator()(const LastModifiedIndexPrimaryKey &a,
|
||||||
|
const LastModifiedIndexPrimaryKey &b) const
|
||||||
|
{
|
||||||
|
if (a.node != NULL && a.node == b.node)
|
||||||
|
return 0;
|
||||||
|
if (a.modified < b.modified)
|
||||||
|
return -1;
|
||||||
|
if (a.modified > b.modified)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// NodeTree
|
||||||
|
typedef TwoKeyAVLTree<Node*, LastModifiedIndexPrimaryKey,
|
||||||
|
LastModifiedIndexPrimaryKeyCompare,
|
||||||
|
LastModifiedIndexGetPrimaryKey>
|
||||||
|
_NodeTree;
|
||||||
|
class LastModifiedIndex::NodeTree : public _NodeTree {};
|
||||||
|
|
||||||
|
|
||||||
|
// IteratorList
|
||||||
|
class LastModifiedIndex::IteratorList : public DLList<Iterator> {};
|
||||||
|
|
||||||
|
|
||||||
|
// Iterator
|
||||||
|
class LastModifiedIndex::Iterator
|
||||||
|
: public NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator>,
|
||||||
|
public DLListLinkImpl<Iterator>, public EntryListener,
|
||||||
|
public NodeListener {
|
||||||
|
public:
|
||||||
|
Iterator();
|
||||||
|
virtual ~Iterator();
|
||||||
|
|
||||||
|
virtual Entry *GetCurrent();
|
||||||
|
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||||
|
|
||||||
|
virtual status_t Suspend();
|
||||||
|
virtual status_t Resume();
|
||||||
|
|
||||||
|
bool SetTo(LastModifiedIndex *index, time_t modified,
|
||||||
|
bool ignoreValue = false);
|
||||||
|
void Unset();
|
||||||
|
|
||||||
|
virtual void EntryRemoved(Entry *entry);
|
||||||
|
virtual void NodeRemoved(Node *node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator> BaseClass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LastModifiedIndex *fIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// LastModifiedIndex
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
LastModifiedIndex::LastModifiedIndex(Volume *volume)
|
||||||
|
: Index(volume, "last_modified", B_INT32_TYPE, true, sizeof(time_t)),
|
||||||
|
fNodes(new(nothrow) NodeTree),
|
||||||
|
fIterators(new(nothrow) IteratorList)
|
||||||
|
{
|
||||||
|
if (fInitStatus == B_OK && (!fNodes || !fIterators))
|
||||||
|
fInitStatus = B_NO_MEMORY;
|
||||||
|
if (fInitStatus == B_OK) {
|
||||||
|
fInitStatus = fVolume->AddNodeListener(this,
|
||||||
|
NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
LastModifiedIndex::~LastModifiedIndex()
|
||||||
|
{
|
||||||
|
if (fVolume)
|
||||||
|
fVolume->RemoveNodeListener(this, NULL);
|
||||||
|
if (fIterators) {
|
||||||
|
// unset the iterators
|
||||||
|
for (Iterator *iterator = fIterators->GetFirst();
|
||||||
|
iterator;
|
||||||
|
iterator = fIterators->GetNext(iterator)) {
|
||||||
|
iterator->SetTo(NULL, 0);
|
||||||
|
}
|
||||||
|
delete fIterators;
|
||||||
|
}
|
||||||
|
if (fNodes)
|
||||||
|
delete fNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountEntries
|
||||||
|
int32
|
||||||
|
LastModifiedIndex::CountEntries() const
|
||||||
|
{
|
||||||
|
return fNodes->CountItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed
|
||||||
|
status_t
|
||||||
|
LastModifiedIndex::Changed(Node *node, time_t oldModified)
|
||||||
|
{
|
||||||
|
status_t error = B_BAD_VALUE;
|
||||||
|
if (node) {
|
||||||
|
NodeTree::Iterator it;
|
||||||
|
Node **foundNode = fNodes->Find(LastModifiedIndexPrimaryKey(node,
|
||||||
|
oldModified), node, &it);
|
||||||
|
if (foundNode && *foundNode == node) {
|
||||||
|
// update the iterators
|
||||||
|
for (Iterator *iterator = fIterators->GetFirst();
|
||||||
|
iterator;
|
||||||
|
iterator = fIterators->GetNext(iterator)) {
|
||||||
|
if (iterator->GetCurrentNode() == node)
|
||||||
|
iterator->NodeRemoved(node);
|
||||||
|
}
|
||||||
|
// remove and re-insert the node
|
||||||
|
fNodes->Remove(it);
|
||||||
|
error = fNodes->Insert(node);
|
||||||
|
|
||||||
|
// udpate live queries
|
||||||
|
time_t newModified = node->GetMTime();
|
||||||
|
fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
|
||||||
|
(const uint8*)&oldModified, sizeof(oldModified),
|
||||||
|
(const uint8*)&newModified, sizeof(newModified));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeAdded
|
||||||
|
void
|
||||||
|
LastModifiedIndex::NodeAdded(Node *node)
|
||||||
|
{
|
||||||
|
if (node)
|
||||||
|
fNodes->Insert(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoved
|
||||||
|
void
|
||||||
|
LastModifiedIndex::NodeRemoved(Node *node)
|
||||||
|
{
|
||||||
|
if (node)
|
||||||
|
fNodes->Remove(node, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalGetIterator
|
||||||
|
AbstractIndexEntryIterator *
|
||||||
|
LastModifiedIndex::InternalGetIterator()
|
||||||
|
{
|
||||||
|
Iterator *iterator = new(nothrow) Iterator;
|
||||||
|
if (iterator) {
|
||||||
|
if (!iterator->SetTo(this, 0, true)) {
|
||||||
|
delete iterator;
|
||||||
|
iterator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalFind
|
||||||
|
AbstractIndexEntryIterator *
|
||||||
|
LastModifiedIndex::InternalFind(const uint8 *key, size_t length)
|
||||||
|
{
|
||||||
|
if (!key || length != sizeof(time_t))
|
||||||
|
return NULL;
|
||||||
|
Iterator *iterator = new(nothrow) Iterator;
|
||||||
|
if (iterator) {
|
||||||
|
if (!iterator->SetTo(this, *(const time_t*)key)) {
|
||||||
|
delete iterator;
|
||||||
|
iterator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AddIterator
|
||||||
|
void
|
||||||
|
LastModifiedIndex::_AddIterator(Iterator *iterator)
|
||||||
|
{
|
||||||
|
fIterators->Insert(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _RemoveIterator
|
||||||
|
void
|
||||||
|
LastModifiedIndex::_RemoveIterator(Iterator *iterator)
|
||||||
|
{
|
||||||
|
fIterators->Remove(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Iterator
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
LastModifiedIndex::Iterator::Iterator()
|
||||||
|
: BaseClass(),
|
||||||
|
fIndex(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
LastModifiedIndex::Iterator::~Iterator()
|
||||||
|
{
|
||||||
|
SetTo(NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
LastModifiedIndex::Iterator::GetCurrent()
|
||||||
|
{
|
||||||
|
return BaseClass::GetCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
LastModifiedIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||||
|
{
|
||||||
|
Entry *entry = GetCurrent();
|
||||||
|
if (entry) {
|
||||||
|
*(time_t*)buffer = entry->GetNode()->GetMTime();
|
||||||
|
*keyLength = sizeof(time_t);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
LastModifiedIndex::Iterator::Suspend()
|
||||||
|
{
|
||||||
|
status_t error = BaseClass::Suspend();
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fNode) {
|
||||||
|
error = fIndex->GetVolume()->AddNodeListener(this, fNode,
|
||||||
|
NODE_LISTEN_REMOVED);
|
||||||
|
if (error == B_OK && fEntry) {
|
||||||
|
error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
|
||||||
|
ENTRY_LISTEN_REMOVED);
|
||||||
|
if (error != B_OK)
|
||||||
|
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||||
|
}
|
||||||
|
if (error != B_OK)
|
||||||
|
BaseClass::Resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
LastModifiedIndex::Iterator::Resume()
|
||||||
|
{
|
||||||
|
status_t error = BaseClass::Resume();
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fEntry)
|
||||||
|
error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
|
||||||
|
if (fNode) {
|
||||||
|
if (error == B_OK)
|
||||||
|
error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||||
|
else
|
||||||
|
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
bool
|
||||||
|
LastModifiedIndex::Iterator::SetTo(LastModifiedIndex *index, time_t modified,
|
||||||
|
bool ignoreValue)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
Unset();
|
||||||
|
// set the new values
|
||||||
|
fIndex = index;
|
||||||
|
if (fIndex)
|
||||||
|
fIndex->_AddIterator(this);
|
||||||
|
fInitialized = fIndex;
|
||||||
|
// get the node's first entry
|
||||||
|
if (fIndex) {
|
||||||
|
// get the first node
|
||||||
|
bool found = true;
|
||||||
|
if (ignoreValue)
|
||||||
|
fIndex->fNodes->GetIterator(&fIterator);
|
||||||
|
else
|
||||||
|
found = fIndex->fNodes->FindFirst(modified, &fIterator);
|
||||||
|
// get the first entry
|
||||||
|
if (found) {
|
||||||
|
if (Node **nodeP = fIterator.GetCurrent()) {
|
||||||
|
fNode = *nodeP;
|
||||||
|
fEntry = fNode->GetFirstReferrer();
|
||||||
|
if (!fEntry)
|
||||||
|
BaseClass::GetNext();
|
||||||
|
if (!ignoreValue && fNode && fNode->GetMTime() != modified)
|
||||||
|
Unset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset
|
||||||
|
void
|
||||||
|
LastModifiedIndex::Iterator::Unset()
|
||||||
|
{
|
||||||
|
if (fIndex) {
|
||||||
|
fIndex->_RemoveIterator(this);
|
||||||
|
fIndex = NULL;
|
||||||
|
}
|
||||||
|
BaseClass::Unset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryRemoved
|
||||||
|
void
|
||||||
|
LastModifiedIndex::Iterator::EntryRemoved(Entry */*entry*/)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
fIsNext = BaseClass::GetNext();
|
||||||
|
Suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoved
|
||||||
|
void
|
||||||
|
LastModifiedIndex::Iterator::NodeRemoved(Node */*node*/)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
fEntry = NULL;
|
||||||
|
fIsNext = BaseClass::GetNext();
|
||||||
|
Suspend();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// LastModifiedIndex.h
|
||||||
|
|
||||||
|
#ifndef LAST_MODIFIED_INDEX_H
|
||||||
|
#define LAST_MODIFIED_INDEX_H
|
||||||
|
|
||||||
|
#include "Index.h"
|
||||||
|
#include "NodeListener.h"
|
||||||
|
#include "TwoKeyAVLTree.h"
|
||||||
|
|
||||||
|
// LastModifiedIndex
|
||||||
|
class LastModifiedIndex : public Index, private NodeListener {
|
||||||
|
public:
|
||||||
|
LastModifiedIndex(Volume *volume);
|
||||||
|
virtual ~LastModifiedIndex();
|
||||||
|
|
||||||
|
virtual int32 CountEntries() const;
|
||||||
|
|
||||||
|
virtual status_t Changed(Node *node, time_t oldModified);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void NodeAdded(Node *node);
|
||||||
|
virtual void NodeRemoved(Node *node);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||||
|
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Iterator;
|
||||||
|
class IteratorList;
|
||||||
|
class NodeTree;
|
||||||
|
friend class Iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _AddIterator(Iterator *iterator);
|
||||||
|
void _RemoveIterator(Iterator *iterator);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeTree *fNodes;
|
||||||
|
IteratorList *fIterators;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LAST_MODIFIED_INDEX_H
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
// List.h
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// Except as contained in this notice, the name of a copyright holder shall
|
||||||
|
// not be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
// dealings in this Software without prior written authorization of the
|
||||||
|
// copyright holder.
|
||||||
|
|
||||||
|
#ifndef LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
#include <new.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
template<typename ITEM>
|
||||||
|
class DefaultDefaultItemCreator {
|
||||||
|
public:
|
||||||
|
static inline ITEM GetItem() { return ITEM(0); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class List
|
||||||
|
\brief A generic list implementation.
|
||||||
|
*/
|
||||||
|
template<typename ITEM,
|
||||||
|
typename DEFAULT_ITEM_SUPPLIER = DefaultDefaultItemCreator<ITEM> >
|
||||||
|
class List {
|
||||||
|
public:
|
||||||
|
typedef ITEM item_t;
|
||||||
|
typedef List list_t;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static item_t sDefaultItem;
|
||||||
|
static const size_t kDefaultChunkSize = 10;
|
||||||
|
static const size_t kMaximalChunkSize = 1024 * 1024;
|
||||||
|
|
||||||
|
public:
|
||||||
|
List(size_t chunkSize = kDefaultChunkSize);
|
||||||
|
~List();
|
||||||
|
|
||||||
|
inline const item_t &GetDefaultItem() const;
|
||||||
|
inline item_t &GetDefaultItem();
|
||||||
|
|
||||||
|
bool AddItem(const item_t &item, int32 index);
|
||||||
|
bool AddItem(const item_t &item);
|
||||||
|
// bool AddList(list_t *list, int32 index);
|
||||||
|
// bool AddList(list_t *list);
|
||||||
|
|
||||||
|
bool RemoveItem(const item_t &item);
|
||||||
|
bool RemoveItem(int32 index);
|
||||||
|
|
||||||
|
bool ReplaceItem(int32 index, const item_t &item);
|
||||||
|
|
||||||
|
bool MoveItem(int32 oldIndex, int32 newIndex);
|
||||||
|
|
||||||
|
void MakeEmpty();
|
||||||
|
|
||||||
|
int32 CountItems() const;
|
||||||
|
bool IsEmpty() const;
|
||||||
|
const item_t &ItemAt(int32 index) const;
|
||||||
|
item_t &ItemAt(int32 index);
|
||||||
|
const item_t *Items() const;
|
||||||
|
int32 IndexOf(const item_t &item) const;
|
||||||
|
bool HasItem(const item_t &item) const;
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
int32 GetCapacity() const { return fCapacity; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline static void _MoveItems(item_t* items, int32 offset, int32 count);
|
||||||
|
bool _Resize(size_t count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t fCapacity;
|
||||||
|
size_t fChunkSize;
|
||||||
|
int32 fItemCount;
|
||||||
|
item_t *fItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
// sDefaultItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::sDefaultItem(
|
||||||
|
DEFAULT_ITEM_SUPPLIER::GetItem());
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::List(size_t chunkSize)
|
||||||
|
: fCapacity(0),
|
||||||
|
fChunkSize(chunkSize),
|
||||||
|
fItemCount(0),
|
||||||
|
fItems(NULL)
|
||||||
|
{
|
||||||
|
if (fChunkSize == 0 || fChunkSize > kMaximalChunkSize)
|
||||||
|
fChunkSize = kDefaultChunkSize;
|
||||||
|
_Resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::~List()
|
||||||
|
{
|
||||||
|
MakeEmpty();
|
||||||
|
free(fItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
inline
|
||||||
|
const List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::GetDefaultItem() const
|
||||||
|
{
|
||||||
|
return sDefaultItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
inline
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::GetDefaultItem()
|
||||||
|
{
|
||||||
|
return sDefaultItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _MoveItems
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::_MoveItems(item_t* items, int32 offset, int32 count)
|
||||||
|
{
|
||||||
|
if (count > 0 && offset != 0)
|
||||||
|
memmove(items + offset, items, count * sizeof(item_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddItem(const item_t &item, int32 index)
|
||||||
|
{
|
||||||
|
bool result = (index >= 0 && index <= fItemCount
|
||||||
|
&& _Resize(fItemCount + 1));
|
||||||
|
if (result) {
|
||||||
|
_MoveItems(fItems + index, 1, fItemCount - index - 1);
|
||||||
|
new(fItems + index) item_t(item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddItem(const item_t &item)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
if ((int32)fCapacity > fItemCount) {
|
||||||
|
new(fItems + fItemCount) item_t(item);
|
||||||
|
fItemCount++;
|
||||||
|
} else {
|
||||||
|
if ((result = _Resize(fItemCount + 1)))
|
||||||
|
new(fItems + (fItemCount - 1)) item_t(item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These don't use the copy constructor!
|
||||||
|
/*
|
||||||
|
// AddList
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddList(list_t *list, int32 index)
|
||||||
|
{
|
||||||
|
bool result = (list && index >= 0 && index <= fItemCount);
|
||||||
|
if (result && list->fItemCount > 0) {
|
||||||
|
int32 count = list->fItemCount;
|
||||||
|
result = _Resize(fItemCount + count);
|
||||||
|
if (result) {
|
||||||
|
_MoveItems(fItems + index, count, fItemCount - index - count);
|
||||||
|
memcpy(fItems + index, list->fItems,
|
||||||
|
list->fItemCount * sizeof(item_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddList
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::AddList(list_t *list)
|
||||||
|
{
|
||||||
|
bool result = (list);
|
||||||
|
if (result && list->fItemCount > 0) {
|
||||||
|
int32 index = fItemCount;
|
||||||
|
int32 count = list->fItemCount;
|
||||||
|
result = _Resize(fItemCount + count);
|
||||||
|
if (result) {
|
||||||
|
memcpy(fItems + index, list->fItems,
|
||||||
|
list->fItemCount * sizeof(item_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// RemoveItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::RemoveItem(const item_t &item)
|
||||||
|
{
|
||||||
|
int32 index = IndexOf(item);
|
||||||
|
bool result = (index >= 0);
|
||||||
|
if (result)
|
||||||
|
RemoveItem(index);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::RemoveItem(int32 index)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < fItemCount) {
|
||||||
|
fItems[index].~item_t();
|
||||||
|
_MoveItems(fItems + index + 1, -1, fItemCount - index - 1);
|
||||||
|
_Resize(fItemCount - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::ReplaceItem(int32 index, const item_t &item)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < fItemCount) {
|
||||||
|
fItems[index] = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::MoveItem(int32 oldIndex, int32 newIndex)
|
||||||
|
{
|
||||||
|
if (oldIndex >= 0 && oldIndex < fItemCount
|
||||||
|
&& newIndex >= 0 && newIndex <= fItemCount) {
|
||||||
|
if (oldIndex < newIndex - 1) {
|
||||||
|
item_t item = fItems[oldIndex];
|
||||||
|
_MoveItems(fItems + oldIndex + 1, -1, newIndex - oldIndex - 1);
|
||||||
|
fItems[newIndex] = item;
|
||||||
|
} else if (oldIndex > newIndex) {
|
||||||
|
item_t item = fItems[oldIndex];
|
||||||
|
_MoveItems(fItems + newIndex, 1, oldIndex - newIndex);
|
||||||
|
fItems[newIndex] = item;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeEmpty
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
void
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::MakeEmpty()
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < fItemCount; i++)
|
||||||
|
fItems[i].~item_t();
|
||||||
|
_Resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountItems
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
int32
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::CountItems() const
|
||||||
|
{
|
||||||
|
return fItemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::IsEmpty() const
|
||||||
|
{
|
||||||
|
return (fItemCount == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ItemAt
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
const List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::ItemAt(int32 index) const
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < fItemCount)
|
||||||
|
return fItems[index];
|
||||||
|
return sDefaultItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ItemAt
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t &
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::ItemAt(int32 index)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < fItemCount)
|
||||||
|
return fItems[index];
|
||||||
|
return sDefaultItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
const List<ITEM, DEFAULT_ITEM_SUPPLIER>::item_t *
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::Items() const
|
||||||
|
{
|
||||||
|
return fItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOf
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
int32
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::IndexOf(const item_t &item) const
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < fItemCount; i++) {
|
||||||
|
if (fItems[i] == item)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasItem
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::HasItem(const item_t &item) const
|
||||||
|
{
|
||||||
|
return (IndexOf(item) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Resize
|
||||||
|
template<typename ITEM, typename DEFAULT_ITEM_SUPPLIER>
|
||||||
|
bool
|
||||||
|
List<ITEM, DEFAULT_ITEM_SUPPLIER>::_Resize(size_t count)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
// calculate the new capacity
|
||||||
|
int32 newSize = count;
|
||||||
|
if (newSize <= 0)
|
||||||
|
newSize = 1;
|
||||||
|
newSize = ((newSize - 1) / fChunkSize + 1) * fChunkSize;
|
||||||
|
// resize if necessary
|
||||||
|
if ((size_t)newSize != fCapacity) {
|
||||||
|
item_t* newItems
|
||||||
|
= (item_t*)realloc(fItems, newSize * sizeof(item_t));
|
||||||
|
if (newItems) {
|
||||||
|
fItems = newItems;
|
||||||
|
fCapacity = newSize;
|
||||||
|
} else
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (result)
|
||||||
|
fItemCount = count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIST_H
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Locking.h
|
||||||
|
|
||||||
|
#ifndef LOCKING_H
|
||||||
|
#define LOCKING_H
|
||||||
|
|
||||||
|
#include "AutoLocker.h"
|
||||||
|
|
||||||
|
class Volume;
|
||||||
|
|
||||||
|
// instantiations
|
||||||
|
typedef AutoLocker<Volume, AutoLockerReadLocking<Volume> > VolumeReadLocker;
|
||||||
|
typedef AutoLocker<Volume, AutoLockerWriteLocking<Volume> > VolumeWriteLocker;
|
||||||
|
|
||||||
|
#endif LOCKING_H
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
// Misc.h
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003, Ingo Weinhold ([email protected])
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// Except as contained in this notice, the name of a copyright holder shall
|
||||||
|
// not be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
// dealings in this Software without prior written authorization of the
|
||||||
|
// copyright holder.
|
||||||
|
|
||||||
|
#ifndef MISC_H
|
||||||
|
#define MISC_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
// min and max
|
||||||
|
// We don't want to include <algobase.h> otherwise we also get <iostream.h>
|
||||||
|
// and other undesired things.
|
||||||
|
template<typename C>
|
||||||
|
static inline C min(const C &a, const C &b) { return (a < b ? a : b); }
|
||||||
|
template<typename C>
|
||||||
|
static inline C max(const C &a, const C &b) { return (a > b ? a : b); }
|
||||||
|
|
||||||
|
// find last (most significant) set bit
|
||||||
|
static inline
|
||||||
|
int
|
||||||
|
fls(uint32 value)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
return -1;
|
||||||
|
int index = 0;
|
||||||
|
#define HAND_OPTIMIZED_FLS 1
|
||||||
|
#if !HAND_OPTIMIZED_FLS
|
||||||
|
// This is the algorithm in its pure form.
|
||||||
|
const uint32 masks[] = {
|
||||||
|
0xffff0000,
|
||||||
|
0xff00ff00,
|
||||||
|
0xf0f0f0f0,
|
||||||
|
0xcccccccc,
|
||||||
|
0xaaaaaaaa,
|
||||||
|
};
|
||||||
|
int range = 16;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (value & masks[i]) {
|
||||||
|
index += range;
|
||||||
|
value &= masks[i];
|
||||||
|
}
|
||||||
|
range /= 2;
|
||||||
|
}
|
||||||
|
#else // HAND_OPTIMIZED_FLS
|
||||||
|
// This is how the compiler should optimize it for us: Unroll the loop and
|
||||||
|
// inline the masks.
|
||||||
|
// 0: 0xffff0000
|
||||||
|
if (value & 0xffff0000) {
|
||||||
|
index += 16;
|
||||||
|
value &= 0xffff0000;
|
||||||
|
}
|
||||||
|
// 1: 0xff00ff00
|
||||||
|
if (value & 0xff00ff00) {
|
||||||
|
index += 8;
|
||||||
|
value &= 0xff00ff00;
|
||||||
|
}
|
||||||
|
// 2: 0xf0f0f0f0
|
||||||
|
if (value & 0xf0f0f0f0) {
|
||||||
|
index += 4;
|
||||||
|
value &= 0xf0f0f0f0;
|
||||||
|
}
|
||||||
|
// 3: 0xcccccccc
|
||||||
|
if (value & 0xcccccccc) {
|
||||||
|
index += 2;
|
||||||
|
value &= 0xcccccccc;
|
||||||
|
}
|
||||||
|
// 4: 0xaaaaaaaa
|
||||||
|
if (value & 0xaaaaaaaa)
|
||||||
|
index++;
|
||||||
|
#endif // HAND_OPTIMIZED_FLS
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// node_child_hash
|
||||||
|
static inline
|
||||||
|
uint32
|
||||||
|
node_child_hash(uint64 id, const char *name)
|
||||||
|
{
|
||||||
|
return uint32(id & 0xffffffff) ^ string_hash(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MISC_H
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
// NameIndex.cpp
|
||||||
|
|
||||||
|
#include <TypeConstants.h>
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "IndexImpl.h"
|
||||||
|
#include "NameIndex.h"
|
||||||
|
#include "ramfs.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// NameIndexPrimaryKey
|
||||||
|
class NameIndexPrimaryKey {
|
||||||
|
public:
|
||||||
|
NameIndexPrimaryKey(const Entry *entry,
|
||||||
|
const char *name = NULL)
|
||||||
|
: entry(entry), name(name ? name : entry->GetName()) {}
|
||||||
|
NameIndexPrimaryKey(const char *name)
|
||||||
|
: entry(NULL), name(name) {}
|
||||||
|
|
||||||
|
const Entry *entry;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NameIndexGetPrimaryKey
|
||||||
|
class NameIndexGetPrimaryKey {
|
||||||
|
public:
|
||||||
|
inline NameIndexPrimaryKey operator()(const Entry *a)
|
||||||
|
{
|
||||||
|
return NameIndexPrimaryKey(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline NameIndexPrimaryKey operator()(const Entry *a) const
|
||||||
|
{
|
||||||
|
return NameIndexPrimaryKey(a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// NameIndexPrimaryKeyCompare
|
||||||
|
class NameIndexPrimaryKeyCompare
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline int operator()(const NameIndexPrimaryKey &a,
|
||||||
|
const NameIndexPrimaryKey &b) const
|
||||||
|
{
|
||||||
|
if (a.entry != NULL && a.entry == b.entry)
|
||||||
|
return 0;
|
||||||
|
return strcmp(a.name, b.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// EntryTree
|
||||||
|
|
||||||
|
typedef TwoKeyAVLTree<Entry*, NameIndexPrimaryKey, NameIndexPrimaryKeyCompare,
|
||||||
|
NameIndexGetPrimaryKey>
|
||||||
|
_EntryTree;
|
||||||
|
|
||||||
|
class NameIndex::EntryTree : public _EntryTree {};
|
||||||
|
|
||||||
|
|
||||||
|
// NameIndexEntryIterator
|
||||||
|
class NameIndexEntryIterator : public AbstractIndexEntryIterator,
|
||||||
|
public EntryListener {
|
||||||
|
public:
|
||||||
|
NameIndexEntryIterator();
|
||||||
|
virtual ~NameIndexEntryIterator();
|
||||||
|
|
||||||
|
virtual Entry *GetCurrent();
|
||||||
|
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
|
||||||
|
virtual Entry *GetPrevious();
|
||||||
|
virtual Entry *GetNext();
|
||||||
|
|
||||||
|
virtual status_t Suspend();
|
||||||
|
virtual status_t Resume();
|
||||||
|
|
||||||
|
bool SetTo(NameIndex *index, const char *name, bool ignoreValue = false);
|
||||||
|
|
||||||
|
virtual void EntryRemoved(Entry *entry);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class NameIndex;
|
||||||
|
|
||||||
|
typedef AbstractIndexEntryIterator BaseClass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NameIndex *fIndex;
|
||||||
|
NameIndex::EntryTree::Iterator fIterator;
|
||||||
|
bool fSuspended;
|
||||||
|
bool fIsNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// NameIndex
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
NameIndex::NameIndex(Volume *volume)
|
||||||
|
: Index(volume, "name", B_STRING_TYPE, false),
|
||||||
|
fEntries(new(nothrow) EntryTree)
|
||||||
|
{
|
||||||
|
if (fInitStatus == B_OK && !fEntries)
|
||||||
|
fInitStatus = B_NO_MEMORY;
|
||||||
|
if (fInitStatus == B_OK) {
|
||||||
|
fInitStatus = fVolume->AddEntryListener(this,
|
||||||
|
NULL, ENTRY_LISTEN_ANY_ENTRY | ENTRY_LISTEN_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
NameIndex::~NameIndex()
|
||||||
|
{
|
||||||
|
if (fVolume)
|
||||||
|
fVolume->RemoveEntryListener(this, NULL);
|
||||||
|
if (fEntries)
|
||||||
|
delete fEntries;
|
||||||
|
// Actually we would need to maintain a list of iterators and unset the
|
||||||
|
// still existing iterators here. But since the name index is deleted
|
||||||
|
// when the volume is unmounted, there shouldn't be any iterators left
|
||||||
|
// anymore.
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountEntries
|
||||||
|
int32
|
||||||
|
NameIndex::CountEntries() const
|
||||||
|
{
|
||||||
|
return fEntries->CountItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changed
|
||||||
|
status_t
|
||||||
|
NameIndex::Changed(Entry *entry, const char *oldName)
|
||||||
|
{
|
||||||
|
status_t error = B_BAD_VALUE;
|
||||||
|
if (entry && oldName) {
|
||||||
|
EntryTree::Iterator it;
|
||||||
|
Entry **foundEntry
|
||||||
|
= fEntries->Find(NameIndexPrimaryKey(entry, oldName), entry, &it);
|
||||||
|
if (foundEntry && *foundEntry == entry) {
|
||||||
|
fEntries->Remove(it);
|
||||||
|
error = fEntries->Insert(entry);
|
||||||
|
|
||||||
|
// udpate live queries
|
||||||
|
_UpdateLiveQueries(entry, oldName, entry->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryAdded
|
||||||
|
void
|
||||||
|
NameIndex::EntryAdded(Entry *entry)
|
||||||
|
{
|
||||||
|
if (entry) {
|
||||||
|
fEntries->Insert(entry);
|
||||||
|
|
||||||
|
// udpate live queries
|
||||||
|
_UpdateLiveQueries(entry, NULL, entry->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryRemoved
|
||||||
|
void
|
||||||
|
NameIndex::EntryRemoved(Entry *entry)
|
||||||
|
{
|
||||||
|
if (entry) {
|
||||||
|
fEntries->Remove(entry, entry);
|
||||||
|
|
||||||
|
// udpate live queries
|
||||||
|
_UpdateLiveQueries(entry, entry->GetName(), NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalGetIterator
|
||||||
|
AbstractIndexEntryIterator *
|
||||||
|
NameIndex::InternalGetIterator()
|
||||||
|
{
|
||||||
|
NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator;
|
||||||
|
if (iterator) {
|
||||||
|
if (!iterator->SetTo(this, NULL, true)) {
|
||||||
|
delete iterator;
|
||||||
|
iterator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalFind
|
||||||
|
AbstractIndexEntryIterator *
|
||||||
|
NameIndex::InternalFind(const uint8 *key, size_t length)
|
||||||
|
{
|
||||||
|
if (!key || length == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// if the key is not null-terminated, copy it
|
||||||
|
uint8 clonedKey[kMaxIndexKeyLength];
|
||||||
|
if (key[length - 1] != '\0') {
|
||||||
|
if (length >= kMaxIndexKeyLength)
|
||||||
|
length = kMaxIndexKeyLength - 1;
|
||||||
|
|
||||||
|
memcpy(clonedKey, key, length);
|
||||||
|
clonedKey[length] = '\0';
|
||||||
|
length++;
|
||||||
|
key = clonedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator;
|
||||||
|
if (iterator) {
|
||||||
|
if (!iterator->SetTo(this, (const char *)key)) {
|
||||||
|
delete iterator;
|
||||||
|
iterator = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _UpdateLiveQueries
|
||||||
|
void
|
||||||
|
NameIndex::_UpdateLiveQueries(Entry* entry, const char* oldName,
|
||||||
|
const char* newName)
|
||||||
|
{
|
||||||
|
fVolume->UpdateLiveQueries(entry, entry->GetNode(), GetName(),
|
||||||
|
GetType(), (const uint8*)oldName, (oldName ? strlen(oldName) : 0),
|
||||||
|
(const uint8*)newName, (newName ? strlen(newName) : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NameIndexEntryIterator
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
NameIndexEntryIterator::NameIndexEntryIterator()
|
||||||
|
: AbstractIndexEntryIterator(),
|
||||||
|
fIndex(NULL),
|
||||||
|
fIterator(),
|
||||||
|
fSuspended(false),
|
||||||
|
fIsNext(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
NameIndexEntryIterator::~NameIndexEntryIterator()
|
||||||
|
{
|
||||||
|
SetTo(NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
NameIndexEntryIterator::GetCurrent()
|
||||||
|
{
|
||||||
|
return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrent
|
||||||
|
Entry *
|
||||||
|
NameIndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength)
|
||||||
|
{
|
||||||
|
Entry *entry = GetCurrent();
|
||||||
|
if (entry) {
|
||||||
|
strncpy((char*)buffer, entry->GetName(), kMaxIndexKeyLength);
|
||||||
|
*keyLength = strlen(entry->GetName());
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrevious
|
||||||
|
Entry *
|
||||||
|
NameIndexEntryIterator::GetPrevious()
|
||||||
|
{
|
||||||
|
if (fSuspended)
|
||||||
|
return NULL;
|
||||||
|
if (!(fIterator.GetCurrent() && fIsNext))
|
||||||
|
fIterator.GetPrevious();
|
||||||
|
fIsNext = false;
|
||||||
|
return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNext
|
||||||
|
Entry *
|
||||||
|
NameIndexEntryIterator::GetNext()
|
||||||
|
{
|
||||||
|
if (fSuspended)
|
||||||
|
return NULL;
|
||||||
|
if (!(fIterator.GetCurrent() && fIsNext))
|
||||||
|
fIterator.GetNext();
|
||||||
|
fIsNext = false;
|
||||||
|
return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend
|
||||||
|
status_t
|
||||||
|
NameIndexEntryIterator::Suspend()
|
||||||
|
{
|
||||||
|
status_t error = (!fSuspended ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fIterator.GetCurrent()) {
|
||||||
|
error = fIndex->GetVolume()->AddEntryListener(this,
|
||||||
|
*fIterator.GetCurrent(), ENTRY_LISTEN_REMOVED);
|
||||||
|
}
|
||||||
|
if (error == B_OK)
|
||||||
|
fSuspended = true;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
status_t
|
||||||
|
NameIndexEntryIterator::Resume()
|
||||||
|
{
|
||||||
|
status_t error = (fSuspended ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fIterator.GetCurrent()) {
|
||||||
|
error = fIndex->GetVolume()->RemoveEntryListener(this,
|
||||||
|
*fIterator.GetCurrent());
|
||||||
|
}
|
||||||
|
if (error == B_OK)
|
||||||
|
fSuspended = false;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTo
|
||||||
|
bool
|
||||||
|
NameIndexEntryIterator::SetTo(NameIndex *index, const char *name,
|
||||||
|
bool ignoreValue)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
fIndex = index;
|
||||||
|
fSuspended = false;
|
||||||
|
fIsNext = false;
|
||||||
|
if (fIndex) {
|
||||||
|
if (ignoreValue) {
|
||||||
|
fIndex->fEntries->GetIterator(&fIterator);
|
||||||
|
return fIterator.GetCurrent();
|
||||||
|
}
|
||||||
|
return fIndex->fEntries->FindFirst(name, &fIterator);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryRemoved
|
||||||
|
void
|
||||||
|
NameIndexEntryIterator::EntryRemoved(Entry */*entry*/)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
fIsNext = GetNext();
|
||||||
|
Suspend();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// NameIndex.h
|
||||||
|
|
||||||
|
#ifndef NAME_INDEX_H
|
||||||
|
#define NAME_INDEX_H
|
||||||
|
|
||||||
|
#include "EntryListener.h"
|
||||||
|
#include "Index.h"
|
||||||
|
#include "TwoKeyAVLTree.h"
|
||||||
|
|
||||||
|
class NameIndexEntryIterator;
|
||||||
|
|
||||||
|
// NameIndex
|
||||||
|
class NameIndex : public Index, private EntryListener {
|
||||||
|
public:
|
||||||
|
NameIndex(Volume *volume);
|
||||||
|
virtual ~NameIndex();
|
||||||
|
|
||||||
|
virtual int32 CountEntries() const;
|
||||||
|
|
||||||
|
virtual status_t Changed(Entry *entry, const char *oldName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void EntryAdded(Entry *entry);
|
||||||
|
virtual void EntryRemoved(Entry *entry);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual AbstractIndexEntryIterator *InternalGetIterator();
|
||||||
|
virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class EntryTree;
|
||||||
|
friend class NameIndexEntryIterator;
|
||||||
|
|
||||||
|
void _UpdateLiveQueries(Entry* entry, const char* oldName,
|
||||||
|
const char* newName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EntryTree *fEntries;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NAME_INDEX_H
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
// Node.cpp
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "EntryIterator.h"
|
||||||
|
#include "LastModifiedIndex.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
// is_user_in_group
|
||||||
|
inline static
|
||||||
|
bool
|
||||||
|
is_user_in_group(gid_t gid)
|
||||||
|
{
|
||||||
|
// Either I miss something, or we don't have getgroups() in the kernel. :-(
|
||||||
|
/*
|
||||||
|
gid_t groups[NGROUPS_MAX];
|
||||||
|
int groupCount = getgroups(NGROUPS_MAX, groups);
|
||||||
|
for (int i = 0; i < groupCount; i++) {
|
||||||
|
if (gid == groups[i])
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return (gid == getegid());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Node::Node(Volume *volume, uint8 type)
|
||||||
|
: fVolume(volume),
|
||||||
|
fID(fVolume->NextNodeID()),
|
||||||
|
fRefCount(0),
|
||||||
|
fMode(0),
|
||||||
|
fUID(0),
|
||||||
|
fGID(0),
|
||||||
|
fATime(0),
|
||||||
|
fMTime(0),
|
||||||
|
fCTime(0),
|
||||||
|
fCrTime(0),
|
||||||
|
fModified(false),
|
||||||
|
fIsKnownToVFS(false),
|
||||||
|
// attribute management
|
||||||
|
fAttributes(),
|
||||||
|
// referrers
|
||||||
|
fReferrers()
|
||||||
|
{
|
||||||
|
// set file type
|
||||||
|
switch (type) {
|
||||||
|
case NODE_TYPE_DIRECTORY:
|
||||||
|
fMode = S_IFDIR;
|
||||||
|
break;
|
||||||
|
case NODE_TYPE_FILE:
|
||||||
|
fMode = S_IFREG;
|
||||||
|
break;
|
||||||
|
case NODE_TYPE_SYMLINK:
|
||||||
|
fMode = S_IFLNK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// set defaults for time
|
||||||
|
fATime = fMTime = fCTime = fCrTime = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
Node::~Node()
|
||||||
|
{
|
||||||
|
// delete all attributes
|
||||||
|
while (Attribute *attribute = fAttributes.GetFirst()) {
|
||||||
|
status_t error = DeleteAttribute(attribute);
|
||||||
|
if (error != B_OK) {
|
||||||
|
FATAL(("Node::~Node(): Failed to delete attribute!\n"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
Node::InitCheck() const
|
||||||
|
{
|
||||||
|
return (fVolume && fID >= 0 ? B_OK : B_NO_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReference
|
||||||
|
status_t
|
||||||
|
Node::AddReference()
|
||||||
|
{
|
||||||
|
if (++fRefCount == 1) {
|
||||||
|
status_t error = GetVolume()->NewVNode(this);
|
||||||
|
if (error != B_OK) {
|
||||||
|
fRefCount--;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
fIsKnownToVFS = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveReference
|
||||||
|
void
|
||||||
|
Node::RemoveReference()
|
||||||
|
{
|
||||||
|
if (--fRefCount == 0) {
|
||||||
|
GetVolume()->RemoveVNode(this);
|
||||||
|
fRefCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link
|
||||||
|
status_t
|
||||||
|
Node::Link(Entry *entry)
|
||||||
|
{
|
||||||
|
PRINT(("Node[%Ld]::Link(): %ld ->...\n", fID, fRefCount));
|
||||||
|
fReferrers.Insert(entry);
|
||||||
|
|
||||||
|
status_t error = AddReference();
|
||||||
|
if (error != B_OK)
|
||||||
|
fReferrers.Remove(entry);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink
|
||||||
|
status_t
|
||||||
|
Node::Unlink(Entry *entry)
|
||||||
|
{
|
||||||
|
PRINT(("Node[%Ld]::Unlink(): %ld ->...\n", fID, fRefCount));
|
||||||
|
RemoveReference();
|
||||||
|
fReferrers.Remove(entry);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMTime
|
||||||
|
void
|
||||||
|
Node::SetMTime(time_t mTime)
|
||||||
|
{
|
||||||
|
time_t oldMTime = fMTime;
|
||||||
|
fATime = fMTime = mTime;
|
||||||
|
if (oldMTime != fMTime) {
|
||||||
|
if (LastModifiedIndex *index = fVolume->GetLastModifiedIndex())
|
||||||
|
index->Changed(this, oldMTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPermissions
|
||||||
|
status_t
|
||||||
|
Node::CheckPermissions(int mode) const
|
||||||
|
{
|
||||||
|
int userPermissions = (fMode & S_IRWXU) >> 6;
|
||||||
|
int groupPermissions = (fMode & S_IRWXG) >> 3;
|
||||||
|
int otherPermissions = fMode & S_IRWXO;
|
||||||
|
// get the permissions for this uid/gid
|
||||||
|
int permissions = 0;
|
||||||
|
uid_t uid = geteuid();
|
||||||
|
// user is root
|
||||||
|
if (uid == 0) {
|
||||||
|
// root has always read/write permission, but at least one of the
|
||||||
|
// X bits must be set for execute permission
|
||||||
|
permissions = userPermissions | groupPermissions | otherPermissions
|
||||||
|
| ACCESS_R | ACCESS_W;
|
||||||
|
// user is node owner
|
||||||
|
} else if (uid == fUID)
|
||||||
|
permissions = userPermissions;
|
||||||
|
// user is in owning group
|
||||||
|
else if (is_user_in_group(fGID))
|
||||||
|
permissions = groupPermissions;
|
||||||
|
// user is one of the others
|
||||||
|
else
|
||||||
|
permissions = otherPermissions;
|
||||||
|
// do the check
|
||||||
|
return ((mode & ~permissions) ? B_NOT_ALLOWED : B_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAttribute
|
||||||
|
status_t
|
||||||
|
Node::CreateAttribute(const char *name, Attribute **_attribute)
|
||||||
|
{
|
||||||
|
status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// create attribute
|
||||||
|
Attribute *attribute = new(nothrow) Attribute(fVolume, NULL, name);
|
||||||
|
if (attribute) {
|
||||||
|
error = attribute->InitCheck();
|
||||||
|
if (error == B_OK) {
|
||||||
|
// add attribute to node
|
||||||
|
error = AddAttribute(attribute);
|
||||||
|
if (error == B_OK)
|
||||||
|
*_attribute = attribute;
|
||||||
|
}
|
||||||
|
if (error != B_OK)
|
||||||
|
delete attribute;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAttribute
|
||||||
|
status_t
|
||||||
|
Node::DeleteAttribute(Attribute *attribute)
|
||||||
|
{
|
||||||
|
status_t error = RemoveAttribute(attribute);
|
||||||
|
if (error == B_OK)
|
||||||
|
delete attribute;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAttribute
|
||||||
|
status_t
|
||||||
|
Node::AddAttribute(Attribute *attribute)
|
||||||
|
{
|
||||||
|
status_t error = (attribute && !attribute->GetNode() ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
error = GetVolume()->NodeAttributeAdded(GetID(), attribute);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fAttributes.Insert(attribute);
|
||||||
|
attribute->SetNode(this);
|
||||||
|
MarkModified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAttribute
|
||||||
|
status_t
|
||||||
|
Node::RemoveAttribute(Attribute *attribute)
|
||||||
|
{
|
||||||
|
status_t error = (attribute && attribute->GetNode() == this
|
||||||
|
? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
// move all iterators pointing to the attribute to the next attribute
|
||||||
|
if (GetVolume()->IteratorLock()) {
|
||||||
|
// set the iterators' current entry
|
||||||
|
Attribute *nextAttr = fAttributes.GetNext(attribute);
|
||||||
|
DLList<AttributeIterator> *iterators
|
||||||
|
= attribute->GetAttributeIteratorList();
|
||||||
|
for (AttributeIterator *iterator = iterators->GetFirst();
|
||||||
|
iterator;
|
||||||
|
iterator = iterators->GetNext(iterator)) {
|
||||||
|
iterator->SetCurrent(nextAttr, true);
|
||||||
|
}
|
||||||
|
// Move the iterators from one list to the other, or just remove
|
||||||
|
// them, if there is no next attribute.
|
||||||
|
if (nextAttr) {
|
||||||
|
DLList<AttributeIterator> *nextIterators
|
||||||
|
= nextAttr->GetAttributeIteratorList();
|
||||||
|
nextIterators->MoveFrom(iterators);
|
||||||
|
} else
|
||||||
|
iterators->RemoveAll();
|
||||||
|
GetVolume()->IteratorUnlock();
|
||||||
|
} else
|
||||||
|
error = B_ERROR;
|
||||||
|
// remove the attribute
|
||||||
|
if (error == B_OK) {
|
||||||
|
error = GetVolume()->NodeAttributeRemoved(GetID(), attribute);
|
||||||
|
if (error == B_OK) {
|
||||||
|
fAttributes.Remove(attribute);
|
||||||
|
attribute->SetNode(NULL);
|
||||||
|
MarkModified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindAttribute
|
||||||
|
status_t
|
||||||
|
Node::FindAttribute(const char *name, Attribute **_attribute) const
|
||||||
|
{
|
||||||
|
status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
/*
|
||||||
|
Attribute *attribute = NULL;
|
||||||
|
while (GetNextAttribute(&attribute) == B_OK) {
|
||||||
|
if (!strcmp(attribute->GetName(), name)) {
|
||||||
|
*_attribute = attribute;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = B_ENTRY_NOT_FOUND;
|
||||||
|
*/
|
||||||
|
error = GetVolume()->FindNodeAttribute(GetID(), name, _attribute);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreviousAttribute
|
||||||
|
status_t
|
||||||
|
Node::GetPreviousAttribute(Attribute **attribute) const
|
||||||
|
{
|
||||||
|
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (!*attribute)
|
||||||
|
*attribute = fAttributes.GetLast();
|
||||||
|
else if ((*attribute)->GetNode() == this)
|
||||||
|
*attribute = fAttributes.GetPrevious(*attribute);
|
||||||
|
else
|
||||||
|
error = B_BAD_VALUE;
|
||||||
|
if (error == B_OK && !*attribute)
|
||||||
|
error = B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextAttribute
|
||||||
|
status_t
|
||||||
|
Node::GetNextAttribute(Attribute **attribute) const
|
||||||
|
{
|
||||||
|
status_t error = (attribute ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (!*attribute)
|
||||||
|
*attribute = fAttributes.GetFirst();
|
||||||
|
else if ((*attribute)->GetNode() == this)
|
||||||
|
*attribute = fAttributes.GetNext(*attribute);
|
||||||
|
else
|
||||||
|
error = B_BAD_VALUE;
|
||||||
|
if (error == B_OK && !*attribute)
|
||||||
|
error = B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFirstReferrer
|
||||||
|
Entry *
|
||||||
|
Node::GetFirstReferrer() const
|
||||||
|
{
|
||||||
|
return fReferrers.GetHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastReferrer
|
||||||
|
Entry *
|
||||||
|
Node::GetLastReferrer() const
|
||||||
|
{
|
||||||
|
return fReferrers.GetTail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreviousReferrer
|
||||||
|
Entry *
|
||||||
|
Node::GetPreviousReferrer(Entry *entry) const
|
||||||
|
{
|
||||||
|
return (entry ? fReferrers.GetPrevious(entry) : NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextReferrer
|
||||||
|
Entry *
|
||||||
|
Node::GetNextReferrer(Entry *entry) const
|
||||||
|
{
|
||||||
|
return (entry ? fReferrers.GetNext(entry) : NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
Node::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
Attribute *attribute = NULL;
|
||||||
|
while (GetNextAttribute(&attribute) == B_OK)
|
||||||
|
attribute->GetAllocationInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
// Node.h
|
||||||
|
|
||||||
|
#ifndef NODE_H
|
||||||
|
#define NODE_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
#include "Attribute.h"
|
||||||
|
#include "Entry.h"
|
||||||
|
#include "fsproto.h"
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
class AllocationInfo;
|
||||||
|
class AttributeIterator;
|
||||||
|
class Directory;
|
||||||
|
class Volume;
|
||||||
|
|
||||||
|
// node type
|
||||||
|
enum {
|
||||||
|
NODE_TYPE_DIRECTORY,
|
||||||
|
NODE_TYPE_FILE,
|
||||||
|
NODE_TYPE_SYMLINK,
|
||||||
|
};
|
||||||
|
|
||||||
|
// access modes
|
||||||
|
enum {
|
||||||
|
ACCESS_R = S_IROTH,
|
||||||
|
ACCESS_W = S_IWOTH,
|
||||||
|
ACCESS_X = S_IXOTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Node : public DLListLinkImpl<Node> {
|
||||||
|
public:
|
||||||
|
Node(Volume *volume, uint8 type);
|
||||||
|
virtual ~Node();
|
||||||
|
|
||||||
|
virtual status_t InitCheck() const;
|
||||||
|
|
||||||
|
inline void SetVolume(Volume *volume) { fVolume = volume; }
|
||||||
|
inline Volume *GetVolume() const { return fVolume; }
|
||||||
|
|
||||||
|
inline vnode_id GetID() const { return fID; }
|
||||||
|
|
||||||
|
status_t AddReference();
|
||||||
|
void RemoveReference();
|
||||||
|
int32 GetRefCount() { return fRefCount; }
|
||||||
|
|
||||||
|
virtual status_t Link(Entry *entry);
|
||||||
|
virtual status_t Unlink(Entry *entry);
|
||||||
|
|
||||||
|
inline bool IsDirectory() const { return S_ISDIR(fMode); }
|
||||||
|
inline bool IsFile() const { return S_ISREG(fMode); }
|
||||||
|
inline bool IsSymLink() const { return S_ISLNK(fMode); }
|
||||||
|
|
||||||
|
virtual status_t SetSize(off_t newSize) = 0;
|
||||||
|
virtual off_t GetSize() const = 0;
|
||||||
|
|
||||||
|
// stat data
|
||||||
|
|
||||||
|
inline void SetMode(mode_t mode)
|
||||||
|
{ fMode = fMode & ~S_IUMSK | mode & S_IUMSK; }
|
||||||
|
inline mode_t GetMode() const { return fMode; }
|
||||||
|
|
||||||
|
inline void SetUID(uid_t uid) { fUID = uid; }
|
||||||
|
inline uid_t GetUID() const { return fUID; }
|
||||||
|
|
||||||
|
inline void SetGID(uid_t gid) { fGID = gid; }
|
||||||
|
inline uid_t GetGID() const { return fGID; }
|
||||||
|
|
||||||
|
inline void SetATime(time_t aTime) { fATime = aTime; }
|
||||||
|
inline time_t GetATime() const { return fATime; }
|
||||||
|
|
||||||
|
void SetMTime(time_t mTime);
|
||||||
|
inline time_t GetMTime() const { return fMTime; }
|
||||||
|
|
||||||
|
inline void SetCTime(time_t cTime) { fCTime = cTime; }
|
||||||
|
inline time_t GetCTime() const { return fCTime; }
|
||||||
|
|
||||||
|
inline void SetCrTime(time_t crTime) { fCrTime = crTime; }
|
||||||
|
inline time_t GetCrTime() const { return fCrTime; }
|
||||||
|
|
||||||
|
inline void MarkModified() { fModified = true; }
|
||||||
|
inline void MarkUnmodified();
|
||||||
|
inline void SetModified(bool modified) { fModified = modified; }
|
||||||
|
inline bool IsModified() const { return fModified; }
|
||||||
|
|
||||||
|
status_t CheckPermissions(int mode) const;
|
||||||
|
|
||||||
|
bool IsKnownToVFS() const { return fIsKnownToVFS; }
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
status_t CreateAttribute(const char *name, Attribute **attribute);
|
||||||
|
status_t DeleteAttribute(Attribute *attribute);
|
||||||
|
status_t AddAttribute(Attribute *attribute);
|
||||||
|
status_t RemoveAttribute(Attribute *attribute);
|
||||||
|
|
||||||
|
status_t FindAttribute(const char *name, Attribute **attribute) const;
|
||||||
|
|
||||||
|
status_t GetPreviousAttribute(Attribute **attribute) const;
|
||||||
|
status_t GetNextAttribute(Attribute **attribute) const;
|
||||||
|
|
||||||
|
Entry *GetFirstReferrer() const;
|
||||||
|
Entry *GetLastReferrer() const;
|
||||||
|
Entry *GetPreviousReferrer(Entry *entry) const;
|
||||||
|
Entry *GetNextReferrer(Entry *entry) const;
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
virtual void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Volume *fVolume;
|
||||||
|
vnode_id fID;
|
||||||
|
int32 fRefCount;
|
||||||
|
mode_t fMode;
|
||||||
|
uid_t fUID;
|
||||||
|
uid_t fGID;
|
||||||
|
time_t fATime;
|
||||||
|
time_t fMTime;
|
||||||
|
time_t fCTime;
|
||||||
|
time_t fCrTime;
|
||||||
|
bool fModified;
|
||||||
|
bool fIsKnownToVFS;
|
||||||
|
|
||||||
|
// attribute management
|
||||||
|
DLList<Attribute> fAttributes;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// entries referring to this node
|
||||||
|
DLList<Entry, GetNodeReferrerLink> fReferrers;
|
||||||
|
};
|
||||||
|
|
||||||
|
// MarkUnmodified
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
Node::MarkUnmodified()
|
||||||
|
{
|
||||||
|
if (fModified) {
|
||||||
|
fCTime = time(NULL);
|
||||||
|
SetMTime(fCTime);
|
||||||
|
fModified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// open_mode_to_access
|
||||||
|
inline static
|
||||||
|
int
|
||||||
|
open_mode_to_access(int openMode)
|
||||||
|
{
|
||||||
|
switch (openMode & O_RWMASK) {
|
||||||
|
case O_RDONLY:
|
||||||
|
return ACCESS_R;
|
||||||
|
case O_WRONLY:
|
||||||
|
return ACCESS_W;
|
||||||
|
case O_RDWR:
|
||||||
|
return ACCESS_R | ACCESS_W;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NodeMTimeUpdater
|
||||||
|
class NodeMTimeUpdater {
|
||||||
|
public:
|
||||||
|
NodeMTimeUpdater(Node *node) : fNode(node) {}
|
||||||
|
~NodeMTimeUpdater()
|
||||||
|
{
|
||||||
|
if (fNode && fNode->IsModified())
|
||||||
|
fNode->MarkUnmodified();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *fNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NODE_H
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
// NodeChildTable.h
|
||||||
|
|
||||||
|
#ifndef NODE_CHILD_TABLE_H
|
||||||
|
#define NODE_CHILD_TABLE_H
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Misc.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "OpenHashTable.h"
|
||||||
|
|
||||||
|
// NodeChildHashElement
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
class NodeChildHashElement : public OpenHashElement {
|
||||||
|
private:
|
||||||
|
typedef NodeChildHashElement<ParentNode, NodeChild> Element;
|
||||||
|
public:
|
||||||
|
|
||||||
|
NodeChildHashElement() : OpenHashElement(), fID(-1), fChild(NULL)
|
||||||
|
{
|
||||||
|
fNext = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32 HashFor(vnode_id id, const char *name)
|
||||||
|
{
|
||||||
|
return node_child_hash(id, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32 HashFor(ParentNode *parent, NodeChild *child)
|
||||||
|
{
|
||||||
|
return node_child_hash(parent->GetID(), child->GetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32 Hash() const
|
||||||
|
{
|
||||||
|
return HashFor(fID, fChild->GetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Equals(vnode_id id, const char *name)
|
||||||
|
{
|
||||||
|
return (fID == id && !strcmp(fChild->GetName(), name));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const OpenHashElement &_element) const
|
||||||
|
{
|
||||||
|
const Element &element = static_cast<const Element&>(_element);
|
||||||
|
return Equals(element.fID, element.fChild->GetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Adopt(Element &element)
|
||||||
|
{
|
||||||
|
fID = element.fID;
|
||||||
|
fChild = element.fChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
vnode_id fID;
|
||||||
|
NodeChild *fChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NodeChildTable
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
class NodeChildTable {
|
||||||
|
public:
|
||||||
|
NodeChildTable();
|
||||||
|
~NodeChildTable();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
status_t AddNodeChild(ParentNode *node, NodeChild *child);
|
||||||
|
status_t AddNodeChild(vnode_id, NodeChild *child);
|
||||||
|
status_t RemoveNodeChild(ParentNode *node, NodeChild *child);
|
||||||
|
status_t RemoveNodeChild(vnode_id id, NodeChild *child);
|
||||||
|
status_t RemoveNodeChild(vnode_id id, const char *name);
|
||||||
|
NodeChild *GetNodeChild(vnode_id id, const char *name);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef NodeChildHashElement<ParentNode, NodeChild> Element;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element *_FindElement(vnode_id id, const char *name) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
OpenHashElementArray<Element> fElementArray;
|
||||||
|
OpenHashTable<Element, OpenHashElementArray<Element> > fTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
// define convenient instantiation types
|
||||||
|
|
||||||
|
// DirectoryEntryTable
|
||||||
|
class DirectoryEntryTable : public NodeChildTable<Directory, Entry> {
|
||||||
|
public:
|
||||||
|
DirectoryEntryTable() {}
|
||||||
|
~DirectoryEntryTable() {}
|
||||||
|
|
||||||
|
void GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
info.AddDirectoryEntryTableAllocation(fTable.ArraySize(),
|
||||||
|
fTable.VectorSize(),
|
||||||
|
sizeof(Element),
|
||||||
|
fTable.CountElements());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NodeAttributeTable
|
||||||
|
class NodeAttributeTable : public NodeChildTable<Node, Attribute> {
|
||||||
|
public:
|
||||||
|
NodeAttributeTable() {}
|
||||||
|
~NodeAttributeTable() {}
|
||||||
|
|
||||||
|
void GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
info.AddNodeAttributeTableAllocation(fTable.ArraySize(),
|
||||||
|
fTable.VectorSize(),
|
||||||
|
sizeof(Element),
|
||||||
|
fTable.CountElements());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// NodeChildTable implementation
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::NodeChildTable()
|
||||||
|
: fElementArray(1000),
|
||||||
|
fTable(1000, &fElementArray)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::~NodeChildTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
status_t
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::InitCheck() const
|
||||||
|
{
|
||||||
|
RETURN_ERROR(fTable.InitCheck() && fElementArray.InitCheck()
|
||||||
|
? B_OK : B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodeChild
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
status_t
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::AddNodeChild(ParentNode *node,
|
||||||
|
NodeChild *child)
|
||||||
|
{
|
||||||
|
status_t error = (node && child ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK)
|
||||||
|
error = AddNodeChild(node->GetID(), child);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodeChild
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
status_t
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::AddNodeChild(vnode_id id,
|
||||||
|
NodeChild *child)
|
||||||
|
{
|
||||||
|
status_t error = (child ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
Element *element = fTable.Add(Element::HashFor(id, child->GetName()));
|
||||||
|
if (element) {
|
||||||
|
element->fID = id;
|
||||||
|
element->fChild = child;
|
||||||
|
} else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNodeChild
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
status_t
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::RemoveNodeChild(ParentNode *node,
|
||||||
|
NodeChild *child)
|
||||||
|
{
|
||||||
|
status_t error = (node && child ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK)
|
||||||
|
error = RemoveNodeChild(node->GetID(), child->GetName());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNodeChild
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
status_t
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::RemoveNodeChild(vnode_id id,
|
||||||
|
NodeChild *child)
|
||||||
|
{
|
||||||
|
status_t error = (child ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK)
|
||||||
|
error = RemoveNodeChild(id, child->GetName());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNodeChild
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
status_t
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::RemoveNodeChild(vnode_id id,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (Element *element = _FindElement(id, name))
|
||||||
|
fTable.Remove(element);
|
||||||
|
else
|
||||||
|
error = B_ERROR;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeChild
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
NodeChild *
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::GetNodeChild(vnode_id id,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
NodeChild *child = NULL;
|
||||||
|
if (Element *element = _FindElement(id, name))
|
||||||
|
child = element->fChild;
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FindElement
|
||||||
|
template<typename ParentNode, typename NodeChild>
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::Element *
|
||||||
|
NodeChildTable<ParentNode, NodeChild>::_FindElement(vnode_id id,
|
||||||
|
const char *name) const
|
||||||
|
{
|
||||||
|
Element *element = fTable.FindFirst(Element::HashFor(id, name));
|
||||||
|
while (element && !element->Equals(id, name)) {
|
||||||
|
if (element->fNext >= 0)
|
||||||
|
element = fTable.ElementAt(element->fNext);
|
||||||
|
else
|
||||||
|
element = NULL;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// undefine the PRINT from <Debug.h>
|
||||||
|
//#undef PRINT
|
||||||
|
|
||||||
|
#endif // NODE_CHILD_TABLE_H
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// NodeListener.cpp
|
||||||
|
|
||||||
|
#include "NodeListener.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
NodeListener::NodeListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
NodeListener::~NodeListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeAdded
|
||||||
|
void
|
||||||
|
NodeListener::NodeAdded(Node */*node*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoved
|
||||||
|
void
|
||||||
|
NodeListener::NodeRemoved(Node */*node*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// NodeListener.h
|
||||||
|
|
||||||
|
#ifndef NODE_LISTENER_H
|
||||||
|
#define NODE_LISTENER_H
|
||||||
|
|
||||||
|
class Node;
|
||||||
|
|
||||||
|
// listening flags
|
||||||
|
enum {
|
||||||
|
NODE_LISTEN_ANY_NODE = 0x01,
|
||||||
|
NODE_LISTEN_ADDED = 0x02,
|
||||||
|
NODE_LISTEN_REMOVED = 0x04,
|
||||||
|
NODE_LISTEN_ALL = NODE_LISTEN_ADDED | NODE_LISTEN_REMOVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
class NodeListener {
|
||||||
|
public:
|
||||||
|
NodeListener();
|
||||||
|
virtual ~NodeListener();
|
||||||
|
|
||||||
|
virtual void NodeAdded(Node *node);
|
||||||
|
virtual void NodeRemoved(Node *node);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NODE_LISTENER_H
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// NodeTable.cpp
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "NodeTable.h"
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
NodeTable::NodeTable()
|
||||||
|
: fElementArray(1000),
|
||||||
|
fNodes(1000, &fElementArray)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
NodeTable::~NodeTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
NodeTable::InitCheck() const
|
||||||
|
{
|
||||||
|
RETURN_ERROR(fNodes.InitCheck() && fElementArray.InitCheck()
|
||||||
|
? B_OK : B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNode
|
||||||
|
status_t
|
||||||
|
NodeTable::AddNode(Node *node)
|
||||||
|
{
|
||||||
|
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK) {
|
||||||
|
NodeHashElement *element
|
||||||
|
= fNodes.Add(NodeHashElement::HashForID(node));
|
||||||
|
if (element)
|
||||||
|
element->fNode = node;
|
||||||
|
else
|
||||||
|
SET_ERROR(error, B_NO_MEMORY);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNode
|
||||||
|
status_t
|
||||||
|
NodeTable::RemoveNode(Node *node)
|
||||||
|
{
|
||||||
|
status_t error = (node ? B_OK : B_BAD_VALUE);
|
||||||
|
if (error == B_OK)
|
||||||
|
error = RemoveNode(node->GetID());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNode
|
||||||
|
status_t
|
||||||
|
NodeTable::RemoveNode(vnode_id id)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (NodeHashElement *element = _FindElement(id))
|
||||||
|
fNodes.Remove(element);
|
||||||
|
else
|
||||||
|
error = B_ERROR;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNode
|
||||||
|
Node *
|
||||||
|
NodeTable::GetNode(vnode_id id)
|
||||||
|
{
|
||||||
|
Node *node = NULL;
|
||||||
|
if (NodeHashElement *element = _FindElement(id))
|
||||||
|
node = element->fNode;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocationInfo
|
||||||
|
void
|
||||||
|
NodeTable::GetAllocationInfo(AllocationInfo &info)
|
||||||
|
{
|
||||||
|
info.AddNodeTableAllocation(fNodes.ArraySize(), fNodes.VectorSize(),
|
||||||
|
sizeof(NodeHashElement),
|
||||||
|
fNodes.CountElements());
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FindElement
|
||||||
|
NodeHashElement *
|
||||||
|
NodeTable::_FindElement(vnode_id id) const
|
||||||
|
{
|
||||||
|
NodeHashElement *element
|
||||||
|
= fNodes.FindFirst(NodeHashElement::HashForID(id));
|
||||||
|
while (element && element->fNode->GetID() != id) {
|
||||||
|
if (element->fNext >= 0)
|
||||||
|
element = fNodes.ElementAt(element->fNext);
|
||||||
|
else
|
||||||
|
element = NULL;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// NodeTable.h
|
||||||
|
|
||||||
|
#ifndef NODE_TABLE_H
|
||||||
|
#define NODE_TABLE_H
|
||||||
|
|
||||||
|
#include "AllocationInfo.h"
|
||||||
|
#include "Node.h"
|
||||||
|
#include "OpenHashTable.h"
|
||||||
|
|
||||||
|
// NodeHashElement
|
||||||
|
class NodeHashElement : public OpenHashElement {
|
||||||
|
public:
|
||||||
|
NodeHashElement() : OpenHashElement(), fNode(NULL)
|
||||||
|
{
|
||||||
|
fNext = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32 HashForID(vnode_id id)
|
||||||
|
{
|
||||||
|
return uint32(id & 0xffffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32 HashForID(Node *node)
|
||||||
|
{
|
||||||
|
return HashForID(node->GetID());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32 Hash() const
|
||||||
|
{
|
||||||
|
return HashForID(fNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const OpenHashElement &element) const
|
||||||
|
{
|
||||||
|
return (static_cast<const NodeHashElement&>(element).fNode == fNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Adopt(NodeHashElement &element)
|
||||||
|
{
|
||||||
|
fNode = element.fNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *fNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NodeTable
|
||||||
|
class NodeTable {
|
||||||
|
public:
|
||||||
|
NodeTable();
|
||||||
|
~NodeTable();
|
||||||
|
|
||||||
|
status_t InitCheck() const;
|
||||||
|
|
||||||
|
status_t AddNode(Node *node);
|
||||||
|
status_t RemoveNode(Node *node);
|
||||||
|
status_t RemoveNode(vnode_id id);
|
||||||
|
Node *GetNode(vnode_id id);
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
void GetAllocationInfo(AllocationInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeHashElement *_FindElement(vnode_id id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenHashElementArray<NodeHashElement> fElementArray;
|
||||||
|
OpenHashTable<NodeHashElement, OpenHashElementArray<NodeHashElement> >
|
||||||
|
fNodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
// undefine the PRINT from <Debug.h>
|
||||||
|
//#undef PRINT
|
||||||
|
|
||||||
|
#endif // NODE_TABLE_H
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
/*
|
||||||
|
Open Tracker License
|
||||||
|
|
||||||
|
Terms and Conditions
|
||||||
|
|
||||||
|
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice applies to all licensees
|
||||||
|
and shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of Be Incorporated shall not be
|
||||||
|
used in advertising or otherwise to promote the sale, use or other dealings in
|
||||||
|
this Software without prior written authorization from Be Incorporated.
|
||||||
|
|
||||||
|
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
|
||||||
|
of Be Incorporated in the United States and other countries. Other brand product
|
||||||
|
names are registered trademarks or trademarks of their respective holders.
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// bonefish:
|
||||||
|
// * removed need for exceptions
|
||||||
|
// * fixed warnings
|
||||||
|
// * implemented rehashing
|
||||||
|
// * hash array and element vector use areas for allocations
|
||||||
|
// TODO:
|
||||||
|
// * shrinking of element vectors
|
||||||
|
|
||||||
|
// Hash table with open addresssing
|
||||||
|
|
||||||
|
#ifndef __OPEN_HASH_TABLE__
|
||||||
|
#define __OPEN_HASH_TABLE__
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <new.h>
|
||||||
|
|
||||||
|
#include "AreaUtils.h"
|
||||||
|
#include "Misc.h"
|
||||||
|
|
||||||
|
// don't include <Debug.h>
|
||||||
|
#define ASSERT(E) (void)0
|
||||||
|
#define TRESPASS() (void)0
|
||||||
|
|
||||||
|
//namespace BPrivate {
|
||||||
|
|
||||||
|
template <class Element>
|
||||||
|
class ElementVector {
|
||||||
|
// element vector for OpenHashTable needs to implement this
|
||||||
|
// interface
|
||||||
|
public:
|
||||||
|
Element &At(int32 index);
|
||||||
|
Element *Add();
|
||||||
|
int32 IndexOf(const Element &) const;
|
||||||
|
void Remove(int32 index);
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenHashElement {
|
||||||
|
public:
|
||||||
|
uint32 Hash() const;
|
||||||
|
bool operator==(const OpenHashElement &) const;
|
||||||
|
void Adopt(OpenHashElement &);
|
||||||
|
// low overhead copy, original element is in undefined state
|
||||||
|
// after call (calls Adopt on BString members, etc.)
|
||||||
|
int32 fNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32 kPrimes [] = {
|
||||||
|
509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
|
||||||
|
524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
|
||||||
|
134217689, 268435399, 536870909, 1073741789, 2147483647, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Element, class ElementVec = ElementVector<Element> >
|
||||||
|
class OpenHashTable {
|
||||||
|
public:
|
||||||
|
OpenHashTable(int32 minSize, ElementVec *elementVector = 0,
|
||||||
|
float maxLoadFactor = 0.8);
|
||||||
|
// it is up to the subclass of OpenHashTable to supply
|
||||||
|
// elementVector
|
||||||
|
~OpenHashTable();
|
||||||
|
|
||||||
|
bool InitCheck() const;
|
||||||
|
|
||||||
|
void SetElementVector(ElementVec *elementVector);
|
||||||
|
|
||||||
|
Element *FindFirst(uint32 elementHash) const;
|
||||||
|
Element *Add(uint32 elementHash);
|
||||||
|
|
||||||
|
void Remove(Element *);
|
||||||
|
|
||||||
|
// when calling Add, any outstanding element pointer may become
|
||||||
|
// invalid; to deal with this, get the element index and restore
|
||||||
|
// it after the add
|
||||||
|
int32 ElementIndex(const Element *) const;
|
||||||
|
Element *ElementAt(int32 index) const;
|
||||||
|
|
||||||
|
int32 ArraySize() const;
|
||||||
|
int32 VectorSize() const;
|
||||||
|
int32 CountElements() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static int32 OptimalSize(int32 minSize);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _RehashIfNeeded();
|
||||||
|
bool _Rehash();
|
||||||
|
|
||||||
|
int32 fArraySize;
|
||||||
|
int32 fInitialSize;
|
||||||
|
int32 fElementCount;
|
||||||
|
int32 *fHashArray;
|
||||||
|
ElementVec *fElementVector;
|
||||||
|
float fMaxLoadFactor;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Element>
|
||||||
|
class OpenHashElementArray : public ElementVector<Element> {
|
||||||
|
// this is a straightforward implementation of an element vector
|
||||||
|
// deleting is handled by linking deleted elements into a free list
|
||||||
|
// the vector never shrinks
|
||||||
|
public:
|
||||||
|
OpenHashElementArray(int32 initialSize);
|
||||||
|
~OpenHashElementArray();
|
||||||
|
|
||||||
|
bool InitCheck() const;
|
||||||
|
|
||||||
|
Element &At(int32 index);
|
||||||
|
const Element &At(int32 index) const;
|
||||||
|
Element *Add(const Element &);
|
||||||
|
Element *Add();
|
||||||
|
void Remove(int32 index);
|
||||||
|
int32 IndexOf(const Element &) const;
|
||||||
|
int32 Size() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element *fData;
|
||||||
|
int32 fSize;
|
||||||
|
int32 fNextFree;
|
||||||
|
int32 fNextDeleted;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
OpenHashTable<Element, ElementVec>::OpenHashTable(int32 minSize,
|
||||||
|
ElementVec *elementVector, float maxLoadFactor)
|
||||||
|
: fArraySize(OptimalSize(minSize)),
|
||||||
|
fInitialSize(fArraySize),
|
||||||
|
fElementCount(0),
|
||||||
|
fElementVector(elementVector),
|
||||||
|
fMaxLoadFactor(maxLoadFactor)
|
||||||
|
{
|
||||||
|
// sanity check the maximal load factor
|
||||||
|
if (fMaxLoadFactor < 0.5)
|
||||||
|
fMaxLoadFactor = 0.5;
|
||||||
|
// allocate and init the array
|
||||||
|
fHashArray = (int32*)AreaUtils::calloc(fArraySize, sizeof(int32));
|
||||||
|
if (fHashArray) {
|
||||||
|
for (int32 index = 0; index < fArraySize; index++)
|
||||||
|
fHashArray[index] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
OpenHashTable<Element, ElementVec>::~OpenHashTable()
|
||||||
|
{
|
||||||
|
AreaUtils::free(fHashArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
bool
|
||||||
|
OpenHashTable<Element, ElementVec>::InitCheck() const
|
||||||
|
{
|
||||||
|
return (fHashArray && fElementVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
int32
|
||||||
|
OpenHashTable<Element, ElementVec>::OptimalSize(int32 minSize)
|
||||||
|
{
|
||||||
|
for (int32 index = 0; ; index++)
|
||||||
|
if (!kPrimes[index] || kPrimes[index] >= (uint32)minSize)
|
||||||
|
return (int32)kPrimes[index];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
Element *
|
||||||
|
OpenHashTable<Element, ElementVec>::FindFirst(uint32 hash) const
|
||||||
|
{
|
||||||
|
ASSERT(fElementVector);
|
||||||
|
hash %= fArraySize;
|
||||||
|
if (fHashArray[hash] < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return &fElementVector->At(fHashArray[hash]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
int32
|
||||||
|
OpenHashTable<Element, ElementVec>::ElementIndex(const Element *element) const
|
||||||
|
{
|
||||||
|
return fElementVector->IndexOf(*element);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
Element *
|
||||||
|
OpenHashTable<Element, ElementVec>::ElementAt(int32 index) const
|
||||||
|
{
|
||||||
|
return &fElementVector->At(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
int32
|
||||||
|
OpenHashTable<Element, ElementVec>::ArraySize() const
|
||||||
|
{
|
||||||
|
return fArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
int32
|
||||||
|
OpenHashTable<Element, ElementVec>::VectorSize() const
|
||||||
|
{
|
||||||
|
return fElementVector->Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
int32
|
||||||
|
OpenHashTable<Element, ElementVec>::CountElements() const
|
||||||
|
{
|
||||||
|
return fElementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
Element *
|
||||||
|
OpenHashTable<Element, ElementVec>::Add(uint32 hash)
|
||||||
|
{
|
||||||
|
ASSERT(fElementVector);
|
||||||
|
_RehashIfNeeded();
|
||||||
|
hash %= fArraySize;
|
||||||
|
Element *result = fElementVector->Add();
|
||||||
|
if (result) {
|
||||||
|
result->fNext = fHashArray[hash];
|
||||||
|
fHashArray[hash] = fElementVector->IndexOf(*result);
|
||||||
|
fElementCount++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
void
|
||||||
|
OpenHashTable<Element, ElementVec>::Remove(Element *element)
|
||||||
|
{
|
||||||
|
_RehashIfNeeded();
|
||||||
|
uint32 hash = element->Hash() % fArraySize;
|
||||||
|
int32 next = fHashArray[hash];
|
||||||
|
ASSERT(next >= 0);
|
||||||
|
|
||||||
|
if (&fElementVector->At(next) == element) {
|
||||||
|
fHashArray[hash] = element->fNext;
|
||||||
|
fElementVector->Remove(next);
|
||||||
|
fElementCount--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 index = next; index >= 0; ) {
|
||||||
|
// look for an existing match in table
|
||||||
|
next = fElementVector->At(index).fNext;
|
||||||
|
if (next < 0) {
|
||||||
|
TRESPASS();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (&fElementVector->At(next) == element) {
|
||||||
|
fElementVector->At(index).fNext = element->fNext;
|
||||||
|
fElementVector->Remove(next);
|
||||||
|
fElementCount--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
index = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
void
|
||||||
|
OpenHashTable<Element, ElementVec>::SetElementVector(ElementVec *elementVector)
|
||||||
|
{
|
||||||
|
fElementVector = elementVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _RehashIfNeeded
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
bool
|
||||||
|
OpenHashTable<Element, ElementVec>::_RehashIfNeeded()
|
||||||
|
{
|
||||||
|
// The load factor range [fMaxLoadFactor / 3, fMaxLoadFactor] is fine,
|
||||||
|
// I think. After rehashing the load factor will be about
|
||||||
|
// fMaxLoadFactor * 2 / 3, respectively fMaxLoadFactor / 2.
|
||||||
|
float loadFactor = (float)fElementCount / (float)fArraySize;
|
||||||
|
if (loadFactor > fMaxLoadFactor
|
||||||
|
|| (fArraySize > fInitialSize && loadFactor < fMaxLoadFactor / 3)) {
|
||||||
|
return _Rehash();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Rehash
|
||||||
|
template<class Element, class ElementVec>
|
||||||
|
bool
|
||||||
|
OpenHashTable<Element, ElementVec>::_Rehash()
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
int32 newSize = max(fInitialSize,
|
||||||
|
int32(fElementCount * 1.73 * fMaxLoadFactor));
|
||||||
|
newSize = OptimalSize(newSize);
|
||||||
|
if (newSize != fArraySize) {
|
||||||
|
PRINT(("_Rehash(): %lu -> %lu (currently %lu entries)\n", fArraySize, newSize,
|
||||||
|
fElementCount));
|
||||||
|
// allocate a new array
|
||||||
|
int32 *newHashArray
|
||||||
|
= (int32*)AreaUtils::calloc(newSize, sizeof(int32));
|
||||||
|
if (newHashArray) {
|
||||||
|
// init the new hash array
|
||||||
|
for (int32 index = 0; index < newSize; index++)
|
||||||
|
newHashArray[index] = -1;
|
||||||
|
// iterate through all elements and put them into the new
|
||||||
|
// hash array
|
||||||
|
for (int i = 0; i < fArraySize; i++) {
|
||||||
|
int32 index = fHashArray[i];
|
||||||
|
while (index >= 0) {
|
||||||
|
// insert the element in the new array
|
||||||
|
Element &element = fElementVector->At(index);
|
||||||
|
int32 next = element.fNext;
|
||||||
|
uint32 hash = (element.Hash() % newSize);
|
||||||
|
element.fNext = newHashArray[hash];
|
||||||
|
newHashArray[hash] = index;
|
||||||
|
// next element in old list
|
||||||
|
index = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete the old array and set the new one
|
||||||
|
AreaUtils::free(fHashArray);
|
||||||
|
fHashArray = newHashArray;
|
||||||
|
fArraySize = newSize;
|
||||||
|
} else
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
OpenHashElementArray<Element>::OpenHashElementArray(int32 initialSize)
|
||||||
|
: fSize(initialSize),
|
||||||
|
fNextFree(0),
|
||||||
|
fNextDeleted(-1)
|
||||||
|
{
|
||||||
|
fData = (Element*)AreaUtils::calloc((size_t)initialSize, sizeof(Element));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
OpenHashElementArray<Element>::~OpenHashElementArray()
|
||||||
|
{
|
||||||
|
AreaUtils::free(fData);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
bool
|
||||||
|
OpenHashElementArray<Element>::InitCheck() const
|
||||||
|
{
|
||||||
|
return fData;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
Element &
|
||||||
|
OpenHashElementArray<Element>::At(int32 index)
|
||||||
|
{
|
||||||
|
ASSERT(index < fSize);
|
||||||
|
return fData[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
const Element &
|
||||||
|
OpenHashElementArray<Element>::At(int32 index) const
|
||||||
|
{
|
||||||
|
ASSERT(index < fSize);
|
||||||
|
return fData[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
int32
|
||||||
|
OpenHashElementArray<Element>::IndexOf(const Element &element) const
|
||||||
|
{
|
||||||
|
int32 result = &element - fData;
|
||||||
|
if (result < 0 || result > fSize)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
int32
|
||||||
|
OpenHashElementArray<Element>::Size() const
|
||||||
|
{
|
||||||
|
return fSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
Element *
|
||||||
|
OpenHashElementArray<Element>::Add(const Element &newElement)
|
||||||
|
{
|
||||||
|
Element *element = Add();
|
||||||
|
if (element)
|
||||||
|
element.Adopt(newElement);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
const int32 kGrowChunk = 10;
|
||||||
|
#else
|
||||||
|
const int32 kGrowChunk = 1024;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
Element *
|
||||||
|
OpenHashElementArray<Element>::Add()
|
||||||
|
{
|
||||||
|
int32 index = fNextFree;
|
||||||
|
if (fNextDeleted >= 0) {
|
||||||
|
index = fNextDeleted;
|
||||||
|
fNextDeleted = At(index).fNext;
|
||||||
|
} else if (fNextFree >= fSize - 1) {
|
||||||
|
int32 newSize = fSize + kGrowChunk;
|
||||||
|
/*
|
||||||
|
Element *newData = (Element *)calloc((size_t)newSize , sizeof(Element));
|
||||||
|
if (!newData)
|
||||||
|
return NULL;
|
||||||
|
memcpy(newData, fData, fSize * sizeof(Element));
|
||||||
|
free(fData);
|
||||||
|
*/
|
||||||
|
Element *newData = (Element*)AreaUtils::realloc(fData,
|
||||||
|
(size_t)newSize * sizeof(Element));
|
||||||
|
if (!newData)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fData = newData;
|
||||||
|
fSize = newSize;
|
||||||
|
index = fNextFree;
|
||||||
|
fNextFree++;
|
||||||
|
} else
|
||||||
|
fNextFree++;
|
||||||
|
|
||||||
|
new (&At(index)) Element;
|
||||||
|
// call placement new to initialize the element properly
|
||||||
|
ASSERT(At(index).fNext == -1);
|
||||||
|
|
||||||
|
return &At(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Element>
|
||||||
|
void
|
||||||
|
OpenHashElementArray<Element>::Remove(int32 index)
|
||||||
|
{
|
||||||
|
// delete by chaining empty elements in a single linked
|
||||||
|
// list, reusing the next field
|
||||||
|
ASSERT(index < fSize);
|
||||||
|
At(index).~Element();
|
||||||
|
// call the destructor explicitly to destroy the element
|
||||||
|
// properly
|
||||||
|
At(index).fNext = fNextDeleted;
|
||||||
|
fNextDeleted = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
//} // namespace BPrivate
|
||||||
|
|
||||||
|
//using namespace BPrivate;
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
|||||||
|
/* Query - query parsing and evaluation
|
||||||
|
*
|
||||||
|
* Copyright 2001-2004, Axel Dörfler, [email protected].
|
||||||
|
* This file may be used under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Adjusted by Ingo Weinhold <[email protected]> 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, [email protected].
|
||||||
|
* 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, [email protected]
|
||||||
|
** 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, [email protected]
|
||||||
|
** This file may be used under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Oh no! C++ in the kernel! Are you nuts?
|
||||||
|
//
|
||||||
|
// - no exceptions
|
||||||
|
// - (almost) no virtuals (well, the Query code now uses them)
|
||||||
|
// - it's basically only the C++ syntax, and type checking
|
||||||
|
// - since one tend to encapsulate everything in classes, it has a slightly
|
||||||
|
// higher memory overhead
|
||||||
|
// - nicer code
|
||||||
|
// - easier to maintain
|
||||||
|
|
||||||
|
|
||||||
|
inline void *operator new(size_t size, const nothrow_t&) throw()
|
||||||
|
{
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *operator new[](size_t size, const nothrow_t&) throw()
|
||||||
|
{
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void operator delete(void *ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void operator delete[](void *ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we're using virtuals
|
||||||
|
extern "C" void __pure_virtual();
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif /* CPP_H */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
|||||||
|
## BeOS Generic Makefile v2.2 ##
|
||||||
|
|
||||||
|
## Fill in this file to specify the project being created, and the referenced
|
||||||
|
## makefile-engine will do all of the hard work for you. This handles both
|
||||||
|
## Intel and PowerPC builds of the BeOS.
|
||||||
|
|
||||||
|
## Application Specific Settings ---------------------------------------------
|
||||||
|
|
||||||
|
# specify the name of the binary
|
||||||
|
NAME= ../ramfs
|
||||||
|
|
||||||
|
# specify the type of binary
|
||||||
|
# APP: Application
|
||||||
|
# SHARED: Shared library or add-on
|
||||||
|
# STATIC: Static library archive
|
||||||
|
# DRIVER: Kernel Driver
|
||||||
|
TYPE= DRIVER
|
||||||
|
|
||||||
|
# add support for new Pe and Eddie features
|
||||||
|
# to fill in generic makefile
|
||||||
|
|
||||||
|
#%{
|
||||||
|
# @src->@
|
||||||
|
|
||||||
|
# specify the source files to use
|
||||||
|
# full paths or paths relative to the makefile can be included
|
||||||
|
# all files, regardless of directory, will have their object
|
||||||
|
# files created in the common object directory.
|
||||||
|
# Note that this means this makefile will not work correctly
|
||||||
|
# if two source files with the same name (source.c or source.cpp)
|
||||||
|
# are included from different directories. Also note that spaces
|
||||||
|
# in folder names do not work well with this makefile.
|
||||||
|
SRCS= AllocationInfo.cpp AreaUtils.cpp Attribute.cpp AttributeIndex.cpp \
|
||||||
|
AttributeIndexImpl.cpp AttributeIterator.cpp \
|
||||||
|
BlockAllocator.cpp BlockAllocatorArea.cpp BlockAllocatorAreaBucket.cpp \
|
||||||
|
BlockReferenceManager.cpp DataContainer.cpp Debug.cpp \
|
||||||
|
Directory.cpp Entry.cpp EntryIterator.cpp EntryListener.cpp File.cpp \
|
||||||
|
Index.cpp IndexDirectory.cpp LastModifiedIndex.cpp Locker.cpp \
|
||||||
|
NameIndex.cpp Node.cpp \
|
||||||
|
NodeListener.cpp \
|
||||||
|
NodeTable.cpp Query.cpp SizeIndex.cpp String.cpp SymLink.cpp Volume.cpp \
|
||||||
|
cpp.cpp kernel_interface.cpp
|
||||||
|
|
||||||
|
# specify the resource files to use
|
||||||
|
# full path or a relative path to the resource file can be used.
|
||||||
|
RSRCS=
|
||||||
|
|
||||||
|
# @<-src@
|
||||||
|
#%}
|
||||||
|
|
||||||
|
# end support for Pe and Eddie
|
||||||
|
|
||||||
|
# specify additional libraries to link against
|
||||||
|
# there are two acceptable forms of library specifications
|
||||||
|
# - if your library follows the naming pattern of:
|
||||||
|
# libXXX.so or libXXX.a you can simply specify XXX
|
||||||
|
# library: libbe.so entry: be
|
||||||
|
#
|
||||||
|
# - if your library does not follow the standard library
|
||||||
|
# naming scheme you need to specify the path to the library
|
||||||
|
# and it's name
|
||||||
|
# library: my_lib.a entry: my_lib.a or path/my_lib.a
|
||||||
|
LIBS= /boot/develop/tools/gnupro/lib/gcc-lib/i586-beos/2.9-beos-000224/libgcc.a
|
||||||
|
# specify additional paths to directories following the standard
|
||||||
|
# libXXX.so or libXXX.a naming scheme. You can specify full paths
|
||||||
|
# or paths relative to the makefile. The paths included may not
|
||||||
|
# be recursive, so include all of the paths where libraries can
|
||||||
|
# be found. Directories where source files are found are
|
||||||
|
# automatically included.
|
||||||
|
LIBPATHS=
|
||||||
|
|
||||||
|
# additional paths to look for system headers
|
||||||
|
# thes use the form: #include <header>
|
||||||
|
# source file directories are NOT auto-included here
|
||||||
|
SYSTEM_INCLUDE_PATHS =
|
||||||
|
|
||||||
|
# additional paths to look for local headers
|
||||||
|
# thes use the form: #include "header"
|
||||||
|
# source file directories are automatically included
|
||||||
|
LOCAL_INCLUDE_PATHS =
|
||||||
|
|
||||||
|
# specify the level of optimization that you desire
|
||||||
|
# NONE, SOME, FULL
|
||||||
|
OPTIMIZE= NONE
|
||||||
|
|
||||||
|
# specify any preprocessor symbols to be defined. The symbols will not
|
||||||
|
# have their values set automatically; you must supply the value (if any)
|
||||||
|
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
|
||||||
|
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
|
||||||
|
# would pass "-DDEBUG" on the compiler's command line.
|
||||||
|
#
|
||||||
|
# USER - [0/1] userland build
|
||||||
|
# DEBUG - [0/1] Enable debugging.
|
||||||
|
# DBG_PRINT - [0/1] Print debug output to file (only if DEBUG).
|
||||||
|
# DBG_PRINT_FILE - [0/1] Name of debug output file
|
||||||
|
# (only if DBG_PRINT).
|
||||||
|
#
|
||||||
|
DEFINES= USER=0 \
|
||||||
|
DEBUG=1 \
|
||||||
|
DEBUG_PRINT=1 \
|
||||||
|
DEBUG_PRINT_FILE=\"/var/log/ramfs.log\" \
|
||||||
|
B_BAD_DATA=B_ERROR
|
||||||
|
|
||||||
|
# specify special warning levels
|
||||||
|
# if unspecified default warnings will be used
|
||||||
|
# NONE = supress all warnings
|
||||||
|
# ALL = enable all warnings
|
||||||
|
WARNINGS = ALL
|
||||||
|
|
||||||
|
# specify whether image symbols will be created
|
||||||
|
# so that stack crawls in the debugger are meaningful
|
||||||
|
# if TRUE symbols will be created
|
||||||
|
SYMBOLS = TRUE
|
||||||
|
|
||||||
|
# specify debug settings
|
||||||
|
# if TRUE will allow application to be run from a source-level
|
||||||
|
# debugger. Note that this will disable all optimzation.
|
||||||
|
DEBUGGER = TRUE
|
||||||
|
|
||||||
|
# specify additional compiler flags for all files
|
||||||
|
#COMPILER_FLAGS = -include "cpp.h" -fno-exceptions -fno-rtti \
|
||||||
|
|
||||||
|
COMPILER_FLAGS = -include "cpp.h" -fno-exceptions \
|
||||||
|
-Wmissing-prototypes -Woverloaded-virtual \
|
||||||
|
-Wpointer-arith -Wcast-align -Wsign-compare
|
||||||
|
|
||||||
|
# specify additional linker flags
|
||||||
|
LINKER_FLAGS =
|
||||||
|
|
||||||
|
# specify the version of this particular item
|
||||||
|
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
|
||||||
|
# This may also be specified in a resource.
|
||||||
|
APP_VERSION =
|
||||||
|
|
||||||
|
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
|
||||||
|
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
|
||||||
|
# instruct the driverinstall rule to place a symlink to your driver's binary in
|
||||||
|
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
|
||||||
|
# /dev/video/usb when loaded. Default is "misc".
|
||||||
|
DRIVER_PATH =
|
||||||
|
|
||||||
|
## include the makefile-engine
|
||||||
|
include $(BUILDHOME)/etc/makefile-engine
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// ramfs.h
|
||||||
|
|
||||||
|
#ifndef RAM_FS_H
|
||||||
|
#define RAM_FS_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
const size_t kMaxIndexKeyLength = 256;
|
||||||
|
|
||||||
|
#endif // RAM_FS_H
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// ramfs_ioctl.h
|
||||||
|
|
||||||
|
#ifndef RAMFS_IOCTL_H
|
||||||
|
#define RAMFS_IOCTL_H
|
||||||
|
|
||||||
|
#include <Drivers.h>
|
||||||
|
|
||||||
|
#define RAMFS_IOCTL_BASE (B_DEVICE_OP_CODES_END + 10001)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RAMFS_IOCTL_GET_ALLOCATION_INFO = RAMFS_IOCTL_BASE, // AllocationInfo*
|
||||||
|
RAMFS_IOCTL_DUMP_INDEX, // const char* name
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RAMFS_IOCTL_H
|
||||||
Reference in New Issue
Block a user