/* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to <http://unlicense.org/> */ package jxtn.core.unix; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Method; import java.nio.channels.FileChannel; import java.nio.file.Path; import sun.nio.ch.FileChannelImpl; /** * Direct mmap() support for over 4GB mapping * <p> * source: http://nyeggen.com/post/2014-05-18-memory-mapping-%3E2gb-of-data-in-java/ * </p> * <p> * TODO: investigate associated concerns by fixed memory mapping, cleanup code, fixes etc in Java source. * </p> * * @author aqd */ public final class FileMapping { private static final Method channelMap0; private static final Method channelUnmap0; static { try { channelMap0 = FileChannelImpl.class.getDeclaredMethod("map0", int.class, long.class, long.class); channelMap0.setAccessible(true); channelUnmap0 = FileChannelImpl.class.getDeclaredMethod("unmap0", long.class, long.class); channelUnmap0.setAccessible(true); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } public static NativeBuffer create(FileChannel channel, long length, boolean canWrite) throws IOException { int imode = canWrite ? 1 : 0; long address; try { address = (long) channelMap0.invoke(channel, imode, 0L, roundTo4096(length)); } catch (ReflectiveOperationException e) { throw new IOException(e.getCause()); } FileMappingFromChannel source = new FileMappingFromChannel(channel, address, length); return wrap(source); } public static NativeBuffer create(Path path, int prot, int flags) throws IOException { int rw = 0; if ((prot & NativeMMap.PROT_READ) != 0 || (prot & NativeMMap.PROT_EXEC) != 0) { if ((prot & NativeMMap.PROT_WRITE) != 0) { rw = NativeFiles.O_RDWR; } else { rw = NativeFiles.O_RDONLY; } } else if ((prot & NativeMMap.PROT_WRITE) != 0) { rw = NativeFiles.O_WRONLY; } int fd = NativeFiles.open(path, rw, 0); if (fd == -1) { throw new IOException(NativeErrno.errName()); } long address, length; try { length = NativeFiles.lseek(fd, 0, NativeFiles.SEEK_END); if (length == -1L) { throw new IOException(NativeErrno.errName()); } address = NativeMMap.mmap(0L, length, prot, flags, fd, 0L); if (address == NativeMMap.MAP_FAILED) { throw new IOException(NativeErrno.errName()); } } finally { NativeIO.close(fd); } FileMappingFromUnix source = new FileMappingFromUnix(path, address, length); return wrap(source); } public static NativeBuffer create(int fd, int prot, int flags) throws IOException { long length = NativeFiles.lseek(fd, 0, NativeFiles.SEEK_END); if (length == -1L) { throw new IOException(NativeErrno.errName()); } return create(fd, 0L, length, prot, flags); } public static NativeBuffer create(int fd, long offset, long length, int prot, int flags) throws IOException { String name = "fd=" + fd; long address = NativeMMap.mmap(0L, length, prot, flags, fd, offset); if (address == NativeMMap.MAP_FAILED) { throw new IOException(NativeErrno.errName()); } FileMappingFromUnix source = new FileMappingFromUnix(name, address, length); return wrap(source); } public static NativeBuffer tryCreate(int fd, int prot, int flags) { long length = NativeFiles.lseek(fd, 0, NativeFiles.SEEK_END); if (length == -1L) { return null; } return tryCreate(fd, 0L, length, prot, flags); } public static NativeBuffer tryCreate(Path path, int prot, int flags) { int rw = 0; if ((prot & NativeMMap.PROT_READ) != 0 || (prot & NativeMMap.PROT_EXEC) != 0) { if ((prot & NativeMMap.PROT_WRITE) != 0) { rw = NativeFiles.O_RDWR; } else { rw = NativeFiles.O_RDONLY; } } else if ((prot & NativeMMap.PROT_WRITE) != 0) { rw = NativeFiles.O_WRONLY; } int fd = NativeFiles.open(path, rw, 0); if (fd == -1) { return null; } long address, length; try { length = NativeFiles.lseek(fd, 0, NativeFiles.SEEK_END); if (length == -1L) { return null; } address = NativeMMap.mmap(0L, length, prot, flags, fd, 0L); if (address == NativeMMap.MAP_FAILED) { return null; } } finally { NativeIO.close(fd); } FileMappingFromUnix source = new FileMappingFromUnix(path, address, length); return wrap(source); } public static NativeBuffer tryCreate(int fd, long offset, long length, int prot, int flags) { String name = "fd=" + fd; long address = NativeMMap.mmap(0L, length, prot, flags, fd, offset); if (address == NativeMMap.MAP_FAILED) { return null; } FileMappingFromUnix source = new FileMappingFromUnix(name, address, length); return wrap(source); } private static NativeBuffer wrap(FileMappingSource source) { return new NativeBuffer(source, source.address, source.length); } private static long roundTo4096(long i) { return (i + 0xfffL) & ~0xfffL; } private static abstract class FileMappingSource implements Closeable { protected final long address; protected final long length; protected FileMappingSource(long address, long length) { this.address = address; this.length = length; } @Override public abstract void close(); @Override protected void finalize() throws Throwable { this.close(); super.finalize(); } } private static final class FileMappingFromChannel extends FileMappingSource { private final String name; private boolean closed; public FileMappingFromChannel(FileChannel channel, long address, long length) { super(address, length); this.name = Channels.toString(channel); } @Override public void close() { if (this.closed) { return; } try { channelUnmap0.invoke(null, this.address, roundTo4096(this.length)); } catch (ReflectiveOperationException e) { e.printStackTrace(); } finally { this.closed = true; } } @Override public String toString() { return "mmap_" + this.name; } } private static final class FileMappingFromUnix extends FileMappingSource { private final Object source; private boolean closed; public FileMappingFromUnix(Object source, long address, long length) { super(address, length); this.source = source; } @Override public void close() { if (this.closed) { return; } try { if (NativeMMap.munmap(this.address, this.length) == -1) { System.err.println("munmap " + this.source + ": " + NativeErrno.errName()); } } finally { this.closed = true; } } @Override public String toString() { return "mmap_" + this.source; } } private FileMapping() { } }