xml 을 프로토콜로 사용 중, 문득 든 생각
structured 모델이라 사용하기 편할 것 같았지만,
사실상 프로토콜로 쓰려고 보니 특정 값만을 "꺼내는" 상황만이 연출되는 경우가 다수였다.
(그래서 항상 xmlpath만을 사용하여 값을 꺼내곤 한다.)
xml 의 전체 스펙을 사용하지도 않으면서 전체 스펙을 지원하는 라이브러리를 사용하는 게
오버스펙인 것 같아서, 간단히 buffer의 xml string에 대해서 xmlpath로 mapping 해주는 정도의
parser를 구현해보았다.
필요조건으로 linked list 형태의 buffer와 미리 지정된 xml path에 mapping code값을 반화해주는 객체함수 정도가 필요하다.
(xml path 를 매번 map에서 뒤지기가 싫었다. 오버헤드 인 것 같아서!! )
대략 코드는 이렇다.
XMLPathMaker.h
#pragma once
//=====================================================
namespace UtilLib
{
//=====================================================
//=====================================================
template <class T, class XML_PATH_MAPPER, int MAX_DUPLICATE_XML_PATH = 10>
class XMLPathMaker
{
public:
XMLPathMaker (){}
~XMLPathMaker (){}
private:
//----------------------------------------------------------
class BufferPosPair
{
public:
BufferPosPair ( const T* buffer
, DWORD pos)
: buffer_(buffer)
, pos_(pos) {}
BufferPosPair ( const BufferPosPair& clone )
: buffer_(clone.buffer_)
, pos_(clone.pos_) {}
BufferPosPair ()
: buffer_(NULL)
, pos_(0) {}
~BufferPosPair () {}
const T* buffer_;
DWORD pos_;
};
//----------------------------------------------------------
class XML_PATH_MAP
{
public:
XML_PATH_MAP () : duplicate_count(0) {}
~XML_PATH_MAP () {}
BufferPosPair xml_path_mapping_pos_[MAX_DUPLICATE_XML_PATH];
int duplicate_count;
void operator= (const BufferPosPair& clone )
{
if ( duplicate_count < MAX_DUPLICATE_XML_PATH )
xml_path_mapping_pos_[duplicate_count++] = clone;
}
BufferPosPair& operator() ( int index = 0)
{
if ( index >= duplicate_count ||
index < 0)
return xml_path_mapping_pos_[MAX_DUPLICATE_XML_PATH-1];
return xml_path_mapping_pos_[index];
}
};
//----------------------------------------------------------
XML_PATH_MAP xml_path_map_[XML_PATH_MAPPER::MAX_PARAMETER_COUNT];
XML_PATH_MAPPER xml_path_mapper_;
static const int MAX_XML_PATH_LEN = 1024;
static const int MAX_ELEMENT_NAME_LEN = 128;
private:
//========================================================
enum RET
{
FAIL,
ELEMENT,
END_TAG,
};
//========================================================
bool Skip ( T*& buffer
, DWORD& pos )
{
if ( NULL == buffer )
return false;
for(;;)
{
switch( buffer->At(pos) )
{
case '\r':
case '\n':
case ' ':
case '\t':
{
buffer = buffer->Next(pos);
if ( NULL == buffer )
return false;
continue;
}
break;
}
break;
}
return true;
}
//========================================================
bool SetXmlPath ( const char (&xml_path)[MAX_XML_PATH_LEN]
,const BufferPosPair& data_pos )
{
// 여기서 xml path 로 data_pos 넣어야 함
int xml_path_mapping_value = xml_path_mapper_( xml_path );
if ( 0 > xml_path_mapping_value )
return false;
if ( xml_path_mapping_value >= XML_PATH_MAPPER::MAX_PARAMETER_COUNT )
return false;
xml_path_map_[xml_path_mapping_value] = data_pos; // 중복 될 때를 생각해야되는데..
return true;
}
//========================================================
RET ParseEndTag ( T*& buffer
, DWORD& pos
, char (&end_element_name)[MAX_ELEMENT_NAME_LEN])
{
int end_element_name_index = 0;
buffer = buffer->Next(pos);
if ( NULL == buffer )
{
return FAIL;
}
// read element name
do
{
if( '>' == buffer->At(pos) )
{
buffer = buffer->Next(pos);
break;
}
if( end_element_name_index >= MAX_ELEMENT_NAME_LEN )
{
return FAIL;
}
end_element_name[end_element_name_index++] = buffer->At(pos);
}while( buffer = buffer->Next(pos) );
if ( NULL == buffer )
return FAIL;
return END_TAG;
}
//========================================================
RET Parse ( T*& buffer
, DWORD& pos
, char (&xml_path)[MAX_XML_PATH_LEN]
, int xml_path_pos
, char (&end_element_name)[MAX_ELEMENT_NAME_LEN])
{
int element_name_index = 0;
char element_name[MAX_ELEMENT_NAME_LEN] = {0};
if ( false == Skip( buffer, pos ) )
return FAIL;
if ( '<' == buffer->At(pos) )
{
buffer = buffer->Next(pos);
if ( NULL == buffer )
return FAIL;
if ( '/' == buffer->At(pos))
return ParseEndTag( buffer, pos, end_element_name );
// read element name
do
{
if( '>' == buffer->At(pos) )
{
buffer = buffer->Next(pos);
break;
}
if( element_name_index >= MAX_ELEMENT_NAME_LEN )
{
return FAIL;
}
element_name[element_name_index++] = buffer->At(pos);
}while( buffer = buffer->Next(pos) );
if ( NULL == buffer )
return FAIL;
// set xmlpath
if ( xml_path_pos + element_name_index + 1 >= MAX_XML_PATH_LEN )
return FAIL;
xml_path[xml_path_pos++] = '/';
memcpy_s( xml_path + xml_path_pos , sizeof(xml_path) - xml_path_pos
, element_name , element_name_index );
xml_path_pos += element_name_index;
xml_path[xml_path_pos] = 0;
char end_element_name[MAX_ELEMENT_NAME_LEN] = {0};
RET ret;
do
{
ret = Parse( buffer, pos, xml_path, xml_path_pos, end_element_name);
if ( FAIL == ret )
return FAIL;
}while( ret != END_TAG ); // data 인 경우는 종료, tag end 인 경우도 종료 흠..
if ( 0 != strcmp( element_name, end_element_name ) )
return FAIL;
return ELEMENT;
}
else
{
// read element data
BufferPosPair data_pos(buffer, pos);
T* data_pos_end_buf = NULL;
int data_pos_end_pos = 0;
while( buffer = buffer->Next(pos) )
{
switch( buffer->At(pos) )
{
case '<':
break;
case '\r':
case '\n':
case ' ':
case '\t':
if ( NULL == data_pos_end_buf ) // for skip word
{
data_pos_end_buf = buffer;
data_pos_end_pos = pos;
}
continue;
default:
if ( NULL != data_pos_end_buf ) // 비교문을 넣을까 말까 고민되네...
{
data_pos_end_buf = NULL;
data_pos_end_pos = 0;
}
continue;
}
if ( NULL == data_pos_end_buf ) // non-exist skip word after data
buffer->Set( pos, 0 );
else
data_pos_end_buf->Set( data_pos_end_pos, 0 );
buffer = buffer->Next(pos);
break;
}
if ( NULL == buffer )
return FAIL;
if ( '/' != buffer->At(pos) )
return FAIL;
if ( false == SetXmlPath( xml_path, data_pos ))
return FAIL;
return ParseEndTag( buffer, pos, end_element_name );
}
return FAIL;
}
public:
//==============================================================================
bool Parse ( T* buffer, DWORD pos = 0)
{
memset(xml_path_map_, 0, sizeof(xml_path_map_));
char element_name[MAX_ELEMENT_NAME_LEN] = {0};
char xml_path[MAX_XML_PATH_LEN] = {0};
int xml_path_pos = 0;
// skip to '<'
do
{
if( '<' == buffer->At(pos) )
break;
}while( buffer = buffer->Next(pos) );
if ( NULL == buffer )
return false;
if ( ELEMENT == Parse( buffer, pos, xml_path, xml_path_pos, element_name) )
return true;
return false;
}
//==============================================================================
template <size_t size>
bool Get ( int i
, char (&data)[size]
, int index_for_multiple_value = 0)
{
if ( i >= XML_PATH_MAPPER::MAX_PARAMETER_COUNT ||
i < 0 )
return false;
XML_PATH_MAP& xml_path_map = xml_path_map_[i];
if ( index_for_multiple_value >= xml_path_map.duplicate_count )
return false;
T* buffer = (T*)xml_path_map(index_for_multiple_value).buffer_;
DWORD pos = xml_path_map(index_for_multiple_value).pos_;
if ( NULL == buffer )
return false;
int data_index = 0;
while ( data_index < size - 1 &&
0 != buffer->At(pos) )
{
data[data_index++] = buffer->At(pos);
buffer = buffer->Next(pos);
if ( NULL == buffer )
return false;
}
data[data_index] = 0;
return true;
}
//==============================================================================
int Size ( int i )
{
if ( i >= XML_PATH_MAPPER::MAX_PARAMETER_COUNT ||
i < 0 )
return -1;
return xml_path_map_[i].duplicate_count;
}
//==============================================================================
};
//==============================================================================
}
Linked Buffer.h
//==========================================================
class Buffer
{
public:
static const int MAX_SIZE = 8192;
byte buffer[MAX_SIZE];
DWORD size;
Buffer* next;
void Set ( DWORD i, byte data);
byte At ( DWORD i) const;
Buffer* Next (DWORD& pos);
DWORD Size () const;
private:
friend class BufferPool;
Buffer (){};
~Buffer (){};
};
XMLPathMapper.h
typedef std::map<const char*, PARAMETER_INDEX, CompareCharForParameter> ParameterMap;
//========================================================================================
class XMLPathMapper
{
private:
static ParameterMap parameter_map;
static ParameterMap& GetParameterMap() { return parameter_map; }
public:
static const int MAX_PARAMETER_COUNT = MAX;
//-----------------------------------------------------------------------------------
static bool Initialize ()
{
std::pair<ParameterMap::iterator, bool> result;
//----------------------------------------------------------------------------
for ( int i = 0; i < (int)MAX; i++)
{
result = GetParameterMap().insert(ParameterMap::value_type( XML_PATH_PARAMETER[i], (PARAMETER_INDEX)i));
if( false == result.second )
return false;
}
//----------------------------------------------------------------------------
return true;
}
//-----------------------------------------------------------------------------------
static const char* GetXMLPath ( int i )
{
if ( i >= (unsigned int)MAX ||
i < 0)
return NULL;
return XML_PATH_PARAMETER[i];
}
//-----------------------------------------------------------------------------------
int operator() ( const char* xml_path )
{
ParameterMap::const_iterator it = GetParameterMap().find(xml_path);
if (it == GetParameterMap().end())
{
return -1;
}
return it->second;
}
//-----------------------------------------------------------------------------------
};
( 요샌 template 이 재미지다~ 아이 재미져~ )