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