252 lines
7.4 KiB
C++
252 lines
7.4 KiB
C++
#include "chunks.h"
|
|
|
|
#define BUFFER_SIZE 0x10000
|
|
#define CHUNK_SIZE 0x1000
|
|
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)
|
|
|
|
/*this file is modified by wingsummer in order to fit the QHexView*/
|
|
|
|
// ***************************************** Constructors and file settings
|
|
|
|
Chunks::Chunks(QObject *parent) : QObject(parent) {}
|
|
|
|
Chunks::Chunks(QIODevice *ioDevice, QObject *parent) : QObject(parent) {
|
|
setIODevice(ioDevice);
|
|
}
|
|
|
|
Chunks::~Chunks() {}
|
|
|
|
bool Chunks::setIODevice(QIODevice *ioDevice) {
|
|
if (ioDevice && ioDevice->isOpen()) {
|
|
ioDevice->setParent(this);
|
|
_size = ioDevice->size();
|
|
_ioDevice = ioDevice;
|
|
} else {
|
|
return false;
|
|
}
|
|
_chunks.clear();
|
|
_pos = 0;
|
|
return true;
|
|
}
|
|
|
|
// ***************************************** Getting data out of Chunks
|
|
|
|
QByteArray Chunks::data(qsizetype pos, qsizetype maxSize) {
|
|
qsizetype ioDelta = 0;
|
|
qsizetype chunkIdx = 0;
|
|
|
|
Chunk chunk;
|
|
QByteArray buffer;
|
|
|
|
// Do some checks and some arrangements
|
|
|
|
if (pos >= _size)
|
|
return buffer;
|
|
|
|
if (maxSize < 0)
|
|
maxSize = _size;
|
|
else if ((pos + maxSize) > _size)
|
|
maxSize = _size - pos;
|
|
|
|
while (maxSize > 0) {
|
|
chunk.absPos = std::numeric_limits<qsizetype>::max();
|
|
bool chunksLoopOngoing = true;
|
|
while ((chunkIdx < _chunks.count()) && chunksLoopOngoing) {
|
|
// In this section, we track changes before our required data and
|
|
// we take the editdet data, if availible. ioDelta is a difference
|
|
// counter to justify the read pointer to the original data, if
|
|
// data in between was deleted or inserted.
|
|
|
|
chunk = _chunks[chunkIdx];
|
|
if (chunk.absPos > pos)
|
|
chunksLoopOngoing = false;
|
|
else {
|
|
chunkIdx += 1;
|
|
qint64 count;
|
|
qint64 chunkOfs = qint64(pos - chunk.absPos);
|
|
if (maxSize > (chunk.data.size() - chunkOfs)) {
|
|
count = qint64(chunk.data.size()) - chunkOfs;
|
|
ioDelta += CHUNK_SIZE - quint64(chunk.data.size());
|
|
} else
|
|
count = maxSize;
|
|
if (count > 0) {
|
|
buffer += chunk.data.mid(int(chunkOfs), int(count));
|
|
maxSize -= count;
|
|
pos += quint64(count);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((maxSize > 0) && (pos < chunk.absPos)) {
|
|
// In this section, we read data from the original source. This only
|
|
// will happen, whe no copied data is available
|
|
|
|
qint64 byteCount;
|
|
QByteArray readBuffer;
|
|
if (chunk.absPos - pos > qsizetype(maxSize))
|
|
byteCount = maxSize;
|
|
else
|
|
byteCount = chunk.absPos - pos;
|
|
|
|
maxSize -= byteCount;
|
|
_ioDevice->seek(pos + ioDelta);
|
|
readBuffer = _ioDevice->read(byteCount);
|
|
buffer += readBuffer;
|
|
pos += quint64(readBuffer.size());
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
bool Chunks::write(QIODevice *iODevice, qsizetype pos, qsizetype count) {
|
|
if (count == -1)
|
|
count = _size;
|
|
|
|
bool ok = iODevice->isOpen() && iODevice->isWritable();
|
|
if (ok) {
|
|
for (auto idx = pos; idx < qsizetype(count); idx += BUFFER_SIZE) {
|
|
QByteArray ba = data(idx, BUFFER_SIZE);
|
|
iODevice->write(ba);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// ***************************************** Search API
|
|
|
|
qsizetype Chunks::indexOf(const QByteArray &ba, qsizetype from) {
|
|
qsizetype result = -1;
|
|
QByteArray buffer;
|
|
|
|
for (auto pos = from; (pos < _size) && (result < 0); pos += BUFFER_SIZE) {
|
|
buffer = data(pos, BUFFER_SIZE + ba.size() - 1);
|
|
int findPos = buffer.indexOf(ba);
|
|
if (findPos >= 0)
|
|
result = pos + findPos;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
qsizetype Chunks::lastIndexOf(const QByteArray &ba, qsizetype from) {
|
|
qint64 result = -1;
|
|
QByteArray buffer;
|
|
|
|
for (auto pos = from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE) {
|
|
auto sPos = pos - BUFFER_SIZE - ba.size() + 1;
|
|
/*if (sPos < 0)
|
|
sPos = 0;*/
|
|
buffer = data(sPos, pos - sPos);
|
|
auto findPos = buffer.lastIndexOf(ba);
|
|
if (findPos >= 0)
|
|
result = sPos + findPos;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ***************************************** Char manipulations
|
|
|
|
bool Chunks::insert(qsizetype pos, char b) {
|
|
if (pos > _size)
|
|
return false;
|
|
qsizetype chunkIdx;
|
|
if (pos == _size) {
|
|
chunkIdx = getChunkIndex(pos - 1);
|
|
} else
|
|
chunkIdx = getChunkIndex(pos);
|
|
auto posInBa = pos - _chunks[chunkIdx].absPos;
|
|
_chunks[chunkIdx].data.insert(int(posInBa), b);
|
|
_chunks[chunkIdx].dataChanged.insert(int(posInBa), char(1));
|
|
for (auto idx = chunkIdx + 1; idx < _chunks.size(); idx++)
|
|
_chunks[idx].absPos += 1;
|
|
_size += 1;
|
|
_pos = pos;
|
|
return true;
|
|
}
|
|
|
|
bool Chunks::overwrite(qsizetype pos, char b) {
|
|
if (pos >= _size)
|
|
return false;
|
|
auto chunkIdx = getChunkIndex(pos);
|
|
auto posInBa = pos - _chunks[chunkIdx].absPos;
|
|
_chunks[chunkIdx].data[int(posInBa)] = b;
|
|
_chunks[chunkIdx].dataChanged[int(posInBa)] = char(1);
|
|
_pos = pos;
|
|
return true;
|
|
}
|
|
|
|
bool Chunks::removeAt(qsizetype pos) {
|
|
if (pos >= _size)
|
|
return false;
|
|
auto chunkIdx = getChunkIndex(pos);
|
|
auto posInBa = pos - _chunks[chunkIdx].absPos;
|
|
_chunks[chunkIdx].data.remove(int(posInBa), 1);
|
|
_chunks[chunkIdx].dataChanged.remove(int(posInBa), 1);
|
|
for (int idx = chunkIdx + 1; idx < _chunks.size(); idx++)
|
|
_chunks[idx].absPos -= 1;
|
|
_size -= 1;
|
|
_pos = pos;
|
|
return true;
|
|
}
|
|
|
|
// ***************************************** Utility functions
|
|
|
|
char Chunks::operator[](qsizetype pos) {
|
|
auto d = data(pos, 1);
|
|
if (d.isEmpty())
|
|
return '0';
|
|
return d.at(0);
|
|
}
|
|
|
|
qsizetype Chunks::pos() { return _pos; }
|
|
|
|
qsizetype Chunks::size() { return _size; }
|
|
|
|
qsizetype Chunks::getChunkIndex(qsizetype absPos) {
|
|
// This routine checks, if there is already a copied chunk available. If so,
|
|
// it returns a reference to it. If there is no copied chunk available,
|
|
// original data will be copied into a new chunk.
|
|
|
|
qsizetype foundIdx = -1;
|
|
qsizetype insertIdx = 0;
|
|
qsizetype ioDelta = 0;
|
|
|
|
// fix the bug by wingsummer
|
|
if (absPos < 0) {
|
|
Chunk newChunk;
|
|
newChunk.data = QByteArray(CHUNK_SIZE, 0);
|
|
newChunk.absPos = 0;
|
|
newChunk.dataChanged = nullptr;
|
|
_chunks.insert(insertIdx, newChunk);
|
|
return insertIdx;
|
|
}
|
|
|
|
for (int idx = 0; idx < _chunks.size(); idx++) {
|
|
Chunk chunk = _chunks[idx];
|
|
if ((absPos >= chunk.absPos) &&
|
|
(absPos < (chunk.absPos + chunk.data.size()))) {
|
|
foundIdx = idx;
|
|
break;
|
|
}
|
|
if (absPos < chunk.absPos) {
|
|
insertIdx = idx;
|
|
break;
|
|
}
|
|
ioDelta += chunk.data.size() - CHUNK_SIZE;
|
|
insertIdx = idx + 1;
|
|
}
|
|
|
|
if (foundIdx == -1) {
|
|
Chunk newChunk;
|
|
qsizetype readAbsPos = absPos - ioDelta;
|
|
qsizetype readPos = (readAbsPos & READ_CHUNK_MASK);
|
|
_ioDevice->seek(qint64(readPos));
|
|
newChunk.data = _ioDevice->read(CHUNK_SIZE);
|
|
newChunk.absPos = absPos - (readAbsPos - readPos);
|
|
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
|
|
_chunks.insert(insertIdx, newChunk);
|
|
foundIdx = insertIdx;
|
|
}
|
|
return foundIdx;
|
|
}
|