1 | /* ============================================================= |
2 | * SmallSQL : a free Java DBMS library for the Java(tm) platform |
3 | * ============================================================= |
4 | * |
5 | * (C) Copyright 2004-2006, by Volker Berlin. |
6 | * |
7 | * Project Info: http://www.smallsql.de/ |
8 | * |
9 | * This library is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU Lesser General Public License as published by |
11 | * the Free Software Foundation; either version 2.1 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
17 | * License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Lesser General Public |
20 | * License along with this library; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
22 | * USA. |
23 | * |
24 | * [Java is a trademark or registered trademark of Sun Microsystems, Inc. |
25 | * in the United States and other countries.] |
26 | * |
27 | * --------------- |
28 | * StorePageMap.java |
29 | * --------------- |
30 | * Author: Volker Berlin |
31 | * |
32 | * Created on 13.08.2004 |
33 | */ |
34 | package smallsql.database; |
35 | |
36 | /** |
37 | * @author Volker Berlin |
38 | */ |
39 | class StorePageMap { |
40 | |
41 | |
42 | |
43 | /** |
44 | * The table, resized as necessary. Length MUST Always be a power of two. |
45 | */ |
46 | private Entry[] table; |
47 | |
48 | /** |
49 | * The number of key-value mappings contained in this identity hash map. |
50 | */ |
51 | private int size; |
52 | |
53 | /** |
54 | * The next size value at which to resize (capacity * load factor). |
55 | * @serial |
56 | */ |
57 | private int threshold; |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | /** |
64 | * Constructs an empty <tt>HashMap</tt> with the default initial capacity |
65 | * (16) and the default load factor (0.75). |
66 | */ |
67 | StorePageMap() { |
68 | threshold = 12; |
69 | table = new Entry[17]; |
70 | } |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | /** |
78 | * Returns the number of key-value mappings in this map. |
79 | * |
80 | * @return the number of key-value mappings in this map. |
81 | */ |
82 | final int size() { |
83 | return size; |
84 | } |
85 | |
86 | /** |
87 | * Returns <tt>true</tt> if this map contains no key-value mappings. |
88 | * |
89 | * @return <tt>true</tt> if this map contains no key-value mappings. |
90 | */ |
91 | final boolean isEmpty() { |
92 | return size == 0; |
93 | } |
94 | |
95 | /** |
96 | * Returns the first StorePage for the given key. |
97 | */ |
98 | final TableStorePage get(long key) { |
99 | int i = (int)(key % table.length); |
100 | Entry e = table[i]; |
101 | while (true) { |
102 | if (e == null) |
103 | return null; |
104 | if (e.key == key) |
105 | return e.value; |
106 | e = e.next; |
107 | } |
108 | } |
109 | |
110 | /** |
111 | * Returns <tt>true</tt> if this map contains a StorePage for the |
112 | * specified key. |
113 | * |
114 | */ |
115 | final boolean containsKey(long key) { |
116 | return (get(key) != null); |
117 | } |
118 | |
119 | |
120 | /** |
121 | * Add the StorePage with the key. Multiple StorePage for the same key are valid. |
122 | * The cause are multiple changes in one transaction. With SavePoints a rollback to a older |
123 | * StorePage is valid.<p> |
124 | * The latest StorePage is placed at first pos. |
125 | */ |
126 | final TableStorePage add(long key, TableStorePage value) { |
127 | int i = (int)(key % table.length); |
128 | |
129 | table[i] = new Entry(key, value, table[i]); |
130 | if (size++ >= threshold) |
131 | resize(2 * table.length); |
132 | return null; |
133 | } |
134 | |
135 | |
136 | /** |
137 | * Rehashes the contents of this map into a new array with a |
138 | * larger capacity. This method is called automatically when the |
139 | * number of keys in this map reaches its threshold. |
140 | * |
141 | * If current capacity is MAXIMUM_CAPACITY, this method does not |
142 | * resize the map, but but sets threshold to Integer.MAX_VALUE. |
143 | * This has the effect of preventing future calls. |
144 | * |
145 | * @param newCapacity the new capacity, MUST be a power of two; |
146 | * must be greater than current capacity unless current |
147 | * capacity is MAXIMUM_CAPACITY (in which case value |
148 | * is irrelevant). |
149 | */ |
150 | final private void resize(int newCapacity) { |
151 | |
152 | Entry[] newTable = new Entry[newCapacity]; |
153 | transfer(newTable); |
154 | table = newTable; |
155 | threshold = (int)(newCapacity * 0.75f); |
156 | } |
157 | |
158 | /** |
159 | * Transfer all entries from current table to newTable. |
160 | */ |
161 | final private void transfer(Entry[] newTable) { |
162 | Entry[] src = table; |
163 | int newCapacity = newTable.length; |
164 | for (int j = 0; j < src.length; j++) { |
165 | Entry e = src[j]; |
166 | if (e != null) { |
167 | src[j] = null; |
168 | do { |
169 | Entry next = e.next; |
170 | e.next = null; |
171 | int i = (int)(e.key % newCapacity); |
172 | //The order for StorePages with the same key must not change |
173 | //that we need to find the end of the link list. This is different to a typical HashTable |
174 | if(newTable[i] == null){ |
175 | newTable[i] = e; |
176 | }else{ |
177 | Entry entry = newTable[i]; |
178 | while(entry.next != null) entry = entry.next; |
179 | entry.next = e; |
180 | } |
181 | e = next; |
182 | } while (e != null); |
183 | } |
184 | } |
185 | } |
186 | |
187 | |
188 | /** |
189 | * Removes the mapping for this key from this map if present. |
190 | * |
191 | * @param key key whose mapping is to be removed from the map. |
192 | * @return previous value associated with specified key, or <tt>null</tt> |
193 | * if there was no mapping for key. A <tt>null</tt> return can |
194 | * also indicate that the map previously associated <tt>null</tt> |
195 | * with the specified key. |
196 | */ |
197 | final TableStorePage remove(long key) { |
198 | int i = (int)(key % table.length); |
199 | Entry prev = table[i]; |
200 | Entry e = prev; |
201 | |
202 | while (e != null) { |
203 | Entry next = e.next; |
204 | if (e.key == key) { |
205 | size--; |
206 | if (prev == e) |
207 | table[i] = next; |
208 | else |
209 | prev.next = next; |
210 | return e.value; |
211 | } |
212 | prev = e; |
213 | e = next; |
214 | } |
215 | return null; |
216 | } |
217 | |
218 | |
219 | /** |
220 | * Removes all mappings from this map. |
221 | */ |
222 | final void clear() { |
223 | Entry tab[] = table; |
224 | for (int i = 0; i < tab.length; i++) |
225 | tab[i] = null; |
226 | size = 0; |
227 | } |
228 | |
229 | /** |
230 | * Returns <tt>true</tt> if this map maps one or more keys to the |
231 | * specified value. |
232 | * |
233 | * @param value value whose presence in this map is to be tested. |
234 | * @return <tt>true</tt> if this map maps one or more keys to the |
235 | * specified value. |
236 | */ |
237 | final boolean containsValue(TableStorePage value) { |
238 | Entry tab[] = table; |
239 | for (int i = 0; i < tab.length ; i++) |
240 | for (Entry e = tab[i] ; e != null ; e = e.next) |
241 | if (value.equals(e.value)) |
242 | return true; |
243 | return false; |
244 | } |
245 | |
246 | |
247 | |
248 | static class Entry{ |
249 | final long key; |
250 | final TableStorePage value; |
251 | Entry next; |
252 | |
253 | /** |
254 | * Create new entry. |
255 | */ |
256 | Entry(long k, TableStorePage v, Entry n) { |
257 | value = v; |
258 | next = n; |
259 | key = k; |
260 | } |
261 | |
262 | |
263 | } |
264 | |
265 | |
266 | } |