加入squirrel 调试器
This commit is contained in:
@@ -48,7 +48,23 @@ set(SDL2TTF_BUILD_STATIC ON CACHE BOOL "Enable SDL2_ttf static library" FORCE)
|
|||||||
set(SDL2TTF_BUILD_EXAMPLES OFF CACHE BOOL "Disable SDL2_ttf examples" FORCE)
|
set(SDL2TTF_BUILD_EXAMPLES OFF CACHE BOOL "Disable SDL2_ttf examples" FORCE)
|
||||||
add_subdirectory(Library/SDL_ttf)
|
add_subdirectory(Library/SDL_ttf)
|
||||||
|
|
||||||
|
# 遍历所有目标,过滤可执行文件和实用工具
|
||||||
|
get_cmake_property(all_targets TARGETS)
|
||||||
|
if(all_targets) # 确保目标列表不为空
|
||||||
|
foreach(target ${all_targets})
|
||||||
|
# 跳过无效目标(如 NOTFOUND)
|
||||||
|
if(target STREQUAL "NOTFOUND")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
# 获取目标类型
|
||||||
|
get_target_property(target_type ${target} TYPE)
|
||||||
|
# 检查类型是否有效(避免非目标的无效值)
|
||||||
|
if(target_type AND (target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "UTILITY"))
|
||||||
|
message(STATUS "Excluding target: ${target} (type: ${target_type})")
|
||||||
|
set_target_properties(${target} PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
# 收集源文件
|
# 收集源文件
|
||||||
file(GLOB_RECURSE SOURCES
|
file(GLOB_RECURSE SOURCES
|
||||||
@@ -78,6 +94,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
|||||||
Library/squirrel/include
|
Library/squirrel/include
|
||||||
Library/squirrel/squirrel
|
Library/squirrel/squirrel
|
||||||
Library/squirrel/sqstdlib
|
Library/squirrel/sqstdlib
|
||||||
|
Library/sqdbg
|
||||||
source
|
source
|
||||||
source_game
|
source_game
|
||||||
)
|
)
|
||||||
|
|||||||
125
source/squirrel/sqdbg/debug.h
Normal file
125
source/squirrel/sqdbg/debug.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// github.com/samisalreadytaken/sqdbg
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SQDBG_DEBUG_H
|
||||||
|
#define SQDBG_DEBUG_H
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <crtdbg.h>
|
||||||
|
|
||||||
|
bool __IsDebuggerPresent();
|
||||||
|
const char *GetModuleBaseName();
|
||||||
|
|
||||||
|
#define DebuggerBreak() do { if ( __IsDebuggerPresent() ) __debugbreak(); } while(0)
|
||||||
|
|
||||||
|
#define Assert( x ) \
|
||||||
|
do { \
|
||||||
|
__CAT( L, __LINE__ ): \
|
||||||
|
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), #x)) ) \
|
||||||
|
{ \
|
||||||
|
if ( !__IsDebuggerPresent() ) \
|
||||||
|
goto __CAT( L, __LINE__ ); \
|
||||||
|
__debugbreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define AssertMsg( x, msg ) \
|
||||||
|
do { \
|
||||||
|
__CAT( L, __LINE__ ): \
|
||||||
|
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), msg)) ) \
|
||||||
|
{ \
|
||||||
|
if ( !__IsDebuggerPresent() ) \
|
||||||
|
goto __CAT( L, __LINE__ ); \
|
||||||
|
__debugbreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define AssertMsg1( x, msg, a1 ) \
|
||||||
|
do { \
|
||||||
|
__CAT( L, __LINE__ ): \
|
||||||
|
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), msg, a1)) ) \
|
||||||
|
{ \
|
||||||
|
if ( !__IsDebuggerPresent() ) \
|
||||||
|
goto __CAT( L, __LINE__ ); \
|
||||||
|
__debugbreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define AssertMsg2( x, msg, a1, a2 ) \
|
||||||
|
do { \
|
||||||
|
__CAT( L, __LINE__ ): \
|
||||||
|
if ( !(x) && (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, GetModuleBaseName(), msg, a1, a2)) ) \
|
||||||
|
{ \
|
||||||
|
if ( !__IsDebuggerPresent() ) \
|
||||||
|
goto __CAT( L, __LINE__ ); \
|
||||||
|
__debugbreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#else
|
||||||
|
extern "C" int printf(const char *, ...);
|
||||||
|
|
||||||
|
#define DebuggerBreak() asm("int3")
|
||||||
|
|
||||||
|
#define Assert( x ) \
|
||||||
|
do { \
|
||||||
|
if ( !(x) ) \
|
||||||
|
{ \
|
||||||
|
::printf("Assertion failed %s:%d: %s\n", __FILE__, __LINE__, #x); \
|
||||||
|
DebuggerBreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define AssertMsg( x, msg ) \
|
||||||
|
do { \
|
||||||
|
if ( !(x) ) \
|
||||||
|
{ \
|
||||||
|
::printf("Assertion failed %s:%d: %s\n", __FILE__, __LINE__, msg); \
|
||||||
|
DebuggerBreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define AssertMsg1( x, msg, a1 ) \
|
||||||
|
do { \
|
||||||
|
if ( !(x) ) \
|
||||||
|
{ \
|
||||||
|
::printf("Assertion failed %s:%d: ", __FILE__, __LINE__); \
|
||||||
|
::printf(msg, a1); \
|
||||||
|
::printf("\n"); \
|
||||||
|
DebuggerBreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define AssertMsg2( x, msg, a1, a2 ) \
|
||||||
|
do { \
|
||||||
|
if ( !(x) ) \
|
||||||
|
{ \
|
||||||
|
::printf("Assertion failed %s:%d: ", __FILE__, __LINE__); \
|
||||||
|
::printf(msg, a1, a2); \
|
||||||
|
::printf("\n"); \
|
||||||
|
DebuggerBreak(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#endif
|
||||||
|
#define Verify( x ) Assert(x)
|
||||||
|
#define STATIC_ASSERT( x ) static_assert( x, #x )
|
||||||
|
#else
|
||||||
|
#define DebuggerBreak() ((void)0)
|
||||||
|
#define Assert( x ) ((void)0)
|
||||||
|
#define AssertMsg( x, msg ) ((void)0)
|
||||||
|
#define AssertMsg1( x, msg, a1 ) ((void)0)
|
||||||
|
#define AssertMsg2( x, msg, a1, a2 ) ((void)0)
|
||||||
|
#define Verify( x ) x
|
||||||
|
#define STATIC_ASSERT( x )
|
||||||
|
#endif // _DEBUG
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define UNREACHABLE() do { Assert(!"UNREACHABLE"); __assume(0); } while(0)
|
||||||
|
#else
|
||||||
|
#include <cstdlib>
|
||||||
|
#define UNREACHABLE() do { Assert(!"UNREACHABLE"); __builtin_unreachable(); } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SQDBG_DEBUG_H
|
||||||
1
source/squirrel/sqdbg/folder-alias.json
Normal file
1
source/squirrel/sqdbg/folder-alias.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1351
source/squirrel/sqdbg/json.h
Normal file
1351
source/squirrel/sqdbg/json.h
Normal file
File diff suppressed because it is too large
Load Diff
849
source/squirrel/sqdbg/net.h
Normal file
849
source/squirrel/sqdbg/net.h
Normal file
@@ -0,0 +1,849 @@
|
|||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// github.com/samisalreadytaken/sqdbg
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SQDBG_NET_H
|
||||||
|
#define SQDBG_NET_H
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#include <debugapi.h>
|
||||||
|
|
||||||
|
inline bool __IsDebuggerPresent()
|
||||||
|
{
|
||||||
|
return IsDebuggerPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *GetModuleBaseName()
|
||||||
|
{
|
||||||
|
static char module[MAX_PATH];
|
||||||
|
int len = GetModuleFileNameA( NULL, module, sizeof(module) );
|
||||||
|
|
||||||
|
if ( len != 0 )
|
||||||
|
{
|
||||||
|
for ( char *pBase = module + len; pBase-- > module; )
|
||||||
|
{
|
||||||
|
if ( *pBase == '\\' )
|
||||||
|
return pBase + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
|
||||||
|
#undef RegisterClass
|
||||||
|
#undef SendMessage
|
||||||
|
#undef Yield
|
||||||
|
#undef CONST
|
||||||
|
#undef PURE
|
||||||
|
|
||||||
|
#undef errno
|
||||||
|
#define errno WSAGetLastError()
|
||||||
|
#define strerr(e) gai_strerror(e)
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define closesocket close
|
||||||
|
#define ioctlsocket ioctl
|
||||||
|
#define strerr(e) strerror(e)
|
||||||
|
|
||||||
|
typedef int SOCKET;
|
||||||
|
#define INVALID_SOCKET -1
|
||||||
|
#define SOCKET_ERROR -1
|
||||||
|
#define SD_BOTH SHUT_RDWR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
class CEntryCounter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int *count;
|
||||||
|
CEntryCounter( int *p ) : count(p) { (*count)++; }
|
||||||
|
~CEntryCounter() { (*count)--; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TRACK_ENTRIES() \
|
||||||
|
static int s_EntryCount = 0; \
|
||||||
|
CEntryCounter entrycounter( &s_EntryCount );
|
||||||
|
#else
|
||||||
|
#define TRACK_ENTRIES()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *sqdbg_malloc( unsigned int size );
|
||||||
|
void *sqdbg_realloc( void *p, unsigned int oldsize, unsigned int size );
|
||||||
|
void sqdbg_free( void *p, unsigned int size );
|
||||||
|
|
||||||
|
#ifndef SQDBG_NET_BUF_SIZE
|
||||||
|
#define SQDBG_NET_BUF_SIZE ( 16 * 1024 )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class CMessagePool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef int index_t;
|
||||||
|
|
||||||
|
#pragma pack(push, 4)
|
||||||
|
struct message_t
|
||||||
|
{
|
||||||
|
index_t next;
|
||||||
|
index_t prev;
|
||||||
|
unsigned short len;
|
||||||
|
char ptr[1];
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct chunk_t
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const index_t INVALID_INDEX = 0x80000000;
|
||||||
|
|
||||||
|
// Message queue is going to be less than 16 unless
|
||||||
|
// there is many variable evaluations at once or network lag
|
||||||
|
static const int MEM_CACHE_CHUNKS_ALIGN = 16;
|
||||||
|
|
||||||
|
// Most messages are going to be less than 256 bytes,
|
||||||
|
// only exceeding it on long file paths and long evaluate strings
|
||||||
|
static const int MEM_CACHE_CHUNKSIZE = 256;
|
||||||
|
|
||||||
|
message_t *Get( index_t index )
|
||||||
|
{
|
||||||
|
Assert( index != INVALID_INDEX );
|
||||||
|
|
||||||
|
int msgIdx = index & 0x0000ffff;
|
||||||
|
int chunkIdx = index >> 16;
|
||||||
|
|
||||||
|
Assert( m_Memory );
|
||||||
|
Assert( chunkIdx < m_MemChunkCount );
|
||||||
|
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
Assert( msgIdx < chunk->count );
|
||||||
|
|
||||||
|
return (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_t *m_Memory;
|
||||||
|
int m_MemChunkCount;
|
||||||
|
int m_ElemCount;
|
||||||
|
|
||||||
|
index_t m_Head;
|
||||||
|
index_t m_Tail;
|
||||||
|
|
||||||
|
index_t NewMessage( char *pcsMsg, int nLength )
|
||||||
|
{
|
||||||
|
if ( !m_Memory )
|
||||||
|
{
|
||||||
|
m_Memory = (chunk_t*)sqdbg_malloc( m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
memset( (char*)m_Memory, 0, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
|
||||||
|
chunk_t *chunk = &m_Memory[0];
|
||||||
|
chunk->count = MEM_CACHE_CHUNKS_ALIGN;
|
||||||
|
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
|
||||||
|
int requiredChunks = ( sizeof(message_t) + nLength - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
int matchedChunks = 0;
|
||||||
|
int msgIdx = 0;
|
||||||
|
int chunkIdx = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
Assert( chunk->count && chunk->ptr );
|
||||||
|
|
||||||
|
message_t *msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
|
||||||
|
if ( msg->len == 0 )
|
||||||
|
{
|
||||||
|
if ( ++matchedChunks == requiredChunks )
|
||||||
|
{
|
||||||
|
msgIdx = msgIdx - matchedChunks + 1;
|
||||||
|
msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
|
||||||
|
Assert( nLength >= 0 );
|
||||||
|
Assert( nLength < ( 1 << ( sizeof(message_t::len) * 8 ) ) );
|
||||||
|
|
||||||
|
msg->next = msg->prev = INVALID_INDEX;
|
||||||
|
msg->len = (unsigned short)nLength;
|
||||||
|
memcpy( msg->ptr, pcsMsg, nLength );
|
||||||
|
|
||||||
|
return ( chunkIdx << 16 ) | msgIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchedChunks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgIdx += ( sizeof(message_t) + msg->len - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
|
||||||
|
Assert( msgIdx < 0x0000ffff );
|
||||||
|
|
||||||
|
if ( msgIdx < chunk->count )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
msgIdx = 0;
|
||||||
|
matchedChunks = 0;
|
||||||
|
|
||||||
|
if ( ++chunkIdx >= m_MemChunkCount )
|
||||||
|
{
|
||||||
|
int oldcount = m_MemChunkCount;
|
||||||
|
m_MemChunkCount += 4;
|
||||||
|
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
|
||||||
|
oldcount * sizeof(chunk_t),
|
||||||
|
m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
memset( (char*)m_Memory + oldcount * sizeof(chunk_t),
|
||||||
|
0,
|
||||||
|
(m_MemChunkCount - oldcount) * sizeof(chunk_t) );
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count == 0 )
|
||||||
|
{
|
||||||
|
Assert( chunk->ptr == NULL );
|
||||||
|
|
||||||
|
chunk->count = ( requiredChunks + ( MEM_CACHE_CHUNKS_ALIGN - 1 ) ) & ~( MEM_CACHE_CHUNKS_ALIGN - 1 );
|
||||||
|
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( chunkIdx < 0x00007fff );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteMessage( message_t *pMsg )
|
||||||
|
{
|
||||||
|
if ( pMsg->len == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Assert( pMsg->len > 0 );
|
||||||
|
Assert( m_ElemCount > 0 );
|
||||||
|
m_ElemCount--;
|
||||||
|
|
||||||
|
int msgIdx = ( ( sizeof(message_t) + pMsg->len +
|
||||||
|
( MEM_CACHE_CHUNKSIZE - 1 ) ) & ~( MEM_CACHE_CHUNKSIZE - 1 ) ) / MEM_CACHE_CHUNKSIZE;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pMsg->next = pMsg->prev = INVALID_INDEX;
|
||||||
|
pMsg->len = 0;
|
||||||
|
pMsg->ptr[0] = 0;
|
||||||
|
|
||||||
|
pMsg = (message_t*)( (char*)pMsg + MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
while ( --msgIdx > 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMessagePool() :
|
||||||
|
m_Memory( NULL ),
|
||||||
|
m_MemChunkCount( 4 ),
|
||||||
|
m_ElemCount( 0 ),
|
||||||
|
m_Head( INVALID_INDEX ),
|
||||||
|
m_Tail( INVALID_INDEX )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CMessagePool()
|
||||||
|
{
|
||||||
|
if ( m_Memory )
|
||||||
|
{
|
||||||
|
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
for ( int msgIdx = 0; msgIdx < chunk->count; )
|
||||||
|
{
|
||||||
|
message_t *msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
Assert( msg->len == 0 && msg->ptr[0] == 0 );
|
||||||
|
msgIdx += ( sizeof(message_t) + msg->len - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
DeleteMessage( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqdbg_free( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( m_ElemCount == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shrink()
|
||||||
|
{
|
||||||
|
Assert( m_ElemCount == 0 );
|
||||||
|
|
||||||
|
if ( !m_Memory )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for ( int chunkIdx = 1; chunkIdx < m_MemChunkCount; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count )
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
for ( int msgIdx = 0; msgIdx < chunk->count; )
|
||||||
|
{
|
||||||
|
message_t *msg = (message_t*)&chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
Assert( msg->len == 0 && msg->ptr[0] == 0 );
|
||||||
|
msgIdx += ( sizeof(message_t) + msg->len - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
|
||||||
|
chunk->count = 0;
|
||||||
|
chunk->ptr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m_MemChunkCount > 4 )
|
||||||
|
{
|
||||||
|
int oldcount = m_MemChunkCount;
|
||||||
|
m_MemChunkCount = 4;
|
||||||
|
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
|
||||||
|
oldcount * sizeof(chunk_t),
|
||||||
|
m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add( char *pcsMsg, int nLength )
|
||||||
|
{
|
||||||
|
index_t newMsg = NewMessage( pcsMsg, nLength );
|
||||||
|
|
||||||
|
m_ElemCount++;
|
||||||
|
|
||||||
|
// Add to tail
|
||||||
|
if ( m_Tail == INVALID_INDEX )
|
||||||
|
{
|
||||||
|
Assert( m_Head == INVALID_INDEX );
|
||||||
|
m_Head = m_Tail = newMsg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Get(newMsg)->prev = m_Tail;
|
||||||
|
Get(m_Tail)->next = newMsg;
|
||||||
|
m_Tail = newMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T, void (T::*callback)( char *ptr, int len ) >
|
||||||
|
void Service( T *ctx )
|
||||||
|
{
|
||||||
|
TRACK_ENTRIES();
|
||||||
|
|
||||||
|
index_t msg = m_Head;
|
||||||
|
|
||||||
|
while ( msg != INVALID_INDEX )
|
||||||
|
{
|
||||||
|
message_t *pMsg = Get(msg);
|
||||||
|
|
||||||
|
Assert( pMsg->len || ( pMsg->next == INVALID_INDEX && pMsg->prev == INVALID_INDEX ) );
|
||||||
|
|
||||||
|
if ( pMsg->len == 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Advance before execution
|
||||||
|
index_t next = pMsg->next;
|
||||||
|
index_t prev = pMsg->prev;
|
||||||
|
|
||||||
|
pMsg->next = INVALID_INDEX;
|
||||||
|
pMsg->prev = INVALID_INDEX;
|
||||||
|
|
||||||
|
if ( prev != INVALID_INDEX )
|
||||||
|
Get(prev)->next = next;
|
||||||
|
|
||||||
|
if ( next != INVALID_INDEX )
|
||||||
|
Get(next)->prev = prev;
|
||||||
|
|
||||||
|
if ( msg == m_Head )
|
||||||
|
{
|
||||||
|
// prev could be non-null on re-entry
|
||||||
|
//Assert( prev == INVALID_INDEX );
|
||||||
|
m_Head = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( msg == m_Tail )
|
||||||
|
{
|
||||||
|
Assert( next == INVALID_INDEX && prev == INVALID_INDEX );
|
||||||
|
m_Tail = INVALID_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
(ctx->*callback)( pMsg->ptr, pMsg->len );
|
||||||
|
|
||||||
|
Assert( Get(msg) == pMsg );
|
||||||
|
|
||||||
|
DeleteMessage( pMsg );
|
||||||
|
msg = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
index_t msg = m_Head;
|
||||||
|
|
||||||
|
while ( msg != INVALID_INDEX )
|
||||||
|
{
|
||||||
|
message_t *pMsg = Get(msg);
|
||||||
|
|
||||||
|
index_t next = pMsg->next;
|
||||||
|
index_t prev = pMsg->prev;
|
||||||
|
|
||||||
|
if ( prev != INVALID_INDEX )
|
||||||
|
Get(prev)->next = next;
|
||||||
|
|
||||||
|
if ( next != INVALID_INDEX )
|
||||||
|
Get(next)->prev = prev;
|
||||||
|
|
||||||
|
if ( msg == m_Head )
|
||||||
|
{
|
||||||
|
Assert( prev == INVALID_INDEX );
|
||||||
|
m_Head = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( msg == m_Tail )
|
||||||
|
{
|
||||||
|
Assert( next == INVALID_INDEX && prev == INVALID_INDEX );
|
||||||
|
m_Tail = INVALID_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteMessage( pMsg );
|
||||||
|
msg = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( m_Head == INVALID_INDEX && m_Tail == INVALID_INDEX );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool SocketWouldBlock()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINPROGRESS;
|
||||||
|
#else
|
||||||
|
return errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CloseSocket( SOCKET *sock )
|
||||||
|
{
|
||||||
|
if ( *sock != INVALID_SOCKET )
|
||||||
|
{
|
||||||
|
shutdown( *sock, SD_BOTH );
|
||||||
|
closesocket( *sock );
|
||||||
|
*sock = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CServerSocket
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SOCKET m_Socket;
|
||||||
|
SOCKET m_ServerSocket;
|
||||||
|
|
||||||
|
CMessagePool m_MessagePool;
|
||||||
|
|
||||||
|
char *m_pRecvBufPtr;
|
||||||
|
char m_pRecvBuf[ SQDBG_NET_BUF_SIZE ];
|
||||||
|
|
||||||
|
bool m_bWSAInit;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char *m_pszLastMsgFmt;
|
||||||
|
const char *m_pszLastMsg;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool IsListening()
|
||||||
|
{
|
||||||
|
return m_ServerSocket != INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsClientConnected()
|
||||||
|
{
|
||||||
|
return m_Socket != INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short GetServerPort()
|
||||||
|
{
|
||||||
|
if ( m_ServerSocket != INVALID_SOCKET )
|
||||||
|
{
|
||||||
|
sockaddr_in addr;
|
||||||
|
socklen_t len = sizeof(addr);
|
||||||
|
|
||||||
|
if ( getsockname( m_ServerSocket, (sockaddr*)&addr, &len ) != SOCKET_ERROR )
|
||||||
|
return ntohs( addr.sin_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListenSocket( unsigned short port )
|
||||||
|
{
|
||||||
|
if ( m_ServerSocket != INVALID_SOCKET )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if ( !m_bWSAInit )
|
||||||
|
{
|
||||||
|
WSADATA wsadata;
|
||||||
|
if ( WSAStartup( MAKEWORD(2,2), &wsadata ) != 0 )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) WSA startup failed";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_bWSAInit = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_ServerSocket = socket( AF_INET, SOCK_STREAM, 0 );
|
||||||
|
|
||||||
|
if ( m_ServerSocket == INVALID_SOCKET )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
Shutdown();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to open socket";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_long iMode = 1;
|
||||||
|
#ifdef _WIN32
|
||||||
|
if ( ioctlsocket( m_ServerSocket, FIONBIO, &iMode ) == SOCKET_ERROR )
|
||||||
|
#else
|
||||||
|
int f = fcntl( m_ServerSocket, F_GETFL );
|
||||||
|
if ( f == -1 || fcntl( m_ServerSocket, F_SETFL, f | O_NONBLOCK ) == -1 )
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
Shutdown();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to set socket non-blocking";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
iMode = 1;
|
||||||
|
|
||||||
|
if ( setsockopt( m_ServerSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&iMode, sizeof(iMode) ) == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
Shutdown();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to set TCP nodelay";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
linger ln;
|
||||||
|
ln.l_onoff = 0;
|
||||||
|
ln.l_linger = 0;
|
||||||
|
|
||||||
|
if ( setsockopt( m_ServerSocket, SOL_SOCKET, SO_LINGER, (char*)&ln, sizeof(ln) ) == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
Shutdown();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to set don't linger";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in addr;
|
||||||
|
memset( &addr, 0, sizeof(addr) );
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons( port );
|
||||||
|
addr.sin_addr.s_addr = htonl( INADDR_ANY );
|
||||||
|
|
||||||
|
if ( bind( m_ServerSocket, (sockaddr*)&addr, sizeof(addr) ) == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
Shutdown();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to bind socket on port";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( listen( m_ServerSocket, 0 ) == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
Shutdown();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to listen to socket";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Listen()
|
||||||
|
{
|
||||||
|
if ( m_ServerSocket == INVALID_SOCKET )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
timeval tv;
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO( &rfds );
|
||||||
|
FD_SET( m_ServerSocket, &rfds );
|
||||||
|
|
||||||
|
select( 0, &rfds, NULL, NULL, &tv );
|
||||||
|
|
||||||
|
if ( !FD_ISSET( m_ServerSocket, &rfds ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FD_CLR( m_ServerSocket, &rfds );
|
||||||
|
|
||||||
|
sockaddr_in addr;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
|
||||||
|
m_Socket = accept( m_ServerSocket, (sockaddr*)&addr, &addrlen );
|
||||||
|
|
||||||
|
if ( m_Socket == INVALID_SOCKET )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
int f = fcntl( m_Socket, F_GETFL );
|
||||||
|
if ( f == -1 || fcntl( m_Socket, F_SETFL, f | O_NONBLOCK ) == -1 )
|
||||||
|
{
|
||||||
|
int err = errno;
|
||||||
|
DisconnectClient();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Failed to set socket non-blocking";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_pszLastMsg = inet_ntoa( addr.sin_addr );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
CloseSocket( &m_Socket );
|
||||||
|
CloseSocket( &m_ServerSocket );
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if ( m_bWSAInit )
|
||||||
|
{
|
||||||
|
WSACleanup();
|
||||||
|
m_bWSAInit = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_MessagePool.Clear();
|
||||||
|
m_pRecvBufPtr = m_pRecvBuf;
|
||||||
|
memset( m_pRecvBuf, -1, sizeof( m_pRecvBuf ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisconnectClient()
|
||||||
|
{
|
||||||
|
CloseSocket( &m_Socket );
|
||||||
|
|
||||||
|
m_MessagePool.Clear();
|
||||||
|
m_pRecvBufPtr = m_pRecvBuf;
|
||||||
|
memset( m_pRecvBuf, -1, sizeof( m_pRecvBuf ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Send( const char *buf, int len )
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int bytesSend = send( m_Socket, buf, len, 0 );
|
||||||
|
|
||||||
|
if ( bytesSend == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
// Keep blocking
|
||||||
|
if ( SocketWouldBlock() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int err = errno;
|
||||||
|
DisconnectClient();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Network error";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( len == bytesSend )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
len -= bytesSend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Recv()
|
||||||
|
{
|
||||||
|
timeval tv;
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO( &rfds );
|
||||||
|
FD_SET( m_Socket, &rfds );
|
||||||
|
|
||||||
|
select( 0, &rfds, NULL, NULL, &tv );
|
||||||
|
|
||||||
|
if ( !FD_ISSET( m_Socket, &rfds ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
FD_CLR( m_Socket, &rfds );
|
||||||
|
|
||||||
|
u_long readlen = 0;
|
||||||
|
ioctlsocket( m_Socket, FIONREAD, &readlen );
|
||||||
|
|
||||||
|
int bufsize = m_pRecvBuf + sizeof(m_pRecvBuf) - m_pRecvBufPtr;
|
||||||
|
|
||||||
|
if ( bufsize <= 0 || (unsigned int)bufsize < readlen )
|
||||||
|
{
|
||||||
|
DisconnectClient();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Net message buffer is full";
|
||||||
|
m_pszLastMsg = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int bytesRecv = recv( m_Socket, m_pRecvBufPtr, bufsize, 0 );
|
||||||
|
|
||||||
|
if ( bytesRecv == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
if ( SocketWouldBlock() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
int err = errno;
|
||||||
|
DisconnectClient();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Network error";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !bytesRecv )
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSASetLastError( WSAECONNRESET );
|
||||||
|
#else
|
||||||
|
errno = ECONNRESET;
|
||||||
|
#endif
|
||||||
|
int err = errno;
|
||||||
|
DisconnectClient();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Client disconnected";
|
||||||
|
m_pszLastMsg = strerr(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pRecvBufPtr += bytesRecv;
|
||||||
|
bufsize -= bytesRecv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Header reader sets message pointer to the content start
|
||||||
|
//
|
||||||
|
template < bool (readHeader)( char **ppMsg, int *pLength ) >
|
||||||
|
bool Parse()
|
||||||
|
{
|
||||||
|
// Nothing to parse
|
||||||
|
if ( m_pRecvBufPtr == m_pRecvBuf )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
char *pMsg = m_pRecvBuf;
|
||||||
|
int nLength = sizeof(m_pRecvBuf);
|
||||||
|
|
||||||
|
while ( readHeader( &pMsg, &nLength ) )
|
||||||
|
{
|
||||||
|
char *pMsgEnd = pMsg + (unsigned int)nLength;
|
||||||
|
|
||||||
|
if ( pMsgEnd >= m_pRecvBuf + sizeof(m_pRecvBuf) )
|
||||||
|
{
|
||||||
|
DisconnectClient();
|
||||||
|
m_pszLastMsgFmt = "(sqdbg) Client disconnected";
|
||||||
|
|
||||||
|
if ( nLength == -1 )
|
||||||
|
{
|
||||||
|
m_pszLastMsg = "malformed message";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_pszLastMsg = "content is too large";
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entire message wasn't received, wait for it
|
||||||
|
if ( m_pRecvBufPtr < pMsgEnd )
|
||||||
|
break;
|
||||||
|
|
||||||
|
m_MessagePool.Add( pMsg, nLength );
|
||||||
|
|
||||||
|
// Last message
|
||||||
|
if ( m_pRecvBufPtr == pMsgEnd )
|
||||||
|
{
|
||||||
|
memset( m_pRecvBuf, 0, m_pRecvBufPtr - m_pRecvBuf );
|
||||||
|
m_pRecvBufPtr = m_pRecvBuf;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next message
|
||||||
|
int shift = m_pRecvBufPtr - pMsgEnd;
|
||||||
|
memmove( m_pRecvBuf, pMsgEnd, shift );
|
||||||
|
memset( m_pRecvBuf + shift, 0, m_pRecvBufPtr - ( m_pRecvBuf + shift ) );
|
||||||
|
m_pRecvBufPtr = m_pRecvBuf + shift;
|
||||||
|
pMsg = m_pRecvBuf;
|
||||||
|
nLength = sizeof(m_pRecvBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T, void (T::*callback)( char *ptr, int len ) >
|
||||||
|
void Execute( T *ctx )
|
||||||
|
{
|
||||||
|
m_MessagePool.Service< T, callback >( ctx );
|
||||||
|
|
||||||
|
if ( m_Socket == INVALID_SOCKET && m_MessagePool.m_ElemCount == 0 )
|
||||||
|
{
|
||||||
|
m_MessagePool.Shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CServerSocket() :
|
||||||
|
m_Socket( INVALID_SOCKET ),
|
||||||
|
m_ServerSocket( INVALID_SOCKET ),
|
||||||
|
m_pRecvBufPtr( m_pRecvBuf ),
|
||||||
|
m_bWSAInit( false )
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( sizeof(m_pRecvBuf) <= ( 1 << ( sizeof(CMessagePool::message_t::len) * 8 ) ) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SQDBG_NET_H
|
||||||
282
source/squirrel/sqdbg/protocol.h
Normal file
282
source/squirrel/sqdbg/protocol.h
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// github.com/samisalreadytaken/sqdbg
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SQDBG_DAP_H
|
||||||
|
#define SQDBG_DAP_H
|
||||||
|
|
||||||
|
#define DAP_HEADER_CONTENTLENGTH "Content-Length"
|
||||||
|
#define DAP_HEADER_END "\r\n\r\n"
|
||||||
|
#define DAP_HEADER_MAXSIZE ( STRLEN(DAP_HEADER_CONTENTLENGTH ": ") + STRLEN(DAP_HEADER_END) + FMT_UINT32_LEN )
|
||||||
|
|
||||||
|
inline void DAP_Serialise( CBuffer *buffer )
|
||||||
|
{
|
||||||
|
Assert( buffer->Size() > 0 && buffer->Size() < INT_MAX );
|
||||||
|
|
||||||
|
char *mem = buffer->Base();
|
||||||
|
int contentSize = buffer->Size() - DAP_HEADER_MAXSIZE;
|
||||||
|
int digits = countdigits( contentSize );
|
||||||
|
int padding = FMT_UINT32_LEN - digits;
|
||||||
|
|
||||||
|
int nearest = 10;
|
||||||
|
while ( contentSize >= nearest )
|
||||||
|
nearest *= 10;
|
||||||
|
|
||||||
|
contentSize += padding;
|
||||||
|
|
||||||
|
if ( contentSize >= nearest )
|
||||||
|
{
|
||||||
|
// Padding between header and content increased content size digits,
|
||||||
|
// add padding in the end to match
|
||||||
|
padding--;
|
||||||
|
digits++;
|
||||||
|
buffer->base.Ensure( buffer->Size() + 1 );
|
||||||
|
mem[buffer->size++] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy( mem, DAP_HEADER_CONTENTLENGTH ": ", STRLEN(DAP_HEADER_CONTENTLENGTH ": ") );
|
||||||
|
|
||||||
|
int idx = STRLEN(DAP_HEADER_CONTENTLENGTH ": ") + digits;
|
||||||
|
|
||||||
|
for ( int i = idx - 1; contentSize; )
|
||||||
|
{
|
||||||
|
char c = contentSize % 10;
|
||||||
|
contentSize /= 10;
|
||||||
|
mem[i--] = '0' + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy( mem + idx, DAP_HEADER_END, STRLEN(DAP_HEADER_END) );
|
||||||
|
idx += STRLEN(DAP_HEADER_END);
|
||||||
|
memset( mem + idx, ' ', padding );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DAP_Free( CBuffer *buffer )
|
||||||
|
{
|
||||||
|
buffer->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ParseFieldName( const char *pMemEnd, char *pStart )
|
||||||
|
{
|
||||||
|
char *c = pStart;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if ( IN_RANGE_CHAR( ((unsigned char*)c)[0], 0x20, 0x7E ) )
|
||||||
|
{
|
||||||
|
if ( c + 1 >= pMemEnd )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ( c[0] == ':' )
|
||||||
|
{
|
||||||
|
if ( c[1] == ' ' )
|
||||||
|
return c - pStart;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ParseFieldValue( const char *pMemEnd, char *pStart )
|
||||||
|
{
|
||||||
|
char *c = pStart;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if ( c + 1 >= pMemEnd )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ( c[0] == '\n' )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ( c[0] == '\r' && c[1] == '\n' )
|
||||||
|
return c - pStart;
|
||||||
|
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool DAP_ReadHeader( char **ppMsg, int *pLength )
|
||||||
|
{
|
||||||
|
char *pMsg = *ppMsg;
|
||||||
|
const char *pMemEnd = pMsg + *pLength;
|
||||||
|
int nContentLength = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int len = ParseFieldName( pMemEnd, pMsg );
|
||||||
|
|
||||||
|
if ( len == 0 )
|
||||||
|
goto invalid;
|
||||||
|
|
||||||
|
if ( len == -1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( len == (int)STRLEN(DAP_HEADER_CONTENTLENGTH) &&
|
||||||
|
!memcmp( pMsg, DAP_HEADER_CONTENTLENGTH, STRLEN(DAP_HEADER_CONTENTLENGTH) ) )
|
||||||
|
{
|
||||||
|
// Duplicate length field
|
||||||
|
if ( nContentLength )
|
||||||
|
goto ignore;
|
||||||
|
|
||||||
|
pMsg += len + 2;
|
||||||
|
|
||||||
|
for ( char *pStart = pMsg;; )
|
||||||
|
{
|
||||||
|
if ( pMsg >= pMemEnd )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( IN_RANGE_CHAR( *pMsg, '0', '9' ) )
|
||||||
|
{
|
||||||
|
nContentLength = nContentLength * 10 + *pMsg - '0';
|
||||||
|
pMsg++;
|
||||||
|
|
||||||
|
if ( pMsg - pStart > (int)FMT_UINT32_LEN )
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
// Strict - no whitespace allowed
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( pMsg + 1 >= pMemEnd )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( pMsg[0] == '\r' && pMsg[1] == '\n' )
|
||||||
|
{
|
||||||
|
if ( nContentLength <= 0 )
|
||||||
|
goto invalid;
|
||||||
|
|
||||||
|
*pLength = nContentLength;
|
||||||
|
pMsg += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ignore unknown header fields
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ignore:
|
||||||
|
pMsg += len + 2;
|
||||||
|
|
||||||
|
len = ParseFieldValue( pMemEnd, pMsg );
|
||||||
|
|
||||||
|
if ( len == 0 )
|
||||||
|
goto invalid;
|
||||||
|
|
||||||
|
if ( len == -1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pMsg += len + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pMsg + 1 >= pMemEnd )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( pMsg[0] == '\r' && pMsg[1] == '\n' )
|
||||||
|
{
|
||||||
|
*ppMsg = pMsg + 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalid:
|
||||||
|
// Signal that the client needs to be dropped
|
||||||
|
*pLength = -1;
|
||||||
|
*ppMsg = pMsg;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SQDBG_VALIDATE_SENT_MSG
|
||||||
|
inline void DAP_Test( CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *scratch, CBuffer *buffer )
|
||||||
|
{
|
||||||
|
char *pMsg = buffer->Base();
|
||||||
|
int nLength = buffer->Size();
|
||||||
|
|
||||||
|
bool res = DAP_ReadHeader( &pMsg, &nLength );
|
||||||
|
Assert( res && nLength < buffer->Size() );
|
||||||
|
|
||||||
|
if ( res )
|
||||||
|
{
|
||||||
|
json_table_t table;
|
||||||
|
JSONParser parser( scratch, pMsg, nLength, &table );
|
||||||
|
|
||||||
|
AssertMsg1( !parser.GetError(), "%s", parser.GetError() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define DAP_Test(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _DAP_INIT_BUF( _buf ) \
|
||||||
|
CBufTmpCache _bufcache( (_buf) ); \
|
||||||
|
(_buf)->size = DAP_HEADER_MAXSIZE; \
|
||||||
|
(void)0
|
||||||
|
|
||||||
|
#define DAP_START_REQUEST( _seq, _cmd ) \
|
||||||
|
{ \
|
||||||
|
_DAP_INIT_BUF( &m_SendBuf ); \
|
||||||
|
{ \
|
||||||
|
wjson_table_t packet( m_SendBuf ); \
|
||||||
|
packet.SetInt( "seq", _seq ); \
|
||||||
|
packet.SetString( "type", "request" ); \
|
||||||
|
packet.SetString( "command", _cmd );
|
||||||
|
|
||||||
|
#define _DAP_START_RESPONSE( _seq, _cmd, _suc ) \
|
||||||
|
if ( IsClientConnected() ) \
|
||||||
|
{ \
|
||||||
|
_DAP_INIT_BUF( &m_SendBuf ); \
|
||||||
|
{ \
|
||||||
|
wjson_table_t packet( m_SendBuf ); \
|
||||||
|
packet.SetInt( "request_seq", _seq ); \
|
||||||
|
packet.SetString( "type", "response" ); \
|
||||||
|
packet.SetString( "command", _cmd ); \
|
||||||
|
packet.SetBool( "success", _suc );
|
||||||
|
|
||||||
|
#define DAP_START_RESPONSE( _seq, _cmd ) \
|
||||||
|
_DAP_START_RESPONSE( _seq, _cmd, true )
|
||||||
|
|
||||||
|
#define DAP_ERROR_RESPONSE( _seq, _cmd ) \
|
||||||
|
_DAP_START_RESPONSE( _seq, _cmd, false )
|
||||||
|
|
||||||
|
#define DAP_ERROR_BODY( _id, _fmt ) \
|
||||||
|
wjson_table_t body = packet.SetTable( "body" ); \
|
||||||
|
wjson_table_t error = body.SetTable( "error" ); \
|
||||||
|
error.SetInt( "id", _id ); \
|
||||||
|
error.SetString( "format", _fmt ); \
|
||||||
|
|
||||||
|
#define DAP_START_EVENT( _seq, _ev ) \
|
||||||
|
{ \
|
||||||
|
_DAP_INIT_BUF( &m_SendBuf ); \
|
||||||
|
{ \
|
||||||
|
wjson_table_t packet( m_SendBuf ); \
|
||||||
|
packet.SetInt( "seq", _seq ); \
|
||||||
|
packet.SetString( "type", "event" ); \
|
||||||
|
packet.SetString( "event", _ev );
|
||||||
|
|
||||||
|
#define DAP_SET( _key, _val ) \
|
||||||
|
packet.Set( _key, _val );
|
||||||
|
|
||||||
|
#define DAP_SET_TABLE( _val ) \
|
||||||
|
wjson_table_t _val = packet.SetTable( #_val );
|
||||||
|
|
||||||
|
#define DAP_SEND() \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
DAP_Serialise( &m_SendBuf ); \
|
||||||
|
Send( m_SendBuf.Base(), m_SendBuf.Size() ); \
|
||||||
|
DAP_Test( &m_ReadBuf, &m_SendBuf ); \
|
||||||
|
DAP_Free( &m_SendBuf ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SQDBG_DAP_H
|
||||||
21243
source/squirrel/sqdbg/server.cpp
Normal file
21243
source/squirrel/sqdbg/server.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1482
source/squirrel/sqdbg/str.h
Normal file
1482
source/squirrel/sqdbg/str.h
Normal file
File diff suppressed because it is too large
Load Diff
683
source/squirrel/sqdbg/vec.h
Normal file
683
source/squirrel/sqdbg/vec.h
Normal file
@@ -0,0 +1,683 @@
|
|||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// github.com/samisalreadytaken/sqdbg
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SQDBG_VEC_H
|
||||||
|
#define SQDBG_VEC_H
|
||||||
|
|
||||||
|
void *sqdbg_malloc( unsigned int size );
|
||||||
|
void *sqdbg_realloc( void *p, unsigned int oldsize, unsigned int size );
|
||||||
|
void sqdbg_free( void *p, unsigned int size );
|
||||||
|
|
||||||
|
class CMemory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#pragma pack(push, 4)
|
||||||
|
struct memory_t
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
unsigned int size;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
memory_t memory;
|
||||||
|
|
||||||
|
char &operator[]( unsigned int i ) const
|
||||||
|
{
|
||||||
|
Assert( memory.size > 0 );
|
||||||
|
return *( memory.ptr + i );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *Base()
|
||||||
|
{
|
||||||
|
return memory.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Size() const
|
||||||
|
{
|
||||||
|
return memory.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Free()
|
||||||
|
{
|
||||||
|
if ( memory.ptr )
|
||||||
|
{
|
||||||
|
sqdbg_free( memory.ptr, memory.size );
|
||||||
|
memory.ptr = 0;
|
||||||
|
memory.size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alloc( unsigned int count )
|
||||||
|
{
|
||||||
|
Assert( count > 0 || memory.size == 0 );
|
||||||
|
|
||||||
|
if ( count == memory.size )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int size = ( count + sizeof(void*) - 1 ) & ~( sizeof(void*) - 1 );
|
||||||
|
|
||||||
|
if ( memory.ptr )
|
||||||
|
{
|
||||||
|
memory.ptr = (char*)sqdbg_realloc( memory.ptr, memory.size, size );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memory.ptr = (char*)sqdbg_malloc( size );
|
||||||
|
}
|
||||||
|
|
||||||
|
AssertOOM( memory.ptr, size );
|
||||||
|
|
||||||
|
if ( memory.ptr )
|
||||||
|
{
|
||||||
|
memory.size = size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memory.size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ensure( unsigned int newcount )
|
||||||
|
{
|
||||||
|
unsigned int oldcount = memory.size;
|
||||||
|
|
||||||
|
if ( newcount <= oldcount )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( oldcount != 0 )
|
||||||
|
{
|
||||||
|
unsigned int i = (unsigned int)0x7fffffffu;
|
||||||
|
|
||||||
|
while ( ( i >> 1 ) > newcount )
|
||||||
|
{
|
||||||
|
i >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( i > 0 );
|
||||||
|
Assert( i > oldcount );
|
||||||
|
Assert( i >= newcount );
|
||||||
|
|
||||||
|
newcount = i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( newcount < 4 )
|
||||||
|
newcount = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
Alloc( newcount );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// GCC requires this to be outside of the class
|
||||||
|
template < bool S >
|
||||||
|
struct _CScratch_members;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct _CScratch_members< true >
|
||||||
|
{
|
||||||
|
int m_LastFreeChunk;
|
||||||
|
int m_LastFreeIndex;
|
||||||
|
int m_PrevChunk;
|
||||||
|
int m_PrevIndex;
|
||||||
|
|
||||||
|
int LastFreeChunk() { return m_LastFreeChunk; }
|
||||||
|
int LastFreeIndex() { return m_LastFreeIndex; }
|
||||||
|
int PrevChunk() { return m_PrevChunk; }
|
||||||
|
int PrevIndex() { return m_PrevIndex; }
|
||||||
|
|
||||||
|
void SetLastFreeChunk( int i ) { m_LastFreeChunk = i; }
|
||||||
|
void SetLastFreeIndex( int i ) { m_LastFreeIndex = i; }
|
||||||
|
void SetPrevChunk( int i ) { m_PrevChunk = i; }
|
||||||
|
void SetPrevIndex( int i ) { m_PrevIndex = i; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct _CScratch_members< false >
|
||||||
|
{
|
||||||
|
int LastFreeChunk() { return 0; }
|
||||||
|
int LastFreeIndex() { return 0; }
|
||||||
|
int PrevChunk() { return 0; }
|
||||||
|
int PrevIndex() { return 0; }
|
||||||
|
|
||||||
|
void SetLastFreeChunk( int ) {}
|
||||||
|
void SetLastFreeIndex( int ) {}
|
||||||
|
void SetPrevChunk( int ) {}
|
||||||
|
void SetPrevIndex( int ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< bool SEQUENTIAL, int MEM_CACHE_CHUNKS_ALIGN = 2048 >
|
||||||
|
class CScratch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const int MEM_CACHE_CHUNKSIZE = 4;
|
||||||
|
static const int INVALID_INDEX = 0x80000000;
|
||||||
|
|
||||||
|
struct chunk_t
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
chunk_t *m_Memory;
|
||||||
|
int m_MemChunkCount;
|
||||||
|
_CScratch_members< SEQUENTIAL > m;
|
||||||
|
|
||||||
|
char *Get( int index )
|
||||||
|
{
|
||||||
|
Assert( index != INVALID_INDEX );
|
||||||
|
|
||||||
|
int msgIdx = index & 0x0000ffff;
|
||||||
|
int chunkIdx = index >> 16;
|
||||||
|
|
||||||
|
Assert( m_Memory );
|
||||||
|
Assert( chunkIdx < m_MemChunkCount );
|
||||||
|
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
Assert( msgIdx < chunk->count );
|
||||||
|
|
||||||
|
return &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
}
|
||||||
|
|
||||||
|
char *Alloc( int size, int *index = NULL )
|
||||||
|
{
|
||||||
|
if ( !m_Memory )
|
||||||
|
{
|
||||||
|
m_MemChunkCount = 4;
|
||||||
|
m_Memory = (chunk_t*)sqdbg_malloc( m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
memset( (char*)m_Memory, 0, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
|
||||||
|
chunk_t *chunk = &m_Memory[0];
|
||||||
|
chunk->count = MEM_CACHE_CHUNKS_ALIGN;
|
||||||
|
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
|
||||||
|
int requiredChunks;
|
||||||
|
int msgIdx;
|
||||||
|
int chunkIdx;
|
||||||
|
int matchedChunks = 0;
|
||||||
|
|
||||||
|
if ( SEQUENTIAL )
|
||||||
|
{
|
||||||
|
requiredChunks = ( size - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
msgIdx = m.LastFreeIndex();
|
||||||
|
chunkIdx = m.LastFreeChunk();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requiredChunks = ( size + sizeof(int) - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
msgIdx = 0;
|
||||||
|
chunkIdx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
Assert( chunk->count && chunk->ptr );
|
||||||
|
|
||||||
|
if ( SEQUENTIAL )
|
||||||
|
{
|
||||||
|
int remainingChunks = chunk->count - msgIdx;
|
||||||
|
|
||||||
|
if ( remainingChunks >= requiredChunks )
|
||||||
|
{
|
||||||
|
m.SetPrevChunk( m.LastFreeChunk() );
|
||||||
|
m.SetPrevIndex( m.LastFreeIndex() );
|
||||||
|
|
||||||
|
m.SetLastFreeIndex( msgIdx + requiredChunks );
|
||||||
|
m.SetLastFreeChunk( chunkIdx );
|
||||||
|
|
||||||
|
if ( index )
|
||||||
|
{
|
||||||
|
Assert( msgIdx < 0x0000ffff );
|
||||||
|
Assert( chunkIdx < 0x00007fff );
|
||||||
|
*index = ( chunkIdx << 16 ) | msgIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *ptr = &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
Assert( *(int*)ptr >= 0 && *(int*)ptr < ( chunk->count - msgIdx ) * MEM_CACHE_CHUNKSIZE );
|
||||||
|
|
||||||
|
if ( *(int*)ptr == 0 )
|
||||||
|
{
|
||||||
|
if ( ++matchedChunks == requiredChunks )
|
||||||
|
{
|
||||||
|
msgIdx = msgIdx - matchedChunks + 1;
|
||||||
|
Assert( !index );
|
||||||
|
ptr = &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ];
|
||||||
|
*(int*)ptr = size;
|
||||||
|
return ptr + sizeof(int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchedChunks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgIdx += ( *(int*)ptr + sizeof(int) - 1 ) / MEM_CACHE_CHUNKSIZE + 1;
|
||||||
|
|
||||||
|
Assert( msgIdx < 0x0000ffff );
|
||||||
|
|
||||||
|
if ( msgIdx < chunk->count )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
matchedChunks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgIdx = 0;
|
||||||
|
|
||||||
|
if ( ++chunkIdx >= m_MemChunkCount )
|
||||||
|
{
|
||||||
|
int oldcount = m_MemChunkCount;
|
||||||
|
m_MemChunkCount <<= 1;
|
||||||
|
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
|
||||||
|
oldcount * sizeof(chunk_t),
|
||||||
|
m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
memset( (char*)m_Memory + oldcount * sizeof(chunk_t),
|
||||||
|
0,
|
||||||
|
(m_MemChunkCount - oldcount) * sizeof(chunk_t) );
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count == 0 )
|
||||||
|
{
|
||||||
|
Assert( chunk->ptr == NULL );
|
||||||
|
|
||||||
|
chunk->count = ( requiredChunks + ( MEM_CACHE_CHUNKS_ALIGN - 1 ) ) & ~( MEM_CACHE_CHUNKS_ALIGN - 1 );
|
||||||
|
chunk->ptr = (char*)sqdbg_malloc( chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
AssertOOM( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
memset( chunk->ptr, 0, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( chunkIdx < 0x00007fff );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Free( void *ptr )
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( !SEQUENTIAL );
|
||||||
|
Assert( m_Memory );
|
||||||
|
Assert( ptr );
|
||||||
|
|
||||||
|
*(char**)&ptr -= sizeof(int);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count && ptr >= chunk->ptr && ptr < chunk->ptr + chunk->count * MEM_CACHE_CHUNKSIZE )
|
||||||
|
{
|
||||||
|
Assert( *(int*)ptr >= 0 );
|
||||||
|
Assert( (char*)ptr + sizeof(int) + *(int*)ptr <= chunk->ptr + chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( found );
|
||||||
|
|
||||||
|
if ( *(int*)ptr )
|
||||||
|
(*(unsigned char**)&ptr)[ *(int*)ptr + sizeof(int) - 1 ] = 0xdd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset( (char*)ptr, 0, *(int*)ptr + sizeof(int) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Free()
|
||||||
|
{
|
||||||
|
if ( !m_Memory )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count )
|
||||||
|
{
|
||||||
|
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqdbg_free( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
|
||||||
|
m_Memory = NULL;
|
||||||
|
m_MemChunkCount = 4;
|
||||||
|
m.SetLastFreeChunk( 0 );
|
||||||
|
m.SetLastFreeIndex( 0 );
|
||||||
|
m.SetPrevChunk( 0 );
|
||||||
|
m.SetPrevIndex( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseShrink()
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( SEQUENTIAL );
|
||||||
|
|
||||||
|
if ( !m_Memory )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for ( int chunkIdx = 4; chunkIdx < m_MemChunkCount; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count )
|
||||||
|
{
|
||||||
|
sqdbg_free( chunk->ptr, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
|
||||||
|
chunk->count = 0;
|
||||||
|
chunk->ptr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
for ( int chunkIdx = 0; chunkIdx < 4; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count )
|
||||||
|
{
|
||||||
|
memset( chunk->ptr, 0xdd, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( m_MemChunkCount > 8 )
|
||||||
|
{
|
||||||
|
int oldcount = m_MemChunkCount;
|
||||||
|
m_MemChunkCount = 8;
|
||||||
|
m_Memory = (chunk_t*)sqdbg_realloc( m_Memory,
|
||||||
|
oldcount * sizeof(chunk_t),
|
||||||
|
m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) );
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetLastFreeChunk( 0 );
|
||||||
|
m.SetLastFreeIndex( 0 );
|
||||||
|
m.SetPrevChunk( 0 );
|
||||||
|
m.SetPrevIndex( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Release()
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( SEQUENTIAL );
|
||||||
|
|
||||||
|
if ( !m_Memory || ( !m.LastFreeChunk() && !m.LastFreeIndex() ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
for ( int chunkIdx = 0; chunkIdx < m_MemChunkCount; chunkIdx++ )
|
||||||
|
{
|
||||||
|
chunk_t *chunk = &m_Memory[ chunkIdx ];
|
||||||
|
|
||||||
|
if ( chunk->count )
|
||||||
|
{
|
||||||
|
memset( chunk->ptr, 0xdd, chunk->count * MEM_CACHE_CHUNKSIZE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m.SetLastFreeChunk( 0 );
|
||||||
|
m.SetLastFreeIndex( 0 );
|
||||||
|
m.SetPrevChunk( 0 );
|
||||||
|
m.SetPrevIndex( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseTop()
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( SEQUENTIAL );
|
||||||
|
|
||||||
|
m.SetLastFreeChunk( m.PrevChunk() );
|
||||||
|
m.SetLastFreeIndex( m.PrevIndex() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename T, bool bExternalMem = false, class CAllocator = CMemory >
|
||||||
|
class vector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef unsigned int I;
|
||||||
|
|
||||||
|
CAllocator base;
|
||||||
|
I size;
|
||||||
|
|
||||||
|
vector() : base(), size(0)
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( !bExternalMem );
|
||||||
|
}
|
||||||
|
|
||||||
|
vector( CAllocator &a ) : base(a), size(0)
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( bExternalMem );
|
||||||
|
}
|
||||||
|
|
||||||
|
vector( I count ) : base(), size(0)
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( !bExternalMem );
|
||||||
|
base.Alloc( count * sizeof(T) );
|
||||||
|
}
|
||||||
|
|
||||||
|
vector( const vector< T > &src ) : base()
|
||||||
|
{
|
||||||
|
STATIC_ASSERT( !bExternalMem );
|
||||||
|
base.Alloc( src.base.Size() );
|
||||||
|
size = src.size;
|
||||||
|
|
||||||
|
for ( I i = 0; i < size; i++ )
|
||||||
|
new( &base[ i * sizeof(T) ] ) T( (T&)src.base[ i * sizeof(T) ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
~vector()
|
||||||
|
{
|
||||||
|
Assert( (unsigned int)size <= base.Size() );
|
||||||
|
|
||||||
|
for ( I i = 0; i < size; i++ )
|
||||||
|
((T&)(base[ i * sizeof(T) ])).~T();
|
||||||
|
|
||||||
|
if ( !bExternalMem )
|
||||||
|
base.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator[]( I i ) const
|
||||||
|
{
|
||||||
|
Assert( size > 0 );
|
||||||
|
Assert( i >= 0 && i < size );
|
||||||
|
Assert( size * sizeof(T) <= base.Size() );
|
||||||
|
return (T&)base[ i * sizeof(T) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
T *Base()
|
||||||
|
{
|
||||||
|
return base.Base();
|
||||||
|
}
|
||||||
|
|
||||||
|
I Size() const
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
I Capacity() const
|
||||||
|
{
|
||||||
|
return base.Size() / sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
T &Top() const
|
||||||
|
{
|
||||||
|
Assert( size > 0 );
|
||||||
|
return (T&)base[ ( size - 1 ) * sizeof(T) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pop()
|
||||||
|
{
|
||||||
|
Assert( size > 0 );
|
||||||
|
((T&)base[ --size * sizeof(T) ]).~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
T &Append()
|
||||||
|
{
|
||||||
|
base.Ensure( ++size * sizeof(T) );
|
||||||
|
Assert( size * sizeof(T) <= base.Size() );
|
||||||
|
return *( new( &base[ ( size - 1 ) * sizeof(T) ] ) T() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Append( const T &src )
|
||||||
|
{
|
||||||
|
base.Ensure( ++size * sizeof(T) );
|
||||||
|
Assert( size * sizeof(T) <= base.Size() );
|
||||||
|
new( &base[ ( size - 1 ) * sizeof(T) ] ) T( src );
|
||||||
|
}
|
||||||
|
|
||||||
|
T &Insert( I i )
|
||||||
|
{
|
||||||
|
Assert( i >= 0 && i <= size );
|
||||||
|
|
||||||
|
base.Ensure( ++size * sizeof(T) );
|
||||||
|
Assert( size * sizeof(T) <= base.Size() );
|
||||||
|
|
||||||
|
if ( i != size - 1 )
|
||||||
|
{
|
||||||
|
memmove( &base[ ( i + 1 ) * sizeof(T) ],
|
||||||
|
&base[ i * sizeof(T) ],
|
||||||
|
( size - ( i + 1 ) ) * sizeof(T) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return *( new( &base[ i * sizeof(T) ] ) T() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove( I i )
|
||||||
|
{
|
||||||
|
Assert( size > 0 );
|
||||||
|
Assert( i >= 0 && i < size );
|
||||||
|
|
||||||
|
((T&)base[ i * sizeof(T) ]).~T();
|
||||||
|
|
||||||
|
if ( i != size - 1 )
|
||||||
|
{
|
||||||
|
memmove( &base[ i * sizeof(T) ],
|
||||||
|
&base[ ( i + 1 ) * sizeof(T) ],
|
||||||
|
( size - ( i + 1 ) ) * sizeof(T) );
|
||||||
|
}
|
||||||
|
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
for ( I i = 0; i < size; i++ )
|
||||||
|
((T&)base[ i * sizeof(T) ]).~T();
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sort( int (*fn)(const T *, const T *) )
|
||||||
|
{
|
||||||
|
Assert( size * sizeof(T) <= base.Size() );
|
||||||
|
|
||||||
|
if ( size > 1 )
|
||||||
|
{
|
||||||
|
qsort( base.Base(), size, sizeof(T), (int (*)(const void *, const void *))fn );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reserve( I count )
|
||||||
|
{
|
||||||
|
Assert( (unsigned int)size <= base.Size() );
|
||||||
|
|
||||||
|
if ( count == 0 )
|
||||||
|
count = 4;
|
||||||
|
|
||||||
|
if ( (unsigned int)count == base.Size() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for ( I i = count; i < size; i++ )
|
||||||
|
((T&)base[ i * sizeof(T) ]).~T();
|
||||||
|
|
||||||
|
base.Alloc( count * sizeof(T) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Purge()
|
||||||
|
{
|
||||||
|
Assert( size * sizeof(T) <= base.Size() );
|
||||||
|
|
||||||
|
for ( I i = 0; i < size; i++ )
|
||||||
|
((T&)base[ i * sizeof(T) ]).~T();
|
||||||
|
|
||||||
|
base.Free();
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMemory base;
|
||||||
|
int size;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
char *Base()
|
||||||
|
{
|
||||||
|
return base.Base() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Size() const
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Capacity() const
|
||||||
|
{
|
||||||
|
return base.Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reserve( int count )
|
||||||
|
{
|
||||||
|
Assert( (unsigned int)size <= base.Size() );
|
||||||
|
|
||||||
|
if ( (unsigned int)count == base.Size() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
base.Alloc( count );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Purge()
|
||||||
|
{
|
||||||
|
base.Free();
|
||||||
|
size = 0;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CBufTmpCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CBuffer *buffer;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
CBufTmpCache( CBuffer *b ) :
|
||||||
|
buffer(b),
|
||||||
|
size(buffer->size)
|
||||||
|
{
|
||||||
|
buffer->offset += buffer->size;
|
||||||
|
buffer->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~CBufTmpCache()
|
||||||
|
{
|
||||||
|
buffer->offset -= size;
|
||||||
|
buffer->size = size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SQDBG_VEC_H
|
||||||
Reference in New Issue
Block a user