1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "HistoryBuffer.h" 7 8 #include <new> 9 10 #include <OS.h> 11 12 #include "TermConst.h" 13 14 15 HistoryBuffer::HistoryBuffer() 16 : 17 fLines(NULL), 18 fWidth(0), 19 fCapacity(0), 20 fNextLine(0), 21 fSize(0), 22 fBuffer(NULL), 23 fBufferSize(0), 24 fBufferAllocationOffset(0) 25 { 26 } 27 28 29 HistoryBuffer::~HistoryBuffer() 30 { 31 delete[] fLines; 32 delete[] fBuffer; 33 } 34 35 36 status_t 37 HistoryBuffer::Init(int32 width, int32 capacity) 38 { 39 if (width <= 0 || capacity <= 0) 40 return B_BAD_VALUE; 41 42 int32 bufferSize = (width + 4) * capacity; 43 44 if (capacity > 0) { 45 fLines = new(std::nothrow) HistoryLine[capacity]; 46 fBuffer = new(std::nothrow) uint8[bufferSize]; 47 48 if (fLines == NULL || fBuffer == NULL) 49 return B_NO_MEMORY; 50 } 51 52 fWidth = width; 53 fCapacity = capacity; 54 fNextLine = 0; 55 fSize = 0; 56 fBufferSize = bufferSize; 57 fBufferAllocationOffset = 0; 58 59 return B_OK; 60 } 61 62 63 void 64 HistoryBuffer::Clear() 65 { 66 fNextLine = 0; 67 fSize = 0; 68 fBufferAllocationOffset = 0; 69 } 70 71 72 TerminalLine* 73 HistoryBuffer::GetTerminalLineAt(int32 index, TerminalLine* buffer) const 74 { 75 HistoryLine* line = LineAt(index); 76 if (line == NULL) 77 return NULL; 78 79 int32 charCount = 0; 80 const char* chars = line->Chars(); 81 buffer->length = 0; 82 uint32 attributes = 0; 83 AttributesRun* attributesRun = line->AttributesRuns(); 84 int32 attributesRunCount = line->attributesRunCount; 85 int32 nextAttributesAt = attributesRunCount > 0 86 ? attributesRun->offset : INT_MAX; 87 88 for (int32 i = 0; i < line->byteLength;) { 89 // get attributes 90 if (charCount == nextAttributesAt) { 91 if (attributesRunCount > 0) { 92 attributes = attributesRun->attributes; 93 nextAttributesAt = attributesRun->offset 94 + attributesRun->length; 95 attributesRun++; 96 attributesRunCount--; 97 } else { 98 attributes = 0; 99 nextAttributesAt = INT_MAX; 100 } 101 } 102 103 // copy character 104 TerminalCell& cell = buffer->cells[charCount++]; 105 int32 charLength = UTF8Char::ByteCount(chars[i]); 106 cell.character.SetTo(chars + i, charLength); 107 i += charLength; 108 109 // set attributes 110 cell.attributes = attributes; 111 112 // full width char? 113 if (cell.character.IsFullWidth()) { 114 cell.attributes |= A_WIDTH; 115 charCount++; 116 } 117 } 118 119 buffer->length = charCount; 120 buffer->softBreak = line->softBreak; 121 122 return buffer; 123 } 124 125 126 void 127 HistoryBuffer::AddLine(const TerminalLine* line) 128 { 129 //debug_printf("HistoryBuffer::AddLine(%p): length: %d\n", line, line->length); 130 // determine the amount of memory we need for the line 131 uint32 attributes = 0; 132 int32 attributesRuns = 0; 133 int32 byteLength = 0; 134 for (int32 i = 0; i < line->length; i++) { 135 const TerminalCell& cell = line->cells[i]; 136 byteLength += cell.character.ByteCount(); 137 if ((cell.attributes & CHAR_ATTRIBUTES) != attributes) { 138 attributes = cell.attributes & CHAR_ATTRIBUTES; 139 if (attributes != 0) 140 attributesRuns++; 141 } 142 if (IS_WIDTH(cell.attributes)) 143 i++; 144 } 145 146 //debug_printf(" attributesRuns: %ld, byteLength: %ld\n", attributesRuns, byteLength); 147 148 // allocate and translate the line 149 HistoryLine* historyLine = _AllocateLine(attributesRuns, byteLength); 150 151 attributes = 0; 152 AttributesRun* attributesRun = historyLine->AttributesRuns(); 153 154 char* chars = historyLine->Chars(); 155 for (int32 i = 0; i < line->length; i++) { 156 const TerminalCell& cell = line->cells[i]; 157 158 // copy char 159 int32 charLength = cell.character.ByteCount(); 160 memcpy(chars, cell.character.bytes, charLength); 161 chars += charLength; 162 163 // deal with attributes 164 if ((cell.attributes & CHAR_ATTRIBUTES) != attributes) { 165 // terminate the previous attributes run 166 if (attributes != 0) { 167 attributesRun->length = i - attributesRun->offset; 168 attributesRun++; 169 } 170 171 attributes = cell.attributes & CHAR_ATTRIBUTES; 172 173 // init the new one 174 if (attributes != 0) { 175 attributesRun->attributes = attributes; 176 attributesRun->offset = i; 177 } 178 } 179 180 if (IS_WIDTH(cell.attributes)) 181 i++; 182 } 183 184 // set the last attributes run's length 185 if (attributes != 0) 186 attributesRun->length = line->length - attributesRun->offset; 187 188 historyLine->softBreak = line->softBreak; 189 //debug_printf(" line: \"%.*s\", history size now: %ld\n", historyLine->byteLength, historyLine->Chars(), fSize); 190 } 191 192 193 void 194 HistoryBuffer::AddEmptyLines(int32 count) 195 { 196 if (count <= 0) 197 return; 198 199 if (count > fCapacity) 200 count = fCapacity; 201 202 if (count + fSize > fCapacity) 203 DropLines(count + fSize - fCapacity); 204 205 // All lines use the same buffer address, since they don't use any memory. 206 AttributesRun* attributesRun 207 = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 208 209 for (int32 i = 0; i < count; i++) { 210 HistoryLine* line = &fLines[fNextLine]; 211 fNextLine = (fNextLine + 1) % fCapacity; 212 line->attributesRuns = attributesRun; 213 line->attributesRunCount = 0; 214 line->byteLength = 0; 215 line->softBreak = false; 216 } 217 218 fSize += count; 219 } 220 221 222 void 223 HistoryBuffer::DropLines(int32 count) 224 { 225 if (count <= 0) 226 return; 227 228 if (count < fSize) { 229 fSize -= count; 230 } else { 231 fSize = 0; 232 fNextLine = 0; 233 fBufferAllocationOffset = 0; 234 } 235 } 236 237 238 HistoryLine* 239 HistoryBuffer::_AllocateLine(int32 attributesRuns, int32 byteLength) 240 { 241 // we need at least one spare line slot 242 int32 toDrop = 0; 243 if (fSize == fCapacity) 244 toDrop = 1; 245 246 int32 bytesNeeded = attributesRuns * sizeof(AttributesRun) + byteLength; 247 248 if (fBufferAllocationOffset + bytesNeeded > fBufferSize) { 249 // drop all lines after the allocation index 250 for (; toDrop < fSize; toDrop++) { 251 HistoryLine* line = _LineAt(fSize - toDrop - 1); 252 int32 offset = (uint8*)line->AttributesRuns() - fBuffer; 253 if (offset < fBufferAllocationOffset) 254 break; 255 } 256 257 fBufferAllocationOffset = 0; 258 } 259 260 // drop all lines interfering 261 int32 nextOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 262 for (; toDrop < fSize; toDrop++) { 263 HistoryLine* line = _LineAt(fSize - toDrop - 1); 264 int32 offset = (uint8*)line->AttributesRuns() - fBuffer; 265 if (offset + line->BufferSize() <= fBufferAllocationOffset 266 || offset >= nextOffset) { 267 break; 268 } 269 } 270 271 DropLines(toDrop); 272 273 // init the line 274 HistoryLine* line = &fLines[fNextLine]; 275 fNextLine = (fNextLine + 1) % fCapacity; 276 fSize++; 277 line->attributesRuns = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 278 line->attributesRunCount = attributesRuns; 279 line->byteLength = byteLength; 280 281 fBufferAllocationOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 282 // DropLines() may have changed fBufferAllocationOffset, so don't use 283 // nextOffset. 284 285 return line; 286 } 287