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