Files
Sun1602/Utility/BitStreamEx.hpp
T
2022-10-26 12:25:11 +08:00

235 lines
6.4 KiB
C++

#pragma once
#if SUN_CODE_BACKUP
//=============================================================================================================================
/// BitStreamEx class
/**
@author Lee Eun-pa <waverix@webzen.co.kr>
@since 2006. 11. 12
@remark
- BitStream 의 Pack & Unpack 관련 개선 방안
@note
- Metadata를 이용한 좀더 확장성 있고 유연한 구조를 만든다.
@history
-
*/
//=============================================================================================================================
#include "BitStream.hpp"
#pragma pack( push, 1 )
struct sPACK_BIT_METAINFO
{
BYTE BIT_SIZE;
BYTE USED_BITS;
};
#define METANODE_4PACK( TypeName, UsedBits ) \
{ BYTES_TO_BITS(sizeof(TypeName)), UsedBits }
#define METANODE_4PACK_EOR() \
{ 0, 0 }
// sPACK_BIT_METAINFO 자료구조 정의 (예제)
//
// const sPACK_BIT_METAINFO c_PACK_METAINFO =
// {
// METANODE_4PACK( BYTE, 3 ),
// METANODE_4PACK( DWORD, 9 ),
// METANODE_4PACK( WORD, 7 ),
// METANODE_4PACK( BOOL, 1 ),
// METANODE_4PACK( DWORD, 30 ),
// METANODE_4PACK_EOR()
// };
//
// 다음은 이러한 메타정보를 추출한 원본 구조체를 보인다.
// #pragma pack( push, 1 )
// struct sSOURCE_STRUCT s_SOURCE_STRUCT =
// {
// BYTE m_byField1;
// DWORD m_dwField2;
// WORD m_wField3;
// BOOL m_bField4;
// DWORD m_dwField5;
// };
// #pragma pack( pop )
//
// 패킹되는 정보는 BYTE의 배열로 충분할 듯.
//
// <WARNING>
// 만약 다음과 같은 구조가 정의되어 있을 경우 (#pragma pack되지 않은 구조체)
//
// struct sSOURCE_STRUCT s_SOURCE_STRUCT2 =
// {
// BYTE m_byField1;
// DWORD m_dwField2;
// WORD m_wField3;
// BOOL m_bField4;
// DWORD m_dwField5;
// };
//
// 이것의 메타정보는 다음과 같을 것이다.
// const sPACK_BIT_METAINFO c_PACK_METAINFO2 =
// {
// METANODE_4PACK( BYTE, 3 ),
// METANODE_4PACK( BYTE, 0 ),
// METANODE_4PACK( BYTE, 0 ),
// METANODE_4PACK( BYTE, 0 ),
// METANODE_4PACK( DWORD, 9 ),
// METANODE_4PACK( WORD, 7 ),
// METANODE_4PACK( WORD, 0 ),
// METANODE_4PACK( BOOL, 1 ),
// METANODE_4PACK( DWORD, 30 ),
// METANODE_4PACK_EOR()
// };
//
// <Application>
// <사용 예제>
// ----------------------------------------------------
// bitStream.SetMetaInfo( c_PACK_METAINFO );
// ----------------------------------------------------
// bitStream.Pack( (BYTE*)&s_SOURCE_STRUCT );
// int readed = bitStream.GetNumberOfBitsUsed();
// bitStream.SetReadOffSet( 0 );
// bitStream.UnPack( (BYTE*)&s_SOURCE_STRUCT, sizeof(s_SOURCE_STRUCT) );
//
// <확장 방안>
// bitStream.SetMetaInfo( c_PACK_METAINFO );
// bitStream.Pack( (BYTE*)&s_SOURCE_STRUCT );
// int readed = bitStream.GetNumberOfBitsUsed();
// bitStream.SetReadOffSet( 0 );
// ----------------------------------------------------
// bitStream.SetMetaInfo( c_PACK_METAINFO2 );
// ----------------------------------------------------
// bitStream.UnPack( (BYTE*)&s_SOURCE_STRUCT2, sizeof(s_SOURCE_STRUCT2) );
//
// -> 결과는 <사용 예제>와 같지만, s_SOURCE_STRUCT2의 실제 구조 수정을 통해 사용되지 않던 부분을 사용할
// 수 있게 된다.
// -> 또는 아예, 기존 크기를 벗어나는 공간을 삽입할 수도 있을 것이다.
// -> 일단 다음과 같이
// METANODE_4PACK( DWORD, 0 ),
// 하나의 필드를 중간에 삽입해 두면, 기존 작업은 그 공간을 건너뛴 작업을 수행할 수 있고, 나중에
// 사용 비트 수를 입력하고 이에 대한 데이터를 준비하면, 확장된 공간이 생겨날 것이다.
//
// 예) 특정 구조체에 버전에 따라 다른 처리를 하게 하고 싶다. 하지만 기존에는 버전 정보가 없는 구조체였다.
// 1. 위와 같이 필드 하나를 추가한다. METANODE_4PACK( BYTE, 0 ), // 버전 정보
// 2. PackedData를 Unpack시켜 추가된 메타정보가 적용된 구조체로 읽어들인다.
// 3. 새로운 구조체에는 버전용 공간인 BYTE가 건너뛰고 정보가 담길 것이다.
// 4. 메타정보의 수정: METANODE_4PACK( BYTE, 4 ) // 버전 정보, 여기서는 4비트로 처리한다.
// 5. 버전 정보를 삽입한다.
// 6. 다시 Pack한다.
// 7. Pack된 정보는 버전 공간이 감안된 새로운 데이터가 될 것이다.
//
#pragma pack( pop )
//
// class BitStreamEx
//
class BitStreamEx : public BitStream
{
public:
BitStreamEx( BYTE* IN OUT pStorage, WORD wCountOfBytes, const sPACK_BIT_METAINFO* IN pMetaInfo ) :
BitStream( pStorage, wCountOfBytes, TRUE ), m_pMetaInfo( (sPACK_BIT_METAINFO*)pMetaInfo ) {}
~BitStreamEx() {}
public:
// <INTERFACEs>
inline VOID SetMetaInfo( const sPACK_BIT_METAINFO* IN pMetaInfo )
{
m_pMetaInfo = (sPACK_BIT_METAINFO*)pMetaInfo;
//DWORD dwByteCount = GetBytesByMetaInfo();
}
//
// <BitStreamEx::Pack>
// <Arguments>
// pPackingStorage : BYTE* =-> MetaInfo를 이용해 Packing할 정보
// wUsedBytes : WORD& =-> 사용된 byte 길이가 될 것이다.
//
VOID Pack( BYTE* IN pPackingStorage )
{
sPACK_BIT_METAINFO* pMETA = m_pMetaInfo;
BYTE* pSrc = (BYTE*)pPackingStorage;
DWORD dwByteCount = 0;
DWORD dwBitCount;
while( dwBitCount = (DWORD)pMETA->BIT_SIZE )
{
DWORD dwBitRange = (DWORD)pMETA->USED_BITS;
if( dwBitRange )
{
BYTE* pSrcPtr = &pSrc[dwByteCount];
Write( pSrcPtr, dwBitRange );
}
dwByteCount += dwBitCount>>3;
++pMETA;
}
}
//
// <BitStreamEx::UnPack>
// <Arguments>
// pUnpackingStorage : BYTE* =-> MetaInfo를 이용해 Packing된 정보를 UnPack하는 공간
// bTF : BOOL =-> pUnpackingStorage을 초기화 할 것인지의 여부,
//
VOID UnPack( BYTE* OUT pUnpackingStorage, WORD wTypeSize, BOOL bTF = TRUE )
{
if( bTF )
memset( pUnpackingStorage, 0, wTypeSize );
sPACK_BIT_METAINFO* pMETA = m_pMetaInfo;
BYTE* pTarget = (BYTE*)pUnpackingStorage;
DWORD dwByteCount = 0;
DWORD dwBitOffset = 0;
DWORD dwBitCount;
while( dwBitCount = pMETA->BIT_SIZE )
{
DWORD dwBitRange = (DWORD)pMETA->USED_BITS;
if( dwBitRange )
{
BYTE* pTargetPtr = &pTarget[dwByteCount];
Read( pTargetPtr, dwBitRange );
}
dwByteCount += dwBitCount>>3;
++pMETA;
}
}
// 아래와 같은 기능도 될 것이다. 하지만 변환 툴에서나 유용한 것이므로 구현하지 않기로 한다.
//VOID ConvertPacked( BYTE* OUT pUnpackingStorage, const sPACK_BIT_METAINFO* pConvertMetaInfo );
//VOID ConvertUnPacked( BYTE* OUT pUnpackingStorage, const sPACK_BIT_METAINFO* pConvertMetaInfo );
DWORD GetBytesByMetaInfo( const sPACK_BIT_METAINFO* pMetaInfo = NULL )
{
sPACK_BIT_METAINFO* pMETA;
pMETA = pMetaInfo ? pMetaInfo : m_pMetaInfo;
DWORD dwBitCount;
DWORD dwBitSum = 0;
while( dwBitCount = pMETA->BIT_SIZE )
{
DWORD dwBitRange = (DWORD)pMETA->USED_BITS;
dwBitSum += dwBitRange;
}
DWORD dwByteCount = BITS_TO_BYTES(dwBitSum);
return dwByteCount;
}
private:
sPACK_BIT_METAINFO* m_pMetaInfo;
};
#endif //SUN_CODE_BACKUP