-- Transport Mechanism Registration Server:  cache for name lookup

-- [Juniper]<Grapevine>MS>RegCache.mesa

-- Randy Gobbel		19-May-81 12:18:12
-- Andrew Birrell	25-Jun-81 16:06:05

DIRECTORY
BodyDefs	USING[ Connect, RName, Timestamp ],
ObjectDirDefs	USING[ ObjectNumber ],
ProtocolDefs	USING[ RNameType ],
RegBTreeDefs	USING[ RegState ],
RegCacheDefs	USING[ ],
Storage		USING[ FreeString, String ],
String		USING[ AppendString, EquivalentStrings,
		       EquivalentSubStrings, SubStringDescriptor ];

RegCache: MONITOR
IMPORTS Storage, String
EXPORTS RegCacheDefs =

BEGIN

-- cache is extremely simple:
--   Linear search for look-up,
--   Round-robin replacement algorithm,
--   Only keeps names in "GV" and "MS" registries.
--   Doesn't keep Log-* nor Archive-*

BeginsWith: PROC[s: STRING, b: STRING] RETURNS[ BOOLEAN ] =
   BEGIN
   pattern: String.SubStringDescriptor ← [b, 0, b.length];
   target: String.SubStringDescriptor ← [s, 0, b.length];
   RETURN[ s.length >= b.length
           AND String.EquivalentSubStrings[@pattern,@target] ]
   END;

EndsWith: PROC[s: STRING, b: STRING] RETURNS[ BOOLEAN ] = INLINE
   BEGIN
   pattern: String.SubStringDescriptor ← [b, 0, b.length];
   target: String.SubStringDescriptor ← [s,s.length-b.length,b.length];
   RETURN[ s.length >= b.length
           AND String.EquivalentSubStrings[@pattern,@target] ]
   END;

Interesting: PROC[ name: BodyDefs.RName ] RETURNS[ BOOLEAN ] = INLINE
   { RETURN[ EndsWith[name, ".ms"L] OR EndsWith[name, ".GV"L] ] };


NameRec: TYPE = RECORD[name: STRING,
                       value: NameVal,
                       connect: BodyDefs.Connect ];

NameVal: TYPE = RECORD[knownReg: BOOLEAN,
                       type: ProtocolDefs.RNameType,
                       stamp: BodyDefs.Timestamp,
                       obj: ObjectDirDefs.ObjectNumber];

CacheIndex: TYPE = [0..25);

cache: ARRAY CacheIndex OF NameRec ← ALL[[name:NIL,value:,connect:NIL]];

rover: CARDINAL ← FIRST[CacheIndex];

AddName: PUBLIC ENTRY PROC[ name: BodyDefs.RName,
                            knownReg: BOOLEAN,
                            type: ProtocolDefs.RNameType,
                            stamp: BodyDefs.Timestamp,
                            obj: ObjectDirDefs.ObjectNumber] =
   BEGIN
   IF Interesting[name]
   AND NOT BeginsWith[name, "Archive-"L]
   AND NOT BeginsWith[name, "Log-"L]
   THEN BEGIN
        InnerFlush[name];
        RemoveEntry[rover];
        cache[rover] ← [ Storage.String[name.length],
                         [knownReg, type, stamp, obj], NIL ];
        String.AppendString[cache[rover].name, name];
        rover ← IF rover = LAST[CacheIndex] THEN FIRST[CacheIndex]
                ELSE SUCC[rover];
        END;
   END;

ReadName: PUBLIC ENTRY PROC[ name: BodyDefs.RName ]
                 RETURNS[ type: ProtocolDefs.RNameType,
                          stamp: BodyDefs.Timestamp,
                          obj: ObjectDirDefs.ObjectNumber ] =
   BEGIN
   type ← notFound;
   IF Interesting[name]
   THEN FOR index: CacheIndex IN CacheIndex
        DO IF String.EquivalentStrings[cache[index].name, name]
           THEN { [,type,stamp,obj] ← cache[index].value; EXIT };
        ENDLOOP;
   END;

FlushName: PUBLIC ENTRY PROC[ name: BodyDefs.RName ] =
   { IF Interesting[name] THEN InnerFlush[name] };

TestKnownReg: PUBLIC ENTRY PROC[ name: BodyDefs.RName ]
                 RETURNS[ state: RegBTreeDefs.RegState ] =
   BEGIN
   state ← bad;
   IF Interesting[name]
   THEN FOR index: CacheIndex IN CacheIndex
        DO IF String.EquivalentStrings[cache[index].name, name]
           THEN { state ← IF cache[index].value.knownReg THEN yes ELSE no;
                  EXIT };
        ENDLOOP;
   END;

InnerFlush: INTERNAL PROC[ name: BodyDefs.RName ] =
   BEGIN
   FOR index: CacheIndex IN CacheIndex
   DO IF String.EquivalentStrings[cache[index].name, name]
      THEN { RemoveEntry[index]; EXIT };
   ENDLOOP;
   END;

RemoveEntry: INTERNAL PROC[ index: CacheIndex ] =
   BEGIN
   IF cache[index].name # NIL
   THEN { Storage.FreeString[cache[index].name];
          cache[index].name ← NIL };
   
   IF cache[index].connect # NIL
   THEN { Storage.FreeString[cache[index].connect];
          cache[index].connect ← NIL };
   END;

AddConnect: PUBLIC ENTRY PROC[ name: BodyDefs.RName,
                               stamp: BodyDefs.Timestamp,
                               connect: BodyDefs.Connect ] =
   BEGIN
   IF Interesting[name]
   THEN FOR index: CacheIndex IN CacheIndex
        DO IF String.EquivalentStrings[cache[index].name, name]
           THEN BEGIN
                IF cache[index].connect # NIL
                THEN Storage.FreeString[cache[index].connect];
                IF stamp = cache[index].value.stamp
                THEN BEGIN
                     cache[index].connect ←
                            Storage.String[connect.length];
                     String.AppendString[cache[index].connect, connect];
                     END
                ELSE cache[index].connect ← NIL;
                EXIT
                END;
        ENDLOOP;
   END;

ReadConnect: PUBLIC ENTRY PROC[ name: BodyDefs.RName,
                               connect: BodyDefs.Connect ]
                RETURNS[ found: BOOLEAN ] =
   BEGIN
   found ← FALSE;
   IF Interesting[name]
   THEN FOR index: CacheIndex IN CacheIndex
        DO IF String.EquivalentStrings[cache[index].name, name]
           THEN BEGIN
                IF cache[index].connect # NIL
                THEN BEGIN
                     connect.length ← 0;
                     String.AppendString[connect, cache[index].connect];
                     found ← TRUE;
                     END;
                EXIT
                END;
        ENDLOOP;
   END;

END.