dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.19k stars 1.56k forks source link

[breaking change] Add `Random.fillBytes` method. #55836

Open lrhn opened 4 months ago

lrhn commented 4 months ago

Change Intent

Add a void fillBytes(TypedData target, int start, int end) method to the Random class, which can efficiently fill a range of bytes with random values.

Justification

On some platforms, we can use underlying platform functionality to implement this more efficiently than just doing repeated .nextInt(256) to fill bytes. Generally, a Random object best knows how much entropy it has, and how many bytes it can effectively fill at a time, which is why the method should be an instance method on the Random object.

The method exists for efficiency only, it's possible to just use .nextInt(256) a number of times.

Impact

It adds a member to the Random interface, which is breaking for any third-party class which implements Random, and is not a mock or fake (has a non-default noSuchMethod).

Such classes are generally only used in tests, which means that no existing production code is likely to be affected. A few projects may find that their tests no longer compile or run, and they'll have to add an extra member to their classes that implement Random.

The examples I've found in internal code search are:

Mitigation

Prepare the known implementing classes for the change by either:

Check if we can find more classes by searching external code as well.

Announce that any class implementing Random will need to prepare (either implement noSuchMethod or be ready to implement fillBytes when it launches, or in advance.)

Change Timeline

Whatever we can make work. This is an addition, not a bug-fix, so it's not urgent.

Associated CLs

https://dart-review.googlesource.com/c/sdk/+/322861

Edit: See also https://github.com/dart-lang/sdk/issues/53339

kevmoo commented 4 months ago

generally LGTM. I wanted this in several places.

QQ: we're sure we can't do this as an extension member?

mnordine commented 4 months ago

Not sure about your impact assessment. For example, we have multiple prngs in our app, including Mersenne Twister, that implement Random, and we can swap out implementations based on our needs.

Regardless, seems like a good breaking change to me.

itsjustkevin commented 4 months ago

@Hixie @vsmenon @leonsenft please take a look at this breaking change request.

Hixie commented 4 months ago

Seems useful. SGTM.

Might be worth adding some sample code to the documentation that describes how a mock could implement this, so that people who are affected can just copy and paste the sample implementation directly. Something like:

@override
void fillBytes(TypedData target, int start, int end) {
  final int count = end - start;
  target.buffer.asUint8List(start, count).replaceRange(0, count, Iterable<int>.generate(count, nextInt));
}
lrhn commented 4 months ago

Making it an extension member would mean doing it from outside the Random object, and using the same algorithm no matter the Random object's class. (Well, unless we recognize all possible Random classes in the SDK and switch on them, but that still doesn't help other potential Random implementations do the same.)

leonsenft commented 4 months ago

LGTM. I took a quick look at existing internal implementations and most already implement noSuchMethod or are generated mocks, so the impact should be minimal to fix any breakages.

itsjustkevin commented 2 months ago

Marking this breaking change as approved. @vsmenon if you object, please speak up.