diff --git a/lib/src/random/generator_js.dart b/lib/src/random/generator_js.dart index 99f6fbb..fec936e 100644 --- a/lib/src/random/generator_js.dart +++ b/lib/src/random/generator_js.dart @@ -1,24 +1,70 @@ // Copyright (c) 2024, Sudipto Chandra // All rights reserved. Check LICENSE file for details. -import 'dart:async'; import 'dart:math' show Random; +import 'dart:js_interop'; const int _mask32 = 0xFFFFFFFF; -int _seedCounter = Zone.current.hashCode; +const bool isDart2JS = bool.fromEnvironment('dart.tool.dart2js'); -/// Returns a secure random generator in JS runtime -Random secureRandom() => Random($generateSeed()); - -/// Generates a random seed in JS runtime -int $generateSeed() { - int code = DateTime.now().millisecondsSinceEpoch; - code -= _seedCounter++; - if (code.bitLength & 1 == 1) { - code *= ~code; +@JS() +@staticInterop +class Process {} + +@JS() +@staticInterop +class Versions {} + +@JS('process') +external Process? get _process; + +extension on Process { + external Versions? get versions; +} + +extension on Versions { + external JSAny get node; +} + +bool get isNodeDart2JS => _process?.versions?.node != null && isDart2JS; + +@JS() +@staticInterop +class Crypto {} + +extension on Crypto { + external int randomInt(final int max); +} + +@JS() +external Crypto require(final String id); + +/// For Node.js environment + dart2js compiler +class NodeRandom implements Random { + @override + int nextInt(final int max) { + if (max < 1 || max > _mask32 + 1) { + throw RangeError.range( + max, 1, _mask32 + 1, 'max', 'max must be <= (1 << 32)'); + } + return require('crypto').randomInt(max); + } + + @override + double nextDouble() { + final int first26Bits = nextInt(1 << 26); + final int next27Bits = nextInt(1 << 27); + final int random53Bits = (first26Bits << 27) + next27Bits; // JS int limit + return random53Bits / (1 << 53); } - code ^= ~_seedCounter << 5; - _seedCounter += code & 7; - return code & _mask32; + + @override + bool nextBool() => nextInt(2) == 1; } + +/// Returns a secure random generator in JS runtime +Random secureRandom() => isNodeDart2JS ? NodeRandom() : Random.secure(); + +/// Generates a random seed +int $generateSeed() => secureRandom().nextInt(_mask32); diff --git a/lib/src/random/generator_vm.dart b/lib/src/random/generator_vm.dart index 3c2bb5d..6e23e0c 100644 --- a/lib/src/random/generator_vm.dart +++ b/lib/src/random/generator_vm.dart @@ -11,6 +11,4 @@ Random secureRandom() => Random.secure(); /// Generates a random seed @pragma('vm:prefer-inline') -int $generateSeed() => - (DateTime.now().microsecondsSinceEpoch & _mask32) ^ - Random.secure().nextInt(_mask32); +int $generateSeed() => Random.secure().nextInt(_mask32); diff --git a/lib/src/random/generators.dart b/lib/src/random/generators.dart index 31462e1..5831cee 100644 --- a/lib/src/random/generators.dart +++ b/lib/src/random/generators.dart @@ -34,13 +34,13 @@ enum RNG { case RNG.keccak: return _keccakGenerateor(seed); case RNG.sha256: - return _hashGenerateor(SHA256Hash(), seed); + return _hashGenerator(SHA256Hash(), seed); case RNG.md5: - return _hashGenerateor(MD4Hash(), seed); + return _hashGenerator(MD4Hash(), seed); case RNG.xxh64: - return _hashGenerateor(XXHash64Sink(111), seed); + return _hashGenerator(XXHash64Sink(111), seed); case RNG.sm3: - return _hashGenerateor(SM3Hash(), seed); + return _hashGenerator(SM3Hash(), seed); case RNG.system: return _systemGenerator(seed); case RNG.secure: @@ -115,7 +115,7 @@ NextIntFunction _keccakGenerateor([int? seed]) { } /// Returns a iterable of 32-bit integers generated from the [sink]. -NextIntFunction _hashGenerateor( +NextIntFunction _hashGenerator( HashDigestSink sink, [ int? seed, ]) {