1 package com.github.davidmoten.rx2.internal.flowable.buffertofile;
2
3 import java.io.File;
4 import java.io.RandomAccessFile;
5 import java.lang.reflect.InvocationTargetException;
6 import java.lang.reflect.Method;
7 import java.nio.channels.FileChannel;
8
9 import com.github.davidmoten.guavamini.annotations.VisibleForTesting;
10
11 import sun.misc.Unsafe;
12 import sun.nio.ch.FileChannelImpl;
13
14 @SuppressWarnings("restriction")
15 public final class MemoryMappedFile {
16
17 private static final Unsafe unsafe;
18 private static final Method mmap;
19 private static final Method unmmap;
20 private static final int BYTE_ARRAY_OFFSET;
21
22 private final File file;
23 private final long size;
24 private long addr;
25
26 static {
27 unsafe = UnsafeAccess.unsafe();
28 mmap = getMethod(FileChannelImpl.class, "map0", int.class, long.class, long.class);
29 unmmap = getMethod(FileChannelImpl.class, "unmap0", long.class, long.class);
30 BYTE_ARRAY_OFFSET = unsafe.arrayBaseOffset(byte[].class);
31 }
32
33 public MemoryMappedFile(File file, long len) {
34 this.file = file;
35 this.size = roundTo4096(len);
36 mapAndSetOffset();
37 }
38
39
40 @VisibleForTesting
41 static Method getMethod(Class<?> cls, String name, Class<?>... params) {
42 Method m;
43 try {
44 m = cls.getDeclaredMethod(name, params);
45 } catch (Exception e) {
46 throw new RuntimeException(e);
47 }
48 m.setAccessible(true);
49 return m;
50 }
51
52
53 private static long roundTo4096(long i) {
54 return (i + 0xfffL) & ~0xfffL;
55 }
56
57
58
59 private void mapAndSetOffset() {
60 try {
61 final RandomAccessFile backingFile = new RandomAccessFile(this.file, "rw");
62 backingFile.setLength(this.size);
63 final FileChannel ch = backingFile.getChannel();
64 this.addr = (Long) mmap.invoke(ch, 1, 0L, this.size);
65
66 ch.close();
67 backingFile.close();
68 } catch (Exception e) {
69 throw new RuntimeException(e);
70 }
71 }
72
73
74
75
76
77
78
79
80
81 public void close() {
82 try {
83 unmmap.invoke(null, addr, this.size);
84 if (!file.delete()) {
85 throw new RuntimeException("could not delete " + file);
86 }
87 } catch (IllegalAccessException e) {
88 throw new RuntimeException(e);
89 } catch (IllegalArgumentException e) {
90 throw new RuntimeException(e);
91 } catch (InvocationTargetException e) {
92 throw new RuntimeException(e);
93 }
94 }
95
96 public int getInt(long pos) {
97 return unsafe.getInt(pos + addr);
98 }
99
100 public void putByte(long pos, byte val) {
101 unsafe.putByte(pos + addr, val);
102 }
103
104 public void putInt(long pos, int val) {
105 unsafe.putInt(pos + addr, val);
106 }
107
108 public void putOrderedInt(long pos, int val) {
109 unsafe.putOrderedInt(null, pos + addr, val);
110 }
111
112 public int getIntVolatile(long pos) {
113 return unsafe.getIntVolatile(null, pos + addr);
114 }
115
116
117 public void getBytes(long pos, byte[] data, long offset, long length) {
118 unsafe.copyMemory(null, pos + addr, data, BYTE_ARRAY_OFFSET + offset, length);
119 }
120
121 public void putBytes(long pos, byte[] data, long offset, long length) {
122 unsafe.copyMemory(data, BYTE_ARRAY_OFFSET + offset, null, pos + addr, length);
123 }
124
125 public byte getByte(long pos) {
126 return unsafe.getByte(pos + addr);
127 }
128
129 }