DIRECTORY
  ConvertUnsafe: TYPE USING [AppendRope],
  Directory: TYPE USING [Error, GetProps, Handle, Lookup],
  File: TYPE USING [Capability, GetSize, nullCapability, read],
  IO: TYPE USING [Close, CreateInputStreamFromRope, GetInt, int, PutFToRope, ROPE],
  PL: TYPE USING[PBug, RErr],
  Process: TYPE USING [Detach],
  PString: TYPE USING [Stream],
  Rope: TYPE USING [Fetch, Flatten, Length, MakeRope, MapType,
  	ROPE, Substr],
  RTTypesBasic: TYPE USING [EstablishFinalization, FinalizationQueue,
  	FQNext, NewFQ],
  Space: TYPE USING [Create, Delete, Handle, LongPointer, MakeReadOnly,
  	Map, virtualMemory];

PStringImpl: CEDAR PROGRAM 
	IMPORTS ConvertUnsafe, Directory, File, IO, PL, Process, Rope, 
		RTTypesBasic, Space
	EXPORTS PString 
	SHARES PString = {
--
OPEN Rope, IO;
Stream: TYPE = PString.Stream;
FileHandle: TYPE = REF FileRecord;
FileRecord: PRIVATE TYPE = RECORD[len: INT, s: Space.Handle, p: LONG POINTER TO PACKED ARRAY [0..0) OF CHARACTER];
fq: RTTypesBasic.FinalizationQueue;
BoundsError: ERROR = CODE;
MinCoerceSize: LONG INTEGER = 100;
OpenFiles: LIST OF FileHandle ← LIST[NIL];
MTSt: ROPE = "";

Cardinal: PUBLIC PROC[in: LONG INTEGER] RETURNS [CARDINAL]  = {
IF in<0 OR in > 177777B THEN PL.PBug["integer too big"];
RETURN[in];
};

Close: PROC [f: FileHandle] =
 {TRUSTED{Space.Delete[f.s]};
  FOR t: LIST OF FileHandle ← OpenFiles, t.rest UNTIL t=NIL
  	DO IF t.first=f THEN t↑ ← t.rest↑
	ENDLOOP};

CopyStream: PUBLIC PROC[n: Stream] RETURNS[Stream] = {RETURN[n]};

ConvertStream: PUBLIC PROC[s: Stream] RETURNS [ans: ROPE] = {
IF s.node=NIL THEN RETURN [MTSt];
RETURN[Substr[s.node, s.place]]};

Empty: PUBLIC PROC[n: ROPE] RETURNS [BOOLEAN] =
     {RETURN[Rope.Length[n]=0]};

EmptyS: PUBLIC PROC[n: Stream] RETURNS [BOOLEAN] =
     {RETURN[n.node=NIL]};

MyFetch: PROC[data: REF, index: INT] RETURNS[CHAR] = TRUSTED {
	fh: FileHandle ← NARROW[data];
	IF index >= fh.len THEN ERROR BoundsError;
	RETURN[(fh.p+(index/2))[index MOD 2]]};

Finalizer: PROC = {
DO
fh: FileHandle = NARROW -- LOOPHOLE -- [RTTypesBasic.FQNext[fq]];
Close[fh];
ENDLOOP};

Item: PUBLIC PROC[s: Stream] RETURNS[c: CHARACTER, ns: Stream] = {
IF s.node=NIL THEN RETURN[0C, s];
ns ← [place: s.place+1, node: s.node];
c ← Fetch[s.node, s.place];
IF ns.place=Length[s.node] THEN ns.node ← NIL;
};

MakeInteger: PUBLIC PROC[n: ROPE] RETURNS[cnt: INT] = {
    RETURN[GetInt[IO.CreateInputStreamFromRope[n]]]
    };

MakeNUM: PUBLIC PROC[i: INT] RETURNS [ROPE] =
      {RETURN[PutFToRope[,int[i]]]};

MyMap: MapType = TRUSTED {
fh: FileHandle ← NARROW[base];
IF start>fh.len THEN ERROR BoundsError;
FOR i: INT IN [start..MIN[fh.len, start+len]) DO
	IF action[(fh.p+(i/2))[i MOD 2]] THEN RETURN[TRUE];
	ENDLOOP;
RETURN[FALSE]};

NewStream: PUBLIC PROC[n: ROPE] RETURNS[s: Stream] = {
IF Empty[n] THEN n ← NIL;
RETURN[[place: 0, node: n]]
};

Open: PROC [rname: ROPE]
      RETURNS [fh: FileHandle, len: LONG INTEGER] =
TRUSTED{cap: File.Capability ← File.nullCapability;
 name: STRING = [100];
 name.length ← 0;
 ConvertUnsafe.AppendRope[name, rname];
  cap ← Directory.Lookup[fileName: name, permissions: File.read
			 ! Directory.Error--[type]-- => IF type=fileNotFound THEN CONTINUE];
IF cap =  File.nullCapability THEN RETURN[NIL,0];
fh ← NEW[FileRecord];  
{t: INT = File.GetSize[cap];
fh.s ← Space.Create[Cardinal[t-1], Space.virtualMemory];
fh.p ← Space.LongPointer[fh.s];
Space.Map[fh.s, [file: cap, base: 1]];
Space.MakeReadOnly[fh.s];
[,,,len,] ← Directory.GetProps[cap, name];
OpenFiles ← CONS[fh, OpenFiles];
}};

StringToFile: PUBLIC PROC[name: ROPE] RETURNS[ans:ROPE] = {
fi: FileHandle;
len: LONG INTEGER;
IF name = NIL THEN PL.RErr["File name must be non-null string"];
IF Length[name] >= 100 THEN PL.RErr["File name too long (>100)"];
ans ← NIL;
[fi, len] ← Open[name];
IF fi=NIL THEN ans ← NIL
ELSE {fi.len ← len;
      ans ← MakeRope[fi, len, MyFetch, MyMap];
      IF Length[ans] < MinCoerceSize THEN ans ← Rope.Flatten[ans]};
};

Sub: PUBLIC PROC[n: ROPE, inx: LONG INTEGER] RETURNS[CHARACTER] = {RETURN[Fetch[n, inx]]};

SubStream: PUBLIC PROC[s:Stream,i: LONG INTEGER] RETURNS [CHARACTER] = {
RETURN[IF s.node = NIL THEN 0C ELSE Sub[s.node,s.place+i]];
};

SubString: PUBLIC PROC[str: ROPE, ii, jj: LONG INTEGER] RETURNS [ans: ROPE] =
      {RETURN[Substr[str, ii, jj-ii]]};


SubStringStream: PUBLIC PROC[s: Stream,i,j: LONG INTEGER] RETURNS [ROPE] = {
RETURN[IF s.node = NIL THEN MTSt ELSE SubString[s.node,s.place+i, s.place+j]];
};


fq ← RTTypesBasic.NewFQ[];
RTTypesBasic.EstablishFinalization[CODE[FileRecord], 1, fq];
TRUSTED{Process.Detach[FORK Finalizer]}
}.