desktop: CalEditDlg: fixed dialog title bar
[barry.git] / src / data.cc
blob1ea2a01890270dd1b34093b657d4f0d9956f65a8
1 ///
2 /// \file data.cc
3 /// Classes to help manage pre-determined data files.
4 ///
6 /*
7 Copyright (C) 2005-2012, 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.
22 #include "data.h"
23 #include <fstream>
24 #include <sstream>
25 #include <iomanip>
26 #include <string>
27 #include <stdexcept>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <locale>
31 #include "ios_state.h"
33 //#define __DEBUG_MODE__
34 #include "debug.h"
37 using namespace std;
40 namespace Barry {
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++ )
46 if( *str != ' ' )
47 return false;
49 for( int i = 0; i < 8 && *str; str++, i++ ) {
50 const char *hexchars = "0123456789abcdef";
51 if( strchr(hexchars, *str) == NULL )
52 return false;
55 if( *str != ':' )
56 return false;
58 return true;
63 ///////////////////////////////////////////////////////////////////////////////
64 // Data class
66 bool Data::bPrintAscii = true;
68 Data::Data()
69 : m_memBlock(new unsigned char[0x4100])
70 , m_blockSize(0x4100)
71 , m_dataStart(m_memBlock + 0x100)
72 , m_dataSize(0)
73 , m_externalData(0)
74 , m_external(false)
75 , m_endpoint(-1)
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)
84 , m_dataSize(0)
85 , m_externalData(0)
86 , m_external(false)
87 , m_endpoint(endpoint)
89 memset(m_memBlock, 0, m_blockSize);
92 Data::Data(const void *ValidData, size_t size)
93 : m_memBlock(0)
94 , m_blockSize(0)
95 , m_dataStart(0)
96 , m_dataSize(size)
97 , m_externalData((const unsigned char*)ValidData)
98 , m_external(true)
99 , m_endpoint(-1)
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
113 if( !m_external )
114 memcpy(m_memBlock, other.m_memBlock, other.m_blockSize);
117 Data::~Data()
119 delete [] m_memBlock;
123 // MakeSpace
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);
138 if( !prepend )
139 prepend = 0x100;
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 ) {
158 newbuf = m_memBlock;
160 else {
161 newbuf = new unsigned char[desiredsize];
162 memset(newbuf, 0, desiredsize);
165 // copy valid data over
166 if( m_external ) {
167 memcpy(newbuf + prepend, m_externalData, m_dataSize);
169 // not external anymore
170 m_external = false;
172 else {
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;
179 m_memBlock = newbuf;
180 m_blockSize = desiredsize;
183 // update m_dataStart
184 m_dataStart = m_memBlock + prepend;
188 size_t Data::AvailablePrependSpace() const
190 if( m_external )
191 return 0;
192 else
193 return m_dataStart - m_memBlock;
196 void Data::InputHexLine(istream &is)
198 ios_format_state state(is);
200 unsigned int values[16];
201 size_t index = 0;
203 size_t address;
204 is >> setbase(16) >> address;
205 if( !is )
206 return; // nothing to do
208 is.ignore(); // eat the ':'
210 while( is && index < 16 ) {
211 is >> setbase(16) >> values[index];
212 if( is )
213 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);
220 while( index-- )
221 m_dataStart[address + index] = (unsigned char) values[index];
222 return;
225 void Data::DumpHexLine(ostream &os, size_t index, size_t size) const
227 ios_format_state state(os);
229 os.setf(ios::right);
231 // index
232 os << " ";
233 os << setbase(16) << setfill('0') << setw(8)
234 << index << ": ";
236 // hex byte data
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] << ' ';
243 else {
244 os << " ";
248 // printable data
249 if( bPrintAscii ) {
250 locale loc = os.getloc();
251 os << ' ';
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 : '.');
258 os << "\n";
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);
276 return m_dataStart;
279 /// Returns size of buffer returned by GetBuffer(). Note that this does not
280 /// include available prepend space.
281 size_t Data::GetBufSize() const
283 if( m_external )
284 return 0;
285 else
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");
293 if( m_external )
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;
301 else {
302 // search for last non-zero value in buffer
303 m_dataSize = GetBufSize() - 1;
304 while( m_dataSize && m_dataStart[m_dataSize] == 0 )
305 --m_dataSize;
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);
315 unsigned int byte;
316 while( iss >> hex >> byte ) {
317 MakeSpace(m_dataSize + 1);
318 m_dataStart[m_dataSize] = (unsigned char) byte;
319 m_dataSize++;
323 /// set buffer to 0 and remove all data
324 void Data::Zap()
326 if( !m_external )
327 memset(m_memBlock, 0, m_blockSize);
328 m_dataSize = 0;
331 Data & Data::operator=(const Data &other)
333 if( this == &other )
334 return *this;
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;
343 else {
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;
353 return *this;
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);
360 offset += size;
362 // if the new end of data is larger than m_dataSize, bump it
363 if( offset > m_dataSize )
364 m_dataSize = offset;
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)
375 MakeSpace(0, size);
376 m_dataStart -= size;
377 m_dataSize += 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() ) {
388 QuickZap();
389 return;
392 if( m_external ) {
393 m_externalData += size;
394 m_dataSize -= size;
396 else {
397 m_dataStart += size;
398 m_dataSize -= size;
402 istream& operator>> (istream &is, Data &data)
404 data.InputHexLine(is);
405 return is;
408 ostream& operator<< (ostream &os, const Data &data)
410 data.DumpHex(os);
411 return os;
415 ///////////////////////////////////////////////////////////////////////////////
416 // Diff class
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());
429 // index
430 os << "> ";
431 os << setbase(16) << setfill('0') << setw(8)
432 << index << ": ";
434 // diff data
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] ) {
441 // differ, print hex
442 os << setbase(16) << setfill('0')
443 << setw(2) << setprecision(2)
444 << (unsigned int) m_new.GetData()[address] << ' ';
446 else {
447 // same, just print spaces
448 os << " ";
451 else {
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]
458 << ' ';
460 else if( address < m_old.GetSize() ) {
461 // new is out of data and old still has some
462 os << "XX ";
464 else {
465 // no more data, just print spaces
466 os << " ";
471 // printable data, just dump new
472 if( Data::PrintAscii() ) {
473 os << ' ';
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 : '.');
480 os << "\n";
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);
494 Compare(os, i, 16);
498 ostream& operator<< (ostream &os, const Diff &diff)
500 diff.Dump(os);
501 return os;
505 ///////////////////////////////////////////////////////////////////////////////
506 // DBData class
508 /// Default constructor, constructs an empty local Data object
509 DBData::DBData()
510 : m_version(REC_VERSION_1) // a reasonable default for now
511 , m_localData(new Data)
512 , m_data(*m_localData)
516 /// Copy constructor - always creates an internal Data object, and
517 /// uses Data object's copy constructor to make it.
518 /// Copies all meta data as well.
519 DBData::DBData(const DBData &other)
520 : m_version(other.m_version)
521 , m_dbName(other.m_dbName)
522 , m_recType(other.m_recType)
523 , m_uniqueId(other.m_uniqueId)
524 , m_offset(other.m_offset)
525 , m_localData(new Data(other.m_data))
526 , m_data(*m_localData)
530 /// Constructs a local Data object that points to external memory
531 DBData::DBData(const void *ValidData, size_t size)
532 : m_version(REC_VERSION_1) // a reasonable default for now
533 , m_localData(new Data)
534 , m_data(*m_localData)
538 DBData::DBData(RecordFormatVersion ver,
539 const std::string &dbName,
540 uint8_t recType,
541 uint32_t uniqueId,
542 size_t offset,
543 const void *ValidData,
544 size_t size)
545 : m_version(ver)
546 , m_dbName(dbName)
547 , m_recType(recType)
548 , m_uniqueId(uniqueId)
549 , m_offset(offset)
550 , m_localData(new Data(ValidData, size))
551 , m_data(*m_localData)
555 /// If copy == false, constructs an external Data object, no local.
556 /// If copy == true, constructs an internal Data object copy
557 DBData::DBData(Data &externalData, bool copy)
558 : m_version(REC_VERSION_1) // a reasonable default for now
559 , m_localData(copy ? new Data(externalData) : 0)
560 , m_data(copy ? *m_localData : externalData)
564 DBData::DBData(RecordFormatVersion ver,
565 const std::string &dbName,
566 uint8_t recType,
567 uint32_t uniqueId,
568 size_t offset,
569 Data &externalData,
570 bool copy)
571 : m_version(ver)
572 , m_dbName(dbName)
573 , m_recType(recType)
574 , m_uniqueId(uniqueId)
575 , m_offset(offset)
576 , m_localData(copy ? new Data(externalData) : 0)
577 , m_data(copy ? *m_localData : externalData)
581 DBData::~DBData()
583 delete m_localData;
586 Data& DBData::UseData()
588 // make sure m_data is not external anymore
589 m_data.GetBuffer();
590 return m_data; // return it
593 // Note: this copy operator does not change what m_data references...
594 // whatever m_data references in the constructor is what will be changed
595 // in this copy.
596 // Note also that the copy *will* involve a memcpy, and maybe a memory
597 // allocation as well.
598 DBData& DBData::operator=(const DBData &other)
600 if( this == &other )
601 return *this;
603 // copy the data block
604 m_data = other.m_data;
606 // copy the metadata
607 CopyMeta(other);
609 return *this;
612 ///////////////////////////////////////////////////////////////////////////////
613 // Utility functions
615 static bool IsEndpointStart(const std::string &line, int &endpoint)
617 if( strncmp(line.c_str(), "sep: ", 5) == 0 ||
618 strncmp(line.c_str(), "rep: ", 5) == 0 )
620 endpoint = atoi(line.c_str() + 5);
621 return true;
623 return false;
626 bool LoadDataArray(const string &filename, std::vector<Data> &array)
628 ifstream in(filename.c_str());
629 return ReadDataArray(in, array);
632 bool ReadDataArray(std::istream &is, std::vector<Data> &array)
634 if( !is )
635 return false;
637 bool bInEndpoint = false;
638 unsigned int nCurrent = 0;
639 size_t nLargestSize = 0x100;
640 while( is ) {
641 string line;
642 getline(is, line);
643 int endpoint;
644 if( bInEndpoint ) {
645 if( IsHexData(line) ) {
646 istringstream sline(line);
647 sline >> array[nCurrent];
648 continue;
650 else {
651 nLargestSize = std::max(nLargestSize,
652 array[nCurrent].GetBufSize());
653 bInEndpoint = false;
657 // check if this line starts a new endpoint
658 if( IsEndpointStart(line, endpoint) ) {
659 bInEndpoint = true;
660 Data chunk(endpoint, nLargestSize);
661 array.push_back(chunk);
662 nCurrent = array.size() - 1;
665 return true;
668 } // namespace Barry