KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > sql > file > SubPageManager


1 package com.quadcap.sql.file;
2
3 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.io.IOException JavaDoc;
42 import java.io.InputStream JavaDoc;
43 import java.io.OutputStream JavaDoc;
44
45 import com.quadcap.util.ConfigNumber;
46 import com.quadcap.util.Debug;
47 import com.quadcap.util.Util;
48
49 /**
50  * This class implements a sub-page manager which operates by allocating
51  * pages as needed from the main file and chopping them up into smaller
52  * pages of the appropriate size.
53  *
54  * @author Stan Bailes
55  */

56 public class SubPageManager implements PageManager {
57     BlockFile file;
58     /**
59      * rootBlock is the root block of the entire block file, and will
60      * always be zero in this implementation.
61      */

62     long rootBlock;
63
64     /** The size of a subpage */
65     int pageSize;
66
67     /** The page shift (file.pageSize >> pageShift == pageSize) */
68     int pageShift;
69
70     /** The offset into the root block of our freelist head */
71     int offset;
72
73     /** (1 << pageShift) */
74     int pagesPerBlock;
75
76     // kinda unique.
77
static final long PAGE_FREE = 0xabcddbca5438182fL;
78
79     /**
80      * Construct a new sub-page manager bound to the specified block file
81      */

82     public SubPageManager(BlockFile file, int pageShift, long rootBlock)
83         throws IOException JavaDoc
84     {
85     this.file = file;
86     this.pageShift = pageShift;
87     this.rootBlock = rootBlock;
88     this.offset = BlockFile.oSUBPAGE_ROOT + pageShift * BlockFile.REF_SIZE;
89     this.pageSize = file.getPageSize() >> pageShift;
90     this.pagesPerBlock = 1 << pageShift;
91         if (pageSize < BlockFile.REF_SIZE * 2) {
92             throw new IOException JavaDoc("Sub page too small: " + pageSize);
93         }
94     }
95
96     /**
97      * Allocate a new block.
98      */

99     public long newPage() throws IOException JavaDoc {
100     long ret = -1;
101         synchronized (file.fileLock) {
102             Block root = file.getBlock(rootBlock);
103             Page page = null;
104             try {
105                 ret = root.readLong(offset);
106                 if (ret == 0) {
107                     ret = allocateNewBlock();
108                 }
109                 page = getPage(ret);
110                 long next = page.readLong(0);
111                 if (page.readLong(BlockFile.REF_SIZE) != PAGE_FREE) {
112                     //#ifdef DEBUG
113
byte[] ff = new byte[pageSize];
114                     page.read(0, ff, 0, pageSize);
115                     Debug.println("Page: " + Util.strBytes(ff));
116                     Debug.println("" + toString() +
117                                   ".newPage() " +
118                                   toString(ret) +
119                                   ", freelist corrupted: ");
120                     //#endif
121
throw new DatafileException("" + toString() +
122                                                ".newPage() " +
123                                                toString(ret) +
124                                                ", freelist corrupted: ");
125
126                 }
127                 root.writeLong(offset, next);
128                 page.clear(); // Can we let caller do this?
129
} finally {
130                 if (page != null) page.decrRefCount();
131                 root.decrRefCount();
132             }
133             //#ifdef DEBUG
134
if (Trace.bit(11)) {
135                 Debug.println("" + toString() + ".newPage() = " +
136                               toString(ret));
137             }
138             if (pageShift(ret) != pageShift) {
139                 throw new DatafileException("" + toString() + ".newPage(), " +
140                                            toString(ret) +
141                                            ", freelist corrupted");
142             }
143             //#endif
144
}
145     return ret;
146     }
147     
148     /**
149      * Free a block.
150      *
151      * @param block a previously allocated block
152      */

153     public void freePage(long block) throws IOException JavaDoc {
154 //- //#ifdef PARANOID
155
//- checkPage(block);
156
//#endif
157
//#ifdef DEBUG
158
if (Trace.bit(11)) {
159         Debug.println("" + toString() + ".freePage(" +
160               toString(block) + ")");
161     }
162     //#endif
163
if (pageShift(block) != pageShift) {
164         throw new DatafileException("" + toString() + ".freePage(" +
165                        toString(block) + "), bad page");
166     }
167         synchronized (file.fileLock) {
168             Block root = file.getBlock(rootBlock);
169             try {
170                 long head = root.readLong(offset);
171                 if (head == block) {
172                     throw new DatafileException("" + toString() + ",freePage(" +
173                                                toString(block) + "), already" +
174                                                " free");
175                 }
176                 Page page = getPage(block);
177                 page.writeLong(0, head);
178                 if (page.readLong(BlockFile.REF_SIZE) == PAGE_FREE) {
179                     throw new DatafileException("" + toString() + ",freePage(" +
180                                                toString(block) + "), already" +
181                                                " free");
182                 }
183                 page.writeLong(BlockFile.REF_SIZE, PAGE_FREE);
184                 root.writeLong(offset, block);
185                 page.decrRefCount();
186             } finally {
187                 root.decrRefCount();
188             }
189         }
190     }
191
192     /**
193      * Allocate new block and slice it up into sub-pages. Link the sub-pages
194      * together and return the head of the list. Lock must be held!
195      */

196     private final long allocateNewBlock() throws IOException JavaDoc {
197     long blk = file.newPage();
198     //#ifdef DEBUG
199
if (Trace.bit(11)) {
200         Debug.println("" + toString() + ".allocateNewBlock: " + blk);
201     }
202     //#endif
203
Block b = file.getBlock(blk);
204     b.writeLong(0, 0);
205     b.writeLong(BlockFile.REF_SIZE, PAGE_FREE);
206     try {
207         long p = makePageNo(blk, 0);
208         for (int i = 1; i < pagesPerBlock; i++) {
209         long np = makePageNo(blk, i);
210         Page page = getPage(np);
211         try {
212             page.writeLong(0, p);
213             page.writeLong(BlockFile.REF_SIZE, PAGE_FREE);
214             p = np;
215         } finally {
216             page.decrRefCount();
217         }
218         }
219         return p;
220     } finally {
221         b.decrRefCount();
222     }
223     }
224
225     /**
226      * Sub-page numbers are encoded as follows:<p>
227      * <pre>
228      * sub_page {
229      * pageShift bit4
230      * pageNum bit16
231      * blockNum bit44
232      * }
233      * </pre><p>
234      *
235      * This has the desirable property of being backward compatible with the
236      * pre-sub-page block numbers, which will have pageShift and pageNum
237      * equal to zero.
238      */

239
240     static int[] PAGE_SHIFT = {63,4};
241     static int[] PAGE_NUM = {59,16};
242 //- //#ifdef PARANOID
243
//- static int[] MAGIC = {43,4};
244
//- static int[] BLOCK_NUM = {39,40};
245
//#else
246
static int[] BLOCK_NUM = {43,44};
247     //#endif
248

249     static final long fIns(long v, int[] f) {
250         return (v & fMask(f)) << fEnd(f);
251     }
252     static final long fMask(int[] f) {
253         return (1L << f[1]) - 1;
254     }
255     static final long fEnd(int[] f) {
256         return f[0] - f[1] + 1;
257     }
258     static final long fExt(long v, int[] f) {
259         return (v >> fEnd(f)) & fMask(f);
260     }
261
262     final long makePageNo(long blk, long page) {
263     // we could mask here to "prevent" errors, but that really doesnt'
264
// help. We could range check 'blk' and 'page' but we want this
265
// fn to be fast, and hope we can be assured that the rest of the
266
// code ensures the invariants.
267
long ret = fIns(pageShift, PAGE_SHIFT)
268 //- //#ifdef PARANOID
269
//- | fIns(0xc, MAGIC)
270
//#endif
271
| fIns(page, PAGE_NUM)
272             | fIns(blk, BLOCK_NUM);
273 //- //#ifdef PARANOID
274
//- if (fExt(ret, BLOCK_NUM) != blk) {
275
//- Debug.println("pageShift: " + pageShift + ": " +
276
//- fIns(pageShift, PAGE_SHIFT));
277
//- Debug.println("page: " + page + ": " +
278
//- fIns(page, PAGE_NUM));
279
//- Debug.println("block: " + blk + ": " +
280
//- fIns(blk, BLOCK_NUM));
281
//- Debug.println("============Bad blk: " + blk + ", " + page +
282
//- ", " + toString(ret));
283
//- }
284
//#endif
285
return ret;
286     }
287
288     /**
289      * Return the number of the block that contains this page.
290      */

291     public static final long pageBlock(long page) {
292 //- //#ifdef PARANOID
293
//- checkPage(page);
294
//#endif
295
return fExt(page, BLOCK_NUM);
296     }
297
298 //- //#ifdef PARANOID
299
//- static public void checkPage(long page) {
300
//- if (fExt(page, PAGE_SHIFT) != 0 &&
301
//- (fExt(page, MAGIC) != 0xc ||
302
//- (fExt(page, PAGE_NUM) >= (1 << fExt(page, PAGE_SHIFT))))) {
303
//- String s = "Page[ps:" + fExt(page, PAGE_SHIFT) +
304
//- " blk:" + fExt(page, BLOCK_NUM) +
305
//- " off:" + fExt(page, PAGE_NUM) + ']';
306
//- throw new RuntimeException("Bad page: " + s);
307
//- }
308
//- }
309
//#endif
310

311     /**
312      * Return the byte offset of this page in the base block.
313      */

314     public final int pageOffset(long page) {
315 //- //#ifdef PARANOID
316
//- checkPage(page);
317
//#endif
318
return (int)(fExt(page, PAGE_NUM) * pageSize);
319     }
320
321     /**
322      * Return the page shift for this page.
323      */

324     static final int pageShift(long page) {
325 //- //#ifdef PARANOID
326
//- checkPage(page);
327
//#endif
328
return (int)(fExt(page, PAGE_SHIFT));
329     }
330     
331     /**
332      * Return the specified page
333      */

334     public Page getPage(long block) throws IOException JavaDoc {
335 //- //#ifdef PARANOID
336
//- checkPage(block);
337
//#endif
338
return new SubPage(this, block);
339     }
340
341     /**
342      * Return the (file) lock
343      */

344     public Object JavaDoc getLock() { return file.getLock(); }
345
346     /**
347      * Return this manager's block size
348      */

349     public final int getPageSize() {
350     return pageSize;
351     }
352
353     /**
354      * Return a new input stream, reading from the region with the specified
355      * root block.
356      *
357      * @param block the root block of the region
358      * @return an InputStream bound to the region.
359      * @exception IOException if the block number isn't valid, or if another
360      * error is detected trying to access the region.
361      */

362     public RandomAccessInputStream getInputStream(long block)
363     throws IOException JavaDoc
364     {
365     return new RandomAccessInputStream(getStream(block));
366     }
367
368     /**
369      * Return a new output stream, writing to the region with the specified
370      * root block.
371      *
372      * @param block the root block of the region
373      * @return an OutputStream bound to the region.
374      * @exception IOException if the block number isn't valid, or if another
375      * error is detected trying to access the region.
376      */

377     public RandomAccessOutputStream getOutputStream(long block)
378     throws IOException JavaDoc
379     {
380     return new RandomAccessOutputStream(getStream(block));
381     }
382
383     public RandomAccess getStream(long blockRef) throws IOException JavaDoc {
384     return new BlockAccess(this, blockRef);
385     }
386
387     /**
388      * Destroy the stream with the specified root page and free up the
389      * storage it was using.
390      *
391      * @param page the root page of the region
392      *
393      * @exception IOException if the page number isn't valid, or if another
394      * error is detected trying to access the region.
395      */

396     public void freeStream(long page) throws IOException JavaDoc {
397         synchronized (file.fileLock) {
398             getStream(page).resize(0);
399             freePage(page);
400         }
401     }
402
403     //#ifdef DEBUG
404
/**
405      * Return a displayable representation for this object.
406      */

407     public String JavaDoc toString() {
408     return "PM" + pageSize + ":[" + rootBlock + "," + offset + "]";
409     }
410     //#endif
411

412     public static String JavaDoc toString(long page) {
413         int psize = 8192 >> pageShift(page);
414         long pnum = fExt(page, PAGE_NUM);
415     return "Page(" + psize +
416             ")[" + pageBlock(page) +
417             ":" + pnum + " (" + (pnum * psize) + ")" +
418             ']';
419     }
420 }
421
Popular Tags