1 /**
2 * JDBM LICENSE v1.00
3 *
4 * Redistribution and use of this software and associated documentation
5 * ("Software"), with or without modification, are permitted provided
6 * that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain copyright
9 * statements and notices. Redistributions must also contain a
10 * copy of this document.
11 *
12 * 2. Redistributions in binary form must reproduce the
13 * above copyright notice, this list of conditions and the
14 * following disclaimer in the documentation and/or other
15 * materials provided with the distribution.
16 *
17 * 3. The name "JDBM" must not be used to endorse or promote
18 * products derived from this Software without prior written
19 * permission of Cees de Groot. For written permission,
20 * please contact cg@cdegroot.com.
21 *
22 * 4. Products derived from this Software may not be called "JDBM"
23 * nor may "JDBM" appear in their names without prior written
24 * permission of Cees de Groot.
25 *
26 * 5. Due credit should be given to the JDBM Project
27 * (http://jdbm.sourceforge.net/).
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
30 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
31 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
33 * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
34 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
38 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
40 * OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
43 * Contributions are Copyright (C) 2000 by their associated contributors.
44 *
45 * $Id: PageManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
46 */
47
48 package jdbm.recman;
49
50 import java.io.*;
51
52 /**
53 * This class manages the linked lists of pages that make up a file.
54 */
55 final class PageManager {
56 // our record file
57 private RecordFile file;
58 // header data
59 private FileHeader header;
60 private BlockIo headerBuf;
61
62 /**
63 * Creates a new page manager using the indicated record file.
64 */
65 PageManager(RecordFile file) throws IOException {
66 this.file = file;
67
68 // check the file header. If the magic is 0, we assume a new
69 // file. Note that we hold on to the file header node.
70 headerBuf = file.get(0);
71 if (headerBuf.readShort(0) == 0)
72 header = new FileHeader(headerBuf, true);
73 else
74 header = new FileHeader(headerBuf, false);
75 }
76
77 /**
78 * Allocates a page of the indicated type. Returns recid of the
79 * page.
80 */
81 long allocate(short type) throws IOException {
82
83 if (type == Magic.FREE_PAGE)
84 throw new Error("allocate of free page?");
85
86 // do we have something on the free list?
87 long retval = header.getFirstOf(Magic.FREE_PAGE);
88 boolean isNew = false;
89 if (retval != 0) {
90 // yes. Point to it and make the next of that page the
91 // new first free page.
92 header.setFirstOf(Magic.FREE_PAGE, getNext(retval));
93 }
94 else {
95 // nope. make a new record
96 retval = header.getLastOf(Magic.FREE_PAGE);
97 if (retval == 0)
98 // very new file - allocate record #1
99 retval = 1;
100 header.setLastOf(Magic.FREE_PAGE, retval + 1);
101 isNew = true;
102 }
103
104 // Cool. We have a record, add it to the correct list
105 BlockIo buf = file.get(retval);
106 PageHeader pageHdr = isNew ? new PageHeader(buf, type)
107 : PageHeader.getView(buf);
108 long oldLast = header.getLastOf(type);
109
110 // Clean data.
111 System.arraycopy(RecordFile.cleanData, 0,
112 buf.getData(), 0,
113 RecordFile.BLOCK_SIZE);
114 pageHdr.setType(type);
115 pageHdr.setPrev(oldLast);
116 pageHdr.setNext(0);
117
118
119 if (oldLast == 0)
120 // This was the first one of this type
121 header.setFirstOf(type, retval);
122 header.setLastOf(type, retval);
123 file.release(retval, true);
124
125 // If there's a previous, fix up its pointer
126 if (oldLast != 0) {
127 buf = file.get(oldLast);
128 pageHdr = PageHeader.getView(buf);
129 pageHdr.setNext(retval);
130 file.release(oldLast, true);
131 }
132
133 // remove the view, we have modified the type.
134 buf.setView(null);
135
136 return retval;
137 }
138
139 /**
140 * Frees a page of the indicated type.
141 */
142 void free(short type, long recid) throws IOException {
143 if (type == Magic.FREE_PAGE)
144 throw new Error("free free page?");
145 if (recid == 0)
146 throw new Error("free header page?");
147
148 // get the page and read next and previous pointers
149 BlockIo buf = file.get(recid);
150 PageHeader pageHdr = PageHeader.getView(buf);
151 long prev = pageHdr.getPrev();
152 long next = pageHdr.getNext();
153
154 // put the page at the front of the free list.
155 pageHdr.setType(Magic.FREE_PAGE);
156 pageHdr.setNext(header.getFirstOf(Magic.FREE_PAGE));
157 pageHdr.setPrev(0);
158
159 header.setFirstOf(Magic.FREE_PAGE, recid);
160 file.release(recid, true);
161
162 // remove the page from its old list
163 if (prev != 0) {
164 buf = file.get(prev);
165 pageHdr = PageHeader.getView(buf);
166 pageHdr.setNext(next);
167 file.release(prev, true);
168 }
169 else {
170 header.setFirstOf(type, next);
171 }
172 if (next != 0) {
173 buf = file.get(next);
174 pageHdr = PageHeader.getView(buf);
175 pageHdr.setPrev(prev);
176 file.release(next, true);
177 }
178 else {
179 header.setLastOf(type, prev);
180 }
181
182 }
183
184
185 /**
186 * Returns the page following the indicated block
187 */
188 long getNext(long block) throws IOException {
189 try {
190 return PageHeader.getView(file.get(block)).getNext();
191 } finally {
192 file.release(block, false);
193 }
194 }
195
196 /**
197 * Returns the page before the indicated block
198 */
199 long getPrev(long block) throws IOException {
200 try {
201 return PageHeader.getView(file.get(block)).getPrev();
202 } finally {
203 file.release(block, false);
204 }
205 }
206
207 /**
208 * Returns the first page on the indicated list.
209 */
210 long getFirst(short type) throws IOException {
211 return header.getFirstOf(type);
212 }
213
214 /**
215 * Returns the last page on the indicated list.
216 */
217 long getLast(short type) throws IOException {
218 return header.getLastOf(type);
219 }
220
221
222 /**
223 * Commit all pending (in-memory) data by flushing the page manager.
224 * This forces a flush of all outstanding blocks (this it's an implicit
225 * {@link RecordFile#commit} as well).
226 */
227 void commit() throws IOException {
228 // write the header out
229 file.release(headerBuf);
230 file.commit();
231
232 // and obtain it again
233 headerBuf = file.get(0);
234 header = new FileHeader(headerBuf, false);
235 }
236
237 /**
238 * Flushes the page manager. This forces a flush of all outstanding
239 * blocks (this it's an implicit {@link RecordFile#commit} as well).
240 */
241 void rollback() throws IOException {
242 // release header
243 file.discard(headerBuf);
244 file.rollback();
245 // and obtain it again
246 headerBuf = file.get(0);
247 if (headerBuf.readShort(0) == 0)
248 header = new FileHeader(headerBuf, true);
249 else
250 header = new FileHeader(headerBuf, false);
251 }
252
253 /**
254 * Closes the page manager. This flushes the page manager and releases
255 * the lock on the header.
256 */
257 void close() throws IOException {
258 file.release(headerBuf);
259 file.commit();
260 headerBuf = null;
261 header = null;
262 file = null;
263 }
264
265 /**
266 * Returns the file header.
267 */
268 FileHeader getFileHeader() {
269 return header;
270 }
271
272 }