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 (burton666@libero.it) 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 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 45 TextGapBuffer::~TextGapBuffer() 46 { 47 free(fBuffer); 48 free(fScratchBuffer); 49 } 50 51 52 void 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 void 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; 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; 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 111 112 void 113 TextGapBuffer::RemoveRange(int32 start, int32 end) 114 { 115 long inAtIndex = start; 116 long inNumItems = end - start; 117 118 if (inNumItems < 1) 119 return; 120 121 inAtIndex = (inAtIndex > fItemCount - 1) ? (fItemCount - 1) : inAtIndex; 122 inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; 123 124 _MoveGapTo(inAtIndex); 125 126 fGapCount += inNumItems; 127 fItemCount -= inNumItems; 128 129 if (fGapCount > kTextGapBufferBlockSize) 130 _ShrinkGapTo(kTextGapBufferBlockSize / 2); 131 } 132 133 134 const char* 135 TextGapBuffer::GetString(int32 fromOffset, int32* _numBytes) 136 { 137 const char* result = ""; 138 if (_numBytes == NULL) 139 return result; 140 141 int32 numBytes = *_numBytes; 142 if (numBytes < 1) 143 return result; 144 145 bool isStartBeforeGap = (fromOffset < fGapIndex); 146 bool isEndBeforeGap = ((fromOffset + numBytes - 1) < fGapIndex); 147 148 if (isStartBeforeGap == isEndBeforeGap) { 149 result = fBuffer + fromOffset; 150 if (!isStartBeforeGap) 151 result += fGapCount; 152 } else { 153 if (fScratchSize < numBytes) { 154 fScratchBuffer = (char*)realloc(fScratchBuffer, numBytes); 155 fScratchSize = numBytes; 156 } 157 158 for (long i = 0; i < numBytes; i++) 159 fScratchBuffer[i] = RealCharAt(fromOffset + i); 160 161 result = fScratchBuffer; 162 } 163 164 // TODO: this could be improved. We are overwriting what we did some lines 165 // ago, we could just avoid to do that. 166 if (fPasswordMode) { 167 uint32 numChars = UTF8CountChars(result, numBytes); 168 uint32 charLen = UTF8CountBytes(B_UTF8_BULLET, 1); 169 uint32 newSize = numChars * charLen; 170 171 if ((uint32)fScratchSize < newSize) { 172 fScratchBuffer = (char*)realloc(fScratchBuffer, newSize); 173 fScratchSize = newSize; 174 } 175 result = fScratchBuffer; 176 177 char* scratchPtr = fScratchBuffer; 178 for (uint32 i = 0; i < numChars; i++) { 179 memcpy(scratchPtr, B_UTF8_BULLET, charLen); 180 scratchPtr += charLen; 181 } 182 183 *_numBytes = newSize; 184 } 185 186 return result; 187 } 188 189 190 bool 191 TextGapBuffer::FindChar(char inChar, int32 fromIndex, int32* ioDelta) 192 { 193 long numChars = *ioDelta; 194 for (long i = 0; i < numChars; i++) { 195 char realChar = RealCharAt(fromIndex + i); 196 if ((realChar & 0xc0) == 0x80) 197 continue; 198 if (realChar == inChar) { 199 *ioDelta = i; 200 return true; 201 } 202 } 203 204 return false; 205 } 206 207 208 const char* 209 TextGapBuffer::Text() 210 { 211 const char* realText = RealText(); 212 213 if (fPasswordMode) { 214 const uint32 numChars = UTF8CountChars(realText, Length()); 215 const uint32 bulletCharLen = UTF8CountBytes(B_UTF8_BULLET, 1); 216 uint32 newSize = numChars * bulletCharLen + 1; 217 218 if ((uint32)fScratchSize < newSize) { 219 fScratchBuffer = (char*)realloc(fScratchBuffer, newSize); 220 fScratchSize = newSize; 221 } 222 223 char* scratchPtr = fScratchBuffer; 224 for (uint32 i = 0; i < numChars; i++) { 225 memcpy(scratchPtr, B_UTF8_BULLET, bulletCharLen); 226 scratchPtr += bulletCharLen; 227 } 228 scratchPtr = '\0'; 229 230 return fScratchBuffer; 231 } 232 233 return realText; 234 } 235 236 237 const char* 238 TextGapBuffer::RealText() 239 { 240 _MoveGapTo(fItemCount); 241 242 if (fGapCount == 0) 243 _EnlargeGapTo(kTextGapBufferBlockSize); 244 245 fBuffer[fItemCount] = '\0'; 246 return fBuffer; 247 } 248 249 250 void 251 TextGapBuffer::GetString(int32 offset, int32 length, char* buffer) 252 { 253 if (buffer == NULL) 254 return; 255 256 int32 textLen = Length(); 257 258 if (offset < 0 || offset > (textLen - 1) || length < 1) { 259 buffer[0] = '\0'; 260 return; 261 } 262 263 length = ((offset + length) > textLen) ? textLen - offset : length; 264 265 bool isStartBeforeGap = (offset < fGapIndex); 266 bool isEndBeforeGap = ((offset + length - 1) < fGapIndex); 267 268 if (isStartBeforeGap == isEndBeforeGap) { 269 char* source = fBuffer + offset; 270 if (!isStartBeforeGap) 271 source += fGapCount; 272 273 memcpy(buffer, source, length); 274 275 } else { 276 // if we are here, it can only be that start is before gap, 277 // and the end is after gap. 278 279 int32 beforeLen = fGapIndex - offset; 280 int32 afterLen = length - beforeLen; 281 282 memcpy(buffer, fBuffer + offset, beforeLen); 283 memcpy(buffer + beforeLen, fBuffer + fGapIndex, afterLen); 284 285 } 286 287 buffer[length] = '\0'; 288 } 289 290 291 bool 292 TextGapBuffer::PasswordMode() const 293 { 294 return fPasswordMode; 295 } 296 297 298 void 299 TextGapBuffer::SetPasswordMode(bool state) 300 { 301 fPasswordMode = state; 302 } 303 304 305 void 306 TextGapBuffer::_MoveGapTo(int32 toIndex) 307 { 308 if (toIndex == fGapIndex) 309 return; 310 if (toIndex > fItemCount) { 311 debugger("MoveGapTo: invalid toIndex supplied"); 312 return; 313 } 314 315 int32 srcIndex = 0; 316 int32 dstIndex = 0; 317 int32 count = 0; 318 if (toIndex > fGapIndex) { 319 srcIndex = fGapIndex + fGapCount; 320 dstIndex = fGapIndex; 321 count = toIndex - fGapIndex; 322 } else { 323 srcIndex = toIndex; 324 dstIndex = toIndex + fGapCount; 325 count = fGapIndex- toIndex; 326 } 327 328 if (count > 0) 329 memmove(fBuffer + dstIndex, fBuffer + srcIndex, count); 330 331 fGapIndex = toIndex; 332 } 333 334 335 void 336 TextGapBuffer::_EnlargeGapTo(int32 inCount) 337 { 338 if (inCount == fGapCount) 339 return; 340 341 fBuffer = (char*)realloc(fBuffer, fItemCount + inCount); 342 memmove(fBuffer + fGapIndex + inCount, fBuffer + fGapIndex + fGapCount, 343 fBufferCount - (fGapIndex + fGapCount)); 344 345 fGapCount = inCount; 346 fBufferCount = fItemCount + fGapCount; 347 } 348 349 350 void 351 TextGapBuffer::_ShrinkGapTo(int32 inCount) 352 { 353 if (inCount == fGapCount) 354 return; 355 356 memmove(fBuffer + fGapIndex + inCount, fBuffer + fGapIndex + fGapCount, 357 fBufferCount - (fGapIndex + fGapCount)); 358 fBuffer = (char*)realloc(fBuffer, fItemCount + inCount); 359 360 fGapCount = inCount; 361 fBufferCount = fItemCount + fGapCount; 362 } 363 364 365 } // namespace BPrivate 366