/*
 * Copyright 2023, ttcoder
 * All rights reserved. Distributed under the terms of the MIT license.
 */
#ifndef cache_nument_H
#define cache_nument_H



class NumEntCache
{
	// Without caching, the NTFS num_dirent() is unbearably slow (it's called for *each*
	// dirhandle.read(), and each read() would calls fuse.op.readdir() (!), so directory
	// reading is o(n²), hence reading even a small directory takes several seconds
	// with a slow back-end).
	// So we make sure num_dirent() short-circuits to a mere "return ..." most of the time
	// and we are then o(1) instead.

public:
	NumEntCache();
	void Clear();
	void Memorize( const char * path, unsigned num_entries );  // only call this if GetNumFor() fails to find <path> !
	bool GetNumFor( const char * path, unsigned & into_numentries );

private:  // Data
#if 0
	String<Vfs::MAX_PATH_LEN>
		path_OneCache;
	unsigned
		nument_OneCache;
#else
///ToDo: increase SlotCount from 1 to e.g. 5 ?
	static const int SlotCount = 1;//10;//1;//20;
	String<Vfs::MAX_PATH_LEN>
		cachedPaths[ SlotCount ] = {};
	unsigned
		cachedNuments[ SlotCount ] = {};
	
	// profiling/benchmarking:
	// -  1 slot:  TSC num_dirent: 9600M (533 calls, last 1582K)
	// -  2 slots: TSC num_dirent: 5783M (533 calls, last 1582K)
	// -  5 slots: TSC num_dirent: 2692M (533 calls, last 1458K)
	// - 10 slots: TSC num_dirent: 2944M (533 calls, last 1713K)...
	// - 20 slots: TSC num_dirent: 2063M (533 calls, last 1485K)

#endif
};


///+ add "inline"... or move to a .cpp
NumEntCache::NumEntCache()
//:	path_OneCache()
//	,nument_OneCache( 0 )
{
}

#if 0
void NumEntCache::Clear()
{
	//Genode::warning( "  NumEntCache::Clear" );///
	
	path_OneCache = "";
	nument_OneCache = 0;
}

void NumEntCache::Memorize( const char * path, unsigned num_entries )
{
	path_OneCache = path;
	nument_OneCache = num_entries;
}

bool NumEntCache::GetNumFor( const char * path, unsigned & into_numentries )
{
	if( path_OneCache != path )
		//
		return false;
	
	into_numentries = nument_OneCache;
	return true;
}

#else

void NumEntCache::Clear()
{
	Genode::log( "      numentcache.Clear" );///
	//GENODE_LOG_TSC(5);
	
	for( int i = 0; i < SlotCount; i++ )
	{
		cachedPaths[i] = "";
		cachedNuments[i] = 0;
	}
}

void NumEntCache::Memorize( const char * path, unsigned num_entries )
{
	//GENODE_LOG_TSC(5);
	
	int free_slot = 0;  // default to force-freeing slot 0 (if the below does not actually find a free slot)
	//STATIC_ASSERT( free_slot < SlotCount );
	
	// find the (generally unique) free slot
	for( int i = 0; i < SlotCount; i++ )
	{
		if( cachedPaths[i] == "" )
		{
			free_slot = i;
			//
			break;  // if there are several free slots, use the _first_ one
			//
		}
	}
	
	// clear the slot ahead (with modulo) of us ;
	// this way we ensure a sort of "rotation", akin to LRU (least recently used)
	int next = free_slot % SlotCount;
	cachedPaths[ next ] = "";
	cachedNuments[ next ] = 0;
	
	// occupy the free slot
	cachedPaths[ free_slot ] = path;
	cachedNuments[ free_slot ] = num_entries;
}

bool NumEntCache::GetNumFor( const char * path, unsigned & into_numentries )
{
	//GENODE_LOG_TSC(5);
	
	for( int i = 0; i < SlotCount; i++ )
	{
		if( cachedPaths[i] == path )
		{
			into_numentries = cachedNuments[i];
			//
			return true;
			//
		}
	}
	
	return false;
}
#endif



#endif // cache_nument.h_H

