00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "BlockArray.h"
00023
00024
00025 #include <assert.h>
00026 #include <sys/mman.h>
00027 #include <sys/param.h>
00028 #include <unistd.h>
00029 #include <stdio.h>
00030
00031
00032 #include <kdebug.h>
00033
00034 using namespace Konsole;
00035
00036 static int blocksize = 0;
00037
00038 BlockArray::BlockArray()
00039 : size(0),
00040 current(size_t(-1)),
00041 index(size_t(-1)),
00042 lastmap(0),
00043 lastmap_index(size_t(-1)),
00044 lastblock(0), ion(-1),
00045 length(0)
00046 {
00047
00048 if (blocksize == 0)
00049 blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize();
00050
00051 }
00052
00053 BlockArray::~BlockArray()
00054 {
00055 setHistorySize(0);
00056 assert(!lastblock);
00057 }
00058
00059 size_t BlockArray::append(Block *block)
00060 {
00061 if (!size)
00062 return size_t(-1);
00063
00064 ++current;
00065 if (current >= size) current = 0;
00066
00067 int rc;
00068 rc = lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); }
00069 rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); }
00070
00071 length++;
00072 if (length > size) length = size;
00073
00074 ++index;
00075
00076 delete block;
00077 return current;
00078 }
00079
00080 size_t BlockArray::newBlock()
00081 {
00082 if (!size)
00083 return size_t(-1);
00084 append(lastblock);
00085
00086 lastblock = new Block();
00087 return index + 1;
00088 }
00089
00090 Block *BlockArray::lastBlock() const
00091 {
00092 return lastblock;
00093 }
00094
00095 bool BlockArray::has(size_t i) const
00096 {
00097 if (i == index + 1)
00098 return true;
00099
00100 if (i > index)
00101 return false;
00102 if (index - i >= length)
00103 return false;
00104 return true;
00105 }
00106
00107 const Block* BlockArray::at(size_t i)
00108 {
00109 if (i == index + 1)
00110 return lastblock;
00111
00112 if (i == lastmap_index)
00113 return lastmap;
00114
00115 if (i > index) {
00116 kDebug(1211) << "BlockArray::at() i > index\n";
00117 return 0;
00118 }
00119
00120
00121
00122
00123
00124
00125 size_t j = i;
00126
00127 assert(j < size);
00128 unmap();
00129
00130 Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize);
00131
00132 if (block == (Block*)-1) { perror("mmap"); return 0; }
00133
00134 lastmap = block;
00135 lastmap_index = i;
00136
00137 return block;
00138 }
00139
00140 void BlockArray::unmap()
00141 {
00142 if (lastmap) {
00143 int res = munmap((char*)lastmap, blocksize);
00144 if (res < 0) perror("munmap");
00145 }
00146 lastmap = 0;
00147 lastmap_index = size_t(-1);
00148 }
00149
00150 bool BlockArray::setSize(size_t newsize)
00151 {
00152 return setHistorySize(newsize * 1024 / blocksize);
00153 }
00154
00155 bool BlockArray::setHistorySize(size_t newsize)
00156 {
00157
00158
00159 if (size == newsize)
00160 return false;
00161
00162 unmap();
00163
00164 if (!newsize) {
00165 delete lastblock;
00166 lastblock = 0;
00167 if (ion >= 0) close(ion);
00168 ion = -1;
00169 current = size_t(-1);
00170 return true;
00171 }
00172
00173 if (!size) {
00174 FILE* tmp = tmpfile();
00175 if (!tmp) {
00176 perror("konsole: cannot open temp file.\n");
00177 } else {
00178 ion = dup(fileno(tmp));
00179 if (ion<0) {
00180 perror("konsole: cannot dup temp file.\n");
00181 fclose(tmp);
00182 }
00183 }
00184 if (ion < 0)
00185 return false;
00186
00187 assert(!lastblock);
00188
00189 lastblock = new Block();
00190 size = newsize;
00191 return false;
00192 }
00193
00194 if (newsize > size) {
00195 increaseBuffer();
00196 size = newsize;
00197 return false;
00198 } else {
00199 decreaseBuffer(newsize);
00200 ftruncate(ion, length*blocksize);
00201 size = newsize;
00202
00203 return true;
00204 }
00205 }
00206
00207 void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2)
00208 {
00209 int res = fseek(fion, cursor * blocksize, SEEK_SET);
00210 if (res)
00211 perror("fseek");
00212 res = fread(buffer2, blocksize, 1, fion);
00213 if (res != 1)
00214 perror("fread");
00215
00216 res = fseek(fion, newpos * blocksize, SEEK_SET);
00217 if (res)
00218 perror("fseek");
00219 res = fwrite(buffer2, blocksize, 1, fion);
00220 if (res != 1)
00221 perror("fwrite");
00222
00223 }
00224
00225 void BlockArray::decreaseBuffer(size_t newsize)
00226 {
00227 if (index < newsize)
00228 return;
00229
00230 int offset = (current - (newsize - 1) + size) % size;
00231
00232 if (!offset)
00233 return;
00234
00235
00236 char *buffer1 = new char[blocksize];
00237
00238 FILE *fion = fdopen(dup(ion), "w+b");
00239 if (!fion) {
00240 delete [] buffer1;
00241 perror("fdopen/dup");
00242 return;
00243 }
00244
00245 int firstblock;
00246 if (current <= newsize) {
00247 firstblock = current + 1;
00248 } else {
00249 firstblock = 0;
00250 }
00251
00252 size_t oldpos;
00253 for (size_t i = 0, cursor=firstblock; i < newsize; i++) {
00254 oldpos = (size + cursor + offset) % size;
00255 moveBlock(fion, oldpos, cursor, buffer1);
00256 if (oldpos < newsize) {
00257 cursor = oldpos;
00258 } else
00259 cursor++;
00260 }
00261
00262 current = newsize - 1;
00263 length = newsize;
00264
00265 delete [] buffer1;
00266
00267 fclose(fion);
00268
00269 }
00270
00271 void BlockArray::increaseBuffer()
00272 {
00273 if (index < size)
00274 return;
00275
00276 int offset = (current + size + 1) % size;
00277 if (!offset)
00278 return;
00279
00280
00281 char *buffer1 = new char[blocksize];
00282 char *buffer2 = new char[blocksize];
00283
00284 int runs = 1;
00285 int bpr = size;
00286
00287 if (size % offset == 0) {
00288 bpr = size / offset;
00289 runs = offset;
00290 }
00291
00292 FILE *fion = fdopen(dup(ion), "w+b");
00293 if (!fion) {
00294 perror("fdopen/dup");
00295 delete [] buffer1;
00296 delete [] buffer2;
00297 return;
00298 }
00299
00300 int res;
00301 for (int i = 0; i < runs; i++)
00302 {
00303
00304 int firstblock = (offset + i) % size;
00305 res = fseek(fion, firstblock * blocksize, SEEK_SET);
00306 if (res)
00307 perror("fseek");
00308 res = fread(buffer1, blocksize, 1, fion);
00309 if (res != 1)
00310 perror("fread");
00311 int newpos = 0;
00312 for (int j = 1, cursor=firstblock; j < bpr; j++)
00313 {
00314 cursor = (cursor + offset) % size;
00315 newpos = (cursor - offset + size) % size;
00316 moveBlock(fion, cursor, newpos, buffer2);
00317 }
00318 res = fseek(fion, i * blocksize, SEEK_SET);
00319 if (res)
00320 perror("fseek");
00321 res = fwrite(buffer1, blocksize, 1, fion);
00322 if (res != 1)
00323 perror("fwrite");
00324 }
00325 current = size - 1;
00326 length = size;
00327
00328 delete [] buffer1;
00329 delete [] buffer2;
00330
00331 fclose(fion);
00332
00333 }
00334