From bb3092b298f13b7d382ea656ee9e615f677b11e0 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Tue, 13 Jan 2015 14:52:01 +0100 Subject: [PATCH] rootfs: convert to BOpenHashTable. * Add an fs-shell compatible version of BOpenHashTable in the fs_shell to keep it working. The header is renamed to KOpenHashTable to avoid a conflict with the OpenHashTable.h available in private/shared which is not API compatible. --- src/system/kernel/fs/rootfs.cpp | 94 +++--- src/tools/fs_shell/KOpenHashTable.h | 497 ++++++++++++++++++++++++++++ 2 files changed, 544 insertions(+), 47 deletions(-) create mode 100644 src/tools/fs_shell/KOpenHashTable.h diff --git a/src/system/kernel/fs/rootfs.cpp b/src/system/kernel/fs/rootfs.cpp index 9c1db887ca..e16329ab17 100644 --- a/src/system/kernel/fs/rootfs.cpp +++ b/src/system/kernel/fs/rootfs.cpp @@ -8,9 +8,11 @@ #if FS_SHELL +# include + # include "fssh_api_wrapper.h" -# include "hash.h" +# include "KOpenHashTable.h" # include "list.h" #else # include @@ -23,14 +25,15 @@ # include # include -# include # include +# include # include # include # include #endif + #if FS_SHELL using namespace FSShell; # define user_strlcpy(to, from, len) (strlcpy(to, from, len), FSSH_B_OK) @@ -71,12 +74,39 @@ struct rootfs_vnode { struct rootfs_stream stream; }; +struct VnodeHash { + typedef ino_t KeyType; + typedef rootfs_vnode ValueType; + + size_t HashKey(KeyType key) const + { + return key; + } + + size_t Hash(ValueType* vnode) const + { + return vnode->id; + } + + bool Compare(KeyType key, ValueType* vnode) const + { + return vnode->id == key; + } + + ValueType*& GetLink(ValueType* value) const + { + return value->all_next; + } +}; + +typedef BOpenHashTable VnodeTable; + struct rootfs { fs_volume* volume; dev_t id; rw_lock lock; ino_t next_vnode_id; - hash_table* vnode_list_hash; + VnodeTable* vnode_list_hash; struct rootfs_vnode* root_vnode; }; @@ -117,32 +147,6 @@ current_timespec() } -static uint32 -rootfs_vnode_hash_func(void* _v, const void* _key, uint32 range) -{ - struct rootfs_vnode* vnode = (rootfs_vnode*)_v; - const ino_t* key = (const ino_t*)_key; - - if (vnode != NULL) - return vnode->id % range; - - return (uint64)*key % range; -} - - -static int -rootfs_vnode_compare_func(void* _v, const void* _key) -{ - struct rootfs_vnode* v = (rootfs_vnode*)_v; - const ino_t* key = (const ino_t*)_key; - - if (v->id == *key) - return 0; - - return -1; -} - - static struct rootfs_vnode* rootfs_create_vnode(struct rootfs* fs, struct rootfs_vnode* parent, const char* name, int type) @@ -188,7 +192,7 @@ rootfs_delete_vnode(struct rootfs* fs, struct rootfs_vnode* v, bool force_delete return EPERM; // remove it from the global hash table - hash_remove(fs->vnode_list_hash, v); + fs->vnode_list_hash->Remove(v); if (S_ISDIR(v->stream.type)) mutex_destroy(&v->stream.dir.cookie_lock); @@ -376,10 +380,9 @@ rootfs_mount(fs_volume* volume, const char* device, uint32 flags, rw_lock_init(&fs->lock, "rootfs"); - fs->vnode_list_hash = hash_init(ROOTFS_HASH_SIZE, - offsetof(rootfs_vnode, all_next), &rootfs_vnode_compare_func, - &rootfs_vnode_hash_func); - if (fs->vnode_list_hash == NULL) { + fs->vnode_list_hash = new(std::nothrow) VnodeTable(); + if (fs->vnode_list_hash == NULL + || fs->vnode_list_hash->Init(ROOTFS_HASH_SIZE) != B_OK) { err = B_NO_MEMORY; goto err2; } @@ -393,7 +396,7 @@ rootfs_mount(fs_volume* volume, const char* device, uint32 flags, vnode->parent = vnode; fs->root_vnode = vnode; - hash_insert(fs->vnode_list_hash, vnode); + fs->vnode_list_hash->Insert(vnode); publish_vnode(volume, vnode->id, vnode, &sVnodeOps, vnode->stream.type, 0); *_rootID = vnode->id; @@ -401,7 +404,7 @@ rootfs_mount(fs_volume* volume, const char* device, uint32 flags, return B_OK; err3: - hash_uninit(fs->vnode_list_hash); + delete fs->vnode_list_hash; err2: rw_lock_destroy(&fs->lock); free(fs); @@ -421,17 +424,14 @@ rootfs_unmount(fs_volume* _volume) put_vnode(fs->volume, fs->root_vnode->id); // delete all of the vnodes - struct hash_iterator i; - hash_open(fs->vnode_list_hash, &i); + VnodeTable::Iterator i(fs->vnode_list_hash); - while (struct rootfs_vnode* vnode = (struct rootfs_vnode*) - hash_next(fs->vnode_list_hash, &i)) { + while (i.HasNext()) { + struct rootfs_vnode* vnode = i.Next(); rootfs_delete_vnode(fs, vnode, true); } - hash_close(fs->vnode_list_hash, &i, false); - - hash_uninit(fs->vnode_list_hash); + delete fs->vnode_list_hash; rw_lock_destroy(&fs->lock); free(fs); @@ -503,7 +503,7 @@ rootfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _vnode, int* _type, if (!reenter) rw_lock_read_lock(&fs->lock); - vnode = (rootfs_vnode*)hash_lookup(fs->vnode_list_hash, &id); + vnode = fs->vnode_list_hash->Lookup(id); if (!reenter) rw_lock_read_unlock(&fs->lock); @@ -644,7 +644,7 @@ rootfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name, return B_NO_MEMORY; rootfs_insert_in_dir(fs, dir, vnode); - hash_insert(fs->vnode_list_hash, vnode); + fs->vnode_list_hash->Insert(vnode); entry_cache_add(fs->volume->id, dir->id, name, vnode->id); notify_entry_created(fs->id, dir->id, name, vnode->id); @@ -877,7 +877,7 @@ rootfs_symlink(fs_volume* _volume, fs_vnode* _dir, const char* name, return B_NO_MEMORY; rootfs_insert_in_dir(fs, dir, vnode); - hash_insert(fs->vnode_list_hash, vnode); + fs->vnode_list_hash->Insert(vnode); vnode->stream.symlink.path = strdup(path); if (vnode->stream.symlink.path == NULL) { @@ -1086,7 +1086,7 @@ rootfs_create_special_node(fs_volume* _volume, fs_vnode* _dir, const char* name, else flags |= B_VNODE_PUBLISH_REMOVED; - hash_insert(fs->vnode_list_hash, vnode); + fs->vnode_list_hash->Insert(vnode); _superVnode->private_node = vnode; _superVnode->ops = &sVnodeOps; diff --git a/src/tools/fs_shell/KOpenHashTable.h b/src/tools/fs_shell/KOpenHashTable.h new file mode 100644 index 0000000000..363c225fdf --- /dev/null +++ b/src/tools/fs_shell/KOpenHashTable.h @@ -0,0 +1,497 @@ +/* + * Copyright 2007, Hugo Santos. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_UTIL_OPEN_HASH_TABLE_H +#define _KERNEL_UTIL_OPEN_HASH_TABLE_H + + +#include "fssh_api_wrapper.h" + +#include +#include + +#ifdef _KERNEL_MODE +# include +# include +# include +#else +# include +#endif + + +/*! + The Definition template must have four methods: `HashKey', `Hash', + `Compare' and `GetLink;. It must also define several types as shown in the + following example: + + struct Foo { + int bar; + + Foo* fNext; + }; + + struct HashTableDefinition { + typedef int KeyType; + typedef Foo ValueType; + + size_t HashKey(KeyType key) const + { + return key >> 1; + } + + size_t Hash(ValueType* value) const + { + return HashKey(value->bar); + } + + bool Compare(KeyType key, ValueType* value) const + { + return value->bar == key; + } + + ValueType*& GetLink(ValueType* value) const + { + return value->fNext; + } + }; +*/ + + +struct MallocAllocator { + void* Allocate(size_t size) const + { + return malloc(size); + } + + void Free(void* memory) const + { + free(memory); + } +}; + + +/** Implements an hash table with open hashing, that is, colliding entries are + * stored in a linked list. The table may be made to adjust its number of slots + * depending on the load factor (this should be enabled unless the object is to + * be used at times where memory allocations aren't possible, such as code + * called byt he memory allocator). + * + * The link between entries is part of the ValueType stored items, which makes + * sure the table can always accept new items and will never fail because it is + * out of memory (except at Init time). + */ +template +class BOpenHashTable { +public: + typedef BOpenHashTable HashTable; + typedef typename Definition::KeyType KeyType; + typedef typename Definition::ValueType ValueType; + + static const size_t kMinimumSize = 8; + + // All allocations are of power of 2 lengths. + + // regrowth factor: 200 / 256 = 78.125% + // 50 / 256 = 19.53125% + + BOpenHashTable() + : + fTableSize(0), + fItemCount(0), + fTable(NULL) + { + } + + BOpenHashTable(const Definition& definition) + : + fDefinition(definition), + fTableSize(0), + fItemCount(0), + fTable(NULL) + { + } + + BOpenHashTable(const Definition& definition, const Allocator& allocator) + : + fDefinition(definition), + fAllocator(allocator), + fTableSize(0), + fItemCount(0), + fTable(NULL) + { + } + + ~BOpenHashTable() + { + fAllocator.Free(fTable); + } + + status_t Init(size_t initialSize = kMinimumSize) + { + if (initialSize > 0 && !_Resize(initialSize)) + return B_NO_MEMORY; + return B_OK; + } + + size_t TableSize() const + { + return fTableSize; + } + + bool IsEmpty() const + { + return fItemCount == 0; + } + + size_t CountElements() const + { + return fItemCount; + } + + ValueType* Lookup(typename TypeOperation::ConstRefT key) const + { + if (fTableSize == 0) + return NULL; + + size_t index = fDefinition.HashKey(key) & (fTableSize - 1); + ValueType* slot = fTable[index]; + + while (slot) { + if (fDefinition.Compare(key, slot)) + break; + slot = _Link(slot); + } + + return slot; + } + + status_t Insert(ValueType* value) + { + if (fTableSize == 0) { + if (!_Resize(kMinimumSize)) + return B_NO_MEMORY; + } else if (AutoExpand && fItemCount >= (fTableSize * 200 / 256)) + _Resize(fTableSize * 2); + + InsertUnchecked(value); + return B_OK; + } + + /*! \brief Inserts a value without resizing the table. + + Use this method if you need to insert a value into the table while + iterating it, as regular insertion can invalidate iterators. + */ + void InsertUnchecked(ValueType* value) + { + if (CheckDuplicates && _ExhaustiveSearch(value)) { +#if defined(_KERNEL_MODE) || defined(FS_SHELL) + panic("Hash Table: value already in table."); +#else + debugger("Hash Table: value already in table."); +#endif + } + + _Insert(fTable, fTableSize, value); + fItemCount++; + } + + // TODO: a ValueType* Remove(const KeyType& key) method is missing + + bool Remove(ValueType* value) + { + if (!RemoveUnchecked(value)) + return false; + + if (AutoExpand && fTableSize > kMinimumSize + && fItemCount < (fTableSize * 50 / 256)) + _Resize(fTableSize / 2); + + return true; + } + + /*! \brief Removes a value without resizing the table. + + Use this method if you need to remove a value from the table while + iterating it, as Remove can invalidate iterators. + + Also use this method if you know you are going to reinsert the item soon + (possibly with a different hash) to avoid shrinking then growing the + table again. + */ + bool RemoveUnchecked(ValueType* value) + { + size_t index = fDefinition.Hash(value) & (fTableSize - 1); + ValueType* previous = NULL; + ValueType* slot = fTable[index]; + + while (slot) { + ValueType* next = _Link(slot); + + if (value == slot) { + if (previous) + _Link(previous) = next; + else + fTable[index] = next; + break; + } + + previous = slot; + slot = next; + } + + if (slot == NULL) + return false; + + if (CheckDuplicates && _ExhaustiveSearch(value)) { +#if defined(_KERNEL_MODE) || defined(FS_SHELL) + panic("Hash Table: duplicate detected."); +#else + debugger("Hash Table: duplicate detected."); +#endif + } + + fItemCount--; + return true; + } + + /*! \brief Removes all elements from the hash table. + + No resizing happens. The elements are not deleted. If \a returnElements + is \c true, the method returns all elements chained via their hash table + link. + */ + ValueType* Clear(bool returnElements = false) + { + if (fItemCount == 0) + return NULL; + + ValueType* result = NULL; + + if (returnElements) { + ValueType** nextPointer = &result; + + // iterate through all buckets + for (size_t i = 0; i < fTableSize; i++) { + ValueType* element = fTable[i]; + if (element != NULL) { + // add the bucket to the list + *nextPointer = element; + + // update nextPointer to point to the fNext of the last + // element in the bucket + while (element != NULL) { + nextPointer = &_Link(element); + element = *nextPointer; + } + } + } + } + + memset(this->fTable, 0, sizeof(ValueType*) * this->fTableSize); + fItemCount = 0; + + return result; + } + + /*! If the table needs resizing, the number of bytes for the required + allocation is returned. If no resizing is needed, 0 is returned. + */ + size_t ResizeNeeded() const + { + size_t size = fTableSize; + if (size == 0 || fItemCount >= (size * 200 / 256)) { + // grow table + if (size == 0) + size = kMinimumSize; + while (fItemCount >= size * 200 / 256) + size <<= 1; + } else if (size > kMinimumSize && fItemCount < size * 50 / 256) { + // shrink table + while (fItemCount < size * 50 / 256) + size >>= 1; + if (size < kMinimumSize) + size = kMinimumSize; + } + + if (size == fTableSize) + return 0; + + return size * sizeof(ValueType*); + } + + /*! Resizes the table using the given allocation. The allocation must not + be \c NULL. It must be of size \a size, which must be a value returned + earlier by ResizeNeeded(). If the size requirements have changed in the + meantime, the method free()s the given allocation and returns \c false, + unless \a force is \c true, in which case the supplied allocation is + used in any event. + Otherwise \c true is returned. + If \a oldTable is non-null and resizing is successful, the old table + will not be freed, but will be returned via this parameter instead. + */ + bool Resize(void* allocation, size_t size, bool force = false, + void** oldTable = NULL) + { + if (!force && size != ResizeNeeded()) { + fAllocator.Free(allocation); + return false; + } + + _Resize((ValueType**)allocation, size / sizeof(ValueType*), oldTable); + return true; + } + + /*! \brief Iterator for BOpenHashMap + + The iterator is not invalidated when removing the current element from + the table, unless the removal triggers a resize. + */ + class Iterator { + public: + Iterator(const HashTable* table) + : fTable(table) + { + Rewind(); + } + + Iterator(const HashTable* table, size_t index, ValueType* value) + : fTable(table), fIndex(index), fNext(value) {} + + bool HasNext() const { return fNext != NULL; } + + ValueType* Next() + { + ValueType* current = fNext; + _GetNext(); + return current; + } + + void Rewind() + { + // get the first one + fIndex = 0; + fNext = NULL; + _GetNext(); + } + + protected: + Iterator() {} + + void _GetNext() + { + if (fNext) + fNext = fTable->_Link(fNext); + + while (fNext == NULL && fIndex < fTable->fTableSize) + fNext = fTable->fTable[fIndex++]; + } + + const HashTable* fTable; + size_t fIndex; + ValueType* fNext; + }; + + Iterator GetIterator() const + { + return Iterator(this); + } + + Iterator GetIterator(typename TypeOperation::ConstRefT key) const + { + if (fTableSize == 0) + return Iterator(this, fTableSize, NULL); + + size_t index = fDefinition.HashKey(key) & (fTableSize - 1); + ValueType* slot = fTable[index]; + + while (slot) { + if (fDefinition.Compare(key, slot)) + break; + slot = _Link(slot); + } + + if (slot == NULL) + return Iterator(this, fTableSize, NULL); + + return Iterator(this, index + 1, slot); + } + +protected: + // for g++ 2.95 + friend class Iterator; + + void _Insert(ValueType** table, size_t tableSize, ValueType* value) + { + size_t index = fDefinition.Hash(value) & (tableSize - 1); + + _Link(value) = table[index]; + table[index] = value; + } + + bool _Resize(size_t newSize) + { + ValueType** newTable + = (ValueType**)fAllocator.Allocate(sizeof(ValueType*) * newSize); + if (newTable == NULL) + return false; + + _Resize(newTable, newSize); + return true; + } + + void _Resize(ValueType** newTable, size_t newSize, void** _oldTable = NULL) + { + for (size_t i = 0; i < newSize; i++) + newTable[i] = NULL; + + if (fTable) { + for (size_t i = 0; i < fTableSize; i++) { + ValueType* bucket = fTable[i]; + while (bucket) { + ValueType* next = _Link(bucket); + _Insert(newTable, newSize, bucket); + bucket = next; + } + } + + if (_oldTable != NULL) + *_oldTable = fTable; + else + fAllocator.Free(fTable); + } else if (_oldTable != NULL) + *_oldTable = NULL; + + fTableSize = newSize; + fTable = newTable; + } + + ValueType*& _Link(ValueType* bucket) const + { + return fDefinition.GetLink(bucket); + } + + bool _ExhaustiveSearch(ValueType* value) const + { + for (size_t i = 0; i < fTableSize; i++) { + ValueType* bucket = fTable[i]; + while (bucket) { + if (bucket == value) + return true; + bucket = _Link(bucket); + } + } + + return false; + } + + Definition fDefinition; + Allocator fAllocator; + size_t fTableSize; + size_t fItemCount; + ValueType** fTable; +}; + +#endif // _KERNEL_UTIL_OPEN_HASH_TABLE_H