001/*
002 * Copyright 2013 Alex Kasko (alexkasko.com)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.alexkasko.unsafe.offheap;
018
019import java.lang.reflect.Method;
020import java.nio.ByteBuffer;
021import java.util.concurrent.atomic.AtomicBoolean;
022
023import static java.nio.ByteOrder.LITTLE_ENDIAN;
024
025/**
026 * Implementation of {@link OffHeapMemory} using {@link ByteBuffer#allocateDirect(int)}
027 *
028 * @author alexkasko
029 * Date: 1/14/13
030*/
031class DirectOffHeapMemory extends OffHeapMemory {
032
033    private final ByteBuffer bb;
034    private final long length;
035    private Object cleaner;
036    private Method clean;
037    private final AtomicBoolean disposed = new AtomicBoolean(false);
038
039    DirectOffHeapMemory(long bytes) {
040        if(bytes > Integer.MAX_VALUE) throw new IllegalArgumentException(
041                "Long-sized allocations are not supported by [" + getClass().getName() + "]");
042        this.length = bytes;
043        this.bb = ByteBuffer.allocateDirect((int) bytes).order(LITTLE_ENDIAN);
044        // http://stackoverflow.com/a/8191493/314015
045        try {
046            setupOpenJdkCleaner();
047        } catch (Exception e) {
048            try {
049                setupAndroidCleaner();
050            } catch (Exception e1) {
051                e.printStackTrace();
052                e1.printStackTrace();
053                throw new RuntimeException(e);
054            }
055        }
056    }
057
058    private void setupOpenJdkCleaner() throws Exception {
059        Method cleanerMethod = bb.getClass().getMethod("cleaner");
060        cleanerMethod.setAccessible(true);
061        this.cleaner = cleanerMethod.invoke(bb);
062        this.clean = cleaner.getClass().getMethod("clean");
063        this.clean.setAccessible(true);
064    }
065
066    private void setupAndroidCleaner() throws Exception {
067        this.clean = bb.getClass().getMethod("free");
068        this.clean.setAccessible(true);
069        this.cleaner = bb;
070    }
071
072    /**
073     * {@inheritDoc}
074     */
075    @Override
076    public boolean isUnsafe() {
077        return false;
078    }
079
080    /**
081     * {@inheritDoc}
082     */
083    @Override
084    public long length() {
085        return length;
086    }
087
088    /**
089     * {@inheritDoc}
090     */
091    @Override
092    public void free() {
093        if(!disposed.compareAndSet(false, true)) return;
094        try {
095            clean.invoke(cleaner);
096        } catch (Throwable e) {
097            e.printStackTrace();
098        }
099    }
100
101    /**
102     * {@inheritDoc}
103     */
104    @Override
105    public void put(long offset, byte[] buffer, int bufferOffset, int bytes) {
106        bb.clear().position((int) offset);
107        bb.put(buffer, bufferOffset, bytes);
108    }
109
110    /**
111     * {@inheritDoc}
112     */
113    @Override
114    public void put(long offset, byte[] buffer) {
115        bb.clear().position((int) offset);
116        bb.put(buffer);
117    }
118
119    /**
120     * {@inheritDoc}
121     */
122    @Override
123    public void get(long offset, byte[] buffer, int bufferOffset, int bytes) {
124        bb.clear().position((int) offset);
125        bb.get(buffer, bufferOffset, bytes);
126    }
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public void get(long offset, byte[] buffer) {
133        bb.clear().position((int) offset);
134        bb.get(buffer);
135    }
136
137    /**
138     * {@inheritDoc}
139     */
140    @Override
141    public byte getByte(long offset) {
142        return bb.get((int) offset);
143    }
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public void putByte(long offset, byte value) {
150        bb.put((int) offset, value);
151    }
152
153    /**
154     * {@inheritDoc}
155     */
156    @Override
157    public short getUnsignedByte(long offset) {
158        return (short) (bb.get((int) offset) & 0xff);
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    public void putUnsignedByte(long offset, short value) {
166        bb.put((int) offset, (byte) value);
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public short getShort(long offset) {
174        return bb.getShort((int) offset);
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    @Override
181    public void putShort(long offset, short value) {
182        bb.putShort((int) offset, value);
183    }
184
185    /**
186     * {@inheritDoc}
187     */
188    @Override
189    public int getUnsignedShort(long offset) {
190        return bb.getShort((int) offset) & 0xffff;
191    }
192
193    /**
194     * {@inheritDoc}
195     */
196    @Override
197    public void putUnsignedShort(long offset, int value) {
198        bb.putShort((int) offset, (short) value);
199    }
200
201    /**
202     * {@inheritDoc}
203     */
204    @Override
205    public int getInt(long offset) {
206        return bb.getInt((int) offset);
207    }
208
209    /**
210     * {@inheritDoc}
211     */
212    @Override
213    public void putInt(long offset, int value) {
214        bb.putInt((int) offset, value);
215    }
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public long getUnsignedInt(long offset) {
222        return bb.getInt((int) offset) & 0xffffffffL;
223    }
224
225    /**
226     * {@inheritDoc}
227     */
228    @Override
229    public void putUnsignedInt(long offset, long value) {
230        bb.putInt((int) offset, (int) value);
231    }
232
233    /**
234     * {@inheritDoc}
235     */
236    @Override
237    public long getLong(long offset) {
238        return bb.getLong((int) offset);
239    }
240
241    /**
242     * {@inheritDoc}
243     */
244    @Override
245    public void putLong(long offset, long value) {
246        bb.putLong((int) offset, value);
247    }
248
249    /**
250     * {@inheritDoc}
251     */
252    @Override
253    public void copy(long offset, OffHeapMemory destination, long destOffset, long bytes) {
254        DirectOffHeapMemory dest = (DirectOffHeapMemory) destination;
255        bb.clear().position((int) offset);
256        dest.bb.clear().position((int) destOffset);
257        dest.bb.put(bb);
258    }
259
260    /**
261     * {@inheritDoc}
262     */
263    @Override
264    public OffHeapMemory clone() {
265        DirectOffHeapMemory res = new DirectOffHeapMemory(length);
266        copy(0, res, 0, length);
267        return res;
268    }
269
270    /**
271     * {@inheritDoc}
272     */
273    @Override
274    public String toString() {
275        final StringBuilder sb = new StringBuilder();
276        sb.append("DirectOffHeapMemory");
277        sb.append("{bb=").append(bb);
278        sb.append(", length=").append(length);
279        sb.append(", cleaner=").append(cleaner);
280        sb.append(", clean=").append(clean);
281        sb.append(", disposed=").append(disposed);
282        sb.append('}');
283        return sb.toString();
284    }
285}