001: /**
002: * com.mckoi.store.HeapStore 20 Feb 2003
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.store;
024:
025: import com.mckoi.util.ByteArrayUtil;
026: import java.util.List;
027: import java.io.ByteArrayInputStream;
028: import java.io.InputStream;
029: import java.io.OutputStream;
030: import java.io.IOException;
031:
032: /**
033: * An implementation of the Store interface that persists information in the
034: * volatile JVM heap memory. Each Area in the store is represented by a byte[]
035: * array from the Java heap.
036: * <p>
037: * Note that in Java there is no way to cast a reference to a numeric value,
038: * or to cast a numeric value back into a reference. This means that
039: * Area lookup has to be coordinated via a hashing algorithm over an array.
040: * There would prehaps be a more efficient way to achieve this with JNI but
041: * it would mean we can't be pure Java and it would require locking the address
042: * of objects in the heap.
043: * <p>
044: * Another alternative way of implementing this class would be to use JNI to
045: * access a C style 'malloc' function in the operating system and wrap the
046: * memory with a native Area implementation.
047: *
048: * @author Tobias Downer
049: */
050:
051: public final class HeapStore implements Store {
052:
053: /**
054: * The fixed area element (a 64 byte area).
055: */
056: private HeapAreaElement fixed_area_element;
057:
058: /**
059: * A hash map of area pointer to byte[] array that represents the area.
060: */
061: private HeapAreaElement[] area_map;
062:
063: /**
064: * A unique id key incremented for each new area created.
065: */
066: private long unique_id_key;
067:
068: /**
069: * Creates the HeapStore.
070: */
071: public HeapStore(int hash_size) {
072: area_map = new HeapAreaElement[hash_size];
073: unique_id_key = 0;
074: }
075:
076: /**
077: * Defaults heap size to 257 elements.
078: */
079: public HeapStore() {
080: this (257);
081: }
082:
083: /**
084: * Searches the hash map and returns the area element for the given pointer.
085: */
086: private HeapAreaElement getAreaElement(long pointer)
087: throws IOException {
088: synchronized (this ) {
089: // Find the pointer in the hash
090: int hash_pos = (int) (pointer % area_map.length);
091: HeapAreaElement prev = null;
092: HeapAreaElement element = area_map[hash_pos];
093: // Search for this pointer
094: while (element != null && element.getID() != pointer) {
095: prev = element;
096: element = element.next_hash_element;
097: }
098: // If not found
099: if (element == null) {
100: throw new IOException("Pointer " + pointer
101: + " is invalid.");
102: }
103: // Move the element to the start of the list.
104: if (prev != null) {
105: prev.next_hash_element = element.next_hash_element;
106: element.next_hash_element = area_map[hash_pos];
107: area_map[hash_pos] = element;
108: }
109: // Return the element
110: return element;
111: }
112: }
113:
114: /**
115: * Returns a MutableArea object for the fixed position.
116: */
117: private HeapAreaElement getFixedAreaElement() {
118: synchronized (this ) {
119: if (fixed_area_element == null) {
120: fixed_area_element = new HeapAreaElement(-1, 64);
121: }
122: return fixed_area_element;
123: }
124: }
125:
126: /**
127: * Returns the HeapAreaElement for the given pointer.
128: */
129: private HeapAreaElement getElement(long pointer) throws IOException {
130: if (pointer == -1) {
131: return getFixedAreaElement();
132: } else {
133: return getAreaElement(pointer);
134: }
135: }
136:
137: // ---------- Implemented from Store ----------
138:
139: public AreaWriter createArea(long size) throws IOException {
140: if (size > Integer.MAX_VALUE) {
141: throw new IOException("'size' is too large.");
142: }
143: synchronized (this ) {
144: // Generate a unique id for this area.
145: long id = unique_id_key;
146: ++unique_id_key;
147:
148: // Create the element.
149: HeapAreaElement element = new HeapAreaElement(id,
150: (int) size);
151: // The position in the hash map
152: int hash_pos = (int) (id % area_map.length);
153: // Add to the chain
154: element.next_hash_element = area_map[hash_pos];
155: // Set the element in the chain
156: area_map[hash_pos] = element;
157: // And return the object
158: return element.getAreaWriter();
159: }
160: }
161:
162: public void deleteArea(long pointer) throws IOException {
163: synchronized (this ) {
164: // Find the pointer in the hash
165: int hash_pos = (int) (pointer % area_map.length);
166: HeapAreaElement prev = null;
167: HeapAreaElement element = area_map[hash_pos];
168: // Search for this pointer
169: while (element != null && element.getID() != pointer) {
170: prev = element;
171: element = element.next_hash_element;
172: }
173: // If not found
174: if (element == null) {
175: throw new IOException("Pointer " + pointer
176: + " is invalid.");
177: }
178: // Remove
179: if (prev == null) {
180: area_map[hash_pos] = element.next_hash_element;
181: } else {
182: prev.next_hash_element = element.next_hash_element;
183: }
184: // Garbage collector should do the rest...
185: }
186: }
187:
188: public InputStream getAreaInputStream(long pointer)
189: throws IOException {
190: return getElement(pointer).getInputStream();
191: }
192:
193: public Area getArea(long pointer) throws IOException {
194: return getElement(pointer).getMutableArea();
195: }
196:
197: public MutableArea getMutableArea(long pointer) throws IOException {
198: return getElement(pointer).getMutableArea();
199: }
200:
201: public void flush() throws IOException {
202: // Not required
203: }
204:
205: public void synch() throws IOException {
206: // Not required
207: }
208:
209: public void lockForWrite() {
210: // Not required
211: }
212:
213: public void unlockForWrite() {
214: // Not required
215: }
216:
217: // ---------- Diagnostic ----------
218:
219: public boolean lastCloseClean() {
220: // Close is not possible with a heap store, so always return true
221: return true;
222: }
223:
224: public List getAllAreas() throws IOException {
225: throw new RuntimeException("PENDING");
226: }
227:
228: // ---------- Inner classes ----------
229:
230: /**
231: * An implementation of Area for a byte[] array from the heap.
232: */
233: private static class HeapArea implements MutableArea {
234:
235: /**
236: * The ID of this area.
237: */
238: private final long id;
239:
240: /**
241: * A pointer to the byte[] array representing the entire area.
242: */
243: private final byte[] heap_area;
244:
245: /**
246: * The start pointer in the heap area.
247: */
248: private final int start_pointer;
249:
250: /**
251: * The current pointer into the area.
252: */
253: private int position;
254:
255: /**
256: * The end pointer of the area.
257: */
258: private final int end_pointer;
259:
260: /**
261: * Constructor.
262: */
263: HeapArea(long id, byte[] heap_area, int offset, int length) {
264: this .id = id;
265: this .heap_area = heap_area;
266: this .start_pointer = offset;
267: this .position = offset;
268: this .end_pointer = offset + length;
269: }
270:
271: private int checkPositionBounds(int diff) throws IOException {
272: final int new_pos = position + diff;
273: if (new_pos > end_pointer) {
274: throw new IOException("Position out of bounds. "
275: + " start=" + start_pointer + " end="
276: + end_pointer + " pos=" + position
277: + " new_pos=" + new_pos);
278: }
279: final int old_pos = position;
280: position = new_pos;
281: return old_pos;
282: }
283:
284: public long getID() {
285: return id;
286: }
287:
288: public int position() {
289: return position - start_pointer;
290: }
291:
292: public int capacity() {
293: return end_pointer - start_pointer;
294: }
295:
296: public void position(int position) throws IOException {
297: int act_position = start_pointer + position;
298: if (act_position >= 0 && act_position < end_pointer) {
299: this .position = act_position;
300: return;
301: }
302: throw new IOException("Moved position out of bounds.");
303: }
304:
305: public void copyTo(AreaWriter destination, int size)
306: throws IOException {
307: final int BUFFER_SIZE = 2048;
308: byte[] buf = new byte[BUFFER_SIZE];
309: int to_copy = Math.min(size, BUFFER_SIZE);
310:
311: while (to_copy > 0) {
312: get(buf, 0, to_copy);
313: destination.put(buf, 0, to_copy);
314: size -= to_copy;
315: to_copy = Math.min(size, BUFFER_SIZE);
316: }
317: }
318:
319: public byte get() throws IOException {
320: return heap_area[checkPositionBounds(1)];
321: }
322:
323: public void put(byte b) throws IOException {
324: heap_area[checkPositionBounds(1)] = b;
325: }
326:
327: public void get(byte[] buf, int off, int len)
328: throws IOException {
329: System.arraycopy(heap_area, checkPositionBounds(len), buf,
330: off, len);
331: }
332:
333: public void put(byte[] buf, int off, int len)
334: throws IOException {
335: System.arraycopy(buf, off, heap_area,
336: checkPositionBounds(len), len);
337: }
338:
339: public void put(byte[] buf) throws IOException {
340: put(buf, 0, buf.length);
341: }
342:
343: public short getShort() throws IOException {
344: short s = ByteArrayUtil.getShort(heap_area,
345: checkPositionBounds(2));
346: return s;
347: }
348:
349: public void putShort(short s) throws IOException {
350: ByteArrayUtil
351: .setShort(s, heap_area, checkPositionBounds(2));
352: }
353:
354: public int getInt() throws IOException {
355: int i = ByteArrayUtil.getInt(heap_area,
356: checkPositionBounds(4));
357: return i;
358: }
359:
360: public void putInt(int i) throws IOException {
361: ByteArrayUtil.setInt(i, heap_area, checkPositionBounds(4));
362: }
363:
364: public long getLong() throws IOException {
365: long l = ByteArrayUtil.getLong(heap_area,
366: checkPositionBounds(8));
367: return l;
368: }
369:
370: public void putLong(long l) throws IOException {
371: ByteArrayUtil.setLong(l, heap_area, checkPositionBounds(8));
372: }
373:
374: public char getChar() throws IOException {
375: char c = ByteArrayUtil.getChar(heap_area,
376: checkPositionBounds(2));
377: return c;
378: }
379:
380: public void putChar(char c) throws IOException {
381: ByteArrayUtil.setChar(c, heap_area, checkPositionBounds(2));
382: }
383:
384: public void checkOut() {
385: // no-op
386: }
387:
388: public String toString() {
389: return "[Area start_pointer=" + start_pointer
390: + " end_pointer=" + end_pointer + " position="
391: + position + "]";
392: }
393:
394: }
395:
396: private static class HeapAreaWriter extends HeapArea implements
397: AreaWriter {
398:
399: public HeapAreaWriter(long id, byte[] heap_area, int offset,
400: int length) {
401: super (id, heap_area, offset, length);
402: }
403:
404: public OutputStream getOutputStream() {
405: return new AbstractStore.AreaOutputStream(this );
406: }
407:
408: public void finish() throws IOException {
409: // Currently, no-op
410: }
411:
412: }
413:
414: /**
415: * An area allocated from the heap store represented by a volatile byte[]
416: * array.
417: */
418: private static class HeapAreaElement {
419:
420: /**
421: * The id of this heap area (used as the hash key).
422: */
423: private final long heap_id;
424:
425: /**
426: * A byte[] array that represents the volatile heap area.
427: */
428: private final byte[] heap_area;
429:
430: /**
431: * The pointer to the next HeapAreaElement in this hash key.
432: */
433: HeapAreaElement next_hash_element;
434:
435: /**
436: * Constructs the HeapAreaElement.
437: */
438: HeapAreaElement(long heap_id, int area_size) {
439: this .heap_id = heap_id;
440: this .heap_area = new byte[area_size];
441: }
442:
443: /**
444: * Returns the heap id for this element.
445: */
446: long getID() {
447: return heap_id;
448: }
449:
450: /**
451: * Returns a new AreaWriter object for this element.
452: */
453: AreaWriter getAreaWriter() {
454: return new HeapAreaWriter(getID(), heap_area, 0,
455: heap_area.length);
456: }
457:
458: /**
459: * Returns a new MutableArea object for this element.
460: */
461: MutableArea getMutableArea() {
462: return new HeapArea(getID(), heap_area, 0, heap_area.length);
463: }
464:
465: /**
466: * Returns a new InputStream that is used to read from the area.
467: */
468: InputStream getInputStream() {
469: return new ByteArrayInputStream(heap_area);
470: }
471:
472: }
473:
474: }
|