Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 60 additions & 14 deletions lib/src/random/generator_js.dart
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 1 addition & 3 deletions lib/src/random/generator_vm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
10 changes: 5 additions & 5 deletions lib/src/random/generators.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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,
]) {
Expand Down
Loading