1 #include "chunks.h"
2 #include <limits.h>
3 
4 #define NORMAL 0
5 #define HIGHLIGHTED 1
6 
7 #define BUFFER_SIZE 0x10000
8 #define CHUNK_SIZE 0x1000
9 #define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)
10 
11 // ***************************************** Constructors and file settings
12 
Chunks(QObject * parent)13 Chunks::Chunks(QObject *parent): QObject(parent)
14 {
15     QBuffer *buf = new QBuffer(this);
16     setIODevice(*buf);
17 }
18 
Chunks(QIODevice & ioDevice,QObject * parent)19 Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent)
20 {
21     setIODevice(ioDevice);
22 }
23 
setIODevice(QIODevice & ioDevice)24 bool Chunks::setIODevice(QIODevice &ioDevice)
25 {
26     _ioDevice = &ioDevice;
27     bool ok = _ioDevice->open(QIODevice::ReadOnly);
28     if (ok)   // Try to open IODevice
29     {
30         _size = _ioDevice->size();
31         _ioDevice->close();
32     }
33     else                                        // Fallback is an empty buffer
34     {
35         QBuffer *buf = new QBuffer(this);
36         _ioDevice = buf;
37         _size = 0;
38     }
39     _chunks.clear();
40     _pos = 0;
41     return ok;
42 }
43 
44 
45 // ***************************************** Getting data out of Chunks
46 
data(qint64 pos,qint64 maxSize,QByteArray * highlighted)47 QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted)
48 {
49     qint64 ioDelta = 0;
50     int chunkIdx = 0;
51 
52     Chunk chunk;
53     QByteArray buffer;
54 
55     // Do some checks and some arrangements
56     if (highlighted)
57         highlighted->clear();
58 
59     if (pos >= _size)
60         return buffer;
61 
62     if (maxSize < 0)
63         maxSize = _size;
64     else
65         if ((pos + maxSize) > _size)
66             maxSize = _size - pos;
67 
68     _ioDevice->open(QIODevice::ReadOnly);
69 
70     while (maxSize > 0)
71     {
72         chunk.absPos = LLONG_MAX;
73         bool chunksLoopOngoing = true;
74         while ((chunkIdx < _chunks.count()) && chunksLoopOngoing)
75         {
76             // In this section, we track changes before our required data and
77             // we take the editdet data, if availible. ioDelta is a difference
78             // counter to justify the read pointer to the original data, if
79             // data in between was deleted or inserted.
80 
81             chunk = _chunks[chunkIdx];
82             if (chunk.absPos > pos)
83                 chunksLoopOngoing = false;
84             else
85             {
86                 chunkIdx += 1;
87                 qint64 count;
88                 qint64 chunkOfs = pos - chunk.absPos;
89                 if (maxSize > ((qint64)chunk.data.size() - chunkOfs))
90                 {
91                     count = (qint64)chunk.data.size() - chunkOfs;
92                     ioDelta += CHUNK_SIZE - chunk.data.size();
93                 }
94                 else
95                     count = maxSize;
96                 if (count > 0)
97                 {
98                     buffer += chunk.data.mid((int)chunkOfs, (int)count);
99                     maxSize -= count;
100                     pos += count;
101                     if (highlighted)
102                         *highlighted += chunk.dataChanged.mid((int)chunkOfs, (int)count);
103                 }
104             }
105         }
106 
107         if ((maxSize > 0) && (pos < chunk.absPos))
108         {
109             // In this section, we read data from the original source. This only will
110             // happen, whe no copied data is available
111 
112             qint64 byteCount;
113             QByteArray readBuffer;
114             if ((chunk.absPos - pos) > maxSize)
115                 byteCount = maxSize;
116             else
117                 byteCount = chunk.absPos - pos;
118 
119             maxSize -= byteCount;
120             _ioDevice->seek(pos + ioDelta);
121             readBuffer = _ioDevice->read(byteCount);
122             buffer += readBuffer;
123             if (highlighted)
124                 *highlighted += QByteArray(readBuffer.size(), NORMAL);
125             pos += readBuffer.size();
126         }
127     }
128     _ioDevice->close();
129     return buffer;
130 }
131 
write(QIODevice & iODevice,qint64 pos,qint64 count)132 bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count)
133 {
134     if (count == -1)
135         count = _size;
136     bool ok = iODevice.open(QIODevice::WriteOnly);
137     if (ok)
138     {
139         for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE)
140         {
141             QByteArray ba = data(idx, BUFFER_SIZE);
142             iODevice.write(ba);
143         }
144         iODevice.close();
145     }
146     return ok;
147 }
148 
149 
150 // ***************************************** Set and get highlighting infos
151 
setDataChanged(qint64 pos,bool dataChanged)152 void Chunks::setDataChanged(qint64 pos, bool dataChanged)
153 {
154     if ((pos < 0) || (pos >= _size))
155         return;
156     int chunkIdx = getChunkIndex(pos);
157     qint64 posInBa = pos - _chunks[chunkIdx].absPos;
158     _chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged);
159 }
160 
dataChanged(qint64 pos)161 bool Chunks::dataChanged(qint64 pos)
162 {
163     QByteArray highlighted;
164     data(pos, 1, &highlighted);
165     return bool(highlighted.at(0));
166 }
167 
168 
169 // ***************************************** Search API
170 
indexOf(const QByteArray & ba,qint64 from)171 qint64 Chunks::indexOf(const QByteArray &ba, qint64 from)
172 {
173     qint64 result = -1;
174     QByteArray buffer;
175 
176     for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE)
177     {
178         buffer = data(pos, BUFFER_SIZE + ba.size() - 1);
179         int findPos = (int)buffer.indexOf(ba);
180         if (findPos >= 0)
181             result = pos + (qint64)findPos;
182     }
183     return result;
184 }
185 
lastIndexOf(const QByteArray & ba,qint64 from)186 qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from)
187 {
188     qint64 result = -1;
189     QByteArray buffer;
190 
191     for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE)
192     {
193         qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1;
194         if (sPos < 0)
195             sPos = 0;
196         buffer = data(sPos, pos - sPos);
197         int findPos = (int)buffer.lastIndexOf(ba);
198         if (findPos >= 0)
199             result = sPos + (qint64)findPos;
200     }
201     return result;
202 }
203 
204 
205 // ***************************************** Char manipulations
206 
insert(qint64 pos,char b)207 bool Chunks::insert(qint64 pos, char b)
208 {
209     if ((pos < 0) || (pos > _size))
210         return false;
211     int chunkIdx;
212     if (pos == _size)
213         chunkIdx = getChunkIndex(pos-1);
214     else
215         chunkIdx = getChunkIndex(pos);
216     qint64 posInBa = pos - _chunks[chunkIdx].absPos;
217     _chunks[chunkIdx].data.insert((int)posInBa, b);
218     _chunks[chunkIdx].dataChanged.insert((int)posInBa, char(1));
219     for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
220         _chunks[idx].absPos += 1;
221     _size += 1;
222     _pos = pos;
223     return true;
224 }
225 
overwrite(qint64 pos,char b)226 bool Chunks::overwrite(qint64 pos, char b)
227 {
228     if ((pos < 0) || (pos >= _size))
229         return false;
230     int chunkIdx = getChunkIndex(pos);
231     qint64 posInBa = pos - _chunks[chunkIdx].absPos;
232     _chunks[chunkIdx].data[(int)posInBa] = b;
233     _chunks[chunkIdx].dataChanged[(int)posInBa] = char(1);
234     _pos = pos;
235     return true;
236 }
237 
removeAt(qint64 pos)238 bool Chunks::removeAt(qint64 pos)
239 {
240     if ((pos < 0) || (pos >= _size))
241         return false;
242     int chunkIdx = getChunkIndex(pos);
243     qint64 posInBa = pos - _chunks[chunkIdx].absPos;
244     _chunks[chunkIdx].data.remove((int)posInBa, 1);
245     _chunks[chunkIdx].dataChanged.remove((int)posInBa, 1);
246     for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
247         _chunks[idx].absPos -= 1;
248     _size -= 1;
249     _pos = pos;
250     return true;
251 }
252 
253 
254 // ***************************************** Utility functions
255 
operator [](qint64 pos)256 char Chunks::operator[](qint64 pos)
257 {
258     return data(pos, 1)[0];
259 }
260 
pos()261 qint64 Chunks::pos()
262 {
263     return _pos;
264 }
265 
size()266 qint64 Chunks::size()
267 {
268     return _size;
269 }
270 
getChunkIndex(qint64 absPos)271 int Chunks::getChunkIndex(qint64 absPos)
272 {
273     // This routine checks, if there is already a copied chunk available. If os, it
274     // returns a reference to it. If there is no copied chunk available, original
275     // data will be copied into a new chunk.
276 
277     int foundIdx = -1;
278     int insertIdx = 0;
279     qint64 ioDelta = 0;
280 
281 
282     for (int idx=0; idx < _chunks.size(); idx++)
283     {
284         Chunk chunk = _chunks[idx];
285         if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size())))
286         {
287             foundIdx = idx;
288             break;
289         }
290         if (absPos < chunk.absPos)
291         {
292             insertIdx = idx;
293             break;
294         }
295         ioDelta += chunk.data.size() - CHUNK_SIZE;
296         insertIdx = idx + 1;
297     }
298 
299     if (foundIdx == -1)
300     {
301         Chunk newChunk;
302         qint64 readAbsPos = absPos - ioDelta;
303         qint64 readPos = (readAbsPos & READ_CHUNK_MASK);
304         _ioDevice->open(QIODevice::ReadOnly);
305         _ioDevice->seek(readPos);
306         newChunk.data = _ioDevice->read(CHUNK_SIZE);
307         _ioDevice->close();
308         newChunk.absPos = absPos - (readAbsPos - readPos);
309         newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
310         _chunks.insert(insertIdx, newChunk);
311         foundIdx = insertIdx;
312     }
313     return foundIdx;
314 }
315 
316 
317 #ifdef MODUL_TEST
chunkSize()318 int Chunks::chunkSize()
319 {
320     return _chunks.size();
321 }
322 
323 #endif
324