28
28
import java .nio .IntBuffer ;
29
29
import java .util .ArrayList ;
30
30
import java .util .Comparator ;
31
+ import java .util .Iterator ;
31
32
import java .util .List ;
33
+ import java .util .NoSuchElementException ;
32
34
import java .util .Objects ;
33
35
import java .util .function .Function ;
36
+ import java .util .function .IntFunction ;
37
+ import java .util .function .IntPredicate ;
38
+ import java .util .function .Predicate ;
39
+ import java .util .function .Supplier ;
40
+ import java .util .stream .Stream ;
34
41
35
42
/**
36
43
* Represents the module entries stored in the buffer of {@code "/packages/xxx"}
@@ -59,11 +66,12 @@ public final class ModuleReference implements Comparable<ModuleReference> {
59
66
private static final int FLAGS_HAS_PREVIEW_VERSION = 0x4 ;
60
67
61
68
/**
62
- * References are ordered so the unique module in which the associated
63
- * package is non-empty comes first.
69
+ * References are ordered with preview versions first which permits early
70
+ * exit when processing preview entries (it's reversed because the default
71
+ * order for a boolean is {@code false < true}).
64
72
*/
65
- private static final Comparator <ModuleReference > NON_EMPTY_FIRST =
66
- Comparator .comparing (ModuleReference ::isEmpty )
73
+ private static final Comparator <ModuleReference > PREVIEW_FIRST =
74
+ Comparator .comparing (ModuleReference ::hasPreviewVersion ). reversed ( )
67
75
.thenComparing (ModuleReference ::name );
68
76
69
77
/** Creates a reference for an empty package (one without content in). */
@@ -109,8 +117,8 @@ public String name() {
109
117
* <p>An invariant of the module system is that while a package may exist
110
118
* under many modules, it is only non-empty in one.
111
119
*/
112
- public boolean isEmpty () {
113
- return ((flags & FLAGS_HAS_CONTENT ) = = 0 );
120
+ public boolean hasContent () {
121
+ return ((flags & FLAGS_HAS_CONTENT ) ! = 0 );
114
122
}
115
123
116
124
/**
@@ -132,7 +140,7 @@ private static boolean hasNormalVersion(int flags) {
132
140
133
141
@ Override
134
142
public int compareTo (ModuleReference rhs ) {
135
- return NON_EMPTY_FIRST .compare (this , rhs );
143
+ return PREVIEW_FIRST .compare (this , rhs );
136
144
}
137
145
138
146
@ Override
@@ -155,40 +163,62 @@ public int hashCode() {
155
163
}
156
164
157
165
/**
158
- * Reads the content buffer of a package subdirectory to construct a list
159
- * of module references .
166
+ * Reads the content buffer of a package subdirectory to return a sequence
167
+ * of module name offsets in the jimage .
160
168
*
161
169
* @param buffer the content buffer of an {@link ImageLocation} with type
162
170
* {@link ImageLocation.LocationType#PACKAGES_DIR PACKAGES_DIR}.
163
- * @param previewMode if {@code true} include all reference; otherwise skip
164
- * preview-only ones.
165
- * @param nameDecoder decoder for module names.
166
- * @return the list of module references for the source package subdirectory.
171
+ * @param includeNormal whether to include name offsets for modules present
172
+ * in normal (non-preview) mode.
173
+ * @param includePreview whether to include name offsets for modules present
174
+ * in preview mode.
175
+ * @return an iterator of module name offsets.
167
176
*/
168
- public static List < ModuleReference > read (
169
- IntBuffer buffer , boolean previewMode , Function < Integer , String > nameDecoder ) {
177
+ public static Iterator < Integer > readNameOffsets (
178
+ IntBuffer buffer , boolean includeNormal , boolean includePreview ) {
170
179
int bufferSize = buffer .capacity ();
171
180
if (bufferSize == 0 || (bufferSize & 0x1 ) != 0 ) {
172
181
throw new IllegalArgumentException ("Invalid buffer size" );
173
182
}
174
- // In non-preview mode we might skip a very small number of preview-only
175
- // entries, but it's not worth "right-sizing" the array for that.
176
- List <ModuleReference > refs = new ArrayList <>(bufferSize / 2 );
177
- for (int i = 0 ; i < bufferSize ; i += 2 ) {
178
- int packageFlags = buffer .get (i );
179
- if ((packageFlags & (FLAGS_HAS_NORMAL_VERSION | FLAGS_HAS_PREVIEW_VERSION )) == 0 ) {
180
- throw new IllegalArgumentException ("Invalid package flags" );
183
+ int testFlags = (includeNormal ? FLAGS_HAS_NORMAL_VERSION : 0 )
184
+ + (includePreview ? FLAGS_HAS_PREVIEW_VERSION : 0 );
185
+ if (testFlags == 0 ) {
186
+ throw new IllegalArgumentException ("Invalid flags" );
187
+ }
188
+
189
+ return new Iterator <Integer >() {
190
+ private int idx = nextIdx (0 );
191
+
192
+ int nextIdx (int idx ) {
193
+ for (; idx < bufferSize ; idx += 2 ) {
194
+ // If any of the test flags are set, include this entry.
195
+ if ((buffer .get (idx ) & testFlags ) != 0 ) {
196
+ return idx ;
197
+ } else if (!includeNormal ) {
198
+ // Preview entries are first in the offset buffer, so we
199
+ // can exit early (by returning the end index) if we are
200
+ // only iterating preview entries, and have run out.
201
+ break ;
202
+ }
203
+ }
204
+ return bufferSize ;
205
+ }
206
+
207
+ @ Override
208
+ public boolean hasNext () {
209
+ return idx < bufferSize ;
181
210
}
182
- if (previewMode || hasNormalVersion (packageFlags )) {
183
- ModuleReference modRef = new ModuleReference (nameDecoder .apply (buffer .get (i + 1 )), packageFlags );
184
- // Validate the unique non-empty reference (if it exists) is first.
185
- if (!refs .isEmpty () && !modRef .isEmpty ()) {
186
- throw new IllegalArgumentException ("Only first reference can have content: " + modRef );
211
+
212
+ @ Override
213
+ public Integer next () {
214
+ if (idx < bufferSize ) {
215
+ int nameOffset = buffer .get (idx + 1 );
216
+ idx = nextIdx (idx + 2 );
217
+ return nameOffset ;
187
218
}
188
- refs . add ( modRef );
219
+ throw new NoSuchElementException ( );
189
220
}
190
- }
191
- return refs ;
221
+ };
192
222
}
193
223
194
224
/**
@@ -206,13 +236,16 @@ public static void write(
206
236
if (buffer .capacity () != (2 * refs .size ())) {
207
237
throw new IllegalArgumentException ("Incorrect output buffer capacity" );
208
238
}
209
- for ( int i = 0 ; i < refs . size (); i ++) {
210
- ModuleReference modRef = refs . get ( i );
211
- if (i > 0 && ! modRef .isEmpty ()) {
212
- throw new IllegalArgumentException ( "Only first reference can be non-empty: " + modRef ) ;
239
+ int withContent = 0 ;
240
+ for ( ModuleReference modRef : refs ) {
241
+ if (modRef .hasContent ()) {
242
+ withContent ++ ;
213
243
}
214
244
buffer .put (modRef .flags );
215
245
buffer .put (nameEncoder .apply (modRef .name ));
216
246
}
247
+ if (withContent > 1 ) {
248
+ throw new IllegalArgumentException ("At most one reference can have content: " + refs );
249
+ }
217
250
}
218
251
}
0 commit comments