////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   MemoryStorage.cpp
//
//  Note:   Kernel & Process
//

#include "hal.h"
#include "eventing.h"

#ifndef SINGULARITY_KERNEL
void DebugPrintEvent(UIntPtr eventHandle)
{
    Class_Microsoft_Singularity_Eventing_SystemControllerProxy::g_DebugPrintLogEntry(eventHandle);
}
#endif

uint32
MemoryStorageGetNextGeneration(PMEMORY_STORAGE Storage)
{
    return (uint32)InterlockedIncrement((volatile INT32* )&Storage->Generation);
}

PMEMORY_HEADER
MemoryStorageAdvanceCursor(PMEMORY_STORAGE Storage,
                           PMEMORY_ZONE CurrentCursor,
                           uint16 size)
{
    PMEMORY_ZONE NewCursor;
    PMEMORY_ZONE CapturedCursor = Storage->ZoneCursor;
    BOOL Reycled = false;

    NewCursor = CurrentCursor;

    if (NewCursor == NULL) {

        NewCursor = Storage->MemoryZoneLink;
    }

    for (NewCursor = NewCursor->Link; NewCursor != CurrentCursor; NewCursor = NewCursor->Link) {

        if (NewCursor == NULL) {

            //  Restart from the begining of the storage
            //  Make sure the policy allows this

            if (Storage->Flags & MEMORY_STORAGE_FLAGS_RECYCLE_MEMORY) {

                if (Reycled) {

                    // Every zone has been tried for this allocation
                    // return back the failure.

                    return NULL;
                }

                if (Storage->Flags & MEMORY_STORAGE_FLAGS_BREAK_ON_RECYCLE) {

                    // If we trap here, there is a request to stop before over-writing
                    // any buffer contents.  That is about to happen, so this is the
                    // last chance to retrieve those contents before they are lost.
                    //
                    // Then proceed from this breakpoint.

                    __debugbreak();
                }

                Reycled = true;

                NewCursor = Storage->MemoryZoneLink;

            } else {

                // We must return here failure, as recycling the memory for older events
                // is not permited

                return NULL;
            }

        }

        if (IsZoneCompleted(NewCursor)) {

            RecycleZone(NewCursor);
        }

        PMEMORY_HEADER Event = AllocateEventEntry(NewCursor, size);
        if (Event != NULL) {

            // we were successfuly in using this zone, set it as current cursor
            // It is possible some other thread already did it, or even moved far ahead
            // But this should not be a problem as there are no guarantees wrt. ordering
            // in the storage

            InterlockedCompareExchangePointer((PVOID *)&Storage->ZoneCursor,
                    (PVOID)NewCursor,
                    (PVOID)CapturedCursor);
            return Event;
        }
    }

    return NULL;
}


uint32
CaptureStackTrace(UIntPtr * StackArray, UINT MaxStackSize)
{

    uint32 Index;
    UIntPtr CurrentFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFramePointer();
    UIntPtr CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CurrentFrame);

    Struct_Microsoft_Singularity_ProcessorContext *processorContext =
        Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext();

    if ((CurrentFrame <= (UIntPtr)processorContext->cpuRecord.interruptStackBegin) &&
        (CurrentFrame > (UIntPtr)processorContext->cpuRecord.interruptStackLimit)) {

        //  Capture the interrupte stack. Always check against the limits
        //  before fetching the next frame

        for (Index = 0; (CallerFrame != 0) && Index < MaxStackSize; Index += 1) {

            if ((CallerFrame > (UIntPtr)processorContext->cpuRecord.interruptStackBegin) ||
                (CallerFrame < (UIntPtr)processorContext->cpuRecord.interruptStackLimit)) {

                return Index;
            }
            StackArray[Index] = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameReturnAddress(CallerFrame);
            CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CallerFrame);
        }
        return Index;
    }


    for (Index = 0; (CallerFrame != 0) && Index < MaxStackSize; Index += 1) {

        StackArray[Index] = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameReturnAddress(CallerFrame);
        CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CallerFrame);
    }
    return Index;
}



PMEMORY_HEADER
InternalLogRecord(UIntPtr StorageHandle,
    uint32 Flags,
    UIntPtr eventType,
    PVOID Buffer,
    uint32 size,
    PVOID * ExtendedBuffer,
    uint32 ExtendedSize )
{
    if (StorageHandle == 0) return NULL;

    PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)StorageHandle;

    UIntPtr Stacks[RECORD_MAXSTACKSIZE];
    uint32 StackSize = 0;
    uint32 allocSize = (uint32)ROUND_UP_TO_POWER2(size, sizeof(UIntPtr));

    if (Flags & RECORD_STACK_TRACES) {

        StackSize = CaptureStackTrace(Stacks, RECORD_MAXSTACKSIZE);

        if (StackSize) {

            // There is no point to save the eip of the current function in the database
            // since is redundant information. Reuse instead that slot to keep the stack size

            Stacks[0] = (UIntPtr)(StackSize - 1);

            // Convert to memory usage

            StackSize *= sizeof(UIntPtr);
        } else {

            // clear the flag as there is no stack available
            Flags &= ~RECORD_STACK_TRACES;
        }
    }

    PMEMORY_ZONE Zone = Storage->ZoneCursor;

    if (Zone == NULL) {

        Zone = Storage->MemoryZoneLink;
    }

    UINT32 EntrySize = GetRecordHeaderSize (Flags, StackSize);

    PMEMORY_HEADER Event = AllocateEventEntry(Zone,
            (uint16)(EntrySize + allocSize + ExtendedSize - sizeof(MEMORY_HEADER)));

    if (Event == NULL) {

        //  The zone is filled up, advance the cursor to the next zone

        Event = MemoryStorageAdvanceCursor(Storage, Zone,
            (uint16)(EntrySize + allocSize + ExtendedSize - sizeof(MEMORY_HEADER)));
    }

    if (Event != NULL) {

        // Copy the entry flags, which also include the layout description

        Event->Flags = (uint16)Flags;
        Event->Type = eventType;
        // Handle the stack traces, if present

        UCHAR * Dest = (UCHAR *)GetRecordInternalStructure(Event, RECORD_STACK_TRACES);

        if (Dest != NULL) {

            memcpy(Dest, Stacks, StackSize);
        }

        //  Copy the remaining portion provided by the user to the buffer

        if (Buffer) {

            memcpy((char *)Event + EntrySize, Buffer, size);
        }

        if (ExtendedSize != 0) {

            *ExtendedBuffer = (char *)Event + EntrySize + allocSize;
        }

    }

    return Event;
}

PMEMORY_HEADER
InternalLogFixedRecord(UIntPtr StorageHandle,
    uint32 Flags,
    UIntPtr eventType,
    PVOID Buffer,
    uint32 size)
{
    PMEMORY_HEADER entry = InternalLogRecord(StorageHandle, Flags, eventType, Buffer, size, NULL, 0);

    if (entry != NULL) {

        if (Flags & Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_DEBUG_PRINT){

            DebugPrintEvent((UIntPtr)entry);
        }

        CommitEventEntry(entry);
    }

    return entry;
}

PMEMORY_HEADER
InternalLogVariableRecord(
    bool doCommit,
    UIntPtr StorageHandle,
    uint32 Flags,
    UIntPtr eventType,
    PVOID Buffer,
    uint32 size,
    int32 variableItemsCount,
    Struct_Microsoft_Singularity_Eventing_ArrayType * variableItems)
{
    if (StorageHandle == 0) return NULL;

    PVOID ExtendedBuffer;
    int i;
    int arrayDescriptorSize = variableItemsCount * sizeof(uint16);

    int32 extendedSize =  arrayDescriptorSize;

    for (i = 0; i < variableItemsCount; i++) {

        extendedSize += (int32)variableItems[i].Length;

        if ((variableItems[i].Type == EVENT_FIELD_TYPE_string)
                ||
            (variableItems[i].Type == EVENT_FIELD_TYPE_szChar)) {

            // Account for the null terminator that ConvertToChars automatically
            // inserts to the string. For szChar data, the string will also have one
            // null character at the end, that has not been encounted for

            extendedSize += 1;
        }
    }

    PMEMORY_HEADER Entry = InternalLogRecord(StorageHandle,
                                             Flags,
                                             eventType,
                                             Buffer,
                                             size,
                                             &ExtendedBuffer,
                                             extendedSize);

    if (Entry == NULL) {
        return Entry;
    }

    if (extendedSize > 0) {

        //  Note the test for extendedSize. This is the only case where we are
        //  allowed to add something after calling InternalLogRecord. We are also required
        //  explicitely commit the entry when we're done

        for (i = 0; i < variableItemsCount; i++) {

            //
            //  For strings, the mechanism wil automatically extend the buffer with one
            //  extra character to include the null terminator. The length in the field
            //  should be however consistent and report the correct length of the buffer
            //

            if (variableItems[i].Type == EVENT_FIELD_TYPE_string) {

                // Account for the null terminator that ConvertToChars automatically
                // inserts to the string.

                unsigned short adjustedLength = variableItems[i].Length + 1;

                C_ASSERT(sizeof(adjustedLength) == sizeof(variableItems[i].Length));
                memcpy(ExtendedBuffer, &adjustedLength, sizeof(adjustedLength));
                ExtendedBuffer = (char *)ExtendedBuffer + sizeof(variableItems[i].Length);


                //  If this was a bartok string, convert it to ascii

                ExtendedBuffer = ConvertToChars((char *)ExtendedBuffer,
                                                (bartok_char *)variableItems[i].Buffer,
                                                (int32)variableItems[i].Length);


                EV_ASSERT(ExtendedBuffer <= (PVOID)((char*)Entry + Entry->Size ));

            } else  if (variableItems[i].Type == EVENT_FIELD_TYPE_szChar) {

                // Account for the null terminator that is automatically
                // inserted to the string.

                unsigned short adjustedLength = variableItems[i].Length + 1;

                //
                //  Save the total size in bytes and save it to the entry. Move the pointer forward
                //

                C_ASSERT(sizeof(adjustedLength) == sizeof(variableItems[i].Length));
                memcpy(ExtendedBuffer, &adjustedLength, sizeof(adjustedLength));
                ExtendedBuffer = (char *)ExtendedBuffer + sizeof(adjustedLength);

                //
                //  Copy now the actual content of the buffer
                //

                memcpy(ExtendedBuffer, variableItems[i].Buffer, (int32)variableItems[i].Length);
                ExtendedBuffer = (char *)ExtendedBuffer + variableItems[i].Length;
                *(char *)ExtendedBuffer = (char)0;
                ExtendedBuffer = (char *)ExtendedBuffer + 1;

            } else {

                //
                //  Save the length of the data and advance the pointer
                //

                memcpy(ExtendedBuffer, &variableItems[i].Length, sizeof(variableItems[i].Length));
                ExtendedBuffer = (char *)ExtendedBuffer + sizeof(variableItems[i].Length);

                // nothing else to do here, just copy the content and advance the pointer

                memcpy(ExtendedBuffer, variableItems[i].Buffer, (int32)variableItems[i].Length);
                ExtendedBuffer = (char *)ExtendedBuffer + variableItems[i].Length;
            }

            EV_ASSERT(ExtendedBuffer <= (PVOID)((char*)Entry + Entry->Size ));

        }

    }

    if (Flags & Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_DEBUG_PRINT){

        DebugPrintEvent((UIntPtr)Entry);
    }

    if (doCommit) {

        CommitEventEntry(Entry);
    }


    return Entry;
}


UIntPtr
OpenLoggingStorage(UIntPtr sourceHandle, uint32 * flags)
{
    if (sourceHandle == 0) return 0;

    PSOURCE_DESCRIPTOR source = HANDLE_TO_SOURCE(sourceHandle);
    if (((source->ControlFlags & (*flags)) >> 16) == 0) return 0;

    *flags |= source->ControlFlags;
    return source->StorageHandle;
}


PMEMORY_ZONE
GetNextZone(PQUERY_VIEW view)
{
    if (view->StartZone == NULL) {
        return NULL;
    }

    for (;;) {

        if (view->Forward) {

            view->CurrentZone = view->CurrentZone->Link;

            if (view->CurrentZone == NULL) {

                view->CurrentZone = view->Storage->MemoryZoneLink;
            }
        } else {

            view->CurrentZone = view->CurrentZone->BkLink;

            if (view->CurrentZone == NULL) {

                view->CurrentZone = view->Storage->BkLink;
            }
        }

        if (view->CurrentZone == view->StartZone) {

            return NULL;
        }

        //  ??? handle the generation wrap

        if (view->CurrentZone->Generation <= view->QueryGeneration) {

            view->CurrentEntryIndex = 0;
            view->ZoneGeneration = view->CurrentZone->Generation;
            return view->CurrentZone;
        }

    }
}

PQUERY_VIEW
CreateQuery(UIntPtr storageHandle, bool forward)
{
    PQUERY_VIEW queryView = AllocateQueryView( );

    if (queryView) {

        queryView->Storage = HANDLE_TO_STORAGE(storageHandle);
        queryView->Forward = forward;
        queryView->QueryGeneration = queryView->Storage->Generation;

        queryView->CurrentZone = NULL;
        queryView->ZoneGeneration = 0;
        queryView->CurrentEntry = NULL;
        queryView->CurrentEntryIndex = 0;
        queryView->EndOfBuffer = false;

        if (forward) {

            queryView->StartZone = queryView->Storage->ZoneCursor;

            if (queryView->StartZone != NULL) {

                queryView->StartZone = queryView->StartZone->Link;
            }

            if (queryView->StartZone == NULL) {

                queryView->StartZone  = queryView->Storage->MemoryZoneLink;
            }

        } else {

            queryView->StartZone = queryView->Storage->ZoneCursor;

            if (queryView->StartZone == NULL) {

                queryView->StartZone  = queryView->Storage->BkLink;
            }
        }

        queryView->CurrentZone = queryView->StartZone;
        queryView->QueryReset = true;

   }

    return queryView;
}

PMEMORY_HEADER
GetNextStorageEntry(PQUERY_VIEW queryView)
{
    PMEMORY_HEADER Entry;

    if (queryView->EndOfBuffer) {

        return NULL;
    }

    if (queryView->CurrentEntry) {

        Entry = GetNextEntry(queryView);

        if (Entry) {

            return Entry;
        }
    }

    PMEMORY_ZONE zone;
    if (queryView->QueryReset) {

        zone = queryView->StartZone;
        queryView->QueryReset = false;
        queryView->ZoneGeneration = zone->Generation;
        queryView->CurrentEntryIndex = 0;

    } else {

        zone = GetNextZone(queryView);
    }

    while (zone != NULL) {

        queryView->CurrentEntry = GetFirstEntry(zone, queryView->Forward);

        if (queryView->CurrentEntry) {
            return queryView->CurrentEntry;
        }

        zone = GetNextZone(queryView);
    }

    queryView->EndOfBuffer = true;

    return NULL;
}

//
//  ABI calls for Eventing.MemoryStorage
//

UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_MemoryStorageCreateImpl(uint32 Flags, UCHAR * InitialBuffer, uint32 BufferSize, uint32 ZoneSize)
{
    if (BufferSize <= sizeof(MEMORY_STORAGE)) {

        return 0;
    }

    PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)InitialBuffer;
    Storage->StorageSize = 0;
    Storage->MemoryZoneLink = NULL;
    Storage->ZoneCursor = NULL;
    Storage->Flags = Flags;
    Storage->BkLink = NULL;
    Storage->Generation = 0;

    ZoneSize = (uint32)ROUND_UP_TO_POWER2(ZoneSize, EV_ZONE_ALIGNMENT);

    if (((Flags & 0xF) == MEMORY_STORAGE_FLAGS_PERMANENT)
            ||
        ((Flags & 0xF) == MEMORY_STORAGE_FLAGS_ACTIVE_STORAGE) ) {

        ZoneSize = BufferSize;
    }

    if (ZoneSize < sizeof(MEMORY_ZONE)) {

        ZoneSize = BufferSize / EV_DEFAULT_ZONE_BUFFER_RATIO;
        ZoneSize = (uint32)ROUND_UP_TO_POWER2(ZoneSize, EV_ZONE_ALIGNMENT);
    }

    if (ZoneSize > EV_MAXIMUM_ZONE_SIZE) {
        Storage->DefaultZoneSize = EV_MAXIMUM_ZONE_SIZE;

    } else {

        Storage->DefaultZoneSize = ZoneSize;
    }

    Storage->ZoneCount = 0;
    InitialBuffer = (UCHAR *)(Storage + 1);
    InitialBuffer = (UCHAR *)ROUND_UP_TO_POWER2(InitialBuffer, EV_ZONE_ALIGNMENT);

    Class_Microsoft_Singularity_Eventing_MemoryStorage::g_MemoryStorageRegisterBufferImpl(
        (UIntPtr)Storage,
        InitialBuffer,
        (uint32)(BufferSize - ((uint8 *)InitialBuffer - (uint8 *)Storage)));

    Storage->ZoneCursor = Storage->MemoryZoneLink;

    return (UIntPtr)Storage;
}

void
Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_MemoryStorageRegisterBufferImpl(UIntPtr StorageHandle, UCHAR * Buffer, uint32 BufferSize)
{

    PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)StorageHandle;
    PMEMORY_ZONE LastZone = NULL;
    PMEMORY_ZONE ZoneChain = NULL;
    PMEMORY_ZONE TempZone;
    PMEMORY_ZONE LastExistingZone = Storage->BkLink;

    InterlockedExchangeAdd((volatile INT32 *)&Storage->StorageSize, BufferSize);

    if ((Storage->DefaultZoneSize != 0) && (BufferSize > Storage->DefaultZoneSize)) {

        //  We have to split the larger chunk in multiple zones

        uint32 MaxZone = BufferSize / Storage->DefaultZoneSize;

        for (uint32 i = 0; i < MaxZone; i++) {

            TempZone = InitializeMemoryZone(Buffer, Storage->DefaultZoneSize, StorageHandle);
            EV_ASSERT(TempZone != NULL);

            InterlockedIncrement((volatile INT32* )&Storage->ZoneCount);

            if (ZoneChain == NULL) {

                ZoneChain = TempZone;
            }

            //  Link the zones togeather. Note the chain is not yet published outside
            //  until is pushed to the memory storage list eventually

            if (LastZone) {

                LastZone->Link = TempZone;
            }

            TempZone->BkLink = LastZone;
            LastZone = TempZone;

            Buffer = (UCHAR *)Buffer + Storage->DefaultZoneSize;
            BufferSize -= Storage->DefaultZoneSize;

        }

    }

    TempZone = InitializeMemoryZone(Buffer, BufferSize, StorageHandle);

    if (TempZone != NULL) {

        InterlockedIncrement((volatile INT32 *)&Storage->ZoneCount);

        // Here the remining portion might be too small for a whole zone.

        if (ZoneChain == NULL) {

            ZoneChain = TempZone;
        }

        //  Link the zones togeather. Note the chain is not yet published outside
        //  until is pushed to the memory storage list eventually

        if (LastZone) {

            LastZone->Link = TempZone;
        }

        TempZone->BkLink = LastZone;
        LastZone = TempZone;
    }

    if (LastZone != NULL) {

        LastZone->Link = NULL;

        if (LastExistingZone == NULL) {

            // The existing list is empty. Insert the new one to the head

            ZoneChain->BkLink = NULL;
            Storage->BkLink = LastZone;
            Storage->MemoryZoneLink = ZoneChain;

        } else {

            LastExistingZone->Link = ZoneChain;
            ZoneChain->BkLink = LastExistingZone;

        }

        Storage->BkLink = LastZone;
    }

}


UIntPtr Class_Microsoft_Singularity_Eventing_EventSource::
g_LogSourceEntryImpl(UIntPtr sourceHandle,
                     uint32 flags,
                     UIntPtr eventType,
                     uint8 * Buffer,
                     int32 size)
{
    UIntPtr storageHandle = OpenLoggingStorage(sourceHandle, &flags);
    return (UIntPtr)InternalLogFixedRecord(storageHandle,
                                           flags,
                                           eventType,
                                           Buffer,
                                           size);
}

UIntPtr Class_Microsoft_Singularity_Eventing_EventSource::
    g_LogSourceEntryImpl(UIntPtr sourceHandle,
                         uint32 flags,
                         UIntPtr eventType,
                         uint8 * Buffer,
                         int32 size,
                         int32 arraysCount,
                         Struct_Microsoft_Singularity_Eventing_ArrayType * arrays)
{
    UIntPtr storageHandle = OpenLoggingStorage(sourceHandle, &flags);
    return (UIntPtr)InternalLogVariableRecord(true,
                                              storageHandle,
                                              flags,
                                              eventType,
                                              Buffer,
                                              size,
                                              arraysCount,
                                              arrays);
}


uint32 Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_GetMemoryStorageOveheadImpl()
{
    // Determine the overhead in the pesimistic case

    return sizeof(MEMORY_STORAGE) +
           (sizeof(MEMORY_ZONE) + EV_ZONE_ALIGNMENT) * EV_DEFAULT_ZONE_BUFFER_RATIO +
           sizeof(MEMORY_HEADER) * EV_DEFAULT_ZONE_BUFFER_RATIO;
}

UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_CreateQueryViewImpl(UIntPtr storageHandle, bool forward)
{
    return (UIntPtr)CreateQuery(storageHandle, forward);
}

void Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_DeleteQueryViewImpl(UIntPtr queryHandle)
{
    UnRegisterQueryView((PQUERY_VIEW)queryHandle);
}

UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_GetNextEntryImpl(UIntPtr queryHandle,
                  UIntPtr * typeHandle,
                  uint32 * userOffset,
                  uint8 * buffer,
                  uint16 bufferSize)
{
    PQUERY_VIEW view = (PQUERY_VIEW)queryHandle;

    PMEMORY_HEADER entry = GetNextStorageEntry(view);

    if (entry != NULL) {

        *typeHandle = entry->Type;

        void * src = GetUserRecordStructure(entry);

        if (entry->Size < bufferSize) {

            bufferSize = entry->Size;
        }

        memcpy(buffer, entry , bufferSize);
        *userOffset = (uint32)((ULONG_PTR)src - (ULONG_PTR)entry);
    }

    return (UIntPtr) entry;
}

UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage::
g_WalkEventDescriptorImpl(UIntPtr eventHandle,
                          UIntPtr currentField,
                          uint16 * offset,
                          uint16 * type,
                          bartok_char * bufferName,
                          uint16 bufferSize)
{
    if (eventHandle == 0) {

        eventHandle = Handle_MEMORY_HEADER;
    }

    PMEMORY_HEADER Entry = HANDLE_TO_HEADER(eventHandle);

    EV_ASSERT(Entry->Flags == RECORD_EVENT_TYPE);

    PEVENT_DESCRIPTOR eventDescriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(Entry);
    PEVENT_FIELD_DESCRIPTOR field = NULL;
    char * src;

    if (currentField == 0) {

        src = GetExtendedString(eventHandle, 1);

    } else if (currentField == eventHandle) {

        // start now with the fields inside the event

        field = eventDescriptor->fieldsLink;

    } else {

        field = (PEVENT_FIELD_DESCRIPTOR)(currentField);
        field = field->fieldsLink;

        if (field == NULL) {

            return 0;
        }
    }

    if (field == NULL) {

        //  This is refers the event descriptor itself.
        //  The type returned will be zero, the only meaningful information
        //  will be the event name

        *offset = 0;
        *type = 0;

    } else {

        *offset = field->Offset;
        *type = field->Type;

        src = GetExtendedString((UIntPtr)((PMEMORY_HEADER)field - 1), 1);
    }

    if ((bufferName != NULL) && (bufferSize != 0)) {


        while ((bufferSize != 0) && (*src)) {

            *bufferName++ = *src++;
            bufferSize -= sizeof(bartok_char);
        }

        if (bufferSize == 0) {

            // Move back one position to insert the null terminator.
            // We have at least this character in the buffer due to the test two levels above

            bufferSize -= sizeof(bartok_char);
        }

        *bufferName = 0;
    }

    if (field == NULL) {

        return eventHandle;
    }

    return (UIntPtr)field;
}



//
///////////////////////////////////////////////////////////////// End of File.
