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.
31 #include "ios_state.h"
33 //#define __DEBUG_MODE__
42 inline bool IsHexData(const std::string
&s
)
44 const char *str
= s
.c_str();
45 for( int i
= 0; i
< 4 && *str
; str
++, i
++ )
49 for( int i
= 0; i
< 8 && *str
; str
++, i
++ ) {
50 const char *hexchars
= "0123456789abcdef";
51 if( strchr(hexchars
, *str
) == NULL
)
63 ///////////////////////////////////////////////////////////////////////////////
66 bool Data::bPrintAscii
= true;
69 : m_memBlock(new unsigned char[0x4100])
71 , m_dataStart(m_memBlock
+ 0x100)
77 memset(m_memBlock
, 0, m_blockSize
);
80 Data::Data(int endpoint
, size_t startsize
, size_t prependsize
)
81 : m_memBlock(new unsigned char[startsize
+ prependsize
])
82 , m_blockSize(startsize
+ prependsize
)
83 , m_dataStart(m_memBlock
+ prependsize
)
87 , m_endpoint(endpoint
)
89 memset(m_memBlock
, 0, m_blockSize
);
92 Data::Data(const void *ValidData
, size_t size
)
97 , m_externalData((const unsigned char*)ValidData
)
103 Data::Data(const Data
&other
)
104 : m_memBlock(other
.m_blockSize
? new unsigned char[other
.m_blockSize
] : 0)
105 , m_blockSize(other
.m_blockSize
)
106 , m_dataStart(m_memBlock
+ other
.AvailablePrependSpace())
107 , m_dataSize(other
.m_dataSize
)
108 , m_externalData(other
.m_externalData
)
109 , m_external(other
.m_external
)
110 , m_endpoint(other
.m_endpoint
)
112 // copy over the raw data
114 memcpy(m_memBlock
, other
.m_memBlock
, other
.m_blockSize
);
119 delete [] m_memBlock
;
125 /// Reallocates buffers so that it is safe to write desiredsize data
126 /// to m_dataStart after it returns. All existing data is preserved.
128 /// This function also performs any copy on write needed.
130 /// If desiredprepend is nonzero, then at least desiredprepend bytes
131 /// of prepend space will exist in the buffer after return.
132 /// If desiredprepend is zero, defaults will be used if needed.
134 void Data::MakeSpace(size_t desiredsize
, size_t desiredprepend
)
136 // use a default prepend size if none currently available
137 size_t prepend
= std::max(AvailablePrependSpace(), desiredprepend
);
141 // GetBufSize() returns 0 if m_external is true
142 if( GetBufSize() < (desiredsize
+ prepend
) ||
143 (desiredprepend
&& AvailablePrependSpace() < desiredprepend
) )
145 // get a proper chunk to avoid future resizes
146 desiredsize
+= 1024 + prepend
;
148 // desired size must be at least the size of our current
149 // data (in case of external data), as well as the size
150 // of our desired prepend space
151 if( desiredsize
< (m_dataSize
+ prepend
) )
152 desiredsize
= m_dataSize
+ prepend
;
154 // setup new zeroed buffer... reuse m_memBlock if it
155 // exists (see operator=())
156 unsigned char *newbuf
= 0;
157 if( m_memBlock
&& m_blockSize
>= desiredsize
) {
161 newbuf
= new unsigned char[desiredsize
];
162 memset(newbuf
, 0, desiredsize
);
165 // copy valid data over
167 memcpy(newbuf
+ prepend
, m_externalData
, m_dataSize
);
169 // not external anymore
173 memcpy(newbuf
+ prepend
, m_dataStart
, m_dataSize
);
176 // install new buffer if we've allocated a new one
177 if( m_memBlock
!= newbuf
) {
178 delete [] m_memBlock
;
180 m_blockSize
= desiredsize
;
183 // update m_dataStart
184 m_dataStart
= m_memBlock
+ prepend
;
188 size_t Data::AvailablePrependSpace() const
193 return m_dataStart
- m_memBlock
;
196 void Data::InputHexLine(istream
&is
)
198 ios_format_state
state(is
);
200 unsigned int values
[16];
204 is
>> setbase(16) >> address
;
206 return; // nothing to do
208 is
.ignore(); // eat the ':'
210 while( is
&& index
< 16 ) {
211 is
>> setbase(16) >> values
[index
];
216 dout("InputHexLine: read " << index
<< " bytes");
218 MakeSpace(address
+ index
); // make space for the new
219 m_dataSize
= std::max(address
+ index
, m_dataSize
);
221 m_dataStart
[address
+ index
] = (unsigned char) values
[index
];
225 void Data::DumpHexLine(ostream
&os
, size_t index
, size_t size
) const
227 ios_format_state
state(os
);
233 os
<< setbase(16) << setfill('0') << setw(8)
237 for( size_t i
= 0; i
< size
; i
++ ) {
238 if( (index
+i
) < GetSize() ) {
239 os
<< setbase(16) << setfill('0')
240 << setw(2) << setprecision(2)
241 << (unsigned int) GetData()[index
+ i
] << ' ';
250 locale loc
= os
.getloc();
252 for( size_t i
= 0; i
< size
&& (index
+i
) < GetSize(); i
++ ) {
253 ostream::traits_type::char_type c
= GetData()[index
+ i
];
254 os
<< setbase(10) << (char) (std::isprint(c
, loc
) ? c
: '.');
261 void Data::DumpHex(ostream
&os
) const
263 for( size_t address
= 0; address
< GetSize(); address
+= 16 ) {
264 DumpHexLine(os
, address
, 16);
268 unsigned char * Data::GetBuffer(size_t requiredsize
)
270 if( requiredsize
== 0 ) {
271 // handle default, use data size
272 requiredsize
= m_dataSize
;
275 MakeSpace(requiredsize
);
279 /// Returns size of buffer returned by GetBuffer(). Note that this does not
280 /// include available prepend space.
281 size_t Data::GetBufSize() const
286 return m_blockSize
- (m_dataStart
- m_memBlock
);
289 void Data::ReleaseBuffer(int datasize
)
291 if( datasize
< 0 && datasize
!= -1)
292 throw std::logic_error("Data::ReleaseBuffer() argument must be -1 or >= 0");
294 throw std::logic_error("Data::ReleaseBuffer() must be called after GetBuffer()");
295 if( !(datasize
== -1 || (unsigned int)datasize
<= GetBufSize()) )
296 throw std::logic_error("Data::ReleaseBuffer() must be called with a size smaller than the original buffer requested");
298 if( datasize
>= 0 ) {
299 m_dataSize
= datasize
;
302 // search for last non-zero value in buffer
303 m_dataSize
= GetBufSize() - 1;
304 while( m_dataSize
&& m_dataStart
[m_dataSize
] == 0 )
309 /// Append bytes of data based on str
310 void Data::AppendHexString(const char *str
)
312 MakeSpace(m_dataSize
+ 512);
314 std::istringstream
iss(str
);
316 while( iss
>> hex
>> byte
) {
317 MakeSpace(m_dataSize
+ 1);
318 m_dataStart
[m_dataSize
] = (unsigned char) byte
;
323 /// set buffer to 0 and remove all data
327 memset(m_memBlock
, 0, m_blockSize
);
331 Data
& Data::operator=(const Data
&other
)
336 if( other
.m_external
) {
337 // just copy over the pointers
338 m_externalData
= other
.m_externalData
;
339 m_external
= other
.m_external
;
340 m_dataSize
= other
.m_dataSize
;
341 m_endpoint
= other
.m_endpoint
;
344 // don't remove our current buffer, only grow it if needed
345 MakeSpace(other
.m_dataSize
);
346 memcpy(m_dataStart
, other
.m_dataStart
, other
.m_dataSize
);
348 // then copy over the data state
349 m_dataSize
= other
.m_dataSize
;
350 m_endpoint
= other
.m_endpoint
;
356 void Data::MemCpy(size_t &offset
, const void *src
, size_t size
)
358 unsigned char *pd
= GetBuffer(offset
+ size
) + offset
;
359 memcpy(pd
, src
, size
);
362 // if the new end of data is larger than m_dataSize, bump it
363 if( offset
> m_dataSize
)
367 void Data::Append(const void *buf
, size_t size
)
369 // MemCpy updates m_datasize via the offset reference
370 MemCpy(m_dataSize
, buf
, size
);
373 void Data::Prepend(const void *buf
, size_t size
)
378 memcpy(m_dataStart
, (const unsigned char*) buf
, size
);
381 /// Removes size bytes from the beginning of the buffer.
382 /// If GetSize() is less than size, then all bytes will be chopped
383 /// and GetSize() will end up 0.
384 void Data::Prechop(size_t size
)
386 // chopping all the bytes that we have?
387 if( size
>= GetSize() ) {
393 m_externalData
+= size
;
402 istream
& operator>> (istream
&is
, Data
&data
)
404 data
.InputHexLine(is
);
408 ostream
& operator<< (ostream
&os
, const Data
&data
)
415 ///////////////////////////////////////////////////////////////////////////////
418 Diff::Diff(const Data
&old
, const Data
&new_
)
419 : m_old(old
), m_new(new_
)
423 void Diff::Compare(ostream
&os
, size_t index
, size_t size
) const
425 ios_format_state
state(os
);
427 size_t min
= std::min(m_old
.GetSize(), m_new
.GetSize());
431 os
<< setbase(16) << setfill('0') << setw(8)
435 for( size_t i
= 0; i
< size
; i
++ ) {
436 size_t address
= index
+ i
;
438 // if data is available, print the diff
439 if( address
< min
) {
440 if( m_old
.GetData()[address
] != m_new
.GetData()[address
] ) {
442 os
<< setbase(16) << setfill('0')
443 << setw(2) << setprecision(2)
444 << (unsigned int) m_new
.GetData()[address
] << ' ';
447 // same, just print spaces
452 // one of the buffers is shorter...
453 if( address
< m_new
.GetSize() ) {
454 // new still has data, print it
455 os
<< setbase(16) << setfill('0')
456 << setw(2) << setprecision(2)
457 << (unsigned int) m_new
.GetData()[address
]
460 else if( address
< m_old
.GetSize() ) {
461 // new is out of data and old still has some
465 // no more data, just print spaces
471 // printable data, just dump new
472 if( Data::PrintAscii() ) {
474 for( size_t i
= 0; i
< size
&& (index
+i
) < m_new
.GetSize(); i
++ ) {
475 int c
= m_new
.GetData()[index
+ i
];
476 os
<< setbase(10) << (char) (isprint(c
) ? c
: '.');
483 void Diff::Dump(std::ostream
&os
) const
485 ios_format_state
state(os
);
487 if( m_old
.GetSize() != m_new
.GetSize() )
488 os
<< "sizes differ: "
489 << m_old
.GetSize() << " != " << m_new
.GetSize() << endl
;
491 size_t max
= std::max(m_old
.GetSize(), m_new
.GetSize());
492 for( size_t i
= 0; i
< max
; i
+= 16 ) {
493 m_old
.DumpHexLine(os
, i
, 16);
498 ostream
& operator<< (ostream
&os
, const Diff
&diff
)
505 ///////////////////////////////////////////////////////////////////////////////
508 /// Default constructor, constructs an empty local Data object
510 : m_version(REC_VERSION_1
) // a reasonable default for now
511 , m_localData(new Data
)
512 , m_data(*m_localData
)
517 /// Constructs a local Data object that points to external memory
518 DBData::DBData(const void *ValidData
, size_t size
)
519 : m_version(REC_VERSION_1
) // a reasonable default for now
520 , m_localData(new Data
)
521 , m_data(*m_localData
)
525 DBData::DBData(RecordFormatVersion ver
,
526 const std::string
&dbName
,
530 const void *ValidData
,
535 , m_uniqueId(uniqueId
)
537 , m_localData(new Data(ValidData
, size
))
538 , m_data(*m_localData
)
542 /// If copy == false, constructs an external Data object, no local.
543 /// If copy == true, constructs an internal Data object copy
544 DBData::DBData(Data
&externalData
, bool copy
)
545 : m_version(REC_VERSION_1
) // a reasonable default for now
546 , m_localData(copy
? new Data(externalData
) : 0)
547 , m_data(copy
? *m_localData
: externalData
)
551 DBData::DBData(RecordFormatVersion ver
,
552 const std::string
&dbName
,
561 , m_uniqueId(uniqueId
)
563 , m_localData(copy
? new Data(externalData
) : 0)
564 , m_data(copy
? *m_localData
: externalData
)
573 Data
& DBData::UseData()
575 // make sure m_data is not external anymore
577 return m_data
; // return it
580 // Note: this copy operator does not change what m_data references...
581 // whatever m_data references in the constructor is what will be changed
583 // Note also that the copy *will* involve a memcpy, and maybe a memory
584 // allocation as well.
585 DBData
& DBData::operator=(const DBData
&other
)
590 // copy the data block
591 m_data
= other
.m_data
;
599 ///////////////////////////////////////////////////////////////////////////////
602 static bool IsEndpointStart(const std::string
&line
, int &endpoint
)
604 if( strncmp(line
.c_str(), "sep: ", 5) == 0 ||
605 strncmp(line
.c_str(), "rep: ", 5) == 0 )
607 endpoint
= atoi(line
.c_str() + 5);
613 bool LoadDataArray(const string
&filename
, std::vector
<Data
> &array
)
615 ifstream
in(filename
.c_str());
616 return ReadDataArray(in
, array
);
619 bool ReadDataArray(std::istream
&is
, std::vector
<Data
> &array
)
624 bool bInEndpoint
= false;
625 unsigned int nCurrent
= 0;
626 size_t nLargestSize
= 0x100;
632 if( IsHexData(line
) ) {
633 istringstream
sline(line
);
634 sline
>> array
[nCurrent
];
638 nLargestSize
= std::max(nLargestSize
,
639 array
[nCurrent
].GetBufSize());
644 // check if this line starts a new endpoint
645 if( IsEndpointStart(line
, endpoint
) ) {
647 Data
chunk(endpoint
, nLargestSize
);
648 array
.push_back(chunk
);
649 nCurrent
= array
.size() - 1;