3 /// Classes to help manage pre-determined data files.
7 Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
32 //#define __DEBUG_MODE__
41 inline bool IsHexData(const std::string
&s
)
43 const char *str
= s
.c_str();
44 for( int i
= 0; i
< 4 && *str
; str
++, i
++ )
48 for( int i
= 0; i
< 8 && *str
; str
++, i
++ ) {
49 const char *hexchars
= "0123456789abcdef";
50 if( strchr(hexchars
, *str
) == NULL
)
62 ///////////////////////////////////////////////////////////////////////////////
65 bool Data::bPrintAscii
= true;
68 : m_memBlock(new unsigned char[0x4100])
70 , m_dataStart(m_memBlock
+ 0x100)
76 memset(m_memBlock
, 0, m_blockSize
);
79 Data::Data(int endpoint
, size_t startsize
, size_t prependsize
)
80 : m_memBlock(new unsigned char[startsize
+ prependsize
])
81 , m_blockSize(startsize
+ prependsize
)
82 , m_dataStart(m_memBlock
+ prependsize
)
86 , m_endpoint(endpoint
)
88 memset(m_memBlock
, 0, m_blockSize
);
91 Data::Data(const void *ValidData
, size_t size
)
96 , m_externalData((const unsigned char*)ValidData
)
102 Data::Data(const Data
&other
)
103 : m_memBlock(other
.m_blockSize
? new unsigned char[other
.m_blockSize
] : 0)
104 , m_blockSize(other
.m_blockSize
)
105 , m_dataStart(m_memBlock
+ other
.AvailablePrependSpace())
106 , m_dataSize(other
.m_dataSize
)
107 , m_externalData(other
.m_externalData
)
108 , m_external(other
.m_external
)
109 , m_endpoint(other
.m_endpoint
)
111 // copy over the raw data
113 memcpy(m_memBlock
, other
.m_memBlock
, other
.m_blockSize
);
118 delete [] m_memBlock
;
124 /// Reallocates buffers so that it is safe to write desiredsize data
125 /// to m_dataStart after it returns. All existing data is preserved.
127 /// This function also performs any copy on write needed.
129 /// If desiredprepend is nonzero, then at least desiredprepend bytes
130 /// of prepend space will exist in the buffer after return.
131 /// If desiredprepend is zero, defaults will be used if needed.
133 void Data::MakeSpace(size_t desiredsize
, size_t desiredprepend
)
135 // use a default prepend size if none currently available
136 size_t prepend
= std::max(AvailablePrependSpace(), desiredprepend
);
140 // GetBufSize() returns 0 if m_external is true
141 if( GetBufSize() < (desiredsize
+ prepend
) ||
142 (desiredprepend
&& AvailablePrependSpace() < desiredprepend
) )
144 // get a proper chunk to avoid future resizes
145 desiredsize
+= 1024 + prepend
;
147 // desired size must be at least the size of our current
148 // data (in case of external data), as well as the size
149 // of our desired prepend space
150 if( desiredsize
< (m_dataSize
+ prepend
) )
151 desiredsize
= m_dataSize
+ prepend
;
153 // setup new zeroed buffer... reuse m_memBlock if it
154 // exists (see operator=())
155 unsigned char *newbuf
= 0;
156 if( m_memBlock
&& m_blockSize
>= desiredsize
) {
160 newbuf
= new unsigned char[desiredsize
];
161 memset(newbuf
, 0, desiredsize
);
164 // copy valid data over
166 memcpy(newbuf
+ prepend
, m_externalData
, m_dataSize
);
168 // not external anymore
172 memcpy(newbuf
+ prepend
, m_dataStart
, m_dataSize
);
175 // install new buffer if we've allocated a new one
176 if( m_memBlock
!= newbuf
) {
177 delete [] m_memBlock
;
179 m_blockSize
= desiredsize
;
182 // update m_dataStart
183 m_dataStart
= m_memBlock
+ prepend
;
187 size_t Data::AvailablePrependSpace() const
192 return m_dataStart
- m_memBlock
;
195 void Data::InputHexLine(istream
&is
)
197 unsigned int values
[16];
201 is
>> setbase(16) >> address
;
203 return; // nothing to do
205 is
.ignore(); // eat the ':'
207 while( is
&& index
< 16 ) {
208 is
>> setbase(16) >> values
[index
];
213 dout("InputHexLine: read " << index
<< " bytes");
215 MakeSpace(address
+ index
); // make space for the new
216 m_dataSize
= std::max(address
+ index
, m_dataSize
);
218 m_dataStart
[address
+ index
] = (unsigned char) values
[index
];
222 void Data::DumpHexLine(ostream
&os
, size_t index
, size_t size
) const
224 ios::fmtflags oldflags
= os
.setf(ios::right
);
228 os
<< setbase(16) << setfill('0') << setw(8)
232 for( size_t i
= 0; i
< size
; i
++ ) {
233 if( (index
+i
) < GetSize() ) {
234 os
<< setbase(16) << setfill('0')
235 << setw(2) << setprecision(2)
236 << (unsigned int) GetData()[index
+ i
] << ' ';
245 locale loc
= os
.getloc();
247 for( size_t i
= 0; i
< size
&& (index
+i
) < GetSize(); i
++ ) {
248 ostream::traits_type::char_type c
= GetData()[index
+ i
];
249 os
<< setbase(10) << (char) (std::isprint(c
, loc
) ? c
: '.');
257 void Data::DumpHex(ostream
&os
) const
259 for( size_t address
= 0; address
< GetSize(); address
+= 16 ) {
260 DumpHexLine(os
, address
, 16);
264 unsigned char * Data::GetBuffer(size_t requiredsize
)
266 if( requiredsize
== 0 ) {
267 // handle default, use data size
268 requiredsize
= m_dataSize
;
271 MakeSpace(requiredsize
);
275 /// Returns size of buffer returned by GetBuffer(). Note that this does not
276 /// include available prepend space.
277 size_t Data::GetBufSize() const
282 return m_blockSize
- (m_dataStart
- m_memBlock
);
285 void Data::ReleaseBuffer(int datasize
)
287 if( datasize
< 0 && datasize
!= -1)
288 throw std::logic_error("Data::ReleaseBuffer() argument must be -1 or >= 0");
290 throw std::logic_error("Data::ReleaseBuffer() must be called after GetBuffer()");
291 if( !(datasize
== -1 || (unsigned int)datasize
<= GetBufSize()) )
292 throw std::logic_error("Data::ReleaseBuffer() must be called with a size smaller than the original buffer requested");
294 if( datasize
>= 0 ) {
295 m_dataSize
= datasize
;
298 // search for last non-zero value in buffer
299 m_dataSize
= GetBufSize() - 1;
300 while( m_dataSize
&& m_dataStart
[m_dataSize
] == 0 )
305 /// Append bytes of data based on str
306 void Data::AppendHexString(const char *str
)
308 MakeSpace(m_dataSize
+ 512);
310 std::istringstream
iss(str
);
312 while( iss
>> hex
>> byte
) {
313 MakeSpace(m_dataSize
+ 1);
314 m_dataStart
[m_dataSize
] = (unsigned char) byte
;
319 /// set buffer to 0 and remove all data
323 memset(m_memBlock
, 0, m_blockSize
);
327 Data
& Data::operator=(const Data
&other
)
332 if( other
.m_external
) {
333 // just copy over the pointers
334 m_externalData
= other
.m_externalData
;
335 m_external
= other
.m_external
;
336 m_dataSize
= other
.m_dataSize
;
337 m_endpoint
= other
.m_endpoint
;
340 // don't remove our current buffer, only grow it if needed
341 MakeSpace(other
.m_dataSize
);
342 memcpy(m_dataStart
, other
.m_dataStart
, other
.m_dataSize
);
344 // then copy over the data state
345 m_dataSize
= other
.m_dataSize
;
346 m_endpoint
= other
.m_endpoint
;
352 void Data::MemCpy(size_t &offset
, const void *src
, size_t size
)
354 unsigned char *pd
= GetBuffer(offset
+ size
) + offset
;
355 memcpy(pd
, src
, size
);
358 // if the new end of data is larger than m_dataSize, bump it
359 if( offset
> m_dataSize
)
363 void Data::Append(const void *buf
, size_t size
)
365 // MemCpy updates m_datasize via the offset reference
366 MemCpy(m_dataSize
, buf
, size
);
369 void Data::Prepend(const void *buf
, size_t size
)
374 memcpy(m_dataStart
, (const unsigned char*) buf
, size
);
377 /// Removes size bytes from the beginning of the buffer.
378 /// If GetSize() is less than size, then all bytes will be chopped
379 /// and GetSize() will end up 0.
380 void Data::Prechop(size_t size
)
382 // chopping all the bytes that we have?
383 if( size
>= GetSize() ) {
389 m_externalData
+= size
;
398 istream
& operator>> (istream
&is
, Data
&data
)
400 data
.InputHexLine(is
);
404 ostream
& operator<< (ostream
&os
, const Data
&data
)
411 ///////////////////////////////////////////////////////////////////////////////
414 Diff::Diff(const Data
&old
, const Data
&new_
)
415 : m_old(old
), m_new(new_
)
419 void Diff::Compare(ostream
&os
, size_t index
, size_t size
) const
421 size_t min
= std::min(m_old
.GetSize(), m_new
.GetSize());
425 os
<< setbase(16) << setfill('0') << setw(8)
429 for( size_t i
= 0; i
< size
; i
++ ) {
430 size_t address
= index
+ i
;
432 // if data is available, print the diff
433 if( address
< min
) {
434 if( m_old
.GetData()[address
] != m_new
.GetData()[address
] ) {
436 os
<< setbase(16) << setfill('0')
437 << setw(2) << setprecision(2)
438 << (unsigned int) m_new
.GetData()[address
] << ' ';
441 // same, just print spaces
446 // one of the buffers is shorter...
447 if( address
< m_new
.GetSize() ) {
448 // new still has data, print it
449 os
<< setbase(16) << setfill('0')
450 << setw(2) << setprecision(2)
451 << (unsigned int) m_new
.GetData()[address
]
454 else if( address
< m_old
.GetSize() ) {
455 // new is out of data and old still has some
459 // no more data, just print spaces
465 // printable data, just dump new
466 if( Data::PrintAscii() ) {
468 for( size_t i
= 0; i
< size
&& (index
+i
) < m_new
.GetSize(); i
++ ) {
469 int c
= m_new
.GetData()[index
+ i
];
470 os
<< setbase(10) << (char) (isprint(c
) ? c
: '.');
477 void Diff::Dump(std::ostream
&os
) const
479 if( m_old
.GetSize() != m_new
.GetSize() )
480 os
<< "sizes differ: "
481 << m_old
.GetSize() << " != " << m_new
.GetSize() << endl
;
483 size_t max
= std::max(m_old
.GetSize(), m_new
.GetSize());
484 for( size_t i
= 0; i
< max
; i
+= 16 ) {
485 m_old
.DumpHexLine(os
, i
, 16);
490 ostream
& operator<< (ostream
&os
, const Diff
&diff
)
497 ///////////////////////////////////////////////////////////////////////////////
500 /// Default constructor, constructs an empty local Data object
502 : m_version(REC_VERSION_1
) // a reasonable default for now
503 , m_localData(new Data
)
504 , m_data(*m_localData
)
509 /// Constructs a local Data object that points to external memory
510 DBData::DBData(const void *ValidData
, size_t size
)
511 : m_version(REC_VERSION_1
) // a reasonable default for now
512 , m_localData(new Data
)
513 , m_data(*m_localData
)
517 DBData::DBData(RecordFormatVersion ver
,
518 const std::string
&dbName
,
522 const void *ValidData
,
527 , m_uniqueId(uniqueId
)
529 , m_localData(new Data(ValidData
, size
))
530 , m_data(*m_localData
)
534 /// If copy == false, constructs an external Data object, no local.
535 /// If copy == true, constructs an internal Data object copy
536 DBData::DBData(Data
&externalData
, bool copy
)
537 : m_version(REC_VERSION_1
) // a reasonable default for now
538 , m_localData(copy
? new Data(externalData
) : 0)
539 , m_data(copy
? *m_localData
: externalData
)
543 DBData::DBData(RecordFormatVersion ver
,
544 const std::string
&dbName
,
553 , m_uniqueId(uniqueId
)
555 , m_localData(copy
? new Data(externalData
) : 0)
556 , m_data(copy
? *m_localData
: externalData
)
565 Data
& DBData::UseData()
567 // make sure m_data is not external anymore
569 return m_data
; // return it
572 // Note: this copy operator does not change what m_data references...
573 // whatever m_data references in the constructor is what will be changed
575 // Note also that the copy *will* involve a memcpy, and maybe a memory
576 // allocation as well.
577 DBData
& DBData::operator=(const DBData
&other
)
582 // copy the data block
583 m_data
= other
.m_data
;
591 ///////////////////////////////////////////////////////////////////////////////
594 static bool IsEndpointStart(const std::string
&line
, int &endpoint
)
596 if( strncmp(line
.c_str(), "sep: ", 5) == 0 ||
597 strncmp(line
.c_str(), "rep: ", 5) == 0 )
599 endpoint
= atoi(line
.c_str() + 5);
605 bool LoadDataArray(const string
&filename
, std::vector
<Data
> &array
)
607 ifstream
in(filename
.c_str());
608 return ReadDataArray(in
, array
);
611 bool ReadDataArray(std::istream
&is
, std::vector
<Data
> &array
)
616 bool bInEndpoint
= false;
617 unsigned int nCurrent
= 0;
618 size_t nLargestSize
= 0x100;
624 if( IsHexData(line
) ) {
625 istringstream
sline(line
);
626 sline
>> array
[nCurrent
];
630 nLargestSize
= std::max(nLargestSize
,
631 array
[nCurrent
].GetBufSize());
636 // check if this line starts a new endpoint
637 if( IsEndpointStart(line
, endpoint
) ) {
639 Data
chunk(endpoint
, nLargestSize
);
640 array
.push_back(chunk
);
641 nCurrent
= array
.size() - 1;
660 typedef std::vector
<Data
> DataVec
;
662 if( !LoadDataArray("data/parsed.log", array
) ) {
663 cout
<< "Can't load file" << endl
;
667 DataVec::iterator i
= array
.begin();
668 Data::PrintAscii(false);
669 for( ; i
!= array
.end(); i
++ ) {
670 cout
<< "Endpoint: " << i
->GetEndpoint() << endl
;
677 one
.GetBuffer()[0] = 0x01;
678 one
.ReleaseBuffer(1);
679 two
.GetBuffer()[0] = 0x02;
680 two
.ReleaseBuffer(2);
682 cout
<< Diff(one
, two
) << endl
;
683 cout
<< Diff(two
, one
) << endl
;
686 two
.ReleaseBuffer(32);
687 cout
<< Diff(one
, two
) << endl
;