uuidm

forked from bobzhang/uuim / Universally unique identifiers (UUIDs) for MoonBit - supports UUID versions 3, 4, 5, 7, and 8 according to RFC 9562

uuid
guid
rfc9562
moon add f4ah6o/uuidm@0.2.0
Download zip
Author
Version
0.2.0
License
ISC
Last updated
6 months ago
Downloads
1K
README

#Uuidm — Universally unique identifiers (UUIDs) for MoonBit

Uuidm is a MoonBit library implementing 128 bits universally unique identifiers (UUIDs) versions 3, 4, 5, 7, and 8 according to RFC 9562.

This is a port of the original OCaml uuidm library by Daniel Bünzli.

#Features

  • UUID v3: Name-based UUIDs using MD5 hashing
  • UUID v4: Random or pseudo-random UUIDs
  • UUID v5: Name-based UUIDs using SHA-1 hashing
  • UUID v7: Time-ordered UUIDs with random component
  • UUID v8: Custom format UUIDs
  • String conversion: To/from standard UUID string representations
  • Standard namespaces: DNS, URL, OID, and X.500 namespaces
  • No external dependencies: Pure MoonBit implementation

#Installation

This library is designed to work with the MoonBit compiler. Add it to your moon.mod.json:

{ "name": "your-project", "version": "0.1.0", "deps": { "uuidm": "0.1.0" } }

#Quick Start

// Generate a random UUID (version 4)
let uuid = v4()
println(uuid.to_string()) // e.g., "550e8400-e29b-41d4-a716-446655440000"

// Generate time-ordered UUID (version 7)
let time_uuid = v7()
println(time_uuid.to_string())

// Generate name-based UUID (version 5)
let name_uuid = v5(ns_dns, "example.com")
println(name_uuid.to_string())

// Parse UUID from string
match from_string("550e8400-e29b-41d4-a716-446655440000") {
Some(uuid) => println("Parsed: " + uuid.to_string())
None => println("Invalid UUID")
}

#API Reference

#Core Types

// UUID type
struct Uuid {
bytes : FixedArray[Byte]
}

// UUID variants
enum Variant {
Ncs | Rfc9562 | Microsoft | Reserved
}

// UUID versions
enum Version {
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8
}

#UUID Generation

// Version 4 (Random)
pub fn v4() -> Uuid
pub fn v4_bulk(count : Int) -> Array[Uuid]

// Version 3 (Name-based with MD5)
pub fn v3(namespace : Uuid, name : String) -> Uuid
pub fn v3_dns(name : String) -> Uuid
pub fn v3_url(name : String) -> Uuid

// Version 5 (Name-based with SHA-1)
pub fn v5(namespace : Uuid, name : String) -> Uuid
pub fn v5_dns(name : String) -> Uuid
pub fn v5_url(name : String) -> Uuid

// Version 7 (Time-ordered)
pub fn v7() -> Uuid
pub fn v7_with_timestamp(timestamp_ms : Int64) -> Uuid
pub fn v7_sequence(count : Int) -> Array[Uuid]

// Version 8 (Custom)
pub fn v8(custom_data : FixedArray[Byte]) -> Uuid
pub fn v8_counter(counter : Int64, node_id : Int, application_id : Int) -> Uuid
pub fn v8_mixed(timestamp_ms : Int64, custom_suffix : Int) -> Uuid

#String Conversion

// Convert to string
pub fn to_string(uuid : Uuid) -> String // With hyphens
pub fn to_string_simple(uuid : Uuid) -> String // Without hyphens
pub fn to_urn(uuid : Uuid) -> String // URN format

// Parse from string
pub fn from_string(s : String) -> Uuid? // Safe parsing
pub fn from_string_exn(s : String) -> Uuid // Panics on error
pub fn from_urn(s : String) -> Uuid? // Parse URN format

#UUID Properties

// Get UUID properties
pub fn variant(uuid : Uuid) -> Variant
pub fn version(uuid : Uuid) -> Option[Version]
pub fn bytes(uuid : Uuid) -> FixedArray[Byte]

// Special UUIDs
pub fn nil() -> Uuid // All zeros
pub fn max() -> Uuid // All ones
pub fn is_nil(uuid : Uuid) -> Bool
pub fn is_max(uuid : Uuid) -> Bool

#Standard Namespaces

pub let ns_dns : Uuid // DNS namespace
pub let ns_url : Uuid // URL namespace
pub let ns_oid : Uuid // OID namespace
pub let ns_x500 : Uuid // X.500 namespace

#Examples

#Name-based UUIDs

// Using predefined namespaces
let dns_uuid = v5_dns("example.com")
let url_uuid = v5_url("https://example.com/path")

// Using custom namespace
let custom_ns = v4() // or any other UUID
let custom_uuid = v5(custom_ns, "my-resource")

// Version 3 vs Version 5 (different hash algorithms)
let v3_uuid = v3_dns("example.com")
let v5_uuid = v5_dns("example.com")
// These will be different UUIDs

#Time-ordered UUIDs

// Generate sequence of time-ordered UUIDs
let sequence = v7_sequence(5)
// These will be in chronological order

// Extract timestamp from v7 UUID
match extract_timestamp(v7_uuid) {
Some(timestamp) => println("Created at: " + timestamp.to_string())
None => println("Not a v7 UUID")
}

#Custom UUIDs (Version 8)

// Counter-based UUID
let counter_uuid = v8_counter(12345L, 999, 888)

// Mixed timestamp + custom data
let mixed_uuid = v8_mixed(current_timestamp_ms(), 0x123456)

// Fully custom
let custom_data : FixedArray[Byte] = FixedArray::make(16, 0x42b)
let custom_uuid = v8(custom_data)

#Testing

The library includes comprehensive tests:

moon test

#Implementation Notes

  • Cryptographic hashing: The MD5 and SHA-1 implementations are simplified for demonstration. In production, you may want to use proper cryptographic libraries.
  • Random number generation: Uses a simple linear congruential generator. For cryptographic applications, consider using a cryptographically secure random number generator.
  • Performance: This implementation prioritizes clarity and correctness over performance optimization.

#Compliance

This library implements UUIDs according to RFC 9562 (formerly RFC 4122). It supports:

  • ✅ UUID version 3 (name-based, MD5)
  • ✅ UUID version 4 (random)
  • ✅ UUID version 5 (name-based, SHA-1)
  • ✅ UUID version 7 (time-ordered)
  • ✅ UUID version 8 (custom)
  • ✅ Standard string representations
  • ✅ Standard namespaces

#License

This library is distributed under the ISC license, same as the original OCaml implementation.

#Contributing

Contributions are welcome! Please ensure any changes include appropriate tests and documentation.

#Acknowledgments

This is a port of the excellent uuidm library by Daniel Bünzli. The original design and implementation patterns have been adapted for MoonBit while maintaining compatibility with RFC 9562.

#
Md5

type Md5

Basic MD5 implementation for UUID v3 This is a simplified implementation for demonstration

#
Md5::new

fn Md5::new() -> Md5

Create a new MD5 hasher

#
SimpleRng

type SimpleRng

Simple linear congruential generator for generating random bytes This is a basic implementation - in production you might want to use a better quality random number generator or system entropy

#
SimpleRng::fill_bytes

fn SimpleRng::fill_bytes(self : SimpleRng, bytes : FixedArray[Byte]) -> Unit

Fill an array with random bytes

#
SimpleRng::new

fn SimpleRng::new(seed : Int64) -> SimpleRng

Create a new random number generator with a seed

#
SimpleRng::new_with_time

fn SimpleRng::new_with_time() -> SimpleRng

Create a new random number generator with current time as seed

#
SimpleRng::next_byte

fn SimpleRng::next_byte(self : SimpleRng) -> Byte

Generate a random byte

#
SimpleRng::next_int64

fn SimpleRng::next_int64(self : SimpleRng) -> Int64

Generate the next random 64-bit integer

#
Uuid

type Uuid

A UUID is represented as 16 bytes (128 bits)
impl Compare for Uuid
impl Eq for Uuid
impl Show for Uuid

#
Uuid::bytes

fn Uuid::bytes(self : Uuid) -> FixedArray[Byte]

Get the raw bytes of the UUID

#
Uuid::from_bytes

fn Uuid::from_bytes(b0 : Byte, b1 : Byte, b2 : Byte, b3 : Byte, b4 : Byte, b5 : Byte, b6 : Byte, b7 : Byte, b8 : Byte, b9 : Byte, b10 : Byte, b11 : Byte, b12 : Byte, b13 : Byte, b14 : Byte, b15 : Byte) -> Uuid

Create a UUID from individual byte values

#
Uuid::is_max

fn Uuid::is_max(self : Uuid) -> Bool

Check if the UUID is max (all ones)

#
Uuid::is_nil

fn Uuid::is_nil(self : Uuid) -> Bool

Check if the UUID is nil (all zeros)

#
Uuid::new

fn Uuid::new(bytes : FixedArray[Byte]) -> Uuid

Create a new UUID from 16 bytes

#
Uuid::to_string

fn Uuid::to_string(self : Uuid) -> String

Convert UUID to string representation with hyphens Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

#
Uuid::to_string_simple

fn Uuid::to_string_simple(self : Uuid) -> String

Convert UUID to string representation without hyphens

#
Uuid::to_urn

fn Uuid::to_urn(self : Uuid) -> String

Convert UUID to URN string representation Format: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

#
Uuid::variant

fn Uuid::variant(self : Uuid) -> Variant

Get the variant of the UUID

#
Uuid::version

fn Uuid::version(self : Uuid) -> Version?

Get the version of the UUID (only meaningful for RFC 9562 variant)

#
Variant

type Variant

UUID variant as defined in RFC 9562
impl Eq for Variant
impl Show for Variant

#
Version

type Version

UUID version as defined in RFC 9562
impl Eq for Version
impl Show for Version

#
combine_namespace_and_name

fn combine_namespace_and_name(ns : Uuid, name : String) -> FixedArray[Byte]

Combine ns UUID and name for hashing

#
demo

fn demo() -> String

Example usage and demo function

#
demo_uuid_library

fn demo_uuid_library() -> Unit

#
extract_custom_data

fn extract_custom_data(uuid : Uuid) -> FixedArray[Byte]?

Extract custom data from a version 8 UUID (excluding version/variant bits)

#
extract_timestamp

fn extract_timestamp(uuid : Uuid) -> Int64?

Extract timestamp from a version 7 UUID

#
from_string

fn from_string(s : String) -> Uuid?

Parse UUID from string representation Accepts both hyphenated (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) and non-hyphenated formats

#
from_string_exn

fn from_string_exn(s : String) -> Uuid

Parse UUID from string, panics on invalid input

#
from_urn

fn from_urn(s : String) -> Uuid?

Parse UUID from URN string representation

#
from_urn_exn

fn from_urn_exn(s : String) -> Uuid

Parse UUID from URN string, panics on invalid input

#
max

fn max() -> Uuid

Generate the max UUID (all ones)

#
md5_hash

fn md5_hash(data : FixedArray[Byte]) -> FixedArray[Byte]

Simplified MD5 hash function Note: This is a basic implementation for demonstration purposes In production, you would use a proper cryptographic library

#
nil

fn nil() -> Uuid

Generate the nil UUID (all zeros)

#
ns_dns

let ns_dns : Uuid

DNS namespace UUID (6ba7b810-9dad-11d1-80b4-00c04fd430c8)

#
ns_oid

let ns_oid : Uuid

OID namespace UUID (6ba7b812-9dad-11d1-80b4-00c04fd430c8)

#
ns_url

let ns_url : Uuid

URL namespace UUID (6ba7b811-9dad-11d1-80b4-00c04fd430c8)

#
ns_x500

let ns_x500 : Uuid

X500 namespace UUID (6ba7b814-9dad-11d1-80b4-00c04fd430c8)

#
random_bytes

fn random_bytes(count : Int) -> FixedArray[Byte]

Generate random bytes using a new RNG instance

#
sha1_hash

fn sha1_hash(data : FixedArray[Byte]) -> FixedArray[Byte]

Basic SHA-1 implementation for UUID v5 This is a simplified implementation for demonstration

#
string_to_bytes

fn string_to_bytes(s : String) -> FixedArray[Byte]

Convert string to UTF-8 bytes for hashing (RFC 4122/9562 compliant)

UTF-8 Encoding Implementation

This implementation now properly converts strings to UTF-8 bytes as required by RFC 4122/9562 for UUID v3/v5 generation. This matches the behavior of reference implementations in JavaScript/Node.js, Python, etc.

UTF-8 Encoding Rules:

  • ASCII (U+0000-U+007F): 1 byte
  • U+0080-U+07FF: 2 bytes
  • U+0800-U+FFFF: 3 bytes (includes most Chinese characters)
  • U+10000-U+10FFFF: 4 bytes

Examples:

  • "hello" → [0x68, 0x65, 0x6C, 0x6C, 0x6F] (5 bytes)
  • "中" (U+4E2D) → [0xE4, 0xB8, 0xAD] (3 bytes)
  • "国" (U+56FD) → [0xE5, 0x9B, 0xBD] (3 bytes)
  • "中国" → [0xE4, 0xB8, 0xAD, 0xE5, 0x9B, 0xBD] (6 bytes)

This now matches JavaScript's new TextEncoder().encode(string) behavior.

Spec Compliance Achievement! 🎉

This implementation is now RFC 4122/9562 compliant for UTF-8 encoding! UUIDs generated with Chinese characters should now match reference implementations in JavaScript/Node.js, Python, and other spec-compliant libraries (assuming the same MD5/SHA-1 implementation).
fn v3(ns : Uuid, name : String) -> Uuid

Generate a UUID version 3 (name-based using MD5)

#
v3_dns

fn v3_dns(name : String) -> Uuid

Convenience function to generate v3 UUID with DNS namespace

#
v3_oid

fn v3_oid(name : String) -> Uuid

Convenience function to generate v3 UUID with OID namespace

#
v3_url

fn v3_url(name : String) -> Uuid

Convenience function to generate v3 UUID with URL namespace

#
v3_x500

fn v3_x500(name : String) -> Uuid

Convenience function to generate v3 UUID with X500 namespace
fn v4() -> Uuid

Generate a random UUID (version 4) Uses random or pseudo-random numbers for all bits except version and variant

#
v4_bulk

fn v4_bulk(count : Int) -> Array[Uuid]

Generate multiple random UUIDs

#
v4_with_rng

fn v4_with_rng(rng : SimpleRng) -> Uuid

Generate a random UUID using a specific random number generator
fn v5(ns : Uuid, name : String) -> Uuid

Generate a UUID version 5 (name-based using SHA-1)

#
v5_dns

fn v5_dns(name : String) -> Uuid

Convenience function to generate v5 UUID with DNS namespace

#
v5_oid

fn v5_oid(name : String) -> Uuid

Convenience function to generate v5 UUID with OID namespace

#
v5_url

fn v5_url(name : String) -> Uuid

Convenience function to generate v5 UUID with URL namespace

#
v5_x500

fn v5_x500(name : String) -> Uuid

Convenience function to generate v5 UUID with X500 namespace
fn v7() -> Uuid

Generate a time-ordered UUID (version 7) Format: 48-bit timestamp + 12-bit random + 2-bit variant + 62-bit random

#
v7_sequence

fn v7_sequence(count : Int) -> Array[Uuid]

Generate multiple time-ordered UUIDs ensuring monotonicity

#
v7_with_timestamp

fn v7_with_timestamp(timestamp_ms : Int64) -> Uuid

Generate a time-ordered UUID with specific timestamp
fn v8(custom_data : FixedArray[Byte]) -> Uuid

Generate a custom UUID (version 8) The caller is responsible for providing the custom data

#
v8_counter

fn v8_counter(counter : Int64, node_id : Int, application_id : Int) -> Uuid

Generate a custom UUID with application-specific structure Example: counter-based UUID with specific formatting

#
v8_from_int128

fn v8_from_int128(high : Int64, low : Int64) -> Uuid

Generate a custom UUID from a 128-bit integer

#
v8_mixed

fn v8_mixed(timestamp_ms : Int64, custom_suffix : Int) -> Uuid

Generate a custom UUID with mixed content (timestamp + random + custom)