Skip to content

Commit 367f2d3

Browse files
Merge pull request #1246 from joerg1985/structure-less-lookups
Optimized Map/Set lookups in Structure
2 parents 816fdd2 + b641071 commit 367f2d3

File tree

2 files changed

+46
-21
lines changed

2 files changed

+46
-21
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Features
1212
* [#1237](https://github.com/java-native-access/jna/pull/1237): *Experimental:* Add artifacts that make jna and jna-platform named modules (provide `module-info.class`). The new artifacts are named `jna-jpms.jar` and `jna-platform-jpms.jar` - [@matthiasblaesing](https://github.com/matthiasblaesing).
1313
* [#1242](https://github.com/java-native-access/jna/pull/1242): Add CallWindowProc to User32 - [@heldplayer](https://github.com/heldplayer).
1414
* [#1239](https://github.com/java-native-access/jna/pull/1239): Improve performance of allocation of `c.s.j.Memory` objects - [@joerg1985](https://github.com/joerg1985).
15+
* [#1246](https://github.com/java-native-access/jna/pull/1246): Improve performance of `c.s.j.Structure#read` and `c.s.j.Structure#write` - [@joerg1985](https://github.com/joerg1985).
1516

1617
Bug Fixes
1718
---------

src/com/sun/jna/Structure.java

+45-21
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,17 @@ public interface ByValue { }
127127
*/
128128
public interface ByReference { }
129129

130+
/** A class to keep NativeString instances alive and avoid writing the same value again and again */
131+
private static class NativeStringTracking {
132+
133+
private final Object value;
134+
private NativeString peer;
135+
136+
NativeStringTracking(Object lastValue) {
137+
this.value = lastValue;
138+
}
139+
}
140+
130141
/** Use the platform default alignment. */
131142
public static final int ALIGN_DEFAULT = 0;
132143
/** No alignment, place all fields on nearest 1-byte boundary */
@@ -157,7 +168,7 @@ public interface ByReference { }
157168
private Map<String, StructField> structFields;
158169
// Keep track of native C strings which have been allocated,
159170
// corresponding to String fields of this Structure
160-
private final Map<String, Object> nativeStrings = new HashMap<String, Object>();
171+
private final Map<String, NativeStringTracking> nativeStrings = new HashMap<String, NativeStringTracking>(8);
161172
private TypeMapper typeMapper;
162173
// This field is accessed by native code
163174
private long typeInfo;
@@ -343,14 +354,15 @@ void useMemory(Pointer m, int offset, boolean force) {
343354
this.memory.write(0, buf, 0, buf.length);
344355
}
345356
else {
346-
// Ensure our memory pointer is initialized, even if we can't
347-
// yet figure out a proper size/layout
348-
this.memory = m.share(offset);
349357
if (size == CALCULATE_SIZE) {
350358
size = calculateSize(false);
351359
}
352360
if (size != CALCULATE_SIZE) {
353361
this.memory = m.share(offset, size);
362+
} else {
363+
// Ensure our memory pointer is initialized, even if we can't
364+
// yet figure out a proper size/layout
365+
this.memory = m.share(offset);
354366
}
355367
}
356368
this.array = null;
@@ -438,6 +450,8 @@ public int size() {
438450
/** Clears the native memory associated with this Structure. */
439451
public void clear() {
440452
ensureAllocated();
453+
// ensure the memory is released and the values are written again
454+
nativeStrings.clear();
441455
memory.clear(size());
442456
}
443457

@@ -508,8 +522,9 @@ public boolean add(Structure o) {
508522
if (!contains(o)) {
509523
ensureCapacity(count+1);
510524
elements[count++] = o;
525+
return true;
511526
}
512-
return true;
527+
return false;
513528
}
514529
private int indexOf(Structure s1) {
515530
for (int i=0;i < count;i++) {
@@ -579,10 +594,9 @@ public void read() {
579594
ensureAllocated();
580595

581596
// Avoid redundant reads
582-
if (busy().contains(this)) {
597+
if (!busy().add(this)) {
583598
return;
584599
}
585-
busy().add(this);
586600
if (this instanceof Structure.ByReference) {
587601
reading().put(getPointer(), this);
588602
}
@@ -593,7 +607,7 @@ public void read() {
593607
}
594608
finally {
595609
busy().remove(this);
596-
if (reading().get(getPointer()) == this) {
610+
if (this instanceof Structure.ByReference && reading().get(getPointer()) == this) {
597611
reading().remove(getPointer());
598612
}
599613
}
@@ -740,8 +754,18 @@ protected Object readField(StructField structField) {
740754

741755
if (fieldType.equals(String.class)
742756
|| fieldType.equals(WString.class)) {
743-
nativeStrings.put(structField.name + ".ptr", memory.getPointer(offset));
744-
nativeStrings.put(structField.name + ".val", result);
757+
if (result != null) {
758+
NativeStringTracking current = new NativeStringTracking(result);
759+
NativeStringTracking previous = nativeStrings.put(structField.name, current);
760+
761+
if (previous != null) {
762+
// regardless of value changed or not, keep the old native string alive
763+
current.peer = previous.peer;
764+
}
765+
} else {
766+
// the value is cleared, we don't need to keep the native string alive
767+
nativeStrings.remove(structField.name);
768+
}
745769
}
746770

747771
// Update the value on the Java field
@@ -769,10 +793,9 @@ public void write() {
769793
}
770794

771795
// Avoid redundant writes
772-
if (busy().contains(this)) {
796+
if (!busy().add(this)) {
773797
return;
774798
}
775-
busy().add(this);
776799
try {
777800
// Write all fields, except those marked 'volatile'
778801
for (StructField sf : fields().values()) {
@@ -840,28 +863,29 @@ protected void writeField(StructField structField) {
840863
// Java strings get converted to C strings, where a Pointer is used
841864
if (String.class == fieldType
842865
|| WString.class == fieldType) {
843-
// Allocate a new string in memory
844-
boolean wide = fieldType == WString.class;
845866
if (value != null) {
867+
NativeStringTracking current = new NativeStringTracking(value);
868+
NativeStringTracking previous = nativeStrings.put(structField.name, current);
869+
846870
// If we've already allocated a native string here, and the
847871
// string value is unchanged, leave it alone
848-
if (nativeStrings.containsKey(structField.name + ".ptr")
849-
&& value.equals(nativeStrings.get(structField.name + ".val"))) {
872+
if (previous != null && value.equals(previous.value)) {
873+
// value is unchanged, keep the old native string alive
874+
current.peer = previous.peer;
850875
return;
851876
}
877+
// Allocate a new string in memory
878+
boolean wide = fieldType == WString.class;
852879
NativeString nativeString = wide
853880
? new NativeString(value.toString(), true)
854881
: new NativeString(value.toString(), encoding);
855-
// Keep track of allocated C strings to avoid
856-
// premature garbage collection of the memory.
857-
nativeStrings.put(structField.name, nativeString);
882+
// value is changed, keep the new native string alive
883+
current.peer = nativeString;
858884
value = nativeString.getPointer();
859885
}
860886
else {
861887
nativeStrings.remove(structField.name);
862888
}
863-
nativeStrings.remove(structField.name + ".ptr");
864-
nativeStrings.remove(structField.name + ".val");
865889
}
866890

867891
try {

0 commit comments

Comments
 (0)