001 /* This library is free software; you can redistribute it and/or
002 * modify it under the terms of the GNU Lesser General Public
003 * License as published by the Free Software Foundation; either
004 * version 2.1 of the License, or (at your option) any later version.
005 * <p/>
006 * This library is distributed in the hope that it will be useful,
007 * but WITHOUT ANY WARRANTY; without even the implied warranty of
008 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
009 * Lesser General Public License for more details.
010 */
011 package com.sun.jna;
012
013 import java.lang.reflect.Array;
014 import java.lang.reflect.Field;
015 import java.lang.reflect.Modifier;
016 import java.nio.Buffer;
017 import java.util.AbstractCollection;
018 import java.util.ArrayList;
019 import java.util.Arrays;
020 import java.util.Collection;
021 import java.util.HashMap;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.LinkedHashMap;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Set;
028 import java.util.WeakHashMap;
029
030 /**
031 * Represents a native structure with a Java peer class. When used as a
032 * function parameter or return value, this class corresponds to
033 * <code>struct*</code>. When used as a field within another
034 * <code>Structure</code>, it corresponds to <code>struct</code>. The
035 * tagging interfaces {@link ByReference} and {@link ByValue} may be used
036 * to alter the default behavior.
037 * <p>
038 * See the <a href={@docRoot}/overview-summary.html>overview</a> for supported
039 * type mappings.
040 * <p>
041 * Structure alignment and type mappings are derived by default from the
042 * enclosing interface definition (if any) by using
043 * {@link Native#getStructureAlignment} and {@link Native#getTypeMapper}.
044 * <p>
045 * Structure fields corresponding to native fields <em>must</em> be public.
046 * The may additionally have the following modifiers:<br>
047 * <ul>
048 * <li><code>volatile</code> JNA will not write the field unless specifically
049 * instructed to do so via {@link #writeField(String)}.
050 * <li><code>final</code> JNA will overwrite the field via {@link #read()},
051 * but otherwise the field is not modifiable from Java. Take care when using
052 * this option, since the compiler will usually assume <em>all</em> accesses
053 * to the field (for a given Structure instance) have the same value.
054 * </ul>
055 * NOTE: Strings are used to represent native C strings because usage of
056 * <code>char *</code> is generally more common than <code>wchar_t *</code>.
057 * <p>
058 * NOTE: This class assumes that fields are returned in {@link Class#getFields}
059 * in the same or reverse order as declared. If your VM returns them in
060 * no particular order, you're out of luck.
061 *
062 * @author Todd Fast, todd.fast@sun.com
063 * @author twall@users.sf.net
064 */
065 public abstract class Structure {
066
067 /** Tagging interface to indicate the value of an instance of the
068 * <code>Structure</code> type is to be used in function invocations rather
069 * than its address. The default behavior is to treat
070 * <code>Structure</code> function parameters and return values as by
071 * reference, meaning the address of the structure is used.
072 */
073 public interface ByValue { }
074 /** Tagging interface to indicate the address of an instance of the
075 * Structure type is to be used within a <code>Structure</code> definition
076 * rather than nesting the full Structure contents. The default behavior
077 * is to inline <code>Structure</code> fields.
078 */
079 public interface ByReference { }
080
081 private static class MemberOrder {
082 public int first;
083 public int middle;
084 public int last;
085 }
086
087 private static final boolean REVERSE_FIELDS;
088 static boolean REQUIRES_FIELD_ORDER;
089
090 static final boolean isPPC;
091 static final boolean isSPARC;
092
093 static {
094 // IBM and JRockit store fields in reverse order; check for it
095 Field[] fields = MemberOrder.class.getFields();
096 REVERSE_FIELDS = "last".equals(fields[0].getName());
097 REQUIRES_FIELD_ORDER = !"middle".equals(fields[1].getName());
098 String arch = System.getProperty("os.arch").toLowerCase();
099 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 }