-- RopeFromImpl.Mesa
-- written by Bill Paxton,  February 1981
-- last edit by Bill Paxton,  December 22, 1981 11:45 am

DIRECTORY
	RopeFrom,
	RopeEdit,
	RopeEditingAlloc,
	RopeReader,
	Rope,
	RopeInline,
	SafeStorage,
	Space,
	Directory,
	RopeEditingBLT;

RopeFromImpl: PROGRAM
   IMPORTS
	RopeEditingBLT, RopeEditingAlloc, RopeReader, RopeInline,
	SafeStorage, RopeFrom, RopeEdit
   EXPORTS RopeFrom
   SHARES Rope =
BEGIN
OPEN
	RopeFrom, RopeInline;

CharsArray: TYPE = RopeReader.CharsArray;
Chars: TYPE = REF CharsArray;
charsPerArray: NAT = RopeReader.charsPerArray;

qZone: PUBLIC ZONE ← SafeStorage.NewZone[quantized]; 
pZone: PUBLIC ZONE ← SafeStorage.NewZone[prefixed]; 


-- Editing Operations

FlatReplaceByChar: PUBLIC PROC [
	base: ROPE, char: CHAR,
	start: Offset ← 0, len, baseSize: Offset ← MaxLen,
	tryAppend: BOOLEAN ← TRUE]
	RETURNS [new: ROPE] = {
	chars: Chars;
	charsRope: ROPE;
	initloc, loc, strt, sze, num, baseNum: NAT;
	reader: RopeReader.Ref;
	size: Offset;
	Get: PROC [count: NAT] = {
		IF count > 0 THEN {
			IF RopeReader.GetChars[reader,chars,count,loc] # count THEN ERROR;
			loc ← loc+count}};
	IF baseSize=MaxLen THEN baseSize ← RopeInline.InlineSize[base];
	IF baseSize=0 THEN RETURN [Character[char]];
	IF start > baseSize THEN start ← baseSize;
	IF len=MaxLen THEN len ← MIN[len,baseSize-start];
	IF (size←baseSize+1-len) > charsPerArray THEN RETURN [ -- treat separately
		FlatReplace[base,start,len,Character[char],size,baseSize,1]];
	IF tryAppend AND
		(new ← TryAppendReplaceByChar[base,char,start,len,baseSize]) # NIL THEN
		RETURN [new];
	sze ← Short[size]; strt ← Short[start];
	num ← Short[len]; baseNum ← Short[baseSize];
	[chars, loc, charsRope] ← RopeEditingAlloc.AllocChars[sze];
	initloc ← loc;
	reader ← RopeReader.GetRopeReader[];
	RopeReader.SetPosition[reader,base];
	Get[strt];
	chars[loc] ← char; loc ← loc+1;
	IF num > 0 THEN RopeReader.SetIndex[reader,strt+num];
	Get[baseNum-(strt+num)];
	RopeReader.FreeRopeReader[reader];
	IF loc-initloc # sze THEN ERROR;
	RETURN [qZone.NEW[Tsubstr ← [node[substr[size,charsRope,initloc,1+RopeEdit.Depth[charsRope]]]]]]};

TryAppendReplaceByChar: PUBLIC PROC [
	base: ROPE, char: CHAR,
	start: Offset ← 0, len, baseSize: Offset ← MaxLen]
	RETURNS [ROPE] = {
	-- returns NIL if cannot do a flat replace
	size: Offset;
	IF baseSize=MaxLen THEN baseSize ← RopeInline.InlineSize[base];
	IF baseSize=0 THEN RETURN [Character[char]];
	IF start > baseSize THEN start ← baseSize;
	IF len=MaxLen THEN len ← MIN[len,baseSize-start];
	IF (size←baseSize+1-len) > charsPerArray THEN RETURN [NIL];
	IF start=baseSize THEN { -- appending to end of rope
	    xstart: Offset;
	    chars: Chars;
	    sze: NAT ← Short[size];
	    strt: NAT ← Short[start];
	    num: NAT ← Short[len];
	    baseNum: NAT ← Short[baseSize];
	    WITH x:base SELECT FROM -- try to tack onto end of previous chars
	    node => WITH x:x SELECT FROM
		substr => IF (xstart←x.start) < charsPerArray THEN {
			ok: BOOLEAN;
			loc: NAT;
			newbase: ROPE ← x.base;
			xstrt: NAT ← Short[xstart];
			[ok,chars] ← RopeEditingAlloc.TryAllocAdjacent[newbase,loc←baseNum+xstrt,1];
			IF ok THEN { -- got it
				chars[loc] ← char;
				RETURN [qZone.NEW[Tsubstr ←
					[node[substr[size,newbase,xstart,1+RopeEdit.Depth[newbase]]]]]]}};
	    ENDCASE; ENDCASE };
	RETURN [NIL] };

FlatReplaceByString: PUBLIC PROC [
	base: ROPE, string: REF READONLY TEXT,
	stringStart: NAT ← 0, stringNum: NAT ← MaxNat,
	start: Offset ← 0, len, baseSize: Offset ← MaxLen,
	tryAppend: BOOLEAN ← TRUE]
	RETURNS [new: ROPE] = {
	chars: Chars;
	charsRope: ROPE;
	initloc, loc, strt, sze, num, baseNum: NAT;
	reader: RopeReader.Ref;
	size: Offset;
	Get: PROC [count: NAT] = {
		IF count > 0 THEN {
			IF RopeReader.GetChars[reader,chars,count,loc] # count THEN ERROR;
			loc ← loc+count}};
	IF baseSize=MaxLen THEN baseSize ← RopeInline.InlineSize[base];
	IF baseSize=0 THEN RETURN [String[string,stringStart,stringNum]];
	IF start > baseSize THEN start ← baseSize;
	IF len=MaxLen THEN len ← MIN[len,baseSize-start];
	IF stringNum=MaxNat THEN {
		stringSize: NAT ← IF string=NIL THEN 0 ELSE string.length;
		IF stringStart >= stringSize THEN
			RETURN [FlatDelete[base,start,len,baseSize]];
		stringNum ← stringSize-stringStart };
	IF (size←baseSize+stringNum-len) > charsPerArray THEN -- treat separately
		RETURN [FlatReplace[base,start,len,String[string,stringStart,stringNum],
			size,baseSize,stringNum]];
	IF tryAppend AND
		(new ← TryAppendReplaceByString[base,string,stringStart,stringNum,start,len,baseSize]) # NIL
		THEN RETURN [new];
	IF size=0 THEN RETURN[NIL];
	sze ← Short[size]; strt ← Short[start];
	num ← Short[len]; baseNum ← Short[baseSize];
	[chars, loc, charsRope] ← RopeEditingAlloc.AllocChars[sze];
	initloc ← loc;
	reader ← RopeReader.GetRopeReader[];
	RopeReader.SetPosition[reader,base];
	Get[strt];
	RopeEditingBLT.StringToArrayBlt[nChars:stringNum, to:chars, toLoc:loc, from:string, fromLoc:stringStart];
	loc ← loc+stringNum;
	IF num > 0 THEN RopeReader.SetIndex[reader,strt+num];
	Get[Short[baseSize]-(strt+num)];
	RopeReader.FreeRopeReader[reader];
	IF loc-initloc # sze THEN ERROR;
	RETURN [qZone.NEW[Tsubstr ← [node[substr[size,charsRope,initloc,1+RopeEdit.Depth[charsRope]]]]]]};

TryAppendReplaceByString: PUBLIC PROC [
	base: ROPE, string: REF READONLY TEXT,
	stringStart: NAT ← 0, stringNum: NAT ← MaxNat,
	start: Offset ← 0, len, baseSize: Offset ← MaxLen]
	RETURNS [ROPE] = {
	size: Offset;
	sze, strt, num, baseNum, loc: NAT;
	IF baseSize=MaxLen THEN baseSize ← RopeInline.InlineSize[base];
	IF baseSize=0 THEN RETURN [String[string,stringStart,stringNum]];
	IF start > baseSize THEN start ← baseSize;
	IF len=MaxLen THEN len ← MIN[len,baseSize-start];
	IF stringNum=MaxNat THEN {
		stringSize: NAT ← IF string=NIL THEN 0 ELSE string.length;
		IF stringStart >= stringSize THEN
			RETURN [FlatDelete[base,start,len,baseSize]];
		stringNum ← stringSize-stringStart };
	IF (size←baseSize+stringNum-len) > charsPerArray THEN  RETURN [NIL];
	IF size=0 THEN RETURN[NIL];
	sze ← Short[size]; strt ← Short[start];
	num ← Short[len]; baseNum ← Short[baseSize];
	IF start = baseSize THEN { -- appending string to end of rope
	    xstart: Offset;
	    chars: Chars;
	    WITH x:base SELECT FROM -- try to tack onto end of previous chars
	    node => WITH x:x SELECT FROM
		substr => IF (xstart←x.start) < charsPerArray THEN {
			ok: BOOLEAN;
			newbase: ROPE ← x.base;
			[ok,chars] ← RopeEditingAlloc.TryAllocAdjacent[newbase,
				loc←baseNum+Short[xstart],stringNum];
			IF ok THEN { -- got it
				RopeEditingBLT.StringToArrayBlt[nChars:stringNum, to:chars, toLoc:loc,
					from:string, fromLoc:stringStart];
				RETURN [qZone.NEW[Tsubstr ← [node[substr[size,newbase,xstart,1+RopeEdit.Depth[newbase]]]]]]}};
	    ENDCASE; ENDCASE };
	RETURN [NIL] };

String: PUBLIC PROC
	[string: REF READONLY TEXT, start: NAT ← 0, len: NAT ← LAST[NAT]]
	RETURNS [ROPE] = {
	-- copies len characters from string starting at start
	MakeRope: PROC [string: REF READONLY TEXT, start, len: NAT]
			RETURNS [ROPE] = {
		chars: Chars;
		loc: NAT;
		base: ROPE;
		IF len = 0 THEN RETURN [NIL];
		IF len > charsPerArray THEN { -- split into balanced parts
			half: NAT ← len/2;
			front: ROPE ← MakeRope[string, start, half];
			back: ROPE ← MakeRope[string, start+half, len-half];
			RETURN [qZone.NEW[Tconcat ← [node[concat[len,front,back,half,
				1+MAX[RopeEdit.Depth[front],RopeEdit.Depth[back]]]]]]] };
		[chars, loc, base] ← RopeEditingAlloc.AllocChars[len];
		RopeEditingBLT.StringToArrayBlt[nChars:len, to:chars, toLoc:loc,
			from:string, fromLoc:start];
		RETURN [qZone.NEW[Tsubstr ← [node[substr[len,base,loc,1+RopeEdit.Depth[base]]]]]]};
	IF string=NIL OR start >= string.length THEN RETURN [NIL];
	len ← MIN[len,string.length-start];
	RETURN [MakeRope[string,start,len]]};

Character: PUBLIC PROC [char: CHARACTER] RETURNS [ROPE] = {
	chars: Chars;
	loc: NAT;
	base: ROPE;
	[chars, loc, base] ← RopeEditingAlloc.AllocChars[1];
	chars[loc] ← char;
	RETURN [qZone.NEW[Tsubstr ← [node[substr[1,base,loc,1+RopeEdit.Depth[base]]]]]]};


-- ***** Initialization

StartRopeFrom: PUBLIC PROC = {
	};

END.