C++/자체 라이브러리

Serialization Deserialization Lib - C++

초심을 찾자 2023. 12. 5. 18:02
SMALL

 

직렬화 역직렬화란?

오늘은 데이터를 서로 주고받을 때 필요한 기능 중 하나인 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 잘못된 부분이 있는 경우 댓글 달아주시면 확인할 수 있도록 하겠습니다.