StringImpl.mesa
Copyright (C) 1984, Xerox Corporation. All rights reserved.
Michael Plass, September 7, 1984 5:14:51 pm PDT
Merge of Pilot StringsImplA and StringsImplB, with Heap references removed.
DIRECTORY Ascii, ByteBlt, Inline, String;
StringImpl: PROGRAM
IMPORTS ByteBlt, Inline, String EXPORTS String = PUBLIC
BEGIN OPEN String;
StringBoundsFault: SIGNAL [s: LONG STRING] RETURNS [ns: LONG STRING] = CODE;
AppendChar: PROCEDURE [s: LONG STRING, c: CHARACTER] =
BEGIN
IF s = NIL THEN RETURN;
UNTIL s.length < s.maxlength DO
temp: LONG STRINGSIGNAL StringBoundsFault[s];
IF temp = NIL THEN RETURN;
s ← temp;
ENDLOOP;
s[s.length] ← c;
s.length ← s.length + 1;
RETURN
END;
AppendString: PROCEDURE [to: LONG STRING, from: LONG STRING] =
BEGIN
n: CARDINAL;
IF to = NIL OR from = NIL THEN RETURN;
WHILE from.length + to.length > to.maxlength DO
temp: LONG STRINGSIGNAL StringBoundsFault[to];
IF temp = NIL THEN EXIT; -- just fill in as much as fits
to ← temp;
ENDLOOP;
n ← MIN[from.length, LOOPHOLE[to.maxlength - to.length, CARDINAL]];
IF to.length MOD 2 = 0 THEN
Inline.LongCOPY[
from: @from.text,
to: to + SIZE[StringBody[to.length]],
nwords: SIZE[StringBody[n]]-SIZE[StringBody[0]]]
ELSE [] ← ByteBlt.ByteBlt[
from: [
blockPointer: LOOPHOLE[@from.text],
startIndex: 0,
stopIndexPlusOne: n],
to: [
blockPointer: LOOPHOLE[@to.text],
startIndex: to.length,
stopIndexPlusOne: to.length + n]];
to.length ← to.length + n;
RETURN
END;
Equal: PROCEDURE [s1, s2: LONG STRING] RETURNS [BOOLEAN] =
BEGIN
i: CARDINAL;
IF s1 = NIL AND s2 = NIL THEN RETURN[TRUE];
IF s1 = NIL OR s2 = NIL OR s1.length # s2.length THEN RETURN[FALSE];
FOR i IN [0..s1.length) DO IF s1[i] # s2[i] THEN RETURN[FALSE]; ENDLOOP;
RETURN[TRUE]
END;
Equivalent: PROCEDURE [s1, s2: LONG STRING]
RETURNS [BOOLEAN] =
BEGIN OPEN Inline;
i: CARDINAL;
casebit: WORD = 40B;
IF s1 = NIL AND s2 = NIL THEN RETURN[TRUE];
IF s1 = NIL OR s2 = NIL OR s1.length # s2.length THEN RETURN[FALSE];
FOR i IN [0..s1.length) DO
IF BITOR[s1[i], casebit] # BITOR[s2[i], casebit] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
END;
AppendSubString: PROCEDURE [to: LONG STRING, from: SubString] =
BEGIN
i, j, n: CARDINAL;
s: LONG STRING;
IF to = NIL OR (s ← from.base) = NIL THEN RETURN;
WHILE from.length + to.length > to.maxlength DO
temp: LONG STRINGSIGNAL StringBoundsFault[to];
IF temp = NIL THEN EXIT; -- just fill in as much as fits
to ← temp;
ENDLOOP;
i ← to.length;
j ← from.offset;
n ← MIN[from.length, LOOPHOLE[to.maxlength - to.length, CARDINAL]];
WHILE n > 0 DO to[i] ← s[j]; i ← i + 1; j ← j + 1; n ← n - 1; ENDLOOP;
to.length ← i;
RETURN
END;
EqualSubString: PROCEDURE [s1, s2: SubString]
RETURNS [BOOLEAN] =
BEGIN
i1, i2, n: CARDINAL;
b1, b2: LONG STRING;
IF s1.length # s2.length THEN RETURN[FALSE];
b1 ← s1.base;
i1 ← s1.offset;
b2 ← s2.base;
i2 ← s2.offset;
FOR n ← s1.length, n - 1 WHILE n > 0 DO
IF b1[i1] # b2[i2] THEN RETURN[FALSE]; i1 ← i1 + 1; i2 ← i2 + 1; ENDLOOP;
RETURN[TRUE]
END;
EquivalentSubString: PROCEDURE [s1, s2: SubString]
RETURNS [BOOLEAN] =
BEGIN OPEN Inline;
casebit: WORD = 40B;
i1, i2, n: CARDINAL;
b1, b2: LONG STRING;
IF s1.length # s2.length THEN RETURN[FALSE];
b1 ← s1.base;
i1 ← s1.offset;
b2 ← s2.base;
i2 ← s2.offset;
FOR n ← s1.length, n - 1 WHILE n > 0 DO
IF BITOR[b1[i1], casebit] # BITOR[b2[i2], casebit] THEN RETURN[FALSE];
i1 ← i1 + 1;
i2 ← i2 + 1;
ENDLOOP;
RETURN[TRUE]
END;
Copy: PROCEDURE [to, from: LONG STRING] = {
n: CARDINAL;
IF to = NIL OR from = NIL THEN RETURN;
WHILE from.length > to.maxlength DO
temp: LONG STRINGSIGNAL StringBoundsFault[to];
IF temp = NIL THEN EXIT; -- just fill in as much as fits
to ← temp;
ENDLOOP;
n ← MIN[from.length, to.maxlength];
Inline.LongCOPY[
from: @from.text,
to: @to.text,
nwords: SIZE[StringBody[n]]-SIZE[StringBody[0]]];
to.length ← n;
RETURN};
DeleteSubString: PROCEDURE [s: SubString] =
BEGIN
b: LONG STRING = s.base;
i: CARDINAL ← s.offset;
j: CARDINAL ← i + s.length;
WHILE j < b.length DO b[i] ← b[j]; i ← i + 1; j ← j + 1; ENDLOOP;
b.length ← i;
RETURN
END;
AppendCharAndGrow: PROCEDURE [
to: LONG POINTER TO LONG STRING, c: CHARACTER, z: UNCOUNTED ZONE] =
BEGIN
extra: CARDINAL ← 6;
temp: LONG STRING ← to^;
IF temp = NIL OR temp.length = temp.maxlength THEN {
temp ← CopyToNewString[temp, z, extra];
FreeString[z, to^];
to^ ← temp;
IF Heap.OwnerChecking[z] THEN SetOwner[temp, GetGF[Runtime.GetCaller[]]]
};
temp[temp.length] ← c;
temp.length ← temp.length + 1;
END;
AppendExtensionIfNeeded: PROCEDURE [
to: LONG POINTER TO LONG STRING, extension: LONG STRING, z: UNCOUNTED ZONE]
RETURNS [done: BOOLEAN] =
BEGIN
i, nCharsToAdd: CARDINAL;
temp: LONG STRING ← to^;
done ← FALSE;
IF EmptyString[temp] THEN RETURN[FALSE];
FOR i DECREASING IN [1..temp.length) DO
SELECT temp[i] FROM
'., Ascii.SP, Ascii.TAB => temp.length ← temp.length - 1;
ENDCASE => EXIT;
ENDLOOP;
FOR i DECREASING IN [0..temp.length) DO
IF temp[i] = '. THEN RETURN[FALSE];
ENDLOOP;
nCharsToAdd ← extension.length + (IF extension[0] # '. THEN 1 ELSE 0);
IF temp.length + nCharsToAdd > temp.maxlength THEN {
temp ← CopyToNewString[temp, z, nCharsToAdd];
FreeString[z, to^];
to^ ← temp;
IF Heap.OwnerChecking[z] THEN SetOwner[temp, GetGF[Runtime.GetCaller[]]]
};
IF extension[0] # '. THEN AppendChar[temp, '.];
AppendString[temp, extension];
done ← TRUE;
END;
AppendStringAndGrow: PROCEDURE [
to: LONG POINTER TO LONG STRING, from: LONG STRING, z: UNCOUNTED ZONE,
extra: CARDINAL ← 0] =
BEGIN
temp: LONG STRING ← to^;
IF temp = NIL OR temp.length + from.length >= temp.maxlength THEN {
temp ← CopyToNewString[temp, z, from.length + extra];
FreeString[z, to^];
to^ ← temp;
IF Heap.OwnerChecking[z] THEN SetOwner[temp, GetGF[Runtime.GetCaller[]]]
};
AppendString[to: temp, from: from];
END;
Replace: PROCEDURE [
to: LONG POINTER TO LONG STRING, from: LONG STRING, z: UNCOUNTED ZONE] =
BEGIN
IF to = NIL THEN RETURN[];
IF to^ # NIL THEN {FreeString[z, to^]; to^ ← NIL};
IF from # NIL THEN to^ ← CopyToNewString[from, z];
IF Heap.OwnerChecking[z] THEN SetOwner[to^, GetGF[Runtime.GetCaller[]]];
RETURN
END;
CopyToNewString: PROCEDURE [
s: LONG STRING, z: UNCOUNTED ZONE, longer: CARDINAL ← 0]
RETURNS [newS: LONG STRING] =
BEGIN
l: CARDINAL = (IF s = NIL THEN 0 ELSE s.length);
IF s = NIL AND longer = 0 THEN RETURN[NIL];
newS ← MakeString[z, l + longer];
Inline.LongCOPY[
from: @s.text,
to: @newS.text,
nwords: SIZE[StringBody[l]]-SIZE[StringBody[0]]];
newS.length ← l;
IF Heap.OwnerChecking[z] THEN
LOOPHOLE[(newS - 1), LONG POINTER]^ ← GetGF[Runtime.GetCaller[]];
END;
ExpandString: PROCEDURE [
s: LONG POINTER TO LONG STRING, longer: CARDINAL, z: UNCOUNTED ZONE] =
BEGIN
newS: LONG STRING ← CopyToNewString[
s^, z, longer + (IF s^ = NIL THEN 0 ELSE s.maxlength - s.length)];
FreeString[z, s^];
s^ ← newS;
IF Heap.OwnerChecking[z] THEN
LOOPHOLE[(newS - 1), LONG POINTER]^ ← GetGF[Runtime.GetCaller[]];
END;
GetGF: PRIVATE PROCEDURE [p: PROGRAM] RETURNS [POINTER] = INLINE {
RETURN[Inline.LowHalf[LOOPHOLE[p]]]};
SetOwner: PRIVATE PROCEDURE [
node: LONG STRING, owner: UNSPECIFIED] = INLINE {
IF node#NIL THEN LOOPHOLE[node - 1, LONG POINTER]^ ← owner};
Overflow: SIGNAL = CODE;
InvalidNumber: SIGNAL = CODE;
NUL: CHARACTER = 0C;
Space: CHARACTER = ' ;
UpperCase: PROCEDURE [c: CHARACTER] RETURNS [CHARACTER] =
BEGIN IF c IN ['a..'z] THEN c ← c + ('A - 'a); RETURN[c] END;
LowerCase: PROCEDURE [c: CHARACTER] RETURNS [CHARACTER] =
BEGIN IF c IN ['A..'Z] THEN c ← c + ('a - 'A); RETURN[c] END;
Compare: PROCEDURE [s1, s2: LONG STRING, ignoreCase: BOOLEANTRUE]
RETURNS [INTEGER] =
BEGIN
i: CARDINAL;
l1: CARDINAL = s1.length;
l2: CARDINAL = s2.length;
c1, c2: CHARACTER;
FOR i IN [0..MIN[l1, l2]) DO
c1 ← s1[i];
c2 ← s2[i];
IF ignoreCase THEN BEGIN c1 ← UpperCase[c1]; c2 ← UpperCase[c2]; END;
SELECT c1 FROM < c2 => RETURN[-1]; > c2 => RETURN[1]; ENDCASE;
ENDLOOP;
RETURN[SELECT l1 FROM < l2 => -1, > l2 => 1, ENDCASE => 0]
END;
StringToDecimal: PROCEDURE [s: LONG STRING] RETURNS [INTEGER] = {
RETURN[StringToNumber[s, 10]]};
StringToOctal: PROCEDURE [s: LONG STRING] RETURNS [CARDINAL] = {
RETURN[StringToNumber[s, 8]]};
StringToNumber: PROCEDURE [s: LONG STRING, radix: CARDINAL]
RETURNS [v: UNSPECIFIED] =
BEGIN OPEN Inline;
char: CHARACTER;
cp: CARDINAL ← 0;
v8, v10: CARDINAL ← 0;
neg: BOOLEANFALSE;
getchar: PROCEDURE =
BEGIN char ← IF cp >= s.length THEN NUL ELSE s[cp]; cp ← cp + 1; END;
getchar[];
WHILE char <= Space DO
IF char = NUL THEN SIGNAL InvalidNumber; getchar[]; ENDLOOP;
IF char = '- THEN BEGIN neg ← TRUE; getchar[] END;
WHILE char IN ['0..'9] DO
v10 ← v10*10 + (char - '0); v8 ← v8*8 + (char - '0); getchar[]; ENDLOOP;
BEGIN
SELECT LOOPHOLE[BITAND[char, 137B], CHARACTER] FROM
NUL => GOTO noexponent;
'B => BEGIN v ← v8; radix ← 8; END;
'D => BEGIN v ← v10; radix ← 10; END;
ENDCASE => GOTO noexponent;
getchar[];
v10 ← 0;
WHILE char IN ['0..'9] DO v10 ← v10*10 + char - '0; getchar[]; ENDLOOP;
THROUGH [1..v10] DO v ← v*radix ENDLOOP;
EXITS noexponent => v ← IF radix = 8 THEN v8 ELSE v10;
END;
IF char # NUL THEN SIGNAL InvalidNumber;
IF neg THEN RETURN[-v];
END;
AppendNumber: PROCEDURE [s: LONG STRING, n: CARDINAL, radix: CARDINAL] =
BEGIN
ps: POINTER TO LONG STRING = @s;
xn: PROCEDURE [n: CARDINAL] =
BEGIN
r: CARDINAL;
[n, r] ← Inline.DIVMOD[n, radix];
IF n # 0 THEN xn[n];
IF r > 9 THEN r ← r + 'A - '0 - 10;
AppendChar[s, r + '0];
END;
xn[n ! StringBoundsFault => RESUME[ps^ ← StringBoundsFault[s]]];
END;
AppendDecimal: PROCEDURE [s: LONG STRING, n: INTEGER] =
BEGIN
IF n < 0 THEN
BEGIN
ps: POINTER TO LONG STRING = @s;
IF n = LAST[INTEGER] THEN BEGIN AppendString[s, "-32768"L]; RETURN END;
AppendChar[s, '- ! StringBoundsFault => RESUME[ps^ ← StringBoundsFault[s]]];
n ← -n
END;
AppendNumber[s, n, 10];
END;
AppendOctal: PROCEDURE [s: LONG STRING, n: UNSPECIFIED] =
BEGIN AppendNumber[s, n, 8]; AppendChar[s, 'B]; END;
AppendLongDecimal: PROCEDURE [s: LONG STRING, n: LONG INTEGER] =
BEGIN
IF n < 0 THEN
BEGIN
ps: POINTER TO LONG STRING = @s;
IF n = LAST[LONG INTEGER] THEN
BEGIN AppendString[s, "-2147483648"L]; RETURN END;
AppendChar[s, '- ! StringBoundsFault => RESUME[ps^ ← StringBoundsFault[s]]];
n ← -n
END;
AppendLongNumber[s, n, 10];
END;
AppendLongNumber: PROCEDURE [
s: LONG STRING, n: LONG UNSPECIFIED, radix: CARDINAL] =
BEGIN
ps: POINTER TO LONG STRING = @s;
xn: PROCEDURE [n: LONG CARDINAL] =
BEGIN
lr: LONG CARDINAL;
r: CARDINAL;
[n, lr] ← Inline.UDDivMod[n, radix];
IF n # 0 THEN xn[n];
IF (r ← Inline.LowHalf[lr]) > 9 THEN r ← r + 'A - '0 - 10;
AppendChar[s, r + '0];
END;
xn[n ! StringBoundsFault => RESUME[ps^ ← StringBoundsFault[s]]];
END;
StringToLongNumber: PROCEDURE [s: LONG STRING, radix: CARDINAL]
RETURNS [v: LONG CARDINAL] =
BEGIN OPEN Inline;
char: CHARACTER;
cp: CARDINAL ← 0;
exp: CARDINAL;
v8, v10: LONG INTEGER ← 0;
neg: BOOLEANFALSE;
getchar: PROCEDURE =
BEGIN char ← IF cp >= s.length THEN NUL ELSE s[cp]; cp ← cp + 1; END;
getchar[];
WHILE char <= Space DO
IF char = NUL THEN SIGNAL InvalidNumber; getchar[]; ENDLOOP;
IF char = '- THEN BEGIN neg ← TRUE; getchar[] END;
WHILE char IN ['0..'9] DO
v10 ← v10*10 + CARDINAL[char - '0];
v8 ← v8*8 + CARDINAL[char - '0];
getchar[];
ENDLOOP;
BEGIN
SELECT LOOPHOLE[BITAND[char, 137B], CHARACTER] FROM
NUL => GOTO noexponent;
'B => BEGIN v ← v8; radix ← 8; END;
'D => BEGIN v ← v10; radix ← 10; END;
ENDCASE => GOTO noexponent;
getchar[];
exp ← 0;
WHILE char IN ['0..'9] DO exp ← exp*10 + char - '0; getchar[]; ENDLOOP;
THROUGH [1..exp] DO v ← v*radix ENDLOOP;
EXITS noexponent => v ← IF radix = 8 THEN v8 ELSE v10;
END;
IF char # NUL THEN SIGNAL InvalidNumber;
IF neg THEN RETURN[-v];
END;
END....