Serialization Deserialization Lib - C++
직렬화 역직렬화란?
오늘은 데이터를 서로 주고받을 때 필요한 기능 중 하나인 Serialization(직렬화)와 Deserialization(역직렬화)에 관하여 포스팅을 해보고자 합니다. 직렬화란 쉽게 말하면 객체를 저장, 전송할 수 있는 특정 포맷상태로 바꾸는 것입니다. 이렇게 말하면 쉽게 말한 게 맞아?라고 말 할 수 있을 것 같습니다.
아래 그림을 보면 구조체 A를 Program B에게 전달을 하기 위해서 ByteData로 변환이 필요합니다. 여기서 많이 쓰시는 방법들이 char* pBuffer에다가 memcpy를 하여 많이들 사용하십니다. ( ※ 오늘 공유할 라이브러리는 memcpy를 사용하지 않고 보내는 방법입니다. ) 여기서 char* pBuffer로 만드는 과정을 Serialization이라고 이해하면 좋을 것 같습니다. 그렇게 생성한 Buffer를 Program B에게 송신하면, B는 받은 Data를 구조체 A로 바꾸기 위해 다시 memcpy(구조체 A, pBuffer , sizeof(구조체 A) )로 하여 변환하게 됩니다. 이 변환하는 과정이 Deserialization이라고 이해하시면 됩니다.
직렬화 역직렬화를 사용해야 하는 이유
서로 다른 프로세스 간 데이터를 주고받기 위해서는 전달하기 위해 값 형식 데이터를 만들어야 합니다. 하나의 예시를 들면 Socket 통신을 통해 데이터를 전달할 때 Buffer에 데이터를 적재하여 전송하여야 합니다. 아래 사진과 같이 여러 변수를 하나의 Buffer에 적재하여 연속적인 데이터를 만들어야 하기 때문에 필요하게 되는 것입니다. 즉 전달해야 하는 데이터들을 통신하기 위한 데이터를 만들기 위해서 꼭 사용해야 하는 것입니다.
직렬화 역직렬화 공통 코드
직렬화와 역직렬화를 진행할 때 공통으로 필요한 기능을 구현해 둔 CBuffer 클래스입니다. 해당 클래스는 Buffer의 위치, 크기, 적재, 추출의 기능을 구현하였습니다. 세부적인 기능에 관해서는 아래 소스코드에서 설명할 수 있도록 하겠습니다.
/*================================================================
** Description : Buffer 상위 클래스
** Author : 초심을 찾자
** Create : 2023.12.05
** Last Modify : 2023.12.05
==================================================================*/
#pragma once
#include <string>
#include <iostream>
using namespace std;
class CBuffer {
const int m_increseSize = 10000;
public:
char* m_pCBuffer;
uint64_t m_iCurrentLoc;
uint64_t m_iBufferSize;
CBuffer();
~CBuffer();
virtual void ResetBufferLoc();
virtual void ResizeBuffer(const uint64_t& bufferLength);
virtual void IncreaseBuffer(const uint64_t& bufferSize);
virtual void PushData(const void* pData, const uint64_t& rDataSize);
virtual void* GetData(const uint64_t& rDataSize);
virtual uint64_t GetBufferLength();
virtual char* GetBuffer();
};
소스코드에서 함수 설명란에 기능은 모두 설명해 두었습니다. 그중에서 추가로 설명이 하면 좋을 것 같다고 생각이 든 부분은 PushData라고 생각이 듭니다. PushData는 직렬화를 할 때 사용되는 함수인데 자동으로 현재 사용 중인 메모리가 부족하면 증감을 시켜주는 기능이 적재되어 있음에 따라 사용자 입장에서 사용하기 편리하게 설계하였습니다.
#include "CBuffer.h"
/************************************************
@함수명 : CBuffer
@기능 : 생성자
@입력값 :
@출력값 :
@비고 :
*************************************************/
//===============================================
CBuffer::CBuffer() :
m_pCBuffer(NULL),
m_iCurrentLoc(0),
m_iBufferSize(0)
//===============================================
{
m_pCBuffer = static_cast<char*>(malloc(0));
}
/************************************************
@함수명 : ~CBuffer
@기능 : 소멸자
@입력값 :
@출력값 :
@비고 :
*************************************************/
//===============================================
CBuffer::~CBuffer()
//===============================================
{
if (NULL != m_pCBuffer)
{
free(m_pCBuffer);
m_pCBuffer = NULL;
}
}
/************************************************
@함수명 : ResetBufferLoc
@기능 : Buffer관련 Data를 모두 초기화하는 함수
@입력값 :
@출력값 :
@비고 :
*************************************************/
//===============================================
void CBuffer::ResetBufferLoc()
//===============================================
{
m_iCurrentLoc = 0;
m_iBufferSize = 0;
if (NULL != m_pCBuffer)
{
ResizeBuffer(0);
}
}
/************************************************
@함수명 : ResizeBuffer
@기능 : 전달받은 Buffer 길이 만큼 buffer를 Resize하는 함수
@입력값 : uint64_t(buffer 길이)
@출력값 :
@비고 :
*************************************************/
//===============================================
void CBuffer::ResizeBuffer(const uint64_t& bufferLength)
//===============================================
{
char* pBackupBuffer = m_pCBuffer;
if (NULL != m_pCBuffer)
{
if (NULL == (m_pCBuffer = static_cast<char*>(realloc(m_pCBuffer, bufferLength))))
{
free(pBackupBuffer);
}
}
}
/************************************************
@함수명 : IncreaseBuffer
@기능 : 전달받은 BufferSize만큼 buffer크기를 더 늘리는 함수
@입력값 : uint64_t( 늘리고자 하는 buffer 길이)
@출력값 :
@비고 :
*************************************************/
//===============================================
void CBuffer::IncreaseBuffer(const uint64_t& bufferSize)
//===============================================
{
if (m_iBufferSize <= m_iCurrentLoc + bufferSize)
{
ResizeBuffer(m_increseSize + m_iBufferSize);
}
}
/************************************************
@함수명 : PushData
@기능 : 전달받은 Data를 전달받은 Size만큼 적재하는 함수
@입력값 : void* ( 적재할 Buffer )
uint64_t ( 적재할 Data 크기 )
@출력값 :
@비고 :
*************************************************/
//===============================================
void CBuffer::PushData(const void* pData, const uint64_t& rDataSize)
//===============================================
{
if (NULL != m_pCBuffer)
{
IncreaseBuffer(rDataSize);
if (NULL != m_pCBuffer)
{
m_iBufferSize += rDataSize;
memcpy(&m_pCBuffer[m_iCurrentLoc], pData, rDataSize);
m_iCurrentLoc += rDataSize;
}
}
}
/************************************************
@함수명 : GetData
@기능 : Buffer에서 전달받은 Size 만큼 반환하는 함수
@입력값 : uint64_t ( buffer size )
@출력값 :
@비고 :
*************************************************/
//===============================================
void* CBuffer::GetData(const uint64_t& rDataSize)
//===============================================
{
m_iCurrentLoc += rDataSize;
if (m_iCurrentLoc > m_iBufferSize)
{
return NULL;
}
else
{
return &m_pCBuffer[m_iCurrentLoc - rDataSize];
}
}
/************************************************
@함수명 : GetBufferLength
@기능 : 현재 Buffer size를 반환해주는 함수
@입력값 :
@출력값 :
@비고 :
*************************************************/
//===============================================
uint64_t CBuffer::GetBufferLength()
//===============================================
{
return m_iBufferSize;
}
/************************************************
@함수명 : GetBuffer
@기능 : 현재 Buffer Data를 반환하는 함수
@입력값 :
@출력값 :
@비고 :
*************************************************/
//===============================================
char* CBuffer::GetBuffer()
//===============================================
{
return m_pCBuffer;
}
직렬화 코드 설명
헤더 파일은 아래와 같으며, Operator연산을 이용하여 구현하였습니다. Operator 연산을 이용한 이유는 매번 함수를 호출하여하는 것보다 직관적이며, 사용할 때 타이핑을 더 짧게 할 수 있기 때문에 사용하였습니다. ( ※ 추후 Operator에 관하여는 설명하는 글을 작성할 수 있도록 하겠습니다. )
함수는 Data Type별로 작성을 하였으며, 구조체 전체를 적재해야 할 경우도 있을 것 같아 추가로 PushBufferData라는 함수를 추가하였습니다.
/*================================================================
** Description : Buffer를 인코딩하는 클래스
** Author : 초심을 찾자
** Create : 2023.12.05
** Last Modify : 2023.12.05
==================================================================*/
#pragma once
#include "CBuffer.h"
#include "CBufferDeserialization.h"
class CBufferSerialization : public CBuffer
{
public:
CBufferSerialization();
~CBufferSerialization();
CBufferSerialization(const CBufferSerialization& rhs);
CBufferSerialization& operator=(const CBufferSerialization& rhs);
CBufferSerialization& operator<<(const char& rhs);
CBufferSerialization& operator<<(const string& rhs);
CBufferSerialization& operator<<(const float& rhs);
CBufferSerialization& operator<<(const double& rhs);
CBufferSerialization& operator<<(const int8_t& rhs);
CBufferSerialization& operator<<(const uint8_t& rhs);
CBufferSerialization& operator<<(const int16_t& rhs);
CBufferSerialization& operator<<(const uint16_t& rhs);
CBufferSerialization& operator<<(const int32_t& rhs);
CBufferSerialization& operator<<(const uint32_t& rhs);
CBufferSerialization& operator<<(const int64_t& rhs);
CBufferSerialization& operator<<(const uint64_t& rhs);
void PushBufferData(const void* pData, const uint64_t& iDataSize , const uint8_t& rICDType = 0);
};
소스 파일은 아래 주석을 확인하면 이해하는데 큰 문제가 없을 것으로 보입니다.
#include "CBufferSerialization.h"
//===============================================
CBufferSerialization::CBufferSerialization()
//===============================================
{
}
//===============================================
CBufferSerialization::~CBufferSerialization()
//===============================================
{
}
//===============================================
CBufferSerialization::CBufferSerialization(const CBufferSerialization& rhs)
//===============================================
{
}
//===============================================
CBufferSerialization& CBufferSerialization::operator=(const CBufferSerialization& rhs)
//===============================================
{
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : char 형을 Buffer에 적재하는 함수
@입력값 : char
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const char& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : string 형을 Buffer에 적재하는 함수
@입력값 : string
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const string& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : float 형을 Buffer에 적재하는 함수
@입력값 : float
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const float& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : double 형을 Buffer에 적재하는 함수
@입력값 : double
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const double& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : int8_t 형을 Buffer에 적재하는 함수
@입력값 : int8_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const int8_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : uint8_t 형을 Buffer에 적재하는 함수
@입력값 : uint8_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const uint8_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : int16_t 형을 Buffer에 적재하는 함수
@입력값 : int16_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const int16_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : uint16_t 형을 Buffer에 적재하는 함수
@입력값 : uint16_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const uint16_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : int32_t 형을 Buffer에 적재하는 함수
@입력값 : int32_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const int32_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : uint32_t 형을 Buffer에 적재하는 함수
@입력값 : uint32_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const uint32_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : int64_t 형을 Buffer에 적재하는 함수
@입력값 : int64_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const int64_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : uint64_t 형을 Buffer에 적재하는 함수
@입력값 : uint64_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferSerialization& CBufferSerialization::operator<<(const uint64_t& rhs)
//===============================================
{
PushData(&rhs, sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator<<
@기능 : uint64_t 형을 Buffer에 적재하는 함수
@입력값 : uint64_t
@출력값 :
@비고 :
*************************************************/
//===============================================
void CBufferSerialization::PushBufferData(const void* pData, const uint64_t& iDataSize, const uint8_t& rICDType)
//===============================================
{
switch (iDataSize)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
PushData(pData, iDataSize);
}
역직렬화 코드 설명
역직렬화 코드는 말 그대로 직렬화를 반대로 하는 동작으로 직렬화 코드를 이해하셨다면 크게 어려움이 없을 거라고 생각이 듭니다. 혹여나 궁금한 사항이 있는 경우 댓글을 달아주시면 답변할 수 있도록 하겠습니다.
/*================================================================
** Description : Buffer를 디코딩하는 클래스
** Author : 초심을 찾자
** Create : 2023.12.05
** Last Modify : 2023.12.05
==================================================================*/
#pragma once
#include "CBuffer.h"
#include "CBufferSerialization.h"
class CBufferDeserialization : public CBuffer
{
public:
CBufferDeserialization();
~CBufferDeserialization();
CBufferDeserialization(const CBufferDeserialization& rhs);
CBufferDeserialization& operator=(const CBufferDeserialization& rhs);
CBufferDeserialization(const CBuffer& rhs);
CBufferDeserialization& operator=(const CBuffer& rhs);
CBufferDeserialization(const char* pBuffer, const uint64_t& iDataSize); // 처음생성할 때 Buffer와 Size를 넣어주면 Deserialization이 가능
CBufferDeserialization& operator>>(char& rhs);
CBufferDeserialization& operator>>(string& rhs);
CBufferDeserialization& operator>>(float& rhs);
CBufferDeserialization& operator>>(double& rhs);
CBufferDeserialization& operator>>(int8_t& rhs);
CBufferDeserialization& operator>>(uint8_t& rhs);
CBufferDeserialization& operator>>(int16_t& rhs);
CBufferDeserialization& operator>>(uint16_t& rhs);
CBufferDeserialization& operator>>(int32_t& rhs);
CBufferDeserialization& operator>>(uint32_t& rhs);
CBufferDeserialization& operator>>(int64_t& rhs);
CBufferDeserialization& operator>>(uint64_t& rhs);
void GetBufferData(void* pData, const uint64_t& iDataSize);
};
소스파일도 주석을 보면 이해하는데 큰 어려움이 없을 거라고 생각이 듭니다.
#include "CBufferDeserialization.h"
//===============================================
CBufferDeserialization::CBufferDeserialization()
//===============================================
{
}
//===============================================
CBufferDeserialization::~CBufferDeserialization()
//===============================================
{
}
//===============================================
CBufferDeserialization::CBufferDeserialization(const CBufferDeserialization& rhs)
//===============================================
{
}
//===============================================
CBufferDeserialization& CBufferDeserialization::operator=(const CBufferDeserialization& rhs)
//===============================================
{
return *this;
}
//===============================================
CBufferDeserialization::CBufferDeserialization(const CBuffer& rhs)
//===============================================
{
ResizeBuffer(rhs.m_iBufferSize);
memcpy(m_pCBuffer, rhs.m_pCBuffer, rhs.m_iBufferSize);
m_iBufferSize = rhs.m_iBufferSize;
m_iCurrentLoc = 0;
}
//===============================================
CBufferDeserialization& CBufferDeserialization::operator=(const CBuffer& rhs)
//===============================================
{
ResizeBuffer(rhs.m_iBufferSize);
memcpy(m_pCBuffer, rhs.m_pCBuffer, m_iBufferSize);
m_iBufferSize = m_iBufferSize;
m_iCurrentLoc = 0;
return *this;
}
//===============================================
CBufferDeserialization::CBufferDeserialization(const char* pBuffer, const uint64_t& iDataSize)
//===============================================
{
ResizeBuffer(iDataSize);
memcpy(m_pCBuffer, pBuffer, iDataSize);
m_iBufferSize = iDataSize;
m_iCurrentLoc = 0;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 char size만큼 데이터를 추출
@입력값 : char
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(char& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 string size만큼 데이터를 추출
@입력값 : string
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(string& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 float size만큼 데이터를 추출
@입력값 : float
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(float& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 double size만큼 데이터를 추출
@입력값 : double
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(double& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 int8_t size만큼 데이터를 추출
@입력값 : int8_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(int8_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 uint8_t size만큼 데이터를 추출
@입력값 : uint8_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(uint8_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 int16_t size만큼 데이터를 추출
@입력값 : int16_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(int16_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 uint16_t size만큼 데이터를 추출
@입력값 : uint16_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(uint16_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 int32_t size만큼 데이터를 추출
@입력값 : int32_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(int32_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 uint32_t size만큼 데이터를 추출
@입력값 : uint32_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(uint32_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 int64_t size만큼 데이터를 추출
@입력값 : int64_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(int64_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : operator>>
@기능 : buffer에서 uint64_t size만큼 데이터를 추출
@입력값 : uint64_t
@출력값 :
@비고 :
*************************************************/
//===============================================
CBufferDeserialization& CBufferDeserialization::operator>>(uint64_t& rhs)
//===============================================
{
memcpy(&rhs, GetData(sizeof(rhs)), sizeof(rhs));
return *this;
}
/************************************************
@함수명 : GetBufferData
@기능 : 전달받은Size만큼 Buffer에서 Data를 추출하여 적재하는 함수
@입력값 : void*(적재할 Buffer Data)
uint64_t(추출할 Data Size)
@출력값 :
@비고 :
*************************************************/
//===============================================
void CBufferDeserialization::GetBufferData(void* pData, const uint64_t& iDataSize)
//===============================================
{
memcpy(pData, GetData(iDataSize), iDataSize);
}
해당 포스팅을 보고 궁금한 사항 or 잘못된 부분이 있는 경우 댓글 달아주시면 확인할 수 있도록 하겠습니다.