# data.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       data.cc
    00003 ///             Classes to help manage pre-determined data files.
    00004 ///
    00005
    00006 /*
    00007     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00008
    00009     This program is free software; you can redistribute it and/or modify
    00010     it under the terms of the GNU General Public License as published by
    00011     the Free Software Foundation; either version 2 of the License, or
    00012     (at your option) any later version.
    00013
    00014     This program is distributed in the hope that it will be useful,
    00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00017
    00018     See the GNU General Public License in the COPYING file at the
    00019     root directory of this project for more details.
    00020 */
    00021
    00022 #include "data.h"
    00023 #include <fstream>
    00024 #include <sstream>
    00025 #include <iomanip>
    00026 #include <string>
    00027 #include <stdexcept>
    00028 #include <string.h>
    00029 #include <stdlib.h>
    00030
    00031 //#define __DEBUG_MODE__
    00032 #include "debug.h"
    00033
    00034
    00035 using namespace std;
    00036
    00037
    00038 namespace Barry {
    00039
    00040 inline bool IsHexData(const std::string &s)
    00041 {
    00042         const char *str = s.c_str();
    00043         for( int i = 0; i < 4 && *str; str++, i++ )
    00044                 if( *str != ' ' )
    00045                         return false;
    00046
    00047         for( int i = 0; i < 8 && *str; str++, i++ ) {
    00048                 const char *hexchars = "0123456789abcdef";
    00049                 if( strchr(hexchars, *str) == NULL )
    00050                         return false;
    00051         }
    00052
    00053         if( *str != ':' )
    00054                 return false;
    00055
    00056         return true;
    00057 }
    00058
    00059
    00060
    00061 ///////////////////////////////////////////////////////////////////////////////
    00062 // Data class
    00063
    00064 bool Data::bPrintAscii = true;
    00065
    00066 Data::Data()
    00067         : m_data(new unsigned char[0x4000]),
    00068         m_bufsize(0x4000),
    00069         m_datasize(0),
    00070         m_endpoint(-1),
    00071         m_externalData(0),
    00072         m_external(false)
    00073 {
    00074         memset(m_data, 0, m_bufsize);
    00075 }
    00076
    00077 Data::Data(int endpoint, size_t startsize)
    00078         : m_data(new unsigned char[startsize]),
    00079         m_bufsize(startsize),
    00080         m_datasize(0),
    00081         m_endpoint(endpoint),
    00082         m_externalData(0),
    00083         m_external(false)
    00084 {
    00085         memset(m_data, 0, m_bufsize);
    00086 }
    00087
    00088 Data::Data(const void *ValidData, size_t size)
    00089         : m_data(0),
    00090         m_bufsize(0),
    00091         m_datasize(size),
    00092         m_endpoint(-1),
    00093         m_externalData((const unsigned char*)ValidData),
    00094         m_external(true)
    00095 {
    00096 }
    00097
    00098 Data::Data(const Data &other)
    00099         : m_data(other.m_bufsize ? new unsigned char[other.m_bufsize] : 0),
    00100         m_bufsize(other.m_bufsize),
    00101         m_datasize(other.m_datasize),
    00102         m_endpoint(other.m_endpoint),
    00103         m_externalData(other.m_externalData),
    00104         m_external(other.m_external)
    00105 {
    00106         // copy over the raw data
    00107         if( !m_external )
    00108                 memcpy(m_data, other.m_data, other.m_bufsize);
    00109 }
    00110
    00111 Data::~Data()
    00112 {
    00113         delete [] m_data;
    00114 }
    00115
    00116 void Data::MakeSpace(size_t desiredsize)
    00117 {
    00118         if( m_bufsize < desiredsize ) {
    00119                 desiredsize += 1024;    // get a proper chunk
    00120                 unsigned char *newbuf = new unsigned char[desiredsize];
    00121                 memcpy(newbuf, m_data, m_bufsize);
    00122                 memset(newbuf + m_bufsize, 0, desiredsize - m_bufsize);
    00123                 delete [] m_data;
    00124                 m_data = newbuf;
    00125                 m_bufsize = desiredsize;
    00126         }
    00127 }
    00128
    00129 // perform the copy on write operation if needed
    00130 void Data::CopyOnWrite(size_t desiredsize)
    00131 {
    00132         if( m_external ) {
    00133                 // make room
    00134                 MakeSpace(std::max(desiredsize, m_datasize));
    00135
    00136                 // copy it over
    00137                 memcpy(m_data, m_externalData, m_datasize);
    00138
    00139                 // not external anymore
    00140                 m_external = false;
    00141         }
    00142 }
    00143
    00144 void Data::InputHexLine(istream &is)
    00145 {
    00146         unsigned int values[16];
    00147         size_t index = 0;
    00148
    00149         size_t address;
    00150         is >> setbase(16) >> address;
    00151         if( !is )
    00152                 return;         // nothing to do
    00153
    00154         is.ignore();            // eat the ':'
    00155
    00156         while( is && index < 16 ) {
    00157                 is >> setbase(16) >> values[index];
    00158                 if( is )
    00159                         index++;
    00160         }
    00161
    00162         dout("InputHexLine: read " << index << " bytes");
    00163
    00164         CopyOnWrite(address + index);
    00165         MakeSpace(address + index);     // make space for the new
    00166         m_datasize = std::max(address + index, m_datasize);
    00167         while( index-- )
    00168                 m_data[address + index] = (unsigned char) values[index];
    00169         return;
    00170 }
    00171
    00172 void Data::DumpHexLine(ostream &os, size_t index, size_t size) const
    00173 {
    00174         ios::fmtflags oldflags = os.setf(ios::right);
    00175
    00176         // index
    00177         os << "    ";
    00178         os << setbase(16) << setfill('0') << setw(8)
    00179            << index << ": ";
    00180
    00181         // hex byte data
    00182         for( size_t i = 0; i < size; i++ ) {
    00183                 if( (index+i) < GetSize() ) {
    00184                         os << setbase(16) << setfill('0')
    00185                            << setw(2) << setprecision(2)
    00186                            << (unsigned int) GetData()[index + i] << ' ';
    00187                 }
    00188                 else {
    00189                         os << "   ";
    00190                 }
    00191         }
    00192
    00193         // printable data
    00194         if( bPrintAscii ) {
    00195                 locale loc = os.getloc();
    00196                 os << ' ';
    00197                 for( size_t i = 0; i < size && (index+i) < GetSize(); i++ ) {
    00198                         ostream::traits_type::char_type c = GetData()[index + i];
    00199                         os << setbase(10) << (char) (isprint(c, loc) ? c : '.');
    00200                 }
    00201         }
    00202
    00203         os << "\n";
    00204         os.flags(oldflags);
    00205 }
    00206
    00207 void Data::DumpHex(ostream &os) const
    00208 {
    00209         for( size_t address = 0; address < GetSize(); address += 16 ) {
    00210                 DumpHexLine(os, address, 16);
    00211         }
    00212 }
    00213
    00214 unsigned char * Data::GetBuffer(size_t requiredsize)
    00215 {
    00216         CopyOnWrite(requiredsize);
    00217         if( requiredsize > 0 )
    00218                 MakeSpace(requiredsize);
    00219         return m_data;
    00220 }
    00221
    00222 void Data::ReleaseBuffer(int datasize)
    00223 {
    00224         assert( datasize >= 0 || datasize == -1 );
    00225         assert( datasize == -1 || (unsigned int)datasize <= m_bufsize );
    00226         assert( !m_external );
    00227
    00228         if( m_external )
    00229                 return;
    00230         if( datasize >= 0 && (unsigned int)datasize > m_bufsize ) {
    00231                 dout("ReleaseBuffer called with datasize("
    00232                         << std::dec << datasize << ") > m_bufsize("
    00233                         << m_bufsize << ")");
    00234                 return;
    00235         }
    00236
    00237         if( datasize >= 0 ) {
    00238                 m_datasize = datasize;
    00239         }
    00240         else {
    00241                 // search for last non-zero value in buffer
    00242                 m_datasize = m_bufsize - 1;
    00243                 while( m_datasize && m_data[m_datasize] == 0 )
    00244                         --m_datasize;
    00245         }
    00246 }
    00247
    00248 /// Append bytes of data based on str
    00249 void Data::AppendHexString(const char *str)
    00250 {
    00251         CopyOnWrite(m_datasize + 512);
    00252
    00253         std::istringstream iss(str);
    00254         unsigned int byte;
    00255         while( iss >> hex >> byte ) {
    00256                 MakeSpace(m_datasize + 1);
    00257                 m_data[m_datasize] = (unsigned char) byte;
    00258                 m_datasize++;
    00259         }
    00260 }
    00261
    00262 /// set buffer to 0 and remove all data
    00263 void Data::Zap()
    00264 {
    00265         if( !m_external )
    00266                 memset(m_data, 0, m_bufsize);
    00267         m_datasize = 0;
    00268 }
    00269
    00270 Data & Data::operator=(const Data &other)
    00271 {
    00272         if( this == &other )
    00273                 return *this;
    00274
    00275         // don't remove our current buffer, only grow it if needed
    00276         MakeSpace(other.m_bufsize);
    00277         memcpy(m_data, other.m_data, other.m_bufsize);
    00278
    00279         // then copy over the data state
    00280         m_datasize = other.m_datasize;
    00281         m_endpoint = other.m_endpoint;
    00282         m_externalData = other.m_externalData;
    00283         m_external = other.m_external;
    00284         return *this;
    00285 }
    00286
    00287 void Data::MemCpy(size_t &offset, const void *src, size_t size)
    00288 {
    00289         unsigned char *pd = GetBuffer(offset + size) + offset;
    00290         memcpy(pd, src, size);
    00291         offset += size;
    00292 }
    00293
    00294 void Data::Append(const void *buf, size_t size)
    00295 {
    00296         // MemCpy updates m_datasize via the offset reference
    00297         MemCpy(m_datasize, buf, size);
    00298 }
    00299
    00300 istream& operator>> (istream &is, Data &data)
    00301 {
    00302         data.InputHexLine(is);
    00303         return is;
    00304 }
    00305
    00306 ostream& operator<< (ostream &os, const Data &data)
    00307 {
    00308         data.DumpHex(os);
    00309         return os;
    00310 }
    00311
    00312
    00313 ///////////////////////////////////////////////////////////////////////////////
    00314 // Diff class
    00315
    00316 Diff::Diff(const Data &old, const Data &new_)
    00317         : m_old(old), m_new(new_)
    00318 {
    00319 }
    00320
    00321 void Diff::Compare(ostream &os, size_t index, size_t size) const
    00322 {
    00323         size_t min = std::min(m_old.GetSize(), m_new.GetSize());
    00324
    00325         // index
    00326         os << ">   ";
    00327         os << setbase(16) << setfill('0') << setw(8)
    00328            << index << ": ";
    00329
    00330         // diff data
    00331         for( size_t i = 0; i < size; i++ ) {
    00332                 size_t address = index + i;
    00333
    00334                 // if data is available, print the diff
    00335                 if( address < min ) {
    00336                         if( m_old.GetData()[address] != m_new.GetData()[address] ) {
    00337                                 // differ, print hex
    00338                                 os << setbase(16) << setfill('0')
    00339                                    << setw(2) << setprecision(2)
    00340                                    << (unsigned int) m_new.GetData()[address] << ' ';
    00341                         }
    00342                         else {
    00343                                 // same, just print spaces
    00344                                 os << "   ";
    00345                         }
    00346                 }
    00347                 else {
    00348                         // one of the buffers is shorter...
    00349                         if( address < m_new.GetSize() ) {
    00350                                 // new still has data, print it
    00351                                 os << setbase(16) << setfill('0')
    00352                                    << setw(2) << setprecision(2)
    00353                                    << (unsigned int) m_new.GetData()[address]
    00354                                    << ' ';
    00355                         }
    00356                         else if( address < m_old.GetSize() ) {
    00357                                 // new is out of data and old still has some
    00358                                 os << "XX ";
    00359                         }
    00360                         else {
    00361                                 // no more data, just print spaces
    00362                                 os << "   ";
    00363                         }
    00364                 }
    00365         }
    00366
    00367         // printable data, just dump new
    00368         if( Data::PrintAscii() ) {
    00369                 os << ' ';
    00370                 for( size_t i = 0; i < size && (index+i) < m_new.GetSize(); i++ ) {
    00371                         int c = m_new.GetData()[index + i];
    00372                         os << setbase(10) << (char) (isprint(c) ? c : '.');
    00373                 }
    00374         }
    00375
    00376         os << "\n";
    00377 }
    00378
    00379 void Diff::Dump(std::ostream &os) const
    00380 {
    00381         if( m_old.GetSize() != m_new.GetSize() )
    00382                 os << "sizes differ: "
    00383                    << m_old.GetSize() << " != " << m_new.GetSize() << endl;
    00384
    00385         size_t max = std::max(m_old.GetSize(), m_new.GetSize());
    00386         for( size_t i = 0; i < max; i += 16 ) {
    00387                 m_old.DumpHexLine(os, i, 16);
    00388                 Compare(os, i, 16);
    00389         }
    00390 }
    00391
    00392 ostream& operator<< (ostream &os, const Diff &diff)
    00393 {
    00394         diff.Dump(os);
    00395         return os;
    00396 }
    00397
    00398
    00399 ///////////////////////////////////////////////////////////////////////////////
    00400 // DBData class
    00401
    00402 /// Default constructor, constructs an empty local Data object
    00403 DBData::DBData()
    00404         : m_version(REC_VERSION_1)  // a reasonable default for now
    00405         , m_localData(new Data)
    00406         , m_data(*m_localData)
    00407 {
    00408 }
    00409
    00410
    00411 /// Constructs a local Data object that points to external memory
    00412 DBData::DBData(const void *ValidData, size_t size)
    00413         : m_version(REC_VERSION_1)  // a reasonable default for now
    00414         , m_localData(new Data)
    00415         , m_data(*m_localData)
    00416 {
    00417 }
    00418
    00419 DBData::DBData(RecordFormatVersion ver,
    00420                 const std::string &dbName,
    00421                 uint8_t recType,
    00422                 uint32_t uniqueId,
    00423                 size_t offset,
    00424                 const void *ValidData,
    00425                 size_t size)
    00426         : m_version(ver)
    00427         , m_dbName(dbName)
    00428         , m_recType(recType)
    00429         , m_uniqueId(uniqueId)
    00430         , m_offset(offset)
    00431         , m_localData(new Data(ValidData, size))
    00432         , m_data(*m_localData)
    00433 {
    00434 }
    00435
    00436 /// If copy == false, constructs an external Data object, no local.
    00437 /// If copy == true, constructs an internal Data object copy
    00438 DBData::DBData(Data &externalData, bool copy)
    00439         : m_version(REC_VERSION_1)  // a reasonable default for now
    00440         , m_localData(copy ? new Data(externalData) : 0)
    00441         , m_data(copy ? *m_localData : externalData)
    00442 {
    00443 }
    00444
    00445 DBData::DBData(RecordFormatVersion ver,
    00446                 const std::string &dbName,
    00447                 uint8_t recType,
    00448                 uint32_t uniqueId,
    00449                 size_t offset,
    00450                 Data &externalData,
    00451                 bool copy)
    00452         : m_version(ver)
    00453         , m_dbName(dbName)
    00454         , m_recType(recType)
    00455         , m_uniqueId(uniqueId)
    00456         , m_offset(offset)
    00457         , m_localData(copy ? new Data(externalData) : 0)
    00458         , m_data(copy ? *m_localData : externalData)
    00459 {
    00460 }
    00461
    00462 DBData::~DBData()
    00463 {
    00464         delete m_localData;
    00465 }
    00466
    00467 Data& DBData::UseData()
    00468 {
    00469         // make sure m_data is not external anymore
    00470         m_data.GetBuffer();
    00471         return m_data;  // return it
    00472 }
    00473
    00474 // Note: this copy operator does not change what m_data references...
    00475 // whatever m_data references in the constructor is what will be changed
    00476 // in this copy.
    00477 // Note also that the copy *will* involve a memcpy, and maybe a memory
    00478 // allocation as well.
    00479 DBData& DBData::operator=(const DBData &other)
    00480 {
    00481         if( this == &other )
    00482                 return *this;
    00483
    00484         // copy the data block
    00485         m_data = other.m_data;
    00486
    00487         // copy the metadata
    00488         CopyMeta(other);
    00489
    00490         return *this;
    00491 }
    00492
    00493 ///////////////////////////////////////////////////////////////////////////////
    00494 // Utility functions
    00495
    00496 static bool IsEndpointStart(const std::string &line, int &endpoint)
    00497 {
    00498         if( strncmp(line.c_str(), "sep: ", 5) == 0 ||
    00499             strncmp(line.c_str(), "rep: ", 5) == 0 )
    00500         {
    00501                 endpoint = atoi(line.c_str() + 5);
    00502                 return true;
    00503         }
    00504         return false;
    00505 }
    00506
    00507 bool LoadDataArray(const string &filename, std::vector<Data> &array)
    00508 {
    00509         ifstream in(filename.c_str());
    00510         return ReadDataArray(in, array);
    00511 }
    00512
    00513 bool ReadDataArray(std::istream &is, std::vector<Data> &array)
    00514 {
    00515         if( !is )
    00516                 return false;
    00517
    00518         bool bInEndpoint = false;
    00519         unsigned int nCurrent = 0;
    00520         size_t nLargestSize = 0x100;
    00521         while( is ) {
    00522                 string line;
    00523                 getline(is, line);
    00524                 int endpoint;
    00525                 if( bInEndpoint ) {
    00526                         if( IsHexData(line) ) {
    00527                                 istringstream sline(line);
    00528                                 sline >> array[nCurrent];
    00529                                 continue;
    00530                         }
    00531                         else {
    00532                                 nLargestSize = std::max(nLargestSize,
    00533                                         array[nCurrent].GetBufSize());
    00534                                 bInEndpoint = false;
    00535                         }
    00536                 }
    00537
    00538                 // check if this line starts a new endpoint
    00539                 if( IsEndpointStart(line, endpoint) ) {
    00540                         bInEndpoint = true;
    00541                         Data chunk(endpoint, nLargestSize);
    00542                         array.push_back(chunk);
    00543                         nCurrent = array.size() - 1;
    00544                 }
    00545         }
    00546         return true;
    00547 }
    00548
    00549 } // namespace Barry
    00550
    00551
    00552 #ifdef __TEST_MODE__
    00553
    00554 #include <iostream>
    00555 #include <iomanip>
    00556 #include "data.h"
    00557
    00558 using namespace std;
    00559
    00560 int main()
    00561 {
    00562         typedef std::vector<Data> DataVec;
    00563         DataVec array;
    00564         if( !LoadDataArray("data/parsed.log", array) ) {
    00565                 cout << "Can't load file" << endl;
    00566                 return 1;
    00567         }
    00568
    00569         DataVec::iterator i = array.begin();
    00570         Data::PrintAscii(false);
    00571         for( ; i != array.end(); i++ ) {
    00572                 cout << "Endpoint: " << i->GetEndpoint() << endl;
    00573                 cout << *i;
    00574                 cout << "\n\n";
    00575         }
    00576
    00577
    00578         Data one, two;
    00579         one.GetBuffer()[0] = 0x01;
    00580         one.ReleaseBuffer(1);
    00581         two.GetBuffer()[0] = 0x02;
    00582         two.ReleaseBuffer(2);
    00583
    00584         cout << Diff(one, two) << endl;
    00585         cout << Diff(two, one) << endl;
    00586
    00587         two.GetBuffer();
    00588         two.ReleaseBuffer(32);
    00589         cout << Diff(one, two) << endl;
    00590 }
    00591
    00592 #endif
    00593