View Javadoc

1   /* This library is free software; you can redistribute it and/or
2    * modify it under the terms of the GNU Lesser General Public
3    * License as published by the Free Software Foundation; either
4    * version 2.1 of the License, or (at your option) any later version.
5    * <p/>
6    * This library is distributed in the hope that it will be useful,
7    * but WITHOUT ANY WARRANTY; without even the implied warranty of
8    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9    * Lesser General Public License for more details.
10   */
11  package com.sun.jna;
12  
13  import java.lang.reflect.Array;
14  import java.lang.reflect.Field;
15  import java.lang.reflect.Modifier;
16  import java.nio.Buffer;
17  import java.util.AbstractCollection;
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.WeakHashMap;
29  
30  /**
31   * Represents a native structure with a Java peer class.  When used as a
32   * function parameter or return value, this class corresponds to
33   * <code>struct*</code>.  When used as a field within another
34   * <code>Structure</code>, it corresponds to <code>struct</code>.  The
35   * tagging interfaces {@link ByReference} and {@link ByValue} may be used
36   * to alter the default behavior.
37   * <p>
38   * See the <a href={@docRoot}/overview-summary.html>overview</a> for supported
39   * type mappings.
40   * <p>
41   * Structure alignment and type mappings are derived by default from the
42   * enclosing interface definition (if any) by using
43   * {@link Native#getStructureAlignment} and {@link Native#getTypeMapper}.
44   * <p>
45   * Structure fields corresponding to native fields <em>must</em> be public.
46   * The may additionally have the following modifiers:<br>
47   * <ul>
48   * <li><code>volatile</code> JNA will not write the field unless specifically
49   * instructed to do so via {@link #writeField(String)}.
50   * <li><code>final</code> JNA will overwrite the field via {@link #read()},
51   * but otherwise the field is not modifiable from Java.  Take care when using
52   * this option, since the compiler will usually assume <em>all</em> accesses
53   * to the field (for a given Structure instance) have the same value.
54   * </ul>
55   * NOTE: Strings are used to represent native C strings because usage of
56   * <code>char *</code> is generally more common than <code>wchar_t *</code>.
57   * <p>
58   * NOTE: This class assumes that fields are returned in {@link Class#getFields}
59   * in the same or reverse order as declared.  If your VM returns them in
60   * no particular order, you're out of luck.
61   *
62   * @author  Todd Fast, todd.fast@sun.com
63   * @author twall@users.sf.net
64   */
65  public abstract class Structure {
66      
67      /** Tagging interface to indicate the value of an instance of the
68       * <code>Structure</code> type is to be used in function invocations rather
69       * than its address.  The default behavior is to treat
70       * <code>Structure</code> function parameters and return values as by
71       * reference, meaning the address of the structure is used.
72       */
73      public interface ByValue { }
74      /** Tagging interface to indicate the address of an instance of the
75       * Structure type is to be used within a <code>Structure</code> definition
76       * rather than nesting the full Structure contents.  The default behavior
77       * is to inline <code>Structure</code> fields.
78       */
79      public interface ByReference { }
80  
81      private static class MemberOrder {
82          public int first;
83          public int middle;
84          public int last;
85      }
86  
87      private static final boolean REVERSE_FIELDS;
88      static boolean REQUIRES_FIELD_ORDER;
89  
90      static final boolean isPPC;
91      static final boolean isSPARC;
92  
93      static {
94          // IBM and JRockit store fields in reverse order; check for it
95          Field[] fields = MemberOrder.class.getFields();
96          REVERSE_FIELDS = "last".equals(fields[0].getName());
97          REQUIRES_FIELD_ORDER = !"middle".equals(fields[1].getName());
98          String arch = System.getProperty("os.arch").toLowerCase();
99          isPPC = "ppc".equals(arch) || "powerpc".equals(arch);
100         isSPARC = "sparc".equals(arch);
101     }
102 
103     /** Use the platform default alignment. */
104     public static final int ALIGN_DEFAULT = 0;
105     /** No alignment, place all fields on nearest 1-byte boundary */
106     public static final int ALIGN_NONE = 1;
107     /** validated for 32-bit x86 linux/gcc; align field size, max 4 bytes */
108     public static final int ALIGN_GNUC = 2;
109     /** validated for w32/msvc; align on field size */
110     public static final int ALIGN_MSVC = 3;
111 
112     private static final int MAX_GNUC_ALIGNMENT = isSPARC ? 8 : Native.LONG_SIZE;
113     protected static final int CALCULATE_SIZE = -1;
114 
115     // This field is accessed by native code
116     private Pointer memory;
117     private int size = CALCULATE_SIZE;
118     private int alignType;
119     private int structAlignment;
120     private final Map structFields = new LinkedHashMap();
121     // Keep track of java strings which have been converted to C strings
122     private final Map nativeStrings = new HashMap();
123     private TypeMapper typeMapper;
124     // This field is accessed by native code
125     private long typeInfo;
126     private List fieldOrder;
127     private boolean autoRead = true;
128     private boolean autoWrite = true;
129     private Structure[] array;
130 
131     protected Structure() {
132         this((Pointer)null);
133     }
134 
135     protected Structure(TypeMapper mapper) {
136         this((Pointer)null, ALIGN_DEFAULT, mapper);
137     }
138 
139     /** Create a structure cast onto pre-allocated memory. */
140     protected Structure(Pointer p) {
141         this(p, ALIGN_DEFAULT);
142     }
143 
144     protected Structure(Pointer p, int alignment) {
145         this(p, alignment, null);
146     }
147 
148     protected Structure(Pointer p, int alignment, TypeMapper mapper) {
149         setAlignType(alignment);
150         setTypeMapper(mapper);
151         if (p != null) {
152             useMemory(p);
153         }
154         else {
155             allocateMemory(CALCULATE_SIZE);
156         }
157     }
158 
159     /** Return all fields in this structure (ordered). */
160     Map fields() {
161         return structFields;
162     }
163 
164     /** Change the type mapping for this structure.  May cause the structure
165      * to be resized and any existing memory to be reallocated.
166      * If <code>null</code>, the default mapper for the
167      * defining class will be used.
168      */
169     protected void setTypeMapper(TypeMapper mapper) {
170         if (mapper == null) {
171             Class declaring = getClass().getDeclaringClass();
172             if (declaring != null) {
173                 mapper = Native.getTypeMapper(declaring);
174             }
175         }
176         this.typeMapper = mapper;
177         this.size = CALCULATE_SIZE;
178         if (this.memory instanceof AutoAllocated) {
179             this.memory = null;
180         }
181     }
182 
183     /** Change the alignment of this structure.  Re-allocates memory if
184      * necessary.  If alignment is {@link #ALIGN_DEFAULT}, the default
185      * alignment for the defining class will be used.
186      */
187     protected void setAlignType(int alignType) {
188         if (alignType == ALIGN_DEFAULT) {
189             Class declaring = getClass().getDeclaringClass();
190             if (declaring != null)
191                 alignType = Native.getStructureAlignment(declaring);
192             if (alignType == ALIGN_DEFAULT) {
193                 if (Platform.isWindows())
194                     alignType = ALIGN_MSVC;
195                 else
196                     alignType = ALIGN_GNUC;
197             }
198         }
199         this.alignType = alignType;
200         this.size = CALCULATE_SIZE;
201         if (this.memory instanceof AutoAllocated) {
202             this.memory = null;
203         }
204     }
205 
206     /** Set the memory used by this structure.  This method is used to
207      * indicate the given structure is nested within another or otherwise
208      * overlaid on some other memory block and thus does not own its own
209      * memory.
210      */
211     protected void useMemory(Pointer m) {
212 		useMemory(m, 0);
213     }
214 
215     /** Set the memory used by this structure.  This method is used to
216      * indicate the given structure is nested within another or otherwise
217      * overlaid on some other memory block and thus does not own its own
218      * memory.
219      */
220     protected void useMemory(Pointer m, int offset) {
221         // Invoking size() here is important when this method is invoked
222         // from the ctor, to ensure fields are properly scanned and allocated
223         try {
224             this.memory = m.share(offset, size());
225             this.array = null;
226         }
227         catch(IndexOutOfBoundsException e) {
228             throw new IllegalArgumentException("Structure exceeds provided memory bounds");
229         }
230     }
231 
232     protected void ensureAllocated() {
233         if (size == CALCULATE_SIZE) {
234             allocateMemory();
235         }
236     }
237 
238     /** Attempt to allocate memory if sufficient information is available.
239      * Returns whether the operation was successful.
240      */
241     protected void allocateMemory() {
242         allocateMemory(calculateSize(true));
243     }
244 
245     /** Provided for derived classes to indicate a different
246      * size than the default.  Returns whether the operation was successful.
247      * Will leave memory untouched if it is non-null and not allocated
248      * by this class.
249      */
250     protected void allocateMemory(int size) {
251         if (size == CALCULATE_SIZE) {
252             // Analyze the struct, but don't worry if we can't yet do it
253             size = calculateSize(false);
254         }
255         else if (size <= 0) {
256             throw new IllegalArgumentException("Structure size must be greater than zero: " + size);
257         }
258         // May need to defer size calculation if derived class not fully
259         // initialized
260         if (size != CALCULATE_SIZE) {
261             if (this.memory == null 
262                 || this.memory instanceof AutoAllocated) {
263                 this.memory = new AutoAllocated(size);
264                 // Always clear new structure memory
265                 this.memory.clear(size);
266             }
267             this.size = size;
268         }
269     }
270 
271     public int size() {
272         ensureAllocated();
273         return size;
274     }
275 
276     public void clear() {
277         memory.clear(size());
278     }
279 
280     /** Return a {@link Pointer} object to this structure.  Note that if you
281      * use the structure's pointer as a function argument, you are responsible
282      * for calling {@link #write()} prior to the call and {@link #read()}
283      * after the call.  These calls are normally handled automatically by the
284      * {@link Function} object when it encounters a {@link Structure} argument
285      * or return value.
286      */
287     public Pointer getPointer() {
288         ensureAllocated();
289         return memory;
290     }
291 
292     //////////////////////////////////////////////////////////////////////////
293     // Data synchronization methods
294     //////////////////////////////////////////////////////////////////////////
295 
296     // Keep track of what is currently being read/written to avoid redundant
297     // reads (avoids problems with circular references).
298     private static final ThreadLocal busy = new ThreadLocal() {
299         /** Avoid using a hash-based implementation since the hash code
300             will change if structure field values change.
301         */
302         class StructureSet extends AbstractCollection implements Set {
303             private Structure[] elements;
304             private int count;
305             private void ensureCapacity(int size) {
306                 if (elements == null) {
307                     elements = new Structure[size*3/2];
308                 }
309                 else if (elements.length < size) {
310                     Structure[] e = new Structure[size*3/2];
311                     System.arraycopy(elements, 0, e, 0, elements.length);
312                     elements = e;
313                 }
314             }
315             public int size() { return count; }
316             public boolean contains(Object o) {
317                 return indexOf(o) != -1;
318             }
319             public boolean add(Object o) {
320                 if (!contains(o)) {
321                     ensureCapacity(count+1);
322                     elements[count++] = (Structure)o;
323                 }
324                 return true;
325             }
326             private int indexOf(Object o) {
327                 Structure s1 = (Structure)o;
328                 for (int i=0;i < count;i++) {
329                     Structure s2 = (Structure)elements[i];
330                     if (s1 == s2
331                         || (s1.baseClass() == s2.baseClass()
332                             && s1.size() == s2.size()
333                             && s1.getPointer().equals(s2.getPointer()))) {
334                         return i;
335                     }
336                 }
337                 return -1;
338             }
339             public boolean remove(Object o) {
340                 int idx = indexOf(o);
341                 if (idx != -1) {
342                     if (--count > 0) {
343                         elements[idx] = elements[count];
344                         elements[count] = null;
345                     }
346                     return true;
347                 }
348                 return false;
349             }
350             public Iterator iterator() {
351                 // never actually used
352                 return null;
353             }
354         }
355         protected synchronized Object initialValue() {
356             return new StructureSet();
357         }
358     };
359     Set busy() {
360         return (Set)busy.get();
361     }
362 
363     /**
364      * Reads the fields of the struct from native memory
365      */
366     public void read() {
367         // convenience: allocate memory if it hasn't been already; this
368         // allows structures to do field-based initialization of arrays and not
369         // have to explicitly call allocateMemory in a ctor
370         ensureAllocated();
371         // Avoid recursive reads
372         if (busy().contains(this)) {
373             return;
374         }
375         busy().add(this);
376         try {
377             for (Iterator i=structFields.values().iterator();i.hasNext();) {
378                 readField((StructField)i.next());
379             }
380         }
381         finally {
382             busy().remove(this);
383         }
384     }
385 
386     /** Force a read of the given field from native memory.  The Java field
387      * will be updated from the current contents of native memory.
388      * @return the new field value, after updating
389      * @throws IllegalArgumentException if no field exists with the given name
390      */
391     public Object readField(String name) {
392         ensureAllocated();
393         StructField f = (StructField)structFields.get(name);
394         if (f == null)
395             throw new IllegalArgumentException("No such field: " + name);
396         return readField(f);
397     }
398 
399     /** Obtain the value currently in the Java field.  Does not read from
400      * memory.
401      */
402     Object getField(StructField structField) {
403         try {
404             return structField.field.get(this);
405         }
406         catch (Exception e) {
407             throw new Error("Exception reading field '"
408                             + structField.name + "' in " + getClass()
409                             + ": " + e);
410         }
411     }
412 
413     void setField(StructField structField, Object value) {
414         try {
415             structField.field.set(this, value);
416         }
417         catch(IllegalAccessException e) {
418             throw new Error("Unexpectedly unable to write to field '"
419                             + structField.name + "' within " + getClass()
420                             + ": " + e);
421         }
422     }
423 
424     /** Only keep the original structure if its native address is unchanged.
425      * Otherwise replace it with a new object.
426      * @param type Structure subclass
427      * @param s Original Structure object
428      * @param address the native <code>struct *</code>
429      * @return Updated <code>Structure.ByReference</code> object
430      */
431     static Structure updateStructureByReference(Class type, Structure s, Pointer address) {
432         if (address == null) {
433             s = null;
434         }
435         else {
436             if (s == null || !address.equals(s.getPointer())) {
437                 s = newInstance(type);
438                 s.useMemory(address);
439             }
440             s.autoRead();
441         }
442         return s;
443     }
444 
445     /** Read the given field and return its value.  The Java field will be
446      * updated from the contents of native memory.
447      */
448     // TODO: make overridable method with calculated native type, offset, etc
449     Object readField(StructField structField) {
450 
451         // Get the offset of the field
452         int offset = structField.offset;
453 
454         // Determine the type of the field
455         Class fieldType = structField.type;
456         FromNativeConverter readConverter = structField.readConverter;
457         if (readConverter != null) {
458             fieldType = readConverter.nativeType();
459         }
460         // Get the current value only for types which might need to be preserved
461         Object currentValue = (Structure.class.isAssignableFrom(fieldType)
462                                || Callback.class.isAssignableFrom(fieldType)
463                                || Buffer.class.isAssignableFrom(fieldType)
464                                || Pointer.class.isAssignableFrom(fieldType)
465                                || fieldType.isArray())
466             ? getField(structField) : null;
467         Object result = memory.getValue(offset, structField.bitOffset, structField.bits, fieldType, currentValue);
468 		// TODO: process against current value here
469                 
470         if (readConverter != null) {
471             result = readConverter.fromNative(result, structField.context);
472         }
473 
474         // Update the value on the field
475         setField(structField, result);
476         return result;
477     }
478 
479     /**
480      * Writes the fields of the struct to native memory
481      */
482     public void write() {
483         // convenience: allocate memory if it hasn't been already; this
484         // allows structures to do field-based initialization of arrays and not
485         // have to explicitly call allocateMemory in a ctor
486         ensureAllocated();
487 
488         // Update native FFI type information, if needed
489         if (this instanceof ByValue) {
490             getTypeInfo();
491         }
492 
493         if (busy().contains(this)) {
494             return;
495         }
496         busy().add(this);
497         try {
498             // Write all fields, except those marked 'volatile'
499             for (Iterator i=structFields.values().iterator();i.hasNext();) {
500                 StructField sf = (StructField)i.next();
501                 if (!sf.isVolatile) {
502                     writeField(sf);
503                 }
504             }
505         }
506         finally {
507             busy().remove(this);
508         }
509     }
510 
511     /** Write the given field to native memory.  The current value in the Java
512      * field will be translated into native memory.
513      * @throws IllegalArgumentException if no field exists with the given name
514      */
515     public void writeField(String name) {
516         ensureAllocated();
517         StructField f = (StructField)structFields.get(name);
518         if (f == null)
519             throw new IllegalArgumentException("No such field: " + name);
520         writeField(f);
521     }
522 
523     /** Write the given field value to the field and native memory.   The
524      * given value will be written both to the Java field and the
525      * corresponding native memory.
526      * @throws IllegalArgumentException if no field exists with the given name
527      */
528     public void writeField(String name, Object value) {
529         ensureAllocated();
530         StructField f = (StructField)structFields.get(name);
531         if (f == null)
532             throw new IllegalArgumentException("No such field: " + name);
533         setField(f, value);
534         writeField(f);
535     }
536 
537     void writeField(StructField structField) {
538 
539         if (structField.isReadOnly) 
540             return;
541 
542         // Get the offset of the field
543         int offset = structField.offset;
544 
545         // Get the value from the field
546         Object value = getField(structField);
547 
548         // Determine the type of the field
549         Class fieldType = structField.type;
550         ToNativeConverter converter = structField.writeConverter;
551         if (converter != null) {
552             value = converter.toNative(value, new StructureWriteContext(this, structField.field));
553             fieldType = converter.nativeType();
554         }
555 
556         // Java strings get converted to C strings, where a Pointer is used
557         if (String.class == fieldType
558             || WString.class == fieldType) {
559 
560             // Allocate a new string in memory
561             boolean wide = fieldType == WString.class;
562             if (value != null) {
563                 NativeString nativeString = new NativeString(value.toString(), wide);
564                 // Keep track of allocated C strings to avoid
565                 // premature garbage collection of the memory.
566                 nativeStrings.put(structField.name, nativeString);
567                 value = nativeString.getPointer();
568             }
569             else {
570                 value = null;
571                 nativeStrings.remove(structField.name);
572             }
573         }
574 
575 		try {
576             memory.setValue(offset, structField.bitOffset, structField.bits, value, fieldType);
577         }
578         catch(IllegalArgumentException e) {
579             e.printStackTrace();
580             String msg = "Structure field \"" + structField.name
581                 + "\" was declared as " + structField.type
582                 + (structField.type == fieldType
583                    ? "" : " (native type " + fieldType + ")")
584                 + ", which is not supported within a Structure";
585             throw new IllegalArgumentException(msg);
586         }
587     }
588 
589     protected List getFieldOrder() {
590         synchronized(this) {
591             if (fieldOrder == null) {
592                 fieldOrder = new ArrayList();
593             }
594             return fieldOrder;
595         }
596     }
597 
598     /** Provided for VMs where the field order as returned by {@link
599      * Class#getFields()} is not predictable.
600      */
601     protected void setFieldOrder(String[] fields) {
602         getFieldOrder().addAll(Arrays.asList(fields));
603     }
604 
605     /** Sort the structure fields according to the given array of names. */
606     protected void sortFields(Field[] fields, String[] names) {
607         for (int i=0;i < names.length;i++) {
608             for (int f=i;f < fields.length;f++) {
609                 if (names[i].equals(fields[f].getName())) {
610                     Field tmp = fields[f];
611                     fields[f] = fields[i];
612                     fields[i] = tmp;
613                     break;
614                 }
615             }
616         }
617     }
618 
619     /** Calculate the amount of native memory required for this structure.
620      * May return {@link #CALCULATE_SIZE} if the size can not yet be
621      * determined (usually due to fields in the derived class not yet
622      * being initialized).
623      * <p>
624      * If the <code>force</code> parameter is <code>true</code> will throw
625      * an {@link IllegalStateException} if the size can not be determined.
626      * @throws IllegalStateException an array field is not initialized
627      * @throws IllegalArgumentException when an unsupported field type is
628      * encountered
629      */
630     int calculateSize(boolean force) {
631         // TODO: maybe cache this information on a per-class basis
632         // so that we don't have to re-analyze this static information each
633         // time a struct is allocated.
634 
635         structAlignment = 1;
636         int calculatedSize = 0;
637         Field[] fields = getClass().getFields();
638         // Restrict to valid fields
639         List flist = new ArrayList();
640         for (int i=0;i < fields.length;i++) {
641             int modifiers = fields[i].getModifiers();
642             if (Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers))
643                 continue;
644             flist.add(fields[i]);
645         }
646         fields = (Field[])flist.toArray(new Field[flist.size()]);
647 
648         if (REVERSE_FIELDS) {
649             for (int i=0;i < fields.length/2;i++) {
650                 int idx = fields.length-1-i;
651                 Field tmp = fields[i];
652                 fields[i] = fields[idx];
653                 fields[idx] = tmp;
654             }
655         }
656         else if (REQUIRES_FIELD_ORDER) {
657             List fieldOrder = getFieldOrder();
658             if (fieldOrder.size() < fields.length) {
659                 if (force) {
660                     throw new Error("This VM does not store fields in a predictable order; you must use setFieldOrder: " + System.getProperty("java.vendor") + ", " + System.getProperty("java.version"));
661                 }
662                 return CALCULATE_SIZE;
663             }
664             sortFields(fields, (String[])fieldOrder.toArray(new String[fieldOrder.size()]));
665         }
666 		
667 		int cumulativeBitOffset = 0;
668         for (int i=0; i<fields.length; i++) {
669             Field field = fields[i];
670             int modifiers = field.getModifiers();
671 
672 			if (Modifier.isTransient(modifiers))
673                 continue;
674 			
675             Class type = field.getType();
676             StructField structField = new StructField();
677             structField.isVolatile = Modifier.isVolatile(modifiers);
678             structField.isReadOnly = Modifier.isFinal(modifiers);
679             if (Modifier.isFinal(modifiers)) {
680                 field.setAccessible(true);
681             }
682             structField.field = field;
683             structField.name = field.getName();
684             structField.type = type;
685 
686             // Check for illegal field types
687             if (Callback.class.isAssignableFrom(type) && !type.isInterface()) {
688                 throw new IllegalArgumentException("Structure Callback field '"
689                                                    + field.getName()
690                                                    + "' must be an interface");
691             }
692             if (type.isArray()
693                 && Structure.class.equals(type.getComponentType())) {
694                 String msg = "Nested Structure arrays must use a "
695                     + "derived Structure type so that the size of "
696                     + "the elements can be determined";
697                 throw new IllegalArgumentException(msg);
698             }
699 
700             int fieldAlignment = 1;
701             if (!Modifier.isPublic(field.getModifiers()))
702                 continue;
703 
704             Object value = getField(structField);
705             if (value == null) {
706                 if (Structure.class.isAssignableFrom(type)
707                     && !(ByReference.class.isAssignableFrom(type))) {
708                     try {
709                         value = newInstance(type);
710                         setField(structField, value);
711                     }
712                     catch(IllegalArgumentException e) {
713                         String msg = "Can't determine size of nested structure: "
714                             + e.getMessage();
715                         throw new IllegalArgumentException(msg);
716                     }
717                 }
718                 else if (type.isArray()) {
719                     // can't calculate size yet, defer until later
720                     if (force) {
721                         throw new IllegalStateException("Array fields must be initialized");
722                     }
723                     return CALCULATE_SIZE;
724                 }
725             }
726             Class nativeType = type;
727             if (NativeMapped.class.isAssignableFrom(type)) {
728                 NativeMappedConverter tc = NativeMappedConverter.getInstance(type);
729                 if (value == null) {
730                     value = tc.defaultValue();
731                     setField(structField, value);
732                 }
733                 nativeType = tc.nativeType();
734                 structField.writeConverter = tc;
735                 structField.readConverter = tc;
736                 structField.context = new StructureReadContext(this, field);
737             }
738             else if (typeMapper != null) {
739                 ToNativeConverter writeConverter = typeMapper.getToNativeConverter(type);
740                 FromNativeConverter readConverter = typeMapper.getFromNativeConverter(type);
741                 if (writeConverter != null && readConverter != null) {
742                     value = writeConverter.toNative(value,
743                                                     new StructureWriteContext(this, structField.field));
744                     nativeType = value != null ? value.getClass() : Pointer.class;
745                     structField.writeConverter = writeConverter;
746                     structField.readConverter = readConverter;
747                     structField.context = new StructureReadContext(this, field);
748                 }
749                 else if (writeConverter != null || readConverter != null) {
750                     String msg = "Structures require bidirectional type conversion for " + type;
751                     throw new IllegalArgumentException(msg);
752                 }
753             }
754             try {
755                 structField.size = Native.getNativeSize(nativeType, value);
756                 fieldAlignment = getNativeAlignment(nativeType, value, i==0);
757             }
758             catch(IllegalArgumentException e) {
759                 // Might simply not yet have a type mapper set
760                 if (!force && typeMapper == null) {
761                     return CALCULATE_SIZE;
762                 }
763                 String msg = "Invalid Structure field in " + getClass() + ", field name '" + structField.name + "', " + structField.type + ": " + e.getMessage();
764                 throw new IllegalArgumentException(msg);
765             }
766 
767 			Integer bits = getBitsAnnotation(field);
768 			if (bits == null || i == 0) {
769 				// Align fields as appropriate
770 				if (cumulativeBitOffset != 0) {
771 					cumulativeBitOffset = 0;
772 					calculatedSize++;
773 				}
774 				structAlignment = Math.max(structAlignment, fieldAlignment);
775 				if ((calculatedSize % fieldAlignment) != 0) {
776 					calculatedSize += fieldAlignment - (calculatedSize % fieldAlignment);
777 				}
778 			}
779 			structField.offset = calculatedSize;
780 			structField.bitOffset = cumulativeBitOffset;
781 			
782 			if (bits != null) {
783 				int nBits = bits.intValue();
784 				structField.bits = nBits;
785 				structField.size = (nBits >>> 3) + ((nBits & 7) != 0 ? 1 : 0);
786                 cumulativeBitOffset += nBits;
787 				calculatedSize += cumulativeBitOffset >>> 3;
788 				cumulativeBitOffset &= 7;
789 			} else {
790 				calculatedSize += structField.size;
791 			}
792 			
793             // Save the field in our list
794             structFields.put(structField.name, structField);
795         }
796 
797 		if (cumulativeBitOffset > 0)
798 			calculatedSize += calculateAlignedSize(calculatedSize + 1);
799 		
800         if (calculatedSize > 0) {
801             int size = calculateAlignedSize(calculatedSize);
802             // Update native FFI type information, if needed
803             if (this instanceof ByValue) {
804                 getTypeInfo();
805             }
806             return size;
807         }
808 
809         throw new IllegalArgumentException("Structure " + getClass()
810                                            + " has unknown size (ensure "
811                                            + "all fields are public)");
812     }
813 
814 	/**
815 	 * Override this in a subclass to support bit fields (@link http://en.wikipedia.org/wiki/C_syntax#Bit_fields) : 
816 	 * <code>
817 	 *	package com.sun.jna;
818 	 *	import java.lang.annotation.*;
819 	 *	
820 	 * 	@Retention(RetentionPolicy.RUNTIME)
821 	 *	@Target( {ElementType.FIELD} )
822 	 *	public @interface Bits {
823 	 *		int value();
824 	 *	}
825 	 * </code>
826 	 * <code>
827 	 *	@Override
828 	 *	protected Integer getBitsAnnotation(Field field) {
829 	 *		Bits bits = field.getAnnotation(Bits.class);
830 	 *		return bits == null ? null : bits.value();
831 	 *	}
832      * </code>
833 	 */
834 	protected Integer getBitsAnnotation(Field field) {
835 		return null;
836 	}
837 	
838     int calculateAlignedSize(int calculatedSize) {
839         // Structure size must be an integral multiple of its alignment,
840         // add padding if necessary.
841         if (alignType != ALIGN_NONE) {
842             if ((calculatedSize % structAlignment) != 0) {
843                 calculatedSize += structAlignment - (calculatedSize % structAlignment);
844             }
845         }
846         return calculatedSize;
847     }
848 
849     protected int getStructAlignment() {
850         if (size == CALCULATE_SIZE) {
851             // calculate size, but don't allocate memory
852             calculateSize(true);
853         }
854         return structAlignment;
855     }
856 
857     /** Overridable in subclasses. */
858     // TODO: write getNaturalAlignment(stack/alloc) + getEmbeddedAlignment(structs)
859     // TODO: move this into a native call which detects default alignment
860     // automatically
861     protected int getNativeAlignment(Class type, Object value, boolean isFirstElement) {
862         int alignment = 1;
863         if (NativeMapped.class.isAssignableFrom(type)) {
864             NativeMappedConverter tc = NativeMappedConverter.getInstance(type); 
865             type = tc.nativeType();
866             value = tc.toNative(value, new ToNativeContext());
867         }
868         int size = Native.getNativeSize(type, value);
869         if (type.isPrimitive() || Long.class == type || Integer.class == type
870             || Short.class == type || Character.class == type
871             || Byte.class == type || Boolean.class == type
872             || Float.class == type || Double.class == type) {
873             alignment = size;
874         }
875         else if (Pointer.class == type
876                  || Buffer.class.isAssignableFrom(type)
877                  || Callback.class.isAssignableFrom(type)
878                  || WString.class == type
879                  || String.class == type) {
880             alignment = Pointer.SIZE;
881         }
882         else if (Structure.class.isAssignableFrom(type)) {
883             if (ByReference.class.isAssignableFrom(type)) {
884                 alignment = Pointer.SIZE;
885             }
886             else {
887                 if (value == null)
888                     value = newInstance(type);
889                 alignment = ((Structure)value).getStructAlignment();
890             }
891         }
892         else if (type.isArray()) {
893             alignment = getNativeAlignment(type.getComponentType(), null, isFirstElement);
894         }
895         else {
896             throw new IllegalArgumentException("Type " + type + " has unknown "
897                                                + "native alignment");
898         }
899         if (alignType == ALIGN_NONE) {
900             alignment = 1;
901         }
902         else if (alignType == ALIGN_MSVC) {
903             alignment = Math.min(8, alignment);
904         }
905         else if (alignType == ALIGN_GNUC) {
906             // NOTE this is published ABI for 32-bit gcc/linux/x86, osx/x86,
907             // and osx/ppc.  osx/ppc special-cases the first element
908             if (!isFirstElement || !(Platform.isMac() && isPPC)) {
909                 alignment = Math.min(MAX_GNUC_ALIGNMENT, alignment);
910             }
911         }
912         return alignment;
913     }
914 
915     public String toString() {
916         return toString(0, true);
917     }
918 
919     private String format(Class type) {
920         String s = type.getName();
921         int dot = s.lastIndexOf(".");
922         return s.substring(dot + 1);
923     }
924 
925     private String toString(int indent, boolean showContents) {
926         String LS = System.getProperty("line.separator");
927         String name = format(getClass()) + "(" + getPointer() + ")";
928         if (!(getPointer() instanceof Memory)) {
929             name += " (" + size() + " bytes)";
930         }
931         String prefix = "";
932         for (int idx=0;idx < indent;idx++) {
933             prefix += "  ";
934         }
935         String contents = LS;
936         if (!showContents) {
937             contents = "...}";
938         }
939         else for (Iterator i=structFields.values().iterator();i.hasNext();) {
940             StructField sf = (StructField)i.next();
941             Object value = getField(sf);
942             String type = format(sf.type);
943             String index = "";
944             contents += prefix;
945             if (sf.type.isArray() && value != null) {
946                 type = format(sf.type.getComponentType());
947                 index = "[" + Array.getLength(value) + "]";
948             }
949             contents += "  " + type + " "
950                 + sf.name + index + "@" + Integer.toHexString(sf.offset);
951             if (value instanceof Structure) {
952                 value = ((Structure)value).toString(indent + 1, !(value instanceof Structure.ByReference));
953             }
954             contents += "=";
955             if (value instanceof Long) {
956                 contents += Long.toHexString(((Long)value).longValue());
957             }
958             else if (value instanceof Integer) {
959                 contents += Integer.toHexString(((Integer)value).intValue());
960             }
961             else if (value instanceof Short) {
962                 contents += Integer.toHexString(((Short)value).shortValue());
963             }
964             else if (value instanceof Byte) {
965                 contents += Integer.toHexString(((Byte)value).byteValue());
966             }
967             else {
968                 contents += String.valueOf(value).trim();
969             }
970             contents += LS;
971             if (!i.hasNext())
972                 contents += prefix + "}";
973         }
974         if (indent == 0 && Boolean.getBoolean("jna.dump_memory")) {
975             byte[] buf = getPointer().getByteArray(0, size());
976             final int BYTES_PER_ROW = 4;
977             contents += LS + "memory dump" + LS;
978             for (int i=0;i < buf.length;i++) {
979                 if ((i % BYTES_PER_ROW) == 0) contents += "[";
980                 if (buf[i] >=0 && buf[i] < 16)
981                     contents += "0";
982                 contents += Integer.toHexString(buf[i] & 0xFF);
983                 if ((i % BYTES_PER_ROW) == BYTES_PER_ROW-1 && i < buf.length-1)
984                     contents += "]" + LS;
985             }
986             contents += "]";
987         }
988         return name + " {" + contents;
989     }
990 
991     /** Returns a view of this structure's memory as an array of structures.
992      * Note that this <code>Structure</code> must have a public, no-arg
993      * constructor.  If the structure is currently using auto-allocated
994      * {@link Memory} backing, the memory will be resized to fit the entire
995      * array. 
996      */
997     public Structure[] toArray(Structure[] array) {
998         ensureAllocated();
999         if (memory instanceof AutoAllocated) {
1000             // reallocate if necessary
1001             Memory m = (Memory)memory;
1002             int requiredSize = array.length * size();
1003             if (m.getSize() < requiredSize) {
1004                 m = new AutoAllocated(requiredSize);
1005                 m.clear();
1006                 useMemory(m);
1007             }
1008         }
1009         array[0] = this;
1010         int size = size();
1011         for (int i=1;i < array.length;i++) {
1012             array[i] = Structure.newInstance(getClass());
1013             array[i].useMemory(memory.share(i*size, size));
1014             array[i].read();
1015         }
1016 
1017         if (!(this instanceof ByValue)) {
1018             // keep track for later auto-read/writes
1019             this.array = array;
1020         }
1021 
1022         return array;
1023     }
1024 
1025     /** Returns a view of this structure's memory as an array of structures.
1026      * Note that this <code>Structure</code> must have a public, no-arg
1027      * constructor.  If the structure is currently using auto-allocated
1028      * {@link Memory} backing, the memory will be resized to fit the entire
1029      * array. 
1030      */
1031     public Structure[] toArray(int size) {
1032         return toArray((Structure[])Array.newInstance(getClass(), size));
1033     }
1034 
1035     private Class baseClass() {
1036         if ((this instanceof Structure.ByReference
1037              || this instanceof Structure.ByValue)
1038             && Structure.class.isAssignableFrom(getClass().getSuperclass())) {
1039             return getClass().getSuperclass();
1040         }
1041         return getClass();
1042     }
1043 
1044     /** This structure is equal to another based on the same data type
1045      * and visible data fields.
1046      */
1047     public boolean equals(Object o) {
1048         if (o == this)
1049             return true;
1050         if (o == null)
1051             return false;
1052         if (o.getClass() != getClass()
1053             && ((Structure)o).baseClass() != baseClass()) {
1054             return false;
1055         }
1056         Structure s = (Structure)o;
1057         if (s.size() == size()) {
1058             clear(); write();
1059             byte[] buf = getPointer().getByteArray(0, size());
1060             s.clear(); s.write();
1061             byte[] sbuf = s.getPointer().getByteArray(0, s.size());
1062             return Arrays.equals(buf, sbuf);
1063         }
1064         return false;
1065     }
1066 
1067     /** Since {@link #equals} depends on the native address, use that
1068      * as the hash code.
1069      */
1070     public int hashCode() {
1071         clear(); write();
1072         return Arrays.hashCode(getPointer().getByteArray(0, size()));
1073     }
1074 
1075     protected void cacheTypeInfo(Pointer p) {
1076         typeInfo = p.peer;
1077     }
1078 
1079     /** Obtain native type information for this structure. */
1080     Pointer getTypeInfo() {
1081         Pointer p = getTypeInfo(this);
1082         cacheTypeInfo(p);
1083         return p;
1084     }
1085 
1086     /** Set whether the structure is automatically synched to native memory
1087         before and after a native function call.  Convenience method for
1088         <pre><code>
1089         boolean auto = ...;
1090         setAutoRead(auto);
1091         setAutoWrite(auto);
1092         </code></pre>
1093     */
1094     public void setAutoSynch(boolean auto) {
1095         setAutoRead(auto);
1096         setAutoWrite(auto);
1097     }
1098 
1099     /** Set whether the struture is written to native memory prior to
1100         a native function call.
1101     */
1102     public void setAutoRead(boolean auto) {
1103         this.autoRead = auto;
1104     }
1105 
1106     /** Returns whether the struture is written to native memory prior to
1107         a native function call.
1108     */
1109     public boolean getAutoRead() {
1110         return this.autoRead;
1111     }
1112 
1113     /** Set whether the structure is read from native memory after a native
1114         function call. 
1115     */
1116     public void setAutoWrite(boolean auto) {
1117         this.autoWrite = auto;
1118     }
1119 
1120     /** Returns whether the structure is read from native memory after a native
1121         function call. 
1122     */
1123     public boolean getAutoWrite() {
1124         return this.autoWrite;
1125     }
1126 
1127     /** Exposed for testing purposes only. */
1128     static Pointer getTypeInfo(Object obj) {
1129         return FFIType.get(obj);
1130     }
1131 
1132     /** Create a new Structure instance of the given type
1133      * @param type
1134      * @return the new instance
1135      * @throws IllegalArgumentException if the instantiation fails
1136      */
1137     public static Structure newInstance(Class type) throws IllegalArgumentException {
1138         try {
1139             Structure s = (Structure)type.newInstance();
1140             if (s instanceof ByValue) {
1141                 s.allocateMemory();
1142             }
1143             return s;
1144         }
1145         catch(InstantiationException e) {
1146             String msg = "Can't instantiate " + type + " (" + e + ")";
1147             throw new IllegalArgumentException(msg);
1148         }
1149         catch(IllegalAccessException e) {
1150             String msg = "Instantiation of " + type
1151                 + " not allowed, is it public? (" + e + ")";
1152             throw new IllegalArgumentException(msg);
1153         }
1154     }
1155 
1156     class StructField extends Object {
1157         public String name;
1158         public Class type;
1159         public Field field;
1160         public int size = -1;
1161         public int offset = -1;
1162 		public int bitOffset = 0, bits = 0;
1163         public boolean isVolatile;
1164         public boolean isReadOnly;
1165         public FromNativeConverter readConverter;
1166         public ToNativeConverter writeConverter;
1167         public FromNativeContext context;
1168     }
1169     /** This class auto-generates an ffi_type structure appropriate for a given
1170      * structure for use by libffi.  The lifecycle of this structure is easier
1171      * to manage on the Java side than in native code.
1172      */
1173     static class FFIType extends Structure {
1174         public static class size_t extends IntegerType {
1175             public size_t() { this(0); }
1176             public size_t(long value) { super(Native.POINTER_SIZE, value); }
1177         }
1178         private static Map typeInfoMap = new WeakHashMap();
1179         // Native.initIDs initializes these fields to their appropriate
1180         // pointer values.  These are in a separate class from FFIType so that
1181         // they may be initialized prior to loading the FFIType class
1182         private static class FFITypes {
1183             private static Pointer ffi_type_void;
1184             private static Pointer ffi_type_float;
1185             private static Pointer ffi_type_double;
1186             private static Pointer ffi_type_longdouble;
1187             private static Pointer ffi_type_uint8;
1188             private static Pointer ffi_type_sint8;
1189             private static Pointer ffi_type_uint16;
1190             private static Pointer ffi_type_sint16;
1191             private static Pointer ffi_type_uint32;
1192             private static Pointer ffi_type_sint32;
1193             private static Pointer ffi_type_uint64;
1194             private static Pointer ffi_type_sint64;
1195             private static Pointer ffi_type_pointer;
1196         }
1197         static {
1198             if (Native.POINTER_SIZE == 0)
1199                 throw new Error("Native library not initialized");
1200             if (FFITypes.ffi_type_void == null)
1201                 throw new Error("FFI types not initialized");
1202             typeInfoMap.put(void.class, FFITypes.ffi_type_void);
1203             typeInfoMap.put(Void.class, FFITypes.ffi_type_void);
1204             typeInfoMap.put(float.class, FFITypes.ffi_type_float);
1205             typeInfoMap.put(Float.class, FFITypes.ffi_type_float);
1206             typeInfoMap.put(double.class, FFITypes.ffi_type_double);
1207             typeInfoMap.put(Double.class, FFITypes.ffi_type_double);
1208             typeInfoMap.put(long.class, FFITypes.ffi_type_sint64);
1209             typeInfoMap.put(Long.class, FFITypes.ffi_type_sint64);
1210             typeInfoMap.put(int.class, FFITypes.ffi_type_sint32);
1211             typeInfoMap.put(Integer.class, FFITypes.ffi_type_sint32);
1212             typeInfoMap.put(short.class, FFITypes.ffi_type_sint16);
1213             typeInfoMap.put(Short.class, FFITypes.ffi_type_sint16);
1214             Pointer ctype = Native.WCHAR_SIZE == 2
1215                 ? FFITypes.ffi_type_uint16 : FFITypes.ffi_type_uint32;
1216             typeInfoMap.put(char.class, ctype);
1217             typeInfoMap.put(Character.class, ctype);
1218             typeInfoMap.put(byte.class, FFITypes.ffi_type_sint8);
1219             typeInfoMap.put(Byte.class, FFITypes.ffi_type_sint8);
1220             typeInfoMap.put(boolean.class, FFITypes.ffi_type_uint32);
1221             typeInfoMap.put(Boolean.class, FFITypes.ffi_type_uint32);
1222             typeInfoMap.put(Pointer.class, FFITypes.ffi_type_pointer);
1223             typeInfoMap.put(String.class, FFITypes.ffi_type_pointer);
1224             typeInfoMap.put(WString.class, FFITypes.ffi_type_pointer);
1225         }
1226         // From ffi.h
1227         private static final int FFI_TYPE_STRUCT = 13;
1228         // Structure fields
1229         public size_t size;
1230         public short alignment;
1231         public short type = FFI_TYPE_STRUCT;
1232         public Pointer elements;
1233 
1234         private FFIType(Structure ref) {
1235             Pointer[] els;
1236             if (ref instanceof Union) {
1237                 StructField sf = ((Union)ref).biggestField;
1238                 els = new Pointer[] {
1239                     get(ref.getField(sf), sf.type), null,
1240                 };
1241             }
1242             else {
1243                 els = new Pointer[ref.fields().size() + 1];
1244                 int idx = 0;
1245                 for (Iterator i=ref.fields().values().iterator();i.hasNext();) {
1246                     StructField sf = (StructField)i.next();
1247                     els[idx++] = get(ref.getField(sf), sf.type);
1248                 }
1249             }
1250             init(els);
1251         }
1252         // Represent fixed-size arrays as structures of N identical elements
1253         private FFIType(Object array, Class type) {
1254             int length = Array.getLength(array);
1255             Pointer[] els = new Pointer[length+1];
1256             Pointer p = get(null, type.getComponentType());
1257             for (int i=0;i < length;i++) {
1258                 els[i] = p;
1259             }
1260             init(els);
1261         }
1262         private void init(Pointer[] els) {
1263             elements = new Memory(Pointer.SIZE * els.length);
1264             elements.write(0, els, 0, els.length);
1265             write();
1266         }
1267 
1268         static Pointer get(Object obj) {
1269             if (obj == null)
1270                 return FFITypes.ffi_type_pointer;
1271             if (obj instanceof Class)
1272                 return get(null, (Class)obj);
1273             return get(obj, obj.getClass());
1274         }
1275 
1276         private static Pointer get(Object obj, Class cls) {
1277             synchronized(typeInfoMap) {
1278                 Object o = typeInfoMap.get(cls);
1279                 if (o instanceof Pointer) {
1280                     return (Pointer)o;
1281                 }
1282                 if (o instanceof FFIType) {
1283                     return ((FFIType)o).getPointer();
1284                 }
1285                 if (Buffer.class.isAssignableFrom(cls)
1286                     || Callback.class.isAssignableFrom(cls)) {
1287                     typeInfoMap.put(cls, FFITypes.ffi_type_pointer);
1288                     return FFITypes.ffi_type_pointer;
1289                 }
1290                 if (Structure.class.isAssignableFrom(cls)) {
1291                     if (obj == null) obj = newInstance(cls);
1292                     if (ByReference.class.isAssignableFrom(cls)) {
1293                         typeInfoMap.put(cls, FFITypes.ffi_type_pointer);
1294                         return FFITypes.ffi_type_pointer;
1295                     }
1296                     FFIType type = new FFIType((Structure)obj);
1297                     typeInfoMap.put(cls, type);
1298                     return type.getPointer();
1299                 }
1300                 if (NativeMapped.class.isAssignableFrom(cls)) {
1301                     NativeMappedConverter c = NativeMappedConverter.getInstance(cls);
1302                     return get(c.toNative(obj, new ToNativeContext()), c.nativeType());
1303                 }
1304                 if (cls.isArray()) {
1305                     FFIType type = new FFIType(obj, cls);
1306                     // Store it in the map to prevent premature GC of type info
1307                     typeInfoMap.put(obj, type);
1308                     return type.getPointer();
1309                 }
1310                 throw new IllegalArgumentException("Unsupported type " + cls);
1311             }
1312         }
1313     }
1314     
1315     private class AutoAllocated extends Memory {
1316         public AutoAllocated(int size) {
1317             super(size);
1318         }
1319     }
1320 
1321     private static void structureArrayCheck(Structure[] ss) {
1322         Pointer base = ss[0].getPointer();
1323         int size = ss[0].size();
1324         for (int si=1;si < ss.length;si++) {
1325             if (ss[si].getPointer().peer != base.peer + size*si) {
1326                 String msg = "Structure array elements must use"
1327                     + " contiguous memory (bad backing address at Structure array index " + si + ")";     
1328                 throw new IllegalArgumentException(msg);
1329             }
1330         }
1331     }
1332 
1333     public static void autoRead(Structure[] ss) {
1334         structureArrayCheck(ss);
1335         if (ss[0].array == ss) {
1336             ss[0].autoRead();
1337         }
1338         else {
1339             for (int si=0;si < ss.length;si++) {
1340                 ss[si].autoRead();
1341             }
1342         }
1343     }
1344 
1345     public void autoRead() {
1346         if (getAutoRead()) {
1347             read();
1348             if (array != null) {
1349                 for (int i=1;i < array.length;i++) {
1350                     array[i].autoRead();
1351                 }
1352             }
1353         }
1354     }
1355 
1356     public static void autoWrite(Structure[] ss) {
1357         structureArrayCheck(ss);
1358         if (ss[0].array == ss) {
1359             ss[0].autoWrite();
1360         }
1361         else {
1362             for (int si=0;si < ss.length;si++) {
1363                 ss[si].autoWrite();
1364             }
1365         }
1366     }
1367 
1368     public void autoWrite() {
1369         if (getAutoWrite()) {
1370             write();
1371             if (array != null) {
1372                 for (int i=1;i < array.length;i++) {
1373                     array[i].autoWrite();
1374                 }
1375             }
1376         }
1377     }
1378 }