Here is my code minimized for the purpose of readability.
I was following this guide: https://www.boost.org/doc/libs/1_63_0/doc/html/boost_asio/example/cpp11/chat/chat_server.cpp
Packet.h:
#ifndef VIBRANIUM_CORE_PACKET_H
#define VIBRANIUM_CORE_PACKET_H
#include <cstdio>
#include <cstdlib>
#include <cstring>
class Packet {
public:
Packet() : body_length_(0)
{
}
enum { max_body_length = 1024 };
enum { header_length = 8 };
char header_[header_length];
const char* data() const;
char* data();
size_t length() const;
const char* body() const;
char* body();
size_t body_length() const;
void body_length(std::size_t new_length);
bool decode_header();
void encode_header();
private:
char data_[header_length + max_body_length];
size_t body_length_;
};
#endif //VIBRANIUM_CORE_PACKET_H
Packet.cpp:
#include "Packet.h"
const char *Packet::data() const {
return data_;
}
char *Packet::data() {
return data_;
}
size_t Packet::length() const {
return header_length + body_length_;
}
const char *Packet::body() const {
return data_ + header_length;
}
char *Packet::body() {
return data_ + header_length;
}
size_t Packet::body_length() const {
return body_length_;
}
void Packet::body_length(std::size_t new_length) {
body_length_ = new_length;
if (body_length_ > max_body_length)
body_length_ = max_body_length;
}
bool Packet::decode_header() {
char header[header_length + 1] = "";
std::strncat(header, data_, header_length);
body_length_ = std::atoi(header);
if (body_length_ > max_body_length)
{
body_length_ = 0;
return false;
}
return true;
}
void Packet::encode_header() {
char header[header_length + 1] = "";
std::sprintf(header, "%4d", static_cast<int>(body_length_));
std::memcpy(data_, header, header_length);
}
ServerOpcode.h:
#ifndef VIBRANIUM_CORE_SERVEROPCODE_H
#define VIBRANIUM_CORE_SERVEROPCODE_H
#include <cstdint>
enum ServerOpcode : uint16_t{
SMSG_AUTH_CONNECTION_RESPONSE = 0x001,
SMSG_LOGIN_REQUEST_RESPONSE = 0x002,
};
#endif //VIBRANIUM_CORE_SERVEROPCODE_H
So I would like to modify encode_header() to set a ServerOpcode as a packet header. I was trying this but it's not working:
void Packet::encode_header(ServerOpcode serverOpcode) {
char header[header_length + 1] = "";
std::strcat(header, static_cast<char>(serverOpcode));
std::memcpy(data_, header, header_length);
}
I have two questions:
How can I make encode header prepend the ServerOpcode? Where is my mistake how can I fix it?
How can I decode an incoming message header than with decode_header()?
Related
I have some simple class with several fields like this:
#ifndef XVector_h
#define XVector_h
struct XVFields
{
unsigned char JointPromosCountSinceBeginning;
unsigned char PromoWeeksCountSinceCurrPromoBeginning;
unsigned char NoPromoWeeksCountSinceLastJointPromo;
bool IsPromo;
};
class XVector
{
public:
XVFields XVFs;
unsigned char *DiscountUsagesCounts;
XVector();
~XVector();
};
#endif XVector_h
It's realization:
#include <sstream>
#include "XVector.h"
#include "DynProgTask.h"
XVector::XVector()
{
memset(&this->XVFs, 0, sizeof(XVFields));
this->DiscountUsagesCounts = (unsigned char*)malloc(DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
memset(this->DiscountUsagesCounts, 0, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
this->UniqueValForHuman_private = NULL;
}
XVector::~XVector()
{
free(this->DiscountUsagesCounts);
}
I Have another class named TaskCase that has XVector as one of it's field. Single TaskCase - single XVector.
I have custom hashers and comparators for both of classes:
#ifndef XVectorHasher_h
#define XVectorHasher_h
#include <Windows.h>
#include "XVector.h"
struct XVectorHasher
{
size_t operator()(const XVector *k) const;
};
struct XVectorComparator
{
bool operator()(const XVector *xv1, const XVector *xv2) const;
};
#endif XVectorHasher_h
cpp:
#include "XVectorHasher.h"
#include "DynProgTask.h"
size_t XVectorHasher::operator()(const XVector *k) const
{
size_t result = 0;
const size_t prime = 31;
int unibytes_count = sizeof(XVFields) + (DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
unsigned char *unibytes = (unsigned char*)malloc(unibytes_count);
memcpy(unibytes, &k->XVFs, sizeof(XVFields));
memcpy(&unibytes[sizeof(XVFields)], k->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
for (size_t i = 0; i < unibytes_count; i++)
result = unibytes[i] + (result * prime);
free(unibytes);
return result;
}
bool XVectorComparator::operator()(const XVector *xv1, const XVector *xv2) const
{
//this operator compares instances bytes to determine their equality
if (memcmp(&xv1->XVFs, &xv2->XVFs, sizeof(XVFields)) != 0)
return false;
if (memcmp(xv1->DiscountUsagesCounts, xv2->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/) != 0)
return false;
return true;
}
Another h:
#ifndef TaskCaseHasher_h
#define TaskCaseHasher_h
#include <Windows.h>
#include "TaskCase.h"
struct TaskCaseHasher
{
size_t operator()(const TaskCase *k) const;
};
struct TaskCaseComparator
{
bool operator()(const TaskCase *xv1, const TaskCase *xv2) const;
};
#endif TaskCaseHasher_h
Another cpp:
#include "XVector.h"
#include "TaskCaseHasher.h"
#include "DynProgTask.h"
size_t TaskCaseHasher::operator()(const TaskCase *tc) const
{
size_t result = 0;
const size_t prime = 31;
XVector *k = const_cast<TaskCase*>(tc)->GET_CurrX();
int unibytes_count = sizeof(XVFields) + (DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
unsigned char *unibytes = (unsigned char*)malloc(unibytes_count);
memcpy(unibytes, &k->XVFs, sizeof(XVFields));
memcpy(&unibytes[sizeof(XVFields)], k->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/);
for (size_t i = 0; i < unibytes_count; i++)
result = unibytes[i] + (result * prime);
free(unibytes);
return result;
}
bool TaskCaseComparator::operator()(const TaskCase *tc1, const TaskCase *tc2) const
{
//this operator compares instances bytes to determine their equality
XVector *xv1 = const_cast<TaskCase*>(tc1)->GET_CurrX();
XVector *xv2 = const_cast<TaskCase*>(tc2)->GET_CurrX();
if (memcmp(&xv1->XVFs, &xv2->XVFs, sizeof(XVFields)) != 0)
return false;
if (memcmp(xv1->DiscountUsagesCounts, xv2->DiscountUsagesCounts, DynProgTask::PresetUsCount/* * sizeof(unsigned char)*/) != 0)
return false;
return true;
}
I have self made single directional list of TaskCase pointers. I iterate over them to find unique TaskCases (the ones which has different XVectors).
I have two different functions for that:
SmartArray<TaskCase*> *TaskStep::GET_UniqueCasesByCurrX() //This one doing it's job in 191 second
{
SmartArray<TaskCase*> *unitcs = new SmartArray<TaskCase*>(this->Cases->Length);
std::unordered_set<XVector*, XVectorHasher, XVectorComparator> unique_xs;
int curr_unique_case_i = 0;
for (TaskCase *itr = this->Cases->Head; itr; itr = itr->NEXT_CASE_PTR)
if (unique_xs.find(itr->GET_CurrX()) == unique_xs.end())
{
unique_xs.insert(itr->GET_CurrX());
unitcs->Data[curr_unique_case_i] = itr;
curr_unique_case_i++;
}
unitcs->Resize(curr_unique_case_i);
return unitcs;
}
std::unordered_set<TaskCase*, TaskCaseHasher, TaskCaseComparator> *TaskStep::GET_UniqueCasesSetByCurrX() //This one doing it's job in 363 seconds
{
std::unordered_set<TaskCase*, TaskCaseHasher, TaskCaseComparator> *unique_tcs = new std::unordered_set<TaskCase*, TaskCaseHasher, TaskCaseComparator>();
for (TaskCase *itr = this->Cases->Head; itr; itr = itr->NEXT_CASE_PTR)
if (unique_tcs->find(itr) == unique_tcs->end())
unique_tcs->insert(itr);
return unique_tcs;
}
std::unordered_set based on XVectors (191 sec) is way faster than std::unordered_set based on TaskCases (363 sec).
But I don't understand why. Anyway it falls to calling GET_CurrX() which returns XVector. In first case I can it by myself and in second case it called automatically inside set. What is possible to do to speedup TaskCase set?
I have implemented a serializer to send data over network. And I have implemented a system that can deserialize primitive data, string, map(string, string), map(string, float), but the error happens with map(string, int) when the deserialized map is used to fetch the value from key. In the debugger I can see that map receive correct value but when I'm trying to get data, I get an error "std::out_of_range at memory location".
Here is my code
#include <stdint.h>
#include <memory>
#include <string>
#include <map>
#include <algorithm>
#define STREAM_ENDIANNESS 0
#define PLATFORM_ENDIANNESS 0
using namespace std;
class OutputMemoryStream
{
void ReallocBuffer(uint32_t inNewLength)
{
mBuffer = static_cast<char*>(std::realloc(mBuffer, inNewLength));
mCapacity = inNewLength;
}
char* mBuffer = nullptr;
uint32_t mHead;
uint32_t mCapacity;
public:
OutputMemoryStream() : mHead(0) { ReallocBuffer(32); }
~OutputMemoryStream()
{
if (mBuffer) { mBuffer = nullptr; }
}
char* GetBufferPtr() const { return mBuffer; }
uint32_t GetLength() const { return mHead; }
void Write(const void* inData, size_t inByteCount)
{
//make sure we have space...
uint32_t resultHead = mHead + static_cast<uint32_t>(inByteCount);
if (resultHead > mCapacity)
{
ReallocBuffer(std::max(mCapacity * 2, resultHead));
}
//copy into buffer at head
std::memcpy(mBuffer + mHead, inData, inByteCount);
//increment head for next write
mHead = resultHead;
}
template< typename T > void Write(T inData)
{
static_assert(std::is_arithmetic< T >::value || std::is_enum< T >::value, "Generic Write only supports primitive data types");
if (STREAM_ENDIANNESS == PLATFORM_ENDIANNESS)
{
Write(&inData, sizeof(inData));
}
else { }
}
template< typename T >
void Write(const std::map< string, T >& inMap)
{
uint32_t elementCount = inMap.size();
Write(elementCount);
for (std::pair<string, T> element : inMap)
{
Write(element.first);
Write(element.second);
}
}
void Write(const std::string& inString)
{
size_t elementCount = inString.size();
Write(elementCount + 1);
Write(inString.data(), (elementCount + 1) * sizeof(char));
}
};
class InputMemoryStream
{
private:
char* mBuffer;
uint32_t mHead;
uint32_t mCapacity;
public:
InputMemoryStream() {}
InputMemoryStream(char* inBuffer, uint32_t inByteCount) : mBuffer(inBuffer), mCapacity(inByteCount), mHead(0) { }
~InputMemoryStream()
{
if (mBuffer) { mBuffer = nullptr; }
}
uint32_t GetRemainingDataSize() const
{
return mCapacity - mHead;
}
void Read(void* outData, uint32_t inByteCount)
{
uint32_t resultHead = mHead + inByteCount;
if (resultHead > mCapacity)
{
//handle error, no data to read!
//...
}
std::memcpy(outData, mBuffer + mHead, inByteCount);
mHead = resultHead;
}
template< typename T > void Read(T& outData)
{
static_assert(std::is_arithmetic< T >::value || std::is_enum< T >::value, "Generic Read only supports primitive data types");
Read(&outData, sizeof(outData));
}
template<typename T1>
void Read(std::map<string, T1> &mapP)
{
size_t elemenCount;
Read(elemenCount);
for (int i = 0; i < elemenCount; i++)
{
string key; T1 value;
Read(key);
Read(value);
std::pair<string, T1> pair(key, value);
mapP.insert(pair);
}
}
void Read(string &outString)
{
size_t strSize;
Read(strSize);
outString.resize(strSize);
for (int i = 0; i < strSize; i++)
{
Read(&outString[i], 1);
}
}
};
class ServerObject
{
OutputMemoryStream outStream;
InputMemoryStream inStream;
map<std::string, int> mapInt;
public:
ServerObject() {};
ServerObject(char* byteArray, int byteCount)
{
InputMemoryStream inStream(byteArray, byteCount);
Deserialize(inStream);
}
~ServerObject() {};
void Serialize()
{
outStream.Write(mapInt);
}
void Deserialize(InputMemoryStream inStream)
{
inStream.Read(mapInt);
}
OutputMemoryStream GetOutStream()
{
return outStream;
}
int GetInt(string key)
{
return mapInt.at(key);
}
void PutInt(string key, int value)
{
mapInt.insert(std::pair<string, int>(key, value));
}
};
int main()
{
ServerObject * so = new ServerObject();
so->PutInt("test", 10);
so->Serialize();
ServerObject * so1 = new ServerObject(so->GetOutStream().GetBufferPtr(), so->GetOutStream().GetLength());
int i = so1->GetInt("test");
system("pause>NULL");
return 0;
}
Your void Write(const std::string& inString) function of OutputMemoryStream should not store additional byte of buffer for null terminator because std::string will not contain null terminator but if you use c_str(), a null terminator will be included in the return from this method. Don't get confused with the internal structure of the memory. std::string stores the length of the string in its member variable so there is no need of null terminator. The function should be as shown below.
void Write(const std::string& inString)
{
size_t elementCount = inString.size();
Write(elementCount);
Write(inString.data(), elementCount * sizeof(char));
}
I am trying to implement a custom data object for XML strings, so that if it exists in the clipboard I can parse that accordingly. The code for the XMLDataObject is as follows:
class XMLDataFormat : public wxDataFormat
{
public:
XMLDataFormat() : wxDataFormat(wxT("XMLDataFormat")) {}
};
class XMLDataObject : public wxDataObjectSimple
{
public:
XMLDataObject(const wxString& xmlstring = wxEmptyString) : wxDataObjectSimple(), m_XMLString(xmlstring)
{
SetFormat(XMLDataFormat());
}
size_t GetLength() const { return m_XMLString.Len() + 1; }
wxString GetXML() const { return m_XMLString; }
void SetXML(const wxString& xml) { m_XMLString = xml; }
// Must provide overloads to avoid hiding them (and warnings about it)
size_t GetDataSize() const
{
return sizeof(void*); //or return GetLength()
}
bool GetDataHere(void *buf) const
{
char* c = _strdup(m_XMLString.c_str());
buf = (void*)c;
return true;
}
bool SetData(size_t len, const void* buf)
{
char* c = (char*)buf;
std::string stdString(c, len);
m_XMLString << stdString;
return true;
}
private:
wxString m_XMLString;
};
I send the data to clipboard (when user clicks copy) in the following fashion:
wxDataObjectComposite* dataobj = new wxDataObjectComposite();
dataobj->Add(new XMLDataObject("XML"));
if (wxTheClipboard->Open()) wxTheClipboard->SetData(dataobj);
wxTheClipboard->Close();
To get the data from the clipboard:
if (wxTheClipboard->Open()) {
XMLDataObject xmlObj;
wxTheClipboard->GetData(xmlObj);
if (xmlObj.GetLength() != 0) wxMessageBox(xmlObj.GetXML());
}
wxTheClipboard->Close();
When user clicks paste, I get weird characters instead of the text "XML". I realized that in the functions bool GetDataHere(void *buf) const and bool SetData(size_t len, const void* buf) the address of buf is different. I am not sure but maybe that is how it should be since Clipboard owns the data or behind the scenes wxWidgets is doing something.
By the way, I am using VS 2015 on Windows 10 and using wxWidgets 3.1.0.
Any suggestions is appreciated.
The following code so far has worked correctly:
class XMLDataFormat : public wxDataFormat
{
public:
XMLDataFormat() : wxDataFormat(wxT("XMLDataFormat")) {}
};
class XMLDataObject : public wxDataObjectSimple
{
public:
XMLDataObject(const wxString& xmlstring = wxEmptyString) : wxDataObjectSimple(), m_XMLString(xmlstring)
{
SetFormat(XMLDataFormat());
}
size_t GetLength() const { return m_XMLString.Len() + 1; }
wxString GetXML() const { return m_XMLString; }
void SetXML(const wxString& xml) { m_XMLString = xml; }
size_t GetDataSize() const
{
return GetLength();
}
bool GetDataHere(void *buf) const
{
//char* c = _strdup(m_XMLString.c_str()); not needed as per suggestion
memcpy(buf, m_XMLString.c_str(), m_XMLString.Len()+1);
return true;
}
bool SetData(size_t len, const void* buf)
{
//char* c = new char[len + 1]; not needed as per suggestion
//memcpy(c, buf, len); not needed as per suggestion
m_XMLString = wxString::FromUTF8((const char*)buf); //changed as per suggestion
//delete c;
return true;
}
virtual size_t GetDataSize(const wxDataFormat&) const
{
return GetDataSize();
}
virtual bool GetDataHere(const wxDataFormat&, void *buf) const
{
return GetDataHere(buf);
}
virtual bool SetData(const wxDataFormat&, size_t len, const void *buf)
{
return SetData(len, buf);
}
private:
wxString m_XMLString;
};
Any suggestions as to improve it is again appreciated.
I was just experimenting and making my own string class. (Mainly because there is some stuff I wanted to build in custom methods like "toBase64" etc. Anyways, I was wondering how you could access the private member of char* when you use &String[0].
I thought you could use operator-overloading but I currently only have it as String[0] returns the char*. (I know & is the pointer operator).
String.h
namespace CoffeeBeans
{
class _declspec(dllexport) Coffee_String
{
char* String;
int StringLength;
public:
Coffee_String();
Coffee_String(LPCSTR CString);
LPSTR operator[](int);
~Coffee_String();
};
}
String.cpp
#include "stdafx.h"
#include "String.h"
#include <Windows.h>
CoffeeBeans::Coffee_String::Coffee_String() {
this->String = nullptr;
this->StringLength = 0;
}
CoffeeBeans::Coffee_String::~Coffee_String() {
if (String != nullptr) {
delete[] this->String;
this->String = nullptr;
this->StringLength = 0;
}
}
CoffeeBeans::Coffee_String::Coffee_String(LPCSTR CString) {
int StringLength = strlen(CString) + 1;
this->String = new char[StringLength]();
this->StringLength = StringLength - 1;
memcpy_s(this->String, StringLength, CString, StringLength);
}
LPSTR CoffeeBeans::Coffee_String::operator[](int)
{
return this->String;
}
Main.cpp
case WM_CREATE:{
CoffeeBeans::Coffee_String String("Test");
//I want to be able to do
//strcpy_s(&String[0], 3, "hi"); //Copy "hi" into the private variable char*String.
//I know this isn't a practical use, I wanted quick example (I would really pass it to recv (WinSock2))
MessageBeep(0);
break;
}
Your operator[] is returning the wrong value. In order for &String[index] to access the correct memory address, operator[] needs to return a reference to the character at the specified index, not return the string pointer itself, as you are currently doing.
If you look at the actual declaration of std::string::operator[], you will see that it returns a std::string::reference (aka char &) or std::string::const_reference (aka const char &) (depending on whether it is being called on a non-const or const std::string object).
Try something more like this:
String.h
namespace CoffeeBeans
{
class _declspec(dllexport) Coffee_String
{
char* String;
int StringLength;
public:
Coffee_String();
Coffee_String(const Coffee_String &src);
Coffee_String(const char *src);
~Coffee_String();
char& operator[](int index);
const char& operator[](int index) const;
Coffee_String& operator=(const Coffee_String &rhs);
};
};
String.cpp
#include "stdafx.h"
#include "String.h"
#include <algorithm>
#include <cstring>
CoffeeBeans::Coffee_String::Coffee_String() {
String = nullptr;
StringLength = 0;
}
CoffeeBeans::Coffee_String::Coffee_String(const CoffeeBeans::Coffee_String &src) {
StringLength = src.StringLength;
String = new char[StringLength+1];
std::copy(src.String, src.String+StringLength, String);
String[StringLength] = 0;
}
CoffeeBeans::Coffee_String::Coffee_String(const char *src) {
StringLength = std::strlen(str);
String = new char[StringLength+1];
std::copy(src, src+StringLength, String);
String[StringLength] = 0;
}
CoffeeBeans::Coffee_String::~Coffee_String() {
delete[] String;
String = nullptr;
StringLength = 0;
}
char& CoffeeBeans::Coffee_String::operator[](int index)
{
return String[index];
}
const char& CoffeeBeans::Coffee_String::operator[](int index) const
{
return String[index];
}
CoffeeBeans::Coffee_String& CoffeeBeans::Coffee_String::operator=(const CoffeeBeans::Coffee_String &rhs);
{
Coffee_String temp(rhs);
std::swap(String, temp.String);
std::swap(StringLength, temp.String);
return *this;
}
Main.cpp
case WM_CREATE: {
CoffeeBeans::Coffee_String String("Test");
strcpy_s(&String[0], 3, "hi"); //Copy "hi" into the private variable char *String...
// note that the content of String will become "hi\0t\0", not "hi\0"
// and StringLength will still be 4...
MessageBeep(0);
break;
}
The ReadBmpFromFile/Stream functions work fine. (Still a little slow but they otherwise work). The WriteBmpToFile/Stream functions work in that they successfully create a valid .bmp file but only the bottom few thousands of pixels, the rest of the file is black.
It shouldn't be that hard to see why, the pixel data is stored from bottom to top so the problem should be near the top of the file.
Reference: BMP File structure
Original:
Copy:
I stepped through with a debugger and the data is successfully being copied from the containing object. It shouldn't fail when I am reading from a file and then immediately writing it back:
if(_keyboard->KeyDown(KEY_S)) {
BitmapFileType bmp_test;
ReadBmpFromFile("maglot.bmp_test", bmp_test);
//Change filename to not destroy original!
WriteBmpToFile("maglot_copy.bmp_test", bmp_test);
//Allegro's implementation to test correct output against.
//save_bmp("maglot_copy.bmp", image->GetImage(), nullptr);
}
Bitmap class definition:
class BitmapFileType {
public:
struct Header {
std::vector<unsigned char> file_signature;
unsigned int file_size;
unsigned short unused1;
unsigned short unused2;
unsigned int pixel_array_offset;
unsigned int DIB_header_size;
unsigned int width;
unsigned int height;
unsigned short plane_count;
unsigned short bpp;
unsigned int compression_method;
unsigned int raw_size;
unsigned int print_resolution_horizontal;
unsigned int print_resolution_vertical;
unsigned int pallete_colors_count;
unsigned int important_colors_count;
Header() :
file_signature(2),
file_size(0),
unused1(0),
unused2(0),
pixel_array_offset(0),
DIB_header_size(0),
width(0),
height(0),
plane_count(0),
bpp(0),
compression_method(0),
raw_size(0),
print_resolution_horizontal(0),
print_resolution_vertical(0),
pallete_colors_count(0),
important_colors_count(0)
{ /* DO NOTHING */ }
Header(const Header& other) :
file_signature(other.file_signature),
file_size(other.file_size),
unused1(other.unused1),
unused2(other.unused2),
pixel_array_offset(other.pixel_array_offset),
DIB_header_size(other.DIB_header_size),
width(other.width),
height(other.height),
plane_count(other.plane_count),
bpp(other.bpp),
compression_method(other.compression_method),
raw_size(other.raw_size),
print_resolution_horizontal(other.print_resolution_horizontal),
print_resolution_vertical(other.print_resolution_vertical),
pallete_colors_count(other.pallete_colors_count),
important_colors_count(other.important_colors_count)
{ /* DO NOTHING */ }
Header& operator=(const Header& rhs) {
if(this == &rhs) return *this;
this->file_signature = rhs.file_signature;
this->file_size = rhs.file_size;
this->unused1 = rhs.unused1;
this->unused2 = rhs.unused2;
this->pixel_array_offset = rhs.pixel_array_offset;
this->DIB_header_size = rhs.DIB_header_size;
this->width = rhs.width;
this->height = rhs.height;
this->plane_count = rhs.plane_count;
this->bpp = rhs.bpp;
this->compression_method = rhs.compression_method;
this->raw_size = rhs.raw_size;
this->print_resolution_horizontal = rhs.print_resolution_horizontal;
this->print_resolution_vertical = rhs.print_resolution_vertical;
this->pallete_colors_count = rhs.pallete_colors_count;
this->important_colors_count = rhs.important_colors_count;
return *this;
}
};
struct PixelData {
std::vector<unsigned char> _pixel_data;
PixelData() : _pixel_data() { /* DO NOTHING */ }
PixelData(const PixelData& other) : _pixel_data(other._pixel_data) { /* DO NOTHING */ }
PixelData(const std::vector<unsigned char>& pixel_data) : _pixel_data(pixel_data) { /* DO NOTHING */ }
};
BitmapFileType() : _header(), _data() { /* DO NOTHING */ }
BitmapFileType(const BitmapFileType::Header& header) : _header(header), _data() { /* DO NOTHING */ }
BitmapFileType(const BitmapFileType::Header& header, const PixelData& data) : _header(header), _data(data) { /* DO NOTHING */ }
BitmapFileType& operator=(const BitmapFileType& rhs) {
if(this == &rhs) return *this;
_header = rhs._header;
_data = rhs._data;
return *this;
}
const Header& GetHeader() const {
return _header;
}
Header& GetHeader() {
return const_cast<Header&>(static_cast<const BitmapFileType&>(*this).GetHeader());
}
const PixelData& GetPixelData() const {
return _data;
}
PixelData& GetPixelData() {
return const_cast<PixelData&>(static_cast<const BitmapFileType&>(*this).GetPixelData());
}
protected:
private:
Header _header;
PixelData _data;
};
Function definitions:
#include "FileTypeUtility.h"
#include <fstream>
void WriteBmpToFile(const std::string& filename, const BitmapFileType& bmp) {
std::ofstream ofs;
ofs.open(filename);
WriteBmpToStream(ofs, bmp);
ofs.close();
}
void ReadBmpFromFile(const std::string& filename, BitmapFileType& bmp) {
std::ifstream ifs;
ifs.open(filename);
ReadBmpFromStream(ifs, bmp);
ifs.close();
}
void ReadBmpFromStream(std::istream& input_stream, BitmapFileType& bmp) {
if(input_stream.fail() || input_stream.bad() || input_stream.eof()) return;
std::vector<unsigned char> file_signature(2);
input_stream.read(reinterpret_cast<char*>(file_signature.data()), file_signature.size());
if((file_signature[0] != 'B' || file_signature[1] != 'M')) {
input_stream.seekg(-2, std::ios_base::cur);
input_stream.setstate(std::ios_base::failbit);
return;
}
unsigned int file_size = 0;
input_stream.read(reinterpret_cast<char*>(&file_size), sizeof(file_size));
unsigned short unused1 = 0;
input_stream.read(reinterpret_cast<char*>(&unused1), sizeof(unused1));
unsigned short unused2 = 0;
input_stream.read(reinterpret_cast<char*>(&unused2), sizeof(unused2));
unsigned int pixel_array_offset = 0;
input_stream.read(reinterpret_cast<char*>(&pixel_array_offset), sizeof(pixel_array_offset));
unsigned int DIB_header_size = 0;
input_stream.read(reinterpret_cast<char*>(&DIB_header_size), sizeof(DIB_header_size));
unsigned int width = 0;
input_stream.read(reinterpret_cast<char*>(&width), sizeof(width));
unsigned int height = 0;
input_stream.read(reinterpret_cast<char*>(&height), sizeof(height));
unsigned short plane_count = 0;
input_stream.read(reinterpret_cast<char*>(&plane_count), sizeof(plane_count));
unsigned short bpp = 0;
input_stream.read(reinterpret_cast<char*>(&bpp), sizeof(bpp));
unsigned int compression_method = 0;
input_stream.read(reinterpret_cast<char*>(&compression_method), sizeof(compression_method));
unsigned int raw_size = 0;
input_stream.read(reinterpret_cast<char*>(&raw_size), sizeof(raw_size));
unsigned int print_resolution_horizontal = 0;
input_stream.read(reinterpret_cast<char*>(&print_resolution_horizontal), sizeof(print_resolution_horizontal));
unsigned int print_resolution_vertical = 0;
input_stream.read(reinterpret_cast<char*>(&print_resolution_vertical), sizeof(print_resolution_vertical));
unsigned int pallete_colors_count = 0;
input_stream.read(reinterpret_cast<char*>(&pallete_colors_count), sizeof(pallete_colors_count));
unsigned int important_colors_count = 0;
input_stream.read(reinterpret_cast<char*>(&important_colors_count), sizeof(important_colors_count));
std::vector<unsigned char> pixel_array(raw_size);
input_stream.read(reinterpret_cast<char*>(pixel_array.data()), pixel_array.size());
BitmapFileType::PixelData bmpPixels(pixel_array);
BitmapFileType::Header bmpHeader;
bmpHeader.file_signature = file_signature;
bmpHeader.file_size = file_size;
bmpHeader.unused1 = unused1;
bmpHeader.unused2 = unused2;
bmpHeader.pixel_array_offset = pixel_array_offset;
bmpHeader.DIB_header_size = DIB_header_size;
bmpHeader.width = width;
bmpHeader.height = height;
bmpHeader.plane_count = plane_count;
bmpHeader.bpp = bpp;
bmpHeader.compression_method = compression_method;
bmpHeader.raw_size = raw_size;
bmpHeader.print_resolution_horizontal = print_resolution_horizontal;
bmpHeader.print_resolution_vertical = print_resolution_vertical;
bmpHeader.pallete_colors_count = pallete_colors_count;
bmpHeader.important_colors_count = important_colors_count;
bmp = BitmapFileType(bmpHeader, bmpPixels);
return;
}
void WriteBmpToStream(std::ostream& output_stream, const BitmapFileType& bmp) {
if(output_stream.fail() || output_stream.bad() || output_stream.eof()) return;
const BitmapFileType::Header& bmpHeader(bmp.GetHeader());
output_stream.write(reinterpret_cast<const char*>(bmpHeader.file_signature.data()), bmpHeader.file_signature.size());
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.file_size), sizeof(bmpHeader.file_size));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.unused1), sizeof(bmpHeader.unused1));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.unused2), sizeof(bmpHeader.unused2));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.pixel_array_offset), sizeof(bmpHeader.pixel_array_offset));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.DIB_header_size), sizeof(bmpHeader.DIB_header_size));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.width), sizeof(bmpHeader.width));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.height), sizeof(bmpHeader.height));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.plane_count), sizeof(bmpHeader.plane_count));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.bpp), sizeof(bmpHeader.bpp));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.compression_method), sizeof(bmpHeader.compression_method));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.raw_size), sizeof(bmpHeader.raw_size));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.print_resolution_horizontal), sizeof(bmpHeader.print_resolution_horizontal));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.print_resolution_vertical), sizeof(bmpHeader.print_resolution_vertical));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.pallete_colors_count), sizeof(bmpHeader.pallete_colors_count));
output_stream.write(reinterpret_cast<const char*>(&bmpHeader.important_colors_count), sizeof(bmpHeader.important_colors_count));
const BitmapFileType::PixelData& bmpPixelData(bmp.GetPixelData());
output_stream.write(reinterpret_cast<const char*>(bmpPixelData._pixel_data.data()), bmpPixelData._pixel_data.size());
return;
}