diff --git a/src/kernel/core/cache/BlockMap.cpp b/src/kernel/core/cache/BlockMap.cpp new file mode 100644 index 0000000000..e59c83f70c --- /dev/null +++ b/src/kernel/core/cache/BlockMap.cpp @@ -0,0 +1,240 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the Haiku License. +*/ + + +/** The BlockMap stores offset:address pairs; you can map an offset to a specific address. + * It has been designed to contain few and mostly contiguous offset mappings - it is used + * by the file cache to keep track about which blocks of the file are already in memory. + * The offsets may spread over a very large amount. + * + * Internally, it stores small and contiguous address arrays of a certain size, and + * accesses those using a hash table. Address values of NULL are equal to non existing + * mappings; that value cannot be stored. At the current size, each hash entry can + * map 60 addresses which corresponds to a file range of 240 kB. + * It currently does not do any locking; it assumes a safe environment which you are + * responsible for to create when you call its functions. + */ + + +#include "BlockMap.h" + +#include +#include + +#include +#include + + +//#define TRACE_BLOCK_MAP +#ifdef TRACE_BLOCK_MAP +# define TRACE(x) dprintf x +#else +# define TRACE(x) +#endif + + +// ToDo: when we have a better allocator, change the number of addresses to a power of two +// currently, this structure takes 256 bytes total + +struct BlockMap::block_entry { + block_entry *next; + uint32 used; + off_t offset; + addr_t address[60]; +}; + +#define BLOCK_ARRAY_SIZE (sizeof(BlockMap::block_entry::address) / sizeof(addr_t)) + +static inline off_t +to_block_entry_offset(off_t offset, uint32 &index) +{ + // ToDo: improve this once we have a power of two array size + off_t baseOffset = (offset / BLOCK_ARRAY_SIZE) * BLOCK_ARRAY_SIZE; + index = uint32(offset - baseOffset); + + return baseOffset; +} + + +static int +block_entry_compare(void *_entry, const void *_offset) +{ + BlockMap::block_entry *entry = (BlockMap::block_entry *)_entry; + const off_t *offset = (const off_t *)_offset; + + return entry->offset - *offset; +} + + +static uint32 +block_entry_hash(void *_entry, const void *_offset, uint32 range) +{ + BlockMap::block_entry *entry = (BlockMap::block_entry *)_entry; + const off_t *offset = (const off_t *)_offset; + + if (entry != NULL) + return entry->offset % range; + + return *offset % range; +} + + +// #pragma mark - + + +BlockMap::BlockMap(off_t size) + : + fSize(size) +{ + fHashTable = hash_init(16, 0, &block_entry_compare, &block_entry_hash); +} + + +BlockMap::~BlockMap() +{ +} + + +/** Checks wether or not the construction of the BlockMap were successful. + */ + +status_t +BlockMap::InitCheck() const +{ + return fHashTable != NULL ? B_OK : B_NO_MEMORY; +} + + +/** Sets the size of the block map - all existing entries beyond this size will be + * removed from the map, and their memory is freed. + */ + +void +BlockMap::SetSize(off_t size) +{ + TRACE(("BlockMap::SetSize(%Ld)\n", size)); + + if (size >= fSize) { + // nothing to do + fSize = size; + return; + } + + // ToDo: remove all mappings beyond the file size +} + + +/** Upon successful exit which is indicated by a return value of B_OK, the "_entry" + * argument points to a block_entry structure containing the data for the given + * offset. + * The offset must have been normalized to the base offset values of a block entry + * already. + */ + +status_t +BlockMap::GetBlockEntry(off_t baseOffset, block_entry **_entry) +{ + block_entry *entry = (block_entry *)hash_lookup(fHashTable, &baseOffset); + if (entry == NULL) + return B_ENTRY_NOT_FOUND; + + *_entry = entry; + return B_OK; +} + + +status_t +BlockMap::Remove(off_t offset, off_t count) +{ + TRACE(("BlockMap::Remove(offset = %Ld, count = %Ld)\n", offset, count)); + + uint32 index; + off_t baseOffset = to_block_entry_offset(offset, index); + block_entry *entry; + + while (count > 0) { + int32 max = min_c(BLOCK_ARRAY_SIZE, index + count); + int32 blocks = max - index; + + if (GetBlockEntry(baseOffset, &entry) == B_OK) { + for (int32 i = index; i < max; i++) { + if (entry->address[i] != NULL) + entry->used--; + + entry->address[i] = NULL; + } + + if (entry->used == 0) { + // release entry if it's no longer used + hash_remove(fHashTable, entry); + free(entry); + } + } + + baseOffset += BLOCK_ARRAY_SIZE; + count -= blocks; + index = 0; + } + + return B_OK; +} + + +status_t +BlockMap::Set(off_t offset, addr_t address) +{ + TRACE(("BlockMap::Set(offset = %Ld, address = %08lx)\n", offset, address)); + + uint32 index; + off_t baseOffset = to_block_entry_offset(offset, index); + + block_entry *entry; + if (GetBlockEntry(baseOffset, &entry) == B_OK) { + // the block already exists, we just need to fill in our new address + if (entry->address[index] == NULL && address != NULL) + entry->used++; + else if (entry->address[index] != NULL && address == NULL) + entry->used--; + + entry->address[index] = address; + return B_OK; + } + + // allocate new block and fill it + + entry = (block_entry *)malloc(sizeof(struct block_entry)); + if (entry == NULL) + return B_NO_MEMORY; + + memset(entry->address, 0, sizeof(entry->address)); + + entry->used = 1; + entry->offset = baseOffset; + + hash_insert(fHashTable, entry); + + entry->address[index] = address; + return B_OK; +} + + +status_t +BlockMap::Get(off_t offset, addr_t &address) +{ + TRACE(("BlockMap::Get(offset = %Ld)\n", offset)); + + uint32 index; + off_t baseOffset = to_block_entry_offset(offset, index); + + block_entry *entry; + if (GetBlockEntry(baseOffset, &entry) == B_OK + && entry->address[index] != NULL) { + address = entry->address[index]; + return B_OK; + } + + return B_ENTRY_NOT_FOUND; +} + diff --git a/src/kernel/core/cache/BlockMap.h b/src/kernel/core/cache/BlockMap.h new file mode 100644 index 0000000000..5a7414dd64 --- /dev/null +++ b/src/kernel/core/cache/BlockMap.h @@ -0,0 +1,35 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the Haiku License. +*/ +#ifndef BLOCK_MAP_H +#define BLOCK_MAP_H + + +#include +#include + + +class BlockMap { + public: + BlockMap(off_t size); + ~BlockMap(); + + status_t InitCheck() const; + + void SetSize(off_t size); + off_t Size() const { return fSize; } + + status_t Remove(off_t offset, off_t count = 1); + status_t Set(off_t offset, addr_t address); + status_t Get(off_t offset, addr_t &address); + + private: + struct block_entry; + status_t GetBlockEntry(off_t offset, block_entry **_entry); + + hash_table *fHashTable; + off_t fSize; +}; + +#endif /* BLOCK_MAP_H */