mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
feat(std/uuid): Implement uuid v5 (#4916)
This commit is contained in:
parent
516d970fd3
commit
df0000ff0a
9 changed files with 180 additions and 11 deletions
|
@ -47,9 +47,9 @@ export class Sha1 {
|
|||
this._finalized = this._hashed = false;
|
||||
}
|
||||
|
||||
update(data: string | ArrayBuffer | ArrayBufferView): void {
|
||||
update(data: string | ArrayBuffer | ArrayBufferView): Sha1 {
|
||||
if (this._finalized) {
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
let notString = true;
|
||||
let message;
|
||||
|
@ -119,6 +119,7 @@ export class Sha1 {
|
|||
this._hBytes += (this._bytes / 4294967296) >>> 0;
|
||||
this._bytes = this._bytes >>> 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
finalize(): void {
|
|
@ -3,20 +3,20 @@ const { test } = Deno;
|
|||
import { assertEquals } from "../testing/asserts.ts";
|
||||
import { Sha1 } from "./sha1.ts";
|
||||
|
||||
test("[ws/sha] test1", () => {
|
||||
test("[util/sha] test1", () => {
|
||||
const sha1 = new Sha1();
|
||||
sha1.update("abcde");
|
||||
assertEquals(sha1.toString(), "03de6c570bfe24bfc328ccd7ca46b76eadaf4334");
|
||||
});
|
||||
|
||||
test("[ws/sha] testWithArray", () => {
|
||||
test("[util/sha] testWithArray", () => {
|
||||
const data = Uint8Array.of(0x61, 0x62, 0x63, 0x64, 0x65);
|
||||
const sha1 = new Sha1();
|
||||
sha1.update(data);
|
||||
assertEquals(sha1.toString(), "03de6c570bfe24bfc328ccd7ca46b76eadaf4334");
|
||||
});
|
||||
|
||||
test("[ws/sha] testSha1WithBuffer", () => {
|
||||
test("[util/sha] testSha1WithBuffer", () => {
|
||||
const data = Uint8Array.of(0x61, 0x62, 0x63, 0x64, 0x65);
|
||||
const sha1 = new Sha1();
|
||||
sha1.update(data.buffer);
|
|
@ -13,6 +13,35 @@ export function bytesToUuid(bytes: number[] | Uint8Array): string {
|
|||
"-",
|
||||
...bits.slice(8, 10),
|
||||
"-",
|
||||
...bits.slice(10),
|
||||
...bits.slice(10, 16),
|
||||
].join("");
|
||||
}
|
||||
|
||||
export function uuidToBytes(uuid: string): number[] {
|
||||
const bytes: number[] = [];
|
||||
|
||||
uuid.replace(/[a-fA-F0-9]{2}/g, (hex: string): string => {
|
||||
bytes.push(parseInt(hex, 16));
|
||||
return "";
|
||||
});
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function stringToBytes(str: string): number[] {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
const bytes = new Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
bytes[i] = str.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function createBuffer(content: number[]): ArrayBuffer {
|
||||
const arrayBuffer = new ArrayBuffer(content.length);
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
uint8Array[i] = content[i];
|
||||
}
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import * as v1 from "./v1.ts";
|
||||
import * as v4 from "./v4.ts";
|
||||
import * as v5 from "./v5.ts";
|
||||
|
||||
export const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
|
@ -21,7 +22,4 @@ const NOT_IMPLEMENTED = {
|
|||
// TODO Implement
|
||||
export const v3 = NOT_IMPLEMENTED;
|
||||
|
||||
export { v1, v4 };
|
||||
|
||||
// TODO Implement
|
||||
export const v5 = NOT_IMPLEMENTED;
|
||||
export { v1, v4, v5 };
|
||||
|
|
|
@ -11,3 +11,7 @@ import "./tests/v1/generate.ts";
|
|||
// V4 Tests
|
||||
import "./tests/v4/validate.ts";
|
||||
import "./tests/v4/generate.ts";
|
||||
|
||||
// V5 Tests
|
||||
import "./tests/v5/validate.ts";
|
||||
import "./tests/v5/generate.ts";
|
||||
|
|
66
std/uuid/tests/v5/generate.ts
Normal file
66
std/uuid/tests/v5/generate.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { assert, assertEquals } from "../../../testing/asserts.ts";
|
||||
const { test } = Deno;
|
||||
import { generate, validate } from "../../v5.ts";
|
||||
const NAMESPACE = "1b671a64-40d5-491e-99b0-da01ff1f3341";
|
||||
test({
|
||||
name: "[UUID] test_uuid_v5",
|
||||
fn(): void {
|
||||
const u = generate({ value: "", namespace: NAMESPACE });
|
||||
assertEquals(typeof u, "string", "returns a string");
|
||||
assert(u !== "", "return string is not empty");
|
||||
},
|
||||
});
|
||||
|
||||
test({
|
||||
name: "[UUID] test_uuid_v5_format",
|
||||
fn(): void {
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
const u = generate({ value: String(i), namespace: NAMESPACE }) as string;
|
||||
assert(validate(u), `${u} is not a valid uuid v5`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
test({
|
||||
name: "[UUID] test_uuid_v5_option",
|
||||
fn(): void {
|
||||
const v5Options = {
|
||||
value: "Hello, World",
|
||||
namespace: NAMESPACE,
|
||||
};
|
||||
const u = generate(v5Options);
|
||||
assertEquals(u, "4b4f2adc-5b27-57b5-8e3a-c4c4bcf94f05");
|
||||
},
|
||||
});
|
||||
|
||||
test({
|
||||
name: "[UUID] test_uuid_v5_buf_offset",
|
||||
fn(): void {
|
||||
const buf = [
|
||||
75,
|
||||
79,
|
||||
42,
|
||||
220,
|
||||
91,
|
||||
39,
|
||||
87,
|
||||
181,
|
||||
142,
|
||||
58,
|
||||
196,
|
||||
196,
|
||||
188,
|
||||
249,
|
||||
79,
|
||||
5,
|
||||
];
|
||||
const origin = JSON.parse(JSON.stringify(buf));
|
||||
generate({ value: "Hello, World", namespace: NAMESPACE }, buf);
|
||||
assertEquals(origin, buf);
|
||||
|
||||
generate({ value: "Hello, World", namespace: NAMESPACE }, buf, 3);
|
||||
assertEquals(origin.slice(0, 3), buf.slice(0, 3));
|
||||
assertEquals(origin, buf.slice(3));
|
||||
},
|
||||
});
|
19
std/uuid/tests/v5/validate.ts
Normal file
19
std/uuid/tests/v5/validate.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { assert } from "../../../testing/asserts.ts";
|
||||
import { generate, validate } from "../../v5.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "[UUID] is_valid_uuid_v5",
|
||||
fn(): void {
|
||||
const u = generate({
|
||||
value: "Hello, World",
|
||||
namespace: "1b671a64-40d5-491e-99b0-da01ff1f3341",
|
||||
}) as string;
|
||||
const t = "4b4f2adc-5b27-57b5-8e3a-c4c4bcf94f05";
|
||||
const n = "4b4f2adc-5b27-17b5-8e3a-c4c4bcf94f05";
|
||||
|
||||
assert(validate(u), `generated ${u} should be valid`);
|
||||
assert(validate(t), `${t} should be valid`);
|
||||
assert(!validate(n), `${n} should not be valid`);
|
||||
},
|
||||
});
|
52
std/uuid/v5.ts
Normal file
52
std/uuid/v5.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import {
|
||||
bytesToUuid,
|
||||
createBuffer,
|
||||
stringToBytes,
|
||||
uuidToBytes,
|
||||
} from "./_common.ts";
|
||||
import { Sha1 } from "../util/sha1.ts";
|
||||
import { isString } from "../node/util.ts";
|
||||
import { assert } from "../testing/asserts.ts";
|
||||
|
||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
|
||||
export function validate(id: string): boolean {
|
||||
return UUID_RE.test(id);
|
||||
}
|
||||
|
||||
interface V5Options {
|
||||
value: string | number[];
|
||||
namespace: string | number[];
|
||||
}
|
||||
|
||||
export function generate(
|
||||
options: V5Options,
|
||||
buf?: number[],
|
||||
offset?: number
|
||||
): string | number[] {
|
||||
const i = (buf && offset) || 0;
|
||||
|
||||
let { value, namespace } = options;
|
||||
if (isString(value)) value = stringToBytes(value as string);
|
||||
if (isString(namespace)) namespace = uuidToBytes(namespace as string);
|
||||
assert(
|
||||
namespace.length === 16,
|
||||
"namespace must be uuid string or an Array of 16 byte values"
|
||||
);
|
||||
|
||||
const content = (namespace as number[]).concat(value as number[]);
|
||||
const bytes = new Sha1().update(createBuffer(content)).digest();
|
||||
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x50;
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
|
||||
if (buf) {
|
||||
for (let idx = 0; idx < 16; ++idx) {
|
||||
buf[i + idx] = bytes[idx];
|
||||
}
|
||||
}
|
||||
|
||||
return buf || bytesToUuid(bytes);
|
||||
}
|
|
@ -4,7 +4,7 @@ import { decode, encode } from "../encoding/utf8.ts";
|
|||
import { hasOwnProperty } from "../util/has_own_property.ts";
|
||||
import { BufReader, BufWriter } from "../io/bufio.ts";
|
||||
import { readLong, readShort, sliceLongToBytes } from "../io/ioutil.ts";
|
||||
import { Sha1 } from "./sha1.ts";
|
||||
import { Sha1 } from "../util/sha1.ts";
|
||||
import { writeResponse } from "../http/io.ts";
|
||||
import { TextProtoReader } from "../textproto/mod.ts";
|
||||
import { Deferred, deferred } from "../util/async.ts";
|
||||
|
|
Loading…
Reference in a new issue