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