/*
 * PROJECT:         ReactOS Registry Library
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            lib/cmlib/cmindex.c
 * PURPOSE:         Configuration Manager - Cell Indexes
 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
 */

/* INCLUDES ******************************************************************/

#include "cmlib.h"
#define NDEBUG
#include <debug.h>

/* GLOBALS *******************************************************************/

#define INVALID_INDEX   0x80000000

#define CmpMaxFastIndexPerHblock                        \
    ((HBLOCK_SIZE - (sizeof(HBIN) + sizeof(HCELL) +     \
                     FIELD_OFFSET(CM_KEY_FAST_INDEX, List))) / sizeof(CM_INDEX))

#define CmpMaxIndexPerHblock                            \
    ((HBLOCK_SIZE - (sizeof(HBIN) + sizeof(HCELL) +     \
                     FIELD_OFFSET(CM_KEY_INDEX, List))) / sizeof(HCELL_INDEX) - 1)

/* FUNCTIONS *****************************************************************/

LONG
NTAPI
CmpDoCompareKeyName(IN PHHIVE Hive,
                    IN PCUNICODE_STRING SearchName,
                    IN HCELL_INDEX Cell)
{
    PCM_KEY_NODE Node;
    UNICODE_STRING KeyName;
    LONG Result;

    /* Get the node */
    Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
    if (!Node) return 2;

    /* Check if it's compressed */
    if (Node->Flags & KEY_COMP_NAME)
    {
        /* Compare compressed names */
        Result = CmpCompareCompressedName(SearchName,
                                          Node->Name,
                                          Node->NameLength);
    }
    else
    {
        /* Compare the Unicode name directly */
        KeyName.Buffer = Node->Name;
        KeyName.Length = Node->NameLength;
        KeyName.MaximumLength = KeyName.Length;
        Result = RtlCompareUnicodeString(SearchName, &KeyName, TRUE);
    }

    /* Release the cell and return the normalized result */
    HvReleaseCell(Hive, Cell);
    return (Result == 0) ? Result : ((Result > 0) ? 1 : -1);
}

LONG
NTAPI
CmpCompareInIndex(IN PHHIVE Hive,
                  IN PCUNICODE_STRING SearchName,
                  IN ULONG Count,
                  IN PCM_KEY_INDEX Index,
                  IN PHCELL_INDEX SubKey)
{
    PCM_KEY_FAST_INDEX FastIndex;
    PCM_INDEX FastEntry;
    LONG Result;
    ULONG i;
    ULONG ActualNameLength = 4, CompareLength, NameLength;
    WCHAR p, pp;

    /* Assume failure */
    *SubKey = HCELL_NIL;

    /* Check if we are a fast or hashed leaf */
    if ((Index->Signature == CM_KEY_FAST_LEAF) ||
        (Index->Signature == CM_KEY_HASH_LEAF))
    {
        /* Get the Fast/Hash Index */
        FastIndex = (PCM_KEY_FAST_INDEX)Index;
        FastEntry = &FastIndex->List[Count];

        /* Check if we are a hash leaf, in which case we skip all this */
        if (Index->Signature == CM_KEY_FAST_LEAF)
        {
            /* Find out just how much of the name is there */
            for (i = 0; i < 4; i++)
            {
                /* Check if this entry is empty */
                if (!FastEntry->NameHint[i])
                {
                    /* Only this much! */
                    ActualNameLength = i;
                    break;
                }
            }

            /* How large is the name and how many characters to compare */
            NameLength = SearchName->Length / sizeof(WCHAR);
            CompareLength = min(NameLength, ActualNameLength);

            /* Loop all the chars we'll test */
            for (i = 0; i < CompareLength; i++)
            {
                /* Get one char from each buffer */
                p = SearchName->Buffer[i];
                pp = FastEntry->NameHint[i];

                /* See if they match and return result if they don't */
                Result = (LONG)RtlUpcaseUnicodeChar(p) -
                         (LONG)RtlUpcaseUnicodeChar(pp);
                if (Result) return (Result > 0) ? 1 : -1;
            }
        }

        /* If we got here then we have to do a full compare */
        Result = CmpDoCompareKeyName(Hive, SearchName, FastEntry->Cell);
        if (Result == 2) return Result;
        if (!Result) *SubKey = FastEntry->Cell;
    }
    else
    {
        /* We aren't, so do a name compare and return the subkey found */
        Result = CmpDoCompareKeyName(Hive, SearchName, Index->List[Count]);
        if (Result == 2) return Result;
        if (!Result) *SubKey = Index->List[Count];
    }

    /* Return the comparison result */
    return Result;
}

ULONG
NTAPI
CmpFindSubKeyInRoot(IN PHHIVE Hive,
                    IN PCM_KEY_INDEX Index,
                    IN PCUNICODE_STRING SearchName,
                    IN PHCELL_INDEX SubKey)
{
    ULONG High, Low = 0, i = 0, ReturnIndex;
    HCELL_INDEX LeafCell;
    PCM_KEY_INDEX Leaf;
    LONG Result;

    /* Verify Index for validity */
    ASSERT(Index->Count != 0);
    ASSERT(Index->Signature == CM_KEY_INDEX_ROOT);

    /* Set high limit and loop */
    High = Index->Count - 1;
    while (TRUE)
    {
        /* Choose next entry */
        i = ((High - Low) / 2) + Low;

        /* Get the leaf cell and then the leaf itself */
        LeafCell = Index->List[i];
        Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
        if (Leaf)
        {
            /* Make sure the leaf is valid */
            ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) ||
                   (Leaf->Signature == CM_KEY_FAST_LEAF) ||
                   (Leaf->Signature == CM_KEY_HASH_LEAF));
            ASSERT(Leaf->Count != 0);

            /* Do the compare */
            Result = CmpCompareInIndex(Hive,
                                       SearchName,
                                       Leaf->Count - 1,
                                       Leaf,
                                       SubKey);
            if (Result == 2) goto Big;

            /* Check if we found the leaf */
            if (!Result)
            {
                /* We found the leaf */
                *SubKey = LeafCell;
                ReturnIndex = i;
                goto Return;
            }

            /* Check for negative result */
            if (Result < 0)
            {
                /* If we got here, we should be at -1 */
                ASSERT(Result == -1);

                /* Do another lookup, since we might still be in the right leaf */
                Result = CmpCompareInIndex(Hive,
                                           SearchName,
                                           0,
                                           Leaf,
                                           SubKey);
                if (Result == 2) goto Big;

                /* Check if it's not below */
                if (Result >= 0)
                {
                    /*
                     * If the name was first below, and now it is above,
                     * then this means that it is somewhere in this leaf.
                     * Make sure we didn't get some weird result
                     */
                    ASSERT((Result == 1) || (Result == 0));

                    /* Return it */
                    *SubKey = LeafCell;
                    ReturnIndex = i;
                    goto Return;
                }

                /* Update the limit to this index, since we know it's not higher. */
                High = i;
            }
            else
            {
                /* Update the base to this index, since we know it's not lower. */
                Low = i;
            }
        }
        else
        {
Big:
            /* This was some sort of special key */
            ReturnIndex = INVALID_INDEX;
            goto ReturnFailure;
        }

        /* Check if there is only one entry left */
        if ((High - Low) <= 1) break;

        /* Release the leaf cell */
        HvReleaseCell(Hive, LeafCell);
    }

    /* Make sure we got here for the right reasons */
    ASSERT((High - Low == 1) || (High == Low));

    /* Get the leaf cell and the leaf */
    LeafCell = Index->List[Low];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if (!Leaf) goto Big;

    /* Do the compare */
    Result = CmpCompareInIndex(Hive,
                               SearchName,
                               Leaf->Count-1,
                               Leaf,
                               SubKey);
    if (Result == 2) goto Big;

    /* Check if we found it */
    if (!Result)
    {
        /* We got lucky... return it */
        *SubKey = LeafCell;
        ReturnIndex = Low;
        goto Return;
    }

    /* It's below, so could still be in this leaf */
    if (Result < 0)
    {
        /* Make sure we're -1 */
        ASSERT(Result == -1);

        /* Do a search from the bottom */
        Result = CmpCompareInIndex(Hive, SearchName, 0, Leaf, SubKey);
        if (Result == 2) goto Big;

        /*
         * Check if it's above, which means that it's within the ranges of our
         * leaf (since we were below before).
         */
        if (Result >= 0)
        {
            /* Sanity check */
            ASSERT((Result == 1) || (Result == 0));

            /* Yep, so we're in the right leaf; return it. */
            *SubKey = LeafCell;
            ReturnIndex = Low;
            goto Return;
        }

        /* It's still below us, so fail */
        ReturnIndex = Low;
        goto ReturnFailure;
    }

    /* Release the leaf cell */
    HvReleaseCell(Hive, LeafCell);

    /* Well the low didn't work too well, so try the high. */
    LeafCell = Index->List[High];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if (!Leaf) goto Big;

    /* Do the compare */
    Result = CmpCompareInIndex(Hive,
                               SearchName,
                               Leaf->Count - 1,
                               Leaf,
                               SubKey);
    if (Result == 2) goto Big;

    /* Check if we found it */
    if (Result == 0)
    {
        /* We got lucky... return it */
        *SubKey = LeafCell;
        ReturnIndex = High;
        goto Return;
    }

    /* Check if we are too high */
    if (Result < 0)
    {
        /* Make sure we're -1 */
        ASSERT(Result == -1);

        /*
         * Once again... since we were first too low and now too high, then
         * this means we are within the range of this leaf... return it.
         */
        *SubKey = LeafCell;
        ReturnIndex = High;
        goto Return;
    }

    /* If we got here, then we are too low, again. */
    ReturnIndex = High;

    /* Failure path */
ReturnFailure:
    *SubKey = HCELL_NIL;

    /* Return path...check if we have a leaf to free */
Return:
    if (Leaf) HvReleaseCell(Hive, LeafCell);

    /* Return the index */
    return ReturnIndex;
}

ULONG
NTAPI
CmpFindSubKeyInLeaf(IN PHHIVE Hive,
                    IN PCM_KEY_INDEX Index,
                    IN PCUNICODE_STRING SearchName,
                    IN PHCELL_INDEX SubKey)
{
    ULONG High, Low = 0, i;
    LONG Result;

    /* Verify Index for validity */
    ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
           (Index->Signature == CM_KEY_FAST_LEAF) ||
           (Index->Signature == CM_KEY_HASH_LEAF));

    /* Get the upper bound and middle entry */
    High = Index->Count - 1;
    i = High / 2;

    /* Check if we don't actually have any entries */
    if (!Index->Count)
    {
        /* Return failure */
        *SubKey = HCELL_NIL;
        return 0;
    }

    /* Start compare loop */
    while (TRUE)
    {
        /* Do the actual comparison and check the result */
        Result = CmpCompareInIndex(Hive, SearchName, i, Index, SubKey);
        if (Result == 2)
        {
            /* Fail with special value */
            *SubKey = HCELL_NIL;
            return INVALID_INDEX;
        }

        /* Check if we got lucky and found it */
        if (!Result) return i;

        /* Check if the result is below us */
        if (Result < 0)
        {
            /* Set the new bound; it can't be higher then where we are now. */
            ASSERT(Result == -1);
            High = i;
        }
        else
        {
            /* Set the new bound... it can't be lower then where we are now. */
            ASSERT(Result == 1);
            Low = i;
        }

        /* Check if this is the last entry, if so, break out and handle it */
        if ((High - Low) <= 1) break;

        /* Set the new index */
        i = ((High - Low) / 2) + Low;
    }

    /*
     * If we get here, High - Low = 1 or High == Low
     * Simply look first at Low, then at High
     */
    Result = CmpCompareInIndex(Hive, SearchName, Low, Index, SubKey);
    if (Result == 2)
    {
        /* Fail with special value */
        *SubKey = HCELL_NIL;
        return INVALID_INDEX;
    }

    /* Check if we got lucky and found it */
    if (!Result) return Low;

    /* Check if the result is below us */
    if (Result < 0)
    {
        /* Return the low entry */
        ASSERT(Result == -1);
        return Low;
    }

    /*
     * If we got here, then just check the high and return it no matter what
     * the result is (since we're a leaf, it has to be near there anyway).
     */
    Result = CmpCompareInIndex(Hive, SearchName, High, Index, SubKey);
    if (Result == 2)
    {
        /* Fail with special value */
        *SubKey = HCELL_NIL;
        return INVALID_INDEX;
    }

    /* Return the high */
    return High;
}

ULONG
NTAPI
CmpComputeHashKey(IN ULONG Hash,
                  IN PCUNICODE_STRING Name,
                  IN BOOLEAN AllowSeparators)
{
    LPWSTR Cp;
    ULONG Value, i;

    /* Make some sanity checks on our parameters */
    ASSERT((Name->Length == 0) ||
           (AllowSeparators) ||
           (Name->Buffer[0] != OBJ_NAME_PATH_SEPARATOR));

    /* If the name is empty, there is nothing to hash! */
    if (!Name->Length) return Hash;

    /* Set the buffer and loop every character */
    Cp = Name->Buffer;
    for (i = 0; i < Name->Length; i += sizeof(WCHAR), Cp++)
    {
        /* Make sure we don't have a separator when we shouldn't */
        ASSERT(AllowSeparators || (*Cp != OBJ_NAME_PATH_SEPARATOR));

        /* Check what kind of char we have */
        if (*Cp >= L'a')
        {
            /* In the lower case region... is it truly lower case? */
            if (*Cp < L'z')
            {
                /* Yes! Calculate it ourselves! */
                Value = *Cp - L'a' + L'A';
            }
            else
            {
                /* No, use the API */
                Value = RtlUpcaseUnicodeChar(*Cp);
            }
        }
        else
        {
            /* Reuse the char, it's already upcased */
            Value = *Cp;
        }

        /* Multiply by a prime and add our value */
        Hash *= 37;
        Hash += Value;
    }

    /* Return the hash */
    return Hash;
}

HCELL_INDEX
NTAPI
CmpDoFindSubKeyByNumber(IN PHHIVE Hive,
                        IN PCM_KEY_INDEX Index,
                        IN ULONG Number)
{
    ULONG i;
    HCELL_INDEX LeafCell = 0;
    PCM_KEY_INDEX Leaf = NULL;
    PCM_KEY_FAST_INDEX FastIndex;
    HCELL_INDEX Result;

    /* Check if this is a root */
    if (Index->Signature == CM_KEY_INDEX_ROOT)
    {
        /* Loop the index */
        for (i = 0; i < Index->Count; i++)
        {
            /* Check if this isn't the first iteration */
            if (i)
            {
                /* Make sure we had something valid, and release it */
                ASSERT(Leaf != NULL );
                ASSERT(LeafCell == Index->List[i - 1]);
                HvReleaseCell(Hive, LeafCell);
            }

            /* Get the leaf cell and the leaf for this index */
            LeafCell = Index->List[i];
            Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
            if (!Leaf) return HCELL_NIL;

            /* Check if the index may be inside it */
            if (Number < Leaf->Count)
            {
                /* Check if this is a fast or hash leaf */
                if ((Leaf->Signature == CM_KEY_FAST_LEAF) ||
                    (Leaf->Signature == CM_KEY_HASH_LEAF))
                {
                    /* Get the fast index */
                    FastIndex = (PCM_KEY_FAST_INDEX)Leaf;

                    /* Look inside the list to get our actual cell */
                    Result = FastIndex->List[Number].Cell;
                    HvReleaseCell(Hive, LeafCell);
                    return Result;
                }
                else
                {
                    /* Regular leaf, so just use the index directly */
                    Result = Leaf->List[Number];

                    /*  Release and return it */
                    HvReleaseCell(Hive,LeafCell);
                    return Result;
                }
            }
            else
            {
                /* Otherwise, skip this leaf */
                Number = Number - Leaf->Count;
            }
        }

        /* Should never get here */
        ASSERT(FALSE);
    }

    /* If we got here, then the cell is in this index */
    ASSERT(Number < Index->Count);

    /* Check if we're a fast or hash leaf */
    if ((Index->Signature == CM_KEY_FAST_LEAF) ||
        (Index->Signature == CM_KEY_HASH_LEAF))
    {
        /* We are, get the fast index and get the cell from the list */
        FastIndex = (PCM_KEY_FAST_INDEX)Index;
        return FastIndex->List[Number].Cell;
    }
    else
    {
        /* We aren't, so use the index directly to grab the cell */
        return Index->List[Number];
    }
}

HCELL_INDEX
NTAPI
CmpFindSubKeyByNumber(IN PHHIVE Hive,
                      IN PCM_KEY_NODE Node,
                      IN ULONG Number)
{
    PCM_KEY_INDEX Index;
    HCELL_INDEX Result = HCELL_NIL;

    /* Check if it's in the stable list */
    if (Number < Node->SubKeyCounts[Stable])
    {
        /* Get the actual key index */
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Stable]);
        if (!Index) return HCELL_NIL;

        /* Do a search inside it */
        Result = CmpDoFindSubKeyByNumber(Hive, Index, Number);

        /* Release the cell and return the result */
        HvReleaseCell(Hive, Node->SubKeyLists[Stable]);
        return Result;
    }
    else if (Hive->StorageTypeCount > Volatile)
    {
        /* It's in the volatile list */
        Number = Number - Node->SubKeyCounts[Stable];
        if (Number < Node->SubKeyCounts[Volatile])
        {
            /* Get the actual key index */
            Index = (PCM_KEY_INDEX)HvGetCell(Hive, Node->SubKeyLists[Volatile]);
            if (!Index) return HCELL_NIL;

            /* Do a search inside it */
            Result = CmpDoFindSubKeyByNumber(Hive, Index, Number);

            /* Release the cell and return the result */
            HvReleaseCell(Hive, Node->SubKeyLists[Volatile]);
            return Result;
        }
    }

    /* Nothing was found */
    return HCELL_NIL;
}

static HCELL_INDEX
NTAPI
CmpFindSubKeyByHash(IN PHHIVE Hive,
                    IN PCM_KEY_FAST_INDEX FastIndex,
                    IN PCUNICODE_STRING SearchName)
{
    ULONG HashKey, i;
    PCM_INDEX FastEntry;

    /* Make sure it's really a hash */
    ASSERT(FastIndex->Signature == CM_KEY_HASH_LEAF);

    /* Compute the hash key for the name */
    HashKey = CmpComputeHashKey(0, SearchName, FALSE);

    /* Loop all the entries */
    for (i = 0; i < FastIndex->Count; i++)
    {
        /* Get the entry */
        FastEntry = &FastIndex->List[i];

        /* Compare the hash first */
        if (FastEntry->HashKey == HashKey)
        {
            /* Go ahead for a full compare */
            if (!(CmpDoCompareKeyName(Hive, SearchName, FastEntry->Cell)))
            {
                /* It matched, return the cell */
                return FastEntry->Cell;
            }
        }
    }

    /* If we got here then we failed */
    return HCELL_NIL;
}

HCELL_INDEX
NTAPI
CmpFindSubKeyByName(IN PHHIVE Hive,
                    IN PCM_KEY_NODE Parent,
                    IN PCUNICODE_STRING SearchName)
{
    ULONG i;
    PCM_KEY_INDEX IndexRoot;
    HCELL_INDEX SubKey, CellToRelease;
    ULONG Found;

    /* Loop each storage type */
    for (i = 0; i < Hive->StorageTypeCount; i++)
    {
        /* Make sure the parent node has subkeys */
        if (Parent->SubKeyCounts[i])
        {
            /* Get the Index */
            IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, Parent->SubKeyLists[i]);
            if (!IndexRoot) return HCELL_NIL;

            /* Get the cell we'll need to release */
            CellToRelease = Parent->SubKeyLists[i];

            /* Check if this is another index root */
            if (IndexRoot->Signature == CM_KEY_INDEX_ROOT)
            {
                /* Lookup the name in the root */
                Found = CmpFindSubKeyInRoot(Hive,
                                            IndexRoot,
                                            SearchName,
                                            &SubKey);

                /* Release the previous cell */
                ASSERT(CellToRelease != HCELL_NIL);
                HvReleaseCell(Hive, CellToRelease);

                /* Make sure we found something valid */
                if (Found & INVALID_INDEX) break;

                /* Get the new Index Root and set the new cell to be released */
                if (SubKey == HCELL_NIL) continue;
                CellToRelease = SubKey;
                IndexRoot = (PCM_KEY_INDEX)HvGetCell(Hive, SubKey);
            }

            /* Make sure the signature is what we expect it to be */
            ASSERT((IndexRoot->Signature == CM_KEY_INDEX_LEAF) ||
                   (IndexRoot->Signature == CM_KEY_FAST_LEAF) ||
                   (IndexRoot->Signature == CM_KEY_HASH_LEAF));

            /* Check if this isn't a hashed leaf */
            if (IndexRoot->Signature != CM_KEY_HASH_LEAF)
            {
                /* Find the subkey in the leaf */
                Found = CmpFindSubKeyInLeaf(Hive,
                                            IndexRoot,
                                            SearchName,
                                            &SubKey);

                /* Release the previous cell */
                ASSERT(CellToRelease != HCELL_NIL);
                HvReleaseCell(Hive, CellToRelease);

                /* Make sure we found a valid index */
                if (Found & INVALID_INDEX) break;
            }
            else
            {
                /* Find the subkey in the hash */
                SubKey = CmpFindSubKeyByHash(Hive,
                                             (PCM_KEY_FAST_INDEX)IndexRoot,
                                             SearchName);

                /* Release the previous cell */
                ASSERT(CellToRelease != HCELL_NIL);
                HvReleaseCell(Hive, CellToRelease);
            }

            /* Make sure we got a valid subkey and return it */
            if (SubKey != HCELL_NIL) return SubKey;
        }
    }

    /* If we got here, then we failed */
    return HCELL_NIL;
}

BOOLEAN
NTAPI
CmpMarkIndexDirty(IN PHHIVE Hive,
                  IN HCELL_INDEX ParentKey,
                  IN HCELL_INDEX TargetKey)
{
    PCM_KEY_NODE Node;
    UNICODE_STRING SearchName;
    BOOLEAN IsCompressed;
    ULONG i, Result;
    PCM_KEY_INDEX Index;
    HCELL_INDEX IndexCell, Child = HCELL_NIL, CellToRelease = HCELL_NIL;

    /* Get the target key node */
    Node = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
    if (!Node) return FALSE;

    /* Check if it's compressed */
    if (Node->Flags & KEY_COMP_NAME)
    {
        /* Remember this for later */
        IsCompressed = TRUE;

        /* Build the search name */
        SearchName.Length = CmpCompressedNameSize(Node->Name,
                                                  Node->NameLength);
        SearchName.MaximumLength = SearchName.Length;
        SearchName.Buffer = Hive->Allocate(SearchName.Length, TRUE, TAG_CM);
        if (!SearchName.Buffer)
        {
            /* Fail */
            HvReleaseCell(Hive, TargetKey);
            return FALSE;
        }

        /* Copy it */
        CmpCopyCompressedName(SearchName.Buffer,
                              SearchName.MaximumLength,
                              Node->Name,
                              Node->NameLength);
    }
    else
    {
        /* Name isn't compressed, build it directly from the node */
        IsCompressed = FALSE;
        SearchName.Length = Node->NameLength;
        SearchName.MaximumLength = Node->NameLength;
        SearchName.Buffer = Node->Name;
    }

    /* We can release the target key now */
    HvReleaseCell(Hive, TargetKey);

    /* Now get the parent key node */
    Node = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
    if (!Node) goto Quickie;

    /* Loop all hive storage */
    for (i = 0; i < Hive->StorageTypeCount; i++)
    {
        /* Check if any subkeys are in this index */
        if (Node->SubKeyCounts[i])
        {
            /* Get the cell index */
            //ASSERT(HvIsCellAllocated(Hive, Node->SubKeyLists[i]));
            IndexCell = Node->SubKeyLists[i];

            /* Check if we had anything to release from before */
            if (CellToRelease != HCELL_NIL)
            {
                /* Release it now */
                HvReleaseCell(Hive, CellToRelease);
                CellToRelease = HCELL_NIL;
            }

            /* Get the key index for the cell */
            Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
            if (!Index) goto Quickie;

            /* Release it at the next iteration or below */
            CellToRelease = IndexCell;

            /* Check if this is a root */
            if (Index->Signature == CM_KEY_INDEX_ROOT)
            {
                /* Get the child inside the root */
                Result = CmpFindSubKeyInRoot(Hive, Index, &SearchName, &Child);
                if (Result & INVALID_INDEX) goto Quickie;
                if (Child == HCELL_NIL) continue;

                /* We found it, mark the cell dirty */
                HvMarkCellDirty(Hive, IndexCell, FALSE);

                /* Check if we had anything to release from before */
                if (CellToRelease != HCELL_NIL)
                {
                    /* Release it now */
                    HvReleaseCell(Hive, CellToRelease);
                    CellToRelease = HCELL_NIL;
                }

                /* Now this child is the index, get the actual key index */
                IndexCell = Child;
                Index = (PCM_KEY_INDEX)HvGetCell(Hive, Child);
                if (!Index) goto Quickie;

                /* Release it later */
                CellToRelease = Child;
            }

            /* Make sure this is a valid index */
            ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
                   (Index->Signature == CM_KEY_FAST_LEAF) ||
                   (Index->Signature == CM_KEY_HASH_LEAF));

            /* Find the child in the leaf */
            Result = CmpFindSubKeyInLeaf(Hive, Index, &SearchName, &Child);
            if (Result & INVALID_INDEX) goto Quickie;
            if (Child != HCELL_NIL)
            {
                /* We found it, free the name now */
                if (IsCompressed) Hive->Free(SearchName.Buffer, 0);

                /* Release the parent key */
                HvReleaseCell(Hive, ParentKey);

                /* Check if we had a left over cell to release */
                if (CellToRelease != HCELL_NIL)
                {
                    /* Release it */
                    HvReleaseCell(Hive, CellToRelease);
                }

                /* And mark the index cell dirty */
                HvMarkCellDirty(Hive, IndexCell, FALSE);
                return TRUE;
            }
        }
    }

Quickie:
    /* Release any cells that we still hold */
    if (Node) HvReleaseCell(Hive, ParentKey);
    if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);

    /* Free the search name and return failure */
    if (IsCompressed) Hive->Free(SearchName.Buffer, 0);
    return FALSE;
}

HCELL_INDEX
NTAPI
CmpAddToLeaf(IN PHHIVE Hive,
             IN HCELL_INDEX LeafCell,
             IN HCELL_INDEX NewKey,
             IN PCUNICODE_STRING Name)
{
    PCM_KEY_INDEX Leaf;
    PCM_KEY_FAST_INDEX FastLeaf;
    ULONG Size, OldSize, EntrySize, i, j;
    HCELL_INDEX NewCell, Child;
    LONG Result;

    /* Mark the leaf dirty */
    HvMarkCellDirty(Hive, LeafCell, FALSE);

    /* Get the leaf cell */
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if (!Leaf)
    {
        /* Shouldn't happen */
        ASSERT(FALSE);
        return HCELL_NIL;
    }

    /* Release it */
    HvReleaseCell(Hive, LeafCell);

    /* Check if this is an index leaf */
    if (Leaf->Signature == CM_KEY_INDEX_LEAF)
    {
        /* This is an old-style leaf */
        FastLeaf = NULL;
        EntrySize = sizeof(HCELL_INDEX);
    }
    else
    {
        /* Sanity check */
        ASSERT((Leaf->Signature == CM_KEY_FAST_LEAF) ||
               (Leaf->Signature == CM_KEY_HASH_LEAF));

        /* This is a new-style optimized fast (or hash) leaf */
        FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
        EntrySize = sizeof(CM_INDEX);
    }

    /* Get the current size of the leaf */
    OldSize = HvGetCellSize(Hive, Leaf);

    /* Calculate the size of the free entries */
    Size = OldSize;
    Size -= EntrySize * Leaf->Count + FIELD_OFFSET(CM_KEY_INDEX, List);

    /* Assume we'll re-use the same leaf */
    NewCell = LeafCell;

    /* Check if we're out of space */
    if ((Size / EntrySize) < 1)
    {
        /* Grow the leaf by 1.5x, making sure we can at least fit this entry */
        Size = OldSize + OldSize / 2;
        if (Size < (OldSize + EntrySize)) Size = OldSize + EntrySize;

        /* Re-allocate the leaf */
        NewCell = HvReallocateCell(Hive, LeafCell, Size);
        if (NewCell == HCELL_NIL) return HCELL_NIL;

        /* Get the leaf cell */
        Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, NewCell);
        if (!Leaf)
        {
            /* This shouldn't happen */
            ASSERT(FALSE);
            return HCELL_NIL;
        }

        /* Release the cell */
        HvReleaseCell(Hive, NewCell);

        /* Update the fast leaf pointer if we had one */
        if (FastLeaf) FastLeaf = (PCM_KEY_FAST_INDEX)Leaf;
    }

    /* Find the insertion point for our entry */
    i = CmpFindSubKeyInLeaf(Hive, Leaf, Name, &Child);
    if (i & INVALID_INDEX) return HCELL_NIL;
    ASSERT(Child == HCELL_NIL);

    /* Check if we're not last */
    if (i != Leaf->Count)
    {
        /* Find out where we should go */
        Result = CmpCompareInIndex(Hive,
                                   Name,
                                   i,
                                   Leaf,
                                   &Child);
        if (Result == 2) return HCELL_NIL;
        ASSERT(Result != 0);

        /* Check if we come after */
        if (Result > 0)
        {
            /* We do, insert us after the key */
            ASSERT(Result == 1);
            i++;
        }

        /* Check if we're still not last */
        if (i != Leaf->Count)
        {
            /* Check if we had a fast leaf or not */
            if (FastLeaf)
            {
                /* Copy the fast indexes */
                RtlMoveMemory(&FastLeaf->List[i + 1],
                              &FastLeaf->List[i],
                              (FastLeaf->Count - i) * sizeof(CM_INDEX));
            }
            else
            {
                /* Copy the indexes themselves */
                RtlMoveMemory(&Leaf->List[i + 1],
                              &Leaf->List[i],
                              (Leaf->Count - i) * sizeof(HCELL_INDEX));
            }
        }
    }

    /* Check if this is a new-style leaf */
    if (FastLeaf)
    {
        /* Set our cell */
        FastLeaf->List[i].Cell = NewKey;

        /* Check if this is a hash leaf */
        if( FastLeaf->Signature == CM_KEY_HASH_LEAF )
        {
            /* Set our hash key */
            FastLeaf->List[i].HashKey = CmpComputeHashKey(0, Name, FALSE);
        }
        else
        {
            /* First, clear the name */
            FastLeaf->List[i].NameHint[0] = 0;
            FastLeaf->List[i].NameHint[1] = 0;
            FastLeaf->List[i].NameHint[2] = 0;
            FastLeaf->List[i].NameHint[3] = 0;

            /* Now, figure out if we can fit */
            if (Name->Length / sizeof(WCHAR) < 4)
            {
                /* We can fit, use our length */
                j = Name->Length / sizeof(WCHAR);
            }
            else
            {
                /* We can't, use a maximum of 4 */
                j = 4;
            }

            /* Now fill out the name hint */
            do
            {
                /* Look for invalid characters and break out if we found one */
                if ((USHORT)Name->Buffer[j - 1] > (UCHAR)-1) break;

                /* Otherwise, copy the a character */
                FastLeaf->List[i].NameHint[j - 1] = (UCHAR)Name->Buffer[j - 1];
            } while (--j > 0);
        }
    }
    else
    {
        /* This is an old-style leaf, just set our index directly */
        Leaf->List[i] = NewKey;
    }

    /* Update the leaf count and return the new cell */
    Leaf->Count += 1;
    return NewCell;
}

HCELL_INDEX
NTAPI
CmpSplitLeaf(IN PHHIVE Hive,
             IN HCELL_INDEX RootCell,
             IN ULONG RootSelect,
             IN HSTORAGE_TYPE Type)
{
    PCM_KEY_INDEX IndexKey, LeafKey, NewKey;
    PCM_KEY_FAST_INDEX FastLeaf;
    HCELL_INDEX LeafCell, NewCell;
    USHORT FirstHalf, LastHalf;
    ULONG EntrySize, TotalSize;

    /* Get the cell */
    IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);

    /* Check if we've got valid IndexKey */
    if (!IndexKey) return HCELL_NIL;

    /* Get the leaf cell and key */
    LeafCell = IndexKey->List[RootSelect];
    LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

    /* Check if we've got valid LeafKey */
    if (!LeafKey) return HCELL_NIL;

    /* We are going to divide this leaf into two halves */
    FirstHalf = (LeafKey->Count / 2);
    LastHalf = LeafKey->Count - FirstHalf;

    /* Now check what kind of hive we're dealing with,
     * and compute entry size
     */
    if (Hive->Version >= 5)
    {
        /* XP Hive: Use hash leaf */
        ASSERT(LeafKey->Signature == CM_KEY_HASH_LEAF);
        EntrySize = sizeof(CM_INDEX);
    }
    else
    {
        /* Use index leaf */
        ASSERT(LeafKey->Signature == CM_KEY_INDEX_LEAF);
        EntrySize = sizeof(HCELL_INDEX);
    }

    /* Compute the total size */
    TotalSize = (EntrySize * LastHalf) + FIELD_OFFSET(CM_KEY_INDEX, List) + 1;

    /* Mark the leaf cell dirty */
    HvMarkCellDirty(Hive, LeafCell, FALSE);

    /* Make sure its type is the same */
    ASSERT(HvGetCellType(LeafCell) == Type);

    /* Allocate the cell, fail in case of error */
    NewCell = HvAllocateCell(Hive, TotalSize, Type, LeafCell);
    if (NewCell == HCELL_NIL) return NewCell;

    /* Get the key */
    NewKey = (PCM_KEY_INDEX)HvGetCell(Hive, NewCell);
    if (!NewKey)
    {
        /* Free the cell and exit - should not happen! */
        ASSERT(FALSE);
        HvFreeCell(Hive, NewCell);
        return HCELL_NIL;
    }

    /* Release the newly created cell */
    HvReleaseCell(Hive, NewCell);

    /* Set its signature according to the version of the hive */
    if (Hive->Version >= 5)
    {
        /* XP Hive: Use hash leaf signature */
        NewKey->Signature = CM_KEY_HASH_LEAF;
    }
    else
    {
        /* Use index leaf signature */
        NewKey->Signature = CM_KEY_INDEX_LEAF;
    }

    /* Calculate the size of the free entries in the root key */
    TotalSize = HvGetCellSize(Hive, IndexKey) -
        (IndexKey->Count * sizeof(HCELL_INDEX)) -
        FIELD_OFFSET(CM_KEY_INDEX, List);

    /* Check if we're out of space */
    if (TotalSize / sizeof(HCELL_INDEX) < 1)
    {
        /* Grow the leaf by one more entry */
        TotalSize = HvGetCellSize(Hive, IndexKey) + sizeof(HCELL_INDEX);

        /* Re-allocate the root */
        RootCell = HvReallocateCell(Hive, RootCell, TotalSize);
        if (RootCell == HCELL_NIL)
        {
            /* Free the cell and exit */
            HvFreeCell(Hive, NewCell);
            return HCELL_NIL;
        }

        /* Get the leaf cell */
        IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, RootCell);
        if (!IndexKey)
        {
            /* This shouldn't happen */
            ASSERT(FALSE);
            return HCELL_NIL;
        }
    }

    /* Splitting is done, now we need to copy the contents,
     * according to the hive version
     */
    if (Hive->Version >= 5)
    {
        /* Copy the fast indexes */
        FastLeaf = (PCM_KEY_FAST_INDEX)LeafKey;
        RtlMoveMemory(&NewKey->List[0],
                      &FastLeaf->List[FirstHalf],
                      LastHalf * EntrySize);
    }
    else
    {
        /* Copy the indexes themselves */
        RtlMoveMemory(&NewKey->List[0],
                      &LeafKey->List[FirstHalf],
                      LastHalf * EntrySize);
    }

    /* Shift the data inside the root key */
    if ((RootSelect + 1) < IndexKey->Count)
    {
        RtlMoveMemory(&IndexKey->List[RootSelect + 2],
                      &IndexKey->List[RootSelect + 1],
                      (IndexKey->Count -
                      (RootSelect + 1)) * sizeof(HCELL_INDEX));
    }

    /* Make sure both old and new computed counts are valid */
    ASSERT(FirstHalf != 0);
    ASSERT(LastHalf != 0);

    /* Update the count value of old and new keys */
    LeafKey->Count = FirstHalf;
    NewKey->Count = LastHalf;

    /* Increase the count value of the root key */
    IndexKey->Count++;

    /* Set the new cell in root's list */
    IndexKey->List[RootSelect + 1] = NewCell;

    /* Return the root cell */
    return RootCell;
}

HCELL_INDEX
NTAPI
CmpSelectLeaf(IN PHHIVE Hive,
              IN PCM_KEY_NODE KeyNode,
              IN PCUNICODE_STRING Name,
              IN HSTORAGE_TYPE Type,
              IN PHCELL_INDEX *RootCell)
{
    PCM_KEY_INDEX IndexKey, LeafKey;
    PCM_KEY_FAST_INDEX FastLeaf;
    HCELL_INDEX LeafCell, CurrentCell;
    ULONG SubKeyIndex;
    LONG Result;

    /* Mark it as dirty */
    HvMarkCellDirty(Hive, KeyNode->SubKeyLists[Type], FALSE);

    /* Get the cell */
    IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Type]);

    /* Check if we've got a valid key */
    if (!IndexKey)
    {
        /* Should not happen! */
        ASSERT(IndexKey != NULL);
        return HCELL_NIL;
    }

    /* Sanity check */
    ASSERT(IndexKey->Signature == CM_KEY_INDEX_ROOT);

    while (TRUE)
    {
        /* Find subkey */
        SubKeyIndex = CmpFindSubKeyInRoot(Hive, IndexKey, Name, &LeafCell);

        /* Make sure we found something valid */
        if (SubKeyIndex & INVALID_INDEX) return HCELL_NIL;

        /* Try to fit it into the LeafCell, if it was found */
        if (LeafCell != HCELL_NIL)
        {
            /* Get the leaf key */
            LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

            /* Check for failure */
            if (!LeafKey) return HCELL_NIL;

            /* Check if it fits into this leaf and break */
            if (LeafKey->Count < CmpMaxIndexPerHblock)
            {
                /* Fill in the result and return it */
                *RootCell = &IndexKey->List[SubKeyIndex];
                return LeafCell;
            }

            /* It didn't fit, so proceed to splitting */
        }
        else
        {
            /* Get the leaf cell at the very end */
            LeafCell = IndexKey->List[SubKeyIndex];
            LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

            /* Return an error in case of problems */
            if (!LeafKey) return HCELL_NIL;

            /* Choose the cell to search from depending on the key type */
            if ((LeafKey->Signature == CM_KEY_FAST_LEAF) ||
                (LeafKey->Signature == CM_KEY_HASH_LEAF))
            {
                FastLeaf = (PCM_KEY_FAST_INDEX)LeafKey;
                CurrentCell = FastLeaf->List[0].Cell;
            }
            else
            {
                /* Make sure it's an index leaf */
                ASSERT(LeafKey->Signature == CM_KEY_INDEX_LEAF);
                CurrentCell = LeafKey->List[0];
            }

            /* Do a name compare */
            Result = CmpDoCompareKeyName(Hive, Name, CurrentCell);

            /* Check for failure */
            if (Result == 2) return HCELL_NIL;

            /* Names can't be equal, ensure that */
            ASSERT(Result != 0);

            /* Check if it's above */
            if (Result >= 0)
            {
                /* Get the cell in the index */
                LeafCell = IndexKey->List[SubKeyIndex];
                LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

                /* Return an error in case of problems */
                if (!LeafKey) return HCELL_NIL;

                /* Check if it fits into this leaf */
                if (LeafKey->Count < CmpMaxIndexPerHblock)
                {
                    /* Fill in the result and return the cell */
                    *RootCell = &IndexKey->List[SubKeyIndex];
                    return LeafCell;
                }

                /* No, it doesn't fit, check the next adjacent leaf */
                if ((SubKeyIndex + 1) < IndexKey->Count)
                {
                    /* Yes, there is space */
                    LeafCell = IndexKey->List[SubKeyIndex + 1];
                    LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

                    /* Return an error in case of problems */
                    if (!LeafKey) return HCELL_NIL;

                    /* Check if it fits and break */
                    if (LeafKey->Count < CmpMaxIndexPerHblock)
                    {
                        /* Fill in the result and return the cell */
                        *RootCell = &IndexKey->List[SubKeyIndex + 1];
                        return LeafCell;
                    }
                }

                /* It didn't fit, so proceed to splitting */
            }
            else
            {
                /* No, it's below, check the subkey index */
                if (SubKeyIndex > 0)
                {
                    /* There should be space at the leaf one before that */
                    LeafCell = IndexKey->List[SubKeyIndex - 1];
                    LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

                    /* Return an error in case of problems */
                    if (!LeafKey) return HCELL_NIL;

                    /* Check if it fits and break */
                    if (LeafKey->Count < CmpMaxIndexPerHblock)
                    {
                        /* Fill in the result and return the cell */
                        *RootCell = &IndexKey->List[SubKeyIndex - 1];
                        return LeafCell;
                    }
                }
                else
                {
                    /* Use the first leaf, if possible */
                    LeafCell = IndexKey->List[0];
                    LeafKey = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);

                    /* Return an error in case of problems */
                    if (!LeafKey) return HCELL_NIL;

                    /* Check if it fits and break */
                    if (LeafKey->Count < CmpMaxIndexPerHblock)
                    {
                        /* Fill in the result and return the cell */
                        *RootCell = &IndexKey->List[0];
                        return LeafCell;
                    }
                }

                /* It didn't fit into either, so proceed to splitting */
            }
        }

        /* We need to split */
        CurrentCell = CmpSplitLeaf(Hive,
                                   KeyNode->SubKeyLists[Type],
                                   SubKeyIndex,
                                   Type);

        /* Return failure in case splitting failed */
        if (CurrentCell == HCELL_NIL) return CurrentCell;

        /* Set the SubKeyLists value with the new key */
        KeyNode->SubKeyLists[Type] = CurrentCell;

        /* Get the new cell */
        IndexKey = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Type]);

        /* Return in case of failure */
        if (!IndexKey) return HCELL_NIL;

        /* Make sure the new key became index root */
        ASSERT(IndexKey->Signature == CM_KEY_INDEX_ROOT);

        /* Now loop over with the new IndexKey value, which definately
         * has the space now
         */
    }

    /* Shouldn't come here */
    return HCELL_NIL;
}

BOOLEAN
NTAPI
CmpAddSubKey(IN PHHIVE Hive,
             IN HCELL_INDEX Parent,
             IN HCELL_INDEX Child)
{
    PCM_KEY_NODE KeyNode;
    PCM_KEY_INDEX Index;
    PCM_KEY_FAST_INDEX OldIndex;
    UNICODE_STRING Name;
    HCELL_INDEX IndexCell = HCELL_NIL, CellToRelease = HCELL_NIL, LeafCell;
    PHCELL_INDEX RootPointer = NULL;
    ULONG Type, i;
    BOOLEAN IsCompressed;
    PAGED_CODE();

    /* Get the key node */
    KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Child);
    if (!KeyNode)
    {
        /* Shouldn't happen */
        ASSERT(FALSE);
        return FALSE;
    }

    /* Check if the name is compressed */
    if (KeyNode->Flags & KEY_COMP_NAME)
    {
        /* Remember for later */
        IsCompressed = TRUE;

        /* Create the compressed name and allocate it */
        Name.Length = CmpCompressedNameSize(KeyNode->Name, KeyNode->NameLength);
        Name.MaximumLength = Name.Length;
        Name.Buffer = Hive->Allocate(Name.Length, TRUE, TAG_CM);
        if (!Name.Buffer)
        {
            /* Release the cell and fail */
            HvReleaseCell(Hive, Child);
            ASSERT(FALSE);
            return FALSE;
        }

        /* Copy the compressed name */
        CmpCopyCompressedName(Name.Buffer,
                              Name.MaximumLength,
                              KeyNode->Name,
                              KeyNode->NameLength);
    }
    else
    {
        /* Remember for later */
        IsCompressed = FALSE;

        /* Build the unicode string */
        Name.Length = KeyNode->NameLength;
        Name.MaximumLength = KeyNode->NameLength;
        Name.Buffer = &KeyNode->Name[0];
    }

    /* Release the cell */
    HvReleaseCell(Hive, Child);

    /* Get the parent node */
    KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Parent);
    if (!KeyNode)
    {
        /* Not handled */
        ASSERT(FALSE);
    }

    /* Find out the type of the cell, and check if this is the first subkey */
    Type = HvGetCellType(Child);
    if (!KeyNode->SubKeyCounts[Type])
    {
        /* Allocate a fast leaf */
        IndexCell = HvAllocateCell(Hive, sizeof(CM_KEY_FAST_INDEX), Type, HCELL_NIL);
        if (IndexCell == HCELL_NIL)
        {
            /* Not handled */
            ASSERT(FALSE);
        }

        /* Get the leaf cell */
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
        if (!Index)
        {
            /* Shouldn't happen */
            ASSERT(FALSE);
        }

        /* Now check what kind of hive we're dealing with */
        if (Hive->Version >= 5)
        {
            /* XP Hive: Use hash leaf */
            Index->Signature = CM_KEY_HASH_LEAF;
        }
        else if (Hive->Version >= 3)
        {
            /* Windows 2000 and ReactOS: Use fast leaf */
            Index->Signature = CM_KEY_FAST_LEAF;
        }
        else
        {
            /* NT 4: Use index leaf */
            Index->Signature = CM_KEY_INDEX_LEAF;
        }

        /* Setup the index list */
        Index->Count = 0;
        KeyNode->SubKeyLists[Type] = IndexCell;
    }
    else
    {
        /* We already have an index, get it */
        Index = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Type]);
        if (!Index)
        {
            /* Not handled */
            ASSERT(FALSE);
        }

        /* Remember to release the cell later */
        CellToRelease = KeyNode->SubKeyLists[Type];

        /* Check if this is a fast leaf that's gotten too full */
        if ((Index->Signature == CM_KEY_FAST_LEAF) &&
            (Index->Count >= CmpMaxFastIndexPerHblock))
        {
            DPRINT("Doing Fast->Slow Leaf conversion\n");

            /* Mark this cell as dirty */
            HvMarkCellDirty(Hive, CellToRelease, FALSE);

            /* Convert */
            OldIndex = (PCM_KEY_FAST_INDEX)Index;

            for (i = 0; i < OldIndex->Count; i++)
            {
                Index->List[i] = OldIndex->List[i].Cell;
            }

            /* Set the new type value */
            Index->Signature = CM_KEY_INDEX_LEAF;
        }
        else if (((Index->Signature == CM_KEY_INDEX_LEAF) ||
                  (Index->Signature == CM_KEY_HASH_LEAF)) &&
                  (Index->Count >= CmpMaxIndexPerHblock))
        {
            /* This is an old/hashed leaf that's gotten too large, root it */
            IndexCell = HvAllocateCell(Hive,
                                      sizeof(CM_KEY_INDEX) +
                                      sizeof(HCELL_INDEX),
                                      Type,
                                      HCELL_NIL);
            if (IndexCell == HCELL_NIL)
            {
                /* Not handled */
                ASSERT(FALSE);
            }

            /* Get the index cell */
            Index = (PCM_KEY_INDEX)HvGetCell(Hive, IndexCell);
            if (!Index)
            {
                /* Shouldn't happen */
                ASSERT(FALSE);
            }

            /* Mark the index as a root, and set the index cell */
            Index->Signature = CM_KEY_INDEX_ROOT;
            Index->Count = 1;
            Index->List[0] = KeyNode->SubKeyLists[Type];
            KeyNode->SubKeyLists[Type] = IndexCell;
        }
    }

    /* Now we can choose the leaf cell */
    LeafCell = KeyNode->SubKeyLists[Type];

    /* Check if we turned the index into a root */
    if (Index->Signature == CM_KEY_INDEX_ROOT)
    {
        DPRINT("Leaf->Root Index Conversion\n");

        /* Get the leaf where to add the new entry (the routine will do
         * the splitting if necessary)
         */
        LeafCell = CmpSelectLeaf(Hive, KeyNode, &Name, Type, &RootPointer);
        if (LeafCell == HCELL_NIL)
        {
            /* Not handled */
            ASSERT(FALSE);
        }
    }

    /* Add our leaf cell */
    LeafCell = CmpAddToLeaf(Hive, LeafCell, Child, &Name);
    if (LeafCell == HCELL_NIL)
    {
        /* Not handled */
        ASSERT(FALSE);
    }

    /* Update the key counts */
    KeyNode->SubKeyCounts[Type]++;

    /* Check if caller wants us to return the leaf */
    if (RootPointer)
    {
        /* Return it */
        *RootPointer = LeafCell;
    }
    else
    {
        /* Otherwise, mark it as the list index for the cell */
        KeyNode->SubKeyLists[Type] = LeafCell;
    }

    /* If the name was compressed, free our copy */
    if (IsCompressed) Hive->Free(Name.Buffer, 0);

    /* Release all our cells */
    if (IndexCell != HCELL_NIL) HvReleaseCell(Hive, IndexCell);
    if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
    HvReleaseCell(Hive, Parent);
    return TRUE;
}

BOOLEAN
NTAPI
CmpRemoveSubKey(IN PHHIVE Hive,
                IN HCELL_INDEX ParentKey,
                IN HCELL_INDEX TargetKey)
{
    PCM_KEY_NODE Node;
    UNICODE_STRING SearchName;
    BOOLEAN IsCompressed;
    WCHAR Buffer[50];
    HCELL_INDEX RootCell = HCELL_NIL, LeafCell, ChildCell;
    PCM_KEY_INDEX Root = NULL, Leaf;
    PCM_KEY_FAST_INDEX Child;
    ULONG Storage, RootIndex = INVALID_INDEX, LeafIndex;
    BOOLEAN Result = FALSE;
    HCELL_INDEX CellToRelease1 = HCELL_NIL, CellToRelease2  = HCELL_NIL;

    /* Get the target key node */
    Node = (PCM_KEY_NODE)HvGetCell(Hive, TargetKey);
    if (!Node) return FALSE;

    /* Make sure it's dirty, then release it */
    ASSERT(HvIsCellDirty(Hive, TargetKey));
    HvReleaseCell(Hive, TargetKey);

    /* Check if the name is compressed */
    if (Node->Flags & KEY_COMP_NAME)
    {
        /* Remember for later */
        IsCompressed = TRUE;

        /* Build the search name */
        SearchName.Length = CmpCompressedNameSize(Node->Name,
                                                  Node->NameLength);
        SearchName.MaximumLength = SearchName.Length;

        /* Do we need an extra bufer? */
        if (SearchName.MaximumLength > sizeof(Buffer))
        {
            /* Allocate one */
            SearchName.Buffer = Hive->Allocate(SearchName.Length, TRUE, TAG_CM);
            if (!SearchName.Buffer) return FALSE;
        }
        else
        {
            /* Otherwise, use our local stack buffer */
            SearchName.Buffer = Buffer;
        }

        /* Copy the compressed name */
        CmpCopyCompressedName(SearchName.Buffer,
                              SearchName.MaximumLength,
                              Node->Name,
                              Node->NameLength);
    }
    else
    {
        /* It's not compressed, build the name directly from the node */
        IsCompressed = FALSE;
        SearchName.Length = Node->NameLength;
        SearchName.MaximumLength = Node->NameLength;
        SearchName.Buffer = Node->Name;
    }

    /* Now get the parent node */
    Node = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
    if (!Node) goto Exit;

    /* Make sure it's dirty, then release it */
    ASSERT(HvIsCellDirty(Hive, ParentKey));
    HvReleaseCell(Hive, ParentKey);

    /* Get the storage type and make sure it's not empty */
    Storage = HvGetCellType(TargetKey);
    ASSERT(Node->SubKeyCounts[Storage] != 0);
    //ASSERT(HvIsCellAllocated(Hive, Node->SubKeyLists[Storage]));

    /* Get the leaf cell now */
    LeafCell = Node->SubKeyLists[Storage];
    Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
    if (!Leaf) goto Exit;

    /* Remember to release it later */
    CellToRelease1 = LeafCell;

    /* Check if the leaf is a root */
    if (Leaf->Signature == CM_KEY_INDEX_ROOT)
    {
        /* Find the child inside the root */
        RootIndex = CmpFindSubKeyInRoot(Hive, Leaf, &SearchName, &ChildCell);
        if (RootIndex & INVALID_INDEX) goto Exit;
        ASSERT(ChildCell != FALSE);

        /* The root cell is now this leaf */
        Root = Leaf;
        RootCell = LeafCell;

        /* And the new leaf is now this child */
        LeafCell = ChildCell;
        Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
        if (!Leaf) goto Exit;

        /* Remember to release it later */
        CellToRelease2 = LeafCell;
    }

    /* Make sure the leaf is valid */
    ASSERT((Leaf->Signature == CM_KEY_INDEX_LEAF) ||
           (Leaf->Signature == CM_KEY_FAST_LEAF) ||
           (Leaf->Signature == CM_KEY_HASH_LEAF));

    /* Now get the child in the leaf */
    LeafIndex = CmpFindSubKeyInLeaf(Hive, Leaf, &SearchName, &ChildCell);
    if (LeafIndex & INVALID_INDEX) goto Exit;
    ASSERT(ChildCell != HCELL_NIL);

    /* Decrement key counts and check if this was the last leaf entry */
    Node->SubKeyCounts[Storage]--;
    if (!(--Leaf->Count))
    {
        /* Free the leaf */
        HvFreeCell(Hive, LeafCell);

        /* Check if we were inside a root */
        if (Root)
        {
            /* Decrease the root count too, since the leaf is going away */
            if (!(--Root->Count))
            {
                /* The root is gone too,n ow */
                HvFreeCell(Hive, RootCell);
                Node->SubKeyLists[Storage] = HCELL_NIL;
            }
            else if (RootIndex < Root->Count)
            {
                /* Bring everything up by one */
                RtlMoveMemory(&Root->List[RootIndex],
                              &Root->List[RootIndex + 1],
                              (Root->Count - RootIndex) * sizeof(HCELL_INDEX));
            }
        }
        else
        {
            /* Otherwise, just clear the cell */
            Node->SubKeyLists[Storage] = HCELL_NIL;
        }
    }
    else if (LeafIndex < Leaf->Count)
    {
        /* Was the leaf a normal index? */
        if (Leaf->Signature == CM_KEY_INDEX_LEAF)
        {
            /* Bring everything up by one */
            RtlMoveMemory(&Leaf->List[LeafIndex],
                          &Leaf->List[LeafIndex + 1],
                          (Leaf->Count - LeafIndex) * sizeof(HCELL_INDEX));
        }
        else
        {
            /* This is a fast index, bring everything up by one */
            Child = (PCM_KEY_FAST_INDEX)Leaf;
            RtlMoveMemory(&Child->List[LeafIndex],
                          &Child->List[LeafIndex+1],
                          (Child->Count - LeafIndex) * sizeof(CM_INDEX));
        }
    }

    /* If we got here, now we're done */
    Result = TRUE;

Exit:
    /* Release any cells we may have been holding */
    if (CellToRelease1 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease1);
    if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);

    /* Check if the name was compressed and not inside our local buffer */
    if ((IsCompressed) && (SearchName.MaximumLength > sizeof(Buffer)))
    {
        /* Free the buffer we allocated */
        Hive->Free(SearchName.Buffer, 0);
    }

    /* Return the result */
    return Result;
}