diff --git a/src/java.base/share/classes/java/security/SecureRandom.java b/src/java.base/share/classes/java/security/SecureRandom.java index 6a1683e993c..4242b5da04a 100644 --- a/src/java.base/share/classes/java/security/SecureRandom.java +++ b/src/java.base/share/classes/java/security/SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package java.security; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; import sun.security.jca.GetInstance; import sun.security.jca.GetInstance.Instance; import sun.security.jca.Providers; @@ -149,10 +148,6 @@ import java.util.regex.Pattern; * @since 1.1 */ -@RandomGeneratorProperties( - name = "SecureRandom", - isStochastic = true -) public class SecureRandom extends java.util.Random { private static final Debug pdebug = diff --git a/src/java.base/share/classes/java/util/Random.java b/src/java.base/share/classes/java/util/Random.java index b1163db0b18..661a29e303b 100644 --- a/src/java.base/share/classes/java/util/Random.java +++ b/src/java.base/share/classes/java/util/Random.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,8 +32,6 @@ import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; -import jdk.internal.util.random.RandomSupport.*; - import static jdk.internal.util.random.RandomSupport.*; import jdk.internal.misc.Unsafe; @@ -77,11 +75,6 @@ import jdk.internal.misc.Unsafe; * @author Frank Yellin * @since 1.0 */ -@RandomGeneratorProperties( - name = "Random", - i = 48, j = 0, k = 0, - equidistribution = 0 -) public class Random implements RandomGenerator, java.io.Serializable { /** diff --git a/src/java.base/share/classes/java/util/SplittableRandom.java b/src/java.base/share/classes/java/util/SplittableRandom.java index 4242322d687..cb1801f5680 100644 --- a/src/java.base/share/classes/java/util/SplittableRandom.java +++ b/src/java.base/share/classes/java/util/SplittableRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ import java.util.stream.LongStream; import java.util.stream.Stream; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A generator of uniform pseudorandom values (with period 264) @@ -87,11 +86,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @author Doug Lea * @since 1.8 */ -@RandomGeneratorProperties( - name = "SplittableRandom", - i = 64, j = 0, k = 0, - equidistribution = 1 -) public final class SplittableRandom implements RandomGenerator, SplittableGenerator { /* diff --git a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java index dd1ea70195b..baa4c0e4f73 100644 --- a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java +++ b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java @@ -85,12 +85,6 @@ import jdk.internal.misc.VM; * @since 1.7 * @author Doug Lea */ - -@RandomGeneratorProperties( - name = "ThreadLocalRandom", - i = 64, j = 0, k = 0, - equidistribution = 1 -) public final class ThreadLocalRandom extends Random { /* * This class implements the java.util.Random API (and subclasses diff --git a/src/java.base/share/classes/java/util/random/RandomGeneratorFactory.java b/src/java.base/share/classes/java/util/random/RandomGeneratorFactory.java index a7c06371368..030f7f70a0a 100644 --- a/src/java.base/share/classes/java/util/random/RandomGeneratorFactory.java +++ b/src/java.base/share/classes/java/util/random/RandomGeneratorFactory.java @@ -25,24 +25,30 @@ package java.util.random; -import java.lang.reflect.Constructor; +import jdk.internal.random.L128X1024MixRandom; +import jdk.internal.random.L128X128MixRandom; +import jdk.internal.random.L128X256MixRandom; +import jdk.internal.random.L32X64MixRandom; +import jdk.internal.random.L64X1024MixRandom; +import jdk.internal.random.L64X128MixRandom; +import jdk.internal.random.L64X128StarStarRandom; +import jdk.internal.random.L64X256MixRandom; +import jdk.internal.random.Xoroshiro128PlusPlus; +import jdk.internal.random.Xoshiro256PlusPlus; + import java.math.BigInteger; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import java.security.SecureRandom; import java.util.Objects; -import java.util.function.Function; import java.util.Map; +import java.util.Random; +import java.util.SplittableRandom; +import java.util.concurrent.ThreadLocalRandom; import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator; import java.util.random.RandomGenerator.JumpableGenerator; import java.util.random.RandomGenerator.LeapableGenerator; import java.util.random.RandomGenerator.SplittableGenerator; import java.util.random.RandomGenerator.StreamableGenerator; -import java.util.ServiceLoader; -import java.util.ServiceLoader.Provider; -import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * This is a factory class for generating multiple random number generators @@ -107,137 +113,268 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * */ public final class RandomGeneratorFactory { - /** - * Instance provider class of random number algorithm. - */ - private final Provider provider; - /** - * Provider RandomGeneratorProperties annotation. - */ - private volatile RandomGeneratorProperties properties; + private static final String DEFAULT_ALGORITHM = "L32X64MixRandom"; - /** - * Default provider constructor. - */ - private volatile Constructor ctor; + private record RandomGeneratorProperties( + Class rgClass, + String name, + String group, + int i, + int j, + int k, + int equidistribution, + int flags) { - /** - * Provider constructor with long seed. - */ - private Constructor ctorLong; + /* single bit masks composable with operator | */ + private static final int INSTANTIABLE = 1 << 0; + private static final int LONG_SEED = 1 << 1; + private static final int BYTE_ARRAY_SEED = 1 << 2; + private static final int STOCHASTIC = 1 << 3; + private static final int HARDWARE = 1 << 4; + private static final int DEPRECATED = 1 << 5; - /** - * Provider constructor with byte[] seed. - */ - private Constructor ctorBytes; + private static final int ALL_CONSTRUCTORS = INSTANTIABLE | LONG_SEED | BYTE_ARRAY_SEED; - - private static class FactoryMapHolder { - static final Map> FACTORY_MAP = createFactoryMap(); + private static final Map FACTORY_MAP = createFactoryMap(); /** - * Returns the factory map, lazily constructing map on first use. + * Returns the factory map, lazily constructing it on first use. + *

Although {@link ThreadLocalRandom} can only be accessed via + * {@link ThreadLocalRandom#current()}, a map entry is added nevertheless + * to record its properties that are otherwise not documented + * anywhere else. + *

Currently, no algorithm is deprecated. * - * @return Map of RandomGeneratorFactory classes. + * @return Map of RandomGeneratorProperties. */ - private static Map> createFactoryMap() { - FactoryMapHolder.class.getModule().addUses(RandomGenerator.class); - return ServiceLoader - .load(RandomGenerator.class, ClassLoader.getPlatformClassLoader()) - .stream() - .filter(p -> !p.type().isInterface()) - .collect(Collectors.toMap(p -> p.type().getSimpleName(), Function.identity())); + private static Map createFactoryMap() { + return Map.ofEntries( + entry(SecureRandom.class, "SecureRandom", "Legacy", + 0, 0, 0, Integer.MAX_VALUE, + INSTANTIABLE | BYTE_ARRAY_SEED | STOCHASTIC | deprecationBit(SecureRandom.class)), + entry(Random.class, "Random", "Legacy", + 48, 0, 0, 0, + INSTANTIABLE | LONG_SEED | deprecationBit(Random.class)), + entry(SplittableRandom.class, "SplittableRandom", "Legacy", + 64, 0, 0, 1, + INSTANTIABLE | LONG_SEED | deprecationBit(SplittableRandom.class)), + entry(L32X64MixRandom.class, "L32X64MixRandom", "LXM", + 64, 1, 32, 1, + ALL_CONSTRUCTORS), + entry(L64X128MixRandom.class, "L64X128MixRandom", "LXM", + 128, 1, 64, 2, + ALL_CONSTRUCTORS), + entry(L64X128StarStarRandom.class, "L64X128StarStarRandom", "LXM", + 128, 1, 64, 2, + ALL_CONSTRUCTORS), + entry(L64X256MixRandom.class, "L64X256MixRandom", "LXM", + 256, 1, 64, 4, + ALL_CONSTRUCTORS), + entry(L64X1024MixRandom.class, "L64X1024MixRandom", "LXM", + 1024, 1, 64, 16, + ALL_CONSTRUCTORS), + entry(L128X128MixRandom.class, "L128X128MixRandom", "LXM", + 128, 1, 128, 1, + ALL_CONSTRUCTORS), + entry(L128X256MixRandom.class, "L128X256MixRandom", "LXM", + 256, 1, 128, 1, + ALL_CONSTRUCTORS), + entry(L128X1024MixRandom.class, "L128X1024MixRandom", "LXM", + 1024, 1, 128, 1, + ALL_CONSTRUCTORS), + entry(Xoroshiro128PlusPlus.class, "Xoroshiro128PlusPlus", "Xoroshiro", + 128, 1, 0, 1, + ALL_CONSTRUCTORS), + entry(Xoshiro256PlusPlus.class, "Xoshiro256PlusPlus", "Xoshiro", + 256, 1, 0, 3, + ALL_CONSTRUCTORS), + entry(ThreadLocalRandom.class, "ThreadLocalRandom", "Legacy", + 64, 0, 0, 1, + deprecationBit(ThreadLocalRandom.class)) + ); + } + + private static Map.Entry entry( + Class rgClass, String name, String group, + int i, int j, int k, int equidistribution, + int flags) { + return Map.entry(name, + new RandomGeneratorProperties(rgClass, name, group, + i, j, k, equidistribution, + flags)); + } + + private static int deprecationBit(Class rgClass) { + return rgClass.isAnnotationPresent(Deprecated.class) ? DEPRECATED : 0; + } + + private RandomGenerator create() { + return switch (name) { + case "Random" -> new Random(); + case "SecureRandom" -> new SecureRandom(); + case "SplittableRandom" -> new SplittableRandom(); + case "L32X64MixRandom" -> new L32X64MixRandom(); + case "L64X128MixRandom" -> new L64X128MixRandom(); + case "L64X128StarStarRandom" -> new L64X128StarStarRandom(); + case "L64X256MixRandom" -> new L64X256MixRandom(); + case "L64X1024MixRandom" -> new L64X1024MixRandom(); + case "L128X128MixRandom" -> new L128X128MixRandom(); + case "L128X256MixRandom" -> new L128X256MixRandom(); + case "L128X1024MixRandom" -> new L128X1024MixRandom(); + case "Xoroshiro128PlusPlus" -> new Xoroshiro128PlusPlus(); + case "Xoshiro256PlusPlus" -> new Xoshiro256PlusPlus(); + default -> throw new InternalError("should not happen"); + }; + } + + private RandomGenerator create(long seed) { + if (isInstantiable() && (flags & LONG_SEED) == 0) { + throw new UnsupportedOperationException("Random algorithm " + + name + " does not support a long seed"); + } + return switch (name) { + case "Random" -> new Random(seed); + case "SplittableRandom" -> new SplittableRandom(seed); + case "L32X64MixRandom" -> new L32X64MixRandom(seed); + case "L64X128MixRandom" -> new L64X128MixRandom(seed); + case "L64X128StarStarRandom" -> new L64X128StarStarRandom(seed); + case "L64X256MixRandom" -> new L64X256MixRandom(seed); + case "L64X1024MixRandom" -> new L64X1024MixRandom(seed); + case "L128X128MixRandom" -> new L128X128MixRandom(seed); + case "L128X256MixRandom" -> new L128X256MixRandom(seed); + case "L128X1024MixRandom" -> new L128X1024MixRandom(seed); + case "Xoroshiro128PlusPlus" -> new Xoroshiro128PlusPlus(seed); + case "Xoshiro256PlusPlus" -> new Xoshiro256PlusPlus(seed); + default -> throw new InternalError("should not happen"); + }; + } + + private RandomGenerator create(byte[] seed) { + if (isInstantiable() && (flags & BYTE_ARRAY_SEED) == 0) { + throw new UnsupportedOperationException("Random algorithm " + + name + " does not support a byte[] seed"); + } + return switch (name) { + case "SecureRandom" -> new SecureRandom(seed); + case "L32X64MixRandom" -> new L32X64MixRandom(seed); + case "L64X128MixRandom" -> new L64X128MixRandom(seed); + case "L64X128StarStarRandom" -> new L64X128StarStarRandom(seed); + case "L64X256MixRandom" -> new L64X256MixRandom(seed); + case "L64X1024MixRandom" -> new L64X1024MixRandom(seed); + case "L128X128MixRandom" -> new L128X128MixRandom(seed); + case "L128X256MixRandom" -> new L128X256MixRandom(seed); + case "L128X1024MixRandom" -> new L128X1024MixRandom(seed); + case "Xoroshiro128PlusPlus" -> new Xoroshiro128PlusPlus(seed); + case "Xoshiro256PlusPlus" -> new Xoshiro256PlusPlus(seed); + default -> throw new InternalError("should not happen"); + }; + } + + private boolean isStochastic() { + return (flags & STOCHASTIC) != 0; + } + + private boolean isHardware() { + return (flags & HARDWARE) != 0; + } + + private boolean isInstantiable() { + return (flags & INSTANTIABLE) != 0; + } + + private boolean isDeprecated() { + return (flags & DEPRECATED) != 0; + } + + private BigInteger period() { + /* + * 0 if i = j = k = 0 + * (2^i - j) 2^k otherwise + */ + return i == 0 && j == 0 && k == 0 + ? BigInteger.ZERO + : BigInteger.ONE.shiftLeft(i).subtract(BigInteger.valueOf(j)).shiftLeft(k); + } + + private int stateBits() { + return i == 0 && k == 0 ? Integer.MAX_VALUE : i + k; } } + /** + * Random generator properties. + */ + private final RandomGeneratorProperties properties; + /** * Private constructor. * - * @param provider Provider class to wrap. + * @param properties Random generator properties. */ - private RandomGeneratorFactory(Provider provider) { - this.provider = provider; + private RandomGeneratorFactory(RandomGeneratorProperties properties) { + this.properties = properties; } /** - * Returns the factory map, lazily constructing map on first call. + * Returns the factory map, lazily constructing the map on first call. * - * @return Map of RandomGeneratorFactory classes. + * @return Map of random generator classes. */ - private static Map> getFactoryMap() { - return FactoryMapHolder.FACTORY_MAP; + private static Map getFactoryMap() { + return RandomGeneratorProperties.FACTORY_MAP; } /** - * Return the annotation for the specified provider. - * - * @return RandomGeneratorProperties annotation for the specified provider. - */ - private RandomGeneratorProperties getProperties() { - if (properties == null) { - synchronized (provider) { - if (properties == null) { - properties = provider.type().getDeclaredAnnotation(RandomGeneratorProperties.class); - Objects.requireNonNull(properties, provider.type() + " missing annotation"); - } - } - } - - return properties; - } - - /** - * Return true if the provider is a subclass of the category. + * Return true if the random generator class is a subclass of the category. * * @param category Interface category, sub-interface of {@link RandomGenerator}. * - * @return true if the provider is a subclass of the category. + * @return true if the random generator class is a subclass of the category. */ private boolean isSubclass(Class category) { - return isSubclass(category, provider); + return isSubclass(category, properties.rgClass()); } /** - * Return true if the provider is a subclass of the category. + * Return true if rgClass is a subclass of the category. * * @param category Interface category, sub-interface of {@link RandomGenerator}. - * @param provider Provider that is being filtered. + * @param rgClass Class that is being filtered. * - * @return true if the provider is a subclass of the category. + * @return true if rgClass is a subclass of the category. */ private static boolean isSubclass(Class category, - Provider provider) { - return provider != null && category.isAssignableFrom(provider.type()); + Class rgClass) { + return rgClass != null && category.isAssignableFrom(rgClass); } /** - * Returns the provider matching name and category. + * Returns a RandomGeneratorProperties instance matching name and category. * - * @param name Name of RandomGenerator - * @param category Interface category, sub-interface of {@link RandomGenerator}. - * - * @return A provider matching name and category. - * - * @throws IllegalArgumentException if provider is not a subclass of category. + * @param name Name of RandomGenerator + * @param category Interface category, sub-interface of {@link RandomGenerator}. + * @return A RandomGeneratorProperties instance matching name and category. + * @throws IllegalArgumentException if the resulting type is not a subclass of category. */ - private static Provider findProvider(String name, - Class category) - throws IllegalArgumentException { - Map> fm = getFactoryMap(); - Provider provider = fm.get(name); - if (provider == null) { + private static RandomGeneratorProperties findClass(String name, + Class category) throws IllegalArgumentException { + RandomGeneratorProperties properties = name != null + ? getFactoryMap().get(name) + : null; + if (properties == null || !properties.isInstantiable()) { throw new IllegalArgumentException("No implementation of the random number generator algorithm \"" + - name + - "\" is available"); - } else if (!isSubclass(category, provider)) { - throw new IllegalArgumentException("The random number generator algorithm \"" + - name + - "\" is not implemented with the interface \"" + - category.getSimpleName() + - "\""); + name + + "\" is available"); } - return provider; + if (!isSubclass(category, properties.rgClass())) { + throw new IllegalArgumentException("The random number generator algorithm \"" + + name + + "\" is not implemented with the interface \"" + + category.getSimpleName() + + "\""); + } + return properties; } /** @@ -255,8 +392,8 @@ public final class RandomGeneratorFactory { static T of(String name, Class category) throws IllegalArgumentException { @SuppressWarnings("unchecked") - T uncheckedRandomGenerator = (T)findProvider(name, category).get(); - return uncheckedRandomGenerator; + T instance = (T) findClass(name, category).create(); + return instance; } /** @@ -273,68 +410,7 @@ public final class RandomGeneratorFactory { */ static RandomGeneratorFactory factoryOf(String name, Class category) throws IllegalArgumentException { - Provider uncheckedProvider = findProvider(name, category); - return new RandomGeneratorFactory<>(uncheckedProvider); - } - - /** - * Fetch the required constructors for class of random number algorithm. - * - * @param randomGeneratorClass class of random number algorithm (provider) - */ - private void getConstructors(Class randomGeneratorClass) { - if (ctor == null) { - synchronized (provider) { - if (ctor == null) { - PrivilegedExceptionAction[]> ctorAction = randomGeneratorClass::getConstructors; - try { - @SuppressWarnings("removal") - Constructor[] ctors = AccessController.doPrivileged(ctorAction); - - Constructor tmpCtor = null; - Constructor tmpCtorLong = null; - Constructor tmpCtorBytes = null; - - - for (Constructor ctorGeneric : ctors) { - @SuppressWarnings("unchecked") - Constructor ctorSpecific = (Constructor) ctorGeneric; - final Class[] parameterTypes = ctorSpecific.getParameterTypes(); - - if (parameterTypes.length == 0) { - tmpCtor = ctorSpecific; - } else if (parameterTypes.length == 1) { - Class argType = parameterTypes[0]; - - if (argType == long.class) { - tmpCtorLong = ctorSpecific; - } else if (argType == byte[].class) { - tmpCtorBytes = ctorSpecific; - } - } - } - - if (tmpCtor == null) { - throw new IllegalStateException("Random algorithm " + name() + " is missing a default constructor"); - } - - // Store specialized constructors first, guarded by ctor - ctorBytes = tmpCtorBytes; - ctorLong = tmpCtorLong; - ctor = tmpCtor; - } catch (PrivilegedActionException ex) { - // Do nothing - } - } - } - } - } - - /** - * Ensure all the required constructors are fetched. - */ - private void ensureConstructors() { - getConstructors(provider.type()); + return new RandomGeneratorFactory<>(findClass(name, category)); } /** @@ -355,7 +431,7 @@ public final class RandomGeneratorFactory { Objects.requireNonNull(name); @SuppressWarnings("unchecked") RandomGeneratorFactory factory = - (RandomGeneratorFactory)factoryOf(name, RandomGenerator.class); + (RandomGeneratorFactory) factoryOf(name, RandomGenerator.class); return factory; } @@ -369,23 +445,21 @@ public final class RandomGeneratorFactory { * @return a {@link RandomGeneratorFactory} */ public static RandomGeneratorFactory getDefault() { - return factoryOf("L32X64MixRandom", RandomGenerator.class); + return factoryOf(DEFAULT_ALGORITHM, RandomGenerator.class); } /** * Returns a non-empty stream of available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}. - *

+ * * RandomGenerators that are marked as deprecated are not included in the result. * * @return a non-empty stream of all available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}. */ public static Stream> all() { - Map> fm = getFactoryMap(); - return fm.values() - .stream() - .filter(p -> !p.type().isAnnotationPresent(Deprecated.class) && - p.type().isAnnotationPresent(RandomGeneratorProperties.class)) - .map(RandomGeneratorFactory::new); + return getFactoryMap().values() + .stream() + .filter(p -> p.isInstantiable() && !p.isDeprecated()) + .map(RandomGeneratorFactory::new); } /** @@ -395,7 +469,7 @@ public final class RandomGeneratorFactory { * @return Name of the algorithm. */ public String name() { - return provider.type().getSimpleName(); + return properties.name(); } /** @@ -405,7 +479,7 @@ public final class RandomGeneratorFactory { * @return Group name of the algorithm. */ public String group() { - return getProperties().group(); + return properties.group(); } /** @@ -416,11 +490,7 @@ public final class RandomGeneratorFactory { * to maintain state of seed. */ public int stateBits() { - RandomGeneratorProperties properties = getProperties(); - int i = properties.i(); - int k = properties.k(); - - return i == 0 && k == 0 ? Integer.MAX_VALUE : i + k; + return properties.stateBits(); } /** @@ -429,7 +499,7 @@ public final class RandomGeneratorFactory { * @return the equidistribution of the algorithm. */ public int equidistribution() { - return getProperties().equidistribution(); + return properties.equidistribution(); } /** @@ -440,16 +510,7 @@ public final class RandomGeneratorFactory { * @return BigInteger period. */ public BigInteger period() { - RandomGeneratorProperties properties = getProperties(); - int i = properties.i(); - int j = properties.j(); - int k = properties.k(); - - if (i == 0 && j == 0 && k == 0) { - return BigInteger.ZERO; - } else { - return BigInteger.ONE.shiftLeft(i).subtract(BigInteger.valueOf(j)).shiftLeft(k); - } + return properties.period(); } /** @@ -460,7 +521,7 @@ public final class RandomGeneratorFactory { * @return true if random generator is statistical. */ public boolean isStatistical() { - return !getProperties().isStochastic(); + return !properties.isStochastic(); } /** @@ -470,7 +531,7 @@ public final class RandomGeneratorFactory { * @return true if random generator is stochastic. */ public boolean isStochastic() { - return getProperties().isStochastic(); + return properties.isStochastic(); } /** @@ -480,7 +541,7 @@ public final class RandomGeneratorFactory { * @return true if random generator is generated by hardware. */ public boolean isHardware() { - return getProperties().isHardware(); + return properties.isHardware(); } /** @@ -547,66 +608,64 @@ public final class RandomGeneratorFactory { * marked for deprecation */ public boolean isDeprecated() { - return provider.type().isAnnotationPresent(Deprecated.class); + return properties.isDeprecated(); } /** - * Create an instance of {@link RandomGenerator} based on + * Create an instance of {@link RandomGenerator} based on the * algorithm chosen. * - * @return new in instance of {@link RandomGenerator}. - * + * @return new instance of {@link RandomGenerator}. */ public T create() { - try { - ensureConstructors(); - return ctor.newInstance(); - } catch (Exception ex) { - // Should never happen. - throw new IllegalStateException("Random algorithm " + name() + " is missing a default constructor", ex); - } + @SuppressWarnings("unchecked") + T instance = (T) properties.create(); + return instance; } /** - * Create an instance of {@link RandomGenerator} based on - * algorithm chosen - * providing a starting long seed. If long seed is not supported by an - * algorithm then the no argument form of create is used. + * Create an instance of {@link RandomGenerator} based on the + * algorithm chosen, + * and the provided {@code seed}. + * If the {@link RandomGenerator} doesn't support instantiation through + * a {@code seed} of type {@code long} then this method throws + * an {@link UnsupportedOperationException}. * * @param seed long random seed value. * - * @return new in instance of {@link RandomGenerator}. + * @return new instance of {@link RandomGenerator}. + * + * @throws UnsupportedOperationException + * if a {@code seed} of type {@code long} in not supported. */ public T create(long seed) { - try { - ensureConstructors(); - return ctorLong.newInstance(seed); - } catch (Exception ex) { - return create(); - } + @SuppressWarnings("unchecked") + T instance = (T) properties.create(seed); + return instance; } /** - * Create an instance of {@link RandomGenerator} based on - * algorithm chosen - * providing a starting byte[] seed. If byte[] seed is not supported by an - * algorithm then the no - * argument form of create is used. + * Create an instance of {@link RandomGenerator} based on the + * algorithm chosen, + * and the provided {@code seed}. + * If the {@link RandomGenerator} doesn't support instantiation through + * a {@code seed} of type {@code byte[]} then this method throws + * an {@link UnsupportedOperationException}. * * @param seed byte array random seed value. * - * @return new in instance of {@link RandomGenerator}. + * @return new instance of {@link RandomGenerator}. + * + * @throws UnsupportedOperationException + * if a {@code seed} of type {@code byte[]} in not supported. * * @throws NullPointerException if seed is null. */ public T create(byte[] seed) { Objects.requireNonNull(seed, "seed must not be null"); - try { - ensureConstructors(); - return ctorBytes.newInstance(seed); - } catch (Exception ex) { - return create(); - } + @SuppressWarnings("unchecked") + T instance = (T) properties.create(seed); + return instance; } } diff --git a/src/java.base/share/classes/java/util/random/package-info.java b/src/java.base/share/classes/java/util/random/package-info.java index 2e16b319f44..9f9df0ff395 100644 --- a/src/java.base/share/classes/java/util/random/package-info.java +++ b/src/java.base/share/classes/java/util/random/package-info.java @@ -81,8 +81,8 @@ *

{@code import java.util.random.*;}
* * Then one can choose a specific implementation by giving the name of a generator - * algorithm to the static method {@link RandomGenerator#of}, in which case the - * no-arguments constructor for that implementation is used: + * algorithm to the static method {@link RandomGenerator#of}, in which case + * a {@link RandomGenerator} is constructed without any seed value: * *
{@code RandomGenerator g = RandomGenerator.of("L64X128MixRandom");}
* @@ -125,8 +125,8 @@ * *

Choosing a Random Number Generator Algorithm

* - *

There are three groups of random number generator algorithm provided - * in Java: the Legacy group, the LXM group, and the Xoroshiro/Xoshiro group. + *

Random number generator algorithms are organized in groups, + * as described {@linkplain java.util.random##algorithms below}. * *

The legacy group includes random number generators that existed * before JDK 17: Random, ThreadLocalRandom, SplittableRandom, and @@ -304,6 +304,13 @@ * 1 * * + * SecureRandom + * Legacy + * BigInteger.ZERO + * Integer.MAX_VALUE + * Integer.MAX_VALUE + * + * * ThreadLocalRandom * * Legacy * BigInteger.ONE.shiftLeft(64) diff --git a/src/java.base/share/classes/jdk/internal/random/L128X1024MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L128X1024MixRandom.java index e6f4f896dc0..4af9ecc3dd4 100644 --- a/src/java.base/share/classes/jdk/internal/random/L128X1024MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L128X1024MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L128X1024MixRandom", - group = "LXM", - i = 1024, j = 1, k = 128, - equidistribution = 1 -) public final class L128X1024MixRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/L128X128MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L128X128MixRandom.java index d03fcfc0864..1323139ea59 100644 --- a/src/java.base/share/classes/jdk/internal/random/L128X128MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L128X128MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L128X128MixRandom", - group = "LXM", - i = 128, j = 1, k = 128, - equidistribution = 1 -) public final class L128X128MixRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/L128X256MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L128X256MixRandom.java index 81440d7316a..19fd31b069a 100644 --- a/src/java.base/share/classes/jdk/internal/random/L128X256MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L128X256MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L128X256MixRandom", - group = "LXM", - i = 256, j = 1, k = 128, - equidistribution = 1 -) public final class L128X256MixRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/L32X64MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L32X64MixRandom.java index 0f6beea7bc7..a9442bd0ef2 100644 --- a/src/java.base/share/classes/jdk/internal/random/L32X64MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L32X64MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L32X64MixRandom", - group = "LXM", - i = 64, j = 1, k = 32, - equidistribution = 1 -) public final class L32X64MixRandom extends AbstractSplittableWithBrineGenerator { /* * Implementation Overview. diff --git a/src/java.base/share/classes/jdk/internal/random/L64X1024MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L64X1024MixRandom.java index e4eb82d450e..074aac0c70e 100644 --- a/src/java.base/share/classes/jdk/internal/random/L64X1024MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L64X1024MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L64X1024MixRandom", - group = "LXM", - i = 1024, j = 1, k = 64, - equidistribution = 16 -) public final class L64X1024MixRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/L64X128MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L64X128MixRandom.java index 7dd757f312b..37e10237c39 100644 --- a/src/java.base/share/classes/jdk/internal/random/L64X128MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L64X128MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L64X128MixRandom", - group = "LXM", - i = 128, j = 1, k = 64, - equidistribution = 2 -) public final class L64X128MixRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/L64X128StarStarRandom.java b/src/java.base/share/classes/jdk/internal/random/L64X128StarStarRandom.java index 15b9bc312da..1d1a00ca006 100644 --- a/src/java.base/share/classes/jdk/internal/random/L64X128StarStarRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L64X128StarStarRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L64X128StarStarRandom", - group = "LXM", - i = 128, j = 1, k = 64, - equidistribution = 2 -) public final class L64X128StarStarRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/L64X256MixRandom.java b/src/java.base/share/classes/jdk/internal/random/L64X256MixRandom.java index bb605df5fdc..109148e3f01 100644 --- a/src/java.base/share/classes/jdk/internal/random/L64X256MixRandom.java +++ b/src/java.base/share/classes/jdk/internal/random/L64X256MixRandom.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import jdk.internal.util.random.RandomSupport; import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "splittable" pseudorandom number generator (PRNG) whose period @@ -75,12 +74,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "L64X256MixRandom", - group = "LXM", - i = 256, j = 1, k = 64, - equidistribution = 4 -) public final class L64X256MixRandom extends AbstractSplittableWithBrineGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/Xoroshiro128PlusPlus.java b/src/java.base/share/classes/jdk/internal/random/Xoroshiro128PlusPlus.java index d20b3cf9e6a..3dc90fff4d0 100644 --- a/src/java.base/share/classes/jdk/internal/random/Xoroshiro128PlusPlus.java +++ b/src/java.base/share/classes/jdk/internal/random/Xoroshiro128PlusPlus.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import java.util.random.RandomGenerator.LeapableGenerator; import jdk.internal.util.random.RandomSupport; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "jumpable and leapable" pseudorandom number generator (PRNG) whose period @@ -72,12 +71,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "Xoroshiro128PlusPlus", - group = "Xoroshiro", - i = 128, j = 1, k = 0, - equidistribution = 1 -) public final class Xoroshiro128PlusPlus implements LeapableGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/random/Xoshiro256PlusPlus.java b/src/java.base/share/classes/jdk/internal/random/Xoshiro256PlusPlus.java index fdefe02dd03..1ae4aeaac02 100644 --- a/src/java.base/share/classes/jdk/internal/random/Xoshiro256PlusPlus.java +++ b/src/java.base/share/classes/jdk/internal/random/Xoshiro256PlusPlus.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import java.util.random.RandomGenerator.LeapableGenerator; import jdk.internal.util.random.RandomSupport; -import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; /** * A "jumpable and leapable" pseudorandom number generator (PRNG) whose period @@ -87,12 +86,6 @@ import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties; * @since 17 * */ -@RandomGeneratorProperties( - name = "Xoshiro256PlusPlus", - group = "Xoshiro", - i = 256, j = 1, k = 0, - equidistribution = 3 -) public final class Xoshiro256PlusPlus implements LeapableGenerator { /* diff --git a/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java b/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java index c80d67005ae..402be6b5dfe 100644 --- a/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,7 @@ package jdk.internal.util.random; -import java.lang.annotation.*; -import java.math.BigInteger; import java.util.Objects; -import java.util.Random; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; @@ -53,49 +50,6 @@ import java.util.stream.StreamSupport; * @since 17 */ public class RandomSupport { - /** - * Annotation providing RandomGenerator properties. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface RandomGeneratorProperties { - /** - * Name of algorithm. - */ - String name(); - - /** - * Category of algorithm. - */ - String group() default "Legacy"; - - /** - * Algorithm period defined as: - * - * BigInteger.ONE.shiftLeft(i) - * .subtract(j) - * .shiftLeft(k) - */ - int i() default 0; - int j() default 0; - int k() default 0; - - /** - * The equidistribution of the algorithm. - */ - int equidistribution() default Integer.MAX_VALUE; - - /** - * Is the algorithm based on entropy (true random.) - */ - boolean isStochastic() default false; - - /** - * Is the algorithm assisted by hardware (fast true random.) - */ - boolean isHardware() default false; - } - /* * Implementation Overview. * diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index edbbad78c9d..a8b0a0c992c 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -419,19 +419,4 @@ module java.base { provides java.nio.file.spi.FileSystemProvider with jdk.internal.jrtfs.JrtFileSystemProvider; - provides java.util.random.RandomGenerator with - java.security.SecureRandom, - java.util.Random, - java.util.SplittableRandom, - jdk.internal.random.L32X64MixRandom, - jdk.internal.random.L64X128MixRandom, - jdk.internal.random.L64X128StarStarRandom, - jdk.internal.random.L64X256MixRandom, - jdk.internal.random.L64X1024MixRandom, - jdk.internal.random.L128X128MixRandom, - jdk.internal.random.L128X256MixRandom, - jdk.internal.random.L128X1024MixRandom, - jdk.internal.random.Xoroshiro128PlusPlus, - jdk.internal.random.Xoshiro256PlusPlus; - } diff --git a/test/jdk/java/util/Random/RandomTestCoverage.java b/test/jdk/java/util/Random/RandomTestCoverage.java index be12d188198..a0e906fbbe2 100644 --- a/test/jdk/java/util/Random/RandomTestCoverage.java +++ b/test/jdk/java/util/Random/RandomTestCoverage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,7 +87,7 @@ public class RandomTestCoverage { DoubleStream doubleStream4 = rng.doubles(5, 0.5, 1.0); } - static void checkPredicates(RandomGeneratorFactory factory) { + static void checkPredicates(RandomGeneratorFactory factory) { RandomGenerator rng = factory.create(); if (rng instanceof ArbitrarilyJumpableGenerator != factory.isArbitrarilyJumpable()) { throw new RuntimeException("isArbitrarilyJumpable failing"); @@ -156,7 +156,7 @@ public class RandomTestCoverage { coverFactory(RandomGeneratorFactory.of(name)); } - static void coverFactory(RandomGeneratorFactory factory) { + static void coverFactory(RandomGeneratorFactory factory) { String name = factory.name(); String group = factory.group(); int stateBits = factory.stateBits(); @@ -171,8 +171,34 @@ public class RandomTestCoverage { boolean isSplittable = factory.isSplittable(); coverRandomGenerator(factory.create()); - coverRandomGenerator(factory.create(12345L)); - coverRandomGenerator(factory.create(new byte[] {1, 2, 3, 4, 5, 6, 7, 8})); + // test create(long) + switch (factory.name()) { + // SecureRandom doesn't have long constructors so we expect + // UnsupportedOperationException + case "SecureRandom" -> { + try { + factory.create(12345L); + throw new AssertionError("RandomGeneratorFactory.create(long) was expected" + + "to throw UnsupportedOperationException for " + factory.name() + " but didn't"); + } catch (UnsupportedOperationException ignored) { + } + } + default -> coverRandomGenerator(factory.create(12345L)); + } + // test create(byte[]) + switch (factory.name()) { + // these don't have byte[] constructors so we expect UnsupportedOperationException + case "Random", + "SplittableRandom" -> { + try { + factory.create(new byte[] {1, 2, 3, 4, 5, 6, 7, 8}); + throw new AssertionError("RandomGeneratorFactory.create(byte[]) was expected" + + "to throw UnsupportedOperationException for " + factory.name() + " but didn't"); + } catch (UnsupportedOperationException ignored) { + } + } + default -> coverRandomGenerator(factory.create(new byte[] {1, 2, 3, 4, 5, 6, 7, 8})); + } } static void coverDefaults() { @@ -188,32 +214,35 @@ public class RandomTestCoverage { coverOf(factory.name()); }); RandomGeneratorFactory.all() - .filter(f -> f.isStreamable()) + .filter(RandomGeneratorFactory::isStreamable) .forEach(factory -> { coverStreamable((StreamableGenerator)factory.create()); }); RandomGeneratorFactory.all() - .filter(f -> f.isSplittable()) + .filter(RandomGeneratorFactory::isSplittable) .forEach(factory -> { coverSplittable((SplittableGenerator)factory.create()); }); RandomGeneratorFactory.all() - .filter(f -> f.isJumpable()) + .filter(RandomGeneratorFactory::isJumpable) .forEach(factory -> { coverJumpable((JumpableGenerator)factory.create()); }); RandomGeneratorFactory.all() - .filter(f -> f.isLeapable()) + .filter(RandomGeneratorFactory::isLeapable) .forEach(factory -> { coverLeapable((LeapableGenerator)factory.create()); }); RandomGeneratorFactory.all() - .filter(f -> f.isArbitrarilyJumpable()) + .filter(RandomGeneratorFactory::isArbitrarilyJumpable) .forEach(factory -> { coverArbitrarilyJumpable((ArbitrarilyJumpableGenerator)factory.create()); }); + RandomGeneratorFactory.all() + .forEach(RandomTestCoverage::checkPredicates); coverRandomGenerator(new SecureRandom()); coverRandomGenerator(ThreadLocalRandom.current()); + coverDefaults(); } }