<> <> <> <> <> <> <> <<>> DIRECTORY RunReader, TextLooks, TextLooksSupport; RunReaderImpl: CEDAR MONITOR IMPORTS TextLooks, TextLooksSupport EXPORTS RunReader SHARES TextLooks = BEGIN OPEN RunReader; Create: PUBLIC PROC RETURNS [Ref] = { RETURN [NEW[Body]] }; SetPosition: PUBLIC PROC [reader: Ref, runs: TextLooks.Runs, index: INT _ 0] = { IF runs # GetRuns[reader] OR index # GetIndex[reader] THEN reader^ _ [0, 0, 0, NIL, FALSE, TextLooks.noLooks, TextLooks.noLooks, runs, index]; }; SetIndex: PUBLIC PROC [reader: Ref, index: INT _ 0] = { IF index # GetIndex[reader] THEN { runs: TextLooks.Runs _ GetRuns[reader]; reader^ _ [0, 0, 0, NIL, FALSE, TextLooks.noLooks, TextLooks.noLooks, runs, index] } }; BackupIndex: PUBLIC PROC [reader: Ref, amount: INT] = { SetIndex[reader, GetIndex[reader]-amount] }; BumpIndex: PUBLIC PROC [reader: Ref, amount: INT] = { SetIndex[reader, GetIndex[reader]+amount] }; Position: PUBLIC PROC [reader: Ref] RETURNS [runs: TextLooks.Runs, index: INT] = { <> RETURN [GetRuns[reader], GetIndex[reader]] }; GetRuns: PUBLIC PROC [reader: Ref] RETURNS [runs: TextLooks.Runs] = { RETURN [reader.runs] }; Get: PUBLIC ReaderProc = { <> current: NAT; base: Base; IF (current_reader.current) >= reader.after THEN { [count, looks] _ ReadRun[reader, get]; RETURN }; reader.current _ current+1; base _ reader.base; count _ IF current=0 THEN base[0].after ELSE base[current].after-base[current-1].after; looks _ base[current].looks; IF reader.changeLooks THEN looks _ TextLooksSupport.ModifyLooks[looks, reader.remove, reader.add] }; Backwards: PUBLIC ReaderProc = { <> current: NAT; base: Base; IF (current_reader.current) <= reader.first THEN { [count, looks] _ ReadRun[reader, backwards]; RETURN }; base _ reader.base; count _ IF (reader.current _ current _ current-1)=0 THEN base[0].after ELSE base[current].after-base[current-1].after; looks _ base[current].looks; IF reader.changeLooks THEN looks _ TextLooksSupport.ModifyLooks[looks, reader.remove, reader.add] }; Peek: PUBLIC ReaderProc = { <> current: NAT; base: Base; IF (current_reader.current) >= reader.after THEN { [count, looks] _ ReadRun[reader, peek]; RETURN }; base _ reader.base; count _ IF current=0 THEN base[0].after ELSE base[current].after-base[current-1].after; looks _ base[current].looks; IF reader.changeLooks THEN looks _ TextLooksSupport.ModifyLooks[looks, reader.remove, reader.add] }; PeekBackwards: PUBLIC ReaderProc = { <> current: NAT; base: Base; IF (current_reader.current) <= reader.first THEN { [count, looks] _ ReadRun[reader, backwards]; RETURN }; base _ reader.base; count _ IF (current _ current-1)=0 THEN base[0].after ELSE base[current].after-base[current-1].after; looks _ base[current].looks; IF reader.changeLooks THEN looks _ TextLooksSupport.ModifyLooks[looks, reader.remove, reader.add] }; GetIndex: PUBLIC PROC [reader: Ref] RETURNS [index: TextLooks.Offset] = { base: Base; offset: TextLooks.Offset; IF reader.after = reader.current THEN RETURN [reader.index]; base _ reader.base; offset _ IF reader.current=0 THEN base[reader.after-1].after ELSE base[reader.after-1].after-base[reader.current-1].after; RETURN [reader.index - offset] }; NoMoreRuns: PUBLIC ERROR = CODE; Mode: TYPE = {get, backwards, peek, peekbackwards}; ReadRun: PROC [reader: Ref, mode: Mode] RETURNS [count: TextLooks.Offset, looks: TextLooks.Looks] = { runs: TextLooks.Runs _ reader.runs; index, current: TextLooks.Offset _ GetIndex[reader]; after: TextLooks.Offset _ TextLooks.Size[runs]; first: TextLooks.Offset _ 0; remove, add: TextLooks.Looks _ TextLooks.noLooks; SetIndx: PROC [index: TextLooks.Offset] = { reader.index _ index }; SetBaseInfo: PROC [current, first, after: NAT, base: Base] = { reader.current _ current; reader.first _ first; reader.after _ after; reader.base _ base }; ClearBaseInfo: PROC = { SetBaseInfo[0, 0, 0, NIL] }; SetLooksInfo: PROC = { reader.remove _ remove; reader.add _ add; reader.changeLooks _ remove # TextLooks.noLooks OR add # TextLooks.noLooks }; Modify: PUBLIC PROC [lks: TextLooks.Looks] RETURNS [TextLooks.Looks] = { RETURN [TextLooksSupport.ModifyLooks[lks, remove, add]] }; SingleRun: PROC = { ClearBaseInfo; SELECT mode FROM get => SetIndx[index + (count _ after-current)]; peek => count _ after-current; backwards => SetIndx[index - (count _ current-first+1)]; peekbackwards => count _ current-first+1; ENDCASE => ERROR }; IF runs=NIL THEN RETURN [LAST[NAT], TextLooks.noLooks]; SELECT mode FROM -- adjust current depending on direction backwards, peekbackwards => IF current=0 THEN ERROR NoMoreRuns ELSE current _ current-1; get, peek => NULL; ENDCASE => ERROR; IF current >= after THEN ERROR NoMoreRuns; WHILE runs # NIL DO TRUSTED {WITH runs SELECT FROM x: REF TextLooks.RunsBody.base => { len: TextLooks.Offset _ after-first; firstRun, lastRun, afterRun, curRun: NAT; -- indexes into runs startRuns, afterRuns, startCur: TextLooks.Offset; [firstRun, lastRun] _ TextLooksSupport.FindBaseRuns[x, first, len]; IF firstRun=lastRun THEN { -- treat as special case SingleRun; RETURN [count, Modify[x[firstRun].looks]] }; IF (firstRun=0 AND first=0) OR (firstRun>0 AND x[firstRun-1].after=first) THEN startRuns _ first ELSE -- only included part of firstRun IF current >= (startRuns _ x[firstRun].after) THEN firstRun _ firstRun+1 -- current not in first run ELSE { -- current is in the first run. must special case it. IF x[lastRun].after=after THEN { -- include all of lastRun afterRuns _ after; afterRun _ lastRun+1 } ELSE { afterRuns _ x[lastRun-1].after; afterRun _ lastRun }; SELECT mode FROM get => { count _ startRuns-current; IF afterRun > firstRun THEN { SetBaseInfo[firstRun+1, firstRun+1, afterRun, x]; SetIndx[index+afterRuns-current]; SetLooksInfo; } ELSE { ClearBaseInfo; SetIndx[index+count] } }; peek => { count _ startRuns-current; ClearBaseInfo; SetIndx[index] }; backwards => { count _ current-first+1; ClearBaseInfo; SetIndx[index-count] }; peekbackwards => { count _ current-first+1; ClearBaseInfo; SetIndx[index] }; ENDCASE => ERROR; RETURN [count, Modify[x[firstRun].looks]] }; IF x[lastRun].after=after THEN { -- included all of lastRun afterRuns _ after; afterRun _ lastRun+1 } ELSE { -- only included part of lastRun <0 since lastRun>firstRun>> IF current >= (afterRuns _ x[lastRun-1].after) THEN { <> SELECT mode FROM get => { count _ after-current; ClearBaseInfo; SetIndx[index+count] }; peek => { count _ after-current; ClearBaseInfo; SetIndx[index] }; backwards => { count _ current-afterRuns+1; IF lastRun > firstRun THEN { SetBaseInfo[lastRun-1, firstRun, lastRun, x]; SetIndx[index-count]; SetLooksInfo; } ELSE { ClearBaseInfo; SetIndx[index-count] } }; peekbackwards => { count _ current-afterRuns+1; ClearBaseInfo; SetIndx[index] }; ENDCASE => ERROR; RETURN [count, Modify[x[lastRun].looks]] }; afterRun _ lastRun }; curRun _ TextLooksSupport.BaseRun[x, current, firstRun, lastRun]; startCur _ IF curRun=0 THEN 0 ELSE x[curRun-1].after; SELECT mode FROM get => { count _ x[curRun].after-current; SetBaseInfo[curRun+1, firstRun, afterRun, x]; SetIndx[index+afterRuns-current]; SetLooksInfo; }; peek => { count _ x[curRun].after-current; IF current = startCur THEN { SetBaseInfo[curRun, firstRun, afterRun, x]; SetIndx[index+afterRuns-current]; SetLooksInfo; } ELSE { ClearBaseInfo; SetIndx[index] } }; backwards => { -- recall that current has been decremented count _ current-startCur+1; SetBaseInfo[curRun, firstRun, afterRun, x]; SetIndx[index+afterRuns-current-1]; SetLooksInfo; }; peekbackwards => { count _ current-startCur+1; IF current = x[curRun].after-1 THEN { SetBaseInfo[curRun+1, firstRun, afterRun, x]; SetIndx[index+afterRuns-current-1]; SetLooksInfo; } ELSE { ClearBaseInfo; SetIndx[index] } }; ENDCASE => ERROR; RETURN [count, Modify[x[curRun].looks]] }; x: REF TextLooks.RunsBody.node.substr => IF current < x.size THEN { offset: TextLooks.Offset; current _ current + (offset _ x.start); first _ first + offset; after _ after + offset; runs _ x.base; LOOP }; x: REF TextLooks.RunsBody.node.concat => IF current < x.size THEN { xpos: TextLooks.Offset; IF current >= (xpos _ x.pos) THEN { current _ current - xpos; after _ after - xpos; first _ IF first <= xpos THEN 0 ELSE first - xpos; runs _ x.rest; LOOP }; IF after > xpos THEN after _ xpos; runs _ x.base; LOOP }; x: REF TextLooks.RunsBody.node.replace => IF current < x.size THEN { xstart: TextLooks.Offset _ x.start; newPos, oldPos: TextLooks.Offset; IF current < xstart THEN { IF after > xstart THEN after _ xstart; runs _ x.base; LOOP }; IF current < (newPos _ x.newPos) THEN { current _ current - xstart; after _ MIN[after, newPos] - xstart; first _ IF first <= xstart THEN 0 ELSE first-xstart; runs _ x.replace; LOOP }; current _ current - newPos + (oldPos _ x.oldPos); after _ after - newPos + oldPos; first _ IF first >= newPos THEN first - newPos + oldPos ELSE oldPos; runs _ x.base; LOOP }; x: REF TextLooks.RunsBody.node.change => { xstart: TextLooks.Offset _ x.start; xend: TextLooks.Offset; IF current < xstart THEN { after _ MIN[after, xstart]; runs _ x.base; LOOP }; IF current < (xend _ xstart+x.len) THEN { after _ MIN[after, xend]; first _ MAX[first, xstart]; [remove, add] _ TextLooksSupport.MergeChanges[x.remove, x.add, remove, add]; IF remove=TextLooks.allLooks THEN { SingleRun; RETURN[count, add] }; runs _ x.base; LOOP }; first _ MAX[first, xend]; runs _ x.base; LOOP }; ENDCASE => ERROR }; EXIT; ENDLOOP; ERROR NoMoreRuns }; MergedGet: PUBLIC ReaderProc = { [count, looks] _ Get[reader]; WHILE reader.current=reader.after OR reader.changeLooks DO nxtCount: TextLooks.Offset; nxtLooks: TextLooks.Looks; [nxtCount, nxtLooks] _ Peek[reader ! NoMoreRuns => EXIT]; IF nxtLooks#looks THEN RETURN; count _ count+nxtCount; [, ] _ Get[reader]; ENDLOOP }; MergedBackwards: PUBLIC ReaderProc = { [count, looks] _ Backwards[reader]; WHILE reader.current=reader.first OR reader.changeLooks DO nxtCount: TextLooks.Offset; nxtLooks: TextLooks.Looks; [nxtCount, nxtLooks] _ PeekBackwards[reader ! NoMoreRuns => EXIT]; IF nxtLooks#looks THEN RETURN; count _ count+nxtCount; [, ] _ Backwards[reader]; ENDLOOP }; Equal: PUBLIC PROC [r1, r2: TextLooks.Runs, rdr1, rdr2: Ref] RETURNS [BOOL] = { <> RETURN [EqSubstrs[r1, r2, 0, 0, LAST[INT], rdr1, rdr2]] }; EqSubstrs: PUBLIC PROC [r1, r2: TextLooks.Runs, start1, start2, len: TextLooks.Offset, rdr1, rdr2: Ref] RETURNS [BOOL] = { <> <> NoLooks: PROC [r: TextLooks.Runs, start, len: TextLooks.Offset, rdr: Ref] RETURNS [BOOL] = { looks: TextLooks.Looks; runLen: TextLooks.Offset; size: TextLooks.Offset _ TextLooks.Size[r]; len _ IF start > size THEN 0 ELSE MIN[len, size-start]; SetPosition[rdr, r, start]; [runLen, looks] _ Get[rdr]; IF looks # TextLooks.noLooks OR runLen < len THEN RETURN [FALSE]; RETURN [TRUE] }; size1, size2: TextLooks.Offset; IF len=0 THEN RETURN[TRUE]; IF r1=NIL THEN { IF r2=NIL THEN RETURN [TRUE]; RETURN [NoLooks[r2, start2, len, rdr2]] }; IF r2=NIL THEN RETURN [NoLooks[r1, start1, len, rdr1]]; size1 _ TextLooks.Size[r1]; size2 _ TextLooks.Size[r2]; IF len=LAST[TextLooks.Offset] THEN { IF (len _ size1-start1) # size2-start2 THEN RETURN [FALSE] } ELSE IF start1+len > size1 THEN RETURN [FALSE] ELSE IF start2+len > size2 THEN RETURN [FALSE]; SetPosition[rdr1, r1, start1]; SetPosition[rdr2, r2, start2]; DO -- check the runs in the specified sections looks1, looks2: TextLooks.Looks; runLen1, runLen2: TextLooks.Offset; [runLen1, looks1] _ MergedGet[rdr1]; [runLen2, looks2] _ MergedGet[rdr2]; IF looks1 # looks2 THEN RETURN [FALSE]; IF runLen1 >= len THEN { IF runLen2 < len THEN RETURN [FALSE]; EXIT }; IF runLen2 # runLen1 THEN RETURN [FALSE]; len _ len-runLen1; ENDLOOP; RETURN [TRUE] }; <<***** Shared readers>> runrdr1, runrdr2, runrdr3: Ref; -- shared run readers GetRunReader: PUBLIC ENTRY PROC RETURNS [reader: Ref] = { ENABLE UNWIND => NULL; IF runrdr3 # NIL THEN { reader _ runrdr3; runrdr3 _ NIL } ELSE IF runrdr2 # NIL THEN { reader _ runrdr2; runrdr2 _ NIL } ELSE IF runrdr1 # NIL THEN { reader _ runrdr1; runrdr1 _ NIL } ELSE reader _ Create[] }; FreeRunReader: PUBLIC ENTRY PROC [reader: Ref] = { ENABLE UNWIND => NULL; SetPosition[reader, NIL]; IF runrdr3 = reader OR runrdr2 = reader OR runrdr1 = reader THEN ERROR; IF runrdr3 = NIL THEN runrdr3 _ reader ELSE IF runrdr2 = NIL THEN runrdr2 _ reader ELSE IF runrdr1 = NIL THEN runrdr1 _ reader; }; END.