-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- RegCache.mesa:  cache for name lookup

-- HGM, 15-Sep-85  5:14:27
-- Randy Gobbel		19-May-81 12:18:12
-- Andrew Birrell	25-Jun-81 16:06:05

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

RegCache: MONITOR IMPORTS Heap, 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: LONG STRING, b: LONG 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: LONG STRING, b: LONG 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: LONG STRING ← NIL, value: NameVal, connect: LONG STRING ← NIL];

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

  CacheIndex: TYPE = [0..25);

  Cache: TYPE = ARRAY CacheIndex OF NameRec;
  cache: LONG POINTER TO Cache ← Heap.systemZone.NEW[Cache];

  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] ← [
        Heap.systemZone.NEW[StringBody[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 Heap.systemZone.FREE[@cache[index].name];
    IF cache[index].connect # NIL THEN Heap.systemZone.FREE[@cache[index].connect];
    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].name # NIL THEN Heap.systemZone.FREE[@cache[index].name];
          IF stamp = cache[index].value.stamp THEN
            BEGIN
            cache[index].connect ← Heap.systemZone.NEW[StringBody[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.