Files
DNF_DEV/source/squirrel/sqdbg/protocol.h
2025-10-23 15:21:33 +08:00

283 lines
5.7 KiB
C

//-----------------------------------------------------------------------
// 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