com.sun.jna.Memory Maven / Gradle / Ivy
/*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.ArrayList;
/**
* A Pointer
to memory obtained from the native heap via a
* call to malloc
.
*
* In some cases it might be necessary to use memory obtained from
* malloc
. For example, Memory
helps
* accomplish the following idiom:
*
* void *buf = malloc(BUF_LEN * sizeof(char));
* call_some_function(buf);
* free(buf);
*
*
* The {@link #finalize} method will free allocated memory when
* this object is no longer referenced.
*
* @author Sheng Liang, originator
* @author Todd Fast, suitability modifications
* @author Timothy Wall
* @see Pointer
*/
public class Memory extends Pointer {
private static ReferenceQueue QUEUE = new ReferenceQueue();
private static LinkedReference HEAD; // the head of the doubly linked list used for instance tracking
/**
* Keep track of all allocated memory so we can dispose of it before
* unloading. This is done using a doubly linked list to enable fast
* removal of tracked instances.
*/
private static class LinkedReference extends WeakReference {
private LinkedReference next;
private LinkedReference prev;
private LinkedReference(Memory referent) {
super(referent, QUEUE);
}
/**
* Add the given {@code instance} to the instance tracking.
*
* @param instance the instance to track
*/
static LinkedReference track(Memory instance) {
// use a different lock here to allow the finialzier to unlink elements too
synchronized (QUEUE) {
LinkedReference stale;
// handle stale references here to avoid GC overheating when memory is limited
while ((stale = (LinkedReference) QUEUE.poll()) != null) {
stale.unlink();
}
}
// keep object allocation outside the syncronized block
LinkedReference entry = new LinkedReference(instance);
synchronized (LinkedReference.class) {
if (HEAD != null) {
entry.next = HEAD;
HEAD = HEAD.prev = entry;
} else {
HEAD = entry;
}
}
return entry;
}
/**
* Remove the related instance from tracking and update the linked list.
*/
private void unlink() {
synchronized (LinkedReference.class) {
LinkedReference next;
if (HEAD != this) {
if (this.prev == null) {
// this entry was detached before, e.g. disposeAll was called and finalizers are running now
return;
}
next = this.prev.next = this.next;
} else {
next = HEAD = HEAD.next;
}
if (next != null) {
next.prev = this.prev;
}
// set prev to null to detect detached entries
this.prev = null;
}
}
}
private static final WeakMemoryHolder buffers = new WeakMemoryHolder();
/** Force cleanup of memory that has associated NIO Buffers which have
been GC'd.
*/
public static void purge() {
buffers.clean();
}
/** Dispose of all allocated memory. */
public static void disposeAll() {
synchronized (LinkedReference.class) {
LinkedReference entry;
while ((entry = HEAD) != null) {
Memory memory = HEAD.get();
if (memory != null) {
// dispose does the unlink call internal
memory.dispose();
} else {
HEAD.unlink();
}
if (HEAD == entry) {
throw new IllegalStateException("the HEAD did not change");
}
}
}
synchronized (QUEUE) {
LinkedReference stale;
// try to release as mutch memory as possible
while ((stale = (LinkedReference) QUEUE.poll()) != null) {
stale.unlink();
}
}
}
/**
* Unit-testing only, ensure the doubly linked list is in a good shape.
*
* @return the number of tracked instances
*/
static int integrityCheck() {
synchronized (LinkedReference.class) {
if (HEAD == null) {
return 0;
}
ArrayList entries = new ArrayList();
LinkedReference entry = HEAD;
while (entry != null) {
entries.add(entry);
entry = entry.next;
}
int index = entries.size() - 1;
entry = entries.get(index);
while (entry != null) {
if (entries.get(index) != entry) {
throw new IllegalStateException(entries.get(index) + " vs. " + entry + " at index " + index);
}
entry = entry.prev;
index--;
}
return entries.size();
}
}
private final LinkedReference reference; // used to track the instance
protected long size; // Size of the malloc'ed space
/** Provide a view into the original memory. Keeps an implicit reference
* to the original to prevent GC.
*/
private class SharedMemory extends Memory {
public SharedMemory(long offset, long size) {
this.size = size;
this.peer = Memory.this.peer + offset;
}
/** No need to free memory. */
@Override
protected synchronized void dispose() {
this.peer = 0;
}
/** Pass bounds check to parent. */
@Override
protected void boundsCheck(long off, long sz) {
Memory.this.boundsCheck(this.peer - Memory.this.peer + off, sz);
}
@Override
public String toString() {
return super.toString() + " (shared from " + Memory.this.toString() + ")";
}
}
/**
* Allocate space in the native heap via a call to C's malloc
.
*
* @param size number of bytes of space to allocate
*/
public Memory(long size) {
this.size = size;
if (size <= 0) {
throw new IllegalArgumentException("Allocation size must be greater than zero");
}
peer = malloc(size);
if (peer == 0)
throw new OutOfMemoryError("Cannot allocate " + size + " bytes");
reference = LinkedReference.track(this);
}
protected Memory() {
super();
reference = null;
}
/** Provide a view of this memory using the given offset as the base address. The
* returned {@link Pointer} will have a size equal to that of the original
* minus the offset.
* @throws IndexOutOfBoundsException if the requested memory is outside
* the allocated bounds.
*/
@Override
public Pointer share(long offset) {
return share(offset, size() - offset);
}
/** Provide a view of this memory using the given offset as the base
* address, bounds-limited with the given size. Maintains a reference to
* the original {@link Memory} object to avoid GC as long as the shared
* memory is referenced.
* @throws IndexOutOfBoundsException if the requested memory is outside
* the allocated bounds.
*/
@Override
public Pointer share(long offset, long sz) {
boundsCheck(offset, sz);
return new SharedMemory(offset, sz);
}
/** Provide a view onto this structure with the given alignment.
* @param byteBoundary Align memory to this number of bytes; should be a
* power of two.
* @throws IndexOutOfBoundsException if the requested alignment can
* not be met.
* @throws IllegalArgumentException if the requested alignment is not
* a positive power of two.
*/
public Memory align(int byteBoundary) {
if (byteBoundary <= 0) {
throw new IllegalArgumentException("Byte boundary must be positive: " + byteBoundary);
}
for (int i=0;i < 32;i++) {
if (byteBoundary == (1< size) {
String msg = "Bounds exceeds available space : size="
+ size + ", offset=" + (off + sz);
throw new IndexOutOfBoundsException(msg);
}
}
//////////////////////////////////////////////////////////////////////////
// Raw read methods
//////////////////////////////////////////////////////////////////////////
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,byte[],int,int)
*/
@Override
public void read(long bOff, byte[] buf, int index, int length) {
boundsCheck(bOff, length * 1L);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,short[],int,int)
*/
@Override
public void read(long bOff, short[] buf, int index, int length) {
boundsCheck(bOff, length * 2L);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,char[],int,int)
*/
@Override
public void read(long bOff, char[] buf, int index, int length) {
boundsCheck(bOff, length * Native.WCHAR_SIZE);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,int[],int,int)
*/
@Override
public void read(long bOff, int[] buf, int index, int length) {
boundsCheck(bOff, length * 4L);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,long[],int,int)
*/
@Override
public void read(long bOff, long[] buf, int index, int length) {
boundsCheck(bOff, length * 8L);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,float[],int,int)
*/
@Override
public void read(long bOff, float[] buf, int index, int length) {
boundsCheck(bOff, length * 4L);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds checks to
* ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,double[],int,int)
*/
@Override
public void read(long bOff, double[] buf, int index, int length) {
boundsCheck(bOff, length * 8L);
super.read(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.read
. But this method performs a bounds checks to
* ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#read(long,Pointer[],int,int)
*/
@Override
public void read(long bOff, Pointer[] buf, int index, int length) {
boundsCheck(bOff, length * Native.POINTER_SIZE);
super.read(bOff, buf, index, length);
}
//////////////////////////////////////////////////////////////////////////
// Raw write methods
//////////////////////////////////////////////////////////////////////////
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,byte[],int,int)
*/
@Override
public void write(long bOff, byte[] buf, int index, int length) {
boundsCheck(bOff, length * 1L);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,short[],int,int)
*/
@Override
public void write(long bOff, short[] buf, int index, int length) {
boundsCheck(bOff, length * 2L);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,char[],int,int)
*/
@Override
public void write(long bOff, char[] buf, int index, int length) {
boundsCheck(bOff, length * Native.WCHAR_SIZE);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,int[],int,int)
*/
@Override
public void write(long bOff, int[] buf, int index, int length) {
boundsCheck(bOff, length * 4L);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,long[],int,int)
*/
@Override
public void write(long bOff, long[] buf, int index, int length) {
boundsCheck(bOff, length * 8L);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,float[],int,int)
*/
@Override
public void write(long bOff, float[] buf, int index, int length) {
boundsCheck(bOff, length * 4L);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,double[],int,int)
*/
@Override
public void write(long bOff, double[] buf, int index, int length) {
boundsCheck(bOff, length * 8L);
super.write(bOff, buf, index, length);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.write
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#write(long,Pointer[],int,int)
*/
@Override
public void write(long bOff, Pointer[] buf, int index, int length) {
boundsCheck(bOff, length * Native.POINTER_SIZE);
super.write(bOff, buf, index, length);
}
//////////////////////////////////////////////////////////////////////////
// Java type read methods
//////////////////////////////////////////////////////////////////////////
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getByte
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#getByte(long)
*/
@Override
public byte getByte(long offset) {
boundsCheck(offset, 1);
return super.getByte(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getByte
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#getByte(long)
*/
@Override
public char getChar(long offset) {
boundsCheck(offset, Native.WCHAR_SIZE);
return super.getChar(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getShort
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#getShort(long)
*/
@Override
public short getShort(long offset) {
boundsCheck(offset, 2);
return super.getShort(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getInt
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#getInt(long)
*/
@Override
public int getInt(long offset) {
boundsCheck(offset, 4);
return super.getInt(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getLong
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#getLong(long)
*/
@Override
public long getLong(long offset) {
boundsCheck(offset, 8);
return super.getLong(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getFloat
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#getFloat(long)
*/
@Override
public float getFloat(long offset) {
boundsCheck(offset, 4);
return super.getFloat(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getDouble
. But this method performs a
* bounds check to ensure that the indirection does not cause memory
* outside the malloc
ed space to be accessed.
*
* @see Pointer#getDouble(long)
*/
@Override
public double getDouble(long offset) {
boundsCheck(offset, 8);
return super.getDouble(offset);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.getPointer
. But this method performs
* a bounds checks to ensure that the indirection does not cause memory
* outside the malloc
ed space to be accessed.
*
* @see Pointer#getPointer(long)
*/
@Override
public Pointer getPointer(long offset) {
boundsCheck(offset, Native.POINTER_SIZE);
return shareReferenceIfInBounds(super.getPointer(offset));
}
/**
* Get a ByteBuffer mapped to a portion of this memory.
* We keep a weak reference to all ByteBuffers provided so that this
* memory object is not GC'd while there are still implicit outstanding
* references to it (it'd be nice if we could attach our own reference to
* the ByteBuffer, but the VM generates the object so we have no control
* over it).
*
* @param offset byte offset from pointer to start the buffer
* @param length Length of ByteBuffer
* @return a direct ByteBuffer that accesses the memory being pointed to,
*/
@Override
public ByteBuffer getByteBuffer(long offset, long length) {
boundsCheck(offset, length);
ByteBuffer b = super.getByteBuffer(offset, length);
// Ensure this Memory object will not be GC'd (and its memory freed)
// if the Buffer is still extant.
buffers.put(b, this);
return b;
}
@Override
public String getString(long offset, String encoding) {
// NOTE: we only make sure the start of the string is within bounds
boundsCheck(offset, 0);
return super.getString(offset, encoding);
}
@Override
public String getWideString(long offset) {
// NOTE: we only make sure the start of the string is within bounds
boundsCheck(offset, 0);
return super.getWideString(offset);
}
//////////////////////////////////////////////////////////////////////////
// Java type write methods
//////////////////////////////////////////////////////////////////////////
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setByte
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#setByte
*/
@Override
public void setByte(long offset, byte value) {
boundsCheck(offset, 1);
super.setByte(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setChar
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#setChar
*/
@Override
public void setChar(long offset, char value) {
boundsCheck(offset, Native.WCHAR_SIZE);
super.setChar(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setShort
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#setShort
*/
@Override
public void setShort(long offset, short value) {
boundsCheck(offset, 2);
super.setShort(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setInt
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#setInt
*/
@Override
public void setInt(long offset, int value) {
boundsCheck(offset, 4);
super.setInt(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setLong
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#setLong
*/
@Override
public void setLong(long offset, long value) {
boundsCheck(offset, 8);
super.setLong(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setFloat
. But this method performs a bounds
* checks to ensure that the indirection does not cause memory outside the
* malloc
ed space to be accessed.
*
* @see Pointer#setFloat
*/
@Override
public void setFloat(long offset, float value) {
boundsCheck(offset, 4);
super.setFloat(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setDouble
. But this method performs a
* bounds checks to ensure that the indirection does not cause memory
* outside the malloc
ed space to be accessed.
*
* @see Pointer#setDouble
*/
@Override
public void setDouble(long offset, double value) {
boundsCheck(offset, 8);
super.setDouble(offset, value);
}
/**
* Indirect the native pointer to malloc
space, a la
* Pointer.setPointer
. But this method performs
* a bounds checks to ensure that the indirection does not cause memory
* outside the malloc
ed space to be accessed.
*
* @see Pointer#setPointer
*/
@Override
public void setPointer(long offset, Pointer value) {
boundsCheck(offset, Native.POINTER_SIZE);
super.setPointer(offset, value);
}
@Override
public void setString(long offset, String value, String encoding) {
boundsCheck(offset, Native.getBytes(value, encoding).length + 1L);
super.setString(offset, value, encoding);
}
@Override
public void setWideString(long offset, String value) {
boundsCheck(offset, (value.length() + 1L) * Native.WCHAR_SIZE);
super.setWideString(offset, value);
}
@Override
public String toString() {
return "allocated@0x" + Long.toHexString(peer) + " (" + size + " bytes)";
}
protected static void free(long p) {
// free(0) is a no-op, so avoid the overhead of the call
if (p != 0) {
Native.free(p);
}
}
protected static long malloc(long size) {
return Native.malloc(size);
}
/** Dumps the contents of this memory object. */
public String dump() {
return dump(0, (int)size());
}
/**
* Check whether the supplied Pointer object points into the memory region
* backed by this memory object. The intention is to prevent premature GC
* of the Memory object.
*
* @param target Pointer to check
* @return {@code target} if target does not point into the region covered
* by this memory object, a newly {@code SharedMemory} object, if the pointer
* points to memory backed by this Memory object.
*/
private Pointer shareReferenceIfInBounds(Pointer target) {
if(target == null) {
return null;
}
long offset = target.peer - this.peer;
if (offset >= 0 && offset < this.size) {
return this.share(offset);
} else {
return target;
}
}
}