From d9bbd2fb5349596694bc428162ab6c9786573593 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 28 Sep 2016 11:18:05 -0700 Subject: [PATCH] make app ids more unique (#6113) * make app ids more unique * in-line the uuid class --- .../flutter_tools/lib/src/base/utils.dart | 35 +++++++++++ .../lib/src/commands/daemon.dart | 7 ++- packages/flutter_tools/test/utils_test.dart | 61 +++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart index 7207d88ba95..2d86985c4fd 100644 --- a/packages/flutter_tools/lib/src/base/utils.dart +++ b/packages/flutter_tools/lib/src/base/utils.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:math' show Random; import 'package:crypto/crypto.dart'; import 'package:path/path.dart' as path; @@ -161,3 +162,37 @@ class SettingsFile { }).join('\n')); } } + +/// A UUID generator. This will generate unique IDs in the format: +/// +/// f47ac10b-58cc-4372-a567-0e02b2c3d479 +/// +/// The generated uuids are 128 bit numbers encoded in a specific string format. +/// +/// For more information, see +/// http://en.wikipedia.org/wiki/Universally_unique_identifier. +class Uuid { + Random _random = new Random(); + + /// Generate a version 4 (random) uuid. This is a uuid scheme that only uses + /// random numbers as the source of the generated uuid. + String generateV4() { + // Generate xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12. + int special = 8 + _random.nextInt(4); + + return + '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-' + '${_bitsDigits(16, 4)}-' + '4${_bitsDigits(12, 3)}-' + '${_printDigits(special, 1)}${_bitsDigits(12, 3)}-' + '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}'; + } + + String _bitsDigits(int bitCount, int digitCount) => + _printDigits(_generateBits(bitCount), digitCount); + + int _generateBits(int bitCount) => _random.nextInt(1 << bitCount); + + String _printDigits(int value, int count) => + value.toRadixString(16).padLeft(count, '0'); +} diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index f87d7f22ef4..7ee74c16bd1 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -9,6 +9,7 @@ import 'dart:io'; import '../android/android_device.dart'; import '../base/context.dart'; import '../base/logger.dart'; +import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; import '../device.dart'; @@ -280,9 +281,9 @@ class AppDomain extends Domain { registerHandler('discover', discover); } - static int _nextAppId = 0; + static Uuid _uuidGenerator = new Uuid(); - static String _getNextAppId() => 'app-${_nextAppId++}'; + static String _getNewAppId() => _uuidGenerator.generateV4(); List _apps = []; @@ -341,7 +342,7 @@ class AppDomain extends Domain { bool supportsRestart = enableHotReload ? device.supportsHotMode : device.supportsRestart; - AppInstance app = new AppInstance(_getNextAppId(), runner); + AppInstance app = new AppInstance(_getNewAppId(), runner); _apps.add(app); _sendAppEvent(app, 'start', { 'deviceId': deviceId, diff --git a/packages/flutter_tools/test/utils_test.dart b/packages/flutter_tools/test/utils_test.dart index bdf50edd30c..c9198bbc862 100644 --- a/packages/flutter_tools/test/utils_test.dart +++ b/packages/flutter_tools/test/utils_test.dart @@ -18,4 +18,65 @@ baz=qux expect(file.values, hasLength(2)); }); }); + + group('uuid', () { + // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + test('simple', () { + Uuid uuid = new Uuid(); + String result = uuid.generateV4(); + expect(result.length, 36); + expect(result[8], '-'); + expect(result[13], '-'); + expect(result[18], '-'); + expect(result[23], '-'); + }); + + test('can parse', () { + Uuid uuid = new Uuid(); + String result = uuid.generateV4(); + expect(int.parse(result.substring(0, 8), radix: 16), isNotNull); + expect(int.parse(result.substring(9, 13), radix: 16), isNotNull); + expect(int.parse(result.substring(14, 18), radix: 16), isNotNull); + expect(int.parse(result.substring(19, 23), radix: 16), isNotNull); + expect(int.parse(result.substring(24, 36), radix: 16), isNotNull); + }); + + test('special bits', () { + Uuid uuid = new Uuid(); + String result = uuid.generateV4(); + expect(result[14], '4'); + expect(result[19].toLowerCase(), isIn('89ab')); + + result = uuid.generateV4(); + expect(result[19].toLowerCase(), isIn('89ab')); + + result = uuid.generateV4(); + expect(result[19].toLowerCase(), isIn('89ab')); + }); + + test('is pretty random', () { + Set set = new Set(); + + Uuid uuid = new Uuid(); + for (int i = 0; i < 64; i++) { + String val = uuid.generateV4(); + expect(set, isNot(contains(val))); + set.add(val); + } + + uuid = new Uuid(); + for (int i = 0; i < 64; i++) { + String val = uuid.generateV4(); + expect(set, isNot(contains(val))); + set.add(val); + } + + uuid = new Uuid(); + for (int i = 0; i < 64; i++) { + String val = uuid.generateV4(); + expect(set, isNot(contains(val))); + set.add(val); + } + }); + }); }