Skip to content

File.readAsBytes should return Uint8List #31547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tvolkert opened this issue Dec 5, 2017 · 12 comments
Closed

File.readAsBytes should return Uint8List #31547

tvolkert opened this issue Dec 5, 2017 · 12 comments
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug

Comments

@tvolkert
Copy link
Contributor

tvolkert commented Dec 5, 2017

File.readAsBytes (and the sync version as well) should return Uint8List rather than List<int>. This is a backwards-compatible change since Uint8List implements List<int>.

@zanderso

@zanderso zanderso added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io labels Dec 5, 2017
@zanderso
Copy link
Member

zanderso commented Dec 5, 2017

The implementation already returns a Uint8List upcast to a List<int>.

@tvolkert
Copy link
Contributor Author

tvolkert commented Dec 5, 2017

Awesome. Any reason not to declare that in the API?

@zanderso
Copy link
Member

zanderso commented Dec 5, 2017

I don't know. /cc @lrhn @floitschG

@tvolkert
Copy link
Contributor Author

tvolkert commented Dec 5, 2017

Here's the use case: I'm reviewing code that defines a new API for comparing binary images as bytes. The API currently takes List<int> for each image's bytes, and in my review, I stated that they should take Uint8List instead. However, if they do that, then callers will have to take the results of File.reasdAsBytes and then call new Uint8List.fromList(bytes) because they can't assume that they already have a Uint8List. They should be able to know that they're receiving a Uint8List from File.reasAsBytes

@tvolkert
Copy link
Contributor Author

tvolkert commented Dec 5, 2017

Also, File implementations shouldn't ever be allowed to return a List<int> that's not a Uint8List.

@matanlurey matanlurey changed the title File.reasAsBytes should return Uint8List File.readAsBytes should return Uint8List Dec 5, 2017
@lrhn
Copy link
Member

lrhn commented Dec 7, 2017

I would recommend changing the API to state that the byte list is a Uint8List, unless there is a good reason not to.

There are two dangers with saying List<int> and always returning Uint8List:

  1. People doing roundabout things to make it Uint8List, or
  2. People starting to depend on it being Uint8List.

In the latter case, someone might try to mock the class fail to return a Uint8List. In the former case, you get unnecessary overhead.
Or, in other words, "under-promise, over-deliver" is not a good way to design APIs :)

There is one danger with saying Uint8List: You may end up in a situation in the future where you try to implement the interface, and your bytes are not in a Uint8List, so you have to copy them then.

I don't think that last danger is an actual problem. That kind of issue only tends to crop up in testing, and performance isn't the same issue there.

@matanlurey
Copy link
Contributor

matanlurey commented Dec 31, 2018

+1. For example, implementing Flutter's AssetBundle class requires ByteData (https://docs.flutter.io/flutter/services/AssetBundle/load.html). I rely on an upcast from List<int> to ByteData now, but that looks... really weird:

import 'dart:io';

import 'package:flutter/services.dart';
import 'package:path/path.dart' as p;

/// A simple implementation of [AssetBundle] that reads files from an asset dir.
/// 
/// This is meant to be similar to the default [rootBundle] for testing.
class DiskAssetBundle extends CachingAssetBundle {
  final Directory _assetPath;

  DiskAssetBundle([Directory assetPath])
      : _assetPath = assetPath ??
            Directory(
              p.join(Directory.current.path, 'assets'),
            ),
        assert(_assetPath.existsSync(), 'Asset folder expected');

  @override
  Future<ByteData> load(String key) {
     return File(p.join(_path, key))
        .readAsBytes()
        .then((data) => ByteData.view(data as ByteBuffer));
  }
}

@dkashkin
Copy link

+1! Whenever I think about binary data "int" and "Uint8" are not the same animal. The semantics of Dart code should make it clear that we're talking about Uint8 all the way.

@sortie sortie added P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug labels Mar 22, 2019
@jamesderlin
Copy link
Contributor

It seems like everybody agrees that this should be done, and nobody's given any reasons not to do it nor described any potential problems. Is there anything holding this up?

@tvolkert
Copy link
Contributor Author

tvolkert commented May 8, 2019

@tvolkert
Copy link
Contributor Author

tvolkert commented May 8, 2019

Breaking change request: #36900

dart-bot pushed a commit that referenced this issue Jun 20, 2019
These methods all were returning Uint8List, yet they were only
declared to return List<int>. This forced callers to either defensively
wrap the return values in Uint8List, or to assume the contravariant
return value:

* Utf8Codec.encode()
* BytesBuilder.takeBytes()
* BytesBuilder.toBytes()
* File.readAsBytes()
* File.readAsBytesSync()
* RandomAccessFile.read()
* RandomAccessFile.readSync()
* Uint8List.sublist()

Since it's related, this change also updates the following sublist()
methods to declare that they return the a sublist of the same type as
the source list:

* Int8List
* Uint8ClampedList
* Int16List
* Uint16List
* Int32List
* Uint32List
* Int64List
* Uint64List
* Float32List
* Float64List
* Float32x4List
* Int32x4List
* Float64x2List

Bug: #36900
Bug: #31547
Bug: #27818
Bug: #35521
Change-Id: Ic3bc1db0d64de36fb68b1d8d98037eed1464f978
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101742
Commit-Queue: Todd Volkert <[email protected]>
Reviewed-by: Lasse R.H. Nielsen <[email protected]>
@tvolkert
Copy link
Contributor Author

This is done now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

7 participants