debian: added giffgaff chatscripts
[barry.git] / src / data.cc
blob027d0ae296893f5f0e94e510fec5e044cb249250
1 ///
2 /// \file data.cc
3 /// Classes to help manage pre-determined data files.
4 ///
6 /*
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.
22 #include "i18n.h"
23 #include "data.h"
24 #include <fstream>
25 #include <sstream>
26 #include <iomanip>
27 #include <string>
28 #include <stdexcept>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <locale>
32 #include "ios_state.h"
34 //#define __DEBUG_MODE__
35 #include "debug.h"
38 using namespace std;
41 namespace Barry {
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++ )
47 if( *str != ' ' )
48 return false;
50 for( int i = 0; i < 8 && *str; str++, i++ ) {
51 const char *hexchars = "0123456789abcdef";
52 if( strchr(hexchars, *str) == NULL )
53 return false;
56 if( *str != ':' )
57 return false;
59 return true;
64 ///////////////////////////////////////////////////////////////////////////////
65 // Data class
67 bool Data::bPrintAscii = true;
69 Data::Data()
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)
73 , m_dataSize(0)
74 , m_externalData(0)
75 , m_external(false)
76 , m_endpoint(-1)
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)
85 , m_dataSize(0)
86 , m_externalData(0)
87 , m_external(false)
88 , m_endpoint(endpoint)
90 memset(m_memBlock, 0, m_blockSize);
93 Data::Data(const void *ValidData, size_t size)
94 : m_memBlock(0)
95 , m_blockSize(0)
96 , m_dataStart(0)
97 , m_dataSize(size)
98 , m_externalData((const unsigned char*)ValidData)
99 , m_external(true)
100 , m_endpoint(-1)
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
114 if( !m_external )
115 memcpy(m_memBlock, other.m_memBlock, other.m_blockSize);
118 Data::~Data()
120 delete [] m_memBlock;
124 // MakeSpace
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);
139 if( !prepend )
140 prepend = 0x100;
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 ) {
159 newbuf = m_memBlock;
161 else {
162 newbuf = new unsigned char[desiredsize];
163 memset(newbuf, 0, desiredsize);
166 // copy valid data over
167 if( m_external ) {
168 memcpy(newbuf + prepend, m_externalData, m_dataSize);
170 // not external anymore
171 m_external = false;
173 else {
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;
180 m_memBlock = newbuf;
181 m_blockSize = desiredsize;
184 // update m_dataStart
185 m_dataStart = m_memBlock + prepend;
189 size_t Data::AvailablePrependSpace() const
191 if( m_external )
192 return 0;
193 else
194 return m_dataStart - m_memBlock;
197 void Data::InputHexLine(istream &is)
199 ios_format_state state(is);
201 unsigned int values[16];
202 size_t index = 0;
204 size_t address;
205 is >> setbase(16) >> address;
206 if( !is )
207 return; // nothing to do
209 is.ignore(); // eat the ':'
211 while( is && index < 16 ) {
212 is >> setbase(16) >> values[index];
213 if( is )
214 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);
221 while( index-- )
222 m_dataStart[address + index] = (unsigned char) values[index];
223 return;
226 void Data::DumpHexLine(ostream &os, size_t index, size_t size) const
228 ios_format_state state(os);
230 os.setf(ios::right);
232 // index
233 os << " ";
234 os << setbase(16) << setfill('0') << setw(8)
235 << index << ": ";
237 // hex byte data
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] << ' ';
244 else {
245 os << " ";
249 // printable data
250 if( bPrintAscii ) {
251 locale loc = os.getloc();
252 os << ' ';
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 : '.');
259 os << "\n";
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);
277 return m_dataStart;
280 /// Returns size of buffer returned by GetBuffer(). Note that this does not
281 /// include available prepend space.
282 size_t Data::GetBufSize() const
284 if( m_external )
285 return 0;
286 else
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"));
294 if( m_external )
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;
302 else {
303 // search for last non-zero value in buffer
304 m_dataSize = GetBufSize() - 1;
305 while( m_dataSize && m_dataStart[m_dataSize] == 0 )
306 --m_dataSize;
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);
316 unsigned int byte;
317 while( iss >> hex >> byte ) {
318 MakeSpace(m_dataSize + 1);
319 m_dataStart[m_dataSize] = (unsigned char) byte;
320 m_dataSize++;
324 /// set buffer to 0 and remove all data
325 void Data::Zap()
327 if( !m_external )
328 memset(m_memBlock, 0, m_blockSize);
329 m_dataSize = 0;
332 Data & Data::operator=(const Data &other)
334 if( this == &other )
335 return *this;
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;
344 else {
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;
354 return *this;
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);
361 offset += size;
363 // if the new end of data is larger than m_dataSize, bump it
364 if( offset > m_dataSize )
365 m_dataSize = offset;
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)
376 MakeSpace(0, size);
377 m_dataStart -= size;
378 m_dataSize += 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() ) {
389 QuickZap();
390 return;
393 if( m_external ) {
394 m_externalData += size;
395 m_dataSize -= size;
397 else {
398 m_dataStart += size;
399 m_dataSize -= size;
403 istream& operator>> (istream &is, Data &data)
405 data.InputHexLine(is);
406 return is;
409 ostream& operator<< (ostream &os, const Data &data)
411 data.DumpHex(os);
412 return os;
416 ///////////////////////////////////////////////////////////////////////////////
417 // Diff class
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());
430 // index
431 os << "> ";
432 os << setbase(16) << setfill('0') << setw(8)
433 << index << ": ";
435 // diff data
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] ) {
442 // differ, print hex
443 os << setbase(16) << setfill('0')
444 << setw(2) << setprecision(2)
445 << (unsigned int) m_new.GetData()[address] << ' ';
447 else {
448 // same, just print spaces
449 os << " ";
452 else {
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]
459 << ' ';
461 else if( address < m_old.GetSize() ) {
462 // new is out of data and old still has some
463 os << "XX ";
465 else {
466 // no more data, just print spaces
467 os << " ";
472 // printable data, just dump new
473 if( Data::PrintAscii() ) {
474 os << ' ';
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 : '.');
481 os << "\n";
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);
495 Compare(os, i, 16);
499 ostream& operator<< (ostream &os, const Diff &diff)
501 diff.Dump(os);
502 return os;
506 ///////////////////////////////////////////////////////////////////////////////
507 // DBData class
509 /// Default constructor, constructs an empty local Data object
510 DBData::DBData()
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,
541 uint8_t recType,
542 uint32_t uniqueId,
543 size_t offset,
544 const void *ValidData,
545 size_t size)
546 : m_version(ver)
547 , m_dbName(dbName)
548 , m_recType(recType)
549 , m_uniqueId(uniqueId)
550 , m_offset(offset)
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,
567 uint8_t recType,
568 uint32_t uniqueId,
569 size_t offset,
570 Data &externalData,
571 bool copy)
572 : m_version(ver)
573 , m_dbName(dbName)
574 , m_recType(recType)
575 , m_uniqueId(uniqueId)
576 , m_offset(offset)
577 , m_localData(copy ? new Data(externalData) : 0)
578 , m_data(copy ? *m_localData : externalData)
582 DBData::~DBData()
584 delete m_localData;
587 Data& DBData::UseData()
589 // make sure m_data is not external anymore
590 m_data.GetBuffer();
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
596 // in this copy.
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)
601 if( this == &other )
602 return *this;
604 // copy the data block
605 m_data = other.m_data;
607 // copy the metadata
608 CopyMeta(other);
610 return *this;
613 ///////////////////////////////////////////////////////////////////////////////
614 // Utility functions
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);
622 return true;
624 return false;
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)
635 if( !is )
636 return false;
638 bool bInEndpoint = false;
639 unsigned int nCurrent = 0;
640 size_t nLargestSize = 0x100;
641 while( is ) {
642 string line;
643 getline(is, line);
644 int endpoint;
645 if( bInEndpoint ) {
646 if( IsHexData(line) ) {
647 istringstream sline(line);
648 sline >> array[nCurrent];
649 continue;
651 else {
652 nLargestSize = std::max(nLargestSize,
653 array[nCurrent].GetBufSize());
654 bInEndpoint = false;
658 // check if this line starts a new endpoint
659 if( IsEndpointStart(line, endpoint) ) {
660 bInEndpoint = true;
661 Data chunk(endpoint, nLargestSize);
662 array.push_back(chunk);
663 nCurrent = array.size() - 1;
666 return true;
669 } // namespace Barry