SECTION "E3" /* This section contains those procedures which are concerned with the manipulation of the work-space. A line is saved in the record-place and its position (in grabs) from the beginning of the record-place is saved in the pointer-place. Blocks of the record-place which are not needed are saved in the record-store and, when required, are swapped in and out. The same applies to the pointer-place. This section is written in a manner such that other sections need have no knowledge of how lines are stored in the work-space. The only exported procedures are: clear_all_flags copy_lines move_windows do_z empty_work_space fetch_grab fetch_line flag_the_line make_space stack_work_space store_line unstack_work_space was_flagged */ GET ".ECCE-HDR" MANIFEST { blk_n = 0 // selector for block number of window written = 1 // selector, true when window written on buff_ptr = 2 // selector for buffer address stride = 16// for interleaving work-space and window store stride_m1 = stride - 1 grab_bsz_m1 = grab_bsz - 1 // for convenience line_extra = 2 // extra words stored beyond end of text. } STATIC { cur_window = ? // file block number of current window first_grab_base = ? // grab base for first work-space grab_base = ? // grab base for current work-space line_base = ? // line base for current work-space max_grab = ? // grab beyond last used old_window = 0 // file block number of old window swap_place = ? // block buffer for text text_max = ? // file block number of largest text // block window_max = 0 // file block number of largest window } LET start_e3() BE {1 swap_place:=work_space+(3*block_csz) // descriptors start after 3 page // aligned buffers of 'block_csz'. swap_place!buff_ptr:=work_space cur_window:=swap_place+3 // each descriptor is 3 words. cur_window!buff_ptr:=work_space+block_csz old_window:=cur_window+3 old_window!buff_ptr:=work_space+(2*block_csz) blk_n ! swap_place, written ! swap_place := 1, FALSE text_max, max_grab, w_space := 0,0,0 line_base := 0 written ! cur_window, written ! old_window := FALSE, FALSE blk_n ! cur_window, blk_n ! old_window := 0, -1 window_max := -1 first_grab_base, grab_base := max_grab, max_grab }1 AND clear_all_flags(i) BE /* Used by 'warn' to clean up the data base after an interrupt. */ FOR i = 1 TO last_line DO {1 LET grb = fetch_grab(i) IF grb < 0 THEN store_grab(i, -grb) }1 AND copy_lines(from_line, to_line, n) BE /* This shifts 'n' grabs from 'from_line' to 'to_line' in the seek-place. */ {1 LET step = (from_line < to_line) -> -1, +1 AND n1 = n - 1 TEST step < 0 THEN { from_line := from_line + n1; to_line := to_line + n1 } ELSE IF w_space=0 THEN log_del(to_line, from_line-to_line) // for undo. FOR i = 1 TO n DO {F store_grab(to_line, fetch_grab(from_line)) from_line := from_line + step to_line := to_line + step }F }1 AND do_z(l_line1, l_line2) BE /* Prints data storage information of interest to the implementer. */ {1 writef("last line: %N, max ptr: %N*N", last_line, max_grab) writes(" line pointer tag length*N") FOR i = l_line1 TO l_line2 DO { LET len = fetch_line(i) writef("%I7 %I< %C %I:*N", i, fetch_grab(i), cur_tag=null -> '*S',cur_tag,len) } }1 AND work_stats() BE /* Produce statistics on workfile utilisation. */ {1 writef("Block size: %N pages, Grab size: %n bytes, stride: %n*N", block_csz/page_size, grab_bsz, stride) writef("Last text block: %n, Last grab block: %n*N", text_max, window_max) writef("Last grab value: %n, Last line: %n*N", max_grab, last_line) }1 AND empty_work_space() BE max_grab := grab_base AND fetch_grab(i) = VALOF /* Fetch the grab of the i-th line. */ {1 // trace("fetch_grab: i=%N", i) RESULTIS grab_ad(i, FALSE) ! 0 }1 AND fetch_line(i, line) = VALOF /* This reads the 'i'th line from the work-space into 'line' yielding the number of characters read. It retrieves the current tag from the end of the line and yields the length of the line (plus one for newline). */ {1 LET grb = fetch_grab(i) LET len = fetch_record(ABS grb, line) cur_tag, tag_pos := line!len, line!(len+1) RESULTIS len+1 }1 AND fetch_max_grab() = max_grab AND fetch_record(grb, line) = VALOF // local to EF6 /* This gets into 'line' the record whose grab position in the work-space is 'grb' and yields the actual length of the line. */ {1 LET block = grb / block_gsz LET block_off = (grb REM block_gsz)*grab_bsz LET line_off = 0 LET remainder = block_bsz - block_off - 1 load_text(block) { LET r = (swap_place!buff_ptr)%block_off LET len = r+line_extra // include tag etc. block_off+:=1 // skip length byte {R IF remainder > len THEN remainder := len copy_and_unpack(remainder,swap_place!buff_ptr,block_off,line+line_off) len := len - remainder UNLESS len > 0 THEN BREAK line_off := line_off + remainder block := block + 1 block_off, remainder := 0, block_bsz load_text(block) }R REPEAT RESULTIS r }1 AND flag_the_line(i) BE /* The line is flagged by complementing the grab. This is used by 'flagged_lines' of EF4. */ store_grab(i, - fetch_grab(i)) AND grab_ad(i, store_) = VALOF /* Yield the address of a cell containing the grab of the 'i'-th line. If 'store_' is true then the window was written. The virtual block number of the window block is mapped onto the block numbers of the store file and the correct block is swapped in, if necessary. */ {1 LET fb, offset = (i/ block_csz)*stride, i REM block_csz load_window(fb) IF store_ THEN written ! cur_window := TRUE RESULTIS (cur_window!buff_ptr + offset) }1 AND load_text(block) BE /* This translates 'block' to the file-block-number and uses this to call 'swap_block'. */ {1 LET fb = (block / stride_m1) * stride + block REM stride_m1 + 1 swap_block(fb, swap_place, @text_max) }1 AND load_window(fb) BE /* The window corresponding to file block number 'fb' is loaded, if it is not already there. In every case, however, this window is now renamed as the current window, 'cur_window'. */ {1 UNLESS fb = blk_n ! cur_window THEN {N LET w = old_window swap_block(fb, w, @window_max) old_window := cur_window; cur_window := w }N }1 AND make_space(make_) BE /* If 'make_' is true, then move up all the cells of the pointer-place from 'cur_line' to 'last_line' to make room for new lines in the work-space. The variable 'w_space' is set to the number of new lines that can be inserted and 'last_line' is modified. If 'make_' is false, then 'w_space' grabs are deleted starting at 'cur_line' and the variables 'w_space' and 'last_line' are corrected. */ {1 TEST make_ THEN { IF last_line > file_lsz - block_csz THEN { warn(m_over) recover() // jump to recovery point in module "E1" } UNLESS cur_line > last_line THEN move_windows(cur_line, last_line) last_line := last_line + block_csz w_space := block_csz } ELSE { copy_lines(cur_line + w_space , cur_line, last_line - cur_line - w_space + 1) last_line := last_line - w_space w_space := 0 } }1 AND move_windows(l1, l2) BE /* The grabs are moved up by 'block_csz' (one window) of lines to make room for new lines. */ {1 l1, l2 := l1 + line_base, l2 + line_base { LET fb1 = (l1/block_csz)*stride AND fb2 = (l2/block_csz)*stride FOR fb = fb2 TO fb1 BY -stride DO {F load_window(fb) {N LET new_fb = fb + stride IF new_fb = blk_n ! old_window THEN blk_n ! old_window, written ! old_window := -1, FALSE save_block(cur_window!buff_ptr , new_fb) IF new_fb>window_max THEN window_max := new_fb }N }F }1 AND restore_max_grab(value) BE max_grab := value /* AND stack_work_space() BE {1 line_base := line_base + last_line line!1, line!2, line!3, line!4 := grab_base, altered_, cur_line, last_line // copy 'pat' and 'file_name' copy_cells(pattern_csz+name_csz+2, pat, line+5) line%0 := line_bsz grab_base := store_new_record() + line_gsz }1 */ AND store_grab(i, g) BE // local to EF6 /* Store the grab 'g' of the 'i'-th line in the work-space. */ {1 // trace("store_grab: i=%N, g=%N", i, g) grab_ad(i, TRUE) ! 0 := g }1 AND store_line(i, len, line) = VALOF /* This writes 'line' to the i-th line of the work-space, yielding the number of characters (plus one for new line). The current tag is put at the end of the line. */ {1 LET v = VEC line_extra LET save1, save2 = line!len, line!(len+1) LET old = fetch_grab(i) IF len=0 THEN line:=v // allocate a buffer line!len, line!(len+1) := cur_tag, tag_pos store_grab(i, store_new_record(len, line)) line!len, line!(len+1) := save1, save2 // restore original buffer. TEST w_space > 0 THEN { log_ins(i) // log insert for undo. w_space := w_space - 1 } ELSE log_cha(i, old) // log change for undo. RESULTIS len+1 }1 AND store_new_record(l, line) = VALOF // local to EF6 /* This puts 'line' to the end of the store-place and yields the grab position at which it is stored. */ {1 LET grb = max_grab LET len = l+1+line_extra // allow for tag & length max_grab := max_grab+(len+grab_bsz_m1)/grab_bsz // in grabs // IF max_grab < grb THEN IF max_grab < 0 THEN // cell overflow { max_grab := grb warn(m_over) recover() // jump to recovery point in module "E1". } store_record(grb, l, line) RESULTIS grb }1 AND store_record(grb, l, line) BE // local to EF6 /* This puts 'line' into the store-space at the grab position 'grb'. */ {1 // trace("store_record: grb=%N", grb) { LET block = grb/block_gsz LET block_off = (grb REM block_gsz)*grab_bsz LET line_off = 0 LET remainder = block_bsz - block_off - 1 LET len = l+line_extra // allow for tag load_text(block) (swap_place!buff_ptr)%block_off:=l // actual length block_off+:=1 // skip length {R IF remainder > len THEN remainder := len copy_and_pack(remainder,line+line_off,swap_place!buff_ptr,block_off) written ! swap_place := TRUE len := len - remainder UNLESS len > 0 THEN BREAK line_off := line_off + remainder block := block + 1 block_off, remainder := 0, block_bsz load_text(block) }R REPEAT }1 AND swap_block(fb, buffer, ref_fb_max) BE /* Get the 'block' into 'swap_place'; this includes writing the old block, if 'block_written_' is true. If the required block has never been written, it need not be read now; however, on some systems it may be advisable to check that it will be possible eventually to write it back. */ {1 LET cb = blk_n ! buffer UNLESS cb = fb THEN {2 IF written ! buffer THEN {3 save_block(buffer!buff_ptr , cb) IF cb > !ref_fb_max THEN !ref_fb_max := cb written ! buffer := FALSE }3 blk_n ! buffer := fb check_blocks(fb,fb) IF fb <= !ref_fb_max THEN restore_block(buffer!buff_ptr , fb) }2 RETURN }1 /* AND unstack_work_space() = VALOF {1 IF grab_base <= first_grab_base THEN RESULTIS FALSE empty_work_space(); fetch_record(grab_base - line_gsz) grab_base, altered_, cur_line, last_line := line!1, line!2, line!3, line!4 // copy 'pat' and 'file_name' copy_cells(pattern_csz+name_csz+2, line+5, pat) line_base := line_base - last_line; RESULTIS TRUE }1 */ AND was_flagged(i) = VALOF /* If the line was flagged, then unflag it and yield true; otherwise, yield false. */ {1 LET grb = fetch_grab(i) IF grb < 0 THEN { store_grab(i, - grb); RESULTIS TRUE } RESULTIS FALSE }1 .