001: /*
002: Copyright (c) 2006, Matthew Estes
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright
009: notice, this list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright
011: notice, this list of conditions and the following disclaimer in the
012: documentation and/or other materials provided with the distribution.
013: * Neither the name of Metanotion Software nor the names of its
014: contributors may be used to endorse or promote products derived from this
015: software without specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018: IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
019: THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
020: PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029: package net.metanotion.io.block;
030:
031: import java.io.File;
032: import java.io.IOException;
033: import java.io.RandomAccessFile;
034: import java.util.HashMap;
035: import java.util.Iterator;
036: import java.util.Set;
037:
038: import net.metanotion.io.RAIFile;
039: import net.metanotion.io.RandomAccessInterface;
040: import net.metanotion.io.Serializer;
041: import net.metanotion.io.data.IntBytes;
042: import net.metanotion.io.data.LongBytes;
043: import net.metanotion.io.data.NullBytes;
044: import net.metanotion.io.data.StringBytes;
045:
046: import net.metanotion.io.block.index.BSkipList;
047:
048: class CorruptFileException extends IOException {
049: }
050:
051: class BadFileFormatException extends IOException {
052: }
053:
054: class BadVersionException extends IOException {
055: }
056:
057: public class BlockFile {
058: public static final long PAGESIZE = 1024;
059: public static final long OFFSET_MOUNTED = 20;
060:
061: public RandomAccessInterface file;
062:
063: private long magicBytes = 0x3141deadbeef0100L;
064: private long fileLen = PAGESIZE * 2;
065: private int freeListStart = 0;
066: private short mounted = 0;
067: public short spanSize = 127;
068:
069: private BSkipList metaIndex = null;
070: private HashMap openIndices = new HashMap();
071:
072: private void mount() throws IOException {
073: file.seek(BlockFile.OFFSET_MOUNTED);
074: mounted = 1;
075: file.writeShort(mounted);
076: }
077:
078: private void writeSuperBlock() throws IOException {
079: file.seek(0);
080: file.writeLong(magicBytes);
081: file.writeLong(fileLen);
082: file.writeInt(freeListStart);
083: file.writeShort(mounted);
084: file.writeShort(spanSize);
085: }
086:
087: private void readSuperBlock() throws IOException {
088: file.seek(0);
089: magicBytes = file.readLong();
090: fileLen = file.readLong();
091: freeListStart = file.readInt();
092: mounted = file.readShort();
093: spanSize = file.readShort();
094: }
095:
096: public static void main(String args[]) {
097: try {
098: RAIFile raif = new RAIFile(new File(args[0]), true, true);
099: BlockFile bf = new BlockFile(raif, true);
100:
101: //bf.metaIndex.delete();
102: bf.makeIndex("foo", new NullBytes(), new NullBytes());
103:
104: BSkipList b = bf.getIndex("foo", new NullBytes(),
105: new NullBytes());
106: System.out.println(bf.allocPage());
107:
108: bf.close();
109: raif.close();
110: } catch (Exception e) {
111: e.printStackTrace();
112: }
113: }
114:
115: public int writeMultiPageData(byte[] data, int page,
116: int[] curPageOff, int[] nextPage) throws IOException {
117: int pageCounter = curPageOff[0];
118: int curNextPage = nextPage[0];
119: int curPage = page;
120: int dct = 0;
121: while (dct < data.length) {
122: int len = ((int) BlockFile.PAGESIZE) - pageCounter;
123: if (len <= 0) {
124: if (curNextPage == 0) {
125: curNextPage = this .allocPage();
126: BlockFile.pageSeek(this .file, curNextPage);
127: this .file.writeInt(0);
128: BlockFile.pageSeek(this .file, curPage);
129: this .file.writeInt(curNextPage);
130: }
131: BlockFile.pageSeek(this .file, curNextPage);
132: curPage = curNextPage;
133: curNextPage = this .file.readInt();
134: pageCounter = 4;
135: len = ((int) BlockFile.PAGESIZE) - pageCounter;
136: }
137: this .file
138: .write(data, dct, Math.min(len, data.length - dct));
139: pageCounter += Math.min(len, data.length - dct);
140: dct += Math.min(len, data.length - dct);
141: }
142: nextPage[0] = curNextPage;
143: curPageOff[0] = pageCounter;
144: return curPage;
145: }
146:
147: public int readMultiPageData(byte[] arr, int page,
148: int[] curPageOff, int[] nextPage) throws IOException {
149: int pageCounter = curPageOff[0];
150: int curNextPage = nextPage[0];
151: int curPage = page;
152: int dct = 0;
153: int res;
154: while (dct < arr.length) {
155: int len = ((int) BlockFile.PAGESIZE) - pageCounter;
156: if (len <= 0) {
157: BlockFile.pageSeek(this .file, curNextPage);
158: curPage = curNextPage;
159: curNextPage = this .file.readInt();
160: pageCounter = 4;
161: len = ((int) BlockFile.PAGESIZE) - pageCounter;
162: }
163: res = this .file.read(arr, dct, Math.min(len, arr.length
164: - dct));
165: if (res == -1) {
166: throw new IOException();
167: }
168: pageCounter += Math.min(len, arr.length - dct);
169: dct += res;
170: }
171: nextPage[0] = curNextPage;
172: curPageOff[0] = pageCounter;
173: return curPage;
174: }
175:
176: public BlockFile(RandomAccessInterface rai) throws IOException {
177: this (rai, false);
178: }
179:
180: public BlockFile(RandomAccessFile raf) throws IOException {
181: this (new RAIFile(raf), false);
182: }
183:
184: public BlockFile(RandomAccessFile raf, boolean init)
185: throws IOException {
186: this (new RAIFile(raf), init);
187: }
188:
189: public BlockFile(File f, boolean init) throws IOException {
190: this (new RAIFile(f, true, true), init);
191: }
192:
193: public BlockFile(RandomAccessInterface rai, boolean init)
194: throws IOException {
195: if (rai == null) {
196: throw new NullPointerException();
197: }
198:
199: file = rai;
200:
201: if (init) {
202: file.setLength(fileLen);
203: writeSuperBlock();
204: BSkipList.init(this , 2, spanSize);
205: }
206:
207: readSuperBlock();
208: if (magicBytes != 0x3141deadbeef0100L) {
209: if ((magicBytes & 0x3141deadbeef0000L) == 0x3141deadbeef0000L) {
210: throw new BadVersionException();
211: } else {
212: throw new BadFileFormatException();
213: }
214: }
215: // if(mounted != 0) { throw new CorruptFileException(); }
216: if (fileLen != file.length()) {
217: throw new CorruptFileException();
218: }
219: mount();
220:
221: metaIndex = new BSkipList(spanSize, this , 2, new StringBytes(),
222: new IntBytes());
223: }
224:
225: public static void pageSeek(RandomAccessInterface file, int page)
226: throws IOException {
227: file.seek((((long) page) - 1L) * BlockFile.PAGESIZE);
228: }
229:
230: public int allocPage() throws IOException {
231: if (freeListStart != 0) {
232: FreeListBlock flb = new FreeListBlock(file, freeListStart);
233: if (flb.len > 0) {
234: flb.len = flb.len - 1;
235: int page = flb.branches[flb.len];
236: flb.writeBlock();
237: return page;
238: } else {
239: freeListStart = flb.nextPage;
240: writeSuperBlock();
241: return flb.page;
242: }
243: }
244: long offset = file.length();
245: fileLen = offset + BlockFile.PAGESIZE;
246: file.setLength(fileLen);
247: writeSuperBlock();
248: return ((int) ((long) (offset / BlockFile.PAGESIZE))) + 1;
249: }
250:
251: public void freePage(int page) throws IOException {
252: System.out.println("Free Page " + page);
253: if (freeListStart == 0) {
254: freeListStart = page;
255: FreeListBlock.initPage(file, page);
256: writeSuperBlock();
257: return;
258: }
259: FreeListBlock flb = new FreeListBlock(file, freeListStart);
260: if (flb.isFull()) {
261: FreeListBlock.initPage(file, page);
262: if (flb.nextPage == 0) {
263: flb.nextPage = page;
264: flb.writeBlock();
265: return;
266: } else {
267: flb = new FreeListBlock(file, page);
268: flb.nextPage = freeListStart;
269: flb.writeBlock();
270: freeListStart = page;
271: writeSuperBlock();
272: return;
273: }
274: }
275: flb.addPage(page);
276: flb.writeBlock();
277: }
278:
279: public BSkipList getIndex(String name, Serializer key,
280: Serializer val) throws IOException {
281: Integer page = (Integer) metaIndex.get(name);
282: if (page == null) {
283: return null;
284: }
285: BSkipList bsl = new BSkipList(spanSize, this , page.intValue(),
286: key, val);
287: openIndices.put(name, bsl);
288: return bsl;
289: }
290:
291: public BSkipList makeIndex(String name, Serializer key,
292: Serializer val) throws IOException {
293: if (metaIndex.get(name) != null) {
294: throw new IOException("Index already exists");
295: }
296: int page = allocPage();
297: metaIndex.put(name, new Integer(page));
298: BSkipList.init(this , page, spanSize);
299: BSkipList bsl = new BSkipList(spanSize, this , page, key, val);
300: openIndices.put(name, bsl);
301: return bsl;
302: }
303:
304: public void delIndex(String name) throws IOException {
305: Integer page = (Integer) metaIndex.remove(name);
306: if (page == null) {
307: return;
308: }
309: NullBytes nb = new NullBytes();
310: BSkipList bsl = new BSkipList(spanSize, this , page.intValue(),
311: nb, nb);
312: bsl.delete();
313: }
314:
315: public void close() throws IOException {
316: metaIndex.close();
317: metaIndex = null;
318:
319: Set oi = openIndices.keySet();
320: Iterator i = oi.iterator();
321: Object k;
322: while (i.hasNext()) {
323: k = i.next();
324: BSkipList bsl = (BSkipList) openIndices.get(k);
325: bsl.close();
326: }
327:
328: // Unmount.
329: file.seek(BlockFile.OFFSET_MOUNTED);
330: file.writeShort(0);
331: }
332: }
|