#include "Col.ph" #ifdef __GNUG__ #pragma implementation #endif #include "RunArray.h" #include "Class.h" #include "Error.h" #include "String.h" #include "Invariant.h" #include "Math.h" #include "ET_stdio.h" InvariantChecker(RunArray); const int cRunArrExpandFactor= 2; // increment of expansion during Shift static const char *cOutOfRangeMsg= "out of range from= %d to= %d length= %d"; inline bool OutOfRange(int from, int to, int len= cMaxInt) { return from < 0 || from > to || to > len; } inline bool HighWaterMark(int level, int size) { return level > size; } inline bool LowWaterMark(int level, int size) { return level < (size/4 -1); } //---- RunArray ---------------------------------------------------------------- NewMetaImpl(RunArray,Object, (TVP(cont, count), TV(runs, count), T(size), T(count), T(length), T(theRun), T(runOffset))); RunArray::RunArray(int elements) { cont= new Object* [size= elements]; runs= new int[size]; length= theRun= runOffset= count= 0; } RunArray::~RunArray() { SafeDelete(cont); SafeDelete(runs); } void RunArray::OutOfRangeError(char *where, int at) { Error(where, "out of range at %d (length %d)", at, length); } Object*& RunArray::operator[](int i) { if (OutOfRange(i, length-1)) OutOfRangeError("operator[]", i); MoveTo(i); return cont[theRun]; } void RunArray::Insert(Object *op, int from, int to, int len) { AssertInvariant(RunArray); if (OutOfRange(from, to, length)) { Error("Insert", cOutOfRangeMsg, from, to, length); return; } if (len < 0 || op == 0) { Error("Insert", "op == 0"); return; } if (len == 0) // store the run Cut(from, to); else { InsertRuns(from, to, &op, &len, 1); length+= len - to + from; } } void RunArray::ChangeRunSize(int i, int shift, RArrayGrowDir gd) { AssertInvariant(RunArray); if (OutOfRange(i,length) || -shift > i) { OutOfRangeError("ChangeRunSize", i); return; } MoveTo(i); if (i == runOffset && i > 0 && gd == eRALeft) // |xxxxx|yyyyy| grow at left margin // v // |xxxxxv|yyyyy| PrevRun(); if (i + shift >= runOffset) { runs[theRun]+= shift; length+= shift; } else Cut(i + shift, i); } void RunArray::Cut(int from, int to) { AssertInvariant(RunArray); if (OutOfRange(from, to, length)) { Error("Cut", cOutOfRangeMsg, from, to, length); return; } InsertRuns(from, to, 0, 0, 0); length-= to-from; } void RunArray::Paste(RunArray *paste, int from, int to) { ReplaceRange(from, to, paste, 0, paste->Size()); } void RunArray::Copy(RunArray *save, int from, int to) { save->ReplaceRange(0, save->Size(), this, from, to); } void RunArray::FreeAll() { Cut(0, Size()); } RunArray *RunArray::Save(int from, int to) { RunArray *save= new RunArray; Copy(save, from, to); return save; } Object *RunArray::RunAt(int i, int *start, int *end, int *runsize, int *lenat) { if (OutOfRange(i, length-1)) { OutOfRangeError("RunAt", i); return 0; } MoveTo(i); *start= runOffset; *end= EndOfRun(); *runsize= runs[theRun]; *lenat= *end - i; return cont[theRun]; } int RunArray::LengthAt(int i) { if (OutOfRange(i, length-1)) { OutOfRangeError("LengthAt", i); return 0; } MoveTo(i); return EndOfRun() - i; } int RunArray::RunSizeAt(int i) { if (OutOfRange(i, length-1)) { OutOfRangeError("RunSizeAt", i); return 0; } MoveTo(i); return runs[theRun]; } OStream &RunArray::PrintOn(OStream &s) { AssertInvariant(RunArray); Object::PrintOn(s); s << count SP; for (int i= 0; i < count; i++) s << cont[i] SP << runs[i] SP; return s; } IStream &RunArray::ReadFrom(IStream &s) { AssertInvariant(RunArray); theRun= runOffset= length= 0; Object::ReadFrom(s); s >> count; if (size < count) { delete cont; delete runs; cont= new Object*[size= count]; runs= new int[count]; } length= theRun= runOffset= 0; for (int i= 0; i < count; i++) { s >> cont[i] >> runs[i]; length+= runs[i]; } return s; } void RunArray::CheckInvariant() { // sum of runlengths == length if (GetAssertLevel() > 10) return; int s= 0; for (int i= 0; i < count; i++) s+= runs[i]; if (length != s) fprintf(stderr, "!!!RunArray Invariant: length %d runs %d\n", length, s); } void RunArray::ReplaceRange(int from, int to, RunArray *src, int sfrom, int sto) { AssertInvariant(RunArray); if (OutOfRange(from, to, length)) { Error("ReplaceRange", cOutOfRangeMsg, from, to, length); return; } src->MoveTo(sfrom); int fromindex= src->theRun; int fromoffset= sfrom-src->runOffset; int fromlen= src->runs[src->theRun]; src->MoveTo(Math::Max(0, sto-1)); int toindex= src->theRun; int tooffset= sto-src->runOffset; int tolen= src->runs[src->theRun]; if (fromindex != toindex) { src->runs[fromindex]= fromlen-fromoffset; src->runs[toindex]= tooffset; } else src->runs[fromindex]= sto-sfrom; InsertRuns(from, to, &src->cont[fromindex], &src->runs[fromindex], toindex-fromindex+1); length+= from-to + sto-sfrom; src->runs[fromindex]= fromlen; src->runs[toindex]= tolen; } //---- private methods --------------------------------------------------------- inline void RunArray::CopyRuns(Object **desto, int *desti, int from, int to, int len) { MemCpy(&desto[to], &cont[from], len * sizeof(Object*)); MemCpy(&desti[to], &runs[from], len * sizeof(int)); } void RunArray::MoveTo(int to) { if (IsInRun(to)) return; // find best start if (to < runOffset) { if (to < runOffset/2) theRun= runOffset= 0; } else { if (to > (length+runOffset)/2) { theRun= count -1; runOffset= length - runs[theRun]; } } if (to < runOffset) // go left while (!IsInRun(to)) PrevRun(); else while (theRun < count && !IsInRun(to)) NextRun(); } void RunArray::Shift(int at, int shift) { Object **tmpop; int *tmpint; if (shift == 0) return; if (-shift > at) { OutOfRangeError("Shift", at); return; } if (HighWaterMark(count + shift, size)) { // expand if (size) size= Math::Max(size * cRunArrExpandFactor,count + shift); else size= 16; tmpop= new Object*[size]; tmpint= new int[size]; CopyRuns(tmpop, tmpint, 0, 0, at); CopyRuns(tmpop, tmpint, at, at + shift, count - at); delete cont; delete runs; cont= tmpop; runs= tmpint; } else { if (LowWaterMark(count + shift, size)) { //--- shrink size= size / cRunArrExpandFactor; tmpop= new Object*[size]; tmpint= new int[size]; CopyRuns(tmpop, tmpint, 0, 0, at + shift); CopyRuns(tmpop, tmpint, at, at + shift, count - at); delete cont; delete runs; cont= tmpop; runs= tmpint; } else CopyRuns(cont, runs, at, at + shift, count -at); } count+= shift; } void RunArray::InsertRuns(int from, int to, Object **value, int *run, int inssize) { // Insert 'inssize' runs pointed to by 'value' between 'from' and 'to' int insert= inssize; int rightindex, rightlen, leftindex, leftlen; int i, j, firstlen= -1, lastlen= -1; Object *blankobj= 0; int blankint= 0; if (!inssize) { // nothing to insert MoveTo(to); if (IsInRun(from) && to - from < runs[theRun]) { runs[theRun]-= to - from; return; } value= &blankobj; run= &blankint; if (to < EndOfRun()) { // set 'value' and 'run' to run right of 'to' and move right to the end of this run value[0]= cont[theRun]; run[0]= EndOfRun() - to; to= EndOfRun(); inssize= insert= 1; } } MoveTo(Math::Max(from-1,0)); leftindex= theRun; // index of run left of 'from' leftlen= from - runOffset; // new length of run left of 'from' // delete runs between from and to while (!IsInRun(to)) { if (runOffset >= from) insert--; NextRun() ; } rightindex= theRun; // index of run right of 'to' rightlen= EndOfRun() - to; // new length of run right of 'to' theRun= leftindex; runOffset= from - leftlen; if (inssize) { firstlen= run[0]; // save length of first and last run lastlen= run[inssize-1]; // to insert } if (leftlen && rightlen && leftindex == rightindex) // insert new runs into one run ==> split this run insert++; if (inssize && leftlen && cont[leftindex]->IsEqual(value[0])) { // left run is equal to the first run to insert // add his length to it run[0]+= leftlen; leftlen= 0; insert--; } if (inssize && rightlen && cont[rightindex]->IsEqual(value[inssize-1])) { // right run is equal to the last run to insert // add his length to it run[inssize-1]+= rightlen; rightlen= 0; insert--; } Shift(rightindex, insert); i= leftindex; // adjust length of left and right run and insert new runs if (leftlen) { if (leftindex == rightindex) // we had to split this run cont[i]= cont[leftindex+insert]; runs[i]= leftlen; i++; } for (j= 0; j < inssize; i++, j++) { cont[i]= value[j]; runs[i]= run[j]; } if (rightlen) runs[i]= rightlen; if (inssize) { // restore length of first and last run to insert run[0]= firstlen; run[inssize-1]= lastlen; } } void RunArray::InspectorId (char *buf, int) { sprintf(buf, "Size %d", length); } //---- RunArrayIter ------------------------------------------------------------ RunArrayIter::RunArrayIter(RunArray *ra) { cs= ra; ce= ci= cp= 0; } Object *RunArrayIter::operator()() { if (ci >= cs->length) return (0); if (ci >= cp + cs->runs[ce]) { cp+= cs->runs[ce]; ce++; } ci++; return cs->cont[ce]; } Object *RunArrayIter::Run(int *start, int *end, int *size) { if (ci >= cs->length) return (0); *start= cp; *size= cs->runs[ce]; *end= *start + *size; cp+= *size; ci= cp; return cs->cont[ce++]; } Object **RunArrayIter::RunPtr(int *start, int *end, int *size) { if (ci >= cs->length) return (0); *start= cp; *size= cs->runs[ce]; *end= *start + *size; cp+= *size; ci= cp; return &cs->cont[ce++]; }