{"id":29945870,"url":"https://github.com/qkhaipham/ezid","last_synced_at":"2025-08-03T05:05:52.621Z","repository":{"id":273238602,"uuid":"915767505","full_name":"qkhaipham/EzId","owner":"qkhaipham","description":"EzId: A .NET library for generating sortable Crockford Base32 encoded IDs. It supports 96 bit \u0026 64 bit formats with customizable separators and source code generation for custom IDs.","archived":false,"fork":false,"pushed_at":"2025-07-24T14:19:41.000Z","size":138,"stargazers_count":1,"open_issues_count":7,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-01T00:31:18.265Z","etag":null,"topics":["64bits","96bits","base32","crockford","generator","id","id-generator","mongo-object-id","objectid","snowflake-id"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qkhaipham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-12T18:42:17.000Z","updated_at":"2025-07-25T12:21:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"27a26e3a-0b0d-4fb0-93d3-e0333069a245","html_url":"https://github.com/qkhaipham/EzId","commit_stats":null,"previous_names":["qkhaipham/ez-id","qkhaipham/ezid"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/qkhaipham/EzId","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qkhaipham%2FEzId","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qkhaipham%2FEzId/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qkhaipham%2FEzId/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qkhaipham%2FEzId/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qkhaipham","download_url":"https://codeload.github.com/qkhaipham/EzId/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qkhaipham%2FEzId/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268496076,"owners_count":24259411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["64bits","96bits","base32","crockford","generator","id","id-generator","mongo-object-id","objectid","snowflake-id"],"created_at":"2025-08-03T05:02:25.037Z","updated_at":"2025-08-03T05:05:52.612Z","avatar_url":"https://github.com/qkhaipham.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EzId\n\nEzId is a lightweight .NET library for generating unique, sortable, and human-friendly readable identifiers (using Crockford Base32 encoding). It supports both 96-bit (inspired on MongoDB's ObjectID ) and 64-bit ID formats ( inspired by Twitter Snowflake ) and provides source generators for custom strongly-typed IDs with customization support for separators.\n\n96-bit IDs: each process uses a random generated 40-bit generatorID and a 24-bit sequence (random start incrementing per ID). This approach makes coordination unnecessary, with collision odds negligible across distributed systems.\n\n64-bit IDs: require manually assigning unique generator IDs (0–1023) for each concurrent process to avoid collisions. Each generator can emit up to 4,096 IDs per millisecond.\n\nExample IDs:\n- 96-bit: `070AB-47XF6Q8NH0-YPA40` (22 chars, dash separators)\n- 96-bit: `070AB_47XF6Q8NH0_YPA40` (22 chars, underscore separators)\n- 96-bit: `070AB47XF6Q8NH0YPA40` (20 chars, no separators)\n- 64-bit: `070-47XF6Q8-YPA` (15 chars, dash separators)\n- 64-bit: `070_47XF6Q8_YPA` (15 chars, underscore separators)\n- 64-bit: `07047XF6Q8YPA` (13 chars, no separators)\n\n---\n\n[![Main workflow](https://github.com/qkhaipham/ezid/actions/workflows/main.yml/badge.svg)](https://github.com/qkhaipham/ezid/actions/workflows/main.yml)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=qkhaipham_EzId\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=qkhaipham_EzId)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=qkhaipham_EzId\u0026metric=coverage)](https://sonarcloud.io/component_measures?id=qkhaipham_EzId\u0026metric=coverage)\n\n|                            |                                                                                                                                                                                                                                                      |\n|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `QKP.EzId`                 | [![NuGet](https://img.shields.io/nuget/v/QKP.EzId.svg)](https://www.nuget.org/packages/QKP.EzId/) [![NuGet](https://img.shields.io/nuget/dt/QKP.EzId.svg)](https://www.nuget.org/packages/QKP.EzId/)                                                 |\n| `QKP.EzId.SourceGenerator` | [![NuGet](https://img.shields.io/nuget/v/QKP.EzId.SourceGenerator.svg)](https://www.nuget.org/packages/QKP.EzId.SourceGenerator) [![NuGet](https://img.shields.io/nuget/dt/QKP.EzId.SourceGenerator.svg)](https://www.nuget.org/packages/QKP.EzId.SourceGenerator/) |\n\n## Features\n\n- Generates unique 64-bit and 96-bit identifiers\n- Source generator for custom strongly-typed ID structs\n- Thread-safe ID generation\n- IDs are sortable by creation time\n- Human-friendly readable format (ID's are encoded with Crockford Base32) with customizable separators\n\n## Installation\n\nInstall the core library:\n\n```bash\ndotnet add package QKP.EzId\n```\n\nFor source generator support (recommended for custom ID types):\n\nRequirements:\n- **.NET SDK 5.0+** \n- **Roslyn 3.8+**\n\n```bash\ndotnet add package QKP.EzId.SourceGenerator\n```\n\n## Usage\n\n### EzId ( 96-bit ID )\n\n```csharp\nusing QKP.EzId;\n\nEzId id = EzId.GetNextId();\nstring idString = id.ToString(); // e.g. \"070AB-47XF6Q8NH0-YPA40\"\nEzId parsedId = EzId.Parse(idString);\n```\n\n### CompactEzId ( 64-bit ID )\n\n```csharp\nusing QKP.EzId;\n\n// Create a CompactEzIdGenerator with a unique generator ID (0-1023)\nvar generator = new CompactEzIdGenerator\u003cCompactEzId\u003e(generatorId: 1);\n\n// Generate a new ID\nCompactEzId id = generator.GetNextId();\n\n// Convert to string\nstring idString = id.ToString(); // e.g. \"070-47XF6Q8-YP0\"\n\n// Parse from string\nCompactEzId parsedId = CompactEzId.Parse(idString);\n```\n\n### Important: Generator ID\n\nThe `generatorId` parameter is crucial for preventing ID collisions across different generators. It must be:\n\n- A unique number between 0 and 1023 (10 bits)\n- Consistent for each generator instance\n- Different for each concurrent generator in your distributed system\n\nFor example:\n\n```csharp\n// Example for distributed system\nvar node1Generator = new CompactEzIdGenerator\u003cCompactEzId\u003e(generatorId: 1);  // For Node 1\nvar node2Generator = new CompactEzIdGenerator\u003cCompactEzId\u003e(generatorId: 2);  // For Node 2\n```\n\n### Source Generated Custom ID Types\n\nYou can create your own strongly-typed IDs using the source generator. Annotate a partial struct with `[EzIdType]` or customize with constructor arguments:\n\n```csharp\nusing QKP.EzId;\n\n// Default: 96-bit, dash separators at positions [5, 15]\n// eg. XXXXX-XXXXXXXXXX-XXXXX\n[EzIdType]\npublic partial struct ProductId { }\n\n// Custom: 96-bit, dash separators at positions [2, 18]\n// eg. XX-XXXXXXXXXXXXXXXX-XX\n[EzIdType(IdBitSize.Bits96, SeparatorOptions.Dash, [2, 18])]\npublic partial struct PriceId { }\n\n// Custom 64-bit, underscore separators at positions [3, 10]\n// eg. XXX_XXXXXXX_XXX\n[EzIdType(IdBitSize.Bits64, SeparatorOptions.Underscore, [3, 10])]\npublic partial struct SessionId { }\n\n// Custom 64-bit, no separators (64-bit)\n// eg. XXXXXXXXXXXXX\n[EzIdType(IdBitSize.Bits64, SeparatorOptions.None, [])]\npublic partial struct UserId { }\n```\n\n#### Usage\n\n```csharp\nvar productId = ProductId.GetNextId();\nstring productId = id.ToString(); // eg. \"070AB-47XF6Q8NH0-YPA40\"\nSessionId parsedProductId = ProductId.Parse(productId);\n\nvar generator = new CompactEzIdGenerator\u003cSessionId\u003e(generatorId: 1);\nSessionId sessionId = generator.GetNextId(); \nstring sessionIdString = id.ToString(); // eg. \"070_47XF6Q8_YP0\"\nSessionId parsedSessionId = SessionId.Parse(sessionIdString);\n```\n\n#### JSON Serialization\n\nSource generated ID types automatically include JSON converters for System.Text.Json:\n\n```csharp\n// System.Text.Json\nvar product = new Product { Id = productId, Name = \"Example\" };\nstring json = JsonSerializer.Serialize(product);\nvar deserializedProduct = JsonSerializer.Deserialize\u003cProduct\u003e(json);\n```\n\n## Why EzId vs. GUID v4 and GUID v7\n\nEzId is tailored for scenarios demanding concise, sortable, and human-friendly identifiers, whereas GUIDs are general-purpose with fixed format and length. The table below highlights the key distinctions:\n\n| Feature            | EzId (96‑bit)                                                          | CompactEzId (64‑bit)                                               | GUID v4                                        | GUID v7                                                        |\n| ------------------ | ---------------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------- | -------------------------------------------------------------- |\n| **Readability**    | 22 chars ( 20 base32 characters + 2 separators)        | 15 chars ( 13 base32 characters + 2 separators )      | 36 chars (32 hex + 4 hyphens)                  | 36 chars (32 hex + 4 hyphens)                                  |\n| **Sortability**    | Embeds second precision timestamp: lexicographical/chronological order          | Embeds ms-precision timestamp: lexicographical/chronological order               | None (fully random)                            | Embeds ms-precision timestamp; lexicographical/chronological order |\n| **Throughput**     | Up to 16,777,216 IDs/sec per process                                   | 4,096 IDs/ms per generator (max 1,024 generators)                  | High, but no built-in coordination or sequence | High, but no built-in coordination or sequence                 |\n| **Collision Risk** | Negligible because of 40-bit random generatorId and 24-bit sequence across IDs | Negligible when generator IDs are unique and sequences initialized | Astronomically low (≈1 in 2¹²²; practically impossible)                                    | Astronomically low (≈1 in 2⁷⁴; practically impossible)                                                    |\n| **Storage**        | 96 bits raw; 22 chars encoded                                    | 64 bits raw; 15 chars encoded                                | 128 bits raw; 36 chars encoded                 | 128 bits raw; 36 chars encoded                                 |\n\n**Choosing the right ID**:\\*\\*:\n\n- Use **EzId** for high‑performance, human‑readable, and time‑sortable IDs in distributed systems.\n- Use **GUID v4** when you need a simple globally unique ID without ordering requirements.\n- Use **GUID v7** when you want a sortable UUID but can tolerate its longer, less readable format.\n\n\n### ID Structure\n\n#### 96-bit ID (EzId and custom 96-bit types)\n- 32 bits: timestamp (seconds since UNIX epoch)\n- 40 bits: generator ID (random per process)\n- 24 bits: sequence (random start, increments per ID)\n\n##### ID Generation\n\n- Generate up to 16,777,216 IDs per second\n\n#### 64-bit ID (CompactEzId and custom 64-bit types)\n- 1 bit unused\n- 41 bits for timestamp (milliseconds since epoch)\n- 10 bits for generator ID (0-1023)\n- 12 bits for sequence number (0-4095)\n\n##### ID Generation\n\n- Supports up to 1024 concurrent generators\n- Generates up to 4096 unique IDs per millisecond per generator\n\nBoth formats are sortable by creation time and support high concurrency.\n\n### Why Base32 (Crockford)\n\nBase32 (Crockford) is chosen for encoding IDs in EzId because it offers:\n\n- Compactness: reduces the character length compared to hexadecimal or Base64, making IDs shorter and more manageable.\n- Readability: omits visually similar characters (I, L, O, U), preventing misinterpretation in human transcription.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the Apache License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqkhaipham%2Fezid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqkhaipham%2Fezid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqkhaipham%2Fezid/lists"}