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