001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util;
018
019import java.util.concurrent.locks.Lock;
020import java.util.concurrent.locks.ReentrantLock;
021import java.util.function.Predicate;
022
023import org.apache.camel.util.function.TriConsumer;
024
025@Deprecated(since = "4.1.0")
026@SuppressWarnings("unchecked")
027public class DoubleMap<K1, K2, V> {
028
029    private static final double MAX_LOAD_FACTOR = 1.2;
030    private static final int MAX_TABLE_SIZE = 32768;
031    private static final int C1 = 0xcc9e2d51;
032    private static final int C2 = 0x1b873593;
033
034    static class Entry {
035        Object k1;
036        Object k2;
037        Object v;
038        Entry next;
039    }
040
041    private final Lock lock = new ReentrantLock();
042    private Entry[] table;
043    private int mask;
044
045    public DoubleMap(int size) {
046        table = new Entry[closedTableSize(size)];
047        mask = table.length - 1;
048    }
049
050    public V get(K1 k1, K2 k2) {
051        Entry[] table = this.table;
052        int mask = this.mask;
053        int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask;
054        for (Entry entry = table[index]; entry != null; entry = entry.next) {
055            if (k1 == entry.k1 && k2 == entry.k2) {
056                return (V) entry.v;
057            }
058        }
059        return null;
060    }
061
062    public void forEach(TriConsumer<K1, K2, V> consumer) {
063        Entry[] table = this.table;
064        for (Entry entry : table) {
065            while (entry != null) {
066                consumer.accept((K1) entry.k1, (K2) entry.k2, (V) entry.v);
067                entry = entry.next;
068            }
069        }
070    }
071
072    public boolean containsKey(K1 k1, K2 k2) {
073        Entry[] table = this.table;
074        int mask = this.mask;
075        int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask;
076        for (Entry entry = table[index]; entry != null; entry = entry.next) {
077            if (k1 == entry.k1 && k2 == entry.k2) {
078                return true;
079            }
080        }
081        return false;
082    }
083
084    public void put(K1 k1, K2 k2, V v) {
085        lock.lock();
086        try {
087            Entry[] table = this.table;
088            int size = size() + 1;
089            int realSize = closedTableSize(size);
090            if (realSize <= table.length) {
091                realSize = table.length;
092                int index = smear(k1.hashCode() * 31 + k2.hashCode()) & (realSize - 1);
093                for (Entry oldEntry = table[index]; oldEntry != null; oldEntry = oldEntry.next) {
094                    if (oldEntry.k1 == k1 && oldEntry.k2 == k2) {
095                        oldEntry.v = v;
096                        return;
097                    }
098                }
099                Entry entry = new Entry();
100                entry.k1 = k1;
101                entry.k2 = k2;
102                entry.v = v;
103                entry.next = table[index];
104                table[index] = entry;
105            } else {
106                Entry[] newT = new Entry[realSize];
107                int index = smear(k1.hashCode() * 31 + k2.hashCode()) & (realSize - 1);
108                Entry entry = new Entry();
109                newT[index] = entry;
110                entry.k1 = k1;
111                entry.k2 = k2;
112                entry.v = v;
113                for (Entry oldEntry : table) {
114                    while (oldEntry != null) {
115                        if (k1 != oldEntry.k1 || k2 != oldEntry.k2) {
116                            index = smear(oldEntry.k1.hashCode() * 31 + oldEntry.k2.hashCode()) & (realSize - 1);
117                            Entry newEntry = new Entry();
118                            newEntry.k1 = oldEntry.k1;
119                            newEntry.k2 = oldEntry.k2;
120                            newEntry.v = oldEntry.v;
121                            newEntry.next = newT[index];
122                            newT[index] = newEntry;
123                        }
124                        oldEntry = oldEntry.next;
125                    }
126                }
127                this.table = newT;
128                this.mask = realSize - 1;
129            }
130        } finally {
131            lock.unlock();
132        }
133    }
134
135    public boolean remove(K1 k1, K2 k2) {
136        lock.lock();
137        try {
138            Entry[] table = this.table;
139            int mask = this.mask;
140            int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask;
141            Entry prevEntry = null;
142            for (Entry oldEntry = table[index]; oldEntry != null; prevEntry = oldEntry, oldEntry = oldEntry.next) {
143                if (oldEntry.k1 == k1 && oldEntry.k2 == k2) {
144                    if (prevEntry == null) {
145                        table[index] = oldEntry.next;
146                    } else {
147                        prevEntry.next = oldEntry.next;
148                    }
149                    return true;
150                }
151            }
152            return false;
153        } finally {
154            lock.unlock();
155        }
156    }
157
158    public V getFirst(Predicate<K1> p1, Predicate<K2> p2) {
159        for (Entry entry : table) {
160            while (entry != null) {
161                if (p1.test((K1) entry.k1) && p2.test((K2) entry.k2)) {
162                    return (V) entry.v;
163                }
164                entry = entry.next;
165            }
166        }
167        return null;
168    }
169
170    public int size() {
171        Entry[] table = this.table;
172        int n = 0;
173        if (table != null) {
174            for (Entry e : table) {
175                for (Entry c = e; c != null; c = c.next) {
176                    n++;
177                }
178            }
179        }
180        return n;
181    }
182
183    public void clear() {
184        lock.lock();
185        try {
186            this.table = new Entry[table.length];
187        } finally {
188            lock.unlock();
189        }
190    }
191
192    static int smear(int hashCode) {
193        return C2 * Integer.rotateLeft(hashCode * C1, 15);
194    }
195
196    static int closedTableSize(int expectedEntries) {
197        // Get the recommended table size.
198        // Round down to the nearest power of 2.
199        expectedEntries = Math.max(expectedEntries, 2);
200        int tableSize = Integer.highestOneBit(expectedEntries);
201        // Check to make sure that we will not exceed the maximum load factor.
202        if (expectedEntries > (int) (MAX_LOAD_FACTOR * tableSize)) {
203            tableSize <<= 1;
204            return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE;
205        }
206        return tableSize;
207    }
208
209}