/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    binvals.c

Abstract:

    Routines to manage binary blocks associated with memdb keys.

Author:

    Jim Schmidt (jimschm) 8-Aug-1996

Revision History:

    Jim Schmidt (jimschm) 21-Oct-1997  Split from memdb.c

--*/

#include "pch.h"
#include "memdbp.h"

#ifndef UNICODE
#error UNICODE required
#endif


static PBINARYBLOCK g_FirstBlockPtr = NULL;

//
// Implementation
//

PBYTE
pGetBinaryData (
    IN      PBINARYBLOCK BlockPtr
    )

/*++

Routine Description:

  pGetBinaryData returns a pointer to the data portion of a
  BINARYBLOCK struct.

Arguments:

  BlockPtr - A pointer to a BINARYBLOCK struct.

Return Value:

  A pointer to the binary data of BlockPtr, or NULL if BlockPtr
  is invalid.

--*/

{
#ifdef DEBUG

    // Verify checked block is valid
    if (BlockPtr && BlockPtr->Signature != SIGNATURE) {
        DEBUGMSG ((
            DBG_ERROR,
            "Signature of %x is invalid, can't get binary data",
            g_FirstBlockPtr
            ));
        return NULL;
    }
#endif

    if (!BlockPtr) {
        return NULL;
    }

    return BlockPtr->Data;
}


DWORD
pGetBinaryDataSize (
    IN      PBINARYBLOCK BlockPtr
    )
{
#ifdef DEBUG
    // Verify checked block is valid
    if (BlockPtr && BlockPtr->Signature != SIGNATURE) {
        DEBUGMSG ((
            DBG_ERROR,
            "Signature of %x is invalid, can't get binary data",
            g_FirstBlockPtr
            ));
        return 0;
    }
#endif

    if (!BlockPtr) {
        return 0;
    }

    return BlockPtr->Size - sizeof (BINARYBLOCK);
}


PCBYTE
GetKeyStructBinaryData (
    PKEYSTRUCT KeyStruct
    )
{
    if (!KeyStruct || !(KeyStruct->Flags & KSF_BINARY)) {
        return NULL;
    }

    return pGetBinaryData (KeyStruct->BinaryPtr);
}


DWORD
GetKeyStructBinarySize (
    PKEYSTRUCT KeyStruct
    )
{
    if (!KeyStruct || !(KeyStruct->Flags & KSF_BINARY)) {
        return 0;
    }

    return pGetBinaryDataSize (KeyStruct->BinaryPtr);
}


PBINARYBLOCK
pGetFirstBinaryBlock (
    VOID
    )

/*++

Routine Description:

  pGetFristBinaryBlock returns a pointer to the first allocated
  BINARYBLOCK struct, or NULL if no structs are allocated.  This
  routine is used with pGetNextBinaryBlock to walk all allocated
  blocks.

Arguments:

  none

Return Value:

  A pointer to the the first allocated BINARYBLOCK struct, or NULL
  if there are no structs allocated.

--*/

{
#ifdef DEBUG
    // Verify checked block is valid
    if (g_FirstBlockPtr && g_FirstBlockPtr->Signature != SIGNATURE) {
        DEBUGMSG ((DBG_ERROR, "First binary block %x signature is invalid", g_FirstBlockPtr));
        return NULL;
    }
#endif

    return g_FirstBlockPtr;
}


PBINARYBLOCK
pGetNextBinaryBlock (
    IN      PBINARYBLOCK BlockPtr
    )

/*++

Routine Description:

  pGetNextBinaryBlock returns a pointer to the next allocated
  BINARYBLOCK struct, or NULL if no more blocks are allocated.

Arguments:

  BlockPtr - The non-NULL return value of pGetFirstBinaryBlock or
             pGetNextBinaryBlock

Return Value:

  A pointer to the next BINARYBLOCK struct, or NULL if no more blocks
  are allocated.

--*/

{
    if (!BlockPtr) {
        return NULL;
    }

#ifdef DEBUG
    // Verify checked block is valid
    if (BlockPtr->NextPtr && BlockPtr->NextPtr->Signature != SIGNATURE) {
        DEBUGMSG ((DBG_ERROR, "Binary block %x signature is invalid", BlockPtr->NextPtr));
        return NULL;
    }
#endif

    return BlockPtr->NextPtr;
}


PBINARYBLOCK
AllocBinaryBlock (
    IN      PCBYTE Data,
    IN      DWORD DataSize,
    IN      DWORD OwningKey
    )

/*++

Routine Description:

  AllocBinaryBlock returns a pointer to an initialized BINARYBLOCK
  structure, or NULL if the structure could not be allocated.  If
  the structure is allocated, Data is copied to the newly allocated
  block.  Call pFreeBinaryBlock to clean up this allocation.

Arguments:

  Data      - A pointer to a block of binary data to be copied into
              the newly allocated structure
  DataSize  - The number of bytes to copy (may be zero)
  OwningKey - The offset of the key who owns the data block

Return Value:

  A pointer to the binary block structure, or NULL if it could not
  be allocated.

--*/

{
    PBINARYBLOCK BlockPtr;
    DWORD AllocSize;

    AllocSize = DataSize + sizeof (BINARYBLOCK);

    BlockPtr = (PBINARYBLOCK) MemAlloc (g_hHeap, 0, AllocSize);
    if (!BlockPtr) {
        // DataSize must be bigger than 2G
        OutOfMemory_Terminate();
    }

    //
    // Initialize block struct
    //

    if (DataSize) {
        CopyMemory (BlockPtr->Data, Data, DataSize);
    }

    BlockPtr->Size = AllocSize;
    BlockPtr->OwningKey = OwningKey;

#ifdef DEBUG
    BlockPtr->Signature = SIGNATURE;
#endif

    //
    // Link block to list of allocated blocks
    //

    BlockPtr->NextPtr = g_FirstBlockPtr;
    if (g_FirstBlockPtr) {
        g_FirstBlockPtr->PrevPtr = BlockPtr;
    }
    g_FirstBlockPtr = BlockPtr;

    BlockPtr->PrevPtr = NULL;

    //
    // Return
    //

    return BlockPtr;
}


VOID
pFreeBinaryBlock (
    PBINARYBLOCK BlockPtr,
    BOOL Delink
    )

/*++

Routine Description:

  pFreeBinaryBlock frees memory allocated for a binary block and optionally
  delinks it from the allocation list.

Arguments:

  BlockPtr  - A pointer to the block to delete
  Delink    - TRUE if structure should be delinked from allocation list,
              or FALSE if the allocation list does not need to be maintained

Return Value:

  none

--*/

{
    if (!BlockPtr) {
        return;
    }

#ifdef DEBUG
    if (BlockPtr->Signature != SIGNATURE) {
        DEBUGMSG ((DBG_ERROR, "Can't free block %x because signature is invalid", BlockPtr));
        return;
    }
#endif

    if (Delink) {

#ifdef DEBUG

        if (BlockPtr->PrevPtr && BlockPtr->PrevPtr->Signature != SIGNATURE) {
            DEBUGMSG ((DBG_ERROR, "Can't free block %x because prev block (%x) signature is invalid", BlockPtr, BlockPtr->PrevPtr));
            return;
        }

        if (BlockPtr->NextPtr && BlockPtr->NextPtr->Signature != SIGNATURE) {
            DEBUGMSG ((DBG_ERROR, "Can't free block %x because next block (%x) signature is invalid", BlockPtr, BlockPtr->NextPtr));
            return;
        }

#endif

        if (BlockPtr->PrevPtr) {
            BlockPtr->PrevPtr->NextPtr = BlockPtr->NextPtr;
        } else {
            g_FirstBlockPtr = BlockPtr->NextPtr;
        }

        if (BlockPtr->NextPtr) {
            BlockPtr->NextPtr->PrevPtr = BlockPtr->PrevPtr;
        }
    }

    MemFree (g_hHeap, 0, BlockPtr);
}


VOID
FreeKeyStructBinaryBlock (
    PKEYSTRUCT KeyStruct
    )

/*++

Routine Description:

  FreeKeyStructBinaryBlock frees a binary block and resets the
  KSF_BINARY flag, if the key struct has a binary block allocated.

Arguments:

  none

Return Value:

  none

--*/

{
    if (KeyStruct->Flags & KSF_BINARY) {
        pFreeBinaryBlock (KeyStruct->BinaryPtr, TRUE);
        KeyStruct->BinaryPtr = NULL;
        KeyStruct->Flags &= ~KSF_BINARY;
    }
}


VOID
FreeAllBinaryBlocks (
    VOID
    )

/*++

Routine Description:

  FreeAllBinaryBlocks removes all memory associated with binary
  blocks.  This function is used for final cleanup.

Arguments:

  none

Return Value:

  none

--*/

{
    PBINARYBLOCK NextBlockPtr;

    while (g_FirstBlockPtr) {
        NextBlockPtr = g_FirstBlockPtr->NextPtr;
        pFreeBinaryBlock (g_FirstBlockPtr, FALSE);
        g_FirstBlockPtr = NextBlockPtr;
    }
}


BOOL
LoadBinaryBlocks (
    HANDLE File
    )
{
    BOOL b;
    DWORD Count;
    DWORD Owner = 0;
    DWORD Size;
    DWORD Read;
    DWORD d;
    PBYTE TempBuf = NULL;
    PBINARYBLOCK NewBlock;

    b = ReadFile (File, &Count, sizeof (DWORD), &Read, NULL);

    if (b && Count) {
        //
        // Alloc binary objects
        //

        for (d = 0 ; b && d < Count ; d++) {
            // Get Size and Owner
            b = ReadFile (File, &Size, sizeof (DWORD), &Read, NULL);
            if (Size > BLOCK_SIZE * 32) {
                b = FALSE;
            }
            if (b) {
                b = ReadFile (File, &Owner, sizeof (DWORD), &Read, NULL);
            }

            // Alloc a temporary buffer to read in data
            if (b) {
                TempBuf = (PBYTE) MemAlloc (g_hHeap, 0, Size);

                b = ReadFile (File, TempBuf, Size, &Read, NULL);

                // If data read OK, create binary block object
                if (b) {
                    NewBlock = AllocBinaryBlock (TempBuf, Size, Owner);
                    if (!NewBlock) {
                        b = FALSE;
                    } else {
                        // Link owner to new memory location
                        MYASSERT (GetKeyStruct (Owner)->Flags & KSF_BINARY);
                        GetKeyStruct(Owner)->BinaryPtr = NewBlock;
                    }
                }

                MemFree (g_hHeap, 0, TempBuf);
                TempBuf = NULL;
            }
        }
    }

    if (TempBuf) {
        MemFree (g_hHeap, 0, TempBuf);
    }

    return b;
}


BOOL
SaveBinaryBlocks (
    HANDLE File
    )
{
    BOOL b;
    DWORD Count;
    DWORD Size;
    PBINARYBLOCK BinaryPtr;
    DWORD Written;

    //
    // Count the binary objects
    //

    BinaryPtr = pGetFirstBinaryBlock();
    Count = 0;

    while (BinaryPtr) {
        Count++;
        BinaryPtr = pGetNextBinaryBlock (BinaryPtr);
    }

    //
    // Write count to disk
    //
    b = WriteFile (File, &Count, sizeof (DWORD), &Written, NULL);

    if (b) {
        //
        // Write the binary objects
        //

        BinaryPtr = pGetFirstBinaryBlock();

        while (b && BinaryPtr) {
            //
            // Format per object:
            //
            //  Size    (DWORD)
            //  Owner   (DWORD)
            //  Data    (Size)
            //

            Size = pGetBinaryDataSize (BinaryPtr);
            b = WriteFile (File, &Size, sizeof (DWORD), &Written, NULL);

            if (b) {
                b = WriteFile (File, &BinaryPtr->OwningKey, sizeof (DWORD), &Written, NULL);
            }

            if (b && Size) {
                b = WriteFile (File, pGetBinaryData (BinaryPtr), Size, &Written, NULL);
                if (Written != Size) {
                    b = FALSE;
                }
            }

            BinaryPtr = pGetNextBinaryBlock(BinaryPtr);
        }
    }

    return b;
}