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 }