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 sun.misc.Unsafe;
020
021import java.lang.reflect.Field;
022import java.util.concurrent.atomic.AtomicBoolean;
023
024/**
025 * Implementation of {@link OffHeapMemory} using {@code sun.misc.Unsafe}
026 *
027 * @author alexkasko
028 * Date: 1/14/13
029 */
030class UnsafeOffHeapMemory extends OffHeapMemory {
031
032    private static final Unsafe UNSAFE;
033    private static final int BYTE_ARRAY_OFFSET;
034
035    static {
036        try {
037            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
038            theUnsafe.setAccessible(true);
039            UNSAFE = (Unsafe) theUnsafe.get(null);
040            int boo = UNSAFE.arrayBaseOffset(byte[].class);
041            // It seems not all Unsafe implementations implement the following method.
042            UNSAFE.copyMemory(new byte[1], boo, new byte[1], boo, 1);
043            BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
044        } catch (Exception e) {
045            throw new RuntimeException(e);
046        }
047    }
048
049    private final long address;
050    private final long length;
051    private final AtomicBoolean disposed = new AtomicBoolean(false);
052
053    UnsafeOffHeapMemory(long bytes) {
054        this.address = UNSAFE.allocateMemory(bytes);
055        this.length = bytes;
056    }
057
058    /**
059     * {@inheritDoc}
060     */
061    @Override
062    public boolean isUnsafe() {
063        return true;
064    }
065
066    /**
067     * {@inheritDoc}
068     */
069    @Override
070    public long length() {
071        return length;
072    }
073
074    /**
075     * {@inheritDoc}
076     */
077    @Override
078    public void free() {
079        if(!disposed.compareAndSet(false, true)) return;
080        UNSAFE.freeMemory(address);
081    }
082
083    /**
084     * {@inheritDoc}
085     */
086    @Override
087    protected void finalize() throws Throwable {
088        free();
089    }
090
091    /**
092     * {@inheritDoc}
093     */
094    @Override
095    public void put(long offset, byte[] buffer, int bufferOffset, int bytes) {
096        assert !disposed.get() : "disposed";
097        assert offset >= 0 : offset;
098        assert offset <= length - bytes : offset;
099        assert bufferOffset >= 0 : bufferOffset;
100        assert bytes > 0 : bytes;
101        assert bytes <= length : bytes;
102        assert null != buffer;
103        assert bufferOffset <= buffer.length - bytes : bufferOffset;
104        UNSAFE.copyMemory(buffer, BYTE_ARRAY_OFFSET + bufferOffset, null, address + offset, bytes);
105    }
106
107    /**
108     * {@inheritDoc}
109     */
110    @Override
111    public void put(long offset, byte[] buffer) {
112        assert !disposed.get() : "disposed";
113        assert offset >= 0 : offset;
114        assert null != buffer;
115        assert offset <= length - buffer.length : offset;
116        assert buffer.length <= length : buffer.length;
117        UNSAFE.copyMemory(buffer, BYTE_ARRAY_OFFSET, null, address + offset, buffer.length);
118    }
119
120    /**
121     * {@inheritDoc}
122     */
123    @Override
124    public void get(long offset, byte[] buffer, int bufferOffset, int bytes) {
125        assert !disposed.get() : "disposed";
126        assert offset >= 0 : offset;
127        assert offset <= length - bytes : offset;
128        assert bufferOffset >= 0 : bufferOffset;
129        assert bytes > 0 : bytes;
130        assert bytes <= length : bytes;
131        assert null != buffer;
132        assert bufferOffset <= buffer.length - bytes : bufferOffset;
133        UNSAFE.copyMemory(null, address + offset, buffer, BYTE_ARRAY_OFFSET + bufferOffset, bytes);
134    }
135
136    /**
137     * {@inheritDoc}
138     */
139    @Override
140    public void get(long offset, byte[] buffer) {
141        assert !disposed.get() : "disposed";
142        assert offset >= 0 : offset;
143        assert null != buffer;
144        assert offset <= length - buffer.length : offset;
145        assert buffer.length <= length : buffer.length;
146        UNSAFE.copyMemory(null, address + offset, buffer, BYTE_ARRAY_OFFSET, buffer.length);
147    }
148
149    /**
150     * {@inheritDoc}
151     */
152    @Override
153    public byte getByte(long offset) {
154        assert !disposed.get() : "disposed";
155        assert offset >= 0 : offset;
156        assert offset <= length - 1 : offset;
157        return UNSAFE.getByte(address + offset);
158    }
159
160    /**
161     * {@inheritDoc}
162     */
163    @Override
164    public void putByte(long offset, byte value) {
165        assert !disposed.get() : "disposed";
166        assert offset >= 0 : offset;
167        assert offset <= length - 1 : offset;
168        UNSAFE.putByte(address + offset, value);
169    }
170
171    /**
172     * {@inheritDoc}
173     */
174    @Override
175    public short getUnsignedByte(long offset) {
176        assert !disposed.get() : "disposed";
177        assert offset >= 0 : offset;
178        assert offset <= length - 1 : offset;
179        return (short) (UNSAFE.getByte(address + offset) & 0xff);
180    }
181
182    /**
183     * {@inheritDoc}
184     */
185    @Override
186    public void putUnsignedByte(long offset, short value) {
187        assert !disposed.get() : "disposed";
188        assert offset >= 0 : offset;
189        assert offset <= length - 1 : offset;
190        assert value >= 0 : value;
191        assert value < 1<<8 : value;
192        UNSAFE.putByte(address + offset, (byte) value);
193    }
194
195    /**
196     * {@inheritDoc}
197     */
198    @Override
199    public short getShort(long offset) {
200        assert !disposed.get() : "disposed";
201        assert offset >= 0 : offset;
202        assert offset <= length - 2 : offset;
203        return UNSAFE.getShort(address + offset);
204    }
205
206    /**
207     * {@inheritDoc}
208     */
209    @Override
210    public void putShort(long offset, short value) {
211        assert !disposed.get() : "disposed";
212        assert offset >= 0 : offset;
213        assert offset <= length - 2 : offset;
214        UNSAFE.putShort(address + offset, value);
215    }
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public int getUnsignedShort(long offset) {
222        assert !disposed.get() : "disposed";
223        assert offset >= 0 : offset;
224        assert offset <= length - 2 : offset;
225        return UNSAFE.getShort(address + offset) & 0xffff;
226    }
227
228    /**
229     * {@inheritDoc}
230     */
231    @Override
232    public void putUnsignedShort(long offset, int value) {
233        assert !disposed.get() : "disposed";
234        assert offset >= 0 : offset;
235        assert offset <= length - 2 : offset;
236        assert value >= 0 : value;
237        assert value < 1<<16 : value;
238        UNSAFE.putShort(address + offset, (short) value);
239    }
240
241    /**
242     * {@inheritDoc}
243     */
244    @Override
245    public int getInt(long offset) {
246        assert !disposed.get() : "disposed";
247        assert offset >= 0 : offset;
248        assert offset <= length - 4 : offset;
249        return UNSAFE.getInt(address + offset);
250    }
251
252    /**
253     * {@inheritDoc}
254     */
255    @Override
256    public void putInt(long offset, int value) {
257        assert !disposed.get() : "disposed";
258        assert offset >= 0 : offset;
259        assert offset <= length - 4 : offset;
260        UNSAFE.putInt(address + offset, value);
261    }
262
263    /**
264     * {@inheritDoc}
265     */
266    @Override
267    public long getUnsignedInt(long offset) {
268        assert !disposed.get() : "disposed";
269        assert offset >= 0 : offset;
270        assert offset <= length - 4 : offset;
271        return UNSAFE.getInt(address + offset) & 0xffffffffL;
272    }
273
274    /**
275     * {@inheritDoc}
276     */
277    @Override
278    public void putUnsignedInt(long offset, long value) {
279        assert !disposed.get() : "disposed";
280        assert offset >= 0 : offset;
281        assert offset <= length - 4 : offset;
282        assert value >= 0 : value;
283        assert value < 1L<<32 : value;
284        UNSAFE.putInt(address + offset, (int) value);
285    }
286
287    /**
288     * {@inheritDoc}
289     */
290    @Override
291    public long getLong(long offset) {
292        assert !disposed.get() : "disposed";
293        assert offset >= 0 : offset;
294        assert offset <= length - 8 : offset;
295        return UNSAFE.getLong(address + offset);
296    }
297
298    /**
299     * {@inheritDoc}
300     */
301    @Override
302    public void putLong(long offset, long value) {
303        assert !disposed.get() : "disposed";
304        assert offset >= 0 : offset;
305        assert offset <= length - 8 : offset;
306        UNSAFE.putLong(address + offset, value);
307    }
308
309    /**
310     * {@inheritDoc}
311     */
312    @Override
313    public void copy(long offset, OffHeapMemory destination, long destOffset, long bytes) {
314        assert destination instanceof UnsafeOffHeapMemory : destination;
315        UnsafeOffHeapMemory dest = (UnsafeOffHeapMemory) destination;
316        assert !disposed.get() : "disposed";
317        assert !dest.disposed.get() : "disposed";
318        assert offset >= 0 : offset;
319        assert offset <= length - bytes : offset;
320        assert destOffset >= 0 : destOffset;
321        assert destOffset <= destination.length() - bytes :  destOffset;
322        UNSAFE.copyMemory(address + offset, dest.address + destOffset, bytes);
323    }
324
325    /**
326     * {@inheritDoc}
327     */
328    @Override
329    public OffHeapMemory clone() {
330        assert !disposed.get() : "disposed";
331        UnsafeOffHeapMemory res = new UnsafeOffHeapMemory(length);
332        UNSAFE.copyMemory(address, res.address, length);
333        return res;
334    }
335
336    /**
337     * {@inheritDoc}
338     */
339    @Override
340    public String toString() {
341        final StringBuilder sb = new StringBuilder();
342        sb.append("UnsafeOffHeapMemory");
343        sb.append("{address=").append(address);
344        sb.append(", length=").append(length);
345        sb.append(", disposed=").append(disposed);
346        sb.append('}');
347        return sb.toString();
348    }
349}