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 23namespace BPrivate { 24 25 26static const int32 kTextGapBufferBlockSize = 2048; 27 28 29TextGapBuffer::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 45TextGapBuffer::~TextGapBuffer() 46{ 47 free(fBuffer); 48 free(fScratchBuffer); 49} 50 51 52void 53TextGapBuffer::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 75bool 76TextGapBuffer::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 114void 115TextGapBuffer::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 136const char* 137TextGapBuffer::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 192bool 193TextGapBuffer::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 210const char* 211TextGapBuffer::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 239const char* 240TextGapBuffer::RealText() 241{ 242 _MoveGapTo(fItemCount); 243 244 if (fGapCount == 0) 245 _EnlargeGapTo(kTextGapBufferBlockSize); 246 247 fBuffer[fItemCount] = '\0'; 248 return fBuffer; 249} 250 251 252void 253TextGapBuffer::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 293bool 294TextGapBuffer::PasswordMode() const 295{ 296 return fPasswordMode; 297} 298 299 300void 301TextGapBuffer::SetPasswordMode(bool state) 302{ 303 fPasswordMode = state; 304} 305 306 307void 308TextGapBuffer::_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 337void 338TextGapBuffer::_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 352void 353TextGapBuffer::_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