flutter_web_plugins/packages/dart-uuid/lib/uuid.dart

365 lines
12 KiB
Dart

library Uuid;
import 'uuid_util.dart';
// import 'package:crypto/crypto.dart';
// import 'package:convert/convert.dart' as convert;
/// uuid for Dart
/// Author: Yulian Kuncheff
/// Released under MIT License.
class Uuid {
// RFC4122 provided namespaces for v3 and v5 namespace based UUIDs
static const NAMESPACE_DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
static const NAMESPACE_URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
static const NAMESPACE_OID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';
static const NAMESPACE_X500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';
static const NAMESPACE_NIL = '00000000-0000-0000-0000-000000000000';
var _seedBytes, _nodeId, _clockSeq, _lastMSecs = 0, _lastNSecs = 0;
Function _globalRNG;
List<String> _byteToHex;
Map<String, int> _hexToByte;
Uuid({Map<String, dynamic> options}) {
options = (options != null) ? options : new Map();
_byteToHex = new List<String>(256);
_hexToByte = new Map<String, int>();
// Easy number <-> hex conversion
for (var i = 0; i < 256; i++) {
var hex = new List<int>();
hex.add(i);
_byteToHex[i] = convert.hex.encode(hex);
_hexToByte[_byteToHex[i]] = i;
}
// Sets initial seedBytes, node, and clock seq based on mathRNG.
var v1PositionalArgs = (options['v1rngPositionalArgs'] != null)
? options['v1rngPositionalArgs']
: [];
var v1NamedArgs = (options['v1rngNamedArgs'] != null)
? options['v1rngNamedArgs'] as Map<Symbol, dynamic>
: const <Symbol, dynamic>{};
_seedBytes = (options['v1rng'] != null)
? Function.apply(options['v1rng'], v1PositionalArgs, v1NamedArgs)
: UuidUtil.mathRNG();
// Set the globalRNG function to mathRNG with the option to set an alternative globally
var gPositionalArgs = (options['grngPositionalArgs'] != null)
? options['grngPositionalArgs']
: [];
var gNamedArgs = (options['grngNamedArgs'] != null)
? options['grngNamedArgs'] as Map<Symbol, dynamic>
: const <Symbol, dynamic>{};
_globalRNG = () {
return (options['grng'] != null)
? Function.apply(options['grng'], gPositionalArgs, gNamedArgs)
: UuidUtil.mathRNG();
};
// Per 4.5, create a 48-bit node id (47 random bits + multicast bit = 1)
_nodeId = [
_seedBytes[0] | 0x01,
_seedBytes[1],
_seedBytes[2],
_seedBytes[3],
_seedBytes[4],
_seedBytes[5]
];
// Per 4.2.2, randomize (14 bit) clockseq
_clockSeq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3ffff;
}
///Parses the provided [uuid] into a list of byte values.
/// Can optionally be provided a [buffer] to write into and
/// a positional [offset] for where to start inputting into the buffer.
List<int> parse(String uuid, {List<int> buffer, int offset = 0}) {
var i = offset, ii = 0;
// Create a 16 item buffer if one hasn't been provided.
buffer = (buffer != null) ? buffer : new List<int>(16);
// Convert to lowercase and replace all hex with bytes then
// string.replaceAll() does a lot of work that I don't need, and a manual
// regex gives me more control.
final RegExp regex = new RegExp('[0-9a-f]{2}');
for (Match match in regex.allMatches(uuid.toLowerCase())) {
if (ii < 16) {
var hex = uuid.toLowerCase().substring(match.start, match.end);
buffer[i + ii++] = _hexToByte[hex];
}
}
// Zero out any left over bytes if the string was too short.
while (ii < 16) {
buffer[i + ii++] = 0;
}
return buffer;
}
/// Unparses a [buffer] of bytes and outputs a proper UUID string.
/// An optional [offset] is allowed if you want to start at a different point
/// in the buffer.
String unparse(List<int> buffer, {int offset = 0}) {
var i = offset;
return '${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}-'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}-'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}-'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}-'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}'
'${_byteToHex[buffer[i++]]}${_byteToHex[buffer[i++]]}';
}
/// v1() Generates a time-based version 1 UUID
///
/// By default it will generate a string based off current time, and will
/// return a string.
///
/// If an optional [buffer] list is provided, it will put the byte data into
/// that buffer and return a buffer.
///
/// Optionally an [offset] can be provided with a start position in the buffer.
///
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// http://tools.ietf.org/html/rfc4122.html#section-4.2.2
String v1({Map<String, dynamic> options}) {
var i = 0;
var buf = new List<int>(16);
options = (options != null) ? options : new Map();
var clockSeq =
(options['clockSeq'] != null) ? options['clockSeq'] : _clockSeq;
// UUID timestamps are 100 nano-second units since the Gregorian epoch,
// (1582-10-15 00:00). Time is handled internally as 'msecs' (integer
// milliseconds) and 'nsecs' (100-nanoseconds offset from msecs) since unix
// epoch, 1970-01-01 00:00.
var mSecs = (options['mSecs'] != null)
? options['mSecs']
: (new DateTime.now()).millisecondsSinceEpoch;
// Per 4.2.1.2, use count of uuid's generated during the current clock
// cycle to simulate higher resolution clock
var nSecs = (options['nSecs'] != null) ? options['nSecs'] : _lastNSecs + 1;
// Time since last uuid creation (in msecs)
var dt = (mSecs - _lastMSecs) + (nSecs - _lastNSecs) / 10000;
// Per 4.2.1.2, Bump clockseq on clock regression
if (dt < 0 && options['clockSeq'] == null) {
clockSeq = clockSeq + 1 & 0x3fff;
}
// Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
// time interval
if ((dt < 0 || mSecs > _lastMSecs) && options['nSecs'] == null) {
nSecs = 0;
}
// Per 4.2.1.2 Throw error if too many uuids are requested
if (nSecs >= 10000) {
throw new Exception('uuid.v1(): Can\'t create more than 10M uuids/sec');
}
_lastMSecs = mSecs;
_lastNSecs = nSecs;
_clockSeq = clockSeq;
// Per 4.1.4 - Convert from unix epoch to Gregorian epoch
mSecs += 12219292800000;
// time Low
var tl = ((mSecs & 0xfffffff) * 10000 + nSecs) % 0x100000000;
buf[i++] = tl >> 24 & 0xff;
buf[i++] = tl >> 16 & 0xff;
buf[i++] = tl >> 8 & 0xff;
buf[i++] = tl & 0xff;
// time mid
var tmh = (mSecs / 0x100000000 * 10000).floor() & 0xfffffff;
buf[i++] = tmh >> 8 & 0xff;
buf[i++] = tmh & 0xff;
// time high and version
buf[i++] = tmh >> 24 & 0xf | 0x10; // include version
buf[i++] = tmh >> 16 & 0xff;
// clockSeq high and reserved (Per 4.2.2 - include variant)
buf[i++] = clockSeq >> 8 | 0x80;
// clockSeq low
buf[i++] = clockSeq & 0xff;
// node
var node = (options['node'] != null) ? options['node'] : _nodeId;
for (var n = 0; n < 6; n++) {
buf[i + n] = node[n];
}
return unparse(buf);
}
/// v1buffer() Generates a time-based version 1 UUID
///
/// By default it will generate a string based off current time, and will
/// place the result into the provided [buffer]. The [buffer] will also be returned..
///
/// Optionally an [offset] can be provided with a start position in the buffer.
///
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// http://tools.ietf.org/html/rfc4122.html#section-4.2.2
List<int> v1buffer(
List<int> buffer, {
Map<String, dynamic> options,
int offset = 0,
}) {
var _buf = parse(v1(options: options));
if (buffer != null) {
buffer.setRange(offset, offset + 16, _buf);
}
return buffer;
}
/// v4() Generates a RNG version 4 UUID
///
/// By default it will generate a string based mathRNG, and will return
/// a string. If you wish to use crypto-strong RNG, pass in UuidUtil.cryptoRNG
///
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// http://tools.ietf.org/html/rfc4122.html#section-4.4
String v4({Map<String, dynamic> options}) {
options = (options != null) ? options : new Map<String, dynamic>();
// Use the built-in RNG or a custom provided RNG
var positionalArgs =
(options['positionalArgs'] != null) ? options['positionalArgs'] : [];
var namedArgs = (options['namedArgs'] != null)
? options['namedArgs'] as Map<Symbol, dynamic>
: const <Symbol, dynamic>{};
var rng = (options['rng'] != null)
? Function.apply(options['rng'], positionalArgs, namedArgs)
: _globalRNG();
// Use provided values over RNG
var rnds = (options['random'] != null) ? options['random'] : rng;
// per 4.4, set bits for version and clockSeq high and reserved
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
return unparse(rnds);
}
/// v4buffer() Generates a RNG version 4 UUID
///
/// By default it will generate a string based off mathRNG, and will
/// place the result into the provided [buffer]. The [buffer] will also be returned.
/// If you wish to have crypto-strong RNG, pass in UuidUtil.cryptoRNG.
///
/// Optionally an [offset] can be provided with a start position in the buffer.
///
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// http://tools.ietf.org/html/rfc4122.html#section-4.4
List<int> v4buffer(
List<int> buffer, {
Map<String, dynamic> options,
int offset = 0,
}) {
var _buf = parse(v4(options: options));
if (buffer != null) {
buffer.setRange(offset, offset + 16, _buf);
}
return buffer;
}
/// v5() Generates a namspace & name-based version 5 UUID
///
/// By default it will generate a string based on a provided uuid namespace and
/// name, and will return a string.
///
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// http://tools.ietf.org/html/rfc4122.html#section-4.4
String v5(String namespace, String name, {Map<String, dynamic> options}) {
options = (options != null) ? options : new Map();
// Check if user wants a random namespace generated by v4() or a NIL namespace.
var useRandom = (options['randomNamespace'] != null)
? options['randomNamespace']
: true;
// If useRandom is true, generate UUIDv4, else use NIL
var blankNS = useRandom ? v4() : NAMESPACE_NIL;
// Use provided namespace, or use whatever is decided by options.
namespace = (namespace != null) ? namespace : blankNS;
// Use provided name,
name = (name != null) ? name : '';
// Convert namespace UUID to Byte List
var bytes = parse(namespace);
// Convert name to a list of bytes
var nameBytes = new List<int>();
for (var singleChar in name.codeUnits) {
nameBytes.add(singleChar);
}
// Generate SHA1 using namespace concatenated with name
List hashBytes =
sha1.convert(new List.from(bytes)..addAll(nameBytes)).bytes;
// per 4.4, set bits for version and clockSeq high and reserved
hashBytes[6] = (hashBytes[6] & 0x0f) | 0x50;
hashBytes[8] = (hashBytes[8] & 0x3f) | 0x80;
return unparse(hashBytes);
}
/// v5buffer() Generates a RNG version 4 UUID
///
/// By default it will generate a string based off current time, and will
/// place the result into the provided [buffer]. The [buffer] will also be returned..
///
/// Optionally an [offset] can be provided with a start position in the buffer.
///
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// http://tools.ietf.org/html/rfc4122.html#section-4.4
List<int> v5buffer(
String namespace,
String name,
List<int> buffer, {
Map<String, dynamic> options,
int offset = 0,
}) {
var _buf = parse(v5(namespace, name, options: options));
if (buffer != null) {
buffer.setRange(offset, offset + 16, _buf);
}
return buffer;
}
}