diff --git a/lib/data.dart b/lib/data.dart index e1679a6..639d959 100644 --- a/lib/data.dart +++ b/lib/data.dart @@ -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 @@ -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? 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 diff --git a/lib/uuid.dart b/lib/uuid.dart index e826180..e65f7be 100644 --- a/lib/uuid.dart +++ b/lib/uuid.dart @@ -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'; @@ -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 @@ -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); } @@ -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 v6buffer( List buffer, { V6Options? config, @@ -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)); } @@ -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); } @@ -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 v7buffer( List buffer, { V7Options? config, @@ -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)); } @@ -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); } @@ -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 v8buffer( List buffer, { V8Options? config, @@ -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 v8gbuffer( + List 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)); + } } diff --git a/lib/v6.dart b/lib/v6.dart index 8d13115..5d83aab 100644 --- a/lib/v6.dart +++ b/lib/v6.dart @@ -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); diff --git a/lib/v7.dart b/lib/v7.dart index 3676bb7..a3fe126 100644 --- a/lib/v7.dart +++ b/lib/v7.dart @@ -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; diff --git a/lib/v8.dart b/lib/v8.dart index 85db132..fe986e9 100644 --- a/lib/v8.dart +++ b/lib/v8.dart @@ -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 diff --git a/lib/v8generic.dart b/lib/v8generic.dart new file mode 100644 index 0000000..6963bb3 --- /dev/null +++ b/lib/v8generic.dart @@ -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 data = + options?.data ?? goptions?.rng?.generate() ?? List.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); + } +} diff --git a/test/uuid_test.dart b/test/uuid_test.dart index dc0953a..c479418 100644 --- a/test/uuid_test.dart +++ b/test/uuid_test.dart @@ -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'; @@ -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 = [0x9E, 0x6B, 0xDE, 0xCE, 0xD8, 0x46]; + var nodeId = [0x9F, 0x6B, 0xDE, 0xCE, 0xD8, 0x46]; var clockSeq = (0xB3 << 8 | 0xC8) & 0x3ffff; final uuid = Uuid().v6( config: V6Options( @@ -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])); + }); + } }); }); }