/*
 * Copyright 2008, Axel Dörfler. All Rights Reserved.
 * Copyright 2007, Hugo Santos. All Rights Reserved.
 *
 * Distributed under the terms of the MIT License.
 */
 
 
#include "HashedObjectCache.h"
 
#include "MemoryManager.h"
#include "slab_private.h"
 
 
RANGE_MARKER_FUNCTION_BEGIN(SlabHashedObjectCache)
 
 
static inline int
__fls0(size_t value)
{
	if (value == 0)
		return -1;
 
	int bit;
	for (bit = 0; value != 1; bit++)
		value >>= 1;
	return bit;
}
 
 
static HashedSlab*
allocate_slab(uint32 flags)
{
	return (HashedSlab*)slab_internal_alloc(sizeof(HashedSlab), flags);
}
 
 
static void
free_slab(HashedSlab* slab, uint32 flags)
{
	slab_internal_free(slab, flags);
}
 
 
// #pragma mark -
 
 
HashedObjectCache::HashedObjectCache()
	:
	hash_table(this)
{
}
 
 
/*static*/ HashedObjectCache*
HashedObjectCache::Create(const char* name, size_t object_size,
	size_t alignment, size_t maximum, size_t magazineCapacity,
	size_t maxMagazineCount, uint32 flags, void* cookie,
	object_cache_constructor constructor, object_cache_destructor destructor,
	object_cache_reclaimer reclaimer)
{
	void* buffer = slab_internal_alloc(sizeof(HashedObjectCache), flags);
	if (buffer == NULL)
		return NULL;
 
	HashedObjectCache* cache = new(buffer) HashedObjectCache();
 
	// init the hash table
	size_t hashSize = cache->hash_table.ResizeNeeded();
	buffer = slab_internal_alloc(hashSize, flags);
	if (buffer == NULL) {
		cache->Delete();
		return NULL;
	}
 
	cache->hash_table.Resize(buffer, hashSize, true);
 
	if (cache->Init(name, object_size, alignment, maximum, magazineCapacity,
			maxMagazineCount, flags, cookie, constructor, destructor,
			reclaimer) != B_OK) {
		cache->Delete();
		return NULL;
	}
 
	if ((flags & CACHE_LARGE_SLAB) != 0)
		cache->slab_size = 128 * object_size;
	else
		cache->slab_size = 8 * object_size;
 
	cache->slab_size = MemoryManager::AcceptableChunkSize(cache->slab_size);
	cache->lower_boundary = __fls0(cache->slab_size);
 
	return cache;
}
 
 
void
HashedObjectCache::Delete()
{
	this->~HashedObjectCache();
	slab_internal_free(this, 0);
}
 
 
slab*
HashedObjectCache::CreateSlab(uint32 flags)
{
	if (!check_cache_quota(this))
		return NULL;
 
	Unlock();
 
	HashedSlab* slab = allocate_slab(flags);
	if (slab != NULL) {
		void* pages = NULL;
		if (MemoryManager::Allocate(this, flags, pages) == B_OK
			&& AllocateTrackingInfos(slab, slab_size, flags) == B_OK) {
			Lock();
			if (InitSlab(slab, pages, slab_size, flags)) {
				hash_table.InsertUnchecked(slab);
				_ResizeHashTableIfNeeded(flags);
				return slab;
			}
			Unlock();
			FreeTrackingInfos(slab, flags);
		}
 
		if (pages != NULL)
			MemoryManager::Free(pages, flags);
 
		free_slab(slab, flags);
	}
 
	Lock();
	return NULL;
}
 
 
void
HashedObjectCache::ReturnSlab(slab* _slab, uint32 flags)
{
	HashedSlab* slab = static_cast<HashedSlab*>(_slab);
 
	hash_table.RemoveUnchecked(slab);
	_ResizeHashTableIfNeeded(flags);
 
	UninitSlab(slab);
 
	Unlock();
	FreeTrackingInfos(slab, flags);
	MemoryManager::Free(slab->pages, flags);
	free_slab(slab, flags);
	Lock();
}
 
 
slab*
HashedObjectCache::ObjectSlab(void* object) const
{
	ASSERT_LOCKED_MUTEX(&lock);
 
	HashedSlab* slab = hash_table.Lookup(::lower_boundary(object, slab_size));
	if (slab == NULL) {
		panic("hash object cache %p: unknown object %p", this, object);
		return NULL;
	}
 
	return slab;
}
 
 
void
HashedObjectCache::_ResizeHashTableIfNeeded(uint32 flags)
{
	size_t hashSize = hash_table.ResizeNeeded();
	if (hashSize != 0) {
		Unlock();
		void* buffer = slab_internal_alloc(hashSize, flags);
		Lock();
 
		if (buffer != NULL) {
			if (hash_table.ResizeNeeded() == hashSize) {
				void* oldHash;
				hash_table.Resize(buffer, hashSize, true, &oldHash);
				if (oldHash != NULL) {
					Unlock();
					slab_internal_free(oldHash, flags);
					Lock();
				}
			}
		}
	}
}
 
 
RANGE_MARKER_FUNCTION_END(SlabHashedObjectCache)

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: lower_boundary.