/*++

Copyright(c) 1999-2000  Microsoft Corporation

Module Name:

    brdgcach.h

Abstract:

    Ethernet MAC level bridge.
    Cache implementation header

Author:

    Mark Aiken

Environment:

    Kernel mode driver

Revision History:

    December  2000 - Original version

--*/

// ===========================================================================
//
// DECLARATIONS
//
// ===========================================================================

typedef struct _CACHE_ENTRY
{
    UINT32          key;
    UINT32          data;
    UINT64          hits;
    UINT64          misses;
} CACHE_ENTRY, *PCACHE_ENTRY;

typedef struct _CACHE
{
    // lock protects all cache fields
    NDIS_SPIN_LOCK  lock;

    // stats
    UINT64          hits;
    UINT64          misses;

    // 2^shiftFactor is the number of entries
    USHORT          shiftFactor;

    // Pointer to the array of entries
    PCACHE_ENTRY    pEntries;
} CACHE, *PCACHE;

//
// Determines the cache slot for key k in cache c. The slot is determined
// as the bottom bits of k.
//
#define CACHE_INDEX(c, k) (k & ((1 << c->shiftFactor) - 1))

// ===========================================================================
//
// INLINES
//
// ===========================================================================

__inline
VOID
BrdgClearCache(
    IN PCACHE       pCache
    )
{
    NdisAcquireSpinLock( &pCache->lock );
    memset( pCache->pEntries, 0, sizeof(CACHE_ENTRY) * (1 << pCache->shiftFactor) );
    NdisReleaseSpinLock( &pCache->lock );
}

__inline
NDIS_STATUS
BrdgInitializeCache(
    IN PCACHE       pCache,
    IN USHORT       shiftFactor
    )
{
    ULONG           numEntries = 1 << shiftFactor;
    NDIS_STATUS     status;

    pCache->shiftFactor = shiftFactor;
    NdisAllocateSpinLock( &pCache->lock );
    status = NdisAllocateMemoryWithTag( &pCache->pEntries, sizeof(CACHE_ENTRY) * numEntries, 'gdrB' );
    pCache->hits = 0L;
    pCache->misses = 0L;

    if( status != NDIS_STATUS_SUCCESS )
    {
        return status;
    }

    // Zero out the array of entries
    memset( pCache->pEntries, 0, sizeof(CACHE_ENTRY) * (1 << pCache->shiftFactor) );
    return NDIS_STATUS_SUCCESS;
}

__inline
VOID
BrdgFreeCache(
    IN PCACHE       pCache
    )
{
    NdisFreeMemory( pCache->pEntries, sizeof(CACHE_ENTRY) * (1 << pCache->shiftFactor), 0 );
}

__inline
UINT32
BrdgProbeCache(
    IN PCACHE       pCache,
    IN UINT32       key
    )
{
    UINT32          index = CACHE_INDEX(pCache, key);
    PCACHE_ENTRY    pEntry = &pCache->pEntries[index];
    UINT32          data = 0L;

    NdisAcquireSpinLock( &pCache->lock );

    if( pEntry->key == key )
    {
        data = pEntry->data;
        pEntry->hits++;
        pCache->hits++;
    }
    else
    {
        pEntry->misses++;
        pCache->misses++;
    }

    NdisReleaseSpinLock( &pCache->lock );

    return data;
}

__inline
BOOLEAN
BrdgUpdateCache(
    IN PCACHE       pCache,
    IN UINT32       key,
    IN UINT32       data
    )
{
    UINT32          index = CACHE_INDEX(pCache, key);
    PCACHE_ENTRY    pEntry = &pCache->pEntries[index];
    BOOLEAN         bUpdated = FALSE;

    NdisAcquireSpinLock( &pCache->lock );

    if( pEntry->key != key &&
        (pEntry->hits < pEntry->misses) )
    {
        pEntry->key = key;
        pEntry->data = data;
        pEntry->hits = 0L;
        pEntry->misses = 0L;
        bUpdated = TRUE;
    }

    NdisReleaseSpinLock( &pCache->lock );

    return bUpdated;
}