/* * Copyright 2014 Ruediger Moeller. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.nustaq.offheap.structs.unsafeimpl; import org.nustaq.offheap.bytez.Bytez; import org.nustaq.offheap.bytez.BytezAllocator; import org.nustaq.offheap.bytez.onheap.HeapBytezAllocator; import org.nustaq.offheap.structs.*; import org.nustaq.offheap.structs.structtypes.StructArray; import org.nustaq.offheap.structs.structtypes.StructByteString; import org.nustaq.offheap.structs.structtypes.StructString; import org.nustaq.serialization.FSTClazzInfo; import org.nustaq.serialization.FSTConfiguration; import org.nustaq.serialization.util.FSTInt2ObjectMap; import org.nustaq.serialization.util.FSTUtil; import javassist.*; import javassist.Modifier; import javassist.bytecode.*; import javassist.expr.ExprEditor; import javassist.expr.FieldAccess; import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.reflect.*; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; /** * manages + generates struct instrumented classes */ public class FSTStructFactory { public static int SIZE_ALIGN = 2; static FSTStructFactory instance; public static FSTStructFactory getInstance() { if (instance==null) { instance = new FSTStructFactory(); // fixme: should be final } return instance; } public static final int MAX_CLASSES = 1000; static FSTConfiguration conf = FSTConfiguration.createStructConfiguration(); ClassPool defaultPool; Loader proxyLoader; ClassLoader parentLoader; { defaultPool = new ClassPool(null) { @Override public CtClass get(String classname) throws NotFoundException { if ( rawByteClassDefs.containsKey(classname)) { try { return makeClass(new ByteArrayInputStream(rawByteClassDefs.get(classname))); } catch (IOException e) { e.printStackTrace(); } } return super.get(classname); } }; defaultPool.appendSystemPath(); proxyLoader = new Loader(FSTStructFactory.class.getClassLoader(), defaultPool) { protected Class loadClassByDelegation(String name) throws ClassNotFoundException { try { return delegateToParent(name); } catch (Exception ex) { return null; } } }; } ConcurrentHashMap<Class, Class> proxyClzMap = new ConcurrentHashMap<Class, Class>(); FSTStructGeneration structGen = new FSTByteArrayUnsafeStructGeneration(); ConcurrentHashMap<String,byte[]> rawByteClassDefs = new ConcurrentHashMap<String, byte[]>(); boolean autoRegister = true; BytezAllocator allocator = new HeapBytezAllocator(); public FSTStructFactory() { registerClz(FSTStruct.class); registerClz(StructString.class); registerClz(StructArray.class); registerClz(StructByteString.class); } public void registerRawClass( String name, byte bytes[] ) { rawByteClassDefs.put(name,bytes); } public <T> Class<T> createStructClz( Class<T> clazz ) throws Exception { //FIXME: ensure FSTStruct is superclass, check protected, no private methods+fields if ( Modifier.isFinal(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers()) ) { throw new RuntimeException("Cannot add final classes to structs "+clazz.getName()); } if ( clazz.getName().endsWith("_Struct") ) { throw new RuntimeException("cannot create Struct on Struct class. Class "+clazz+" is already instrumented" ); } String proxyName = clazz.getName()+"_Struct"; Class present = null; try { present = proxyLoader.loadClass(proxyName); } catch (ClassNotFoundException ex) { // } if ( present != null ) return present; ClassPool pool = defaultPool; CtClass newClz = pool.makeClass(proxyName); CtClass orig = null; if ( rawByteClassDefs.get(clazz.getName()) != null ) { orig = pool.makeClass( new ByteArrayInputStream(rawByteClassDefs.get(clazz.getName()))); } else { orig = pool.getOrNull(clazz.getName()); if ( orig == null ) { pool.insertClassPath(new ClassClassPath(clazz)); orig = pool.get(clazz.getName()); if (orig == null) { throw new RuntimeException("unable to locate class byte code for "+clazz.getName()); } } } newClz.setSuperclass(orig); final FSTClazzInfo clInfo = conf.getClassInfo(clazz); CtMethod[] methods = orig.getMethods(); for (int i = 0; i < methods.length; i++) { CtMethod method = methods[i]; final Class curClz = Class.forName( method.getDeclaringClass().getName() ); boolean allowed = ((method.getModifiers() & AccessFlag.ABSTRACT) == 0 ) && (method.getModifiers() & AccessFlag.NATIVE) == 0 && (method.getModifiers() & AccessFlag.FINAL) == 0 && ( !method.getDeclaringClass().getName().equals(FSTStruct.class.getName()) ||method.getName().equals("getFieldValues")) && ! method.getDeclaringClass().getName().equals(Object.class.getName()); allowed &= method.getAnnotation(NoAssist.class) == null; allowed &= (method.getModifiers() & AccessFlag.STATIC) == 0; if ( allowed && (method.getModifiers() & AccessFlag.FINAL) != 0 && ! method.getDeclaringClass().getName().equals("java.lang.Object") ) { throw new RuntimeException("final methods are not allowed for struct classes:"+method.getName()); } if ( allowed && (method.getModifiers() & AccessFlag.PRIVATE) != 0 && ! method.getDeclaringClass().getName().equals("java.lang.Object")) { throw new RuntimeException("private methods are not allowed for struct classes:"+method.getName()); } if ( allowed ) { ClassMap mp = new ClassMap(); mp.fix(clazz.getName()); mp.fix(clazz.getSuperclass().getName()); // ?? only depth 2 ?? method = new CtMethod(method,newClz,mp); String methName = method.getName(); // array access: // void [name](int, type) // [type] [name](int) FSTClazzInfo.FSTFieldInfo arrayFi = checkForSpecialArrayMethod(clInfo, method, "", null, null); // array length: // int [name]Len() FSTClazzInfo.FSTFieldInfo lenfi = checkForSpecialArrayMethod(clInfo, method, "Len", CtClass.intType, new CtClass[0]); // get byte index of array data: // int [name]Index() FSTClazzInfo.FSTFieldInfo indexfi = checkForSpecialArrayMethod(clInfo, method, "Index", CtClass.intType, new CtClass[0]); // get size of array element: // int [name]ElementSize() FSTClazzInfo.FSTFieldInfo elemlen = checkForSpecialArrayMethod(clInfo, method, "ElementSize", CtClass.intType, new CtClass[0]); // CREATE non volatile pointer to array[0] element: // type [name]Pointer() OR type [name]Pointer(pointerToSetup) (for reuse) FSTClazzInfo.FSTFieldInfo pointerfi = checkForSpecialArrayMethod(clInfo, method, "Pointer", null, null); // get byte index to structure or array header element: // type [name]StructIndex() FSTClazzInfo.FSTFieldInfo structIndex = checkForSpecialMethod(clInfo, method, "StructIndex", CtClass.intType, new CtClass[0], false); // set with CAS // boolean [name]CAS(expectedValue,value) FSTClazzInfo.FSTFieldInfo casAcc = checkForSpecialMethod(clInfo, method, "CAS", CtClass.booleanType, null, false); if ( casAcc != null ) { structGen.defineStructSetCAS(casAcc, clInfo, method); newClz.addMethod(method); } else if ( pointerfi != null ) { structGen.defineArrayPointer(pointerfi, clInfo, method); newClz.addMethod(method); } else if ( structIndex != null ) { structGen.defineFieldStructIndex(structIndex, clInfo, method); newClz.addMethod(method); } else if ( indexfi != null ) { structGen.defineArrayIndex(indexfi, clInfo, method); newClz.addMethod(method); } else if ( elemlen != null ) { structGen.defineArrayElementSize(elemlen, clInfo, method); newClz.addMethod(method); } else if ( arrayFi != null ) { structGen.defineArrayAccessor(arrayFi, clInfo, method); newClz.addMethod(method); } else if ( methName.endsWith("Len") && lenfi != null ) { structGen.defineArrayLength(lenfi, clInfo, method); newClz.addMethod(method); } else { if ( methName.equals("getFieldValues") && ( (clInfo.getClazz().getSuperclass().getName().equals("de.nustaq.reallive.impl.RLStructRow")) // oops || (curClz != FSTStruct.class) ) ) { FSTClazzInfo.FSTFieldInfo[] fieldInfo = clInfo.getFieldInfo(); StringBuilder body = new StringBuilder("{ return new Object[] { "); for (int j = 0; j < fieldInfo.length; j++) { FSTClazzInfo.FSTFieldInfo fstFieldInfo = fieldInfo[j]; int modifiers = fstFieldInfo.getField().getModifiers(); if ( (java.lang.reflect.Modifier.isProtected(modifiers) || java.lang.reflect.Modifier.isPublic(modifiers)) && !java.lang.reflect.Modifier.isStatic(modifiers) ) { body.append( "\""+fstFieldInfo.getName()+"\", " ); Class type = fstFieldInfo.getType(); if ( FSTStruct.class.isAssignableFrom(type) ) { body.append(fstFieldInfo.getName()).append(".getFieldValues()"); } else { if ( type.isPrimitive() ) { if ( long.class == type ) { body.append("new Long("+fstFieldInfo.getName()+")"); } else if ( float.class == type ||double.class == type ) { body.append("new Double("+fstFieldInfo.getName()+")"); } else { body.append("new Integer("+fstFieldInfo.getName()+")"); } } else { body.append(fstFieldInfo.getName()); } } if ( j != fieldInfo.length-1 ) body.append(","); } } body.append("}; }"); method.setBody(body.toString()); } newClz.addMethod(method); method.instrument( new ExprEditor() { @Override public void edit(FieldAccess f) throws CannotCompileException { try { if ( ! f.isStatic() ) { CtClass type = null; type = f.getField().getType(); FSTClazzInfo.FSTFieldInfo fieldInfo = clInfo.getFieldInfo(f.getFieldName(), null); if ( fieldInfo == null ) { return; } if ( f.isReader() ) { structGen.defineStructReadAccess(f, type, fieldInfo); } else if ( f.isWriter() ) { structGen.defineStructWriteAccess(f, type, fieldInfo); } } } catch (Exception e) { e.printStackTrace(); } } }); } } } return (Class<T>) loadProxyClass(clazz, pool, newClz); } FSTClazzInfo.FSTFieldInfo checkForSpecialArrayMethod( FSTClazzInfo clzInfo, CtMethod method, String postFix, Object returnType, CtClass requiredArgs[] ) { return checkForSpecialMethod(clzInfo, method, postFix, returnType, requiredArgs, true); } FSTClazzInfo.FSTFieldInfo checkForSpecialMethod(FSTClazzInfo clzInfo, CtMethod method, String postFix, Object returnType, CtClass requiredArgs[], boolean array) { int len = postFix.length(); String methName = method.getName(); if ( ! methName.endsWith(postFix) ) { return null; } FSTClazzInfo.FSTFieldInfo res = clzInfo.getFieldInfo(methName.substring(0, methName.length() - len), null); if ( res == null ) { return null; } if ( array && res.isArray() && res.getArrayType().isArray() ) { throw new RuntimeException("nested arrays not supported "+res.getDesc()); } if ( array && !res.isArray() ) { //throw new RuntimeException("expect array type for field "+res.getDesc()+" special method:"+method); // just ignore return null; } if ( res.isArray() || ! array ) { if ( returnType instanceof Class ) { try { if ( ! method.getReturnType().getName().equals(((Class) returnType).getName()) ) { throw new RuntimeException("expected method "+method+" to return "+returnType ); } } catch (NotFoundException e) { e.printStackTrace(); } } else if ( returnType instanceof CtClass ) { try { if ( ! method.getReturnType().equals(returnType) ) { throw new RuntimeException("expected method "+method+" to return "+returnType ); } } catch (NotFoundException e) { e.printStackTrace(); } } return res; } return null; } private <T> Class loadProxyClass(Class<T> clazz, ClassPool pool, final CtClass cc) throws ClassNotFoundException { Class ccClz; Loader cl = new Loader(clazz.getClassLoader(), pool) { protected Class loadClassByDelegation(String name) throws ClassNotFoundException { if ( name.equals(cc.getName()) ) return null; return delegateToParent(name); } }; ccClz = cl.loadClass(cc.getName()); return ccClz; } public Class getProxyClass(Class clz) throws Exception { // synchronized (this) { Class res = proxyClzMap.get(clz); if ( res == null ) { res = createStructClz(clz); proxyClzMap.put(clz,res); } return res; } } public <T extends FSTStruct> T createWrapper(Class<T> onHeap, Bytez bytes, long index) throws Exception { Class proxy = getProxyClass(onHeap); T res = (T) FSTUtil.getUnsafe().allocateInstance(proxy); res.baseOn(bytes, index, this); return res; } public <T extends FSTStruct> T createEmptyStructPointer(Class<T> onHeap) { try { return createWrapper(onHeap,null,0); } catch (Exception e) { FSTUtil.<RuntimeException>rethrow(e); } return null; } /** * allocates a StructAccessor ("pointer") matching the struct data expected in the byte * array at given position. The resulting pointer object is not "volatile" (not a cached instance) * @param b * @param index * @return */ public FSTStruct createStructWrapper(Bytez b, long index) { int clzId = b.getInt(index + 4); return createStructPointer(b, index, clzId); } /** * allocates a StructAccessor ("pointer") matching the struct data expected in the byte * array at given position with given classId. The resulting pointer object is not "volatile" (not a cached instance). * The class id should match the Struct stored in the byte array. (classId must be the correct struct or a superclass of it) * @param b * @param index * @return */ public FSTStruct createStructPointer(Bytez b, long index, int clzId) { // synchronized (this) // FIXME FIXME FIXME: contention point // desynced expecting class registering happens on startup { Class clazz = mIntToClz.get(clzId); if (clazz==null) throw new RuntimeException("unregistered class "+clzId); try { return (FSTStruct) createWrapper(clazz, b, index); } catch (Exception e) { throw new RuntimeException(e); } } } public FSTStruct createTypedArrayBasePointer(Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) { int arrayElementZeroindex = base.getInt(objectBaseOffset + arrayStructIndex); int elemSiz = base.getInt(objectBaseOffset+arrayStructIndex+8); int len = base.getInt(objectBaseOffset+arrayStructIndex+4); int clId = base.getInt(objectBaseOffset+arrayStructIndex+12); FSTStruct structPointer = null; if ( clId <= 0 ) { // untyped array structPointer = new FSTStruct(); structPointer.baseOn(base,objectBaseOffset+arrayElementZeroindex,this); } else { structPointer = createStructPointer(base, (int) (objectBaseOffset+arrayElementZeroindex), clId); } structPointer.___elementSize = elemSiz; return structPointer; } public void fillTypedArrayBasePointer(FSTStruct result, Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) { int arrayElementZeroindex = base.getInt(objectBaseOffset+arrayStructIndex); int elemSiz = base.getInt(objectBaseOffset+arrayStructIndex+8); // int len = unsafe.getInt(base,objectBaseOffset+arrayStructIndex+4); // int clId = unsafe.getInt(base,objectBaseOffset+arrayStructIndex+12); result.baseOn(base, objectBaseOffset + arrayElementZeroindex, this); result.___elementSize = elemSiz; } public void fillPrimitiveArrayBasePointer(FSTStruct result, Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) { int arrayElementZeroindex = base.getInt(objectBaseOffset+arrayStructIndex); result.baseOn(base,objectBaseOffset+arrayElementZeroindex,this); } public FSTStruct createPrimitiveArrayBasePointer(Bytez base, long objectBaseOffset /*offset of object containing array*/, int arrayStructIndex /*position of array header in struct*/) { int arrayElementZeroindex = base.getInt(objectBaseOffset+arrayStructIndex); // int len = unsafe.getInt(base,objectBaseOffset+arrayStructIndex+4); FSTStruct structPointer = new FSTStruct(); structPointer.baseOn(base,objectBaseOffset+arrayElementZeroindex,this); return structPointer; } public <T extends FSTStruct> StructArray<T> toStructArray(int size, T onHeap) { StructArray<T> arr = new StructArray<T>(size,onHeap); return toStruct(arr); } public <T extends FSTStruct> T toStruct(T onHeap) { return toStruct(onHeap,allocator); } public <T extends FSTStruct> T toStruct(T onHeap, BytezAllocator alloc) { if ( onHeap.isOffHeap() ) { return onHeap; } try { Bytez b = toByteArray(onHeap, alloc); return (T)createWrapper(onHeap.getClass(),b,0); } catch (Exception e) { if ( e instanceof RuntimeException ) throw (RuntimeException)e; else throw new RuntimeException(e); } } ThreadLocal<Object[]> cachedWrapperMap = new ThreadLocal<Object[]>() { @Override protected Object[] initialValue() { return new Object[MAX_CLASSES]; } }; public void detach(FSTStruct structPointer) { int id = structPointer.getClzId(); Object o = cachedWrapperMap.get()[id]; if ( o == structPointer ) cachedWrapperMap.get()[id] = null; } public FSTStruct getStructPointerByOffset(Bytez b, long offset) { if ( b.length() < offset+8 ) throw new RuntimeException("array to short "+b.length()+" offset "+offset); int clzId = b.getInt(offset+4); int ptr = b.getInt(offset); if (clzId <= 0) { return null; } Object[] wrapperMap = cachedWrapperMap.get(); Object res = wrapperMap[clzId]; if ( res != null ) { ((FSTStruct)res).baseOn(b, offset, this); return (FSTStruct) res; } res = createStructPointer(b, (int) (offset), clzId); wrapperMap[clzId] = res; return (FSTStruct) res; } /** * returns a struct wrapper for given structured object from the thread local wrapper cache. * @param b * @param index * @return */ public FSTStruct getStructPointer(Bytez b, long index) { return getStructPointerByOffset(b,index); } public static int align(int val, int align) { while( val%align != 0 ) val++; return val; } public int calcStructSize(FSTStruct onHeapStruct) { try { if ( onHeapStruct == null ) { return 0; } if (onHeapStruct.isOffHeap()) return onHeapStruct.getByteSize(); int siz = 8; FSTClazzInfo clInfo = conf.getClassInfo(onHeapStruct.getClass()); FSTClazzInfo.FSTFieldInfo fis[] = clInfo.getFieldInfo(); for (int i = 0; i < fis.length; i++) { FSTClazzInfo.FSTFieldInfo fi = fis[i]; if ( fi.getField().getDeclaringClass() == FSTStruct.class ) continue; int modifiers = fi.getField().getModifiers(); if ( ! Modifier.isProtected(modifiers) && ! Modifier.isPublic(modifiers) ) throw new RuntimeException("all fields of a structable class must be public or protected. Field:"+fi.getName()+" in class "+fi.getField().getDeclaringClass().getName() ); // FIXME: check for null refs, check for FSTStruct subclasses if ( fi.getType().isArray() ) { if ( fi.getType().getComponentType().isArray() ) { throw new RuntimeException("nested arrays not supported"); } // if array is @aligned, add align-1 to size (overestimation), because I don't know the exact position of the array data here // embedded object data is currently not aligned, only the header position respects the @align if ( fi.isIntegral() ) { // prim array Object objectValue = fi.getObjectValue(onHeapStruct); if ( objectValue == null ) { throw new RuntimeException("arrays in struct templates must not be null !"); } siz += Array.getLength(objectValue) * fi.getComponentStructSize() + fi.getStructSize() + fi.getAlignPad()+(fi.getAlign()>0?fi.getAlign()-1:0); } else { // object array Object objectValue[] = (Object[]) fi.getObjectValue(onHeapStruct); if (objectValue==null) { siz+=fi.getStructSize()+fi.getAlignPad()+(fi.getAlign()>0?fi.getAlign()-1:0); } else { int elemSiz = computeElemSize(onHeapStruct,objectValue, fi); siz += Array.getLength(objectValue) * elemSiz + fi.getStructSize() + fi.getAlignPad()+(fi.getAlign()>0?fi.getAlign()-1:0); } } } else if ( fi.isIntegral() ) { // && ! array siz += fi.getStructSize(); } else { // objectref FSTStruct obj = (FSTStruct) fi.getObjectValue(onHeapStruct); siz += fi.getStructSize()+calcStructSize(obj)+fi.getAlignPad(); } } if ( onHeapStruct instanceof FSTEmbeddedBinary) { siz+=((FSTEmbeddedBinary) onHeapStruct).getEmbeddedSizeAdditon(this); } return siz; } catch ( Exception e ) { throw new RuntimeException(e); } } protected int computeElemSize(Object container, Object[] objectValue, FSTClazzInfo.FSTFieldInfo fi) { if ( container instanceof FSTArrayElementSizeCalculator) { int res = ((FSTArrayElementSizeCalculator) container).getElementSize(fi.getField(),this); if ( res >= 0 ) return res; } Templated annotation = fi.getField().getAnnotation(Templated.class); if ( annotation != null ) { Object template = objectValue[0]; return align(calcStructSize((FSTStruct) template),SIZE_ALIGN); } int elemSiz = 0; for (int j = 0; j < objectValue.length; j++) { Object o = objectValue[j]; if ( o != null ) elemSiz=Math.max( elemSiz, calcStructSize((FSTStruct) o) ); } return align(elemSiz,SIZE_ALIGN); } FSTInt2ObjectMap<Class> mIntToClz = new FSTInt2ObjectMap<Class>(97); // id to onheap class HashMap<Class,Integer> mClzToInt = new HashMap<Class,Integer>(); // reverse int idCount = 1; public void registerClz(Class ... classes) { for (int i = 0; i < classes.length; i++) { Class c = classes[i]; if ( mClzToInt.containsKey(c) ) { continue; } int id = idCount++; mIntToClz.put(id,c); mClzToInt.put(c,id); try { getProxyClass(c); } catch (Exception e) { FSTUtil.<RuntimeException>rethrow(e); } } } // register from top to bottom to avoid interference with application (fastcast) public void registerSystemClz(byte startVal, Class ... classes) { for (int i = 0; i < classes.length; i++) { Class c = classes[i]; if ( mClzToInt.containsKey(c) ) { continue; } int id = startVal--; mIntToClz.put(id,c); mClzToInt.put(c,id); try { getProxyClass(c); } catch (Exception e) { FSTUtil.<RuntimeException>rethrow(e); } } } public void registerClzId(Class c, int id) { mIntToClz.put(id,c); mClzToInt.put(c,id); } public int getClzId(Class c) { Integer integer = mClzToInt.get(c); if (autoRegister && integer == null && c != null ) { if ( c.getName().endsWith("_Struct") ) return getClzId(c.getSuperclass()); registerClz(c); return getClzId(c); } return integer == null ? 0: integer; } public Class getClazz(int clzId) { return mIntToClz.get(clzId); } public Bytez toByteArray(FSTStruct onHeapStruct) { return toByteArray(onHeapStruct,allocator); } public Bytez toByteArray(FSTStruct onHeapStruct, BytezAllocator allocator) { try { int sz = align(calcStructSize(onHeapStruct),SIZE_ALIGN); Bytez b = allocator.alloc(sz); toByteArray(onHeapStruct,b,0); return b; } catch (Exception e) { if ( e instanceof RuntimeException ) throw (RuntimeException)e; else throw new RuntimeException(e); } } static class ForwardEntry { ForwardEntry(int pointerPos, Object forwardObject, FSTClazzInfo.FSTFieldInfo fsfi) { this.pointerPos = pointerPos; this.forwardObject = forwardObject; fi = fsfi; } FSTClazzInfo.FSTFieldInfo fi; int pointerPos; Object forwardObject; FSTStruct template; } public int toByteArray(FSTStruct onHeapStruct, Bytez bytes, int index) throws Exception { ArrayList<ForwardEntry> positions = new ArrayList<ForwardEntry>(); if ( onHeapStruct == null ) { return index; } if (onHeapStruct.isOffHeap()) { // unsafe.copyMemory(onHeapStruct.___bytes,onHeapStruct.___offset,bytes,FSTStruct.bufoff+index,onHeapStruct.getByteSize()); onHeapStruct.___bytes.copyTo(bytes, index, onHeapStruct.___offset, onHeapStruct.getByteSize()); return onHeapStruct.getByteSize(); } int initialIndex =index; Class<? extends FSTStruct> aClass = onHeapStruct.getClass(); int clzId = getClzId(aClass); bytes.putInt(index+4,clzId); index+=8; FSTClazzInfo clInfo = conf.getClassInfo(aClass); FSTClazzInfo.FSTFieldInfo fis[] = clInfo.getFieldInfo(); for (int i = 0; i < fis.length; i++) { FSTClazzInfo.FSTFieldInfo fi = fis[i]; if ( fi.getField().getDeclaringClass() == FSTStruct.class ) continue; index+=fi.getAlignPad(); if ( fi.getType().isArray() ) { if ( fi.getType().getComponentType().isArray() ) { throw new RuntimeException("nested arrays not supported"); } if ( fi.isIntegral() ) { // prim array Object objectValue = fi.getObjectValue(onHeapStruct); positions.add(new ForwardEntry(index,objectValue,fi)); index += fi.getStructSize(); } else { // object array Object objArr[] = (Object[]) fi.getObjectValue(onHeapStruct); if ( objArr == null ) { bytes.putInt(index, -1); index+=fi.getStructSize(); } else { Templated takeFirst = fi.getField().getAnnotation(Templated.class); ForwardEntry fe = new ForwardEntry(index, objArr, fi); if ( takeFirst != null ) { fe.template = (FSTStruct) objArr[0]; } positions.add(fe); index += fi.getStructSize(); int elemSiz = computeElemSize(onHeapStruct,objArr,fi); bytes.putInt(index-8,elemSiz); } } } else if ( fi.isIntegral() ) { // && ! array Class type = fi.getType(); int structIndex = fi.getStructOffset(); if ( index != structIndex+initialIndex ) { throw new RuntimeException("internal error. please file an issue"); } if ( type == boolean.class ) { bytes.putBool(index, fi.getBooleanValue(onHeapStruct)); } else if ( type == byte.class ) { bytes.put(index, (byte) fi.getByteValue(onHeapStruct)); } else if ( type == char.class ) { bytes.putChar(index, (char) fi.getCharValue(onHeapStruct)); } else if ( type == short.class ) { bytes.putShort(index, (short) fi.getShortValue(onHeapStruct)); } else if ( type == int.class ) { bytes.putInt(index, fi.getIntValue(onHeapStruct)); } else if ( type == long.class ) { bytes.putLong( index, fi.getLongValue(onHeapStruct) ); } else if ( type == float.class ) { bytes.putFloat(index, fi.getFloatValue(onHeapStruct)); } else if ( type == double.class ) { bytes.putDouble(index, fi.getDoubleValue(onHeapStruct)); } else { throw new RuntimeException("this is an error"); } index += fi.getStructSize(); } else { // objectref Object obj = fi.getObjectValue(onHeapStruct); int structIndex = fi.getStructOffset(); if ( index != structIndex+initialIndex ) { throw new RuntimeException("internal error. please file an issue"); } if ( obj == null ) { bytes.putInt(index, -1); bytes.putInt(index+4, -1); index+=fi.getStructSize(); } else { Object objectValue = fi.getObjectValue(onHeapStruct); positions.add(new ForwardEntry(index,objectValue,fi)); index += fi.getStructSize(); } } } for ( int i=0; i < positions.size(); i++) { ForwardEntry en = positions.get(i); Object o = en.forwardObject; if ( o == null ) { throw new RuntimeException("this is a bug"); } Class c = o.getClass(); if (c.isArray()) { if ( en.fi.getAlign() > 0 ) { while( (index%en.fi.getAlign()) != 0 ) { index++; } } long siz = 0; if ( c == byte[].class ) { siz = Array.getLength(o); bytes.set(index,(byte[])o,0, (int) siz); } else if ( c == boolean[].class ) { siz = Array.getLength(o); bytes.setBoolean(index, (boolean[]) o, 0, (int) siz); } else if ( c == char[].class ) { siz = Array.getLength(o); bytes.setChar(index, (char[]) o, 0, (int) siz); siz *= 2; } else if ( c == short[].class ) { siz = Array.getLength(o); // * FSTUtil.chscal; bytes.setShort(index, (short[]) o, 0, (int) siz); siz *= 2; } else if ( c == int[].class ) { siz = Array.getLength(o); // * FSTUtil.intscal; bytes.setInt(index, (int[]) o, 0, (int) siz); siz *= 4; } else if ( c == long[].class ) { siz = Array.getLength(o); // * FSTUtil.longscal; bytes.setLong(index, (long[]) o, 0, (int) siz); siz *= 8; } else if ( c == float[].class ) { siz = Array.getLength(o); // * FSTUtil.floatscal; bytes.setFloat(index, (float[]) o, 0, (int) siz); siz *= 4; } else if ( c == double[].class ) { siz = Array.getLength(o); // * FSTUtil.doublescal; bytes.setDouble(index, (double[]) o, 0, (int) siz); siz *= 8; } else { Object[] objArr = (Object[]) o; int elemSiz = bytes.getInt(en.pointerPos+8); siz = Array.getLength(o) * elemSiz; int tmpIndex = index; Bytez templatearr = null; boolean hasClzId = false; if ( onHeapStruct instanceof FSTArrayElementSizeCalculator ) { Class elemClz = ((FSTArrayElementSizeCalculator)onHeapStruct).getElementType(en.fi.getField(),this); if ( elemClz != null ) { int clid = getClzId(elemClz); bytes.putInt(en.pointerPos + 12, clid); hasClzId = true; } } if (en.template != null) { templatearr = toByteArray(en.template); // fixme: unnecessary alloc if ( ! hasClzId ) { bytes.putInt(en.pointerPos + 12, getClzId(en.template.getClass())); hasClzId = true; } } for (int j = 0; j < objArr.length; j++) { Object objectValue = objArr[j]; if ( templatearr != null ) { // unsafe.copyMemory(templatearr,FSTStruct.bufoff,bytes,FSTStruct.bufoff+tmpIndex,templatearr.length); templatearr.copyTo(bytes,tmpIndex,0,templatearr.length()); tmpIndex+=elemSiz; } else { if ( objectValue == null ) { bytes.putInt(tmpIndex + 4, -1); tmpIndex += elemSiz; } else { toByteArray((FSTStruct) objectValue, bytes, tmpIndex); bytes.putInt(tmpIndex, elemSiz); // need to patch size in case of smaller objects in obj array tmpIndex += elemSiz; if ( !hasClzId ) { bytes.putInt(en.pointerPos + 12, getClzId(en.fi.getArrayType())); hasClzId = true; } } } } } bytes.putInt(en.pointerPos, index - initialIndex); // offset to real data bytes.putInt(en.pointerPos + 4, Array.getLength(o)); // array len index+=siz; } else { // object ref or objarray elem int newoffset = toByteArray((FSTStruct) o, bytes, index); bytes.putInt(en.pointerPos, index - initialIndex); index = newoffset; } } if ( onHeapStruct instanceof FSTEmbeddedBinary ) { FSTEmbeddedBinary embeddedBinary = (FSTEmbeddedBinary) onHeapStruct; index = embeddedBinary.insertEmbedded(this, bytes, index); } bytes.putInt(initialIndex, index - initialIndex); // set object size return index; } public int getShallowStructSize(Class clz) { return conf.getClassInfo(clz).getStructSize(); } Class classForName( String name ) throws ClassNotFoundException { try { return Class.forName(name); } catch ( ClassNotFoundException ex ) { if ( parentLoader != null ) { return parentLoader.loadClass(name); } throw ex; } } public ClassLoader getParentLoader() { return parentLoader; } public void setParentLoader(ClassLoader parentLoader) { this.parentLoader = parentLoader; } }