2828import java .nio .IntBuffer ;
2929import java .util .ArrayList ;
3030import java .util .Comparator ;
31+ import java .util .Iterator ;
3132import java .util .List ;
33+ import java .util .NoSuchElementException ;
3234import java .util .Objects ;
3335import 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 ;
3441
3542/**
3643 * Represents the module entries stored in the buffer of {@code "/packages/xxx"}
@@ -59,11 +66,12 @@ public final class ModuleReference implements Comparable<ModuleReference> {
5966 private static final int FLAGS_HAS_PREVIEW_VERSION = 0x4 ;
6067
6168 /**
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}).
6472 */
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 ( )
6775 .thenComparing (ModuleReference ::name );
6876
6977 /** Creates a reference for an empty package (one without content in). */
@@ -109,8 +117,8 @@ public String name() {
109117 * <p>An invariant of the module system is that while a package may exist
110118 * under many modules, it is only non-empty in one.
111119 */
112- public boolean isEmpty () {
113- return ((flags & FLAGS_HAS_CONTENT ) = = 0 );
120+ public boolean hasContent () {
121+ return ((flags & FLAGS_HAS_CONTENT ) ! = 0 );
114122 }
115123
116124 /**
@@ -132,7 +140,7 @@ private static boolean hasNormalVersion(int flags) {
132140
133141 @ Override
134142 public int compareTo (ModuleReference rhs ) {
135- return NON_EMPTY_FIRST .compare (this , rhs );
143+ return PREVIEW_FIRST .compare (this , rhs );
136144 }
137145
138146 @ Override
@@ -155,40 +163,62 @@ public int hashCode() {
155163 }
156164
157165 /**
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 .
160168 *
161169 * @param buffer the content buffer of an {@link ImageLocation} with type
162170 * {@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.
167176 */
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 ) {
170179 int bufferSize = buffer .capacity ();
171180 if (bufferSize == 0 || (bufferSize & 0x1 ) != 0 ) {
172181 throw new IllegalArgumentException ("Invalid buffer size" );
173182 }
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 ;
181210 }
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 ;
187218 }
188- refs . add ( modRef );
219+ throw new NoSuchElementException ( );
189220 }
190- }
191- return refs ;
221+ };
192222 }
193223
194224 /**
@@ -206,13 +236,16 @@ public static void write(
206236 if (buffer .capacity () != (2 * refs .size ())) {
207237 throw new IllegalArgumentException ("Incorrect output buffer capacity" );
208238 }
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 ++ ;
213243 }
214244 buffer .put (modRef .flags );
215245 buffer .put (nameEncoder .apply (modRef .name ));
216246 }
247+ if (withContent > 1 ) {
248+ throw new IllegalArgumentException ("At most one reference can have content: " + refs );
249+ }
217250 }
218251}
0 commit comments