Skip to content

Commit 6c5af8a

Browse files
committed
Fix double-counting of StructLayout fields
1 parent 932964e commit 6c5af8a

2 files changed

Lines changed: 34 additions & 26 deletions

File tree

src/org/jruby/ext/ffi/StructLayout.java

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ public final class StructLayout extends Type {
5959
static final String CLASS_NAME = "StructLayout";
6060

6161
/** The name:offset map for this struct */
62-
private final Map<IRubyObject, Member> fields;
62+
private final Map<IRubyObject, Member> fieldMap;
6363

6464
/** The ordered list of field names (as symbols) */
6565
private final List<RubySymbol> fieldNames;
66+
67+
/** The ordered list of field names (as symbols) */
68+
private final List<Member> fields;
6669

6770
private final int cacheableFieldCount;
6871
private final int[] cacheIndexMap;
@@ -95,17 +98,21 @@ public static RubyClass createStructLayoutClass(Ruby runtime, RubyModule module)
9598
* @param size the total size of the struct.
9699
* @param minAlign The minimum alignment required when allocating memory.
97100
*/
98-
StructLayout(Ruby runtime, Map<IRubyObject, Member> fields, int size, int minAlign) {
101+
StructLayout(Ruby runtime, Collection<RubySymbol> fieldNames, Map<IRubyObject, Member> fields, int size, int minAlign) {
99102
super(runtime, runtime.fastGetModule("FFI").fastGetClass(CLASS_NAME), NativeType.STRUCT, size, minAlign);
100103
//
101104
// fields should really be an immutable map as it is never modified after construction
102105
//
103-
this.fields = immutableMap(fields);
104-
this.cacheIndexMap = new int[fields.size()];
105-
this.referenceIndexMap = new int[fields.size()];
106+
this.fieldMap = immutableMap(fields);
107+
this.cacheIndexMap = new int[fieldNames.size()];
108+
this.referenceIndexMap = new int[fieldNames.size()];
106109

107110
int cfCount = 0, refCount = 0;
108-
for (Member m : fields.values()) {
111+
List<Member> fieldList = new ArrayList<Member>(fieldNames.size());
112+
113+
for (RubySymbol fieldName : fieldNames) {
114+
Member m = fields.get(fieldName);
115+
fieldList.add(m);
109116
if (m.isCacheable()) {
110117
cacheIndexMap[m.index] = cfCount++;
111118
} else {
@@ -121,13 +128,8 @@ public static RubyClass createStructLayoutClass(Ruby runtime, RubyModule module)
121128
this.referenceFieldCount = refCount;
122129

123130
// Create the ordered list of field names from the map
124-
List<RubySymbol> names = new ArrayList<RubySymbol>(fields.size());
125-
for (Map.Entry<IRubyObject, Member> e : fields.entrySet()) {
126-
if (e.getKey() instanceof RubySymbol) {
127-
names.add((RubySymbol) e.getKey());
128-
}
129-
}
130-
this.fieldNames = Collections.unmodifiableList(names);
131+
this.fieldNames = Collections.unmodifiableList(new ArrayList<RubySymbol>(fieldNames));
132+
this.fields = Collections.unmodifiableList(fieldList);
131133
}
132134

133135
/**
@@ -192,15 +194,15 @@ public IRubyObject members(ThreadContext context) {
192194
public IRubyObject offsets(ThreadContext context) {
193195
Ruby runtime = context.getRuntime();
194196
RubyArray offsets = RubyArray.newArray(runtime);
195-
for (Map.Entry<IRubyObject, Member> e : fields.entrySet()) {
196-
if (e.getKey() instanceof RubySymbol) {
197-
RubyArray offset = RubyArray.newArray(runtime);
198-
// Assemble a [ :name, offset ] array
199-
offset.append(e.getKey());
200-
offset.append(runtime.newFixnum(e.getValue().offset));
201-
offsets.append(offset);
202-
}
197+
198+
for (RubySymbol name : fieldNames) {
199+
RubyArray offset = RubyArray.newArray(runtime);
200+
// Assemble a [ :name, offset ] array
201+
offset.append(name);
202+
offset.append(runtime.newFixnum(fieldMap.get(name).offset));
203+
offsets.append(offset);
203204
}
205+
204206
return offsets;
205207
}
206208

@@ -242,7 +244,7 @@ public IRubyObject offset_of(ThreadContext context, IRubyObject fieldName) {
242244
* @return A <tt>Member</tt> descriptor.
243245
*/
244246
final Member getMember(Ruby runtime, IRubyObject name) {
245-
Member f = fields.get(name);
247+
Member f = fieldMap.get(name);
246248
if (f != null) {
247249
return f;
248250
}
@@ -278,7 +280,7 @@ public final int getFieldCount() {
278280
}
279281

280282
public final java.util.Collection<Member> getFields() {
281-
return Collections.unmodifiableCollection(fields.values());
283+
return fields;
282284
}
283285

284286
/**

src/org/jruby/ext/ffi/StructLayoutBuilder.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.util.ArrayList;
3232
import java.util.Collection;
3333
import java.util.LinkedHashMap;
34+
import java.util.LinkedList;
35+
import java.util.List;
3436
import java.util.Map;
3537
import org.jruby.Ruby;
3638
import org.jruby.RubyClass;
@@ -55,6 +57,8 @@
5557
public final class StructLayoutBuilder extends RubyObject {
5658
public static final String CLASS_NAME = "StructLayoutBuilder";
5759

60+
private final List<RubySymbol> fieldNames = new LinkedList<RubySymbol>();
61+
5862
private final Map<IRubyObject, StructLayout.Member> fields = new LinkedHashMap<IRubyObject, StructLayout.Member>();
5963
/** The current size of the layout in bytes */
6064
private int size = 0;
@@ -98,7 +102,7 @@ public static StructLayoutBuilder newInstance(ThreadContext context, IRubyObject
98102

99103
@JRubyMethod(name = "build")
100104
public StructLayout build(ThreadContext context) {
101-
return new StructLayout(context.getRuntime(), fields, minAlign + ((size - 1) & ~(minAlign - 1)), minAlign);
105+
return new StructLayout(context.getRuntime(), fieldNames, fields, minAlign + ((size - 1) & ~(minAlign - 1)), minAlign);
102106
}
103107

104108
@JRubyMethod(name = "size")
@@ -119,9 +123,9 @@ private static final int alignMember(int offset, int align) {
119123
return align + ((offset - 1) & ~(align - 1));
120124
}
121125

122-
private static final IRubyObject createSymbolKey(Ruby runtime, IRubyObject key) {
126+
private static final RubySymbol createSymbolKey(Ruby runtime, IRubyObject key) {
123127
if (key instanceof RubySymbol) {
124-
return key;
128+
return (RubySymbol) key;
125129
}
126130
return runtime.getSymbolTable().getSymbol(key.asJavaString());
127131
}
@@ -131,8 +135,10 @@ private static IRubyObject createStringKey(Ruby runtime, IRubyObject key) {
131135
}
132136

133137
private final IRubyObject storeField(Ruby runtime, IRubyObject name, StructLayout.Member field, int align, int size) {
138+
134139
fields.put(createStringKey(runtime, name), field);
135140
fields.put(createSymbolKey(runtime, name), field);
141+
fieldNames.add(createSymbolKey(runtime, name));
136142
this.size = Math.max(this.size, (int) field.offset + size);
137143
this.minAlign = Math.max(this.minAlign, align);
138144
return this;

0 commit comments

Comments
 (0)