//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1997.
//
//  File:       lht.cxx
//
//  Contents:   Context handle management for servers
//
//  Classes:
//
//  Functions:
//
//  History:    1-31-97   RichardW   Created
//
//----------------------------------------------------------------------------

#include <lsapch.hxx>

#include "sht.hxx"
#include "lht.hxx"

//
// Due to the high number of connections for servers, the client context list
// for a particular session could grow into the thousands.  At that stage, a
// linear list that is searched to validate a handle is prohibitively expensive.
//
// Thus, the faster, if more expensive to set up and add handle package
//

HP_INITIALIZE_FN    LhtInitialize ;
HP_CREATE_FN        LhtCreate ;
HP_DELETE_FN        LhtDelete ;
HP_ADD_HANDLE_FN    LhtAddHandle ;
HP_DELETE_HANDLE_FN LhtDeleteHandle ;
HP_VALIDATE_HANDLE_FN LhtValidateHandle ;
HP_REF_HANDLE_FN    LhtRefHandle ;
HP_DEREF_HANDLE_KEY_FN LhtDerefHandleKey ;
HP_GET_HANDLE_CONTEXT_FN LhtGetHandleContext ;
HP_RELEASE_CONTEXT_FN LhtReleaseContext ;

HANDLE_PACKAGE  LargeHandlePackage = {
                    sizeof( LARGE_HANDLE_TABLE ),
                    LhtInitialize,
                    LhtCreate,
                    LhtDelete,
                    LhtAddHandle,
                    LhtDeleteHandle,
                    LhtValidateHandle,
                    LhtRefHandle,
                    LhtDerefHandleKey,
                    LhtGetHandleContext,
                    LhtReleaseContext
                    };


ULONG   LhtFastMem ;
ULONG   LhtShiftValues[] = { 4, 12, 16, 20 };


#define IndexFromHandle( Level, Handle )    \
            ( ( ((PSecHandle) Handle)->dwUpper >> LhtShiftValues[ Level ] ) & HANDLE_TABLE_MASK )

#define LhtLockTable( t ) \
            if ( (((PLARGE_HANDLE_TABLE) t)->Flags & LHT_NO_SERIALIZE ) == 0 ) \
            {                                                                  \
                RtlEnterCriticalSection( &((PLARGE_HANDLE_TABLE)t)->Lock ); \
            }

#define LhtUnlockTable( t ) \
            if ( (((PLARGE_HANDLE_TABLE) t)->Flags & LHT_NO_SERIALIZE ) == 0 ) \
            {                                                                  \
                RtlLeaveCriticalSection( &((PLARGE_HANDLE_TABLE)t)->Lock ); \
            }


#define LHT_ACTION_ADDREF       0
#define LHT_ACTION_DELREF       1
#define LHT_ACTION_FORCEDEL     2
#define LHT_ACTION_VALIDATE     3
#define LHT_ACTION_ADDHANDLE    4
#define LHT_ACTION_DELHANDLE    5

#define LHT_ACTION_MASK     0x0000FFFF
#define LHT_ACTION_LOCKED   0x00010000
#define LHTP_DEREF_NOT_DEL  0x10000000
#define LHTP_HANDLE_CHECKED 0x20000000

//+---------------------------------------------------------------------------
//
//  Function:   LhtInitialize
//
//  Synopsis:   Initializes the LHT handle package
//
//  Arguments:  (none)
//
//  History:    2-03-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtInitialize(
    VOID
    )
{
    return TRUE ;
}



//+---------------------------------------------------------------------------
//
//  Function:   LhtCreate
//
//  Synopsis:   Creates a large handle table.  The table is referenced through
//              the returned pointer
//
//  Arguments:  [Flags] -- Flags as defined by handle.hxx
//
//  History:    2-03-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PVOID
LhtCreate(
    IN ULONG Flags,
    IN PVOID HandleTable,
    IN PHP_ENUM_CALLBACK_FN Callback
    )
{
    PLARGE_HANDLE_TABLE Table ;
    ULONG i;

    if ( HandleTable )
    {
        Table = (PLARGE_HANDLE_TABLE) HandleTable ;
    }
    else
    {
        Table = (PLARGE_HANDLE_TABLE) LsapAllocatePrivateHeap( sizeof( LARGE_HANDLE_TABLE ) );
    }

    if ( Table )
    {
        Table->Tag = LHT_TAG ;
        Table->Flags = 0 ;

        Table->Flags = (Flags & HANDLE_PACKAGE_GENERAL_FLAGS);


        if ( Flags & HANDLE_PACKAGE_CALLBACK_ON_DELETE )
        {
            Table->DeleteCallback = Callback ;
        }
        else
        {
            Table->DeleteCallback = NULL ;
        }

        if ( HandleTable )
        {
            Table->Flags |= LHT_NO_FREE ;
        }

        Table->Depth = 0 ;


        if ( ( Flags & LHT_NO_SERIALIZE ) == 0 )
        {
            NTSTATUS Status = RtlInitializeCriticalSection( &Table->Lock );

            if (!NT_SUCCESS(Status))
            {
                if ( !HandleTable )
                {
                    LsapFreePrivateHeap( Table );
                }

                Table = NULL ;
            }
        }
    }

    if ( Table )
    {

        for ( i = 0 ; i < HANDLE_TABLE_SIZE ; i++ )
        {
            SmallHandlePackage.Create( Flags | HANDLE_PACKAGE_NO_SERIALIZE,
                                        & Table->Lists[i],
                                        Callback );
        }

    }

    return Table ;

}

//+---------------------------------------------------------------------------
//
//  Function:   LhtpDeleteTable
//
//  Synopsis:   Delete table worker function
//
//  Arguments:  [Table]    --
//              [Callback] --
//
//  History:    4-15-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
VOID
LhtpDeleteTable(
    PLARGE_HANDLE_TABLE Table,
    PHP_ENUM_CALLBACK_FN Callback
    )
{
    ULONG Index ;
    PSEC_HANDLE_ENTRY Entry ;
    PLIST_ENTRY Scan ;

    LhtLockTable( Table );

    for ( Index = 0 ; Index < HANDLE_TABLE_SIZE ; Index++ )
    {
        if ( Table->Lists[Index].Flags & LHT_SUB_TABLE )
        {
            LhtpDeleteTable( (PLARGE_HANDLE_TABLE) Table->Lists[Index].List.Flink,
                             Callback );

        }
        else
        {
            SmallHandlePackage.Delete( &Table->Lists[ Index ], Callback );
        }
    }

    LhtUnlockTable( Table );

    if ( (Table->Flags & LHT_NO_SERIALIZE) == 0 )
    {
        RtlDeleteCriticalSection(  &Table->Lock );
    }

    if ( ( Table->Flags & LHT_NO_FREE ) == 0 )
    {
        LsapFreePrivateHeap( Table );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   LhtDelete
//
//  Synopsis:   Delete a table
//
//  Arguments:  [HandleTable] --
//              [Callback]    --
//
//  History:    4-15-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtDelete(
    PVOID   HandleTable,
    PHP_ENUM_CALLBACK_FN Callback
    )
{
    PLARGE_HANDLE_TABLE Table ;

    Table = (PLARGE_HANDLE_TABLE) HandleTable ;

    LhtLockTable( Table );

    Table->Flags |= LHT_DELETE_PENDING ;

    LhtpDeleteTable( Table, Callback );

    return TRUE ;
}

//+---------------------------------------------------------------------------
//
//  Function:   LhtpFindHandle
//
//  Synopsis:   Worker function that grovels a handle table
//
//  Arguments:  [HandleTable] -- Table to scan
//              [Handle]      -- handle to search for
//              [Action]      -- action to take on the handle record
//
//  History:    2-03-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PSEC_HANDLE_ENTRY
LhtpFindHandle(
    PVOID   HandleTable,
    PSecHandle  Handle,
    ULONG   Action,
    PBOOL   Removed,
    PVOID * FinalTable OPTIONAL
    )
{
    PLARGE_HANDLE_TABLE Table ;
    PSEC_HANDLE_ENTRY   Entry ;
    PLIST_ENTRY         Scan ;
    ULONG Index ;
    BOOL Locked ;

    Table = (PLARGE_HANDLE_TABLE) HandleTable ;

    LhtLockTable( Table );

    Entry = NULL ;

    while ( TRUE )
    {
        Index = (ULONG) IndexFromHandle( Table->Depth, Handle );

        if ( Table->Lists[ Index ].Flags & LHT_SUB_TABLE )
        {
            Table = (PLARGE_HANDLE_TABLE) Table->Lists[ Index ].List.Flink ;

            continue;
        }

        Entry = ShtpFindHandle( &Table->Lists[ Index ], Handle, Action, Removed );

        if ( FinalTable )
        {
            *FinalTable = &Table->Lists[ Index ] ;
        }

        break;
    }

    Table = (PLARGE_HANDLE_TABLE) HandleTable ;

    LhtUnlockTable( Table );

    return Entry ;


}

//+---------------------------------------------------------------------------
//
//  Function:   LhtpConvertSmallToLarge
//
//  Synopsis:   Worker to convert small tables to large
//
//  Arguments:  [Small] --
//              [Large] --
//
//  History:    7-08-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtpConvertSmallToLarge(
    PSMALL_HANDLE_TABLE Small,
    PLARGE_HANDLE_TABLE Large
    )
{
    ULONG NewIndex ;
    PSEC_HANDLE_ENTRY Entry ;

    while ( Entry = ShtpPopHandle( Small ) )
    {
        NewIndex = (ULONG) IndexFromHandle( Large->Depth, &(Entry->Handle) );

        ShtpInsertHandle( &Large->Lists[ NewIndex ], Entry );

    }

    return TRUE ;
}


//+---------------------------------------------------------------------------
//
//  Function:   LhtpExpandTable
//
//  Synopsis:   Expands the given index into its own table
//
//  Effects:    New table associated with given index
//
//  Arguments:  [Table] -- Source table
//              [Index] -- Source index
//
//  Requires:   Table must be write-locked
//
//  History:    1-31-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtpExpandTable(
   PLARGE_HANDLE_TABLE  Table,
   ULONG    Index
   )
{
    PLARGE_HANDLE_TABLE NewTable ;
    PLIST_ENTRY List;
    ULONG NewFlags ;

    NewFlags = HANDLE_PACKAGE_NO_SERIALIZE ;

    if ( Table->DeleteCallback )
    {
        NewFlags |= HANDLE_PACKAGE_CALLBACK_ON_DELETE ;
    }

    NewTable = (PLARGE_HANDLE_TABLE) LhtCreate( NewFlags |
                                                    LHT_CHILD,
                                                NULL,
                                                Table->DeleteCallback );

    if ( !NewTable )
    {
        return FALSE ;
    }

    NewTable->Depth = Table->Depth + 1 ;

    NewTable->Parent = Table ;

    NewTable->IndexOfParent = Index ;

    LhtpConvertSmallToLarge( &Table->Lists[ Index ], NewTable );

    Table->Lists[ Index ].List.Flink = (PLIST_ENTRY) NewTable ;

    Table->Lists[ Index ].Flags = LHT_SUB_TABLE ;

    return TRUE ;

}

//+---------------------------------------------------------------------------
//
//  Function:   LhtConvertSmallToLarge
//
//  Synopsis:   Converts a small handle table to a large one
//
//  Arguments:  [Small] --
//
//  History:    7-08-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PVOID
LhtConvertSmallToLarge(
    PVOID Small
    )
{
    PLARGE_HANDLE_TABLE Large ;
    PSMALL_HANDLE_TABLE SmallTable = (PSMALL_HANDLE_TABLE) Small ;
    PULONG Tag ;

    Tag = (PULONG) Small ;

    if ( *Tag == LHT_TAG )
    {
        return Small ;
    }

    if ( *Tag != SHT_TAG )
    {
        return NULL ;
    }

    Large = (PLARGE_HANDLE_TABLE) LhtCreate( (SmallTable->DeleteCallback ?
                                                HANDLE_PACKAGE_CALLBACK_ON_DELETE : 0),
                                             NULL,
                                             SmallTable->DeleteCallback );

    if ( Large )
    {
        LhtpConvertSmallToLarge( SmallTable, Large );

        ShtDelete( Small, NULL );

        return Large ;
    }

    return NULL ;
}

//+---------------------------------------------------------------------------
//
//  Function:   LhtAddHandle
//
//  Synopsis:   Add a handle to a handle table
//
//  Arguments:  [HandleTable] -- Table to add the handle to
//              [Handle]      -- Handle to add
//
//  History:    2-03-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtAddHandle(
    PVOID   HandleTable,
    PSecHandle  Handle,
    PVOID   Context,
    ULONG Flags
    )
{
    PSEC_HANDLE_ENTRY   Entry ;
    PLARGE_HANDLE_TABLE Table ;
    ULONG Index ;


    Table = (PLARGE_HANDLE_TABLE) HandleTable ;

    LhtLockTable( Table );

    Entry = LhtpFindHandle( HandleTable, 
                            Handle, 
                            LHT_ACTION_ADDHANDLE, 
                            NULL, 
                            NULL );

    if ( Entry )
    {
        LhtUnlockTable( Table );

        return TRUE ;
    }

    //
    // No entry, need to insert one.
    //

    while ( TRUE )
    {
        Index = (ULONG) IndexFromHandle( Table->Depth, Handle );

        if ( Table->Lists[ Index ].Flags & LHT_SUB_TABLE )
        {
            Table = (PLARGE_HANDLE_TABLE) Table->Lists[ Index ].List.Flink ;

            continue;
        }

        if(SmallHandlePackage.AddHandle( &Table->Lists[ Index ], Handle, Context, Flags ))
        {
            if ( Table->Lists[ Index ].Count > HANDLE_SPLIT_THRESHOLD )
            {
                LhtpExpandTable( Table, Index );
            }

            break;
        }

        LhtUnlockTable( (PLARGE_HANDLE_TABLE)HandleTable );
        return FALSE;
    }

    Table = (PLARGE_HANDLE_TABLE) HandleTable ;

    Table->Count++;

    LhtUnlockTable( Table );

    return TRUE ;
}

//+---------------------------------------------------------------------------
//
//  Function:   LhtDeleteHandle
//
//  Synopsis:   Delete a handle from the table
//
//  Arguments:  [HandleTable] -- Table
//              [Handle]      -- Handle to delete
//              [Force]       -- Force delete, even if handle is not ref'd to zero
//
//  History:    2-03-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtDeleteHandle(
    PVOID       HandleTable,
    PSecHandle  Handle,
    ULONG       Options
    )
{
    PSEC_HANDLE_ENTRY Entry ;
    PLARGE_HANDLE_TABLE Table ;
    BOOL Removed;
    ULONG Action ;

    if ( Options & DELHANDLE_FORCE )
    {
        Action = LHT_ACTION_FORCEDEL ;
    }
    else if ( Options & LHTP_DEREF_NOT_DEL )
    {
        Action = LHT_ACTION_DELREF | LHTP_HANDLE_CHECKED ;
    }
    else 
    {
        Action = LHT_ACTION_DELHANDLE ;
    }


    Entry = LhtpFindHandle( HandleTable,
                           Handle,
                           Action,
                           &Removed,
                           NULL );

    if ( Entry )
    {
        if ( Removed )
        {
            Table = (PLARGE_HANDLE_TABLE) HandleTable ;

            LhtLockTable( Table );

            Table->Count--;

            LhtUnlockTable( Table );

            if ( (Table->DeleteCallback) &&
                 ((Options & DELHANDLE_NO_CALLBACK) == 0 ) &&
                 ((Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
            {
                Table->DeleteCallback(
                                &Entry->Handle,
                                Entry->Context,
                                Entry->HandleIssuedCount    // Entry->RefCount
                                );
            }

            if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
            {
                LsapFreePrivateHeap( Entry );
            }
        }

        return TRUE ;
    }

    return FALSE ;
}


//+---------------------------------------------------------------------------
//
//  Function:   LhtValidateHandle
//
//  Synopsis:   Validate that a handle is within the table
//
//  Arguments:  [HandleTable] --
//              [Handle]      --
//
//  History:    2-03-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOL
LhtValidateHandle(
    PVOID       HandleTable,
    PSecHandle  Handle,
    BOOL        Deref
    )
{
    PSEC_HANDLE_ENTRY Entry ;
    PLARGE_HANDLE_TABLE Table ;
    BOOL Removed ;

    Entry = LhtpFindHandle( 
                HandleTable, 
                Handle, 
                (Deref ? LHT_ACTION_DELHANDLE : LHT_ACTION_VALIDATE), 
                &Removed,
                NULL );

    if ( Entry )
    {

        if ( Removed )
        {
            Table = (PLARGE_HANDLE_TABLE) HandleTable ;

            LhtLockTable( Table );

            Table->Count--;

            LhtUnlockTable( Table );

            if ( ( Table->DeleteCallback ) &&
                 ( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK) == 0 ) )
            {
                Table->DeleteCallback(
                            &Entry->Handle,
                            Entry->Context,
                            Entry->HandleIssuedCount    // Entry->RefCount
                            );
            }

            if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
            {
                LsapFreePrivateHeap( Entry );
            }
        }

        return TRUE ;
    }
    else
    {
        return FALSE ;
    }
}

PVOID
LhtRefHandle(
    PVOID HandleTable,
    PSecHandle Handle
    )
{
    PSEC_HANDLE_ENTRY Entry ;

    Entry = LhtpFindHandle(
                HandleTable,
                Handle,
                LHT_ACTION_ADDREF,
                NULL,
                NULL );

    return Entry ;
}

VOID
LhtDerefHandleKey(
    PVOID HandleTable,
    PVOID HandleKey
    )
{
    PSEC_HANDLE_ENTRY Entry = (PSEC_HANDLE_ENTRY) HandleKey ;

    LhtDeleteHandle( HandleTable, &Entry->Handle, LHTP_DEREF_NOT_DEL );
}

PVOID
LhtGetHandleContext(
    PVOID       HandleTable,
    PSecHandle  Handle
    )
{
    PSEC_HANDLE_ENTRY Entry ;

    Entry = LhtpFindHandle( 
                HandleTable, 
                Handle, 
                LHT_ACTION_ADDREF, 
                NULL,
                NULL );

    if ( Entry )
    {
        return Entry->Context ;
    }
    else
    {
        return NULL ;
    }
}


BOOL
LhtReleaseContext(
    PVOID       HandleTable,
    PSecHandle  Handle
    )
{
    PSEC_HANDLE_ENTRY Entry ;
    PLARGE_HANDLE_TABLE Table ;
    BOOL Removed;

    Entry = LhtpFindHandle( HandleTable,
                           Handle,
                           LHT_ACTION_DELREF,
                           &Removed,
                           NULL );

    if ( Entry )
    {
        if ( Removed )
        {
            Table = (PLARGE_HANDLE_TABLE) HandleTable ;

            LhtLockTable( Table );

            Table->Count--;

            LhtUnlockTable( Table );

            if ( ( Table->DeleteCallback ) &&
                 ( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
            {
                Table->DeleteCallback( &Entry->Handle, Entry->Context, Entry->RefCount );
            }

            if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
            {
                LsapFreePrivateHeap( Entry );
            }
        }

        return TRUE ;
    }

    return FALSE ;
}