1 /*
2 * Copyright 2001-2006, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (stefano.ceccherini@gmail.com)
8 */
9
10
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14
15 #include <utf8_functions.h>
16
17 #include <File.h>
18 #include <InterfaceDefs.h> // for B_UTF8_BULLET
19
20 #include "TextGapBuffer.h"
21
22
23 namespace BPrivate {
24
25
26 static const int32 kTextGapBufferBlockSize = 2048;
27
28
TextGapBuffer()29 TextGapBuffer::TextGapBuffer()
30 :
31 fItemCount(0),
32 fBuffer(NULL),
33 fBufferCount(kTextGapBufferBlockSize + fItemCount),
34 fGapIndex(fItemCount),
35 fGapCount(fBufferCount - fGapIndex),
36 fScratchBuffer(NULL),
37 fScratchSize(0),
38 fPasswordMode(false)
39 {
40 fBuffer = (char*)malloc(kTextGapBufferBlockSize + fItemCount);
41 fScratchBuffer = NULL;
42 }
43
44
~TextGapBuffer()45 TextGapBuffer::~TextGapBuffer()
46 {
47 free(fBuffer);
48 free(fScratchBuffer);
49 }
50
51
52 void
InsertText(const char * inText,int32 inNumItems,int32 inAtIndex)53 TextGapBuffer::InsertText(const char* inText, int32 inNumItems, int32 inAtIndex)
54 {
55 if (inNumItems < 1)
56 return;
57
58 inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex;
59 inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex;
60
61 if (inAtIndex != fGapIndex)
62 _MoveGapTo(inAtIndex);
63
64 if (fGapCount < inNumItems)
65 _EnlargeGapTo(inNumItems + kTextGapBufferBlockSize);
66
67 memcpy(fBuffer + fGapIndex, inText, inNumItems);
68
69 fGapCount -= inNumItems;
70 fGapIndex += inNumItems;
71 fItemCount += inNumItems;
72 }
73
74
75 bool
InsertText(BFile * file,int32 fileOffset,int32 inNumItems,int32 inAtIndex)76 TextGapBuffer::InsertText(BFile* file, int32 fileOffset, int32 inNumItems,
77 int32 inAtIndex)
78 {
79 off_t fileSize;
80
81 if (file->GetSize(&fileSize) != B_OK
82 || !file->IsReadable())
83 return false;
84
85 // Clamp the text length to the file size
86 fileSize -= fileOffset;
87
88 if (fileSize < inNumItems)
89 inNumItems = fileSize;
90
91 if (inNumItems < 1)
92 return false;
93
94 inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex;
95 inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex;
96
97 if (inAtIndex != fGapIndex)
98 _MoveGapTo(inAtIndex);
99
100 if (fGapCount < inNumItems)
101 _EnlargeGapTo(inNumItems + kTextGapBufferBlockSize);
102
103 // Finally, read the data and put it into the buffer
104 if (file->ReadAt(fileOffset, fBuffer + fGapIndex, inNumItems) > 0) {
105 fGapCount -= inNumItems;
106 fGapIndex += inNumItems;
107 fItemCount += inNumItems;
108 }
109
110 return true;
111 }
112
113
114 void
RemoveRange(int32 start,int32 end)115 TextGapBuffer::RemoveRange(int32 start, int32 end)
116 {
117 int32 inAtIndex = start;
118 int32 inNumItems = end - start;
119
120 if (inNumItems < 1)
121 return;
122
123 inAtIndex = (inAtIndex > fItemCount - 1) ? (fItemCount - 1) : inAtIndex;
124 inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex;
125
126 _MoveGapTo(inAtIndex);
127
128 fGapCount += inNumItems;
129 fItemCount -= inNumItems;
130
131 if (fGapCount > kTextGapBufferBlockSize)
132 _ShrinkGapTo(kTextGapBufferBlockSize / 2);
133 }
134
135
136 const char*
GetString(int32 fromOffset,int32 * _numBytes)137 TextGapBuffer::GetString(int32 fromOffset, int32* _numBytes)
138 {
139 const char* result = "";
140 if (_numBytes == NULL)
141 return result;
142
143 int32 numBytes = *_numBytes;
144 if (numBytes < 1)
145 return result;
146
147 bool isStartBeforeGap = fromOffset < fGapIndex;
148 bool isEndBeforeGap = (fromOffset + numBytes - 1) < fGapIndex;
149
150 if (isStartBeforeGap == isEndBeforeGap) {
151 result = fBuffer + fromOffset;
152 if (!isStartBeforeGap)
153 result += fGapCount;
154 } else {
155 if (fScratchSize < numBytes) {
156 fScratchBuffer = (char*)realloc(fScratchBuffer, numBytes);
157 fScratchSize = numBytes;
158 }
159
160 for (int32 i = 0; i < numBytes; i++)
161 fScratchBuffer[i] = RealCharAt(fromOffset + i);
162
163 result = fScratchBuffer;
164 }
165
166 // TODO: this could be improved. We are overwriting what we did some lines
167 // ago, we could just avoid to do that.
168 if (fPasswordMode) {
169 uint32 numChars = UTF8CountChars(result, numBytes);
170 uint32 charLen = UTF8CountBytes(B_UTF8_BULLET, 1);
171 uint32 newSize = numChars * charLen;
172
173 if ((uint32)fScratchSize < newSize) {
174 fScratchBuffer = (char*)realloc(fScratchBuffer, newSize);
175 fScratchSize = newSize;
176 }
177 result = fScratchBuffer;
178
179 char* scratchPtr = fScratchBuffer;
180 for (uint32 i = 0; i < numChars; i++) {
181 memcpy(scratchPtr, B_UTF8_BULLET, charLen);
182 scratchPtr += charLen;
183 }
184
185 *_numBytes = newSize;
186 }
187
188 return result;
189 }
190
191
192 bool
FindChar(char inChar,int32 fromIndex,int32 * ioDelta)193 TextGapBuffer::FindChar(char inChar, int32 fromIndex, int32* ioDelta)
194 {
195 int32 numChars = *ioDelta;
196 for (int32 i = 0; i < numChars; i++) {
197 char realChar = RealCharAt(fromIndex + i);
198 if ((realChar & 0xc0) == 0x80)
199 continue;
200 if (realChar == inChar) {
201 *ioDelta = i;
202 return true;
203 }
204 }
205
206 return false;
207 }
208
209
210 const char*
Text()211 TextGapBuffer::Text()
212 {
213 const char* realText = RealText();
214
215 if (fPasswordMode) {
216 const uint32 numChars = UTF8CountChars(realText, Length());
217 const uint32 bulletCharLen = UTF8CountBytes(B_UTF8_BULLET, 1);
218 uint32 newSize = numChars * bulletCharLen + 1;
219
220 if ((uint32)fScratchSize < newSize) {
221 fScratchBuffer = (char*)realloc(fScratchBuffer, newSize);
222 fScratchSize = newSize;
223 }
224
225 char* scratchPtr = fScratchBuffer;
226 for (uint32 i = 0; i < numChars; i++) {
227 memcpy(scratchPtr, B_UTF8_BULLET, bulletCharLen);
228 scratchPtr += bulletCharLen;
229 }
230 *scratchPtr = '\0';
231
232 return fScratchBuffer;
233 }
234
235 return realText;
236 }
237
238
239 const char*
RealText()240 TextGapBuffer::RealText()
241 {
242 _MoveGapTo(fItemCount);
243
244 if (fGapCount == 0)
245 _EnlargeGapTo(kTextGapBufferBlockSize);
246
247 fBuffer[fItemCount] = '\0';
248 return fBuffer;
249 }
250
251
252 void
GetString(int32 offset,int32 length,char * buffer)253 TextGapBuffer::GetString(int32 offset, int32 length, char* buffer)
254 {
255 if (buffer == NULL)
256 return;
257
258 int32 textLen = Length();
259
260 if (offset < 0 || offset > (textLen - 1) || length < 1) {
261 buffer[0] = '\0';
262 return;
263 }
264
265 length = ((offset + length) > textLen) ? textLen - offset : length;
266
267 bool isStartBeforeGap = (offset < fGapIndex);
268 bool isEndBeforeGap = ((offset + length - 1) < fGapIndex);
269
270 if (isStartBeforeGap == isEndBeforeGap) {
271 char* source = fBuffer + offset;
272 if (!isStartBeforeGap)
273 source += fGapCount;
274
275 memcpy(buffer, source, length);
276
277 } else {
278 // if we are here, it can only be that start is before gap,
279 // and the end is after gap.
280
281 int32 beforeLen = fGapIndex - offset;
282 int32 afterLen = length - beforeLen;
283
284 memcpy(buffer, fBuffer + offset, beforeLen);
285 memcpy(buffer + beforeLen, fBuffer + fGapIndex + fGapCount, afterLen);
286
287 }
288
289 buffer[length] = '\0';
290 }
291
292
293 bool
PasswordMode() const294 TextGapBuffer::PasswordMode() const
295 {
296 return fPasswordMode;
297 }
298
299
300 void
SetPasswordMode(bool state)301 TextGapBuffer::SetPasswordMode(bool state)
302 {
303 fPasswordMode = state;
304 }
305
306
307 void
_MoveGapTo(int32 toIndex)308 TextGapBuffer::_MoveGapTo(int32 toIndex)
309 {
310 if (toIndex == fGapIndex)
311 return;
312 if (toIndex > fItemCount) {
313 debugger("MoveGapTo: invalid toIndex supplied");
314 return;
315 }
316
317 int32 srcIndex = 0;
318 int32 dstIndex = 0;
319 int32 count = 0;
320 if (toIndex > fGapIndex) {
321 srcIndex = fGapIndex + fGapCount;
322 dstIndex = fGapIndex;
323 count = toIndex - fGapIndex;
324 } else {
325 srcIndex = toIndex;
326 dstIndex = toIndex + fGapCount;
327 count = fGapIndex- toIndex;
328 }
329
330 if (count > 0)
331 memmove(fBuffer + dstIndex, fBuffer + srcIndex, count);
332
333 fGapIndex = toIndex;
334 }
335
336
337 void
_EnlargeGapTo(int32 inCount)338 TextGapBuffer::_EnlargeGapTo(int32 inCount)
339 {
340 if (inCount == fGapCount)
341 return;
342
343 fBuffer = (char*)realloc(fBuffer, fItemCount + inCount);
344 memmove(fBuffer + fGapIndex + inCount, fBuffer + fGapIndex + fGapCount,
345 fBufferCount - (fGapIndex + fGapCount));
346
347 fGapCount = inCount;
348 fBufferCount = fItemCount + fGapCount;
349 }
350
351
352 void
_ShrinkGapTo(int32 inCount)353 TextGapBuffer::_ShrinkGapTo(int32 inCount)
354 {
355 if (inCount == fGapCount)
356 return;
357
358 memmove(fBuffer + fGapIndex + inCount, fBuffer + fGapIndex + fGapCount,
359 fBufferCount - (fGapIndex + fGapCount));
360 fBuffer = (char*)realloc(fBuffer, fItemCount + inCount);
361
362 fGapCount = inCount;
363 fBufferCount = fItemCount + fGapCount;
364 }
365
366
367 } // namespace BPrivate
368