001    package org.bridj;
002    import java.lang.reflect.TypeVariable;
003    import java.lang.reflect.WildcardType;
004    import org.bridj.CallIO.NativeObjectHandler;
005    import org.bridj.util.*;
006    import java.lang.reflect.ParameterizedType;
007    import java.lang.reflect.Method;
008    import java.lang.reflect.Modifier;
009    import java.lang.reflect.Member;
010    import java.lang.reflect.AnnotatedElement;
011    import java.lang.reflect.Type;
012    import java.nio.*;
013    import java.util.ArrayList;
014    import java.util.Collections;
015    import java.util.Comparator;
016    import java.util.HashMap;
017    import java.util.LinkedHashMap;
018    import java.util.HashSet;
019    import java.util.List;
020    import java.util.Map;
021    import java.util.TreeMap;
022    import java.util.Set;
023    import org.bridj.ann.Virtual;
024    import org.bridj.ann.Array;
025    import org.bridj.ann.Union;
026    import org.bridj.ann.Bits;
027    import org.bridj.ann.Field;
028    import org.bridj.ann.Struct;
029    import org.bridj.ann.Alignment;
030    import static org.bridj.Pointer.*;
031    import static org.bridj.util.AnnotationUtils.*;
032    
033    /**
034     * Representation of a C struct's memory layout, built thanks to the annotations found in the Java bindings.<br>
035     * End-users should not use this class, it's used by runtimes.<br>
036     * Annotations currently used are {@link org.bridj.ann.Virtual}, {@link org.bridj.ann.Array}, {@link org.bridj.ann.Bits}, {@link org.bridj.ann.Field}, {@link org.bridj.ann.Alignment} and soon {@link org.bridj.ann.Struct}
037     * @author ochafik
038     */
039    public class StructIO {
040    
041        static Map<Type, StructIO> structIOs = new HashMap<Type, StructIO>();
042    
043        /**
044         * Interface for type customizers that can be used to perform platform-specific type adjustments or other hacks.<br>
045         * A type customizer can be specified with {@link Struct#customizer() }.<br>
046         * Each implementation must have a default constructor, and an unique instance of each implementation class will be cached by {@link StructIO#getCustomizer(java.lang.Class) }.
047         * @deprecated The StructIO API is subject to future changes. Use this with care and be prepared to migrate your code...
048         */
049        @Deprecated
050        public interface Customizer {
051                    /**
052                     * Last chance to remove field declarations
053                     */
054                    void beforeAggregation(StructIO io, List<FieldDecl> fieldDecls);
055                    /**
056                     * Last chance to remove aggregated fields
057                     */
058                    void beforeLayout(StructIO io, List<AggregatedFieldDesc> aggregatedFields);
059                    /**
060                     * This method can alter the aggregated fields and may even call again the performLayout(aggregatedFields) method.
061                     * This is before field offsets and sizes are propagated to field declarations.
062                     */
063                    void afterLayout(StructIO io, List<AggregatedFieldDesc> aggregatedFields);
064                    /**
065                     * Called after everything is setup in the StructIO.<br>
066                     * It is the most dangerous callback, here it's advised to only call the prependBytes, appendBytes and setFieldOffset methods.
067                     */
068                    void afterBuild(StructIO io);
069        }
070        public static class DefaultCustomizer implements Customizer {
071                    public void beforeAggregation(StructIO io, List<FieldDecl> fieldDecls) {}
072                    public void beforeLayout(StructIO io, List<AggregatedFieldDesc> aggregatedFields) {}
073                    public void afterLayout(StructIO io, List<AggregatedFieldDesc> aggregatedFields) {}
074                    public void afterBuild(StructIO io) {}
075        };
076        
077            static Customizer dummyCustomizer = new DefaultCustomizer();
078        static Map<Class, Customizer> customizers = new HashMap<Class, Customizer>();
079        static synchronized Customizer getCustomizer(Class<?> structClass) {
080                    Customizer c = customizers.get(structClass);
081                    if (c == null) {
082                            Struct s = structClass.getAnnotation(Struct.class);
083                            if (s != null) {
084                                    Class<? extends Customizer> customizerClass = s.customizer();
085                                    if (customizerClass != null && customizerClass != Customizer.class) {
086                                            try {
087                                                    c = customizerClass.newInstance();
088                                            } catch (Throwable th) {
089                                                    throw new RuntimeException("Failed to create customizer of class " + customizerClass.getName() + " for struct class " + structClass.getName() + " : " + th, th);
090                                            }
091                                    }
092                            }
093                            if (c == null)
094                                    c = dummyCustomizer;
095                            customizers.put(structClass, c);
096                    }
097                    return c;
098        }
099        public static StructIO getInstance(Type structType) {
100            return getInstance(Utils.getClass(structType), structType);
101        }
102        public static StructIO getInstance(Class structClass, Type structType) {
103            synchronized (structIOs) {
104                StructIO io = structIOs.get(structType == null ? structClass : structType);
105                if (io == null) {
106                    io = new StructIO(structClass, structType);
107                    if (io != null)
108                            registerStructIO(structClass, structType, io);
109                }
110                return (StructIO)io;
111            }
112        }
113    
114        public static synchronized StructIO registerStructIO(Class structClass, Type structType, StructIO io) {
115            structIOs.put(structType, io);
116            return io;
117        }
118    
119        /**
120         * Internal metadata on a struct field
121         */
122        public static class FieldDesc {
123                    public long alignment = -1;
124            public long byteOffset = -1, byteLength = -1;
125                    public long bitOffset, bitLength = -1;
126            public long arrayLength = 1;
127            public boolean isArray, isNativeObject;
128            public Type nativeTypeOrPointerTargetType;
129            public java.lang.reflect.Field field;
130            Type valueType;
131            Method getter;
132            String name;
133            boolean isCLong, isSizeT;
134                    
135            public void offset(long bytes) {
136                            byteOffset += bytes;
137                    }
138            @Override
139            public String toString() {
140                            return "Field(byteOffset = " + byteOffset + ", byteLength = " + byteLength + ", bitOffset = " + bitOffset + ", bitLength = " + bitLength + (nativeTypeOrPointerTargetType == null ? "" : ", ttype = " + nativeTypeOrPointerTargetType) + ")";
141            }
142            }
143            public static class FieldDecl {
144                    final FieldDesc desc = new FieldDesc();
145                    Method setter;
146                    long index = -1, unionWith = -1;//, byteOffset = -1;
147                    Class<?> valueClass;
148            Class<?> declaringClass;
149            boolean isBitField;
150    
151            @Override
152            public String toString() {
153                return desc.name + " (index = " + index + (unionWith < 0 ? "" : ", unionWith = " + unionWith) + ", desc = " + desc + ")";
154            }
155        }
156        public static class AggregatedFieldDesc extends FieldDesc {
157                    public List<FieldDecl> fields = new ArrayList<FieldDecl>();
158        }
159        
160            
161        static class SolidRanges {
162                    long[] offsets, lengths;
163                    static class Builder {
164                            List<Long> offsets = new ArrayList<Long>(), lengths = new ArrayList<Long>();
165                            long lastOffset = -1, nextOffset = 0;
166                            int count;
167                            void add(FieldDesc f) {
168                                    long offset = f.byteOffset;
169                                    long length = f.byteLength;
170                                    
171                                    if (offset == lastOffset) {
172                                            lengths.set(count - 1, Math.max(lengths.get(count - 1), length));       
173                                    } else if (offset == nextOffset && count != 0) {
174                                            lengths.set(count - 1, lengths.get(count - 1) + length);
175                                    } else {
176                                            offsets.add(offset);
177                                            lengths.add(length);
178                                            count++;
179                                    }
180                                    lastOffset = offset;
181                                    nextOffset = offset + length;
182                            }
183                            SolidRanges toSolidRanges() {
184                                    SolidRanges r = new SolidRanges();
185                                    r.offsets = new long[count];
186                                    r.lengths = new long[count];
187                                    for (int i = 0; i < count; i++) {
188                                            r.offsets[i] = offsets.get(i);
189                                            r.lengths[i] = lengths.get(i);
190                                    }
191                                    return r;
192                            }
193                    }
194            
195        }
196            protected PointerIO<?> pointerIO;
197            protected volatile FieldDesc[] fields;
198            private long structSize = -1;
199        private long structAlignment = -1;
200            protected final Class<?> structClass;
201            protected final Type structType;
202            protected boolean hasFieldFields;
203    
204            public void prependBytes(long bytes) {
205                    build();
206                    for (FieldDesc field : fields) {
207                            field.offset(bytes);
208                    }
209                    structSize += bytes;
210            }
211            public void appendBytes(long bytes) {
212                    build();
213                    structSize += bytes;
214            }
215            public void setFieldOffset(String fieldName, long fieldOffset, boolean propagateChanges) {
216                    build();
217    
218                    long propagatedOffset = 0;
219                    for (FieldDesc field : fields) {
220                            if (field.name.equals(fieldName)) {
221                                    propagatedOffset = fieldOffset - field.byteOffset;
222                                    field.offset(propagatedOffset);
223                                    if (!propagateChanges)
224                                            return;
225                                    structSize += propagatedOffset;
226                            } else if (propagateChanges)
227                                    field.offset(propagatedOffset);
228                            return;
229                    }
230            }
231            
232            Customizer customizer;
233        public StructIO(Class<?> structClass, Type structType) {
234                    this.structClass = structClass;
235            this.structType = structType;
236            this.customizer = getCustomizer(structClass);
237            // Don't call build here, for recursive initialization cases (TODO test this)
238            }
239            
240            boolean isVirtual() {
241                    for (Method m : structClass.getMethods()) {
242                            if (m.getAnnotation(Virtual.class) != null)
243                                    return true;
244                    }
245                    return false;
246            }
247            public Class<?> getStructClass() {
248                    return structClass;
249            }
250            public Type getStructType() {
251                    return structType;
252            }
253            
254            @Override
255            public String toString() {
256                    return "StructIO(" + Utils.toString(structType) + ")";
257            }
258            
259            public synchronized PointerIO<?> getPointerIO() {
260                    if (pointerIO == null)
261                            pointerIO = new CommonPointerIOs.StructPointerIO(this);
262                            
263                    return pointerIO;
264            }
265    
266        protected long alignSize(long size, long alignment) {
267            if (alignment > 1) {
268                long r = size % alignment;
269                if (r != 0)
270                    size += alignment - r;
271            }
272            return size;
273        }
274    
275    
276            /// Call whenever an instanceof a struct that depends on that StructIO is created
277            void build() {
278                    if (fields == null) {
279                            synchronized (this) {
280                                    if (fields == null) {
281                                            fields = computeStructLayout();
282                        if (fields.length == 0) {
283                            if (BridJ.verbose)
284                                BridJ.info("No fields found in " + Utils.toString(structType) + " (maybe they weren't declared as public ?)");
285                        }
286                                            customizer.afterBuild(this);
287                                            if (BridJ.debug)
288                                                    BridJ.info(describe());
289                                    }
290                            }
291                    }
292            }
293        
294            public final long getStructSize() {
295                    build();
296                    return structSize;
297            }
298    
299        public final long getStructAlignment() {
300                    build();
301                    return structAlignment;
302            }
303            
304            /**
305             * Orders the fields to match the actual structure layout
306             */
307            protected void orderFields(List<FieldDecl> fields) {
308                Collections.sort(fields, new Comparator<FieldDecl>() {
309            
310                    //@Override
311                    public int compare(FieldDecl o1, FieldDecl o2) {
312                        long d = o1.index - o2.index;
313                        if (d != 0)
314                            return d < 0 ? -1 : d == 0 ? 0 : 1;
315            
316                        if (o1.declaringClass.isAssignableFrom(o2.declaringClass))
317                            return -1;
318                        if (o2.declaringClass.isAssignableFrom(o1.declaringClass))
319                            return 1;
320                        
321                        throw new RuntimeException("Failed to order fields " + o2.desc.name + " and " + o2.desc.name);
322                    }
323            
324                });
325            }
326    
327        protected boolean acceptFieldGetter(Member member, boolean getter) {
328            if ((member instanceof Method) && ((Method)member).getParameterTypes().length != (getter ? 0 : 1))
329                return false;
330            
331            if (((AnnotatedElement)member).getAnnotation(Field.class) == null)
332                return false;
333    
334            int modifiers = member.getModifiers();
335            
336            return //Modifier.isNative(modifiers) && 
337                    !Modifier.isStatic(modifiers);// &&
338                    //!forbiddenGetterNames.contains(method.getName());
339        }
340    
341        protected FieldDecl createFieldDecl(java.lang.reflect.Field getter) {
342            FieldDecl field = createFieldDecl((Member)getter);
343            field.desc.field = getter;
344            field.desc.valueType = getter.getGenericType();
345            field.valueClass = getter.getType();
346            return field;
347        }
348        protected FieldDecl createFieldDecl(Method getter) {
349            FieldDecl field = createFieldDecl((Member)getter);
350            field.desc.getter = getter;
351            field.desc.valueType = getter.getGenericReturnType();
352            field.valueClass = getter.getReturnType();
353            return field;
354        }
355            protected FieldDecl createFieldDecl(Member member) {
356            FieldDecl field = new FieldDecl();
357            field.declaringClass = member.getDeclaringClass();
358    
359            String name = member.getName();
360            if (name.matches("get[A-Z].*"))
361                name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
362    
363            field.desc.name = name;
364    
365            AnnotatedElement getter = (AnnotatedElement)member;
366            Field fil = getter.getAnnotation(Field.class);
367            Bits bits = getter.getAnnotation(Bits.class);
368            Array arr = getter.getAnnotation(Array.class);
369            if (fil != null) {
370                field.index = fil.value();
371                //field.byteOffset = fil.offset();
372                field.unionWith = fil.unionWith();
373            }
374            if (field.unionWith < 0 && field.declaringClass.getAnnotation(Union.class) != null)
375                            field.unionWith = 0;
376                    
377            if (bits != null)
378                field.desc.bitLength = bits.value();
379            if (arr != null) {
380                long length = 1;
381                for (long dim : arr.value())
382                    length *= dim;
383                field.desc.arrayLength = length;
384                field.desc.isArray = true;
385            }
386            field.desc.isCLong = isAnnotationPresent(org.bridj.ann.CLong.class, getter);
387            field.desc.isSizeT = isAnnotationPresent(org.bridj.ann.Ptr.class, getter);
388            return field;
389        }
390    
391        /**
392         * Creates a list of structure fields
393         */
394            protected List<FieldDecl> listFields() {
395                    List<FieldDecl> list = new ArrayList<FieldDecl>();
396                    for (Method method : structClass.getMethods()) {
397                if (acceptFieldGetter(method, true)) {
398                    FieldDecl io = createFieldDecl(method);
399                    try {
400                            Method setter = structClass.getMethod(method.getName(), io.valueClass);
401                            if (acceptFieldGetter(setter, false))
402                                    io.setter = setter;
403                    } catch (Exception ex) {
404                                    //assert BridJ.info("No setter for getter " + method);
405                    }
406                    if (io != null)
407                        list.add(io);
408                }
409            }
410            
411            int nFieldFields = 0;
412            for (java.lang.reflect.Field field : structClass.getFields()) {
413                if (acceptFieldGetter(field, true)) {
414                    FieldDecl io = createFieldDecl(field);
415                    if (io != null) {             
416                        list.add(io);
417                        nFieldFields++;
418                    }
419                }
420            }
421                    if (nFieldFields > 0)
422                            BridJ.warning("Struct " + structClass.getName() + " has " + nFieldFields + " struct fields implemented as Java fields, which won't give the best performance and might require counter-intuitive calls to BridJ.readFromNative / .writeToNative. Please consider using JNAerator to generate your struct instead.");
423                    
424                    return list;
425            }
426            
427            protected static long primTypeAlignment(Class<?> primType, long length) {
428                    if (isDouble(primType) && 
429                            !BridJ.alignDoubles && 
430                            Platform.isLinux() &&
431                            !Platform.is64Bits()) 
432                    {
433                            return 4;
434                    }
435                    return length;
436            }
437            
438            private static boolean isDouble(Class<?> primType) {
439                    return primType == Double.class || primType == double.class;
440            }
441            protected static int primTypeLength(Class<?> primType) {
442            if (primType == Integer.class || primType == int.class)
443                return 4;
444            else if(primType == Long.class || primType == long.class)
445                return 8;
446            else if(primType == Short.class || primType == short.class)
447                return 2;
448            else if(primType == Byte.class || primType == byte.class)
449                return 1;
450            else if(primType == Character.class || primType == char.class)
451                return 2;
452            else if(primType == Boolean.class || primType == boolean.class) {
453                /*
454                BOOL is int, not C++'s bool !
455                if (Platform.isWindows())
456                                    return 4;
457                            else
458                            */ 
459                            return 1;
460            } else if(primType == Float.class || primType == float.class)
461                return 4;
462            else if(isDouble(primType))
463                return 8;
464            else if(Pointer.class.isAssignableFrom(primType))
465                return Pointer.SIZE;
466            else
467                            throw new UnsupportedOperationException("Field type " + primType.getName() + " not supported yet");
468    
469            }
470        private List<AggregatedFieldDesc> aggregatedFields;
471    
472        public List<AggregatedFieldDesc> getAggregatedFields() {
473            build();
474            return aggregatedFields;
475        }
476        
477        
478        
479            protected FieldDesc[] computeStructLayout() {
480                    List<FieldDecl> fieldDecls = listFields();
481                    orderFields(fieldDecls);
482                    
483                    customizer.beforeAggregation(this, fieldDecls);
484                    
485                    Map<Pair<Class<?>, Long>, List<FieldDecl>> fieldsMap = new LinkedHashMap<Pair<Class<?>, Long>, List<FieldDecl>>();
486            for (FieldDecl field : fieldDecls) {
487                            if (field.index < 0)
488                                    throw new RuntimeException("Negative field index not allowed for field " + field.desc.name);
489                            
490                            long index = field.unionWith >= 0 ? field.unionWith : field.index;
491                            Pair<Class<?>, Long> key = new Pair<Class<?>, Long>(field.declaringClass, index);
492                            List<FieldDecl> siblings = fieldsMap.get(key);
493                            if (siblings == null)
494                                    fieldsMap.put(key, siblings = new ArrayList<FieldDecl>());
495                            siblings.add(field);
496            }
497                    
498            Alignment alignment = structClass.getAnnotation(Alignment.class);
499            structAlignment = alignment != null ? alignment.value() : 1; //TODO get platform default alignment
500    
501            aggregatedFields = new ArrayList<AggregatedFieldDesc>();
502            for (List<FieldDecl> fieldGroup : fieldsMap.values()) {
503                            AggregatedFieldDesc aggregatedField = aggregateFields(fieldGroup);
504                            if (aggregatedField != null)
505                                    aggregatedFields.add(aggregatedField);
506                    }
507                    
508                    // Last chance to remove fields :
509                    customizer.beforeLayout(this, aggregatedFields);
510                    
511                    performLayout(aggregatedFields);
512            customizer.afterLayout(this, aggregatedFields);
513                    
514            List<FieldDesc> fieldDescs = new ArrayList<FieldDesc>();
515            SolidRanges.Builder rangesBuilder = new SolidRanges.Builder();
516                    for (AggregatedFieldDesc aggregatedField : aggregatedFields) {
517                            for (FieldDecl field : aggregatedField.fields) {//fieldGroup) {
518                                    // Propagate offsets of aggregated field descs to field decls's descs
519                                    FieldDesc desc = field.desc;
520                                    desc.byteOffset = aggregatedField.byteOffset;
521                                    desc.byteLength = aggregatedField.byteLength;
522                                    desc.bitOffset = aggregatedField.bitOffset;
523                                    
524                                    fieldDescs.add(desc);
525                                    rangesBuilder.add(desc);
526                            
527                                    hasFieldFields = hasFieldFields || desc.field != null;
528                            }
529            }
530            solidRanges = rangesBuilder.toSolidRanges();
531                    return fieldDescs.toArray(new FieldDesc[fieldDescs.size()]);
532            }
533    
534            protected long alignmentOf(Type tpe) {
535              Class c = PointerIO.getClass(tpe);
536              if (StructObject.class.isAssignableFrom(c))
537                return StructIO.getInstance(c).getStructAlignment();
538              return BridJ.sizeOf(tpe);
539            }
540            protected AggregatedFieldDesc aggregateFields(List<FieldDecl> fieldGroup) {
541                    AggregatedFieldDesc aggregatedField = new AggregatedFieldDesc();
542                    boolean isMultiFields = fieldGroup.size() > 1;
543                    aggregatedField.fields.addAll(fieldGroup);
544                    for (FieldDecl field : fieldGroup) {
545                            if (field.valueClass.isArray())
546                                    throw new RuntimeException("Struct fields cannot be array types : please use a combination of Pointer and @Array (for instance, an int[10] is a @Array(10) Pointer<Integer>).");
547                            if (field.valueClass.isPrimitive()) {
548                                    if (field.desc.isCLong)
549                                            field.desc.byteLength = CLong.SIZE;
550                                    else if (field.desc.isSizeT)
551                                            field.desc.byteLength = SizeT.SIZE;
552                                    else {
553                                            field.desc.byteLength = primTypeLength(field.valueClass);
554                                            field.desc.alignment = primTypeAlignment(field.valueClass, field.desc.byteLength);
555                                    }
556                            } else if (field.valueClass == CLong.class) {
557                                    field.desc.byteLength = CLong.SIZE;
558                            } else if (field.valueClass == SizeT.class) {
559                                    field.desc.byteLength = SizeT.SIZE;
560                            } else if (StructObject.class.isAssignableFrom(field.valueClass)) {
561                                    field.desc.nativeTypeOrPointerTargetType = field.desc.valueType;
562                                    StructIO io = StructIO.getInstance(field.valueClass, field.desc.valueType);             
563                                    field.desc.byteLength = io.getStructSize();                             
564                                    field.desc.alignment = io.getStructAlignment();
565                                    field.desc.isNativeObject = true;
566                            } else if (ValuedEnum.class.isAssignableFrom(field.valueClass)) {
567                                    field.desc.nativeTypeOrPointerTargetType = (field.desc.valueType instanceof ParameterizedType) ? PointerIO.getClass(((ParameterizedType)field.desc.valueType).getActualTypeArguments()[0]) : null;
568                                    Class c = PointerIO.getClass(field.desc.nativeTypeOrPointerTargetType);
569                                    if (IntValuedEnum.class.isAssignableFrom(c))
570                                            field.desc.byteLength = 4;
571                                    else
572                                            throw new RuntimeException("Enum type unknown : " + c);
573                                    //field.callIO = CallIO.Utils.createPointerCallIO(field.valueClass, field.desc.valueType);
574                            } else if (TypedPointer.class.isAssignableFrom(field.valueClass)) {
575                                    field.desc.nativeTypeOrPointerTargetType = field.desc.valueType;
576                                    if (field.desc.isArray)
577                                            throw new RuntimeException("Typed pointer field cannot be an array : " + field.desc.name);
578                                    field.desc.byteLength = Pointer.SIZE;
579                                    //field.callIO = CallIO.Utils.createPointerCallIO(field.valueClass, field.desc.valueType);
580                            } else if (Pointer.class.isAssignableFrom(field.valueClass)) {
581                                    Type tpe = (field.desc.valueType instanceof ParameterizedType) ? ((ParameterizedType)field.desc.valueType).getActualTypeArguments()[0] : null;
582                                    if (!(tpe instanceof WildcardType) && !(tpe instanceof TypeVariable))
583                                            field.desc.nativeTypeOrPointerTargetType = tpe;
584                                    if (field.desc.isArray) {
585                                            field.desc.byteLength = BridJ.sizeOf(field.desc.nativeTypeOrPointerTargetType);
586                                            field.desc.alignment = alignmentOf(field.desc.nativeTypeOrPointerTargetType);
587                                    } else
588                                            field.desc.byteLength = Pointer.SIZE;
589                                    //field.callIO = CallIO.Utils.createPointerCallIO(field.valueClass, field.desc.valueType);
590                            } else if (Buffer.class.isAssignableFrom(field.valueClass)) {
591                                    if (field.valueClass == IntBuffer.class)
592                                            field.desc.byteLength = 4;
593                                    else if (field.valueClass == LongBuffer.class)
594                                            field.desc.byteLength = 8;
595                                    else if (field.valueClass == ShortBuffer.class)
596                                            field.desc.byteLength = 2;
597                                    else if (field.valueClass == ByteBuffer.class)
598                                            field.desc.byteLength = 1;
599                                    else if (field.valueClass == FloatBuffer.class)
600                                            field.desc.byteLength = 4;
601                                    else if (field.valueClass == DoubleBuffer.class)
602                                            field.desc.byteLength = 8;
603                                    else
604                                            throw new UnsupportedOperationException("Field array type " + field.valueClass.getName() + " not supported yet");
605                            } else if (field.valueClass.isArray() && field.valueClass.getComponentType().isPrimitive()) {
606                                    field.desc.byteLength = primTypeLength(field.valueClass.getComponentType());
607                                    field.desc.alignment = primTypeAlignment(field.valueClass, field.desc.byteLength);
608                            } else {
609                                    //throw new UnsupportedOperationException("Field type " + field.valueClass.getName() + " not supported yet");
610                                    StructIO io = StructIO.getInstance(field.valueClass, field.desc.valueType);
611                                    long s = io.getStructSize();
612                                    if (s > 0)
613                                            field.desc.byteLength = s;
614                                    else
615                                            throw new UnsupportedOperationException("Field type " + field.valueClass.getName() + " not supported yet");
616                            }
617                            
618                            aggregatedField.alignment = Math.max(
619                                    aggregatedField.alignment, 
620                                    field.desc.alignment >= 0 ?
621                                    field.desc.alignment :
622                                    field.desc.byteLength
623                            );
624                            
625                            long length = field.desc.arrayLength * field.desc.byteLength;
626                            if (length >= aggregatedField.byteLength)
627                                    aggregatedField.byteLength = length;
628                            
629                            if (field.desc.bitLength >= 0) {
630                                    if (isMultiFields)
631                                            throw new RuntimeException("No support for bit fields unions yet !");
632                                    aggregatedField.bitLength = field.desc.bitLength;
633                                    aggregatedField.byteLength = (aggregatedField.bitLength >>> 3) + ((aggregatedField.bitLength & 7) != 0 ? 1 : 0);
634                            }
635                            
636                            //if (!isMultiFields) {
637                            //aggregatedField.isArray = field.desc.isArray;
638                            //aggregatedField.isNativeObject = field.desc.isNativeObject; 
639                            //aggregatedField.nativeTypeOrPointerTargetType = field.desc.nativeTypeOrPointerTargetType;
640                            //aggregatedField.name = field.desc.name;
641                            //aggregatedField.valueType = field.desc.valueType;
642                            //}
643                            //if (fieldDeclaringType == null)
644                            //      fieldDeclaringType = field.declaringClass;
645                            //else if (!fieldDeclaringType.equals(field.declaringClass))
646                            //      throw new RuntimeException("Fields in the same field group must pertain to the same declaring class : " + fieldGroup);
647                            
648                    }
649                    return aggregatedField;
650            }
651            
652            protected void performLayout(Iterable<AggregatedFieldDesc> aggregatedFields) {
653                    structSize = 0;
654                    structAlignment = -1;
655                    
656            Struct s = structClass.getAnnotation(Struct.class);
657            int pack = s != null ? s.pack() : -1;
658            
659            if (isVirtual()) {
660                            structSize += Pointer.SIZE;
661                            if (Pointer.SIZE >= structAlignment)
662                                    structAlignment = Pointer.SIZE;
663                    }
664    
665            int cumulativeBitOffset = 0;
666            
667            for (FieldDesc aggregatedField : aggregatedFields) {
668                structAlignment = Math.max(structAlignment, aggregatedField.alignment);
669                            
670                if (aggregatedField.bitLength < 0) {
671                                    // Align fields as appropriate
672                                    if (cumulativeBitOffset != 0) {
673                                            cumulativeBitOffset = 0;
674                                            structSize++;
675                                    }
676                    //structAlignment = Math.max(structAlignment, aggregatedField.alignment);
677                    structSize = alignSize(structSize, pack > 0 ? pack : aggregatedField.alignment);
678                            }
679                            long 
680                                    fieldByteOffset = structSize, 
681                                    fieldBitOffset = cumulativeBitOffset;
682                            
683                            if (aggregatedField.bitLength >= 0) {
684                                    //fieldByteLength = (aggregatedField.bitLength >>> 3) + ((aggregatedField.bitLength & 7) != 0 ? 1 : 0);
685                    cumulativeBitOffset += aggregatedField.bitLength;
686                                    structSize += cumulativeBitOffset >>> 3;
687                                    cumulativeBitOffset &= 7;
688                            } else {
689                    structSize += aggregatedField.byteLength;
690                            }
691                            
692                            aggregatedField.byteOffset = fieldByteOffset;
693                            aggregatedField.bitOffset = fieldBitOffset;
694            }       
695            
696            if (cumulativeBitOffset > 0)
697                            structSize = alignSize(structSize + 1, structAlignment);
698            else if (structSize > 0)
699                structSize = alignSize(structSize, pack > 0 ? pack : structAlignment);
700    
701            }
702            
703            SolidRanges solidRanges;
704            
705            public boolean equal(StructObject a, StructObject b) {
706                    return compare(a, b) == 0;      
707            }
708            public int compare(StructObject a, StructObject b) {
709                    Pointer<StructObject> pA = pointerTo(a), pB = pointerTo(b);
710                    if (pA == null || pB == null)
711                            return pA != null ? 1 : pB != null ? -1 : 0;
712                    
713                    long[] offsets = solidRanges.offsets, lengths = solidRanges.lengths;
714                    for (int i = 0, n = offsets.length; i < n; i++) {
715                            long offset = offsets[i], length = lengths[i];
716                            int cmp = pA.compareBytesAtOffset(offset, pB, offset, length);
717                            if (cmp != 0)
718                                    return cmp;     
719                    }
720                    return 0;
721            }
722            
723            public final String describe(StructObject struct) {
724                    StringBuilder b = new StringBuilder();
725                    b.append(describe(structType)).append(" { ");
726                    for (FieldDesc fd : fields) {
727                            b.append("\n\t").append(fd.name).append(" = ");
728                            try {
729                                    Object value;
730                                    if (fd.getter != null)
731                                            value = fd.getter.invoke(struct);
732                                    else
733                                            value = fd.field.get(struct);
734                                    
735                                    if (value instanceof String)
736                                            b.append('"').append(value.toString().replaceAll("\"", "\\\"")).append('"');
737                                    else if (value instanceof Character)
738                                            b.append('\'').append(value).append('\'');
739                                    else if (value instanceof NativeObject) {
740                                            String d = BridJ.describe((NativeObject)value);
741                                            b.append(d.replaceAll("\n", "\n\t"));
742                                    } else
743                                            b.append(value);
744                            } catch (Throwable th) {
745                                    if (BridJ.debug)
746                                            th.printStackTrace();
747                                    b.append("?");
748                            }
749                            b.append("; ");
750                    }
751                    b.append("\n}");
752                    return b.toString();
753            }
754            static String describe(Type t) {
755                    if (t == null)
756                            return "?";
757                    if (t instanceof Class)
758                            return ((Class)t).getSimpleName();
759                    return t.toString().
760                            replaceAll("\\bjava\\.lang\\.", "").
761                            replaceAll("\\borg\\.bridj\\.cpp\\.com\\.", "").
762                            replaceAll("\\borg\\.bridj\\.Pointer\\b", "Pointer");
763                            
764            }
765        
766            public final String describe() {
767                    StringBuilder b = new StringBuilder();
768                    b.append("// ");
769                    b.append("size = ").append(structSize).append(", ");
770                    b.append("alignment = ").append(structAlignment);
771                    b.append("\nstruct ");
772                    b.append(describe(structType)).append(" { ");
773                    for (int iField = 0, nFields = fields.length; iField < nFields; iField++) {
774                            FieldDesc fd = fields[iField];
775                            b.append("\n\t");
776                            b.append("@Field(").append(iField).append(") ");
777                            if (fd.isCLong)
778                                    b.append("@CLong ");
779                            else if (fd.isSizeT)
780                                    b.append("@Ptr ");
781                            b.append(describe(fd.valueType)).append(" ").append(fd.name).append("; ");
782                            
783                            b.append("// ");
784                            b.append("offset = ").append(fd.byteOffset).append(", ");
785                            b.append("length = ").append(fd.byteLength).append(", ");
786                            if (fd.bitOffset != 0)
787                                    b.append("bitOffset = ").append(fd.bitOffset).append(", ");
788                            if (fd.bitLength != -1)
789                                    b.append("bitLength = ").append(fd.bitLength).append(", ");
790                            if (fd.arrayLength != 1)
791                                    b.append("arrayLength = ").append(fd.arrayLength).append(", ");
792                            if (fd.alignment != 1)
793                                    b.append("alignment = ").append(fd.alignment);//.append(", ");
794                    }
795                    b.append("\n}");
796                    return b.toString();
797            }
798            
799            /**
800             * Write struct fields implemented as Java fields to the corresponding native memory (Java fields are written to native memory).<br>
801             * This does not concern normal structs as generated by JNAerator (which use getters and setters methods that read and write the fields directly from / to the native memory), but rather structs that are in the JNA style.
802             */
803            public final void writeFieldsToNative(StructObject struct) {
804                if (!hasFieldFields)
805                    return;
806                try {
807                    for (FieldDesc fd : fields) {
808                        if (fd.field == null)
809                            continue;
810                                    
811                        if (fd.isArray)
812                            continue;
813    
814                        Object value = fd.field.get(struct);
815                        if (value instanceof NativeObject) {//fd.isNativeObject) {
816                                    if (value != null) 
817                                            BridJ.writeToNative((NativeObject)value);
818                                    continue;
819                        }
820                        Pointer ptr = struct.peer.offset(fd.byteOffset);
821                        Type tpe = fd.isNativeObject || fd.isArray ? fd.nativeTypeOrPointerTargetType : fd.field.getGenericType();
822                        ptr = ptr.as(tpe);
823                        ptr = fixIntegralTypeIOToMatchLength(ptr, fd.byteLength, fd.arrayLength);
824                        
825                        if (fd.isCLong && CLong.SIZE == 4 || fd.isSizeT && SizeT.SIZE == 4)
826                            value = (int)(long)(Long)value;
827                        
828                        ptr.set(value);
829                    }
830                } catch (Throwable th) {
831                    throw new RuntimeException("Unexpected error while writing fields from struct " + Utils.toString(structType) + " (" + pointerTo(struct) + ")", th);
832                }
833            }
834            static Pointer fixIntegralTypeIOToMatchLength(Pointer ptr, long byteLength, long arrayLength) {
835            long targetSize = ptr.getTargetSize();
836                    if (targetSize * arrayLength == byteLength)
837                            return ptr;
838                    
839                    Type targetType = ptr.getTargetType();
840                    if (!Utils.isSignedIntegral(targetType))
841                return ptr;
842                            //throw new UnsupportedOperationException("Cannot change byte length of non-signed-integral fields (field type = " + Utils.toString(targetType) + ", target size = " + ptr.getTargetSize() + ", byteLength = " + byteLength + ")");
843                    
844                    switch ((int)byteLength) {
845                    case 1:
846                            return ptr.as(byte.class);
847                    case 2:
848                            return ptr.as(short.class);
849                    case 4:
850                            return ptr.as(int.class);
851                    case 8:
852                            return ptr.as(long.class);
853                    default:
854                            return ptr; // this case happens... TODO check // throw new RuntimeException("Invalid integral type byte length : " + byteLength);
855                    }
856            }
857            /**
858             * Read struct fields implemented as Java fields from the corresponding native memory (Java fields are read from native memory).<br>
859             * This does not concern normal structs as generated by JNAerator (which use getters and setters methods that read and write the fields directly from / to the native memory), but rather structs that are in the JNA style.
860             */
861            public final void readFieldsFromNative(StructObject struct) {
862                if (!hasFieldFields)
863                    return;
864                try {
865                    for (FieldDesc fd : fields) {
866                        if (fd.field == null)
867                            continue;
868    
869                        Pointer ptr = struct.peer.offset(fd.byteOffset);
870                        Type tpe = fd.isNativeObject || fd.isArray ? fd.nativeTypeOrPointerTargetType : fd.field.getGenericType();
871                        ptr = ptr.as(tpe);
872                        ptr = fixIntegralTypeIOToMatchLength(ptr, fd.byteLength, fd.arrayLength);
873                        Object value;
874                        if (fd.isArray) {
875                            ptr = ptr.validElements(fd.arrayLength);
876                            value = ptr;
877                        } else {
878                            value = ptr.get();
879                        }
880                        fd.field.set(struct, value);
881                        
882                        if (value instanceof NativeObject) {//if (fd.isNativeObject) {
883                                    if (value != null)
884                                            BridJ.readFromNative((NativeObject)value);
885                        }
886                    }
887                } catch (Throwable th) {
888                    throw new RuntimeException("Unexpected error while reading fields from struct " + Utils.toString(structType) + " (" + pointerTo(struct) + ") : " + th, th);
889                }
890            }
891            public final <T> Pointer<T> getPointerField(StructObject struct, int fieldIndex) {
892            FieldDesc fd = fields[fieldIndex];
893            Pointer<T> p;
894            if (fd.isArray) {
895                            p = struct.peer.offset(fd.byteOffset).as(fd.nativeTypeOrPointerTargetType);
896                            p = p.validElements(fd.arrayLength);
897            } else {
898                            p = struct.peer.getPointerAtOffset(fd.byteOffset, fd.nativeTypeOrPointerTargetType);
899            }
900                    return p;
901            }
902            
903            public final <T> void setPointerField(StructObject struct, int fieldIndex, Pointer<T> value) {
904                    FieldDesc fd = fields[fieldIndex];
905                    struct.peer.setPointerAtOffset(fd.byteOffset, value);
906            }
907            
908            public final <T extends TypedPointer> T getTypedPointerField(StructObject struct, int fieldIndex) {
909                    FieldDesc fd = fields[fieldIndex];
910                    PointerIO<T> pio = PointerIO.getInstance(fd.nativeTypeOrPointerTargetType);
911                    return pio.castTarget(struct.peer.getSizeTAtOffset(fd.byteOffset));
912            }
913            public final <O extends NativeObject> O getNativeObjectField(StructObject struct, int fieldIndex) {
914                    FieldDesc fd = fields[fieldIndex];
915                    return (O)struct.peer.offset(fd.byteOffset).getNativeObject(fd.nativeTypeOrPointerTargetType);
916            }
917    
918            public final <O extends NativeObject> void setNativeObjectField(StructObject struct, int fieldIndex, O value) {
919                    FieldDesc fd = fields[fieldIndex];
920                    struct.peer.offset(fd.byteOffset).setNativeObject(value, fd.nativeTypeOrPointerTargetType);
921            }
922            
923            public final <E extends Enum<E>> IntValuedEnum<E> getEnumField(StructObject struct, int fieldIndex) {
924            FieldDesc fd = fields[fieldIndex];
925                    return FlagSet.fromValue(struct.peer.getIntAtOffset(fd.byteOffset), (Class<E>)fd.nativeTypeOrPointerTargetType);
926            }
927            
928            public final void setEnumField(StructObject struct, int fieldIndex, ValuedEnum<?> value) {
929                    FieldDesc fd = fields[fieldIndex];
930                    struct.peer.setIntAtOffset(fd.byteOffset, (int)value.value());
931            }
932            
933        public final void setIntField(StructObject struct, int fieldIndex, int value) {
934                    FieldDesc fd = fields[fieldIndex];
935                                    if (4 != fd.byteLength)
936                            struct.peer.setSignedIntegralAtOffset(fd.byteOffset, value, fd.byteLength);
937                                    struct.peer.setIntAtOffset(fd.byteOffset, value);
938            }
939            public final int getIntField(StructObject struct, int fieldIndex) {
940                    FieldDesc fd = fields[fieldIndex];
941                                    if (4 != fd.byteLength)
942                            return (int)struct.peer.getSignedIntegralAtOffset(fd.byteOffset, fd.byteLength);
943                                    return struct.peer.getIntAtOffset(fd.byteOffset);
944            }
945        public final void setLongField(StructObject struct, int fieldIndex, long value) {
946                    FieldDesc fd = fields[fieldIndex];
947                                    if (8 != fd.byteLength)
948                            struct.peer.setSignedIntegralAtOffset(fd.byteOffset, value, fd.byteLength);
949                                    struct.peer.setLongAtOffset(fd.byteOffset, value);
950            }
951            public final long getLongField(StructObject struct, int fieldIndex) {
952                    FieldDesc fd = fields[fieldIndex];
953                                    if (8 != fd.byteLength)
954                            return (long)struct.peer.getSignedIntegralAtOffset(fd.byteOffset, fd.byteLength);
955                                    return struct.peer.getLongAtOffset(fd.byteOffset);
956            }
957        public final void setShortField(StructObject struct, int fieldIndex, short value) {
958                    FieldDesc fd = fields[fieldIndex];
959                                    if (2 != fd.byteLength)
960                            struct.peer.setSignedIntegralAtOffset(fd.byteOffset, value, fd.byteLength);
961                                    struct.peer.setShortAtOffset(fd.byteOffset, value);
962            }
963            public final short getShortField(StructObject struct, int fieldIndex) {
964                    FieldDesc fd = fields[fieldIndex];
965                                    if (2 != fd.byteLength)
966                            return (short)struct.peer.getSignedIntegralAtOffset(fd.byteOffset, fd.byteLength);
967                                    return struct.peer.getShortAtOffset(fd.byteOffset);
968            }
969        public final void setByteField(StructObject struct, int fieldIndex, byte value) {
970                    FieldDesc fd = fields[fieldIndex];
971                                    if (1 != fd.byteLength)
972                            struct.peer.setSignedIntegralAtOffset(fd.byteOffset, value, fd.byteLength);
973                                    struct.peer.setByteAtOffset(fd.byteOffset, value);
974            }
975            public final byte getByteField(StructObject struct, int fieldIndex) {
976                    FieldDesc fd = fields[fieldIndex];
977                                    if (1 != fd.byteLength)
978                            return (byte)struct.peer.getSignedIntegralAtOffset(fd.byteOffset, fd.byteLength);
979                                    return struct.peer.getByteAtOffset(fd.byteOffset);
980            }
981        public final void setCharField(StructObject struct, int fieldIndex, char value) {
982                    FieldDesc fd = fields[fieldIndex];
983                                    struct.peer.setCharAtOffset(fd.byteOffset, value);
984            }
985            public final char getCharField(StructObject struct, int fieldIndex) {
986                    FieldDesc fd = fields[fieldIndex];
987                                    return struct.peer.getCharAtOffset(fd.byteOffset);
988            }
989        public final void setFloatField(StructObject struct, int fieldIndex, float value) {
990                    FieldDesc fd = fields[fieldIndex];
991                                    struct.peer.setFloatAtOffset(fd.byteOffset, value);
992            }
993            public final float getFloatField(StructObject struct, int fieldIndex) {
994                    FieldDesc fd = fields[fieldIndex];
995                                    return struct.peer.getFloatAtOffset(fd.byteOffset);
996            }
997        public final void setDoubleField(StructObject struct, int fieldIndex, double value) {
998                    FieldDesc fd = fields[fieldIndex];
999                                    struct.peer.setDoubleAtOffset(fd.byteOffset, value);
1000            }
1001            public final double getDoubleField(StructObject struct, int fieldIndex) {
1002                    FieldDesc fd = fields[fieldIndex];
1003                                    return struct.peer.getDoubleAtOffset(fd.byteOffset);
1004            }
1005        public final void setBooleanField(StructObject struct, int fieldIndex, boolean value) {
1006                    FieldDesc fd = fields[fieldIndex];
1007                                    struct.peer.setBooleanAtOffset(fd.byteOffset, value);
1008            }
1009            public final boolean getBooleanField(StructObject struct, int fieldIndex) {
1010                    FieldDesc fd = fields[fieldIndex];
1011                                    return struct.peer.getBooleanAtOffset(fd.byteOffset);
1012            }
1013    
1014        public final void setSizeTField(StructObject struct, int fieldIndex, long value) {
1015                    FieldDesc fd = fields[fieldIndex];
1016                    struct.peer.setSizeTAtOffset(fd.byteOffset, value);
1017            }
1018            public final long getSizeTField(StructObject struct, int fieldIndex) {
1019                    FieldDesc fd = fields[fieldIndex];
1020                    return struct.peer.getSizeTAtOffset(fd.byteOffset);
1021            }
1022        public final void setCLongField(StructObject struct, int fieldIndex, long value) {
1023                    FieldDesc fd = fields[fieldIndex];
1024                    struct.peer.setCLongAtOffset(fd.byteOffset, value);
1025            }
1026            public final long getCLongField(StructObject struct, int fieldIndex) {
1027                    FieldDesc fd = fields[fieldIndex];
1028                    return struct.peer.getCLongAtOffset(fd.byteOffset);
1029            }
1030    }