Skip to content

Commit

Permalink
Add generic UUID v8 so you can customize it. Cleanup tests based on n…
Browse files Browse the repository at this point in the history
…ew specs, and update URLs of the RFC
  • Loading branch information
daegalus committed Feb 7, 2024
1 parent 5e3538c commit b6cb8cf
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 21 deletions.
10 changes: 9 additions & 1 deletion lib/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class V7Options {
const V7Options(this.time, this.randomBytes);
}

/// [V8Options] stores the options passed into the v7 function.
/// [V8Options] stores the options passed into the v8 function.
/// [time] is the time in milliseconds since the unix epoch
/// [randomBytes] is the random bytes to use to generate the UUID. Primarily used
/// for testing, or recreating a UUID
Expand All @@ -95,6 +95,14 @@ class V8Options {
const V8Options(this.time, this.randomBytes);
}

/// [V8GenericOptions] stores the options passed into the v8g function.
/// [data] is the 122 bytes of custom data.
class V8GenericOptions {
final List<int>? data;

const V8GenericOptions(this.data);
}

/// [V1State] stores the state of the v1 function.
/// [nodeId] is the node id currently being used
/// [clockSeq] is the clock sequence currently being used
Expand Down
75 changes: 60 additions & 15 deletions lib/uuid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'v5.dart';
import 'v6.dart';
import 'v7.dart';
import 'v8.dart';
import 'v8generic.dart';

export 'uuid_value.dart';
export 'enums.dart';
Expand All @@ -37,12 +38,6 @@ class Uuid {
static const NAMESPACE_NIL = '00000000-0000-0000-0000-000000000000';

final GlobalOptions? goptions;
// final UuidV1 _uuidv1;
// final UuidV4 _uuidv4;
// final UuidV5 _uuidv5;
// final UuidV6 _uuidv6;
// final UuidV7 _uuidv7;
// final UuidV8 _uuidv8;

/// Creates a new instance of the Uuid class.
/// Optionally you can pass in a [GlobalOptions] object to set global options
Expand Down Expand Up @@ -489,7 +484,7 @@ class Uuid {
/// The first argument is a [V6Options] object that takes the same options as
/// the options map.
///
/// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format#section-4.3
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-6
String v6({V6Options? config}) {
return UuidV6(goptions: goptions).generate(options: config);
}
Expand All @@ -508,7 +503,7 @@ class Uuid {
/// The second optional argument is a [V6Options] object that takes the same options as
/// the options map. This is the preferred way to pass options.
///
/// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format#section-4.3
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-6
List<int> v6buffer(
List<int> buffer, {
V6Options? config,
Expand All @@ -526,7 +521,7 @@ class Uuid {
/// The first argument is a [V6Options] object that takes the same options as
/// the options map. This is the preferred way to pass options.
///
/// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format#section-4.3
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-6
UuidValue v6obj({V6Options? config}) {
return UuidValue.fromString(v6(config: config));
}
Expand All @@ -539,7 +534,7 @@ class Uuid {
/// The first argument is a [V7Options] object that takes the same options as
/// the options map.
///
/// https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html#name-uuidv7-layout-and-bit-order
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7
String v7({V7Options? config}) {
return UuidV7(goptions: goptions).generate(options: config);
}
Expand All @@ -555,7 +550,7 @@ class Uuid {
/// The first optional argument is a [V7Options] object that takes the same options as
/// the options map.
///
/// https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html#name-uuidv7-layout-and-bit-order
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7
List<int> v7buffer(
List<int> buffer, {
V7Options? config,
Expand All @@ -573,7 +568,7 @@ class Uuid {
/// The first argument is a [V7Options] object that takes the same options as
/// the options map.
///
/// https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html#name-uuidv7-layout-and-bit-order
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7
UuidValue v7obj({V7Options? config}) {
return UuidValue.fromString(v7(config: config));
}
Expand All @@ -586,7 +581,7 @@ class Uuid {
/// The first argument is a [V8Options] object that takes the same options as
/// the options map.
///
/// https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html#name-uuidv7-layout-and-bit-order
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
String v8({V8Options? config}) {
return UuidV8(goptions: goptions).generate(options: config);
}
Expand All @@ -602,7 +597,7 @@ class Uuid {
/// The first optional argument is a [V8Options] object that takes the same options as
/// the options map.
///
/// https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html#name-uuidv7-layout-and-bit-order
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
List<int> v8buffer(
List<int> buffer, {
V8Options? config,
Expand All @@ -620,8 +615,58 @@ class Uuid {
/// The first argument is a [V8Options] object that takes the same options as
/// the options map.
///
/// https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html#name-uuidv7-layout-and-bit-order
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
UuidValue v8obj({V8Options? config}) {
return UuidValue.fromString(v8(config: config));
}

/// Generates a draft time-based version 8 UUID
///
/// Takes in 128 bits (16 bytes) of custom data, and produces a valid V8 uuid.
/// Bits 48-51 and bits 64-65 will be modified to create a valid uuid.
///
/// The first argument is a [V8GenericOptions] object that takes the same options as
/// the options map.
///
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
String v8g({V8GenericOptions? config}) {
return UuidV8Generic(goptions: goptions).generate(options: config);
}

/// Generates a draft time-based version 8 UUID into a provided buffer
///
/// Takes in 128 bits (16 bytes) of custom data, and produces a valid V8 uuid.
/// Bits 48-51 and bits 64-65 will be modified to create a valid uuid.
/// It 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 optional argument is a [V8GenericOptions] object that takes the same options as
/// the options map.
///
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
List<int> v8gbuffer(
List<int> buffer, {
V8GenericOptions? config,
int offset = 0,
}) {
return UuidParsing.parse(v8g(config: config),
buffer: buffer, offset: offset);
}

/// Generates a draft time-based version 8 UUID as a [UuidValue] object
///
/// Takes in 128 bits (16 bytes) of custom data, and produces a valid V8 uuid.
/// Bits 48-51 and bits 64-65 will be modified to create a valid uuid.
/// It will return it as a [UuidValue] object.
///
/// The first argument is a [V8GenericOptions] object that takes the same options as
/// the options map.
///
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
UuidValue v8gobj({V8GenericOptions? config}) {
return UuidValue.fromString(v8g(config: config));
}
}
2 changes: 1 addition & 1 deletion lib/v6.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class UuidV6 {
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format#section-4.3
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7
String generate({V6Options? options}) {
_init();
var buf = Uint8List(16);
Expand Down
2 changes: 1 addition & 1 deletion lib/v7.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class UuidV7 {
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format#section-4.3
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7
String generate({V7Options? options}) {
var buf = Uint8List(16);
int time = options?.time ?? DateTime.timestamp().millisecondsSinceEpoch;
Expand Down
2 changes: 1 addition & 1 deletion lib/v8.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class UuidV8 {
/// The first argument is an options map that takes various configuration
/// options detailed in the readme.
///
/// https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format#section-4.3
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
///
/// 0 10 20 30
/// 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F
Expand Down
47 changes: 47 additions & 0 deletions lib/v8generic.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'dart:typed_data';
import 'package:uuid/data.dart';

import 'parsing.dart';

class UuidV8Generic {
final GlobalOptions? goptions;

const UuidV8Generic({this.goptions});

/// V8Generic() Generates a time-based version 8 UUID
///
/// Takes in 128 bits (16 bytes) of custom data, and produces a valid V8 uuid.
/// Bits 48-51 and bits 64-65 will be modified to create a valid uuid.
///
/// https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-8
///
/// 0 10 20 30
/// 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | custom_a |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | custom_a | ver | custom_b |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |var| custom_c |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | custom_c |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
///
/// 48 bits - custom_a
/// 4 bits - version
/// 12 bits - custom_b
/// 2 bits - variant
/// 62 bits - custom_c
String generate({V8GenericOptions? options}) {
var buf = Uint8List(16);

List<int> data =
options?.data ?? goptions?.rng?.generate() ?? List<int>.filled(16, 0);

buf.setRange(0, 16, data);
buf.setRange(6, 7, [buf.getRange(6, 7).last & 0x0f | 0x80]);
buf.setRange(8, 9, [buf.getRange(8, 9).last & 0x3f | 0x80]);

return UuidParsing.unparse(buf);
}
}
38 changes: 36 additions & 2 deletions test/uuid_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// TODO: Remove this ignore when we remove the deprecated options.
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:test/test.dart';
import 'package:uuid/data.dart';
import 'package:uuid/uuid.dart';
Expand Down Expand Up @@ -551,11 +552,11 @@ void main() {
for (final testCase in {
'Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00': [
1645557742000,
'1EC9414C-232A-6B00-B3C8-9E6BDECED846'
'1EC9414C-232A-6B00-B3C8-9F6BDECED846'
],
}.entries) {
test(testCase.key, () {
var nodeId = <int>[0x9E, 0x6B, 0xDE, 0xCE, 0xD8, 0x46];
var nodeId = <int>[0x9F, 0x6B, 0xDE, 0xCE, 0xD8, 0x46];
var clockSeq = (0xB3 << 8 | 0xC8) & 0x3ffff;
final uuid = Uuid().v6(
config: V6Options(
Expand Down Expand Up @@ -607,6 +608,39 @@ void main() {
expect(uuid.toUpperCase(), equals(testCase.value[1]));
});
}

for (final testCase in {
'UUIDv8 Generic, SHA512/256 random numbers': [
[
0xdb,
0xc9,
0xed,
0x5e,
0xf2,
0xf5,
0x43,
0x13,
0x8a,
0xf4,
0x5e,
0x8b,
0x58,
0x5f,
0x91,
0x31,
0xa6,
0x16
],
'20220222-1922-8422-9222-73AF3E41FFC4'
],
}.entries) {
test(testCase.key, () {
final rand = MathRNG(seed: 1).generate();
final uuid =
Uuid().v8(config: V8Options(testCase.value[0] as DateTime, rand));
expect(uuid.toUpperCase(), equals(testCase.value[1]));
});
}
});
});
}

0 comments on commit b6cb8cf

Please sign in to comment.