Insert an array of tables into one table SQLite C/C++ - c++

I made my own database format, and it sadly required too much memory and the size of it got horrendous and upkeep was horrible.
So I'm looking for a way to store an array of a struct that's in an object into a table.
I'm guessing I need to use a blob, but all other options are welcome. An easy way to implement a blob would be helpful as well.
I've attached my saving code and related structures(Updated from my horrible post earlier)
#include "stdafx.h"
#include <string>
#include <stdio.h>
#include <vector>
#include "sqlite3.h"
using namespace std;
struct PriceEntry{
float cardPrice;
string PriceDate;
int Edition;
int Rarity;
};
struct cardEntry{
string cardName;
long pesize;
long gsize;
vector<PriceEntry> cardPrices;
float vThreshold;
int fav;
};
vector<cardEntry> Cards;
void FillCards(){
int i=0;
int j=0;
char z[32]={0};
for(j=0;j<3;j++){
cardEntry tmpStruct;
sprintf(z, "Card Name: %d" , i);
tmpStruct.cardName=z;
tmpStruct.vThreshold=1.00;
tmpStruct.gsize=0;
tmpStruct.fav=1;
for(i=0;i<3;i++){
PriceEntry ss;
ss.cardPrice=i+1;
ss.Edition=i;
ss.Rarity=i-1;
sprintf(z,"This is struct %d", i);
ss.PriceDate=z;
tmpStruct.cardPrices.push_back(ss);
}
tmpStruct.pesize=tmpStruct.cardPrices.size();
Cards.push_back(tmpStruct);
}
}
int SaveCards(){
// Create an int variable for storing the return code for each call
int retval;
int CardCounter=0;
int PriceEntries=0;
char tmpQuery[256]={0};
int q_cnt = 5,q_size = 256;
sqlite3_stmt *stmt;
sqlite3 *handle;
retval = sqlite3_open("sampledb.sqlite3",&handle);
if(retval)
{
printf("Database connection failed\n");
return -1;
}
printf("Connection successful\n");
//char create_table[100] = "CREATE TABLE IF NOT EXISTS users (uname TEXT PRIMARY KEY,pass TEXT NOT NULL,activated INTEGER)";
char create_table[] = "CREATE TABLE IF NOT EXISTS Cards (CardName TEXT, PriceNum NUMERIC, Threshold NUMERIC, Fav NUMERIC);";
retval = sqlite3_exec(handle,create_table,0,0,0);
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(handle) );
for(CardCounter=0;CardCounter<Cards.size();CardCounter++){
char Query[512]={0};
for(PriceEntries=0;PriceEntries<Cards[CardCounter].cardPrices.size();PriceEntries++){
//Here is where I need to find out the process of storing the vector of PriceEntry for Cards then I can modify this loop to process the data
}
sprintf(Query,"INSERT INTO Cards VALUES('%s', %d, %f, %d)",
Cards[CardCounter].cardName.c_str(),
Cards[CardCounter].pesize,
Cards[CardCounter].vThreshold,
Cards[CardCounter].fav); //My insert command
retval = sqlite3_exec(handle,Query,0,0,0);
if(retval){
printf( "Could not prepare statement: %s\n", sqlite3_errmsg(handle) );
}
}
// Insert first row and second row
sqlite3_close(handle);
return 0;
}
I tried googling but my results didn't suffice.

You have two types here: Cards and PriceEntries. And for each Card there can be many PriceEntries.
You can store Cards in one table, one Card per row. But you're puzzled about how to store the PriceEntries, right?
What you'd normally do here is have a second table for PriceEntries, keyed off a unique column (or columns) of the Cards table. I guess the CardName is unique to each card? Let's go with that. So your PriceEntry table would have a column CardName, followed by columns of PriceEntry information. You'll have a row for each PriceEntry, even if there are duplicates in the CardName column.
The PriceEntry table might look like:
CardName | Some PE value | Some other PE value
Ace | 1 | 1
Ace | 1 | 5
2 | 2 | 3
and so on. So when you want to find the array of PriceEntries for a card, you'd do
select * from PriceEntry where CardName = 'Ace'
And from the example data above you'd get back 2 rows, which you could shove into an array (if you wanted to).
No need for BLOBs!

This is a simple serialization and deserialization system. The class PriceEntry has been extended with serialization support (very simply). Now all you have to do is serialize a PriceEntry (or a set of them) to binary data and store it in a blob column. Later on, you get the blob data and from that deserialize a new PriceEntry with the same values. An example of how it is used is given at the bottom. Enjoy.
#include <iostream>
#include <vector>
#include <string>
#include <cstring> // for memcpy
using std::vector;
using std::string;
// deserialization archive
struct iarchive
{
explicit iarchive(vector<unsigned char> data)
: _data(data)
, _cursor(0)
{}
void read(float& v) { read_var(v); }
void read(int& v) { read_var(v); }
void read(size_t& v) { read_var(v); }
void read(string& v) { read_string(v); }
vector<unsigned char> data() { return _data; }
private:
template <typename T>
void read_var(T& v)
{
// todo: check that the cursor will not be past-the-end after the operation
// read the binary data
std::memcpy(reinterpret_cast<void*>(&v), reinterpret_cast<const void*>(&_data[_cursor]), sizeof(T));
// advance the cursor
_cursor += sizeof(T);
}
inline
void
read_string(string& v)
{
// get the array size
size_t sz;
read_var(sz);
// get alignment padding
size_t padding = sz % 4;
if (padding == 1) padding = 3;
else if (padding == 3) padding = 1;
// todo: check that the cursor will not be past-the-end after the operation
// resize the string
v.resize(sz);
// read the binary data
std::memcpy(reinterpret_cast<void*>(&v[0]), reinterpret_cast<const void*>(&_data[_cursor]), sz);
// advance the cursor
_cursor += sz + padding;
}
vector<unsigned char> _data; // archive data
size_t _cursor; // current position in the data
};
// serialization archive
struct oarchive
{
void write(float v) { write_var(v); }
void write(int v) { write_var(v); }
void write(size_t v) { write_var(v); }
void write(const string& v) { write_string(v); }
vector<unsigned char> data() { return _data; }
private:
template <typename T>
void write_var(const T& v)
{
// record the current data size
size_t s(_data.size());
// enlarge the data
_data.resize(s + sizeof(T));
// store the binary data
std::memcpy(reinterpret_cast<void*>(&_data[s]), reinterpret_cast<const void*>(&v), sizeof(T));
}
void write_string(const string& v)
{
// write the string size
write(v.size());
// get alignment padding
size_t padding = v.size() % 4;
if (padding == 1) padding = 3;
else if (padding == 3) padding = 1;
// record the data size
size_t s(_data.size());
// enlarge the data
_data.resize(s + v.size() + padding);
// store the binary data
std::memcpy(reinterpret_cast<void*>(&_data[s]), reinterpret_cast<const void*>(&v[0]), v.size());
}
vector<unsigned char> _data; /// archive data
};
struct PriceEntry
{
PriceEntry()
{}
PriceEntry(iarchive& in) // <<< deserialization support
{
in.read(cardPrice);
in.read(PriceDate);
in.read(Edition);
in.read(Rarity);
}
void save(oarchive& out) const // <<< serialization support
{
out.write(cardPrice);
out.write(PriceDate);
out.write(Edition);
out.write(Rarity);
}
float cardPrice;
string PriceDate;
int Edition;
int Rarity;
};
int main()
{
// create a PriceEntry
PriceEntry x;
x.cardPrice = 1;
x.PriceDate = "hi";
x.Edition = 3;
x.Rarity = 0;
// serialize it
oarchive out;
x.save(out);
// create a deserializer archive, from serialized data
iarchive in(out.data());
// deserialize a PriceEntry
PriceEntry y(in);
std::cout << y.cardPrice << std::endl;
std::cout << y.PriceDate << std::endl;
std::cout << y.Edition << std::endl;
std::cout << y.Rarity << std::endl;
}

Related

Read 2d array from txt file in c++ [duplicate]

This question already has answers here:
Characters duplicate in a multidimensional array
(3 answers)
Closed 1 year ago.
This is my code
#include<bits/stdc++.h>
using namespace std;
int main()
{
char arr1[10][10];
cout << "Reading Start" << endl;
ifstream rfile("test.txt");
rfile.getline(arr1[10], 10);
int i, j;
for (i = 0; i < 6; i++)
{
for (j = 0; i < 6; j++)
{
cout << arr1[i][j];
}
}
cout << "\nRead Done" << endl << endl;
rfile.close();
}
This is my test.txt file
0 4 7 0 0 0
4 0 0 5 3 0
7 0 0 0 6 0
0 5 3 0 0 2
0 3 4 0 0 2
0 0 0 2 2 0
I want to read this matrix but when using the above code then it shows core dumped output, can anyone give me a better solution to do this thing?
can anyone give me a better solution to do this thing?
A better alternative would be to use a 2D vector as shown below. The advantage of using a vector over an array is that you don't need to specify(know) the rows and columns beforehand. That is, the text input file can have as many rows and columns and there is no need to ask the user(or preallocate) how many rows and columns does the file have. std::vector will take care of it as shown below.
The below program uses a 2D std::vector for storing information(like integers values in this case) in 2D manner. After reading all the values from the file you can process the vector according to your needs. The program shown reads data(int values) from input.txt and store those in a 2D vector. Also, this program works even if there are uneven number of columns. You can use the below program as a reference(starting point).
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include<fstream>
int main() {
std::string line;
int word;
std::ifstream inFile("input.txt");
//create/use a std::vector instead of builit in array
std::vector<std::vector<int>> vec;
if(inFile)
{
while(getline(inFile, line, '\n'))
{
//create a temporary vector that will contain all the columns
std::vector<int> tempVec;
std::istringstream ss(line);
//read word by word(or int by int)
while(ss >> word)
{
//std::cout<<"word:"<<word<<std::endl;
//add the word to the temporary vector
tempVec.push_back(word);
}
//now all the words from the current line has been added to the temporary vector
vec.emplace_back(tempVec);
}
}
else
{
std::cout<<"file cannot be opened"<<std::endl;
}
inFile.close();
//now you can do the whatever processing you want on the vector
//lets check out the elements of the 2D vector so the we can confirm if it contains all the right elements(rows and columns)
for(std::vector<int> &newvec: vec)
{
for(const int &elem: newvec)
{
std::cout<<elem<<" ";
}
std::cout<<std::endl;
}
return 0;
}
The output of the above program can be seen here. The input file through which int values are read is also given at the above mentioned link.
Advantages of using vector
You don't need to ask the user for the number of rows and columns in the input file. That is you don't have to fix(hardcode) the size of your array.
The above program works even if there are uneven entries in any particular row.
std::vector takes care of memory management for you. So you don't have to use new and delete by yourself which needs more attention/care.(in case you're thinking of creating array on heap)
Because there are so many possible solutions, let us just show some of them.
The basic difference is:
If we know the dimensions of the array at compile time, so, if there are compile time constants, then we can still use a C-Style array, or better, a std::array.
If we do not know the dimensions of the source data array, then we need a dynamic container that can grow, for example a std::vector.
In all cases, we can use the index operator [] with a standard for loop or a range based for loop with references. There is no difference.
Examples:
C-Style array with standard for loops and index based access
#include <iostream>
#include <fstream>
constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
char array2D[NumberOfRows][NumberOfColumns]{};
// Read the rows and columns from the source file
for (int row = 0; row < NumberOfRows; ++row)
for (int col = 0; col < NumberOfColumns; ++col)
sourceFileStream >> array2D[row][col];
// Debug output
for (int row = 0; row < NumberOfRows; ++row) {
for (int col = 0; col < NumberOfColumns; ++col) std::cout << array2D[row][col] << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
C-Style array with range based for loop and reference access
#include <iostream>
#include <fstream>
constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
char array2D[NumberOfRows][NumberOfColumns]{};
// Read the rows and columns from the source file
for (auto& row : array2D)
for (auto& col : row)
sourceFileStream >> col;
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
C++ std::array with range based for loop
#include <iostream>
#include <fstream>
#include <array>
constexpr int NumberOfRows = 6;
constexpr int NumberOfColumns = 6;
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array toholdall data and initialize it with all 0
std::array<std::array<char, NumberOfColumns>, NumberOfRows> array2D{};
// Read the rows and columns from the source file
for (auto& row : array2D)
for (auto& col : row)
sourceFileStream >> col;
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
Dynamic solution, with a std::vector
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
int main() {
// Open the sourcefile
std::ifstream sourceFileStream{ "test.txt" };
// And check, if it could be opened
if (sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
std::vector<std::vector<char>> array2D{};
// Read the rows and columns from the source file
std::string line{};
while (std::getline(sourceFileStream, line)) {
// Add a new row to our matrix
array2D.push_back(std::vector<char>{});
// Read all column data
char c{};
for (std::istringstream iss(line); iss >> c; array2D.back().push_back(c))
;
}
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
More modern and compact C++ solution
#include <vector>
#include <string>
#include <iterator>
int main() {
// Open the sourcefile and check, if it could be opened
if (std::ifstream sourceFileStream{ "test.txt"}; sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
std::vector<std::vector<char>> array2D{};
// Read the rows and columns from the source file
for (std::string line{}; std::getline(sourceFileStream, line);) {
std::istringstream iss(line);
array2D.push_back({ std::istream_iterator<char>(iss), {} });
}
// Debug output
for (const auto& row : array2D) {
for (const auto& col : row) std::cout << col << ' ';
std::cout << '\n';
}
}
else std::cerr << "\nError: Could not open source file\n\n";
}
And now, we imagine that we do not have any vector or even a string.
For that, we build a small class "DynamicArray" with some functions and an iterator. This can easily be extended.
And the result will be that in main, only one small statement, sourceFileStream >> dada; will read all the data into a 2d array.
Please note. We are only using stream functions for stream io, nothing more.
Cool . . .
#include <iostream>
#include <sstream>
#include <fstream>
// The Dynamic Array has an initial capacity.
// If more elements will be added, there will be a reallocation with doublecapacity
constexpr unsigned int InitialCapacity{ 4 };
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
protected:
// Internal data ------------------------------------------------------------------------------
T* data{}; // Dynamic Storage for Data
unsigned int numberOfElements{}; // Number oe elements currently in the container
unsigned int capacity{ InitialCapacity }; // Current maximum capacity of the container
public:
// Construction and Destruction ---------------------------------------------------------------
DynamicArray() { data = new T[capacity]; } // Default constructor. Allocate new memory
DynamicArray(const DynamicArray& other) { // Copy constructor. Make a deep copy
capacity = numberOfElements = other.numberOfElements;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < other.numberOfElements; ++k)
data[k] = other.data[k]; // Copy data
}
~DynamicArray() { delete[] data; } // Destructor: Release previously allocated memory
bool empty() { return numberOfElements == 0; }
void clear() { numberOfElements = 0; }; // Clear will not delete anything. Just set element count to 0
void push_back(const T& d) { // Add a new element at the end
if (numberOfElements >= capacity) { // Check, if capacity of this dynamic array is big enough
capacity *= 2; // Obviously not, we will double the capacity
T* temp = new T[capacity]; // Allocate new and more memory
for (unsigned int k = 0; k < numberOfElements; ++k)
temp[k] = data[k]; // Copy data from old memory to new memory
delete[] data; // Release old memory
data = temp; // And assign newly allocated memory to old pointer
}
data[numberOfElements++] = d; // And finally, stor the given fata at the end of the container
}
// Add iterator properties to class ---------------------------------------------------------------
// Local class for iterator
class iterator{
T* iter{}; // This will be the iterator
public: // Define alias names necessary for the iterator functionality
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
explicit iterator(T* i) : iter(i) {}; // Default constructor for the iterator
T operator *() const { return *iter; } // Dereferencing
iterator& operator ++() { ++iter; return *this; } // Pre-Increment
bool operator != (const iterator& other) { return iter != other.iter; } // Comparison
};
// Begin and end function to initiliaze an iterator
iterator begin() const { return iterator(data); }
iterator end() const { return iterator (data + numberOfElements); }
// Operators for class------------------------ ---------------------------------------------------------------
T& operator[] (const size_t i) { return data[i]; } // Index operator, get data at given index. No boundary chek
DynamicArray& operator=(const DynamicArray& other) { // Assignment operator. Make a deep copy
if (this != &other) { // Prevent self-assignment
delete[] data; // Release any previosly existing memory
capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
data = new int[capacity]; // Get new memory, depending on size of other
for (unsigned int k = 0; k < numberOfElements; ++k) // Copy other data
data[k] = other.data[k];
}
return *this;
}
// Extractor and Inserter ------------------------ ---------------------------------------------------------------
friend std::istream& operator >> (std::istream& is, DynamicArray& d) {
std::stringstream ss{};
for (char c{}; (is.get(c) and c != '\n'); ss << c); // Read one line until newline into a stringstream
for (T x{}; ss >> x; d.push_back(x)); // Now extract the data from there
return is;
}
friend std::ostream& operator << (std::ostream& os, const DynamicArray& d) {
for (unsigned int k = 0; k < d.numberOfElements; ++k) // Ultra simple output
os << d.data[k] << ' ';
return os;
}
};
// -----------------------------------------------------------------------------------------------------------
// Very simple 2d array. Derived from standard dynamic array and just defining differen input and output
template <typename T>
class Dynamic2dArray : public DynamicArray<DynamicArray<T>> {
public:
friend std::istream& operator >> (std::istream& is, Dynamic2dArray& d) {
for (DynamicArray<T> temp{}; is >> temp; d.push_back(temp), temp.clear());
return is;
}
friend std::ostream& operator << (std::ostream& os, const Dynamic2dArray& d) {
for (unsigned int k = 0; k < d.numberOfElements; ++k)
os << d.data[k] << '\n';
return os;
}
};
// -----------------------------------------------------------------------------------------------------------
int main() {
// Open the sourcefile and check, if it could be opened
if (std::ifstream sourceFileStream{ "test.txt" }; sourceFileStream) {
// Define 2D array to hold all data and initialize it with all 0
Dynamic2dArray<int> dada;
// Read complete matrix from file
sourceFileStream >> dada;
// Debug output. Show complete Matrix
std::cout << dada;
}
else std::cerr << "\n\nError. Could not open source file\n\n";
}
Basically, everything is the same, somehow . . .
There are a lot of other ways to perform the specific task, but i guess your method is not wrong, and you have just made a simple typing mistake in your second for loop condition. so ill just fix your code for you.
and also you could just input single values at a time as u go.
#include<bits/stdc++.h>
using namespace std;
int main()
{
char arr1[10][10];
cout <<"Reading Start" <<endl;
ifstream rfile("test.txt");
int i,j;
for(i=0;i<6;i++){
for(j=0;j<6;j++){
rfile >> arr1[i][j];
cout << arr1[i][j] << " ";
}
cout << endl;
}
cout <<"\nRead Done" <<endl<<endl;
rfile.close();
}
Output :

C++ Struct,Reference and Instancing the right way?

I'm creating a replay system Basically it stores user inputs as:
std::list<ReplayBuffer>
which is a:
struct ReplayBuffer
{
std::bitset<10> flags;
float frameTime;
uint64_t frameID;
};
now I've created two more structs, ReplayFile (the file information), and ReplayFileWriter(Write the file information into a file of course)
struct ReplayFile
{
ReplayFile(std::list<ReplayBuffer>* _buffers) {
time_t t = time(0);
date = ctime(&t);
_version = 1;
buffers = *_buffers;
}
uint16_t _version;
char* date;
std::list<ReplayBuffer> buffers;
};
struct ReplayFileWriter
{
std::ofstream file;
ReplayFileWriter(ReplayFile *data) {
file.open("replay.cdr", std::ofstream::out);
file << data;
file.close();
}
};
What I want to do is, type this:
ReplayFileWriter(new ReplayFile(&buffers));
so I pass to ReplayFile the adress of the buffers (list) and to ReplayFileWriter the adress a instance of ReplayFile) how I'm supposed to do this (I'm new to cpp, watched some videos into pointer, but when using as parameters is hard to find the right way to place "* and &"
What you are probably looking for is something like this:
struct ReplayBuffer { /* as in your code */ };
struct ReplayFile {
explicit ReplayFile(const std::list<ReplayBuffer>& _buffers)
: _version(1), buffers(_buffers) {
time_t t = time(nullptr);
date = ctime(&t);
}
uint16_t _version;
std::string date;
std::list<ReplayBuffer> buffers;
};
ostream& operator<<(ostream& os, const ReplayFile& data) {
/* Write contents of `data` to `os` here.
Implementation left as an exercise for the reader. */
return os;
}
void WriteReplayFile(const ReplayFile& data) {
file.open("replay.cdr", std::ofstream::out);
file << data;
file.close();
}
int main() {
std::list<ReplayBuffer> buffers = ...; // initialized somehow.
WriteReplayFile(ReplayFile(buffers));
}
The interesting part is in operator<<. That's where you get to define your file format.

how to pack multi-key map with msgpack-c

I am trying to use msgpack (in c version, not c++) to replace our own serialization method, which is in primary xml based. It is quite straight forward to pack some ordinary data. However, we have a lot of k-v based structures like
struct Table {
struct Key {
// Multi-keys
int key1;
int key2;
};
struct Attr {
// Attributes
int attr1;
bool attr2;
char[8] attr3;
};
}
How to pack multi-key table with msg_pack_map in msgpack-c? (unfortunately, our system is exception disabled, so I cannot use the c++ version "msgpack.hpp")
my code snippet:
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer pk;
msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
msgpack_packer_map(&pk, 10) // 10 pairs
for(int i= 0; i<10; ++i) {
// key
msgpack_pack_array(&pk, 2);
msgpack_pack_int(&pk, i);
msgpack_pack_int(&pk, 100+i);
// attr
msgpack_pack_array(&pk, 3);
msgpack_pack_int(&pk, 1);
msgpack_pack_true(&pk);
msgpack_pack_str(&pk, 7);
msgpack_pack_str_body(&pk, "example");
}
I assume in msgpack, we have to use msgpack_pack_array to pack struct.
Is my code right, or is there any better way to do that?
Yes, you are right. There are two subtle mistake in your code snippet.
msgpack_packer_map should be msgpack_pack_map. msgpack_pack_str_body requires a length of the string as the third argument.
I updated your code snippet, and added test codes.
See:
http://melpon.org/wandbox/permlink/RuZKLmzwStHej5TP
#include <msgpack.h>
#include <msgpack.hpp>
#include <map>
#include <iostream>
struct Key {
// Multi-keys
int key1;
int key2;
MSGPACK_DEFINE(key1, key2);
};
inline bool operator<(Key const& lhs, Key const& rhs) {
if (lhs.key1 < rhs.key1) return true;
if (lhs.key1 > rhs.key1) return false;
if (lhs.key2 < rhs.key2) return true;
return false;
}
struct Attr {
// Attributes
int attr1;
bool attr2;
std::string attr3;
MSGPACK_DEFINE(attr1, attr2, attr3);
};
int main() {
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer pk;
msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
msgpack_pack_map(&pk, 10); // 10 pairs
for(int i= 0; i<10; ++i) {
// key
msgpack_pack_array(&pk, 2);
msgpack_pack_int(&pk, i);
msgpack_pack_int(&pk, 100+i);
// attr
msgpack_pack_array(&pk, 3);
msgpack_pack_int(&pk, 1);
msgpack_pack_true(&pk);
msgpack_pack_str(&pk, 7);
msgpack_pack_str_body(&pk, "example", 7);
}
{
auto upd = msgpack::unpack(sbuf.data, sbuf.size);
std::cout << upd.get() << std::endl;
auto tbl = upd.get().as<std::map<Key, Attr>>();
}
msgpack_sbuffer_destroy(&sbuf);
}
That is a c++ code but I use C API in the packing part. You can check the unpacked msgpack object. It is also converted as C++ map successfully.
Here is C API documentation:
https://github.com/msgpack/msgpack-c/wiki/v1_1_c_overview

C++ abstraction of libpng causes Crash in malloc -- While deleting a "Jagged" 2D Array

The class corresponding to this crash is:
#ifndef IMAGE_DATA_
#define IMAGE_DATA_
#include <stdexcept>
template <typename data_type>
class ImageData
{
public:
ImageData(unsigned long width, unsigned long height);
~ImageData();
data_type **&get_data();
unsigned long int get_width() const
{
return _m_Width;
}
unsigned long int get_height() const
{
return _m_Height;
}
protected:
ImageData(ImageData &copy);
ImageData& operator= (ImageData &copy);
private:
data_type **_m_rData;
unsigned long _m_Width;
unsigned long _m_Height;
};
template <typename data_type>
ImageData<data_type>::ImageData(unsigned long width, unsigned long height) :
_m_rData(NULL),
_m_Width(width),
_m_Height(height)
{
if (width == 0 || height == 0)
throw std::runtime_error("Invalid width or height");
try {
_m_rData = new data_type*[_m_Height]();
for (unsigned long int i = 0; i < _m_Height; ++i) {
_m_rData[i] = NULL;
}
for (unsigned long int i = 0; i < _m_Height; ++i) {
_m_rData[i] = new data_type[_m_Width];
}
}
catch (std::bad_alloc e) {
throw std::runtime_error("Failure to create space for Image");
}
}
template <typename data_type>
ImageData<data_type>::~ImageData()
{
for (unsigned long i = 0; i < _m_Height; ++i) {
delete [] _m_rData[i];
_m_rData[i] = NULL;
}
delete [] _m_rData;
_m_rData = NULL;
}
template <typename data_type>
data_type **&ImageData<data_type>::get_data()
{
return _m_rData;
}
#endif
And it is used in the following manner:
PNGFileReader::PNGFileReader(const std::string &path) :
_m_Image(NULL),
_m_pPNG(NULL),
_m_pPNGInfo(NULL)
{
...
/*
* Read Image in all at once into users data
*/
_m_Image = new ImageData<unsigned char>(width, height);
png_read_image(_m_pPNG, _m_Image->get_data());
png_read_end(_m_pPNG, NULL);
fclose(_m_CFilePointer);
_m_CFilePointer = NULL;
}
PNGFileReader::~PNGFileReader()
{
if (_m_CFilePointer) {
fclose(_m_CFilePointer);
}
png_destroy_read_struct(&_m_pPNG, &_m_pPNGInfo, NULL);
delete _m_Image;
}
When stepping through with the debugger the _m_rData in the ImageData class is the same pointer as when I used new on it. I have even tried to wrap the delete statement inside ImageData destructor with if == NULL statments. However, I still get a sigabrt while running my code. The stack trace from gdb is:
0 __GI_raise raise.c 64 0x3512a36285
1 __GI_abort abort.c 91 0x3512a37b9b
2 __libc_message libc_fatal.c 198 0x3512a77a7e
3 malloc_printerr malloc.c 5021 0x3512a7dda6
4 _int_free malloc.c 3942 0x3512a7f08e
5 ImageData<unsigned char>::~ImageData imagedata.h 57 0x40236d
6 PNGFileReader::~PNGFileReader pngfilereader.cpp 59 0x401ed3
7 main main.cpp 8 0x40246a
UPDATE
For anyone that is curios the following now works. Apparently it is an issue with how png_alligns its data. This forces you I guess to use libpng's method calls which internally use free and malloc, not new. This is essentially the same things as calling free(data) where data was created with data = new type[N]. The code below depicts how to correctly use libpng.
#ifndef PNG_FILE_READER_H_
#define PNG_FILE_READER_H_
#include "imagedata.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#include <iostream>
#include <vector>
#include <string>
template <typename data_type>
class ImageData;
class PNGFileReader
{
public:
// Ctor and Dtor
PNGFileReader(const std::string &path);
~PNGFileReader();
// For testing purposes
friend std::ostream &operator<< (std::ostream &out,
PNGFileReader *object)
{
for (unsigned long i = 0; i < object->get_image_height(); ++i) {
for (unsigned long j = 0; j < object->get_image_width(); ++j) {
png_byte c = object->_m_ImageData[i][j];
out << c;
}
}
return out;
}
// Getters
long unsigned int get_image_width() const;
long unsigned int get_image_height() const;
private:
// Helper functions:
bool _create_png_structs();
// Member variables:
FILE *_m_CFilePointer;
unsigned long int _m_ImageWidth;
unsigned long int _m_ImageHeight;
png_bytepp _m_ImageData;
png_structp _m_pPNG;
png_infop _m_pPNGInfo;
// Enums
enum PNGBOOL {NOT_PNG, PNG};
enum PNGERRORS {ERROR, SUCCESS};
};
#endif /* PNG_FILE_READER_H_ */
#include "pngfilereader.h"
#include "filereader.h"
#include <stdexcept>
PNGFileReader::PNGFileReader(const std::string &path) :
_m_ImageData(NULL),
_m_pPNG(NULL),
_m_pPNGInfo(NULL)
{
/*
* Check if first 8 bytes are the correct PNG header
*/
enum {BYTES_TO_READ = 8};
unsigned char sig[BYTES_TO_READ];
FileReader(path, sig, BYTES_TO_READ);
bool not_png = png_sig_cmp(sig, 0, BYTES_TO_READ);
if (not_png) {
throw std::runtime_error("Your file is not of PNG format");
}
/*
* Create the png structs using a FILE *. libpng requires
* this type and will not take a C++ stream
*/
_m_CFilePointer = fopen(path.c_str(), "rb");
if (!_m_CFilePointer) {
throw std::runtime_error("Failure to open PNG file");
}
if (!_create_png_structs()) {
throw std::runtime_error("Failure to create PNG structs");
}
/*
* Initialize PNG io and read data into PNG structs
*/
png_init_io(_m_pPNG, _m_CFilePointer);
png_read_info(_m_pPNG, _m_pPNGInfo);
_m_ImageHeight = png_get_image_height(_m_pPNG, _m_pPNGInfo);
_m_ImageWidth = png_get_rowbytes(_m_pPNG, _m_pPNGInfo);
/*
* Create sufficient PNG Space and Read Image in all at
* once into users data. Note that you have to use png's
* types to prevent sigabrt (6) while freeing memory.
*/
_m_ImageData = (png_bytepp)png_malloc(_m_pPNG,
sizeof(png_bytep)*_m_ImageHeight);
if (_m_ImageData == NULL) {
throw std::runtime_error("Memory allocation failure");
}
for (unsigned long int i = 0; i < _m_ImageHeight; ++i) {
_m_ImageData[i] = NULL;
}
for (unsigned long int i = 0; i < _m_ImageHeight; ++i) {
_m_ImageData[i] = (png_bytep)png_malloc(_m_pPNG,
sizeof(png_byte)*_m_ImageWidth);
if (_m_ImageData[i] == NULL) {
throw std::runtime_error("Memory allocation failure.");
}
}
png_read_image(_m_pPNG, _m_ImageData);
png_read_end(_m_pPNG, NULL);
fclose(_m_CFilePointer);
_m_CFilePointer = NULL;
}
PNGFileReader::~PNGFileReader()
{
if (_m_CFilePointer) {
fclose(_m_CFilePointer);
}
/*
* Free all resources (-1)
*/
png_free_data(_m_pPNG, _m_pPNGInfo, PNG_FREE_ALL, -1);
for (unsigned long int i = 0; i < _m_ImageHeight; ++i) {
png_free(_m_pPNG, _m_ImageData[i]);
}
free(_m_ImageData);
png_destroy_read_struct(&_m_pPNG, &_m_pPNGInfo, NULL);
}
// Getters
long unsigned int PNGFileReader::get_image_width() const
{
return _m_ImageWidth;
}
long unsigned int PNGFileReader::get_image_height() const
{
return _m_ImageHeight;
}
// Private helper functions
bool PNGFileReader::_create_png_structs()
{
/*
* Create the pointer to main libpng struct, as well as
* two info structs to maintain information after, and
* prior to all operations on png m_Data. Only necessary
* to release resource after function succeeds.
*/
_m_pPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
NULL, NULL);
if (!_m_pPNG){
return PNGFileReader::ERROR;
}
_m_pPNGInfo = png_create_info_struct(_m_pPNG);
if (!_m_pPNGInfo) {
return PNGFileReader::ERROR;
}
return PNGFileReader::SUCCESS;
}
If you need a really 2D array to pass to a library, but want to have the flexibility of a jagged array, what you do is
Allocate the first level pointer block as usual
Instead of allocating m separate rows of n cells (one for each pointer in the first level block) you allocate a single set of n*m cells and then set the first level pointers to point at every nth location. This way the main allocation is sized and laid out in memory just as a 2D array, but you can still use the two-pointer-dereference [][] syntax to get to the cells.
Pass the start of the second level allocation to the library.
This works because there are strict requirements on who multidimensional arrays are laid out in memory (i.e. the must be contiguous at every level of interpretation).

Adding a string or char array to a byte vector

I'm currently working on a class to create and read out packets send through the network, so far I have it working with 16bit and 8bit integers (Well unsigned but still).
Now the problem is I've tried numerous ways of copying it over but somehow the _buffer got mangled, it segfaulted, or the result was wrong.
I'd appreciate if someone could show me a working example.
My current code can be seen below.
Thanks, Xeross
Main
#include <iostream>
#include <stdio.h>
#include "Packet.h"
using namespace std;
int main(int argc, char** argv)
{
cout << "#################################" << endl;
cout << "# Internal Use Only #" << endl;
cout << "# Codename PACKETSTORM #" << endl;
cout << "#################################" << endl;
cout << endl;
Packet packet = Packet();
packet.SetOpcode(0x1f4d);
cout << "Current opcode is: " << packet.GetOpcode() << endl << endl;
packet.add(uint8_t(5))
.add(uint16_t(4000))
.add(uint8_t(5));
for(uint8_t i=0; i<10;i++)
printf("Byte %u = %x\n", i, packet._buffer[i]);
printf("\nReading them out: \n1 = %u\n2 = %u\n3 = %u\n4 = %s",
packet.readUint8(),
packet.readUint16(),
packet.readUint8());
return 0;
}
Packet.h
#ifndef _PACKET_H_
#define _PACKET_H_
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
using namespace std;
class Packet
{
public:
Packet() : m_opcode(0), _buffer(0), _wpos(0), _rpos(0) {}
Packet(uint16_t opcode) : m_opcode(opcode), _buffer(0), _wpos(0), _rpos(0) {}
uint16_t GetOpcode() { return m_opcode; }
void SetOpcode(uint16_t opcode) { m_opcode = opcode; }
Packet& add(uint8_t value)
{
if(_buffer.size() < _wpos + 1)
_buffer.resize(_wpos + 1);
memcpy(&_buffer[_wpos], &value, 1);
_wpos += 1;
return *this;
}
Packet& add(uint16_t value)
{
if(_buffer.size() < _wpos + 2)
_buffer.resize(_wpos + 2);
memcpy(&_buffer[_wpos], &value, 2);
_wpos += 2;
return *this;
}
uint8_t readUint8()
{
uint8_t result = _buffer[_rpos];
_rpos += sizeof(uint8_t);
return result;
}
uint16_t readUint16()
{
uint16_t result;
memcpy(&result, &_buffer[_rpos], sizeof(uint16_t));
_rpos += sizeof(uint16_t);
return result;
}
uint16_t m_opcode;
std::vector<uint8_t> _buffer;
protected:
size_t _wpos; // Write position
size_t _rpos; // Read position
};
#endif // _PACKET_H_
Since you're using an std::vector for your buffer, you may as well let it keep track of the write position itself and avoid having to keep manually resizing it. You can also avoid writing multiple overloads of the add function by using a function template:
template <class T>
Packet& add(T value) {
std::copy((uint8_t*) &value, ((uint8_t*) &value) + sizeof(T), std::back_inserter(_buffer));
return *this;
}
Now you can write any POD type to your buffer.
implicitly:
int i = 5;
o.write(i);
or explictly:
o.write<int>(5);
To read from the buffer, you will need to keep track of a read position:
template <class T>
T read() {
T result;
uint8_t *p = &_buffer[_rpos];
std::copy(p, p + sizeof(T), (uint8_t*) &result);
_rpos += sizeof(T);
return result;
}
You will need to explicitly pass a type parameter to read. i.e.
int i = o.read<int>();
Caveat: I have used this pattern often, but since I am typing this off the top of my head, there may be a few errors in the code.
Edit: I just noticed that you want to be able to add strings or other non-POD types to your buffer. You can do that via template specialization:
template <>
Packet& add(std::string s) {
add(string.length());
for (size_t i = 0; i < string.length(); ++i)
add(string[i]);
return *this;
}
This tells the compiler: if add is called with a string type, use this function instead of the generic add() function.
and to read a string:
template <>
std::string read<>() {
size_t len = read<size_t>();
std::string s;
while (len--)
s += read<char>();
return s;
}
You could use std::string as internal buffer and use append() when adding new elements.
Thus adding strings or const char* would be trivial.
Adding/writing uint8 can be done with casting it to char, writing uint16 - to char* with length sizeof(uint16_t).
void write_uint16( uint16_t val )
{
m_strBuffer.append( (char*)(&var), sizeof(val) );
}
Reading uint16:
uint16_t read_int16()
{
return ( *(uint16_t*)(m_strBuffer.c_str() + m_nOffset) );
}
You appear to be attempting to print ten bytes out of the buffer when you've only added four, and thus you're running off the end of the vector. This could be causing your seg fault.
Also your printf is trying to print a character as an unsigned int with %x. You need to use static_cast<unsigned>(packet._buffer[i]) as the parameter.
Stylistically:
Packet packet = Packet(); could potentially result in two objects being constructed. Just use Packet packet;
Generally try to avoid protected attributes (protected methods are fine) as they reduce encapsulation of your class.