/* * Copyright 2001-2006, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers (mflerackers@androme.be) * Stefano Ceccherini (stefano.ceccherini@gmail.com) */ #include #include #include #include #include #include // for B_UTF8_BULLET #include "TextGapBuffer.h" namespace BPrivate { static const int32 kTextGapBufferBlockSize = 2048; TextGapBuffer::TextGapBuffer() : fItemCount(0), fBuffer(NULL), fBufferCount(kTextGapBufferBlockSize + fItemCount), fGapIndex(fItemCount), fGapCount(fBufferCount - fGapIndex), fScratchBuffer(NULL), fScratchSize(0), fPasswordMode(false) { fBuffer = (char*)malloc(kTextGapBufferBlockSize + fItemCount); fScratchBuffer = NULL; } TextGapBuffer::~TextGapBuffer() { free(fBuffer); free(fScratchBuffer); } void TextGapBuffer::InsertText(const char* inText, int32 inNumItems, int32 inAtIndex) { if (inNumItems < 1) return; inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex; inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; if (inAtIndex != fGapIndex) _MoveGapTo(inAtIndex); if (fGapCount < inNumItems) _EnlargeGapTo(inNumItems + kTextGapBufferBlockSize); memcpy(fBuffer + fGapIndex, inText, inNumItems); fGapCount -= inNumItems; fGapIndex += inNumItems; fItemCount += inNumItems; } bool TextGapBuffer::InsertText(BFile* file, int32 fileOffset, int32 inNumItems, int32 inAtIndex) { off_t fileSize; if (file->GetSize(&fileSize) != B_OK || !file->IsReadable()) return false; // Clamp the text length to the file size fileSize -= fileOffset; if (fileSize < inNumItems) inNumItems = fileSize; if (inNumItems < 1) return false; inAtIndex = (inAtIndex > fItemCount) ? fItemCount : inAtIndex; inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; if (inAtIndex != fGapIndex) _MoveGapTo(inAtIndex); if (fGapCount < inNumItems) _EnlargeGapTo(inNumItems + kTextGapBufferBlockSize); // Finally, read the data and put it into the buffer if (file->ReadAt(fileOffset, fBuffer + fGapIndex, inNumItems) > 0) { fGapCount -= inNumItems; fGapIndex += inNumItems; fItemCount += inNumItems; } return true; } void TextGapBuffer::RemoveRange(int32 start, int32 end) { int32 inAtIndex = start; int32 inNumItems = end - start; if (inNumItems < 1) return; inAtIndex = (inAtIndex > fItemCount - 1) ? (fItemCount - 1) : inAtIndex; inAtIndex = (inAtIndex < 0) ? 0 : inAtIndex; _MoveGapTo(inAtIndex); fGapCount += inNumItems; fItemCount -= inNumItems; if (fGapCount > kTextGapBufferBlockSize) _ShrinkGapTo(kTextGapBufferBlockSize / 2); } const char* TextGapBuffer::GetString(int32 fromOffset, int32* _numBytes) { const char* result = ""; if (_numBytes == NULL) return result; int32 numBytes = *_numBytes; if (numBytes < 1) return result; bool isStartBeforeGap = fromOffset < fGapIndex; bool isEndBeforeGap = (fromOffset + numBytes - 1) < fGapIndex; if (isStartBeforeGap == isEndBeforeGap) { result = fBuffer + fromOffset; if (!isStartBeforeGap) result += fGapCount; } else { if (fScratchSize < numBytes) { fScratchBuffer = (char*)realloc(fScratchBuffer, numBytes); fScratchSize = numBytes; } for (int32 i = 0; i < numBytes; i++) fScratchBuffer[i] = RealCharAt(fromOffset + i); result = fScratchBuffer; } // TODO: this could be improved. We are overwriting what we did some lines // ago, we could just avoid to do that. if (fPasswordMode) { uint32 numChars = UTF8CountChars(result, numBytes); uint32 charLen = UTF8CountBytes(B_UTF8_BULLET, 1); uint32 newSize = numChars * charLen; if ((uint32)fScratchSize < newSize) { fScratchBuffer = (char*)realloc(fScratchBuffer, newSize); fScratchSize = newSize; } result = fScratchBuffer; char* scratchPtr = fScratchBuffer; for (uint32 i = 0; i < numChars; i++) { memcpy(scratchPtr, B_UTF8_BULLET, charLen); scratchPtr += charLen; } *_numBytes = newSize; } return result; } bool TextGapBuffer::FindChar(char inChar, int32 fromIndex, int32* ioDelta) { int32 numChars = *ioDelta; for (int32 i = 0; i < numChars; i++) { char realChar = RealCharAt(fromIndex + i); if ((realChar & 0xc0) == 0x80) continue; if (realChar == inChar) { *ioDelta = i; return true; } } return false; } const char* TextGapBuffer::Text() { const char* realText = RealText(); if (fPasswordMode) { const uint32 numChars = UTF8CountChars(realText, Length()); const uint32 bulletCharLen = UTF8CountBytes(B_UTF8_BULLET, 1); uint32 newSize = numChars * bulletCharLen + 1; if ((uint32)fScratchSize < newSize) { fScratchBuffer = (char*)realloc(fScratchBuffer, newSize); fScratchSize = newSize; } char* scratchPtr = fScratchBuffer; for (uint32 i = 0; i < numChars; i++) { memcpy(scratchPtr, B_UTF8_BULLET, bulletCharLen); scratchPtr += bulletCharLen; } *scratchPtr = '\0'; return fScratchBuffer; } return realText; } const char* TextGapBuffer::RealText() { _MoveGapTo(fItemCount); if (fGapCount == 0) _EnlargeGapTo(kTextGapBufferBlockSize); fBuffer[fItemCount] = '\0'; return fBuffer; } void TextGapBuffer::GetString(int32 offset, int32 length, char* buffer) { if (buffer == NULL) return; int32 textLen = Length(); if (offset < 0 || offset > (textLen - 1) || length < 1) { buffer[0] = '\0'; return; } length = ((offset + length) > textLen) ? textLen - offset : length; bool isStartBeforeGap = (offset < fGapIndex); bool isEndBeforeGap = ((offset + length - 1) < fGapIndex); if (isStartBeforeGap == isEndBeforeGap) { char* source = fBuffer + offset; if (!isStartBeforeGap) source += fGapCount; memcpy(buffer, source, length); } else { // if we are here, it can only be that start is before gap, // and the end is after gap. int32 beforeLen = fGapIndex - offset; int32 afterLen = length - beforeLen; memcpy(buffer, fBuffer + offset, beforeLen); memcpy(buffer + beforeLen, fBuffer + fGapIndex + fGapCount, afterLen); } buffer[length] = '\0'; } bool TextGapBuffer::PasswordMode() const { return fPasswordMode; } void TextGapBuffer::SetPasswordMode(bool state) { fPasswordMode = state; } void TextGapBuffer::_MoveGapTo(int32 toIndex) { if (toIndex == fGapIndex) return; if (toIndex > fItemCount) { debugger("MoveGapTo: invalid toIndex supplied"); return; } int32 srcIndex = 0; int32 dstIndex = 0; int32 count = 0; if (toIndex > fGapIndex) { srcIndex = fGapIndex + fGapCount; dstIndex = fGapIndex; count = toIndex - fGapIndex; } else { srcIndex = toIndex; dstIndex = toIndex + fGapCount; count = fGapIndex- toIndex; } if (count > 0) memmove(fBuffer + dstIndex, fBuffer + srcIndex, count); fGapIndex = toIndex; } void TextGapBuffer::_EnlargeGapTo(int32 inCount) { if (inCount == fGapCount) return; fBuffer = (char*)realloc(fBuffer, fItemCount + inCount); memmove(fBuffer + fGapIndex + inCount, fBuffer + fGapIndex + fGapCount, fBufferCount - (fGapIndex + fGapCount)); fGapCount = inCount; fBufferCount = fItemCount + fGapCount; } void TextGapBuffer::_ShrinkGapTo(int32 inCount) { if (inCount == fGapCount) return; memmove(fBuffer + fGapIndex + inCount, fBuffer + fGapIndex + fGapCount, fBufferCount - (fGapIndex + fGapCount)); fBuffer = (char*)realloc(fBuffer, fItemCount + inCount); fGapCount = inCount; fBufferCount = fItemCount + fGapCount; } } // namespace BPrivate