-- 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.