Dictionary flush in LZW algorithm - c++

I have compressor/decompressor for LZW.
Now i want to use custom variable(size) to keep dictionary size in bytes and then it becomes large enough i revert dictionary to initial state and start fill it again.
But i dont realize how to implement it. I had several tries.
First time i flushed dict then it had maxSize elements, however it may become to large.
Next time i increased size by size of new element in dict, but program
took several Gb from memory and broke everything at the end.
Coder
void lzwCodeFile(istream &in, ostream &out, uint32_t maxSize) {
unordered_map<string, uint32_t> mp;
clearAndFill(mp);
uint32_t code = 256;
uint32_t size = 256;
string w = "";
string tmp;
uint8_t c;
while (in.read((char *) &c, sizeof c)) {
tmp = w;
tmp += c;
if (mp.find(tmp) != mp.end()) {
w = tmp;
} else {
uint32_t val = mp[w];
out.write((const char *) &val, sizeof(mp[w]));
//cout << val << " ";
mp[tmp] = code++;
size += tmp.size();
w = c;
}
}
if (w.size()) {
uint32_t val = mp[w];
out.write((const char *) &val, sizeof(mp[w]));
//cout << val << " ";
}
}
Decoder
void lzwDecodeFile(istream &in, ostream &out, uint32_t maxSize) {
unordered_map<uint32_t, string> mp;
uint32_t code = 256;
uint32_t size = 256;
clearAndFillRev(mp);
string tmp, w;
uint32_t k;
in.read((char *) &k, sizeof k);
w = "" + mp[k];
string entry;
out << w;
while (in.read((char *) &k, sizeof k)) {
// finded
if (mp.find(k) != mp.end()) {
entry = mp[k];
} else {
entry = w + w[0];
}
out << entry;
tmp = w + entry[0];
mp[code++]=tmp;
w = entry;
}
}

Related

2 functions that should be the same give different results

I'm reading a file into a vector, to read bytes off of it. The standalone function works fine, but I don't want to open and close a file and create a vector every time i access, since im going to be grabbing lots of bytes. So I opted to put it in a file class. However, this has lead to me getting entirely different results when I use the file class.
This is a working standalone function:
template<class T>
T readFromNewFile(std::string path, size_t size = 0, uint offset = 0)
{
std::ifstream in(path.c_str(), std::ios::binary | std::ios::ate);
if (in.is_open())
{
std::streamoff max = in.tellg();
size_t _size = size == 0 ? (size_t)max : size;
in.seekg(offset, std::ios::beg);
std::vector<uint> v(_size);
in.read((char*)v.data(), _size);
uint temp = 0;
std::stringstream ss;
for (uint it = 0; it < v.size(); it++)
{
std::cout << "readFromNewFile: Adding offset: " << offset + it << ", with value: " << v[it] << "\n";
temp += v[it];
}
ss << temp;
T myVar;
ss >> myVar;
return myVar;
}
return T();
}
The file class in question:
class File
{
public:
File() {}
File(std::string path)
{
LOAD(path);
}
~File() {}
void LOAD(std::string path)
{
std::ifstream in(path.c_str(), std::ios::binary | std::ios::ate);
if (in.is_open())
{
std::streamoff max = in.tellg();
size_t _size = (size_t)max;
in.seekg(0, std::ios::beg);
data.clear();
data.resize(_size);
in.read((char*)data.data(), _size);
}
}
template<class T>
T readFromVector(size_t size = 0, uint offset = 0)
{
uint temp = 0;
std::stringstream ss;
for (uint it = offset; it < (offset + size) && it < data.size(); it++)
{
std::cout << "readFromVector: reading offset: " << it << ", with value: " << data[it] << "\n";
temp += data[it];
}
ss << temp;
T myVar;
ss >> myVar;
return myVar;
}
private:
std::vector<uint> data;
};
The code is all called in this block:
bool ArmourManager::init()
{
std::string armourFile = "resources\\armor.am_dat";
Offsets::ArmourOffsets offsets;
data.LOAD(armourFile); // data is a File object in ArmourManager class
uint armourOffset = 0x0A;
uint textOffset = 0x41C06;
// armours start at offset 0x0A, increase by 0x3C for every armour in file
// appears to end at 187390 ? 0x2DBFE‬
//std::cout << "[OFFSET]\t[IDX]\t[RAR]\t[SLOT]\t[DEF]\t[SLOTS]\n";
while (armourOffset < 0x2DBFE)
{
uint ind = SetMaker::readFromNewFile<uint>(armourFile, 4, armourOffset + offsets.Index);
uint x = data.readFromVector<uint>(4, armourOffset + offsets.Index);
std::cout << ind << " / " << x << "\n";
Console Output Here
From what i can tell with debugging, the data vector isn't being populated correctly, it's being filled with weird data.
Checking first index of both vectors
Even when I tried something like this,
std::vector<uint> x(_size);
in.read((char*)x.data(), _size);
std::cout << "x byte 0: " << x[0] << "\n";
the result is still the exact same.

How should I parse and check through a string?

Okay so I have a C++ project I want to complete which involves receiving NMEA sentences and parsing through them to check if they conform to the "grammar rules" of NMEA.
The current method I have been doing this is using if statements but this is obviously not good coding practice.
What are other methods I could use to attempt to check string sentences for specific characters and groups of characters?
Let us assume that you have NMEA data like this
$GPGGA,124613.90,5543.3221231,N,03739.1368442,E,1,15,0.69,147.0851,M,14.4298,M,,*54
$GPGSV,3,1,10,27,12,078,41,05,31,308,49,16,25,043,44,02,11,268,44*7E
$GPGSV,3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48*71
$GPGSV,3,3,10,23,30,116,46,04,37,114,47*79
$GLGSV,2,1,07,84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53*67
$GLGSV,2,2,07,77,67,195,47,76,50,047,54,66,32,144,52*5C
$GPGGA,124614.00,5543.3221239,N,03739.1368445,E,1,15,0.69,147.0864,M,14.4298,M,,*53
$GPGSV,3,1,10,27,12,078,41,05,31,308,49,16,25,043,43,02,11,268,44*79
$GPGSV,3,2,10,26,03,031,39,07,74,216,52,09,58,121,52,30,39,234,48*71
$GPGSV,3,3,10,23,30,116,46,04,37,114,47*79
$GLGSV,2,1,07,84,17,338,43,78,15,212,48,85,12,032,46,67,84,223,53*67
$GLGSV,2,2,07,77,67,195,47,76,50,047,54,66,32,144,52*5C
And if we want to extract GGA and GSV data, you may want to use the following code:
#include <string>
#include <ctime>
#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>
constexpr size_t NumberOfFixQualityStrings = 9;
constexpr size_t NumberOfSatellitesPerGSVSentencePart = 4;
constexpr size_t MaxNumberOfPartsInSentence = 10;
constexpr size_t MaxTokensInSentence = 64;
constexpr size_t NumberOfFieldsInGGA = 12;
std::string fixQualityString[NumberOfFixQualityStrings]{
"invalid", "GPS fix (SPS)", "DGPS fix", "PPS fix", "Real Time Kinematic", "Float RTK",
"estimated (dead reckoning", "Manual input mode", "Simulation mode" };
// essential fix data which provide 3D location and accuracy data
struct GGA {
// Time of last satellite fix
unsigned int fixTimeInUtcHours{};
unsigned int fixTimeInUtcMinutes{};
unsigned int fixTimeInUtcSeconds{};
unsigned int fixTimeInUtcMilliSeconds{};
// Position: Lattitude
unsigned int lattitudeInDegree{};
double lattitudeInMinutes{};
std::string lattitideDirection{};
// Position: Longitude
unsigned int longitudeInDegree{};
double longitudeInMinutes{};
std::string longitudeDirection{};
// FixQuality // see dteails as string above
unsigned int fixQuality{};
std::string fixQualityString{};
// Number of satellites being tracked (can be more than shown in GSV, not all are beeing used for calculation)
unsigned int numberOfTrackedSatellites{};
// Horizontal dilution of position
double horizontalDilution{};
// Altitude, Meters, above mean sea level
double altitude{};
std::string altitudeDimension{};
// Height of geoid (mean sea level) above WGS84 ellipsoid
double goidHight{};
std::string goidHightDimension{};
};
// Detail information for satellites in satellit view (GSV)
struct SatelliteData {
std::string satellitePRNnumber{};
double elevationInDegress{};
double azimuthInDegrees{};
double snr{}; // signal noise ratio
};
// Part of a GSV sentence
struct GSVSentencePart {
size_t numberOfSentencesForFullData{};
size_t sentencePartNumber{};
size_t numberOfSatellitesInView{};
size_t numberOfSatellitesInThisPart{};
SatelliteData satelliteData[NumberOfSatellitesPerGSVSentencePart];
};
struct GSV
{
GSVSentencePart gsvSentencePart[MaxNumberOfPartsInSentence];
size_t numberOfParts{};
};
bool checksumTest(std::string& line) {
bool result{ false };
// Check, if there is a 2 digt checksum at the end and convert it to decimal
if (size_t pos{}, checkSumGiven{ std::stoul(line.substr(line.size() - 2), &pos, 16) }; pos == 2)
{
// Strip off checksum part
line = line.substr(1,line.size() - 4);
// Calculate checksum
unsigned char calculatedChecksum{ 0U }; for (const unsigned char c : line) calculatedChecksum ^= c;
// Get result
result = (calculatedChecksum == checkSumGiven);
}
return result;
}
// Split all strings into a tokens
size_t splitIntoTokens(std::string& s, std::string (&tokens)[MaxTokensInSentence]) {
// Number of converted tokens
size_t numberOfTokens{ 0 };
// First check checksum
if (checksumTest(s)) {
// Now split along each comma
for (size_t i{ 0U }, startpos{ 0U }; i < s.size(); ++i) {
// So, if there is a comma or the end of the string
if ((s[i] == ',') || (i == (s.size() - 1))) {
// Copy substring
tokens[numberOfTokens++] = s.substr(startpos, i - startpos);
startpos = i + 1;
}
}
}
return numberOfTokens;
}
GGA convertStringToGGA(std::string& s) {
GGA gga;
// Split string into tokens and check, if it worked
if (std::string tokens[MaxTokensInSentence]; splitIntoTokens(s, tokens) > NumberOfFieldsInGGA && tokens[0] == "GPGGA") {
gga.fixTimeInUtcHours = std::stoul(tokens[1].substr(0, 2));
gga.fixTimeInUtcMinutes = std::stoul(tokens[1].substr(2, 2));
gga.fixTimeInUtcSeconds = std::stoul(tokens[1].substr(4, 2));
gga.fixTimeInUtcMilliSeconds = std::stod(tokens[1].substr(6, 2))*1000.0;
gga.lattitudeInDegree = std::stoul(tokens[2].substr(0, 2));
gga.lattitudeInMinutes = std::stod(tokens[2].substr(2));
gga.lattitideDirection = tokens[3];
gga.longitudeInDegree = std::stoul(tokens[4].substr(0, 2));
gga.longitudeInMinutes = std::stod(tokens[4].substr(2));
gga.longitudeDirection = tokens[5];
gga.fixQuality = std::stoul(tokens[6]);
gga.fixQualityString = (gga.fixQuality < NumberOfFixQualityStrings) ? fixQualityString[gga.fixQuality] : fixQualityString[0];
gga.numberOfTrackedSatellites = std::stoul(tokens[7]);
gga.horizontalDilution = std::stod(tokens[8]);
gga.altitude = std::stod(tokens[9]);
gga.altitudeDimension = tokens[10];
gga.goidHight = std::stod(tokens[11]);
gga.goidHightDimension = tokens[12];
}
return gga;
}
GSVSentencePart convertToGSVSentencePart(std::string& s) {
GSVSentencePart gsvsp;
// Split string into tokens and check, if it worked
std::string tokens[MaxTokensInSentence];
if (size_t numberOfCOnvertedTokens = splitIntoTokens(s, tokens); numberOfCOnvertedTokens > 0 && tokens[0] == "GPGSV") {
gsvsp.numberOfSentencesForFullData = std::stoul(tokens[1]);
gsvsp.sentencePartNumber = std::stoul(tokens[2]);
gsvsp.numberOfSatellitesInView = std::stoul(tokens[3]);
gsvsp.numberOfSatellitesInThisPart = 0;
for (size_t currentToken = 4; currentToken < numberOfCOnvertedTokens; currentToken += 4) {
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].satellitePRNnumber = tokens[currentToken];
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].elevationInDegress = stod(tokens[currentToken + 1]);
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].azimuthInDegrees= stod(tokens[currentToken + 2]);
gsvsp.satelliteData[gsvsp.numberOfSatellitesInThisPart].snr = stod(tokens[currentToken + 3]);
++gsvsp.numberOfSatellitesInThisPart;
}
}
return gsvsp;
}
std::string calculateElapsedTime(const GGA& previousGGA, const GGA& nextGGA) {
std::tm tmPrevious{}, tmNext{};
tmPrevious.tm_year = 100; tmPrevious.tm_mon = 1; tmPrevious.tm_mday = 1;
tmNext.tm_year = 100; tmNext.tm_mon = 1; tmNext.tm_mday = 1;
tmPrevious.tm_hour = previousGGA.fixTimeInUtcHours;
tmPrevious.tm_min = previousGGA.fixTimeInUtcMinutes;
tmPrevious.tm_sec = previousGGA.fixTimeInUtcSeconds;
std::time_t previousTime = std::mktime(&tmPrevious);
tmNext.tm_hour = nextGGA.fixTimeInUtcHours;
tmNext.tm_min = nextGGA.fixTimeInUtcMinutes;
tmNext.tm_sec = nextGGA.fixTimeInUtcSeconds;
std::time_t nextTime = std::mktime(&tmNext);
double diff = std::difftime(nextTime, previousTime);
diff = diff + 1.0*nextGGA.fixTimeInUtcMilliSeconds/1000.0- 1.0*previousGGA.fixTimeInUtcMilliSeconds/1000.0;
return std::to_string(diff);
}
int main() {
// Open file and check, if it is open
if (std::ifstream nmeaFile("r:\\log.txt"); nmeaFile) {
GGA previousGGA;
GGA nextGGA;
GSV gsv;
size_t state{ 0 };
for (std::string line{}; std::getline(nmeaFile, line); ) {
switch ( state) {
case 0: // wait for first GGA data
if (line.substr(0, 6) == "$GPGGA") {
previousGGA = nextGGA;
nextGGA = convertStringToGGA(line);
state = 1;
gsv = {};
}
break;
case 1: // wait for GSV
if (line.substr(0, 6) == "$GPGSV") {
gsv.gsvSentencePart[gsv.numberOfParts] = convertToGSVSentencePart(line);
if (gsv.gsvSentencePart[gsv.numberOfParts].numberOfSentencesForFullData ==
gsv.gsvSentencePart[gsv.numberOfParts].sentencePartNumber) {
state = 0;
++gsv.numberOfParts;
// Now all data are available in reable and structed format.
// You can do, what you want with them
// For example, we can print all Satellite Data:
size_t counter{ 0 };
for (size_t i = 0; i < gsv.numberOfParts; ++i) {
for (size_t j = 0; j < gsv.gsvSentencePart[i].numberOfSatellitesInThisPart; j++) {
std::cout << "Satellite: " << std::setw(2) << ++counter << " Satellite name: " <<
std::setw(3) << gsv.gsvSentencePart[i].satelliteData[j].satellitePRNnumber <<
" SNR: " << std::setw(8) << gsv.gsvSentencePart[i].satelliteData[j].snr <<
" Elapsed time: "<< calculateElapsedTime(previousGGA, nextGGA)<< " s\n";
}
}
--gsv.numberOfParts;
}
++gsv.numberOfParts;
}
break;
}
}
}
return 0;
}
Coding style is "beginner"-level for easier understanding.
Modern C++ approach would be totally different, but not so easy to understand.

C++ class objects copy constructor and operator=

I'm currently building a library In C++. I have met this problem few days ago and I'm unable to fix it. I have shorten the code so it can be seen easier.
Below is my code:
class String
{
private:
mutable char* v;
mutable int l = 0;
public:
String()
{
l++;
v = new char[1];
*v = '\0';
}
String(const char* value)
{
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
String(const String& value)
{
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
int len() const
{
return l - 1;
}
char* val() const
{
return v;
}
char* operator=(const char* value) const
{
delete[] v;
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char* operator=(const String& value) const
{
delete[] v;
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char operator[](const int& index) const
{
return v[index];
}
};
class StringArray
{
private:
union ArrayDef
{
public:
mutable String stringV;
mutable int intV;
ArrayDef()
{
}
ArrayDef(const String& value)
: stringV(value)
{
}
ArrayDef(const int& value)
: intV(value)
{
}
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
String operator=(const String& value) const
{
stringV = value;
return stringV;
}
int operator=(const int& value) const
{
intV = value;
return intV;
}
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
};
mutable ArrayDef* arrdef;
mutable int arrLen = 0;
public:
StringArray()
{
}
void add(const ArrayDef& value) const
{
ArrayDef temp[arrLen + 1];
for (int i = 0; i < arrLen; i++)
temp[i] = arrdef[i];
temp[arrLen] = value;
arrLen++;
delete[] arrdef;
arrdef = new ArrayDef[arrLen];
for (int i = 0; i < arrLen; i++)
arrdef[i] = temp[i];
}
int len() const
{
return arrLen;
}
ArrayDef val(const int& index) const
{
return arrdef[index];
}
};
And my driver code:
#include <iostream>
int main()
{
StringArray arr;
arr.add(String("Hello"));
arr.add(String("World"));
std::cout << "Length of the array: " << arr.len() << std::endl;
int indexOfString = 1;
int indexOfCharacter = 2;
char s = arr.val(indexOfString).stringV[indexOfCharacter];
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have created two class, that is, String and StringArray class.
For String class, I need to always add a null character after the char pointer array for safety issue.
For StringArray class, I uses union because it's actually an array for multiple types.
It can be successfully compiled but it output some random character and it is different every time I run it.
Any answers will be appreciated, and please tell me why and how it don't works. Thank you.
From,
HaiQin.
This code is just a collection of antipatters that makes it difficult to study. What is the reason of making the internal data mutable? Why do you need to play with length and l where sometimes it is the length of the string, sometimes it is the size of array? The operator operator= returns char* which is a bad practice. Using const int& index as a parameter is a strange choice. You allocate arrays multiple times but you have no destructor that frees the memory.
Here your assignment operator returns a value, not reference!
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
Next comes even more dangerous practice:
// Recollect that this is a union
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
You are assigning both fields of the union at the same time! Did you mean struct?
Try to fix that. Start with changing union to structure.
One of the things that certainly will not work is the ArrayDef copy constructor and operator=(const ArrayDef & value). This is because you may only use the active value in the union, not both at the same time. This is usually solved by using a tagged union. Is there a reason you cannot use the Standard Template Library?
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> arr;
arr.push_back(std::string("Hello"));
arr.push_back(std::string("World"));
std::cout << "Length of the array: " << arr.size() << std::endl;
constexpr int indexOfString = 1; // second string - starting from 0!
constexpr int indexOfCharacter = 2; // third character
char s = arr.at(indexOfString).c_str()[indexOfCharacter]; // using interfaces closest to the original
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}

Pointer and orphan memory management

Firstly here is my code:
header:
#pragma once
#include <iostream>
using namespace std;
class CString
{
private://Main attribute
char* str;
private:// Aux attribute
int len;
public:
//constructor and destructor
CString(char* x);
CString() { len = 0; str = NULL; }
~CString()
{
if (NULL != str)
delete[] str;
str = NULL;
}
//some operator
CString operator+(CString x);
CString operator+(char* x);
void operator=(CString x);
//operator() is to extract a part of other CString object
CString operator()(unsigned pos, unsigned c_len);
//operator[] return position of CString::str[pos]
char& operator[](unsigned pos);
//Ostream output
friend ostream& operator<<(ostream& os, CString x);
};
//to do char+CString
CString operator+(char* a, CString x);
header cpp code:
#include "CString.h"
CString::CString(char * x)
{
len = 0;
while(x[len])
len++;
str = new char[len];
for (int i = 0;i < len;i++)
str[i] = x[i];
if (str[len - 1] != '\0')
{
len++;
char* tmp;
tmp = new char[len];
for (int i = 0;i < len - 1;i++)
tmp[i] = str[i];
delete[]str;
str = tmp;
tmp = NULL;
str[len - 1] = '\0';
}
}
CString CString::operator+(CString x)
{
CString* result;
result = new CString;
result->len = this->len + x.len - 1;
result.str=new char[result.len];
for (int i = 0; i < this->len - 1;i++)
{
result->str[i] = this->str[i];
}
for (int i = 0, j = this->len - 1;i < x.len;i++, j++)
{
result->str[j] = x.str[i];
}
return *result;
}
CString CString::operator+(char * x)
{
return CString(*this+CString(x));
}
void CString::operator=(CString x)
{
str = new char[x.len];
for (int i = 0; i < x.len;i++)
str[i] = x.str[i];
len = x.len;
}
CString CString::operator()(unsigned pos, unsigned c_len)
{
CString* result;
result = new CString;
result->len = c_len;
result.str=new char[c_len];
for (int i = pos;i < pos + c_len;i++)
result->str[i - pos] = str[i];
return *result;
}
char& CString::operator[](unsigned pos)
{
if (pos < len - 1)
{
char* ptr;
ptr = this->str + pos;
return *ptr;
}
else
{
int o_len = len;
len = pos + 2;
char* tmp;
tmp = new char[len];
for (int i = 0;i < o_len;i++)
{
tmp[i] = str[i];
}
tmp[len - 1] = '\0';
delete[]str;
str = tmp;
tmp = NULL;
return *(str + pos);
}
}
ostream & operator<<(ostream & os, CString x)
{
os << x.str;
return os;
}
CString operator+(char * a, CString x)
{
return CString(CString(a) + x);
}
main:
CString a("string 1"), b = "Initialize " + a;
b[15] = '2'; cout << a + " - " + b << endl;
CString c = a + b;
cout << "String extracted from string \"" << c
<< "\" from position 3 with length of 6 is: \""
<< c(3, 6) << "\"" << endl;
Problem:
When I try to compile this program, there is no error, but still operator+(CString), operator(), and destructor seem to malfunction.
When I debug this program, my program triggered a breakpoint some where at line CString a("String 1"), b = "initilize " + a; I have no idea why. I am do not know if there is any problem for the rest of the program because I always get stuck at that line. SO if somebody can find out any other problem, please tell me, that will save me another day.
Btw, I just wonder at operator+(CString) and operator(), I have create a pointer then I use new and then I return that pointer, so that I have no change to delete that pointer, will it leave me an orphan memory? Since I have read another question about return a class object, I found out that if I use CString result and then return result, result would be destroyed before return. SO is there any better way to do that?
Summary:
1.My program triggered a breakpoint some where in the second line of main().
2.How to properly return a class object?
P.S: I am really bad at communicating and just have 1 year period of C/C++ learning. So if I have type some thing could give you a cancer, please forgive me.
Sincerely thank you.

What does it mean to multiply structure members in this c++ code

I have a question concerning this code from Thinking in c++ book , this is a tiny c style library for learning the process of memory allocation , what does it mean to write
int startBytes = s->next * s->size;
in this code , what does this multiplication mean ?
//: C04:CLib.h
// Header file for a C-like library
// An array-like entity created at runtime
typedef struct CStashTag {
int size;
// Size of each space
int quantity; // Number of storage spaces
int next;
// Next empty space
// Dynamically allocated array of bytes:
unsigned char* storage;
} CStash;
void initialize(CStash* s, int size);
void cleanup(CStash* s);
int add(CStash* s, const void* element);
void* fetch(CStash* s, int index);
int count(CStash* s);
void inflate(CStash* s, int increase);
///:~
//: C04:CLib.cpp {O}
// Implementation of example C-like library
// Declare structure and functions:
#include "CLib.h"
#include <iostream>
#include <cassert>
using namespace std;
/ Quantity of elements to add
// when increasing storage:
const int increment = 100;
void initialize(CStash* s, int sz) {
s->size = sz;
s->quantity = 0;
s->storage = 0;
s->next = 0;
}
int add(CStash* s, const void* element) {
if(s->next >= s->quantity) //Enough space left?
inflate(s, increment);
// Copy element into storage,
// starting at next empty space:
int startBytes = s->next * s->size;
unsigned char* e = (unsigned char*)element;
for(int i = 0; i < s->size; i++)
s->storage[startBytes + i] = e[i];
s->next++;
return(s->next - 1); // Index number
}
void* fetch(CStash* s, int index) {
// Check index boundaries:
assert(0 <= index);
if(index >= s->next)
return 0; // To indicate the end
// Produce pointer to desired element:
return &(s->storage[index * s->size]);
}
int count(CStash* s) {
return s->next; // Elements in CStash
}
void inflate(CStash* s, int increase) {
assert(increase > 0);
int newQuantity = s->quantity + increase;
int newBytes = newQuantity * s->size;
int oldBytes = s->quantity * s->size;
unsigned char* b = new unsigned char[newBytes];
for(int i = 0; i < oldBytes; i++)
b[i] = s->storage[i]; // Copy old to new
delete [](s->storage); // Old storage
s->storage = b; // Point to new memory
s->quantity = newQuantity;
}
void cleanup(CStash* s) {
if(s->storage != 0) {
cout << "freeing storage" << endl;
delete []s->storage;
}
} ///:~
//: C04:CLibTest.cpp
//{L} CLib
// Test the C-like library
#include "CLib.h"
#include <fstream>
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
int main() {
// Define variables at the beginning
// of the block, as in C:
CStash intStash, stringStash;
int i;
char* cp;
ifstream in;
string line;
const int bufsize = 80;
// Now remember to initialize the variables:
initialize(&intStash, sizeof(int));
for(i = 0; i < 100; i++)
add(&intStash, &i);
for(i = 0; i < count(&intStash); i++)
cout << "fetch(&intStash, " << i << ") = "
<< *(int*)fetch(&intStash, i)
<< endl;
// Holds 80-character strings:
initialize(&stringStash, sizeof(char)*bufsize);
in.open("CLibTest.cpp");
assert(in);
while(getline(in, line))
add(&stringStash, line.c_str());
i = 0;
while((cp = (char*)fetch(&stringStash,i++))!=0)
cout << "fetch(&stringStash, " << i << ") = "
<< cp << endl;
cleanup(&intStash);
cleanup(&stringStash);
} ///:~
It looks like it's getting the next free location by multiplying the size of the object by the index number of the next available space. So if the next space is 10, and the object size is 10, it will start allocating at byte index 100.
It is a straight multiplication of the values of next and size, I'm going to guess it's calculating an offset somewhere. From looking at the code, size is set by the sz parameter of the initialize function.