/**
* Copyright (C) Zhang,Yuexiang (xfeep)
*
*/
package nginx.clojure;
import static nginx.clojure.MiniConstants.STRING_CHAR_ARRAY_OFFSET;
import static nginx.clojure.MiniConstants.STRING_OFFSET_OFFSET;
import java.io.FileDescriptor;
import java.io.RandomAccessFile;
import java.lang.ref.Reference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.security.AccessControlContext;
import sun.misc.Unsafe;
import sun.nio.cs.ThreadLocalCoders;
public class HackUtils {
private static final long threadLocalsOffset;
private static final long inheritableThreadLocalsOffset;
private static final long contextClassLoaderOffset;
private static final long inheritedAccessControlContextOffset;
private static final Method createInheritedMap;
private static final Class threadLocalMapClass;
private static final long threadLocalMapTableFieldOffset;// = UNSAFE.objectFieldOffset(threadLocalMapTableField);
private static final long threadLocalMapSizeFieldOffset;// = UNSAFE.objectFieldOffset(threadLocalMapSizeField);
private static final long threadLocalMapThresholdFieldOffset;// = UNSAFE.objectFieldOffset(threadLocalMapThresholdField);
private static final Class threadLocalMapEntryClass;
private static final long threadLocalMapEntryValueFieldOffset;
private static final long threadLocalMapEntryReferentFieldOffset;
private static final long threadLocalMapEntryQueueFieldOffset;
private static final Class randomAccessFileClass;
private static final long randomAccessFileFdFieldOffset;
private static final Class fileDescriptorClass;
private static final long fileDescriptorClassFdFieldOffset;
/*use it carefully!!*/
public static Unsafe UNSAFE = null;
public HackUtils() {
}
public static void initUnsafe() {
if (UNSAFE != null) {
return;
}
try{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
UNSAFE = (Unsafe)field.get(null);
STRING_CHAR_ARRAY_OFFSET = UNSAFE.objectFieldOffset(String.class.getDeclaredField("value"));
}catch (Exception e){
throw new RuntimeException(e);
}
try {
STRING_OFFSET_OFFSET = UNSAFE.objectFieldOffset(String.class.getDeclaredField("offset"));
} catch (NoSuchFieldException e) {
STRING_OFFSET_OFFSET = -1;
}
}
static {
initUnsafe();
try {
threadLocalsOffset = UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("threadLocals"));
inheritableThreadLocalsOffset = UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("inheritableThreadLocals"));
contextClassLoaderOffset = UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("contextClassLoader"));
long _inheritedAccessControlContextOffset = -1;
try {
_inheritedAccessControlContextOffset = UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("inheritedAccessControlContext"));
} catch (NoSuchFieldException e) {
}
inheritedAccessControlContextOffset = _inheritedAccessControlContextOffset;
threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
createInheritedMap = ThreadLocal.class.getDeclaredMethod("createInheritedMap", threadLocalMapClass);
createInheritedMap.setAccessible(true);
threadLocalMapTableFieldOffset = UNSAFE.objectFieldOffset(threadLocalMapClass.getDeclaredField("table"));
threadLocalMapSizeFieldOffset = UNSAFE.objectFieldOffset(threadLocalMapClass.getDeclaredField("size"));
threadLocalMapThresholdFieldOffset = UNSAFE.objectFieldOffset(threadLocalMapClass.getDeclaredField("threshold"));
threadLocalMapEntryClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry");
threadLocalMapEntryValueFieldOffset = UNSAFE.objectFieldOffset(threadLocalMapEntryClass.getDeclaredField("value"));
threadLocalMapEntryReferentFieldOffset = UNSAFE.objectFieldOffset(Reference.class.getDeclaredField("referent"));
threadLocalMapEntryQueueFieldOffset = UNSAFE.objectFieldOffset(Reference.class.getDeclaredField("queue"));
randomAccessFileClass = Class.forName("java.io.RandomAccessFile");
randomAccessFileFdFieldOffset = UNSAFE.objectFieldOffset(randomAccessFileClass.getDeclaredField("fd"));
fileDescriptorClass = Class.forName("java.io.FileDescriptor");
fileDescriptorClassFdFieldOffset = UNSAFE.objectFieldOffset(fileDescriptorClass.getDeclaredField("fd"));
} catch (Exception ex) {
throw new AssertionError(ex);
}
}
public static RandomAccessFile buildShadowRandomAccessFile(int fd) {
RandomAccessFile rf = null;
try {
rf = (RandomAccessFile) UNSAFE.allocateInstance(randomAccessFileClass);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
FileDescriptor fileDescriptor = new FileDescriptor();
UNSAFE.putInt(fileDescriptor, fileDescriptorClassFdFieldOffset, fd);
UNSAFE.putObject(rf, randomAccessFileFdFieldOffset, fileDescriptor);
return rf;
}
public static Object getThreadLocals(Thread thread) {
return UNSAFE.getObject(thread, threadLocalsOffset);
}
public static void setThreadLocals(Thread thread, Object threadLocals) {
UNSAFE.putObject(thread, threadLocalsOffset, threadLocals);
}
public static Object getInheritableThreadLocals(Thread thread) {
return UNSAFE.getObject(thread, inheritableThreadLocalsOffset);
}
public static void setInheritablehreadLocals(Thread thread, Object inheritableThreadLocals) {
UNSAFE.putObject(thread, inheritableThreadLocalsOffset, inheritableThreadLocals);
}
public static Object createInheritedMap(Object inheritableThreadLocals) {
return cloneThreadLocalMap(inheritableThreadLocals);
}
public static Object cloneThreadLocalMap(Object o) {
try {
Object clone = UNSAFE.allocateInstance(threadLocalMapClass);
Object origTable = UNSAFE.getObject(o, threadLocalMapTableFieldOffset);
int len = Array.getLength(origTable);
Object tableClone = Array.newInstance(threadLocalMapEntryClass, len);
for (int i = 0; i < len; i++) {
Object entry = Array.get(origTable, i);
if (entry != null)
Array.set(tableClone, i, cloneThreadLocalMapEntry(entry));
}
UNSAFE.putObject(clone, threadLocalMapTableFieldOffset, tableClone);
UNSAFE.putInt(clone, threadLocalMapSizeFieldOffset, UNSAFE.getInt(o, threadLocalMapSizeFieldOffset));
UNSAFE.putInt(clone, threadLocalMapThresholdFieldOffset, UNSAFE.getInt(o, threadLocalMapThresholdFieldOffset));
return clone;
} catch (Exception ex) {
throw new AssertionError(ex);
}
}
private static Object cloneThreadLocalMapEntry(Object entry) {
try {
Object clone = UNSAFE.allocateInstance(threadLocalMapEntryClass);
UNSAFE.putObject(clone, threadLocalMapEntryReferentFieldOffset, UNSAFE.getObject(entry, threadLocalMapEntryReferentFieldOffset));
UNSAFE.putObject(clone, threadLocalMapEntryValueFieldOffset, UNSAFE.getObject(entry, threadLocalMapEntryValueFieldOffset));
UNSAFE.putObject(clone, threadLocalMapEntryQueueFieldOffset, UNSAFE.getObject(entry, threadLocalMapEntryQueueFieldOffset));
return clone;
} catch (Exception e) {
throw new AssertionError(e);
}
}
public static ClassLoader getContextClassLoader(Thread thread) {
return (ClassLoader) UNSAFE.getObject(thread, contextClassLoaderOffset);
}
public static void setContextClassLoader(Thread thread, ClassLoader classLoader) {
UNSAFE.putObject(thread, contextClassLoaderOffset, classLoader);
}
public static AccessControlContext getInheritedAccessControlContext(Thread thread) {
if (inheritedAccessControlContextOffset < 0)
return null;
return (AccessControlContext) UNSAFE.getObject(thread, inheritedAccessControlContextOffset);
}
public static void setInheritedAccessControlContext(Thread thread, AccessControlContext accessControlContext) {
if (inheritedAccessControlContextOffset >= 0)
UNSAFE.putObject(thread, inheritedAccessControlContextOffset, accessControlContext);
}
public static int putBuffer(ByteBuffer dst, ByteBuffer src) {
int c = 0;
while (dst.hasRemaining() && src.hasRemaining()) {
dst.put(src.get());
c ++;
}
return c;
}
public static ByteBuffer encode(String s, Charset cs, ByteBuffer bb) {
//for safe
if (s == null) {
throw new NullPointerException("string should not be null");
}
CharsetEncoder ce = ThreadLocalCoders.encoderFor(cs)
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
CharBuffer cb = CharBuffer.wrap((char[])UNSAFE.getObject(s, STRING_CHAR_ARRAY_OFFSET),
STRING_OFFSET_OFFSET > 0 ? UNSAFE.getInt(s, STRING_OFFSET_OFFSET) : 0, s.length());
ce.reset();
CoderResult rt = ce.encode(cb, bb, true);
if (rt == CoderResult.OVERFLOW) {
bb.flip();
ByteBuffer lbb = ByteBuffer.allocate((int)(s.length() * (double)ce.maxBytesPerChar()));
lbb.put(bb);
bb = lbb;
rt = ce.encode(cb, bb, true);
}
if (rt != CoderResult.UNDERFLOW) {
throw new RuntimeException(rt.toString());
}
rt = ce.flush(bb);
if (rt != CoderResult.UNDERFLOW) {
throw new RuntimeException(rt.toString());
}
bb.flip();
if (bb.remaining() < bb.capacity()) {
bb.array()[bb.arrayOffset()+bb.remaining()] = 0; // for char* c language is ended with '\0'
}
return bb;
}
public static ByteBuffer encodeLowcase(String s, Charset cs, ByteBuffer bb) {
if (bb.isDirect()) {
return encode(s.toLowerCase(), cs, bb);
}
encode(s, cs, bb);
int len = bb.remaining();
byte[] array = bb.array();
for (int i = 0; i < len; i++) {
byte b = array[i];
array[i] = b >= 'A' && b <= 'Z' ? (byte) (b | 0x20) : b;
}
return bb;
}
public static String decode(ByteBuffer bb, Charset cs, CharBuffer cb) {
CharsetDecoder de = ThreadLocalCoders.decoderFor(cs)
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
de.reset();
int len = bb.remaining();
CoderResult rt = de.decode(bb, cb, true);
if (rt == CoderResult.OVERFLOW) {
cb.flip();
CharBuffer lcb = CharBuffer.allocate((int)(len * (double)de.maxCharsPerByte()));
lcb.put(cb);
cb = lcb;
rt = de.decode(bb, cb, true);
}
if (rt != CoderResult.UNDERFLOW) {
throw new RuntimeException(rt.toString());
}
rt = de.flush(cb);
if (rt != CoderResult.UNDERFLOW) {
throw new RuntimeException(rt.toString());
}
cb.flip();
return cb.toString();
}
public static CharBuffer decodeValid(ByteBuffer bb, Charset cs, CharBuffer cb) {
CharsetDecoder de = ThreadLocalCoders.decoderFor(cs)
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
de.reset();
int len = bb.remaining();
CoderResult rt = de.decode(bb, cb, true);
if (rt == CoderResult.OVERFLOW) {
cb.flip();
CharBuffer lcb = CharBuffer.allocate((int)(len * (double)de.maxCharsPerByte()));
lcb.put(cb);
cb = lcb;
rt = de.decode(bb, cb, true);
}
rt = de.flush(cb);
cb.flip();
return cb;
}
public static String truncateToDotAppendString(String s, int max) {
StringBuilder sb = new StringBuilder();
if (s == null) {
sb.append("<NULL>");
}else if (s.length() == 0) {
sb.append("<EMPTY>");
}else {
int alen = Math.min(max, s.length());
sb.append(s.substring(0, alen));
if (alen < s.length()) {
sb.append("...");
}
}
return sb.toString();
}
}