home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
sa104os2.zip
/
SATHR104.ZIP
/
SATHER
/
LIBRARY
/
FMAP.SA
< prev
next >
Wrap
Text File
|
1995-02-05
|
10KB
|
261 lines
-- Copyright (C) International Computer Science Institute, 1994. COPYRIGHT --
-- NOTICE: This code is provided "AS IS" WITHOUT ANY WARRANTY and is subject --
-- to the terms of the SATHER LIBRARY GENERAL PUBLIC LICENSE contained in --
-- the file "Doc/License" of the Sather distribution. The license is also --
-- available from ICSI, 1947 Center St., Suite 600, Berkeley CA 94704, USA. --
--------> Please email comments to "sather-bugs@icsi.berkeley.edu". <----------
-- fmap.sa: Hash table based maps from objects to objects.
-------------------------------------------------------------------
class FMAP{K,T} is
-- Hash array based maps from key objects of type K to target
-- objects of type T requiring writebacks.
--
-- In this form void may not be a key, `key_nil' may be redefined.
--
-- If K is a subtype of $IS_EQ{K}, then `is_eq' will be used for
-- key equality test (eg. string equality for STR), otherwise
-- object equality is used. Notice that in an FMAP{$OB}, unless
-- $IS_EQ{$OB} has been defined (_not_ $IS_EQ{K}), the equality
-- routine will not be used.
--
-- If K is a subtype of $HASH, then `hash' will be used for the hash
-- value, otherwise the element `id' will be used.
--
-- May be inherited with `key_eq', `key_nil', and `key_hash' redefined
-- to get a different behavior.
--
-- The tables grow by amortized doubling and so require writeback
-- when inserting and deleting elements. We
-- keep the load factor at .5 to cut down on collision snowballing.
-- The simple collision resolution allows us to support deletions,
-- but makes the behavior quadratic with poor hash functions.
-- Puts a sentinel at the end of the table to avoid one check while
-- searching.
private include AREF{TUP{K,T}};
private attr hsize:INT; -- Number of stored entries.
invariant:BOOL is
-- Class invariant.
return void(self) or hsize.is_bet(0,asize) end;
key_eq(k1,k2:K):BOOL is
-- True if `k1' is equal to `k2' for the semantics of this set.
-- If K is a subtype of $IS_EQ{K}, use K::is_eq (eg. string
-- equality for STR), otherwise use object equality.
-- Often redefined in descendants.
typecase k1
when $IS_EQ{K} then return k1.is_eq(k2)
-- else return SYS::ob_eq(k1,k2) end end; -- NLP
else; end; return SYS::ob_eq(k1,k2); end; -- NLP
key_nil:K is
-- The key value used to represent an empty hash entry.
-- If T descends from $NIL{T}, use T::nil, otherwise use void.
-- Often redefined in descendants.
k:K;
typecase k
when $NIL{K} then return k.nil
-- else return void end end; -- NLP
else; end; return void; end; -- NLP
key_hash(k:K):INT is
-- The hash index into self for `k'. Must have the property that
-- two elements which are `key_eq' have the same hash value.
-- If K is a subtype of $HASH use K::hash, otherwise use `id'.
-- Often redefined in descendants.
typecase k
when $HASH then return k.hash
-- else return SYS::id(k) end end; -- NLP
else; end; return SYS::id(k); end; -- NLP
size:INT is
-- Number of entries in the table. Self may be void.
-- if void(self) then return 0 else return hsize end end; -- NLP
if void(self) then return 0; end; return hsize; end; -- NLP
create:SAME is return void end;
create(n:INT):SAME
-- Make a table capable of dealing with `n' elements without
-- expansion. You can simply insert into a void table to create
-- one as well. Self may be void (and usually is).
pre n>=1 is
return allocate(1.lshift((3*n/2).highest_bit+1)+1) end;
private allocate(n:INT):SAME is
-- Allocate `n' locations (must be power of 2 plus 1) and
-- initialize to `(elt_nil,void)'.
r::=new(n);
if ~void(key_nil) then loop r.aset!(#(key_nil,void)) end end;
return r end;
pairs!:TUP{K,T} is
-- Yield the input/output pairs of self in an arbitrary order.
-- Do not insert or delete from self while calling this.
-- Self may be void.
if ~void(self) then
loop r::=aelt!;
if ~key_eq(r.t1,key_nil) then yield r end end end end;
keys!:K is
-- Yield the keys in self in an arbitrary order. Do not insert
-- or delete from self while calling this.
-- Self may be void.
if ~void(self) then
loop r::=aelt!.t1;
if ~key_eq(r,key_nil) then yield r end end end end;
targets!:T is
-- Yield the target objects contained in self in an arbitrary
-- order. Do not insert or delete from self while calling this.
-- Self may be void.
if ~void(self) then
loop e::=aelt!;
if ~key_eq(e.t1,key_nil) then yield e.t2 end end end end;
test(k:K):BOOL is
-- True if the key `k' is mapped by self.
-- Self may be void.
if void(self) then return false end;
h::=key_hash(k).band(asize-2);
loop tk::=[h].t1;
if key_eq(tk,k) then return true
elsif key_eq(tk,key_nil) then break! end;
h:=h+1 end;
if h=asize-1 then h:=0; -- hit sentinel
loop tk::=[h].t1;
if key_eq(tk,k) then return true
elsif key_eq(tk,key_nil) then break! end;
h:=h+1 end;
assert h/=asize-1 end;
return false end;
get(k:K):T is
-- If `k' is a key, return the corresponding target, otherwise
-- return void. Self may be void.
if void(self) then return void end;
h::=key_hash(k).band(asize-2);
loop tk::=[h].t1;
if key_eq(tk,k) then return [h].t2
elsif key_eq(tk,key_nil) then break! end;
h:=h+1 end;
if h=asize-1 then h:=0; -- hit sentinel
loop tk::=[h].t1;
if key_eq(tk,k) then return [h].t2
elsif key_eq(tk,key_nil) then break! end;
h:=h+1 end;
assert h/=asize-1 end; -- table mustn't be filled
return void end;
get_pair(k:K):TUP{K,T} is
-- If `k' is a key, return the corresponding key/target pair.
-- Otherwise return #(key_nil,void). Useful when different
-- objects are treated as equal by `key_eq'.
-- Self may be void.
if void(self) then return #(key_nil,void) end;
h::=key_hash(k).band(asize-2);
loop tk::=[h].t1;
if key_eq(tk,k) then return [h]
elsif key_eq(tk,key_nil) then break! end;
h:=h+1 end;
if h=asize-1 then h:=0; -- hit sentinel
loop tk::=[h].t1;
if key_eq(tk,k) then return [h]
elsif key_eq(tk,key_nil) then break! end;
h:=h+1 end;
assert h/=asize-1 end; -- table mustn't be filled
return #(key_nil,void) end;
private double_size:SAME
-- A new table of twice the size of self with self's entries
-- copied over.
pre ~void(self) is
ns::=(asize-1)*2+1; r::=allocate(ns);
loop r:=r.insert_pair(pairs!) end;
return r end;
insert(k:K,t:T):SAME is
-- A possibly new table which includes the key/target pair `k',
-- `t'. If `k' is already present, replaces the current key and
-- target with `k,t'. Usage: `tbl:=tbl.insert(k,t)'. Creates a
-- new table if void(self).
r::=self;
if void(r) then r:=allocate(5)
elsif (hsize+1)*2>asize then r:=double_size end;
h::=r.key_hash(k).band(r.asize-2);
asm::=r.asize-1;
loop tk::=r[h].t1;
if key_eq(tk,key_nil) then break! end;
if key_eq(tk,k) then r[h]:=#(k,t); return r end;
h:=h+1 end;
if h=asm then h:=0; -- hit sentinel
loop tk::=r[h].t1;
if key_eq(tk,key_nil) then break! end;
if key_eq(tk,k) then r[h]:=#(k,t); return r end;
h:=h+1 end;
assert h/=asm end; -- table mustn't be filled
r[h]:=#(k,t); r.hsize:=r.hsize+1; return r end;
insert_pair(p:TUP{K,T}):SAME is
-- Insert the key/target pair held by the tuple arg.
-- If the key is already present, replaces it with the new
-- key and target. `tbl:=tbl.insert(p)'. Creates a new table
-- if void(self).
return insert(p.t1,p.t2) end;
private halve_size:SAME
-- A new table of half the size of self with self's entries
-- copied over.
pre ~void(self) and hsize<(asize-1)/4 is
ns::=(asize-1)/2+1; r::=allocate(ns);
loop r:=r.insert_pair(pairs!) end;
return r end;
delete(k:K):SAME is
-- A possibly new table which deletes the element with key
-- `k' if it is contained in self. Usage: `tbl:=tbl.delete(k)'.
-- Self may be void.
if void(self) then return void end;
h::=key_hash(k).band(asize-2);
loop tk::=[h].t1;
if key_eq(tk,key_nil) then return self
elsif key_eq(tk,k) then break! end;
if h=asize-2 then h:=0 else h:=h+1 end end;
[h]:=#(key_nil,void); -- h is the index of arg
hsize:=hsize-1; i::=h;
-- Now check the block after h for collisions.
loop
if i=asize-2 then i:=0 else i:=i+1 end;
tk::=[i].t1;
if key_eq(tk,key_nil) then break! end;
hsh::=key_hash(tk).band(asize-2);
if hsh<=i then -- block doesn't wrap around
if h<i and h>=hsh then -- hole in way
[h]:=[i]; h:=i; [i]:=#(key_nil,void) end;
else -- block wraps
if h>=hsh or h<i then -- hole in way
[h]:=[i]; h:=i; [i]:=#(key_nil,void) end end end;
if asize>=33 and hsize<(asize-1)/4 then return halve_size
-- else return self end end; -- NLP
end; return self; end; -- NLP
clear:SAME is
-- Clear out self, return the space if it has 17 or less entries
-- otherwise return void. Self may be void.
if void(self) then return void end;
if asize<=17 then r::=self; r.hsize:=0;
loop aset!(#(key_nil,void)) end; return self
-- else return void end end; -- NLP
end; return void; end; -- NLP
is_empty:BOOL is
-- True if the set is empty. Self may be void.
return void(self) or hsize=0 end;
end; -- class FMAP{K,T}
-------------------------------------------------------------------