/*
* PROJECT:         ReactOS Kernel
* LICENSE:         GPL - See COPYING in the top level directory
* FILE:            ntoskrnl/cm/cm_x.h
* PURPOSE:         Inlined Functions for the Configuration Manager
* PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
*/

//
// Returns whether or not this is a small valued key
//
FORCEINLINE
BOOLEAN
CmpIsKeyValueSmall(OUT PULONG RealLength,
                   IN ULONG Length)
{
    /* Check if the length has the special size value */
    if (Length >= CM_KEY_VALUE_SPECIAL_SIZE)
    {
        /* It does, so this is a small key: return the real length */
        *RealLength = Length - CM_KEY_VALUE_SPECIAL_SIZE;
        return TRUE;
    }

    /* This is not a small key, return the length we read */
    *RealLength = Length;
    return FALSE;
}

//
// Returns whether or not this is a big valued key
//
FORCEINLINE
BOOLEAN
CmpIsKeyValueBig(IN PHHIVE Hive,
                 IN ULONG Length)
{
    /* Check if the hive is XP Beta 1 or newer */
    if (Hive->Version >= HSYS_WHISTLER_BETA1)
    {
        /* Check if the key length is valid for a big value key */
        if ((Length < CM_KEY_VALUE_SPECIAL_SIZE) && (Length > CM_KEY_VALUE_BIG))
        {
            /* Yes, this value is big */
            return TRUE;
        }
    }

    /* Not a big value key */
    return FALSE;
}

//
// Returns the hashkey corresponding to a convkey
//
#define GET_HASH_KEY(ConvKey)                                       \
    ((CMP_HASH_IRRATIONAL * (ConvKey)) % CMP_HASH_PRIME)

//
// Returns the index into the hash table, or the entry itself
//
#define GET_HASH_INDEX(ConvKey)                                     \
    GET_HASH_KEY(ConvKey) % CmpHashTableSize
#define GET_HASH_ENTRY(Table, ConvKey)                              \
    (Table[GET_HASH_INDEX(ConvKey)])
#define ASSERT_VALID_HASH(h)                                        \
    ASSERT_KCB_VALID(CONTAINING_RECORD((h), CM_KEY_CONTROL_BLOCK, KeyHash))

//
// Returns whether or not the cell is cached
//
#define CMP_IS_CELL_CACHED(c)                                       \
    (((c) & HCELL_CACHED) && ((c) != HCELL_NIL))

//
// Return data from a cached cell
//
#define CMP_GET_CACHED_CELL(c)                                      \
    (ULONG_PTR)((c) & ~HCELL_CACHED)
#define CMP_GET_CACHED_DATA(c)                                      \
    (&(((PCM_CACHED_VALUE_INDEX)(CMP_GET_CACHED_CELL(c)))->Data.CellData))
#define CMP_GET_CACHED_INDEX(c)                                     \
    (&(((PCM_CACHED_ENTRY)(CMP_GET_CACHED_CELL(c)))->CellIndex))
#define CMP_GET_CACHED_VALUE(c)                                     \
    (&(((PCM_CACHED_VALUE)(CMP_GET_CACHED_CELL(c)))->KeyValue))

//
// Makes sure that the registry is locked
//
#define CMP_ASSERT_REGISTRY_LOCK()                                  \
    ASSERT((CmpSpecialBootCondition == TRUE) ||                     \
           (CmpTestRegistryLock() == TRUE))

//
// Makes sure that the registry is locked or loading
//
#define CMP_ASSERT_REGISTRY_LOCK_OR_LOADING(h)                      \
    ASSERT((CmpSpecialBootCondition == TRUE) ||                     \
           (((PCMHIVE)h)->HiveIsLoading == TRUE) ||                 \
           (CmpTestRegistryLock() == TRUE))

//
// Makes sure that the registry is exclusively locked
//
#define CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK()                        \
    ASSERT((CmpSpecialBootCondition == TRUE) ||                     \
           (CmpTestRegistryLockExclusive() == TRUE))

//
// Makes sure that the registry is exclusively locked or loading
//
#define CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(h)            \
    ASSERT((CmpSpecialBootCondition == TRUE) ||                     \
           (((PCMHIVE)h)->HiveIsLoading == TRUE) ||                 \
           (CmpTestRegistryLockExclusive() == TRUE))

//
// Makes sure this is a valid KCB
//
#define ASSERT_KCB_VALID(k)                                         \
    ASSERT((k)->Signature == CM_KCB_SIGNATURE)

//
// Checks if a KCB is exclusively locked
//
#define CmpIsKcbLockedExclusive(k)                                  \
    (GET_HASH_ENTRY(CmpCacheTable,                                  \
                    (k)->ConvKey).Owner == KeGetCurrentThread())

//
// Exclusively acquires a KCB
//
#define CmpAcquireKcbLockExclusive(k)                               \
{                                                                   \
    ExAcquirePushLockExclusive(&GET_HASH_ENTRY(CmpCacheTable,       \
                                              (k)->ConvKey).Lock);  \
    GET_HASH_ENTRY(CmpCacheTable,                                   \
                   (k)->ConvKey).Owner = KeGetCurrentThread();      \
}

//
// Exclusively acquires a KCB by index
//
#define CmpAcquireKcbLockExclusiveByIndex(i)                        \
{                                                                   \
    ExAcquirePushLockExclusive(&CmpCacheTable[(i)].Lock);           \
    CmpCacheTable[(i)].Owner = KeGetCurrentThread();                \
}

//
// Exclusively acquires a KCB by key
//
#define CmpAcquireKcbLockExclusiveByKey(k)                          \
{                                                                   \
    ExAcquirePushLockExclusive(&GET_HASH_ENTRY(CmpCacheTable,       \
                                              (k)).Lock);           \
    GET_HASH_ENTRY(CmpCacheTable,                                   \
                   (k)).Owner = KeGetCurrentThread();               \
}


//
// Shared acquires a KCB
//
#define CmpAcquireKcbLockShared(k)                                  \
{                                                                   \
    ExAcquirePushLockShared(&GET_HASH_ENTRY(CmpCacheTable,          \
                                            (k)->ConvKey).Lock);    \
}

//
// Shared acquires a KCB by index
//
#define CmpAcquireKcbLockSharedByIndex(i)                           \
{                                                                   \
    ExAcquirePushLockShared(&CmpCacheTable[(i)].Lock);              \
}

//
// Tries to convert a KCB lock
//
FORCEINLINE
BOOLEAN
CmpTryToConvertKcbSharedToExclusive(IN PCM_KEY_CONTROL_BLOCK k)
{
    ASSERT(CmpIsKcbLockedExclusive(k) == FALSE);
    if (ExConvertPushLockSharedToExclusive(
            &GET_HASH_ENTRY(CmpCacheTable, k->ConvKey).Lock))
    {
        GET_HASH_ENTRY(CmpCacheTable,
                       k->ConvKey).Owner = KeGetCurrentThread();
        return TRUE;
    }
    return FALSE;
}

//
// Releases an exlusively or shared acquired KCB
//
#define CmpReleaseKcbLock(k)                                        \
{                                                                   \
    GET_HASH_ENTRY(CmpCacheTable, (k)->ConvKey).Owner = NULL;       \
    ExReleasePushLock(&GET_HASH_ENTRY(CmpCacheTable,                \
                                      (k)->ConvKey).Lock);          \
}

//
// Releases an exlusively or shared acquired KCB by index
//
#define CmpReleaseKcbLockByIndex(i)                                 \
{                                                                   \
    CmpCacheTable[(i)].Owner = NULL;                                \
    ExReleasePushLock(&CmpCacheTable[(i)].Lock);                    \
}

//
// Releases an exlusively or shared acquired KCB by key
//
#define CmpReleaseKcbLockByKey(k)                                   \
{                                                                   \
    GET_HASH_ENTRY(CmpCacheTable, (k)).Owner = NULL;                \
    ExReleasePushLock(&GET_HASH_ENTRY(CmpCacheTable,                \
                                      (k)).Lock);                   \
}

//
// Converts a KCB lock
//
FORCEINLINE
VOID
CmpConvertKcbSharedToExclusive(IN PCM_KEY_CONTROL_BLOCK k)
{
    ASSERT(CmpIsKcbLockedExclusive(k) == FALSE);  
    CmpReleaseKcbLock(k);
    CmpAcquireKcbLockExclusive(k);
}

//
// Exclusively acquires an NCB
//
#define CmpAcquireNcbLockExclusive(n)                               \
{                                                                   \
    ExAcquirePushLockExclusive(&GET_HASH_ENTRY(CmpNameCacheTable,   \
                                              (n)->ConvKey).Lock);  \
}

//
// Exclusively acquires an NCB by key
//
#define CmpAcquireNcbLockExclusiveByKey(k)                          \
{                                                                   \
    ExAcquirePushLockExclusive(&GET_HASH_ENTRY(CmpNameCacheTable,   \
                                               (k)).Lock);          \
}

//
// Releases an exlusively or shared acquired NCB
//
#define CmpReleaseNcbLock(k)                                        \
{                                                                   \
    ExReleasePushLock(&GET_HASH_ENTRY(CmpNameCacheTable,            \
                                     (k)->ConvKey).Lock);           \
}

//
// Releases an exlusively or shared acquired NCB by key
//
#define CmpReleaseNcbLockByKey(k)                                   \
{                                                                   \
    ExReleasePushLock(&GET_HASH_ENTRY(CmpNameCacheTable,            \
                                      (k)).Lock);                   \
}

//
// Asserts that either the registry or the hash entry is locked
//
#define CMP_ASSERT_HASH_ENTRY_LOCK(k)                               \
{                                                                   \
    ASSERT(((GET_HASH_ENTRY(CmpCacheTable, k).Owner ==              \
            KeGetCurrentThread())) ||                               \
           (CmpTestRegistryLockExclusive() == TRUE));               \
}

//
// Asserts that either the registry or the KCB is locked
//
#define CMP_ASSERT_KCB_LOCK(k)                                      \
{                                                                   \
    ASSERT((CmpIsKcbLockedExclusive(k) == TRUE) ||                  \
           (CmpTestRegistryLockExclusive() == TRUE));               \
}

//
// Gets the page attached to the KCB
//
#define CmpGetAllocPageFromKcb(k)                                   \
    (PCM_ALLOC_PAGE)(((ULONG_PTR)(k)) & ~(PAGE_SIZE - 1))

//
// Gets the page attached to the delayed allocation
//
#define CmpGetAllocPageFromDelayAlloc(a)                            \
    (PCM_ALLOC_PAGE)(((ULONG_PTR)(a)) & ~(PAGE_SIZE - 1))

//
// Makes sure that the registry is locked for flushes
//
#define CMP_ASSERT_FLUSH_LOCK(h)                                    \
    ASSERT((CmpSpecialBootCondition == TRUE) ||                     \
           (((PCMHIVE)h)->HiveIsLoading == TRUE) ||                 \
           (CmpTestHiveFlusherLockShared((PCMHIVE)h) == TRUE) ||    \
           (CmpTestHiveFlusherLockExclusive((PCMHIVE)h) == TRUE) || \
           (CmpTestRegistryLockExclusive() == TRUE));

//
// Asserts that either the registry or the KCB is locked
//
#define CMP_ASSERT_HASH_ENTRY_LOCK(k)                               \
{                                                                   \
    ASSERT(((GET_HASH_ENTRY(CmpCacheTable, k).Owner ==              \
            KeGetCurrentThread())) ||                               \
           (CmpTestRegistryLockExclusive() == TRUE));               \
}

//
// Gets the page attached to the KCB
//
#define CmpGetAllocPageFromKcb(k)                                   \
    (PCM_ALLOC_PAGE)(((ULONG_PTR)(k)) & ~(PAGE_SIZE - 1))

//
// Gets the page attached to the delayed allocation
//
#define CmpGetAllocPageFromDelayAlloc(a)                            \
    (PCM_ALLOC_PAGE)(((ULONG_PTR)(a)) & ~(PAGE_SIZE - 1))
