--Transport Mechanism Filestore - Bit maps --

-- Randy Gobbel		19-May-81 13:16:21 --
-- A.D.Birrell		December 13, 1978  5:09 PM --

DIRECTORY
   BitMapDefs	USING[ MapLevel, Map, MapIndex ],
   Storage	USING[ Node ],
   Inline	USING[ BITSHIFT, BITAND, BITOR, BITNOT ];

BitMap: PROGRAM
   IMPORTS Inline, Storage
   EXPORTS BitMapDefs =
BEGIN

OPEN BitMapDefs, Inline;

Allocate: PROCEDURE[CARDINAL]RETURNS[POINTER] = Storage.Node;

BitPos:   TYPE = [0..16); -- name of a bit in a word --
unset:    WORD = 0B; -- all bits 0 --
allSet:   WORD = 177777B; -- all bits 1 --

MapFull: ERROR = CODE;

FindFree: PUBLIC PROCEDURE[ m: Map ] RETURNS[ MapIndex ] =
   BEGIN
   wPos: MapIndex = IF m.level=0 THEN 0 ELSE FindFree[m.next];
   word: WORD = m.data[wPos];
   -- now find the first 0 bit in 'word' --
   -- more efficient not to parameterise by 'width' --
   bPos: BitPos ← 0;
   half: WORD ← BITAND[word,377B];
   IF half=377B THEN BEGIN half ← BITSHIFT[word,-8]; bPos ← bPos+8 END;
   BEGIN
      quart: WORD ← BITAND[half,17B];
      IF quart=17B THEN BEGIN quart ← BITSHIFT[half,-4]; bPos ← bPos+4 END;
      BEGIN
         eight: WORD ← BITAND[quart,3B];
         IF eight=3B THEN BEGIN eight ← BITSHIFT[quart,-2]; bPos ← bPos+2 END;
         IF eight=1B
         THEN bPos ← bPos+1
         ELSE IF eight=3B THEN ERROR MapFull[];
      END;
   END;
   RETURN[ BITOR[ BITSHIFT[wPos,4], bPos ] ]
   END;

Set: PUBLIC PROCEDURE[ m: Map, pos: MapIndex ] =
   BEGIN
   wPos: MapIndex = BITSHIFT[pos,-4];
   word: POINTER TO WORD = @(m.data[wPos]);
   word↑ ← BITOR[ word↑, BITSHIFT[ 1B, BITAND[pos,17B] ] ];
   IF word↑ = allSet AND m.level # 0 THEN Set[m.next, wPos];
   END;

Test: PUBLIC PROCEDURE[ m: Map, pos: MapIndex ] RETURNS[ BOOLEAN ] =
   BEGIN
   wPos: MapIndex = BITSHIFT[pos,-4];
   RETURN[ IF wPos >= LENGTH[m.data]
           THEN FALSE
           ELSE BITAND[ m.data[wPos], BITSHIFT[1B,BITAND[pos,17B]] ] # 0
             ]
   END;

Clear: PUBLIC PROCEDURE[ m: Map, pos: MapIndex ] =
   BEGIN
   wPos: MapIndex = BITSHIFT[pos,-4];
   word: POINTER TO WORD = @(m.data[wPos]);
   bPos: BitPos = BITAND[pos,17B];
   IF word↑ = allSet AND m.level # 0 THEN Clear[m.next, wPos];
   word↑ ← BITAND[ word↑, BITNOT[BITSHIFT[1B,bPos]] ];
   END;

Create: PUBLIC PROCEDURE[ size: CARDINAL ] RETURNS[ m: Map ] =
   BEGIN
   -- beware of overflow if size = LAST[CARDINAL] --
   words: CARDINAL = size/16 + (IF size MOD 16 = 0 THEN 0 ELSE 1);
   m ← Allocate[SIZE[MapLevel]];
   m.data ← DESCRIPTOR[ Allocate[words], words ];
   BEGIN
      index: CARDINAL;
      FOR index IN [0..words) DO m.data[index] ← unset ENDLOOP;
   END;
   IF words > 1
   THEN BEGIN
        m.next ← Create[words];
        m.level ← m.next.level + 1;
        END
   ELSE BEGIN
        m.next ← NIL --only a safety precaution--;
        m.level ← 0;
        END;
   END;

END.