Copyright (C) 1994, Digital Equipment Corp.
MODULE******************************************************************* For Debugging Only *******************************************************************; IMPORT MTextPrivate, Wr, Fmt, Rd, Text, Thread; FROM MText IMPORT T; FROM MTextPrivate IMPORT Node, NodeType; FROM Stdio IMPORT stderr; <* FATAL Thread.Alerted, Wr.Failure, Rd.Failure *> MTextDebug
A generator for node i.d. numbers. These exist only to make structural printouts more readable, and are assigned only when the nodes are printed.
VAR idCounter: CARDINAL := 0; PROCEDUREDump gives a structural printout of everything interesting about an MText: size, files held open, tree structure, list of heads.Id (node: T): CARDINAL RAISES {} = BEGIN IF node.id = 0 THEN INC(idCounter); node.id := idCounter END; RETURN node.id END Id;
PROCEDUREVerify verifies all the properties which I assert to be true of a consistent mutable text tree or subtree: It contains no NIL Node pointers except top.up.Dump (m: T) = VAR index: CARDINAL; wr := stderr; PROCEDURE Space (n: INTEGER) = BEGIN FOR i := 1 TO n DO Wr.PutChar(wr, ' ') END END Space; PROCEDURE DumpTree (node: Node; indent: INTEGER) = BEGIN CASE node.type OF NodeType.tree => DumpTree(node.left, indent + 2); Space(indent); Wr.PutText(wr, "[n" & Fmt.Int(Id (node)) & " len=" & Fmt.Int(node.length) & "]\n"); IF (node.right.type = NodeType.tree) AND (node.right.sub) THEN DumpTree(node.right.left, indent + 2); Space(indent + 1); Wr.PutText(wr, "[+" & Fmt.Int(Id (node.right)) & " len=" & Fmt.Int(node.right.length) & "]\n"); DumpTree(node.right.right, indent + 2) ELSE DumpTree(node.right, indent + 2) END | NodeType.anchor => Space(indent); Wr.PutText( wr, "[a" & Fmt.Int(Id (node)) & " len=" & Fmt.Int(node.length) & " index=" & Fmt.Int(index) & "]\n") | NodeType.text => Space(indent); Wr.PutText( wr, "[t" & Fmt.Int(Id (node)) & " len=" & Fmt.Int(node.length) & " index=" & Fmt.Int(index) & "]<"); IF node.length < 20 THEN Wr.PutText(wr, node.text & ">\n") ELSE Wr.PutText(wr, Text.Sub(node.text, 0, 20) & "...>\n") END; INC(index, node.length) | NodeType.buf => Space(indent); Wr.PutText( wr, "[b" & Fmt.Int(Id (node)) & " len=" & Fmt.Int(node.length) & " index=" & Fmt.Int(index) & "]<"); IF node.length < 20 THEN Wr.PutString(wr, SUBARRAY(node.buffer^, 0, node.length)); Wr.PutText(wr, ">\n") ELSE Wr.PutString(wr, SUBARRAY(node.buffer^, 0, 20)); Wr.PutText(wr, "...>\n") END; INC(index, node.length) | NodeType.file => Space(indent); Wr.PutText( wr, "[f" & Fmt.Int(Id (node)) & ",start=" & Fmt.Int(node.start) & " len=" & Fmt.Int(node.length) & " index=" & Fmt.Int(index) & "]<"); Rd.Seek(node.file, node.start); IF node.length < 20 THEN Wr.PutText(wr, Rd.GetText(node.file, node.length) & ">\n") ELSE Wr.PutText(wr, Rd.GetText(node.file, 20) & "...>\n") END; INC(index, node.length) ELSE <* ASSERT FALSE *> END END DumpTree; BEGIN LOCK m.lock DO Wr.PutText(wr, "mtext of length " & Fmt.Int(m.length) & ", height " & Fmt.Int(m.height) & ", root [" & Fmt.Int(Id (m.root)) & "]\n"); index := 0; DumpTree(m.root, 0); Wr.PutText(wr, "\n"); Wr.Flush(wr) END END Dump;
For every interior node,
node.left.up = node
node.right.up = node
node.length = node.left.length + node.right.length
node.leftSize = node.left.length NOT node.left.sub
If node.sub then node.right.sub = FALSE (2-3 tree condition)
For the top node,
top.up = NIL
top.root # NIL
top.root.up = top
top.length = top.root.length - 1
top.root.sub = FALSE
top.height = height of the tree at top.root
For each head on the top.heads list,
top.root is an ancestor of head.node
0 <= head.index <= head.node.length
head.index = head.node.length only if
head.node is the final anchor node
EXCEPTION VerifyError;
PROCEDURE Verify (wr: Wr.T; node: Node; msg: TEXT) =
<* FATAL VerifyError *>
VAR
height: INTEGER;
root: Node;
PROCEDURE Err (err: TEXT) =
BEGIN
Wr.PutText(
wr, "Verify of " & msg & ": " & err & " in [" & Fmt.Int(Id (node))
& "] at height " & Fmt.Int(height) & "\n");
Wr.Flush(wr);
RAISE VerifyError
END Err;
PROCEDURE ErrN (err1: TEXT; n: INTEGER; err2: TEXT) =
BEGIN
Wr.PutText(
wr, "Verify of " & msg & ": " & err1 & Fmt.Int(n) & err2 & " in ["
& Fmt.Int(Id (node)) & "] at height " & Fmt.Int(height) & "\n");
Wr.Flush(wr);
RAISE VerifyError
END ErrN;
BEGIN
height := -1; (* used before height is computed *)
IF node.type = NodeType.top THEN
IF node.up # NIL THEN Err("top.up # NIL") END;
root := node.root;
IF root = NIL THEN Err("root is NIL") END;
IF root.up # node THEN Err("inconsistent uplink to top") END;
IF node.length # root.length - 1 THEN
ErrN("root length ", root.length, " is wrong")
END;
IF (root.type = NodeType.tree) AND (root.sub) THEN
Err("root is a sub")
END;
height := Height(root);
IF node.height # height THEN Err("height is wrong") END;
Verify(wr, root, msg);
ELSIF node.type = NodeType.tree THEN (* regular interior node *)
height := Height(node);
IF (node.left = NIL) OR (node.right = NIL) THEN
Err("node has NIL child")
END;
IF node.left.up # node THEN Err("inconsistent left uplink") END;
IF node.right.up # node THEN Err("inconsistent right uplink") END;
IF node.length # node.left.length + node.right.length THEN
Err("length is wrong")
END;
IF node.leftSize # node.left.length THEN
Err("leftSize is wrong")
END;
IF (node.left.type = NodeType.tree) AND (node.left.sub) THEN
Err("sub on left")
END;
IF (node.sub) AND (node.right.type = NodeType.tree)
AND (node.right.sub) THEN
Err("double sub")
END;
Verify(wr, node.left, msg);
Verify(wr, node.right, msg);
ELSIF node.type = NodeType.text THEN
height := 0;
IF node.length # Text.Length(node.text) THEN
Err("text node length is wrong")
END;
ELSIF node.type = NodeType.buf THEN
root := node;
WHILE root.type # NodeType.top DO root := root.up END;
IF node.length > root.bufMax THEN Err("buf node too long") END;
ELSIF node.type = NodeType.anchor THEN
IF node.length # 1 THEN Err("anchor\'s length is not 1") END
END
END Verify;
PROCEDURE Height (node: Node): CARDINAL =
VAR h: CARDINAL := 0;
BEGIN
WHILE node.type = NodeType.tree DO h := h + 1; node := node.left END;
RETURN h
END Height;
BEGIN
END MTextDebug.