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 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 bool 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 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* 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 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* 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* 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 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 294 TextGapBuffer::PasswordMode() const 295 { 296 return fPasswordMode; 297 } 298 299 300 void 301 TextGapBuffer::SetPasswordMode(bool state) 302 { 303 fPasswordMode = state; 304 } 305 306 307 void 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 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 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