-- File: AddressCacheImpl.mesa - last edit:
-- AOF                 31-Aug-86 18:11:17
-- BGY                 21-Aug-84 20:15:57
-- Copyright (C) 1984, 1986 by Xerox Corporation. All rights reserved. 

DIRECTORY
  AddressCache,
  Heap,
  NSName,
  NSString,
  String,
  System;
  
AddressCacheImpl: MONITOR 
  IMPORTS Heap, NSName, NSString, String
  EXPORTS AddressCache = {
  
  Handle: TYPE = LONG POINTER TO Object;
  
  Object: TYPE = RECORD[
    name: LONG STRING ← NIL,
    addr: System.NetworkAddress ← System.nullNetworkAddress,
    next: Handle ← NIL];
  
  root, free: Handle ← NIL;
  cacheSize, inCache: CARDINAL ← 0;
  avail, hits, accesses: CARDINAL ← 0;
  zone: UNCOUNTED ZONE ← Heap.Create[
    initial: 5, increment: 5, largeNodeThreshold: 256];
  
  GetSize: PUBLIC ENTRY PROC RETURNS[size: CARDINAL] = {size ← cacheSize};
  
  SetSize: PUBLIC ENTRY PROC[size: CARDINAL] = {
    Remove: PROC[this: Handle] RETURNS [Handle]= {
      IF this.next # NIL THEN this.next ← Remove[this.next];
      IF size = cacheSize THEN RETURN[this];
      zone.FREE[@this.name];
      zone.FREE[@this];
      inCache ← inCache - 1;
      cacheSize ← cacheSize - 1;
      RETURN[this];
      };
    
    IF size = cacheSize THEN RETURN
    ELSE IF size < cacheSize THEN {
      -- remove from free list, then cache
      IF free # NIL THEN free ← Remove[free];
      IF size < cacheSize THEN root ← Remove[root]}
    ELSE {  -- add to free list
      delta: CARDINAL ← size - cacheSize;
      THROUGH [0..delta) DO free ← zone.NEW[Object ← [next: free]] ENDLOOP;
      cacheSize ← size;
      };
    };
  
  Flush: PUBLIC ENTRY PROC = {
    -- remove from cache to free list
    this: Handle ← NIL;
    FOR this ← root, root UNTIL this = NIL DO
      root ← this.next;
      zone.FREE[@this.name];
      this↑ ←  [
        name: NIL,
        addr: System.nullNetworkAddress,
        next: free];
      free ← this;
      ENDLOOP;
    inCache ← 0;
    };
    
  AddNSName: PUBLIC PROC[name: NSName.Name, addr: System.NetworkAddress] = {
    qualified: LONG STRING ← [NSName.maxFullNameLength];
    string: NSString.String ← NSString.StringFromMesaString[qualified];
    string ← NSName.AppendNameToString[string, name, TRUE];
    qualified.length ← string.length;
    AddEntry[qualified, addr];
    };
    
  AddEntry: PUBLIC ENTRY PROC[name: LONG STRING, addr: System.NetworkAddress] = {
    IF cacheSize = 0 THEN RETURN;
    IF free # NIL THEN {  -- take from free list to cache
      new: Handle ← free;
      free ← new.next;
      new↑ ← [
        name: String.CopyToNewString[name, zone],
	addr: addr, next: root];
      root ← new;
      inCache ← inCache + 1;
      }
    ELSE {  -- have to remove least recently used entry
      last: Handle ← root;
      lru: Handle ← root;
      IF root = NIL THEN RETURN;
      FOR i: Handle ← root, i.next UNTIL i = NIL DO
        IF i.next # NIL THEN last ← i ELSE lru ← i;
	ENDLOOP;
      last.next ← NIL;
      zone.FREE[@lru.name];
      lru↑ ← [
        name: String.CopyToNewString[name, zone],
	addr: addr, next: root];
      root ← lru;
      };
    };
    
  Enumerate: PUBLIC ENTRY PROC[
    proc: PROC[name: LONG STRING, addr: System.NetworkAddress]] = {
    FOR i: Handle ← root, i.next UNTIL i = NIL DO
      proc[i.name, i.addr];
      ENDLOOP;
    };
  
  LookupNSName: PUBLIC PROC[name: NSName.Name] 
    RETURNS [addr: System.NetworkAddress, hit: BOOLEAN] = {
    qualified: LONG STRING ← [NSName.maxFullNameLength];
    string: NSString.String ← NSString.StringFromMesaString[qualified];
    string ← NSName.AppendNameToString[string, name, TRUE];
    qualified.length ← string.length;
    [addr, hit] ← Lookup[qualified];
    };
    
  Lookup: PUBLIC ENTRY PROC[name: LONG STRING] 
    RETURNS [addr: System.NetworkAddress, hit: BOOLEAN] = {
    accesses ← accesses + 1;
    hit ← FALSE; addr ← System.nullNetworkAddress;
    IF cacheSize = 0 THEN RETURN;
    FOR i: Handle ← root, i.next UNTIL i = NIL DO
      IF (String.Compare[name, i.name, TRUE] = 0) THEN {
        hits ← hits + 1; RETURN[i.addr, TRUE]};
      ENDLOOP;
    };

  GetStatistics: PUBLIC PROC RETURNS[CARDINAL, CARDINAL, CARDINAL] = {
    RETURN[inCache, hits, accesses]};
    
  }.