source: trunk/abcl/src/org/armedbear/lisp/WeakHashTable.java

Last change on this file was 13440, checked in by ehuelsmann, 13 years ago

Rename writeToString() to printObject() since that's what it's being used for.
Additionally, create princToString() for use in error messages, making the

required replacement where appropriate.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 29.3 KB
Line 
1/*
2 * HashTable.java
3 *
4 * Copyright (C) 2002-2007 Peter Graves
5 * Copyright (C) 2010 Erik Huelsmann
6 * Copyright (C) 2011 Mark Evenson
7 * $Id: WeakHashTable.java 13440 2011-08-05 21:25:10Z ehuelsmann $
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 *
24 * As a special exception, the copyright holders of this library give you
25 * permission to link this library with independent modules to produce an
26 * executable, regardless of the license terms of these independent
27 * modules, and to copy and distribute the resulting executable under
28 * terms of your choice, provided that you also meet, for each linked
29 * independent module, the terms and conditions of the license of that
30 * module.  An independent module is a module which is not derived from
31 * or based on this library.  If you modify this library, you may extend
32 * this exception to your version of the library, but you are not
33 * obligated to do so.  If you do not wish to do so, delete this
34 * exception statement from your version.
35 */
36package org.armedbear.lisp;
37
38import static org.armedbear.lisp.Lisp.*;
39
40import java.lang.ref.WeakReference;
41import java.lang.ref.Reference;
42import java.lang.ref.ReferenceQueue;
43import java.util.Collections;
44import java.util.HashMap;
45import java.util.Map;
46import java.util.concurrent.locks.ReentrantLock;
47
48// ??? Replace standard Hashtable when this code is working; maybe not
49// because we have additional places for locking here.
50//
51// We can't simply extend HashTable as the methods returning HashEntry
52// are referring to different types as HashEntry is internal to this
53// class.
54//
55// XXX individuals are invited to figure out how to use Java generics
56// to simplify/beautify things here, but I couldn't get the
57// WeakHashTable type to be parameterized on an enclosed type.
58public class WeakHashTable
59    extends LispObject
60    implements org.armedbear.lisp.protocol.Hashtable
61{
62    protected static final float loadFactor = 0.75f;
63    protected final LispObject rehashSize;
64    protected final LispObject rehashThreshold;
65    /**
66     * The rounded product of the capacity and the load factor. When the number
67     * of elements exceeds the threshold, the implementation calls rehash().
68     */
69    protected int threshold;
70    /** Array containing the actual key-value mappings. */
71    @SuppressWarnings("VolatileArrayField")
72    protected volatile HashEntry[] buckets;
73    /** The actual current number of key-value pairs. */
74    protected volatile int count;
75    final Comparator comparator;
76    final private ReentrantLock lock = new ReentrantLock();
77    HashEntry bucketType;
78    final LispObject weakness;
79
80    private WeakHashTable(Comparator c, int size, LispObject rehashSize, 
81                          LispObject rehashThreshold, LispObject weakness) 
82    {
83        this.rehashSize = rehashSize;
84        this.rehashThreshold = rehashThreshold;
85        bucketType = null;
86        this.weakness = weakness;
87        if (weakness.equals(Keyword.KEY)) {
88            bucketType = this.new HashEntryWeakKey();
89        } else if (weakness.equals(Keyword.VALUE)) {
90            bucketType = this.new HashEntryWeakValue();
91        } else if (weakness.equals(Keyword.KEY_AND_VALUE)) {
92            bucketType = this.new HashEntryWeakKeyAndValue();
93        } else if (weakness.equals(Keyword.KEY_OR_VALUE)) {
94            bucketType = this.new HashEntryWeakKeyOrValue();
95        } else {
96            // We handle this check in the wrapping Lisp code.
97            assert false 
98                : "Bad weakness argument to WeakHashTable type constructor.";
99        }
100        buckets = bucketType.makeArray(size);
101        threshold = (int) (size * loadFactor);
102        comparator = c;
103    }
104
105    protected static int calculateInitialCapacity(int size) {
106        int capacity = 1;
107        while (capacity < size) {
108            capacity <<= 1;
109        }
110        return capacity;
111    }
112
113    // XXX only WEAK references types are implemented for WeakHashTable.
114    // XXX This enum is currently unused in this code
115    enum ReferenceType {
116        NORMAL, 
117        WEAK, 
118        SOFT
119    }
120 
121    // XXX This enum is currently unused in this code
122    enum WeaknessType {
123            /** KEY means that the key of an entry must be live to
124                guarantee that the entry is preserved. */
125        KEY,
126            /** VALUE means that the value of an entry must be live to
127                guarantee that the entry is preserved. */
128        VALUE,
129            /** KEY-AND-VALUE means that both the key and the value
130                must be live to guarantee that the entry is preserved. */
131        KEY_AND_VALUE,
132            /** KEY-OR-VALUE means that either the key or the value
133                must be live to guarantee that the entry is preserved. */
134        KEY_OR_VALUE
135    }
136
137    public static WeakHashTable newEqHashTable(int size, LispObject rehashSize,
138                                               LispObject rehashThreshold,
139                                               LispObject weakness) 
140    {
141        return new WeakHashTable(new Comparator(), size, 
142                                 rehashSize, rehashThreshold, weakness);
143    }
144
145    public static WeakHashTable newEqlHashTable(int size, LispObject rehashSize,
146                                                LispObject rehashThreshold,
147                                                LispObject weakness) 
148    {
149        return new WeakHashTable(new EqlComparator(), size, 
150                                 rehashSize, rehashThreshold, weakness);
151    }
152
153    public static WeakHashTable newEqualHashTable(int size, LispObject rehashSize,
154                                                  LispObject rehashThreshold,
155                                                  LispObject weakness) 
156    {
157        return new WeakHashTable(new EqualComparator(), size, 
158                                 rehashSize, rehashThreshold, weakness);
159    }
160
161    public static WeakHashTable newEqualpHashTable(int size, LispObject rehashSize,
162                                                   LispObject rehashThreshold,
163                                                   LispObject weakness) 
164    {
165        return new WeakHashTable(new EqualpComparator(), size, 
166                                 rehashSize, rehashThreshold, weakness);
167    }
168
169    public final LispObject getRehashSize() {
170        return rehashSize;
171    }
172
173    public final LispObject getRehashThreshold() {
174        return rehashThreshold;
175    }
176
177    /** How many hash buckets exist in the underlying data structure.  */
178    public int getSize() {
179        HashEntry[] b = getTable();
180        return b.length;
181    }
182
183    /** Number of entries stored in the hash buckets. */
184    public int getCount() {
185        getTable(); // To force gc on entries
186        return count;
187    }
188
189    @Override
190    public LispObject typeOf() {
191        return Symbol.HASH_TABLE;
192    }
193
194    @Override
195    public LispObject classOf() {
196        return BuiltInClass.HASH_TABLE;
197    }
198
199    @Override
200    public LispObject typep(LispObject type) {
201        if (type == Symbol.HASH_TABLE) {
202            return T;
203        }
204        if (type == BuiltInClass.HASH_TABLE) {
205            return T;
206        }
207        return super.typep(type);
208    }
209
210    // XXX Not thread-safe as hash entries can be GCd "out from under"
211    // the invoking thread.  But the HashTable implementation
212    // seemingly suffers from the same problem if entries are
213    // removed/added while this method executes.
214    @Override 
215    public boolean equalp(LispObject obj) { 
216        if (this == obj) {
217            return true;
218        }
219        if (obj instanceof WeakHashTable) {
220            WeakHashTable ht = (WeakHashTable) obj;
221            if (count != ht.count) {
222                return false;
223            }
224            if (getTest() != ht.getTest()) {
225                return false;
226            }
227            LispObject entries = ENTRIES();
228            while (entries != NIL) {
229                LispObject entry = entries.car();
230                LispObject key = entry.car();
231                LispObject value = entry.cdr();
232                if (!value.equalp(ht.get(key))) {
233                    return false;
234                }
235                entries = entries.cdr();
236            }
237            return true;
238        }
239        return false;
240    }
241
242    @Override
243    public LispObject getParts() {
244        HashEntry[] b = getTable();;
245        LispObject parts = NIL;
246        for (int i = 0; i < b.length; i++) {
247            HashEntry e = b[i];
248            while (e != null) {
249                LispObject key = e.getKey();
250                LispObject value = e.getValue();
251                if (key != null && value != null) {
252                    parts = parts.push(new Cons("KEY [bucket " + i + "]", key));
253                    parts = parts.push(new Cons("VALUE", value));
254                } else {
255                    assert false
256                        : "Dangling hash entries encountered.";
257                }
258                e = e.getNext();
259            }
260        }
261        return parts.nreverse();
262    }
263
264    public void clear() {
265        lock.lock();
266        try {
267            buckets = bucketType.makeArray(buckets.length);
268            count = 0;
269            while (queue.poll() != null)
270                ;
271        } finally {
272            lock.unlock();
273        }
274    }
275
276    // gethash key hash-table &optional default => value, present-p
277    public LispObject gethash(LispObject key) {
278        LispObject value = get(key);
279        final LispObject presentp;
280        if (value == null) {
281            value = presentp = NIL;
282        } else {
283            presentp = T;
284        }
285        return LispThread.currentThread().setValues(value, presentp);
286    }
287
288    // gethash key hash-table &optional default => value, present-p
289    public LispObject gethash(LispObject key, LispObject defaultValue) {
290        LispObject value = get(key);
291        final LispObject presentp;
292        if (value == null) {
293            value = defaultValue;
294            presentp = NIL;
295        } else {
296            presentp = T;
297        }
298        return LispThread.currentThread().setValues(value, presentp);
299    }
300
301    public LispObject gethash1(LispObject key) {
302        final LispObject value = get(key);
303        return value != null ? value : NIL;
304    }
305
306    public LispObject puthash(LispObject key, LispObject newValue) {
307        put(key, newValue);
308        return newValue;
309    }
310
311    // remhash key hash-table => generalized-boolean
312    public LispObject remhash(LispObject key) {
313        // A value in a Lisp hash table can never be null, so...
314        return remove(key) != null ? T : NIL;
315    }
316
317    @Override
318    public String printObject() {
319        if (Symbol.PRINT_READABLY.symbolValue(LispThread.currentThread()) != NIL) {
320            error(new PrintNotReadable(list(Keyword.OBJECT, this)));
321            return null; // Not reached.
322        }
323        StringBuilder sb = new StringBuilder(getTest().princToString());
324        sb.append(' ');
325        sb.append(Symbol.HASH_TABLE.princToString());
326        sb.append(' ');
327        if (bucketType instanceof HashEntryWeakKey) {
328            sb.append("WEAKNESS :KEY");
329        } else if (bucketType instanceof HashEntryWeakValue) {
330            sb.append("WEAKNESS :VALUE");
331        } else if (bucketType instanceof HashEntryWeakKeyAndValue) {
332            sb.append("WEAKNESS :KEY-AND-VALUE");
333        } else if (bucketType instanceof HashEntryWeakKeyOrValue) {
334            sb.append("WEAKNESS :KEY-OR-VALUE");
335        }
336        sb.append(' ');
337        sb.append(count);
338        if (count == 1) {
339            sb.append(" entry");
340        } else {
341            sb.append(" entries");
342        }
343        sb.append(", ");
344        sb.append(buckets.length);
345        sb.append(" buckets");
346        return unreadableString(sb.toString());
347    }
348
349    public Symbol getTest() {
350        return comparator.getTest();
351    }
352   
353    public LispObject getWeakness() {
354        return weakness;
355    }
356
357    HashEntry[] getTable() {
358        lock.lock();
359        try {
360            bucketType.expungeQueue();
361            return buckets;
362        } finally {
363            lock.unlock();
364        }
365    }
366
367    protected HashEntry getEntry(LispObject key) {
368        HashEntry[] b = getTable();
369        int hash = comparator.hash(key);
370        HashEntry e = b[hash & (b.length - 1)];
371        while (e != null) {
372            if (hash == e.getHash() 
373                && (key == e.getKey() 
374                    || comparator.keysEqual(key, e.getKey()))) {
375                return e;
376            }
377            e = e.getNext();
378        }
379        return null;
380    }
381
382    public LispObject get(LispObject key) {
383        HashEntry e = getEntry(key);
384        LispObject v = (e == null) ? null : e.getValue();
385       
386        if (e == null || v != null) {
387            return v;
388        }
389        return e.getValue();
390    }
391
392    public void put(LispObject key, LispObject value) {
393        HashEntry e = getEntry(key);
394        if (e != null) {
395            e.setValue(value);
396        } else {
397            // Not found. We need to add a new entry.
398            if (++count > threshold) {
399                rehash();
400            }
401            int hash = comparator.hash(key);
402            int index = hash & (buckets.length - 1);
403            buckets[index] = bucketType.makeInstance(key, hash, 
404                                                     value, buckets[index],
405                                                     index);
406        }
407    }
408
409    public LispObject remove(LispObject key) {
410        lock.lock();
411        try {
412            bucketType.expungeQueue();
413            int index = comparator.hash(key) & (buckets.length - 1);
414
415            HashEntry e = buckets[index];
416            HashEntry last = null;
417            while (e != null) {
418                LispObject entryKey = e.getKey();
419                if (entryKey == null) {
420                    e.clear();
421                    if (last == null) {
422                        buckets[index] = e.getNext();
423                    } else {
424                        last.setNext(e.getNext());
425                    }
426                    --count;
427                } else if (comparator.keysEqual(key, entryKey)) {
428                    e.clear();
429                    if (last == null) {
430                        buckets[index] = e.getNext();
431                    } else {
432                        last.setNext(e.getNext());
433                    }
434                    --count;
435                    return e.getValue();
436                }
437                last = e;
438                e = e.getNext();
439            }
440            return null;
441        } finally {
442            lock.unlock();
443        }
444    }
445
446   
447    /**
448     * Internal removal of the HashEntry associated with the
449     * Reference used for a hashtables with soft/weak references.
450     */
451    private void remove(Reference ref) {
452        assert lock.isHeldByCurrentThread();
453        HashEntry entry = entryLookup.get(ref);
454        // assert entry != null
455        //     : "Failed to find hash entry for reference.";
456        if (entry == null) {
457            return; // XXX how does this happen?
458        }
459        int index = entry.getSlot();
460        HashEntry e = this.buckets[index];
461        HashEntry last = null;
462        while (e != null) {
463            if (e.equals(entry)) {
464                if (last == null) {
465                    this.buckets[index] = e.getNext();
466                } else {
467                    last.setNext(e.getNext());
468                }
469                --count;
470                break;
471            }
472            last = e;
473            e = e.getNext();
474        }
475    }
476
477    protected void rehash() {
478        lock.lock();
479        try {
480            final int newCapacity = buckets.length * 2;
481            threshold = (int) (newCapacity * loadFactor);
482            int mask = newCapacity - 1;
483            HashEntry[] newBuckets = bucketType.makeArray(newCapacity);
484
485            for (int i = buckets.length; i-- > 0;) {
486                HashEntry e = buckets[i];
487                while (e != null) {
488                    LispObject key = e.getKey();
489                    LispObject value = e.getValue();
490                    if (key == null || value == null) {
491                        e.clear();
492                        e = e.getNext();
493                        continue;
494                    }
495                    final int index = comparator.hash(key) & mask;
496                    e.clear();
497                    newBuckets[index] 
498                        = bucketType.makeInstance(key, 
499                                                  e.getHash(), 
500                                                  value,
501                                                  newBuckets[index],
502                                                  index);
503                    e = e.getNext();
504                }
505            }
506            buckets = newBuckets;
507        } finally {
508            lock.unlock();
509        }
510    }
511
512    @Deprecated
513    public LispObject ENTRIES() {
514        return getEntries();
515    }
516
517    /** @returns A list of (key . value) pairs. */
518    public LispObject getEntries() {
519        HashEntry[] b = getTable();
520        LispObject list = NIL;
521        for (int i = b.length; i-- > 0;) {
522            HashEntry e = b[i];
523            while (e != null) {
524                LispObject key = e.getKey();
525                LispObject value = e.getValue();
526                if (key != null && value != null) {
527                    list = new Cons(new Cons(key, value), list);
528                } else {
529                    assert false 
530                        : "ENTRIES encounted dangling entries.";
531                }
532                e = e.getNext();
533            }
534        }
535        return list;
536    }
537
538    public LispObject MAPHASH(LispObject function) {
539        HashEntry[] b = getTable();
540        for (int i = b.length; i-- > 0;) {
541            HashEntry e = b[i];
542            while (e != null) {
543                LispObject key = e.getKey();
544                LispObject value = e.getValue();
545                if (key != null && value != null) {
546                    function.execute(key, value);
547                } else {
548                    assert false 
549                        : "MAPHASH encountered dangling entries.";
550                }
551                e = e.getNext();
552            }
553        }
554        return NIL;
555    }
556
557    protected static class Comparator {
558        Symbol getTest() {
559            return Symbol.EQ;
560        }
561
562        boolean keysEqual(LispObject key1, LispObject key2) {
563            return key1 == key2;
564        }
565
566        int hash(LispObject key) {
567            return key.sxhash();
568        }
569    }
570
571    protected static class EqlComparator extends Comparator {
572        @Override
573        Symbol getTest() {
574            return Symbol.EQL;
575        }
576
577        @Override
578        boolean keysEqual(LispObject key1, LispObject key2) {
579            return key1.eql(key2);
580        }
581    }
582
583    protected static class EqualComparator extends Comparator {
584        @Override
585        Symbol getTest() {
586            return Symbol.EQUAL;
587        }
588
589        @Override
590        boolean keysEqual(LispObject key1, LispObject key2) {
591            return key1.equal(key2);
592        }
593    }
594
595    protected static class EqualpComparator extends Comparator {
596        @Override
597        Symbol getTest() {
598            return Symbol.EQUALP;
599        }
600
601        @Override
602        boolean keysEqual(LispObject key1, LispObject key2) {
603            return key1.equalp(key2);
604        }
605
606        @Override
607        int hash(LispObject key) {
608            return key.psxhash();
609        }
610    }
611
612    abstract class HashEntry
613    {
614        LispObject key;
615        int hash;
616        volatile LispObject value;
617        HashEntry next;
618        int slot;
619
620        public HashEntry() {};
621
622        public HashEntry(LispObject key, int hash, LispObject value, 
623                         HashEntry next, int slot)
624        {
625            this.key = key;
626            this.hash = hash;
627            this.value = value;
628            this.next = next;
629            this.slot = slot;
630        }
631
632        public LispObject getKey() {
633            return key;
634        }
635
636        public void setKey(LispObject key) {
637            this.key = key;
638        }
639
640        public int getHash() {
641            return hash;
642        }
643
644        public void setHash(int hash) {
645            this.hash = hash;
646        }
647
648        public LispObject getValue() {
649            return value;
650        }
651
652        public void setValue(LispObject value) {
653            this.value = value;
654        }
655
656        public HashEntry getNext() {
657            return next;
658        }
659
660        public void setNext(HashEntry next) {
661            this.next = next;
662        }
663
664        public int getSlot() {
665            return slot;
666        }
667       
668        public void setSlot(int slot) {
669            this.slot = slot;
670        }
671
672        abstract HashEntry[] makeArray(int length);
673
674        abstract HashEntry makeInstance(LispObject key, int hash, 
675                                        LispObject value, 
676                                        HashEntry next, int slot);
677        abstract void expungeQueue();
678        abstract void clear();
679    }
680
681    ReferenceQueue<LispObject> queue
682        = new ReferenceQueue<LispObject>();
683
684    Map<Reference, HashEntry> entryLookup
685        = Collections.synchronizedMap(new HashMap<Reference, HashEntry>());
686
687    class HashEntryWeakKey 
688        extends HashEntry
689    {
690        private WeakReference<LispObject> key;
691       
692        public HashEntryWeakKey() {};
693
694        public HashEntryWeakKey(LispObject key, int hash, LispObject value, 
695                                HashEntry next, int slot)
696        {
697            this.hash = hash;
698            this.value = value;
699            this.next = next;
700            this.slot = slot;
701
702            this.key = new WeakReference<LispObject>(key, queue);
703            entryLookup.put(this.key, this);
704        }
705
706        public LispObject getKey() {
707            return key.get();
708        }
709
710        public void setKey(LispObject key) {
711            java.lang.ref.WeakReference<LispObject> old = this.key;
712            old.clear();
713            this.key = new WeakReference<LispObject>(key, queue);
714            entryLookup.put(this.key, this);
715        }
716
717        HashEntryWeakKey[] makeArray(int length) {
718            return new HashEntryWeakKey[length];
719        }
720
721        HashEntry makeInstance(LispObject key, int hash, LispObject value, 
722                               HashEntry next, int slot) 
723        {
724            return new HashEntryWeakKey(key, hash, value, next, slot);
725        } 
726
727        void expungeQueue() {
728            Reference ref = queue.poll();
729            while (ref != null) {
730                WeakHashTable.this.remove(ref);
731                entryLookup.remove(ref);
732                ref = queue.poll();
733            }
734        }
735
736        /** Remove referenced objects from GC queue structures. */
737        void clear() {
738            key.clear();
739            assert entryLookup.containsKey(key) 
740                : "Key was not in lookup table";
741            entryLookup.remove(key);
742        }
743    }
744
745    class HashEntryWeakValue
746        extends HashEntry
747    {
748        private WeakReference<LispObject> value;
749       
750        public HashEntryWeakValue() {};
751
752        public HashEntryWeakValue(LispObject key, int hash, LispObject value, 
753                                  HashEntry next, int slot)
754        {
755            this.hash = hash;
756            this.key = key;
757            this.next = next;
758            this.slot = slot;
759
760            this.value = new WeakReference<LispObject>(value, queue);
761            entryLookup.put(this.value, this);
762        }
763
764        public LispObject getValue() {
765            return value.get();
766        }
767
768        public void setValue(LispObject value) {
769            java.lang.ref.WeakReference<LispObject> old = this.value;
770            old.clear();
771            this.value = new WeakReference<LispObject>(value, queue);
772            entryLookup.put(this.value, this);
773        }
774
775        HashEntryWeakValue[] makeArray(int length) {
776            return new HashEntryWeakValue[length];
777        }
778
779        HashEntryWeakValue makeInstance(LispObject key, int hash, LispObject value, 
780                               HashEntry next, int slot) 
781        {
782            return new HashEntryWeakValue(key, hash, value, next, slot);
783        } 
784
785        void expungeQueue() {
786            Reference ref = queue.poll();
787            while (ref != null) {
788                WeakHashTable.this.remove(ref);
789                entryLookup.remove(ref);
790                ref = queue.poll();
791            }
792        }
793
794        /** Remove referenced objects from GC queue structures. */
795        void clear() {
796            value.clear();
797            assert entryLookup.containsKey(value) 
798                : "Value was not in lookup table.";
799            entryLookup.remove(value);
800        }
801    }
802
803    class HashEntryWeakKeyAndValue
804        extends HashEntry
805    {
806        private WeakReference<LispObject> key;
807        private WeakReference<LispObject> value;
808       
809        public HashEntryWeakKeyAndValue() {};
810
811        public HashEntryWeakKeyAndValue(LispObject key, int hash, 
812                                        LispObject value, 
813                                        HashEntry next, int slot)
814        {
815            this.hash = hash;
816            this.next = next;
817            this.slot = slot;
818           
819            this.key = new WeakReference<LispObject>(key, queue);
820            entryLookup.put(this.key, this);
821
822            this.value = new WeakReference<LispObject>(value, queue);
823            entryLookup.put(this.value, this);
824           
825        }
826
827        public LispObject getKey() {
828            return key.get();
829        }
830
831        public void setKey(LispObject key) {
832            java.lang.ref.WeakReference<LispObject> old = this.key;
833            entryLookup.remove(old);
834            old.clear();
835            this.key = new WeakReference<LispObject>(key, queue);
836            entryLookup.put(this.key, this);
837        }
838
839        public LispObject getValue() {
840            return value.get();
841        }
842
843        public void setValue(LispObject value) {
844            java.lang.ref.WeakReference<LispObject> old = this.value;
845            entryLookup.remove(old);
846            old.clear();
847            this.value = new WeakReference<LispObject>(value, queue);
848            entryLookup.put(this.value, this);
849        }
850
851        HashEntryWeakKeyAndValue[] makeArray(int length) {
852            return new HashEntryWeakKeyAndValue[length];
853        }
854
855        HashEntryWeakKeyAndValue makeInstance(LispObject key, int hash, 
856                                              LispObject value, 
857                                              HashEntry next, int slot) 
858        {
859            return new HashEntryWeakKeyAndValue(key, hash, value, next, slot);
860        } 
861
862        void expungeQueue() {
863            Reference ref = queue.poll();
864            while (ref != null) {
865                HashEntry entry = entryLookup.get(ref);
866                if (entry == null) {
867                    ref = queue.poll();
868                    continue;
869                }
870                if (entry.getKey() == null
871                    && entry.getValue() == null) {
872                    WeakHashTable.this.remove(ref);
873                    entryLookup.remove(ref);
874                } else {
875                    entryLookup.remove(ref);
876                }
877                ref = queue.poll();
878            }
879        }
880
881        /** Remove referenced objects from GC queue structures. */
882        void clear() {
883            key.clear();
884            value.clear();
885            entryLookup.remove(key);
886            entryLookup.remove(value);
887        }
888    }
889
890    class HashEntryWeakKeyOrValue
891        extends HashEntryWeakKeyAndValue
892    {
893        public HashEntryWeakKeyOrValue() {};
894
895        public HashEntryWeakKeyOrValue(LispObject key, int hash, 
896                                       LispObject value, 
897                                       HashEntry next, int slot)
898        {
899            super(key, hash, value, next, slot);
900        }
901        HashEntryWeakKeyOrValue[] makeArray(int length) {
902            return new HashEntryWeakKeyOrValue[length];
903        }
904
905        HashEntryWeakKeyOrValue makeInstance(LispObject key, int hash, 
906                                             LispObject value, 
907                                             HashEntry next, int slot) 
908        {
909            return new HashEntryWeakKeyOrValue(key, hash, value, next, slot);
910        } 
911
912        void expungeQueue() {
913            Reference ref = queue.poll();
914            while (ref != null) {
915                HashEntry entry = entryLookup.get(ref);
916                if (entry == null) {
917                    ref = queue.poll();
918                    continue;
919                }
920                WeakHashTable.this.remove(ref);
921                entryLookup.remove(entry.key);
922                entryLookup.remove(entry.value);
923                ref = queue.poll();
924            }
925        }
926    }
927
928    // For EQUALP hash tables.
929    @Override
930    public int psxhash() {
931        long result = 2062775257; // Chosen at random.
932        result = mix(result, count);
933        result = mix(result, getTest().sxhash());
934        return (int) (result & 0x7fffffff);
935    }
936}
Note: See TracBrowser for help on using the repository browser.