8332086: Remove the usage of ServiceLoader in j.u.r.RandomGeneratorFactory

8332476: j.u.r.RandomGeneratorFactor.create(long|byte[]) should throw rather than silently fallback to no-arg create()

Reviewed-by: jpai
This commit is contained in:
Raffaello Giulietti 2024-05-21 12:53:03 +00:00
parent 5cf8288b80
commit 42e3c842ae
19 changed files with 337 additions and 397 deletions

View File

@ -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 =

View File

@ -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 {
/**

View File

@ -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 2<sup>64</sup>)
@ -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 {
/*

View File

@ -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

View File

@ -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<T extends RandomGenerator> {
/**
* Instance provider class of random number algorithm.
*/
private final Provider<? extends RandomGenerator> provider;
/**
* Provider RandomGeneratorProperties annotation.
*/
private volatile RandomGeneratorProperties properties;
private static final String DEFAULT_ALGORITHM = "L32X64MixRandom";
/**
* Default provider constructor.
*/
private volatile Constructor<T> ctor;
private record RandomGeneratorProperties(
Class<? extends RandomGenerator> rgClass,
String name,
String group,
int i,
int j,
int k,
int equidistribution,
int flags) {
/**
* Provider constructor with long seed.
*/
private Constructor<T> 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<T> ctorBytes;
private static final int ALL_CONSTRUCTORS = INSTANTIABLE | LONG_SEED | BYTE_ARRAY_SEED;
private static class FactoryMapHolder {
static final Map<String, Provider<? extends RandomGenerator>> FACTORY_MAP = createFactoryMap();
private static final Map<String, RandomGeneratorProperties> FACTORY_MAP = createFactoryMap();
/**
* Returns the factory map, lazily constructing map on first use.
* Returns the factory map, lazily constructing it on first use.
* <p> 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.
* <p> Currently, no algorithm is deprecated.
*
* @return Map of RandomGeneratorFactory classes.
* @return Map of RandomGeneratorProperties.
*/
private static Map<String, Provider<? extends RandomGenerator>> 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<String, RandomGeneratorProperties> 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<String, RandomGeneratorProperties> entry(
Class<? extends RandomGenerator> 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<? extends RandomGenerator> 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<? extends RandomGenerator> 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<String, Provider<? extends RandomGenerator>> getFactoryMap() {
return FactoryMapHolder.FACTORY_MAP;
private static Map<String, RandomGeneratorProperties> 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<? extends RandomGenerator> 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<? extends RandomGenerator> category,
Provider<? extends RandomGenerator> provider) {
return provider != null && category.isAssignableFrom(provider.type());
Class<? extends RandomGenerator> 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<? extends RandomGenerator> findProvider(String name,
Class<? extends RandomGenerator> category)
throws IllegalArgumentException {
Map<String, Provider<? extends RandomGenerator>> fm = getFactoryMap();
Provider<? extends RandomGenerator> provider = fm.get(name);
if (provider == null) {
private static RandomGeneratorProperties findClass(String name,
Class<? extends RandomGenerator> 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<T extends RandomGenerator> {
static <T extends RandomGenerator> T of(String name, Class<T> 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<T extends RandomGenerator> {
*/
static <T extends RandomGenerator> RandomGeneratorFactory<T> factoryOf(String name, Class<T> category)
throws IllegalArgumentException {
Provider<? extends RandomGenerator> 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<? extends RandomGenerator> randomGeneratorClass) {
if (ctor == null) {
synchronized (provider) {
if (ctor == null) {
PrivilegedExceptionAction<Constructor<?>[]> ctorAction = randomGeneratorClass::getConstructors;
try {
@SuppressWarnings("removal")
Constructor<?>[] ctors = AccessController.doPrivileged(ctorAction);
Constructor<T> tmpCtor = null;
Constructor<T> tmpCtorLong = null;
Constructor<T> tmpCtorBytes = null;
for (Constructor<?> ctorGeneric : ctors) {
@SuppressWarnings("unchecked")
Constructor<T> ctorSpecific = (Constructor<T>) 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<T extends RandomGenerator> {
Objects.requireNonNull(name);
@SuppressWarnings("unchecked")
RandomGeneratorFactory<T> factory =
(RandomGeneratorFactory<T>)factoryOf(name, RandomGenerator.class);
(RandomGeneratorFactory<T>) factoryOf(name, RandomGenerator.class);
return factory;
}
@ -369,23 +445,21 @@ public final class RandomGeneratorFactory<T extends RandomGenerator> {
* @return a {@link RandomGeneratorFactory}
*/
public static RandomGeneratorFactory<RandomGenerator> getDefault() {
return factoryOf("L32X64MixRandom", RandomGenerator.class);
return factoryOf(DEFAULT_ALGORITHM, RandomGenerator.class);
}
/**
* Returns a non-empty stream of available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}.
* <p>
*
* 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<RandomGeneratorFactory<RandomGenerator>> all() {
Map<String, Provider<? extends RandomGenerator>> 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<T extends RandomGenerator> {
* @return Name of the <a href="package-summary.html#algorithms">algorithm</a>.
*/
public String name() {
return provider.type().getSimpleName();
return properties.name();
}
/**
@ -405,7 +479,7 @@ public final class RandomGeneratorFactory<T extends RandomGenerator> {
* @return Group name of the <a href="package-summary.html#algorithms">algorithm</a>.
*/
public String group() {
return getProperties().group();
return properties.group();
}
/**
@ -416,11 +490,7 @@ public final class RandomGeneratorFactory<T extends RandomGenerator> {
* 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<T extends RandomGenerator> {
* @return the equidistribution of the <a href="package-summary.html#algorithms">algorithm</a>.
*/
public int equidistribution() {
return getProperties().equidistribution();
return properties.equidistribution();
}
/**
@ -440,16 +510,7 @@ public final class RandomGeneratorFactory<T extends RandomGenerator> {
* @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<T extends RandomGenerator> {
* @return true if random generator is statistical.
*/
public boolean isStatistical() {
return !getProperties().isStochastic();
return !properties.isStochastic();
}
/**
@ -470,7 +531,7 @@ public final class RandomGeneratorFactory<T extends RandomGenerator> {
* @return true if random generator is stochastic.
*/
public boolean isStochastic() {
return getProperties().isStochastic();
return properties.isStochastic();
}
/**
@ -480,7 +541,7 @@ public final class RandomGeneratorFactory<T extends RandomGenerator> {
* @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<T extends RandomGenerator> {
* 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
* <a href="package-summary.html#algorithms">algorithm</a> 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
* <a href="package-summary.html#algorithms">algorithm</a> 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
* <a href="package-summary.html#algorithms">algorithm</a> 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
* <a href="package-summary.html#algorithms">algorithm</a> chosen
* providing a starting byte[] seed. If byte[] seed is not supported by an
* <a href="package-summary.html#algorithms">algorithm</a> then the no
* argument form of create is used.
* Create an instance of {@link RandomGenerator} based on the
* <a href="package-summary.html#algorithms">algorithm</a> 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;
}
}

View File

@ -81,8 +81,8 @@
* <blockquote>{@code import java.util.random.*;}</blockquote>
*
* 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:
*
* <blockquote>{@code RandomGenerator g = RandomGenerator.of("L64X128MixRandom");}</blockquote>
*
@ -125,8 +125,8 @@
*
* <h2>Choosing a Random Number Generator Algorithm</h2>
*
* <p> There are three groups of random number generator algorithm provided
* in Java: the Legacy group, the LXM group, and the Xoroshiro/Xoshiro group.
* <p> Random number generator algorithms are organized in groups,
* as described {@linkplain java.util.random##algorithms below}.
*
* <p> The legacy group includes random number generators that existed
* before JDK 17: Random, ThreadLocalRandom, SplittableRandom, and
@ -304,6 +304,13 @@
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <th scope="row" style="text-align:left">SecureRandom</th>
* <td style="text-align:left">Legacy</td>
* <td style="text-align:left">BigInteger.ZERO</td>
* <td style="text-align:right">Integer.MAX_VALUE</td>
* <td style="text-align:right">Integer.MAX_VALUE</td>
* </tr>
* <tr>
* <th scope="row" style="text-align:left">ThreadLocalRandom <sup>*</sup></th>
* <td style="text-align:left">Legacy</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(64)</td>

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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.

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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 {
/*

View File

@ -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.
*

View File

@ -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;
}

View File

@ -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<RandomGenerator> 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<RandomGenerator> 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();
}
}