373 lines
8.3 KiB
C++
373 lines
8.3 KiB
C++
|
// memman.cpp : Defines the entry point for the console application.
|
|||
|
//
|
|||
|
|
|||
|
#include "kosSyst.h"
|
|||
|
#include "mcsmemm.h"
|
|||
|
|
|||
|
|
|||
|
void * __cdecl operator new ( size_t count, size_t element_size )
|
|||
|
{
|
|||
|
return allocmem( (Dword)(count * element_size) );
|
|||
|
}
|
|||
|
|
|||
|
void * __cdecl operator new [] ( size_t amount )
|
|||
|
{
|
|||
|
return allocmem( (Dword)amount );
|
|||
|
}
|
|||
|
|
|||
|
void * __cdecl operator new ( size_t amount )
|
|||
|
{
|
|||
|
return allocmem( (Dword)amount );
|
|||
|
}
|
|||
|
|
|||
|
void __cdecl operator delete ( void *pointer )
|
|||
|
{
|
|||
|
if ( pointer != NULL ) freemem( pointer );
|
|||
|
}
|
|||
|
|
|||
|
void __cdecl operator delete [] ( void *pointer )
|
|||
|
{
|
|||
|
if ( pointer != NULL ) freemem( pointer );
|
|||
|
}
|
|||
|
|
|||
|
void __cdecl initHeap(void)
|
|||
|
{
|
|||
|
__asm
|
|||
|
{
|
|||
|
push 68
|
|||
|
pop eax
|
|||
|
push 11
|
|||
|
pop ebx
|
|||
|
int 0x40
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#pragma data_seg(".CRT$XCB")
|
|||
|
__declspec(allocate(".CRT$XCB")) void (__cdecl *initHeapPtr)(void) = &initHeap;
|
|||
|
|
|||
|
__declspec(noinline) Byte* __fastcall allocmem( Dword reqsize )
|
|||
|
{
|
|||
|
initHeapPtr; // force dependency
|
|||
|
__asm
|
|||
|
{
|
|||
|
push 68
|
|||
|
pop eax
|
|||
|
push 12
|
|||
|
pop ebx
|
|||
|
int 0x40
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
__declspec(noinline) void __fastcall freemem( void *vaddress )
|
|||
|
{
|
|||
|
initHeapPtr; // force dependency
|
|||
|
__asm
|
|||
|
{
|
|||
|
push 68
|
|||
|
pop eax
|
|||
|
push 13
|
|||
|
pop ebx
|
|||
|
int 0x40
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
/*
|
|||
|
|
|||
|
//
|
|||
|
Dword mmMutex = FALSE;
|
|||
|
MemBlock *rootfree = NULL;
|
|||
|
MemBlock *rootuser = NULL;
|
|||
|
bool mmInitialized = false;
|
|||
|
Byte *mmHeapTop = NULL;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
Byte * AllocMemFromSystem( Dword reqSize )
|
|||
|
{
|
|||
|
Byte *result;
|
|||
|
sProcessInfo pInfo;
|
|||
|
|
|||
|
//
|
|||
|
if ( mmInitialized )
|
|||
|
{
|
|||
|
result = mmHeapTop;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
kos_ProcessInfo( &pInfo );
|
|||
|
//
|
|||
|
result = (Byte *)(pInfo.processInfo.used_memory + 1);
|
|||
|
//
|
|||
|
mmInitialized = true;
|
|||
|
}
|
|||
|
//
|
|||
|
if ( ! kos_ApplicationMemoryResize( ((Dword)result) + reqSize ) )
|
|||
|
{
|
|||
|
result = NULL;
|
|||
|
}
|
|||
|
//
|
|||
|
mmHeapTop = result + reqSize;
|
|||
|
//
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
Byte *allocmem( Dword reqsize )
|
|||
|
{
|
|||
|
MemBlock *BlockForCheck;
|
|||
|
MemBlock *LastKnownGood;
|
|||
|
Dword tail;
|
|||
|
Byte *address;
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if( ( tail = reqsize % SIZE_ALIGN ) != 0 )
|
|||
|
{
|
|||
|
reqsize += SIZE_ALIGN - tail;
|
|||
|
}
|
|||
|
|
|||
|
LastKnownGood = NULL;
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
while ( rtlInterlockedExchange( &mmMutex, TRUE ) )
|
|||
|
{
|
|||
|
//
|
|||
|
kos_Pause( 1 );
|
|||
|
}
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
|||
|
if( rootfree != NULL )
|
|||
|
{
|
|||
|
for ( BlockForCheck = rootfree; ; BlockForCheck = BlockForCheck->Next )
|
|||
|
{
|
|||
|
if ( BlockForCheck->Size >= reqsize )
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if ( LastKnownGood != NULL )
|
|||
|
{
|
|||
|
if ( LastKnownGood->Size >= BlockForCheck->Size )
|
|||
|
LastKnownGood = BlockForCheck;
|
|||
|
}
|
|||
|
else
|
|||
|
LastKnownGood = BlockForCheck;
|
|||
|
if ( LastKnownGood->Size == reqsize )
|
|||
|
break;
|
|||
|
}
|
|||
|
if ( BlockForCheck->Next == NULL )
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( LastKnownGood != NULL )
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
tail = LastKnownGood->Size - reqsize;
|
|||
|
if ( tail >= ( sizeof(MemBlock) + SIZE_ALIGN ) )
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
BlockForCheck = (MemBlock *)( ( (Byte *)LastKnownGood ) + tail );
|
|||
|
BlockForCheck->Size = reqsize;
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if( rootuser != NULL )
|
|||
|
{
|
|||
|
BlockForCheck->Next = rootuser;
|
|||
|
rootuser->Previous = BlockForCheck;
|
|||
|
BlockForCheck->Previous = NULL;
|
|||
|
rootuser = BlockForCheck;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
rootuser = BlockForCheck;
|
|||
|
BlockForCheck->Next = NULL;
|
|||
|
BlockForCheck->Previous = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
LastKnownGood->Size = tail - sizeof(MemBlock);
|
|||
|
address = ( (Byte *)BlockForCheck ) + sizeof(MemBlock);
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rtlInterlockedExchange( &mmMutex, FALSE );
|
|||
|
|
|||
|
return address;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if ( LastKnownGood->Previous != NULL )
|
|||
|
{
|
|||
|
LastKnownGood->Previous->Next = LastKnownGood->Next;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rootfree = LastKnownGood->Next;
|
|||
|
}
|
|||
|
if( LastKnownGood->Next != NULL )
|
|||
|
{
|
|||
|
LastKnownGood->Next->Previous = LastKnownGood->Previous;
|
|||
|
}
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if( rootuser != NULL )
|
|||
|
{
|
|||
|
LastKnownGood->Next = rootuser;
|
|||
|
rootuser->Previous = LastKnownGood;
|
|||
|
LastKnownGood->Previous = NULL;
|
|||
|
rootuser = LastKnownGood;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
rootuser = LastKnownGood;
|
|||
|
LastKnownGood->Next = NULL;
|
|||
|
LastKnownGood->Previous = NULL;
|
|||
|
}
|
|||
|
//
|
|||
|
address = ( (Byte *)LastKnownGood ) + sizeof(MemBlock);
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rtlInterlockedExchange( &mmMutex, FALSE );
|
|||
|
|
|||
|
return address;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
LastKnownGood = (MemBlock *)AllocMemFromSystem( reqsize + sizeof(MemBlock) );
|
|||
|
//
|
|||
|
if( LastKnownGood != NULL )
|
|||
|
{
|
|||
|
LastKnownGood->Size = reqsize;
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if( rootuser != NULL )
|
|||
|
{
|
|||
|
LastKnownGood->Next = rootuser;
|
|||
|
rootuser->Previous = LastKnownGood;
|
|||
|
LastKnownGood->Previous = NULL;
|
|||
|
rootuser = LastKnownGood;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
rootuser = LastKnownGood;
|
|||
|
LastKnownGood->Next = NULL;
|
|||
|
LastKnownGood->Previous = NULL;
|
|||
|
}
|
|||
|
address = ( (Byte *)LastKnownGood ) + sizeof(MemBlock);
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rtlInterlockedExchange( &mmMutex, FALSE );
|
|||
|
|
|||
|
return address;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rtlInterlockedExchange( &mmMutex, FALSE );
|
|||
|
|
|||
|
//
|
|||
|
rtlDebugOutString( "allocmem failed." );
|
|||
|
kos_ExitApp();
|
|||
|
//
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
Dword freemem( void *vaddress )
|
|||
|
{
|
|||
|
Dword result;
|
|||
|
|
|||
|
Byte *checknext, *address = (Byte *)vaddress;
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
while ( rtlInterlockedExchange( &mmMutex, TRUE ) )
|
|||
|
{
|
|||
|
//
|
|||
|
kos_Pause( 1 );
|
|||
|
}
|
|||
|
|
|||
|
MemBlock *released = (MemBlock *)( address - sizeof(MemBlock) );
|
|||
|
|
|||
|
result = released->Size;
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if ( released->Previous != NULL )
|
|||
|
{
|
|||
|
released->Previous->Next = released->Next;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
rootuser = released->Next;
|
|||
|
}
|
|||
|
if ( released->Next != NULL )
|
|||
|
{
|
|||
|
released->Next->Previous = released->Previous;
|
|||
|
}
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
released->Next = rootfree;
|
|||
|
released->Previous = NULL;
|
|||
|
rootfree = released;
|
|||
|
if ( released->Next != NULL )
|
|||
|
{
|
|||
|
released->Next->Previous = released;
|
|||
|
}
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
checknext = (Byte *)(rootfree) + ( rootfree->Size + sizeof(MemBlock) );
|
|||
|
//
|
|||
|
for ( released = rootfree->Next; released != NULL; released = released->Next )
|
|||
|
{
|
|||
|
if ( checknext == (Byte *)released )
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
released->Previous->Next = released->Next;
|
|||
|
if( released->Next != NULL )
|
|||
|
{
|
|||
|
released->Next->Previous = released->Previous;
|
|||
|
}
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rootfree->Size += released->Size + sizeof(MemBlock);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
//<2F><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
|||
|
checknext = (Byte *)(rootfree);
|
|||
|
//
|
|||
|
if ( released == NULL )
|
|||
|
{
|
|||
|
for ( released = rootfree->Next; released != NULL; released = released->Next )
|
|||
|
{
|
|||
|
if ( checknext == (Byte *)released + ( released->Size + sizeof(MemBlock) ) )
|
|||
|
{
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
released->Size += rootfree->Size + sizeof(MemBlock);
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
released->Previous->Next = released->Next;
|
|||
|
if ( released->Next != NULL )
|
|||
|
{
|
|||
|
released->Next->Previous = released->Previous;
|
|||
|
}
|
|||
|
//<2F> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
if ( rootfree->Next != NULL )
|
|||
|
{
|
|||
|
rootfree->Next->Previous = released;
|
|||
|
}
|
|||
|
released->Next = rootfree->Next;
|
|||
|
released->Previous = NULL;
|
|||
|
rootfree = released;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
rtlInterlockedExchange( &mmMutex, FALSE );
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
*/
|