I am coding an ADTF recording file reader in C++. I have already read the header using the structure specified here
https://support.digitalwerk.net/adtf_libraries/adtf-streaming-library/v2/DATFileFormatSpecification.pdf
typedef struct tagFileHeader {
int ui32FileId;
int ui32VersionId;
int ui32Flags;
int ui32ExtensionCount;
long long ui64ExtensionOffset;
long long ui64DataOffset;
long long ui64DataSize;
long long ui64ChunkCount;
long long ui64MaxChunkSize;
long long ui64Duration;
long long ui64FileTime;
char ui8HeaderByteOrder;
long long ui64TimeOffset;
char ui8PatchNumber;
char _reserved[54];
char strDescription[1912];
} tFileHeader; // size is 2048 Bytes
I read the heder
ifstream file("myfile.dat", std::ifstream::binary);
char buffer[2048];
file.read(buffer, 2048);
const tagFileHeader* header = reinterpret_cast<const tagFileHeader*>(buffer);
And now I need to read the chunks. This is the chunks header, extracted from the same document
typedef struct tagChunkHeader {
long long ui64TimeStamp;
int ui32RefMasterTableIndex;
int ui32OffsetToLast;
int ui32Size;
short ui16StreamId;
short ui16Flags;
long long ui64StreamIndex;
} tChunkHeader; // size is 32 Bytes
Reading the chunks
for (int c = 0; c < header->ui64ChunkCount; ++c)
{
char chunkHeaderBuffer[32];
file.read(chunkHeaderBuffer, 32);
const tChunkHeader* chunk = reinterpret_cast<const tChunkHeader*>(chunkHeaderBuffer);
//Skeep chunk data
file.seekg(chunk->ui32Size, ios_base::cur);
}
I don't know how to interpret the chunk data. Is this specified in another document that I am missing?
Thanks
For the sake of completeness:
The chunk data layout depends on the original sample data and the used serialization. So there is not one single data layout. You have to deserialize the chunk data with the correct deserialization implementation and can then interpret the deserialized data with the correct struct definition. The information about the used serialization is stored within the index extension of a stream.
As C-3PFLO has already stated, the adtf_file library does all this for you, but you need all required deserializer plugins.
Here is a example (based on upcoming ADTF File Library 0.5.0) how to access dat files and extend the reader with additional adtffileplugins. Use this read dat files which contains e.g. flexray data recorded with ADTF 2.x:
/**
* #file
* ADTF File Access example
*
* #copyright
* #verbatim
Copyright # 2017 Audi Electronics Venture GmbH. All rights reserved.
This Source Code Form is subject to the terms of the Mozilla
Public License, v. 2.0. If a copy of the MPL was not distributed
with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
#endverbatim
*/
#include <adtf_file/standard_adtf_file_reader.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <map>
// initalize ADTF File and Plugin Mechanism
static adtf_file::Objects oObjects;
static adtf_file::PluginInitializer oInitializer([]
{
adtf_file::add_standard_objects();
});
void query_file_info(adtf_file::Reader& reader)
{
using namespace adtf_file;
//setup file version
uint32_t ifhd_version = reader.getFileVersion();
std::string adtf_version("ADTF 3 and higher");
if (ifhd_version < ifhd::v400::version_id)
{
adtf_version = "below ADTF 3";
}
//begin print
std::cout << std::endl << "File Header" << std::endl;
std::cout << "------------------------------------------------------------------------------" << std::endl;
std::cout << "File version : " << reader.getFileVersion() << " - " << adtf_version << std::endl;
std::cout << "Date : " << reader.getDateTime().format("%d.%m.%y - %H:%M:%S") << std::endl;
std::cout << "Duration : " << reader.getDuration().count() << std::endl;
std::cout << "Short description : " << getShortDescription(reader.getDescription()) << std::endl;
std::cout << "Long description : " << getLongDescription(reader.getDescription()) << std::endl;
std::cout << "Chunk count : " << reader.getItemCount() << std::endl;
std::cout << "Extension count : " << reader.getExtensions().size() << std::endl;
std::cout << "Stream count : " << reader.getStreams().size() << std::endl;
std::cout << std::endl << "Streams" << std::endl;
std::cout << "------------------------------------------------------------------------------" << std::endl;
auto streams = reader.getStreams();
for (const auto& current_stream : streams)
{
auto property_stream_type = std::dynamic_pointer_cast<const PropertyStreamType>(current_stream.initial_type);
if (property_stream_type)
{
std::string stream_meta_type = property_stream_type->getMetaType();
std::cout << "Stream #" << current_stream.stream_id << " : " << current_stream.name << std::endl;
std::cout << " MetaType : " << stream_meta_type << std::endl;
property_stream_type->iterateProperties(
[&](const char* name,
const char* type,
const char* value) -> void
{
std::cout << " " << name << " - " << value << std::endl;
});
}
}
}
class StreamsInfo
{
typedef std::map<uint16_t, std::chrono::microseconds> LastTimesMap;
typedef std::map<uint16_t, std::string> StreamNameMap;
public:
StreamsInfo(adtf_file::Reader& reader)
{
auto streams = reader.getStreams();
for (auto current_stream : streams)
{
_map_stream_name[current_stream.stream_id] = current_stream.name;
UpdateType(current_stream.stream_id, current_stream.initial_type);
}
}
~StreamsInfo() = default;
std::string GetDiffToLastChunkTime(const uint16_t& stream_id, const std::chrono::microseconds& current_time)
{
return GetLastTimeStamp(_map_last_chunk_time, stream_id, current_time);
}
std::string GetDiffToLastSampleStreamTime(const uint16_t& stream_id, const std::chrono::microseconds& current_time)
{
return GetLastTimeStamp(_map_last_stream_time, stream_id, current_time);
}
std::string GetStreamName(const uint16_t& stream_id)
{
return _map_stream_name[stream_id];
}
void UpdateType(const uint16_t& stream_id, const std::shared_ptr<const adtf_file::StreamType>& type)
{
auto property_stream_type = std::dynamic_pointer_cast<const adtf_file::PropertyStreamType>(type);
if (property_stream_type)
{
_map_stream_meta_type[stream_id] = property_stream_type->getMetaType();
}
}
std::string GetLastStreamMetaType(const uint16_t& stream_id)
{
return _map_stream_meta_type[stream_id];
}
private:
std::string GetLastTimeStamp(LastTimesMap& map_last_times,
const uint16_t& stream_id,
const std::chrono::microseconds& current_time)
{
std::chrono::microseconds result(-1);
LastTimesMap::iterator it = map_last_times.find(stream_id);
if (it != map_last_times.end())
{
result = current_time - it->second;
it->second = current_time;
}
else
{
if (current_time.count() != -1)
{
map_last_times[stream_id] = current_time;
}
}
if (result.count() >= 0)
{
return a_util::strings::format("%lld", result.count());
}
else
{
return "";
}
}
LastTimesMap _map_last_chunk_time;
LastTimesMap _map_last_stream_time;
StreamNameMap _map_stream_name;
StreamNameMap _map_stream_meta_type;
};
void access_file_data(adtf_file::Reader& reader, const std::string& csv_file_path)
{
using namespace adtf_file;
//load stream information
StreamsInfo stream_info(reader);
std::cout << std::endl << "File data" << std::endl;
std::cout << "------------------------------------------------------------------------------" << std::endl;
utils5ext::File csv_file;
csv_file.open(csv_file_path, utils5ext::File::om_append | utils5ext::File::om_write);
//set the labels
csv_file.writeLine("stream;stream_name;chunk_type;stream_type;chunk_time;samplestream_time;chunk_time_delta_to_lastofstream;samplestream_time_delta_to_lastofstream");
size_t item_count = 0;
for (;; ++item_count)
{
try
{
auto item = reader.getNextItem();
std::chrono::microseconds chunk_time = item.time_stamp;
std::string chunk_type;
auto type = std::dynamic_pointer_cast<const StreamType>(item.stream_item);
auto data = std::dynamic_pointer_cast<const Sample>(item.stream_item);
auto trigger = std::dynamic_pointer_cast<const Trigger>(item.stream_item);
std::chrono::microseconds sample_time(-1);
std::string sample_time_string("");
if (type)
{
//the type change is part of the
chunk_type = "stream_type";
stream_info.UpdateType(item.stream_id,
type);
}
else if (data)
{
chunk_type = "sample";
auto sample_data = std::dynamic_pointer_cast<const DefaultSample>(data);
if (sample_data)
{
sample_time = sample_data->getTimeStamp();
sample_time_string = a_util::strings::format("%lld", sample_time.count());
}
}
else if (trigger)
{
chunk_type = "trigger";
}
csv_file.writeLine(a_util::strings::format("%d;%s;%s;%s;%lld;%s;%s;%s",
static_cast<int>(item.stream_id),
stream_info.GetStreamName(item.stream_id).c_str(),
chunk_type.c_str(),
stream_info.GetLastStreamMetaType(item.stream_id).c_str(),
chunk_time.count(),
sample_time_string.c_str(),
stream_info.GetDiffToLastChunkTime(item.stream_id, chunk_time).c_str(),
stream_info.GetDiffToLastSampleStreamTime(item.stream_id, sample_time).c_str()
));
}
catch (const exceptions::EndOfFile&)
{
break;
}
}
csv_file.close();
}
adtf_file::Reader create_reader(const a_util::filesystem::Path& adtfdat_file_path)
{
//open file -> create reader from former added settings
adtf_file::Reader reader(adtfdat_file_path,
adtf_file::getFactories<adtf_file::StreamTypeDeserializers,
adtf_file::StreamTypeDeserializer>(),
adtf_file::getFactories<adtf_file::SampleDeserializerFactories,
adtf_file::SampleDeserializerFactory>(),
std::make_shared<adtf_file::sample_factory<adtf_file::DefaultSample>>(),
std::make_shared<adtf_file::stream_type_factory<adtf_file::DefaultStreamType>>());
return reader;
}
int main(int argc, char* argv[])
{
if (argc < 3 || argv[1] == NULL || argv[2] == NULL)
{
std::cerr << "usage: " << argv[0] << " <adtfdat> <csv> [<adtffileplugin> ...]" << std::endl;
return -1;
}
//set path for adtfdat|dat and csv file
a_util::filesystem::Path adtfdat_file = argv[1];
a_util::filesystem::Path csv_file = argv[2];
try
{
//verify adtf|dat file
if (("adtfdat" != adtfdat_file.getExtension())
&& ("dat" != adtfdat_file.getExtension()))
{
throw std::runtime_error(adtfdat_file + " is not valid, please use .adtfdat (ADTF 3.x) or .dat (ADTF 2.x).");
}
//verify csv file
if ("csv" != csv_file.getExtension())
{
throw std::runtime_error(csv_file + " is not valid, please use .csv for sample data export.");
}
//check for additional adtffileplugins
for (int i = 3; i < argc; i++)
{
a_util::filesystem::Path adtffileplugin = argv[i];
if ("adtffileplugin" == adtffileplugin.getExtension())
{
adtf_file::loadPlugin(adtffileplugin);
}
}
//setup reader
auto reader = create_reader(adtfdat_file);
//print information about adtfdat|dat file
query_file_info(reader);
//export sample data
access_file_data(reader, csv_file);
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -2;
}
return 0;
}
Is there any reason why you try to reimplement an ADTF DAT File Reader ? It will be provided by the ADTF Streaming Library and should provide to access any data stored in a dat file. See the File Access Example (https://support.digitalwerk.net/adtf_libraries/adtf-streaming-library/v2/api/page_fileaccess.html) how to use the reader as well as the API itself and all other examples.
Hint: You can also use the successor - ADTF File Library with the same possibilities but with two more benefits: Complete Open Source to see how the (adtf)dat file handling works and also support for files created with ADTF 3.x. See https://support.digitalwerk.net/adtf_libraries/adtf-file-library/v0/html/index.html
For those interested in downloading the streaming Library, here follows the link
https://support.digitalwerk.net/projects/download-center/repository/show/adtf-libraries/adtf-streaming-library/release-2.9.0
Related
Consider the following sensor.proto file that makes use of the repeated field to initialize multiple messages.
syntax = "proto3";
package HUBSensors;
message Device {
string name = 1;
int32 id = 2;
message Sensor {
string name = 1;
double temperature = 2;
int32 humidity = 3;
enum SwitchLevel {
CLOSED = 0;
OPEN = 1;
}
SwitchLevel door = 4;
}
repeated Sensor sensors = 3;
}
Now I want to serialize some random data to a file. As an example, I will have one device with multiple sensors, hence the repeated file in the proto. I use the following code.
inline void serialize_to_file( const std::string &fileName )
{
HUBSensors::Device device;
device.set_name("HUB");
device.set_id(1234);
device.add_sensors()->set_name("Laboratory");
device.add_sensors()->set_temperature(23.3);
device.add_sensors()->set_humidity(2);
device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);
device.add_sensors()->set_name("Chml Laboratory");
device.add_sensors()->set_temperature(2.3);
device.add_sensors()->set_humidity(5);
device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);
device.add_sensors()->set_name("GU Laboratory");
device.add_sensors()->set_temperature(8.3);
device.add_sensors()->set_humidity(2);
device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);
std::ofstream ofs(fileName, std::ios_base::out | std::ios_base::binary );
device.SerializeToOstream( &ofs );
google::protobuf::ShutdownProtobufLibrary();
}
To parse the data and print out the result I use the following:
inline void parse_from_file( const std::string &fileName )
{
HUBSensors::Device device;
std::ifstream myFile;
myFile.exceptions( std::ifstream::badbit );
try {
myFile.open(fileName);
while ( myFile.good() )
{
device.ParseFromIstream( &myFile );
//std::cout << device.sensors_size() << std::endl;
std::cout << "Device Name : " << device.name() << std::endl;
std::cout << "^^^^^^" << std::endl;
for ( size_t i = 0; i < device.sensors_size(); i+=4)
{
std::cout << "Sensors Name : " << device.sensors(i).name() << std::endl;
std::cout << "Temperature : " << device.sensors(i+1).temperature() << std::endl;
std::cout << "Humidity : " << device.sensors(i+2).humidity() << std::endl;
std::cout << " Door Status : " << device.sensors(i+3).door() << std::endl;
std::cout << "^^^^^^" << std::endl;
}
}
}
catch ( const std::ifstream::failure &e )
{
std::cerr << "Error Occurred when accessing the file" << std::endl;
std::cout << e.what() << std::endl;
}
myFile.close();
google::protobuf::ShutdownProtobufLibrary();
}
A main file to reproduce the results:
#include <iostream>
#include <fstream>
#include <stdexcept>
#include "sensor.pb.h"
int main() {
GOOGLE_PROTOBUF_VERIFY_VERSION;
const std::string fileName = "./device.data";
serialize_to_file( fileName );
parse_from_file( fileName );
return 0;
}
It doesn't seem intuitive to iterate through the total sensor size and getting the correct index to display the appropriate field.
Even by checking
std::cout << device.sensors_size() << std::endl;
will output size 12 which it does not feel correct, or
std::cout << decice.sensors(2).name() << std::endl;
will not output anything since it is in the incorrect index.
What is a better way to use the libprotobuf to define a repeated field for serializing/deserializing better.
EDIT: As the answer suggests, serializing to the file should be as
inline void serialize_to_file( const std::string &fileName )
{
HUBSensors::Device device;
device.set_name("HUB");
device.set_id(1234);
auto sensor1 = device.add_sensors();
auto sensor2 = device.add_sensors();
auto sensor3 = device.add_sensors();
sensor1->set_name("Laboratory");
sensor1->set_temperature(23.3);
sensor1->set_humidity(2);
sensor1->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);
sensor2->set_name("GU Laboratory");
sensor2->set_temperature(44.3);
sensor2->set_humidity(4);
sensor2->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);
sensor3->set_name("Chml Laboratory");
sensor3->set_temperature(13.345);
sensor3->set_humidity(6);
sensor3->set_door(HUBSensors::Device_Sensor_SwitchLevel_CLOSED);
std::ofstream ofs(fileName, std::ios_base::out | std::ios_base::binary );
device.SerializeToOstream( &ofs );
google::protobuf::ShutdownProtobufLibrary();
}
Instead of
device.add_sensors()->set_name("Laboratory");
device.add_sensors()->set_temperature(23.3);
device.add_sensors()->set_humidity(2);
device.add_sensors()->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);
you should write
auto sensor = device.add_sensors();
sensor->set_name("Laboratory");
sensor->set_temperature(23.3);
sensor->set_humidity(2);
sensor->set_door(HUBSensors::Device_Sensor_SwitchLevel_OPEN);
This way you will have 3 sensors, which is your intent I suppose, and all of them will have every data member set.
I try to create a small library to listen for multiple mice on MAC and PC. (right now MAC)
I have started something simple that does not work ATM. Since I am a noob in C++ I wanted to ask the community for help in this matter. How should I design it in code? I wanted to use smart pointers here is my code, feedl free to download it:
Github:
Open Source Project
Everything in one file:
Device Class
class Device;
class Device {
public:
Device(){
std::cout << "###### Create Device - Empty" << std::endl;
this->x_previous = 0;
this->y_previous = 0;
this->x_current = 0;
this->y_current = 0;
}
Device( size_t _deviceID, std::string _device_name){
std::cout << "###### Create Device - 0.0/0.0" << std::endl;
this->device_id = std::make_shared<size_t>(_deviceID);
this->device_name = std::make_shared<std::string>(_device_name);
this->x_previous = 0;
this->y_previous = 0;
this->x_current = 0;
this->y_current = 0;
}
Device(size_t _deviceID, std::string _device_name, float _xStart, float _yStart){
std::cout << "###### Create Device - " << _xStart << "/" << _yStart << std::endl;
this->device_id = std::make_shared<size_t>(_deviceID);
this->device_name = std::make_shared<std::string>(_device_name);
this->x_previous = _xStart;
this->y_previous = _yStart;
this->x_current = _xStart;
this->y_current = _yStart;
}
~Device(){
std::cout << "###### Destroyed Device" << std::endl;
}
const size_t getId () const{
return (size_t)this->device_id.get();
};
const std::string getName() const{
return "Not Implementet yet"; //this->device_name.get() does not work because of std::basic_string wtf?
};
const float getDeltaX() const{
return x_previous - x_current;
};
const float getDeltaY() const{
return y_previous - y_current;
};
private:
std::shared_ptr<size_t> device_id;
std::shared_ptr<std::string> device_name;
float x_previous;
float y_previous;
float x_current;
float y_current;
};
Devices Class
class Devices{
public:
Devices(){
std::cout << "###### Created Empty Devices List" << std::endl;
this->list = std::unique_ptr<std::list<Device> >();
}
explicit Devices(std::unique_ptr<std::list<Device> > _list){
std::cout << "###### Created Moved Devices List" << std::endl;
this->list = std::move(_list);
}
~Devices(){
std::cout << "###### Destroyed Devices List" << std::endl;
}
std::unique_ptr<std::list<Device> > list;
void getDevicesArray() {
CFMutableDictionaryRef usb_dictionary;
io_iterator_t io_device_iterator;
kern_return_t assembler_kernel_return_value;
io_service_t device_id;
// set up a matching dictionary for the class
usb_dictionary = IOServiceMatching(kIOUSBDeviceClassName);
if (usb_dictionary == NULL) {
std::cout << "failed to fetch USB dictionary" << std::endl;
return; // still empty
}
// Now we have a dictionary, get an iterator.
assembler_kernel_return_value = IOServiceGetMatchingServices(kIOMasterPortDefault, usb_dictionary, &io_device_iterator);
if (assembler_kernel_return_value != KERN_SUCCESS) {
std::cout << "failed to get a kern_return" << std::endl;
return; // still empty
}
io_name_t device_name = "unkown device";
device_id = IOIteratorNext(io_device_iterator); // getting first device
while (device_id) {
device_id = IOIteratorNext(io_device_iterator); //set id type: io_service_t
IORegistryEntryGetName(device_id, device_name); //set name type: io_name_t
this->list.get()->push_back(Device(device_id, device_name));
}
//Done, release the iterator
IOObjectRelease(io_device_iterator);
}
void printDeviceIDs(){
for (auto const& device : *this->list.get()) {
std::cout << "#" << device.getId() << std::endl;
std::cout << "| name: " << "\t" << device.getName() << std::endl;
std::cout << "#-----------------------------------------------#" << std::endl;
}
}
};
main
int main(int argc, const char *argv[])
{
std::shared_ptr<Devices> devices;
devices->printDeviceIDs();
devices->getDevicesArray();
devices->printDeviceIDs();
}
Someone knows of a good pattern for that?
Also maybe I use smart pointers completely wrong?
Also the iOKit library is from 1985 or something so it is not very descriptive...
Thanks in advance.
As mentioned by other users this is more of a review rather than a error code question;
being that said the only thing I can find is the following:
std::unique_ptr<std::list<Device> > list;
you should change that to the following:
std::list<Device> list;
and on your main
int main(int argc, const char *argv[])
{
std::shared_ptr<Devices> devices;
devices->printDeviceIDs();
devices->getDevicesArray();
devices->printDeviceIDs();
}
should probably only suffice to use:
Devices devices; devices.printDeviceIDs();
I need to store a string along with an integer in a circular buffer and then have to iterate through it, searching (with a substr) for existence of a string which then gets copied along with the integer into result buffer.
I've written the below proof of concept code but it terminates pre-maturely after the first time &res gets copied into out and I'm not exactly sure why, can anyone help me here?
This is what I have so far:
#include <iostream>
#include <boost/circular_buffer.hpp>
#include <boost/algorithm/string/predicate.hpp>
#define CB_SZ 4
#define ARR 7
struct cb_dat_t{
std::string lp;
int cnf;
};
int buffer_check(cb_dat_t &in, boost::circular_buffer<cb_dat_t> &buff, cb_dat_t *out);
int main(void)
{
int i = 0;
cb_dat_t in[ARR];
cb_dat_t out;
boost::circular_buffer<cb_dat_t> cb(CB_SZ);
in[0].lp = "ABC";
in[0].cnf = 78;
in[1].lp = "ABCDE";
in[1].cnf = 63;
in[2].lp = "AB";
in[2].cnf = 92;
in[3].lp = "1234";
in[3].cnf = 85;
in[4].lp = "23";
in[4].cnf = 71;
in[5].lp = "ABC";
in[5].cnf = 63;
in[6].lp = "BC";
in[6].cnf = 71;
for (i=0; i<ARR; i++) {
buffer_check(in[i], cb, &out);
std::cout << "result[" << i << "] " << out.lp << " " << out.cnf << std::endl;
}
std::cout << "all done!" <<std::endl;
return 0;
}
int buffer_check(cb_dat_t &in, boost::circular_buffer<cb_dat_t> &buff, cb_dat_t *out)
{
cb_dat_t res;
if (!buff.size()){
std::cout << "buff.size() " << buff.size() << std::endl;
buff.push_back(in);
memcpy(out,&in,sizeof(cb_dat_t));
return 0;
}
boost::circular_buffer<cb_dat_t>::iterator itr = buff.begin();
while (itr!=buff.end()) {
if (boost::contains(itr->lp,in.lp)) {
std::cout << itr->lp << " contains " << in.lp << std::endl;
memcpy(&res,&itr,sizeof(cb_dat_t));
} else {
std::cout << itr->lp << " does not contain " << in.lp <<std::endl;
memcpy(&res,&in,sizeof(cb_dat_t));
}
itr++;
}
buff.push_back(in);
memcpy(out,&res,sizeof(cb_dat_t));
std::cout << "buff.size() " << buff.size() << std::endl;
return 0;
}
Where the output is:
./circular
buff.size() 0
result[0] ABC 78
ABC does not contain ABCDE
buff.size() 2
Command terminated
I'm not sure why g++ needs me to do memcpy(&res,&itr,sizeof(cb_dat_t));, itr is a pointer already, isn't it? It complains when I do memcpy(&res,itr,sizeof(cb_dat_t)); instead.
You shouldn't be using memcpy at all. Since cb_dat_t is not a POD type (in this case because it contains a member with a constructor), you should be using the assignment operation to copy cb_dat_t objects. The four memcpy calls in buffer_check can be replaced with
*out = in;
res = *itr;
res = in;
*out = res;
memcpy will not handle std::string properly. The compiler generated default assignment operator for cb_dat_t will properly copy all members of the structure.
I'm writing a project to simulate (an oversimplified version of) a blockchain. I've written a class called Block to manage blocks in the blockchain:
(You may notice that I am referencing some additional classes/headers which are not listed in an #include statement at the top. To my knowledge, all the dependencies are adequately taken care of by the makefile.)
#include "Block.h"
/**** constructors ****/
Block::Block()
{
prevHash = "";
merkleRoot = "";
nonce = "";
}
Block::Block(std::string str)
{
std::vector<std::string> strings;
std::string word;
int pos = str.find(" ");
while(pos != std::string::npos)
{
word = str.substr(0, pos);
strings.push_back(word);
pos = str.find(" ");
str = str.substr(pos+1);
}
if(strings.size() != 3)
{
prevHash = "";
merkleRoot = "";
nonce = "";
}
else
{
prevHash = strings.at(0);
merkleRoot = strings.at(1);
nonce = strings.at(2);
}
}
Block::Block(std::vector<Block> &blocks, std::string merkle)
{
// use prev block from Block vector to get hash of prev block
Block prevBlock = blocks.at(0);
prevHash = picosha2::hash256_hex_string(utils::hexToString(prevBlock.toString()));
merkleRoot = merkle;
nonce = mine();
}
Block::Block(std::string hash, std::string merkle, std::string n)
{
prevHash = hash;
merkleRoot = merkle;
nonce = n;
}
Block::Block(const Block &b) //copy constructor
{
prevHash = b.getPrevHash();
merkleRoot = b.getMerkleRoot();
nonce = b.mine();
}
Block::~Block() //destructor
{
}
/**** public methods ****/
bool Block::isValid()
{
std::string hash = picosha2::hash256_hex_string(utils::hexToString(this->toString()));
if(hash[0] == '0')
return true;
return false;
}
std::string Block::toString()
{
return prevHash + merkleRoot + nonce;
}
std::string Block::mine() const
{
// brute force nonces so hash begins with zero/block is valid
for(int i = 0; i < pow(2, 8); i++)
{
Block temp(this->getPrevHash(), this->getMerkleRoot(), std::to_string(i));
if(temp.isValid())
{
// convert i to hex string and return
std::stringstream stream;
stream << std::hex << std::to_string(i);
return stream.str();
}
}
std::cout << "No nonce found." << std::endl;
return "";
}
// calculate this block's hash
std::string Block::calcHash()
{
std::string hash = picosha2::hash256_hex_string(utils::hexToString(this->toString()));
return hash;
}
std::string Block::getPrevHash() const
{
return prevHash;
}
std::string Block::getMerkleRoot() const
{
return merkleRoot;
}
std::string Block::getNonce() const
{
return nonce;
}
So, the actual problem: I can't add instances of the Block class to my vector of Blocks (or an array of Blocks, for that matter; I tried that too). I can create a Block, print it out, basically anything I want. But if I add it to a vector, my computer throws a runtime error. Below is my main file and the output of a run:
#include "main.h"
// function prototype
std::string merkleRoot(std::vector<Transaction> &);
int main(int argc, char ** argv)
{
int numblocks = 0;
int numtransactions = 0;
std::vector<Block> blocks;
std::vector<Transaction> transactions;
if(argc != 3)
{
std::cout << "Usage: main [blockchain.txt] [transaction.txt]" << std::endl;
exit(1);
}
std::string blockchainFileName = argv[1];
std::string transactionFileName = argv[2];
std::cout << "blockchainFileName: " << blockchainFileName << std::endl;
std::cout << "transactionFileName: " << transactionFileName << std::endl;
std::ifstream blockchainFile(blockchainFileName);
std::ifstream transactionFile(transactionFileName);
if(!blockchainFile)
{
std::cout << "Cannot open " << blockchainFileName << std::endl;
exit(1);
}
if(!transactionFile)
{
std::cout << "Cannot open " << transactionFileName << std::endl;
exit(1);
}
/*** Read in blockchain ***/
std::string line;
int count = 0;
while(std::getline(blockchainFile, line))
{
std::vector<Block> temp;
std::cout << "line number " << ++count << std::endl;
Block newBlock(line);
std::cout << newBlock.toString() << std::endl;
blocks.push_back(newBlock);
}
}
Output:
$ ./main textfiles/blockchain_1.txt textfiles/transactions_1.txt
blockchainFileName: textfiles/blockchain_1.txt
transactionFileName: textfiles/transactions_1.txt
line number 1
026765a1c8235d4ac23d2582cda3b9f5c062f805540320173eb9e9148c0dc518704b42e4b11ca131b443c2b02a07ec0b45407f1b125027e3e68b86ace692445800000001
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
Abort trap: 6
blockchain_1.txt file:
026765a1c8235d4ac23d2582cda3b9f5c062f805540320173eb9e9148c0dc518 704b42e4b11ca131b443c2b02a07ec0b45407f1b125027e3e68b86ace6924458 00000001
0b53181ae351f4508363cdc3e8fb3e819fb706c4ba98a3005a980a837561074a 06aa7a8cbda7ac4351c0cae116c589c2eb0ca96cb4c90844812945cb4ffe27c5 00000019
000000000000000000ad6e90c0790e83760a9d13728c23474352a2c8c7a6e0eb 2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3 0000000f
transactions_1.txt file:
Bob Alice 5.0
Alice Bob 1.0
John Bill 2.4
Bill Alice 1.3
John Bill 2.7
Bob John 7.9
Tom Todd 4.5
Todd Bob 12.0
I've dug around for several hours now and I'm kind of out of leads. I will say that I am a little rusty with C++, particularly constructing and using my own classes. If anyone has any ideas, I'd really appreciate it! Let me know if you need any additional parts of my code.
I am using "libspatialindex" library for creating an R-tree index. My data is 2 dimensional with the following values:
(1, (1,5)),
(19, (6,8)),
(20, (3,8)),
(4, (1,9)).
The description of the data is:
1 is a data point and it is present in the interval (1,5)
19 is a data point and it is present in the interval (6,8)
20 is a data point and it is present in the interval (3,8)
4 is a data point and it is present in the interval (1,9)
I am trying to bulk load the above data into R-Tree. For doing so I am using the following test code from libspatialindex. However, I am not getting as to what should be the format of input data file which is passed as an object to *MyDataStream stream(argv[1]);*
The test code which I am using is:
// Copyright (c) 2002, Marios Hadjieleftheriou
// include library header file.
#include <spatialindex/SpatialIndex.h>
using namespace SpatialIndex;
#define INSERT 1
#define DELETE 0
#define QUERY 2
class MyDataStream : public IDataStream
{
public:
MyDataStream(std::string inputFile) : m_pNext(0)
{
m_fin.open(inputFile.c_str());
if (! m_fin)
throw Tools::IllegalArgumentException("Input file not found.");
readNextEntry();
}
virtual ~MyDataStream()
{
if (m_pNext != 0) delete m_pNext;
}
virtual IData* getNext()
{
if (m_pNext == 0) return 0;
RTree::Data* ret = m_pNext;
m_pNext = 0;
readNextEntry();
return ret;
}
virtual bool hasNext()
{
return (m_pNext != 0);
}
virtual uint32_t size()
{
throw Tools::NotSupportedException("Operation not supported.");
}
virtual void rewind()
{
if (m_pNext != 0)
{
delete m_pNext;
m_pNext = 0;
}
m_fin.seekg(0, std::ios::beg);
readNextEntry();
}
void readNextEntry()
{
id_type id;
uint32_t op;
double low[2], high[2];
m_fin >> op >> id >> low[0] >> low[1] >> high[0] >> high[1];
if (m_fin.good())
{
if (op != INSERT)
throw Tools::IllegalArgumentException(
"The data input should contain insertions only."
);
Region r(low, high, 2);
m_pNext = new RTree::Data(sizeof(double), reinterpret_cast<byte*>(low), r, id);
// Associate a bogus data array with every entry for testing purposes.
// Once the data array is given to RTRee:Data a local copy will be created.
// Hence, the input data array can be deleted after this operation if not
// needed anymore.
}
}
std::ifstream m_fin;
RTree::Data* m_pNext;
};
int main(int argc, char** argv)
{
try
{
if (argc != 5)
{
std::cerr << "Usage: " << argv[0] << " input_file tree_file capacity utilization." << std::endl;
return -1;
}
std::string baseName = argv[2];
double utilization = atof(argv[4]);
IStorageManager* diskfile = StorageManager::createNewDiskStorageManager(baseName, 4096);
// Create a new storage manager with the provided base name and a 4K page size.
StorageManager::IBuffer* file = StorageManager::createNewRandomEvictionsBuffer(*diskfile, 10, false);
// applies a main memory random buffer on top of the persistent storage manager
// (LRU buffer, etc can be created the same way).
MyDataStream stream(argv[1]);
// Create and bulk load a new RTree with dimensionality 2, using "file" as
// the StorageManager and the RSTAR splitting policy.
id_type indexIdentifier;
ISpatialIndex* tree = RTree::createAndBulkLoadNewRTree(
RTree::BLM_STR, stream, *file, utilization, atoi(argv[3]), atoi(argv[3]), 2, SpatialIndex::RTree::RV_RSTAR, indexIdentifier);
std::cerr << *tree;
std::cerr << "Buffer hits: " << file->getHits() << std::endl;
std::cerr << "Index ID: " << indexIdentifier << std::endl;
bool ret = tree->isIndexValid();
if (ret == false) std::cerr << "ERROR: Structure is invalid!" << std::endl;
else std::cerr << "The stucture seems O.K." << std::endl;
delete tree;
delete file;
delete diskfile;
// delete the buffer first, then the storage manager
// (otherwise the the buffer will fail trying to write the dirty entries).
}
catch (Tools::Exception& e)
{
std::cerr << "******ERROR******" << std::endl;
std::string s = e.what();
std::cerr << s << std::endl;
return -1;
}
return 0;
}
Chop out readNextEntry and just test that:
void readNextEntry(istream& is)
{
id_type id;
uint32_t op;
double low[2], high[2];
is >> op >> id >> low[0] >> low[1] >> high[0] >> high[1];
}
We can create a test input stream using istringstream:
bool isGood(const std::string& input) {
std::istringstream ss(input);
readNextEntry(ss);
return ss.good();
}
And demonstrate:
int main() {
std::cout << "Good? " << isGood("asdf qwer zxcv uyio ghjk") << std::endl;
std::cout << "Good? " << isGood("0.0 0 0 0 0") << std::endl;
std::cout << "Good? " << isGood("1 2 3 4 5") << std::endl;
}