3 /// Classes to help manage pre-determined data files.
7 Copyright (C) 2005-2013, 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 #include "ios_state.h"
34 //#define __DEBUG_MODE__
43 inline bool IsHexData(const std::string
&s
)
45 const char *str
= s
.c_str();
46 for( int i
= 0; i
< 4 && *str
; str
++, i
++ )
50 for( int i
= 0; i
< 8 && *str
; str
++, i
++ ) {
51 const char *hexchars
= "0123456789abcdef";
52 if( strchr(hexchars
, *str
) == NULL
)
64 ///////////////////////////////////////////////////////////////////////////////
67 bool Data::bPrintAscii
= true;
70 : m_memBlock(new unsigned char[BARRY_DATA_DEFAULT_SIZE
+ BARRY_DATA_DEFAULT_PREPEND_SIZE
])
71 , m_blockSize(BARRY_DATA_DEFAULT_SIZE
+ BARRY_DATA_DEFAULT_PREPEND_SIZE
)
72 , m_dataStart(m_memBlock
+ BARRY_DATA_DEFAULT_PREPEND_SIZE
)
78 memset(m_memBlock
, 0, m_blockSize
);
81 Data::Data(int endpoint
, size_t startsize
, size_t prependsize
)
82 : m_memBlock(new unsigned char[startsize
+ prependsize
])
83 , m_blockSize(startsize
+ prependsize
)
84 , m_dataStart(m_memBlock
+ prependsize
)
88 , m_endpoint(endpoint
)
90 memset(m_memBlock
, 0, m_blockSize
);
93 Data::Data(const void *ValidData
, size_t size
)
98 , m_externalData((const unsigned char*)ValidData
)
104 Data::Data(const Data
&other
)
105 : m_memBlock(other
.m_blockSize
? new unsigned char[other
.m_blockSize
] : 0)
106 , m_blockSize(other
.m_blockSize
)
107 , m_dataStart(m_memBlock
+ other
.AvailablePrependSpace())
108 , m_dataSize(other
.m_dataSize
)
109 , m_externalData(other
.m_externalData
)
110 , m_external(other
.m_external
)
111 , m_endpoint(other
.m_endpoint
)
113 // copy over the raw data
115 memcpy(m_memBlock
, other
.m_memBlock
, other
.m_blockSize
);
120 delete [] m_memBlock
;
126 /// Reallocates buffers so that it is safe to write desiredsize data
127 /// to m_dataStart after it returns. All existing data is preserved.
129 /// This function also performs any copy on write needed.
131 /// If desiredprepend is nonzero, then at least desiredprepend bytes
132 /// of prepend space will exist in the buffer after return.
133 /// If desiredprepend is zero, defaults will be used if needed.
135 void Data::MakeSpace(size_t desiredsize
, size_t desiredprepend
)
137 // use a default prepend size if none currently available
138 size_t prepend
= std::max(AvailablePrependSpace(), desiredprepend
);
142 // GetBufSize() returns 0 if m_external is true
143 if( GetBufSize() < (desiredsize
+ prepend
) ||
144 (desiredprepend
&& AvailablePrependSpace() < desiredprepend
) )
146 // get a proper chunk to avoid future resizes
147 desiredsize
+= 1024 + prepend
;
149 // desired size must be at least the size of our current
150 // data (in case of external data), as well as the size
151 // of our desired prepend space
152 if( desiredsize
< (m_dataSize
+ prepend
) )
153 desiredsize
= m_dataSize
+ prepend
;
155 // setup new zeroed buffer... reuse m_memBlock if it
156 // exists (see operator=())
157 unsigned char *newbuf
= 0;
158 if( m_memBlock
&& m_blockSize
>= desiredsize
) {
162 newbuf
= new unsigned char[desiredsize
];
163 memset(newbuf
, 0, desiredsize
);
166 // copy valid data over
168 memcpy(newbuf
+ prepend
, m_externalData
, m_dataSize
);
170 // not external anymore
174 memcpy(newbuf
+ prepend
, m_dataStart
, m_dataSize
);
177 // install new buffer if we've allocated a new one
178 if( m_memBlock
!= newbuf
) {
179 delete [] m_memBlock
;
181 m_blockSize
= desiredsize
;
184 // update m_dataStart
185 m_dataStart
= m_memBlock
+ prepend
;
189 size_t Data::AvailablePrependSpace() const
194 return m_dataStart
- m_memBlock
;
197 void Data::InputHexLine(istream
&is
)
199 ios_format_state
state(is
);
201 unsigned int values
[16];
205 is
>> setbase(16) >> address
;
207 return; // nothing to do
209 is
.ignore(); // eat the ':'
211 while( is
&& index
< 16 ) {
212 is
>> setbase(16) >> values
[index
];
217 dout("InputHexLine: read " << index
<< " bytes");
219 MakeSpace(address
+ index
); // make space for the new
220 m_dataSize
= std::max(address
+ index
, m_dataSize
);
222 m_dataStart
[address
+ index
] = (unsigned char) values
[index
];
226 void Data::DumpHexLine(ostream
&os
, size_t index
, size_t size
) const
228 ios_format_state
state(os
);
234 os
<< setbase(16) << setfill('0') << setw(8)
238 for( size_t i
= 0; i
< size
; i
++ ) {
239 if( (index
+i
) < GetSize() ) {
240 os
<< setbase(16) << setfill('0')
241 << setw(2) << setprecision(2)
242 << (unsigned int) GetData()[index
+ i
] << ' ';
251 locale loc
= os
.getloc();
253 for( size_t i
= 0; i
< size
&& (index
+i
) < GetSize(); i
++ ) {
254 ostream::traits_type::char_type c
= GetData()[index
+ i
];
255 os
<< setbase(10) << (char) (std::isprint(c
, loc
) ? c
: '.');
262 void Data::DumpHex(ostream
&os
) const
264 for( size_t address
= 0; address
< GetSize(); address
+= 16 ) {
265 DumpHexLine(os
, address
, 16);
269 unsigned char * Data::GetBuffer(size_t requiredsize
)
271 if( requiredsize
== 0 ) {
272 // handle default, use data size
273 requiredsize
= m_dataSize
;
276 MakeSpace(requiredsize
);
280 /// Returns size of buffer returned by GetBuffer(). Note that this does not
281 /// include available prepend space.
282 size_t Data::GetBufSize() const
287 return m_blockSize
- (m_dataStart
- m_memBlock
);
290 void Data::ReleaseBuffer(int datasize
)
292 if( datasize
< 0 && datasize
!= -1)
293 throw std::logic_error(_("Data::ReleaseBuffer() argument must be -1 or >= 0"));
295 throw std::logic_error(_("Data::ReleaseBuffer() must be called after GetBuffer()"));
296 if( !(datasize
== -1 || (unsigned int)datasize
<= GetBufSize()) )
297 throw std::logic_error(_("Data::ReleaseBuffer() must be called with a size smaller than the original buffer requested"));
299 if( datasize
>= 0 ) {
300 m_dataSize
= datasize
;
303 // search for last non-zero value in buffer
304 m_dataSize
= GetBufSize() - 1;
305 while( m_dataSize
&& m_dataStart
[m_dataSize
] == 0 )
310 /// Append bytes of data based on str
311 void Data::AppendHexString(const char *str
)
313 MakeSpace(m_dataSize
+ 512);
315 std::istringstream
iss(str
);
317 while( iss
>> hex
>> byte
) {
318 MakeSpace(m_dataSize
+ 1);
319 m_dataStart
[m_dataSize
] = (unsigned char) byte
;
324 /// set buffer to 0 and remove all data
328 memset(m_memBlock
, 0, m_blockSize
);
332 Data
& Data::operator=(const Data
&other
)
337 if( other
.m_external
) {
338 // just copy over the pointers
339 m_externalData
= other
.m_externalData
;
340 m_external
= other
.m_external
;
341 m_dataSize
= other
.m_dataSize
;
342 m_endpoint
= other
.m_endpoint
;
345 // don't remove our current buffer, only grow it if needed
346 MakeSpace(other
.m_dataSize
);
347 memcpy(m_dataStart
, other
.m_dataStart
, other
.m_dataSize
);
349 // then copy over the data state
350 m_dataSize
= other
.m_dataSize
;
351 m_endpoint
= other
.m_endpoint
;
357 void Data::MemCpy(size_t &offset
, const void *src
, size_t size
)
359 unsigned char *pd
= GetBuffer(offset
+ size
) + offset
;
360 memcpy(pd
, src
, size
);
363 // if the new end of data is larger than m_dataSize, bump it
364 if( offset
> m_dataSize
)
368 void Data::Append(const void *buf
, size_t size
)
370 // MemCpy updates m_datasize via the offset reference
371 MemCpy(m_dataSize
, buf
, size
);
374 void Data::Prepend(const void *buf
, size_t size
)
379 memcpy(m_dataStart
, (const unsigned char*) buf
, size
);
382 /// Removes size bytes from the beginning of the buffer.
383 /// If GetSize() is less than size, then all bytes will be chopped
384 /// and GetSize() will end up 0.
385 void Data::Prechop(size_t size
)
387 // chopping all the bytes that we have?
388 if( size
>= GetSize() ) {
394 m_externalData
+= size
;
403 istream
& operator>> (istream
&is
, Data
&data
)
405 data
.InputHexLine(is
);
409 ostream
& operator<< (ostream
&os
, const Data
&data
)
416 ///////////////////////////////////////////////////////////////////////////////
419 Diff::Diff(const Data
&old
, const Data
&new_
)
420 : m_old(old
), m_new(new_
)
424 void Diff::Compare(ostream
&os
, size_t index
, size_t size
) const
426 ios_format_state
state(os
);
428 size_t min
= std::min(m_old
.GetSize(), m_new
.GetSize());
432 os
<< setbase(16) << setfill('0') << setw(8)
436 for( size_t i
= 0; i
< size
; i
++ ) {
437 size_t address
= index
+ i
;
439 // if data is available, print the diff
440 if( address
< min
) {
441 if( m_old
.GetData()[address
] != m_new
.GetData()[address
] ) {
443 os
<< setbase(16) << setfill('0')
444 << setw(2) << setprecision(2)
445 << (unsigned int) m_new
.GetData()[address
] << ' ';
448 // same, just print spaces
453 // one of the buffers is shorter...
454 if( address
< m_new
.GetSize() ) {
455 // new still has data, print it
456 os
<< setbase(16) << setfill('0')
457 << setw(2) << setprecision(2)
458 << (unsigned int) m_new
.GetData()[address
]
461 else if( address
< m_old
.GetSize() ) {
462 // new is out of data and old still has some
466 // no more data, just print spaces
472 // printable data, just dump new
473 if( Data::PrintAscii() ) {
475 for( size_t i
= 0; i
< size
&& (index
+i
) < m_new
.GetSize(); i
++ ) {
476 int c
= m_new
.GetData()[index
+ i
];
477 os
<< setbase(10) << (char) (isprint(c
) ? c
: '.');
484 void Diff::Dump(std::ostream
&os
) const
486 ios_format_state
state(os
);
488 if( m_old
.GetSize() != m_new
.GetSize() )
489 os
<< _("sizes differ: ")
490 << m_old
.GetSize() << " != " << m_new
.GetSize() << endl
;
492 size_t max
= std::max(m_old
.GetSize(), m_new
.GetSize());
493 for( size_t i
= 0; i
< max
; i
+= 16 ) {
494 m_old
.DumpHexLine(os
, i
, 16);
499 ostream
& operator<< (ostream
&os
, const Diff
&diff
)
506 ///////////////////////////////////////////////////////////////////////////////
509 /// Default constructor, constructs an empty local Data object
511 : m_version(REC_VERSION_1
) // a reasonable default for now
512 , m_localData(new Data
)
513 , m_data(*m_localData
)
517 /// Copy constructor - always creates an internal Data object, and
518 /// uses Data object's copy constructor to make it.
519 /// Copies all meta data as well.
520 DBData::DBData(const DBData
&other
)
521 : m_version(other
.m_version
)
522 , m_dbName(other
.m_dbName
)
523 , m_recType(other
.m_recType
)
524 , m_uniqueId(other
.m_uniqueId
)
525 , m_offset(other
.m_offset
)
526 , m_localData(new Data(other
.m_data
))
527 , m_data(*m_localData
)
531 /// Constructs a local Data object that points to external memory
532 DBData::DBData(const void *ValidData
, size_t size
)
533 : m_version(REC_VERSION_1
) // a reasonable default for now
534 , m_localData(new Data
)
535 , m_data(*m_localData
)
539 DBData::DBData(RecordFormatVersion ver
,
540 const std::string
&dbName
,
544 const void *ValidData
,
549 , m_uniqueId(uniqueId
)
551 , m_localData(new Data(ValidData
, size
))
552 , m_data(*m_localData
)
556 /// If copy == false, constructs an external Data object, no local.
557 /// If copy == true, constructs an internal Data object copy
558 DBData::DBData(Data
&externalData
, bool copy
)
559 : m_version(REC_VERSION_1
) // a reasonable default for now
560 , m_localData(copy
? new Data(externalData
) : 0)
561 , m_data(copy
? *m_localData
: externalData
)
565 DBData::DBData(RecordFormatVersion ver
,
566 const std::string
&dbName
,
575 , m_uniqueId(uniqueId
)
577 , m_localData(copy
? new Data(externalData
) : 0)
578 , m_data(copy
? *m_localData
: externalData
)
587 Data
& DBData::UseData()
589 // make sure m_data is not external anymore
591 return m_data
; // return it
594 // Note: this copy operator does not change what m_data references...
595 // whatever m_data references in the constructor is what will be changed
597 // Note also that the copy *will* involve a memcpy, and maybe a memory
598 // allocation as well.
599 DBData
& DBData::operator=(const DBData
&other
)
604 // copy the data block
605 m_data
= other
.m_data
;
613 ///////////////////////////////////////////////////////////////////////////////
616 static bool IsEndpointStart(const std::string
&line
, int &endpoint
)
618 if( strncmp(line
.c_str(), "sep: ", 5) == 0 ||
619 strncmp(line
.c_str(), "rep: ", 5) == 0 )
621 endpoint
= atoi(line
.c_str() + 5);
627 bool LoadDataArray(const string
&filename
, std::vector
<Data
> &array
)
629 ifstream
in(filename
.c_str());
630 return ReadDataArray(in
, array
);
633 bool ReadDataArray(std::istream
&is
, std::vector
<Data
> &array
)
638 bool bInEndpoint
= false;
639 unsigned int nCurrent
= 0;
640 size_t nLargestSize
= 0x100;
646 if( IsHexData(line
) ) {
647 istringstream
sline(line
);
648 sline
>> array
[nCurrent
];
652 nLargestSize
= std::max(nLargestSize
,
653 array
[nCurrent
].GetBufSize());
658 // check if this line starts a new endpoint
659 if( IsEndpointStart(line
, endpoint
) ) {
661 Data
chunk(endpoint
, nLargestSize
);
662 array
.push_back(chunk
);
663 nCurrent
= array
.size() - 1;