001: // kelondroNIOFileRA.java
002: // -----------------------
003: // part of The Kelondro Database
004: // (C) by Michael Peter Christen; mc@anomic.de
005: // first published on http://www.anomic.de
006: // Frankfurt, Germany, 2002
007: // last major change: 21.04.2004
008: //
009: // This program is free software; you can redistribute it and/or modify
010: // it under the terms of the GNU General Public License as published by
011: // the Free Software Foundation; either version 2 of the License, or
012: // (at your option) any later version.
013: //
014: // This program is distributed in the hope that it will be useful,
015: // but WITHOUT ANY WARRANTY; without even the implied warranty of
016: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: // GNU General Public License for more details.
018: //
019: // You should have received a copy of the GNU General Public License
020: // along with this program; if not, write to the Free Software
021: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: //
023: // Using this software in any meaning (reading, learning, copying, compiling,
024: // running) means that you agree that the Author(s) is (are) not responsible
025: // for cost, loss of data or any harm that may be caused directly or indirectly
026: // by usage of this softare or this documentation. The usage of this software
027: // is on your own risk. The installation and usage (starting/running) of this
028: // software may allow other people or application to access your computer and
029: // any attached devices and is highly dependent on the configuration of the
030: // software which must be done by the user of the software; the author(s) is
031: // (are) also not responsible for proper configuration and usage of the
032: // software, even if provoked by documentation provided together with
033: // the software.
034: //
035: // Any changes to this file according to the GPL as documented in the file
036: // gpl.txt aside this file in the shipment you received can be done to the
037: // lines that follows this copyright notice here, but changes must not be
038: // done inside the copyright notive above. A re-distribution must contain
039: // the intact and unchanged copyright notice.
040: // Contributions and changes to the program code must be marked as such.
041:
042: package de.anomic.kelondro;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.io.RandomAccessFile;
047: import java.nio.MappedByteBuffer;
048: import java.nio.channels.FileChannel;
049:
050: public class kelondroNIOFileRA extends kelondroAbstractRA implements
051: kelondroRA {
052:
053: protected final static long headSize = 1024;
054:
055: protected RandomAccessFile RAFile;
056: protected FileChannel RAChannel;
057: protected MappedByteBuffer bufferHead, bufferBody, bufferTail;
058: protected long seekPos;
059: protected long bodyOffset, tailOffset, tailCurrSize, tailMaxSize;
060: protected boolean mapBody;
061: protected boolean wroteHead, wroteBody, wroteTail;
062:
063: public kelondroNIOFileRA(String file, boolean mapBody,
064: long tailMaxSize) throws IOException {
065: this (new File(file), mapBody, tailMaxSize);
066: }
067:
068: public kelondroNIOFileRA(File file, boolean mapBody,
069: long tailMaxSize) throws IOException {
070: this .name = file.getName();
071: this .seekPos = 0;
072: this .bodyOffset = headSize;
073: if (bodyOffset >= file.length()) {
074: bodyOffset = file.length();
075: mapBody = false;
076: }
077: this .tailOffset = file.length();
078: this .tailMaxSize = tailMaxSize;
079: this .tailCurrSize = 0;
080: this .mapBody = mapBody;
081: this .RAFile = new RandomAccessFile(file, "rw");
082: this .RAChannel = RAFile.getChannel();
083: this .bufferHead = RAChannel.map(FileChannel.MapMode.READ_WRITE,
084: 0, (int) bodyOffset);
085: if (mapBody)
086: this .bufferBody = RAChannel.map(
087: FileChannel.MapMode.READ_WRITE, bodyOffset,
088: (int) (tailOffset - bodyOffset));
089: else
090: this .bufferBody = null;
091: this .bufferTail = null;
092: this .wroteHead = false;
093: this .wroteBody = false;
094: this .wroteTail = false;
095: System.out.println("initialized " + name + " mapBody = "
096: + ((mapBody) ? "true" : "false") + ", bodyOffset = "
097: + bodyOffset + ", tailOffset = " + tailOffset);
098: }
099:
100: private boolean growTail(long newPos) throws IOException {
101: if (newPos >= tailMaxSize) {
102: System.out.println("cannot grow " + name);
103: return false;
104: }
105: if (tailCurrSize == 0) {
106: // first grow
107: this .tailCurrSize = newPos;
108: if (tailCurrSize < 1024)
109: tailCurrSize = 1024;
110: if (tailCurrSize > tailMaxSize)
111: tailCurrSize = tailMaxSize;
112: } else {
113: // next grow
114: tailCurrSize = tailCurrSize * 2;
115: if (tailCurrSize < newPos)
116: tailCurrSize = newPos;
117: if (tailCurrSize > tailMaxSize)
118: tailCurrSize = tailMaxSize;
119: bufferTail.force();
120: }
121: System.out.println("growing " + name + " nextSize="
122: + tailCurrSize);
123: bufferTail = RAChannel.map(FileChannel.MapMode.READ_WRITE,
124: tailOffset, (int) tailCurrSize);
125: wroteTail = false;
126: return true;
127: }
128:
129: public long length() throws IOException {
130: return RAFile.length();
131: }
132:
133: public long available() throws IOException {
134: return RAFile.length() - RAFile.getFilePointer();
135: }
136:
137: // pseudo-native method read
138: public int read() throws IOException {
139: int r;
140: if (seekPos < bodyOffset) {
141: r = 0xFF & ((int) bufferHead.get((int) seekPos));
142: } else if (seekPos < tailOffset) {
143: if (mapBody) {
144: r = 0xFF & ((int) bufferBody
145: .get((int) (seekPos - bodyOffset)));
146: } else {
147: RAFile.seek(seekPos);
148: r = RAFile.read();
149: }
150: } else if (seekPos < (tailOffset + tailCurrSize)) {
151: r = 0xFF & ((int) bufferTail
152: .get((int) (seekPos - tailOffset)));
153: } else {
154: r = -1;
155: while (growTail(seekPos)) {
156: if (seekPos < (tailOffset + tailCurrSize)) {
157: r = 0xFF & ((int) bufferTail
158: .get((int) (seekPos - tailOffset)));
159: break;
160: } else {
161: RAFile.seek(seekPos);
162: r = RAFile.read();
163: break;
164: }
165: }
166: }
167: seekPos++;
168: return r;
169: }
170:
171: // pseudo-native method write
172: public void write(int b) throws IOException {
173: if (seekPos < bodyOffset) {
174: bufferHead.put((int) seekPos, (byte) (b & 0xff));
175: wroteHead = true;
176: } else if (seekPos < tailOffset) {
177: if (mapBody) {
178: bufferBody.put((int) (seekPos - bodyOffset),
179: (byte) (b & 0xff));
180: wroteBody = true;
181: } else {
182: RAFile.seek(seekPos);
183: RAFile.write(b);
184: }
185: } else if (seekPos < (tailOffset + tailCurrSize)) {
186: bufferTail.put((int) (seekPos - tailOffset),
187: (byte) (b & 0xff));
188: wroteTail = true;
189: } else {
190: while (growTail(seekPos)) {
191: if (seekPos < (tailOffset + tailCurrSize)) {
192: bufferTail.put((int) (seekPos - tailOffset),
193: (byte) (b & 0xff));
194: wroteTail = true;
195: break;
196: } else {
197: RAFile.seek(seekPos);
198: RAFile.write(b);
199: break;
200: }
201: }
202: }
203: seekPos++;
204: }
205:
206: public int read(byte[] b, int off, int len) throws IOException {
207: for (int i = 0; i < len; i++) {
208: b[off + i] = (byte) read();
209: }
210: return len;
211: }
212:
213: public void write(byte[] b, int off, int len) throws IOException {
214: for (int i = 0; i < len; i++) {
215: write(b[off + i]);
216: }
217: }
218:
219: public void seek(long pos) throws IOException {
220: seekPos = pos;
221: }
222:
223: public void close() throws IOException {
224: if (wroteHead) {
225: bufferHead.force();
226: System.out.println("wrote " + name + " head");
227: }
228: if ((wroteBody) && (mapBody)) {
229: bufferBody.force();
230: System.out.println("wrote " + name + " body");
231: }
232: if (wroteTail) {
233: bufferTail.force();
234: System.out.println("wrote " + name + " tail");
235: }
236: RAChannel.close();
237: RAChannel = null;
238: RAFile.close();
239: RAFile = null;
240: }
241:
242: protected void finalize() throws Throwable {
243: if (RAChannel != null) {
244: try {
245: RAChannel.close();
246: } catch (Exception e) {
247: }
248: }
249: if (RAFile != null) {
250: try {
251: RAFile.close();
252: } catch (Exception e) {
253: }
254: }
255: }
256:
257: public static void test1(kelondroRA ra) throws IOException {
258: for (int i = 0; i < 2048; i++) {
259: ra.seek(i);
260: ra.write(32);
261: }
262: }
263:
264: public static void main(String[] args) {
265: // tests...
266: File f = new File("/yacy/nio.test.txt");
267: if (f.exists())
268: f.delete();
269:
270: System.out.println("* fill with blanks");
271: try {
272: kelondroRA ra = new kelondroNIOFileRA(f, true, 2046);
273: test1(ra);
274: ra.close();
275: } catch (IOException e) {
276: e.printStackTrace();
277: }
278:
279: System.out.println("* write in at head");
280: try {
281: kelondroRA ra = new kelondroNIOFileRA(f, true, 10);
282: ra.seek(8);
283: ra.write((byte) 'h');
284: ra.close();
285: } catch (IOException e) {
286: e.printStackTrace();
287: }
288:
289: System.out.println("* write in at body");
290: try {
291: kelondroRA ra = new kelondroNIOFileRA(f, true, 10);
292: ra.seek(1024);
293: ra.write((byte) 'b');
294: ra.close();
295: } catch (IOException e) {
296: e.printStackTrace();
297: }
298:
299: System.out.println("* write in at tail");
300: try {
301: kelondroRA ra = new kelondroNIOFileRA(f, true, 10);
302: ra.seek(2048);
303: ra.write((byte) 't');
304: ra.close();
305: } catch (IOException e) {
306: e.printStackTrace();
307: }
308:
309: System.out.println("* write in behind tail");
310: try {
311: kelondroRA ra = new kelondroNIOFileRA(f, true, 10);
312: ra.seek(2059);
313: ra.write((byte) 'x');
314: ra.close();
315: } catch (IOException e) {
316: e.printStackTrace();
317: }
318: }
319:
320: }
|