fast_cuid2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +118 -0
- data/ext/fast_cuid2/extconf.rb +16 -0
- data/ext/fast_cuid2/fast_cuid2.c +119 -0
- data/fast_cuid2.gemspec +34 -0
- data/lib/fast_cuid2/fast_cuid2.so +0 -0
- data/lib/gem/fast_cuid2/version.rb +5 -0
- data/lib/gem/fast_cuid2.rb +15 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 674b326baeddf83d78dd24637f903fbcf6a823c46cb0e2d951409d0b5b539b94
|
4
|
+
data.tar.gz: f26f14bec785defaa804cb62e6602e78946f7c331dab1e167d7234165f5d4f93
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a416b5eac1fcff9cac876689482c641f9df932d49c5424294b55b94d7e462dd83cd4f43f858370dd9c3768668927e2889cdae6b889945d22528b03b1369db6be
|
7
|
+
data.tar.gz: 5b3efb13674b84b39aefa5cad16ba47d040d90a0817fd25118cd29e88076064b8c6efd90d8d58ac63a1751a9c446e9b8739039534ce5b05116c86e429e8570a2
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# FastCuid2 🚀
|
2
|
+
|
3
|
+
A high-performance CUID2 (Collision-resistant Unique ID) generator for Ruby, implemented in C for maximum speed while maintaining cryptographic security.
|
4
|
+
|
5
|
+
## What is CUID2? 🤔
|
6
|
+
|
7
|
+
CUID2 is the next generation of collision-resistant ids, designed to be:
|
8
|
+
- 🔒 Secure: Uses cryptographically secure random numbers
|
9
|
+
- 📏 Shorter: 24 characters vs CUID1's 25
|
10
|
+
- 🔗 URL-safe: Uses a restricted character set
|
11
|
+
- ⏰ Time-sortable: Includes a timestamp component
|
12
|
+
- 🎯 Unique: Has a negligible chance of collision
|
13
|
+
|
14
|
+
## Installation 💿
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'fast_cuid2'
|
20
|
+
```
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ gem install fast_cuid2
|
32
|
+
```
|
33
|
+
|
34
|
+
## Usage 🛠️
|
35
|
+
|
36
|
+
### Basic Usage 💡
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'fast_cuid2'
|
40
|
+
|
41
|
+
# Generate a new CUID2
|
42
|
+
id = FastCuid2.generate
|
43
|
+
puts id # => "k2zt4qxvz5..."
|
44
|
+
|
45
|
+
# Use as a before_create callback in your models
|
46
|
+
class User < ApplicationRecord
|
47
|
+
before_create :set_cuid2
|
48
|
+
|
49
|
+
private
|
50
|
+
def set_cuid2
|
51
|
+
self.id = FastCuid2.generate
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Database Migrations 🗄️
|
57
|
+
|
58
|
+
Example migration for creating a new table with CUID2:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class CreateUsers < ActiveRecord::Migration[7.0]
|
62
|
+
def change
|
63
|
+
create_table :users, id: false do |t|
|
64
|
+
t.string :id, primary_key: true, null: false, limit: 24
|
65
|
+
t.timestamps
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Adding a CUID2 column to an existing table:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class AddReferenceToPosts < ActiveRecord::Migration[7.0]
|
75
|
+
def change
|
76
|
+
add_column :posts, :reference_id, :string, limit: 24
|
77
|
+
add_index :posts, :reference_id, unique: true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
## CUID2 Format 📋
|
83
|
+
|
84
|
+
Each CUID2 is a 24-character string that:
|
85
|
+
- 📝 Always starts with a letter
|
86
|
+
- ⌨️ Uses only lowercase letters and numbers (excluding i, l, o, u)
|
87
|
+
- 🕒 Includes a time component for rough sorting
|
88
|
+
- 🔐 Has cryptographically secure random data
|
89
|
+
|
90
|
+
## Performance ⚡
|
91
|
+
|
92
|
+
FastCuid2 is implemented in C for maximum performance. It uses:
|
93
|
+
- 🔒 OpenSSL's CSPRNG for secure random numbers
|
94
|
+
- ⚙️ Optimized bit operations
|
95
|
+
- 💾 Minimal memory allocations
|
96
|
+
- 🔄 Thread-safe implementation
|
97
|
+
|
98
|
+
## Development 👩💻
|
99
|
+
|
100
|
+
After checking out the repo, run:
|
101
|
+
|
102
|
+
```bash
|
103
|
+
$ bundle install
|
104
|
+
$ rake compile # Builds the C extension
|
105
|
+
$ rake spec # Runs the tests
|
106
|
+
```
|
107
|
+
|
108
|
+
## Contributing 🤝
|
109
|
+
|
110
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sebyx07/fast_cuid2. This project is intended to be a safe, welcoming space for collaboration.
|
111
|
+
|
112
|
+
## License 📜
|
113
|
+
|
114
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
115
|
+
|
116
|
+
## Code of Conduct 🤝
|
117
|
+
|
118
|
+
Everyone interacting in the FastCuid2 project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/sebyx07/fast_cuid2/blob/master/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mkmf'
|
4
|
+
|
5
|
+
# Add optimization flags
|
6
|
+
$CFLAGS << ' -O3 -Wall -Wextra'
|
7
|
+
|
8
|
+
# Check for required headers
|
9
|
+
have_header('ruby.h')
|
10
|
+
have_library('crypto')
|
11
|
+
have_header('openssl/rand.h')
|
12
|
+
|
13
|
+
extension_name = 'fast_cuid2/fast_cuid2'
|
14
|
+
dir_config(extension_name)
|
15
|
+
|
16
|
+
create_makefile(extension_name)
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <time.h>
|
3
|
+
#include <openssl/rand.h>
|
4
|
+
#include <string.h>
|
5
|
+
|
6
|
+
/* Constants for CUID2 generation */
|
7
|
+
#define CUID2_LENGTH 24
|
8
|
+
#define CUID2_BUFFER_SIZE (CUID2_LENGTH + 1)
|
9
|
+
#define TIMESTAMP_LENGTH 6
|
10
|
+
#define RANDOM_BYTES_LENGTH 12
|
11
|
+
#define BASE32_CHARS_LENGTH 32
|
12
|
+
#define TIMESTAMP_BITS 5
|
13
|
+
#define RANDOM_CHUNK_SIZE 2
|
14
|
+
#define BASE32_CHARS_PER_CHUNK 3
|
15
|
+
|
16
|
+
/* Base32 alphabet excluding i, l, o, u for better readability */
|
17
|
+
static const char BASE32_CHARS[] = "0123456789abcdefghjkmnpqrstvwxyz";
|
18
|
+
|
19
|
+
static VALUE rb_mFastCuid2;
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Encodes an integer into base32 representation.
|
23
|
+
* @param num The number to encode
|
24
|
+
* @param output The buffer to write to
|
25
|
+
* @param length The number of characters to write
|
26
|
+
*/
|
27
|
+
static void encode_base32(uint64_t num, char *output, int length) {
|
28
|
+
for (int i = length - 1; i >= 0; i--) {
|
29
|
+
output[i] = BASE32_CHARS[num & 0x1f];
|
30
|
+
num >>= TIMESTAMP_BITS;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Generates cryptographically secure random bytes.
|
36
|
+
* @param buf Buffer to store random bytes
|
37
|
+
* @param length Number of random bytes to generate
|
38
|
+
* @raises RuntimeError if random byte generation fails
|
39
|
+
*/
|
40
|
+
static void generate_random_bytes(unsigned char *buf, int length) {
|
41
|
+
if (RAND_bytes(buf, length) != 1) {
|
42
|
+
rb_raise(rb_eRuntimeError, "Failed to generate secure random bytes");
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Ensures the first character of the CUID2 is a letter.
|
48
|
+
* @param cuid The CUID2 string to modify
|
49
|
+
*/
|
50
|
+
static void ensure_first_char_is_letter(char *cuid) {
|
51
|
+
if (cuid[0] >= '0' && cuid[0] <= '9') {
|
52
|
+
int offset = 10 + (cuid[0] - '0'); // Map digit to letter range
|
53
|
+
cuid[0] = BASE32_CHARS[offset];
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Generates a CUID2 (Collision-resistant Unique IDentifier).
|
59
|
+
* Format: <6 chars timestamp><18 chars random>
|
60
|
+
* @return [String] A 24-character CUID2
|
61
|
+
* @raise [RuntimeError] If generation fails
|
62
|
+
*/
|
63
|
+
static VALUE rb_generate_cuid2(VALUE self) {
|
64
|
+
char cuid[CUID2_BUFFER_SIZE] = {0};
|
65
|
+
struct timespec ts;
|
66
|
+
unsigned char random_bytes[RANDOM_BYTES_LENGTH];
|
67
|
+
|
68
|
+
// Get current timestamp in milliseconds
|
69
|
+
if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
|
70
|
+
rb_raise(rb_eRuntimeError, "Failed to get system time");
|
71
|
+
}
|
72
|
+
uint64_t timestamp = (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
|
73
|
+
|
74
|
+
// Generate random bytes
|
75
|
+
generate_random_bytes(random_bytes, RANDOM_BYTES_LENGTH);
|
76
|
+
|
77
|
+
// Encode timestamp (first 6 chars)
|
78
|
+
encode_base32(timestamp, cuid, TIMESTAMP_LENGTH);
|
79
|
+
|
80
|
+
// Fill remaining 18 chars with random data
|
81
|
+
// Process 2 bytes at a time to generate 3 base32 chars
|
82
|
+
for (int i = 0; i < RANDOM_BYTES_LENGTH; i += RANDOM_CHUNK_SIZE) {
|
83
|
+
uint32_t rand_chunk =
|
84
|
+
((uint32_t)random_bytes[i] << 8) |
|
85
|
+
((uint32_t)random_bytes[i + 1]);
|
86
|
+
encode_base32(rand_chunk, cuid + TIMESTAMP_LENGTH + (i / RANDOM_CHUNK_SIZE) * BASE32_CHARS_PER_CHUNK, BASE32_CHARS_PER_CHUNK);
|
87
|
+
}
|
88
|
+
|
89
|
+
ensure_first_char_is_letter(cuid);
|
90
|
+
|
91
|
+
return rb_str_new2(cuid);
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Validates a CUID2 string.
|
96
|
+
* @param str [String] The string to validate
|
97
|
+
* @return [Boolean] true if valid CUID2, false otherwise
|
98
|
+
*/
|
99
|
+
static VALUE rb_validate_cuid2(VALUE self, VALUE str) {
|
100
|
+
Check_Type(str, T_STRING);
|
101
|
+
|
102
|
+
if (RSTRING_LEN(str) != CUID2_LENGTH) return Qfalse;
|
103
|
+
|
104
|
+
const char *cuid = StringValuePtr(str);
|
105
|
+
for (int i = 0; i < CUID2_LENGTH; i++) {
|
106
|
+
if (!strchr(BASE32_CHARS, cuid[i])) return Qfalse;
|
107
|
+
}
|
108
|
+
|
109
|
+
// First character must be a letter
|
110
|
+
if (strchr("0123456789", cuid[0])) return Qfalse;
|
111
|
+
|
112
|
+
return Qtrue;
|
113
|
+
}
|
114
|
+
|
115
|
+
void Init_fast_cuid2(void) {
|
116
|
+
rb_mFastCuid2 = rb_define_module("FastCuid2");
|
117
|
+
rb_define_singleton_method(rb_mFastCuid2, "generate", rb_generate_cuid2, 0);
|
118
|
+
rb_define_singleton_method(rb_mFastCuid2, "valid?", rb_validate_cuid2, 1);
|
119
|
+
}
|
data/fast_cuid2.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/gem/fast_cuid2/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'fast_cuid2'
|
7
|
+
spec.version = FastCuid2::VERSION
|
8
|
+
spec.authors = ['sebi']
|
9
|
+
spec.email = ['gore.sebyx@yahoo.com']
|
10
|
+
|
11
|
+
spec.summary = 'Fast CUID2 generator with C extension'
|
12
|
+
spec.description = 'High-performance CUID2 (Collision-resistant Unique ID) generator ' \
|
13
|
+
'implemented in C for maximum speed while maintaining cryptographic security.'
|
14
|
+
spec.homepage = 'https://github.com/sebyx07/fast_cuid2'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
spec.required_ruby_version = '>= 3.0.0'
|
17
|
+
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
20
|
+
|
21
|
+
# Include C extension
|
22
|
+
spec.extensions = ['ext/fast_cuid2/extconf.rb']
|
23
|
+
|
24
|
+
# Include both lib and ext directories
|
25
|
+
spec.files = Dir.glob('{lib,ext}/{**/*,*}') +
|
26
|
+
['README.md', File.basename(__FILE__)]
|
27
|
+
|
28
|
+
# Set require paths for both the gem and extension
|
29
|
+
spec.require_paths = %w[lib/gem lib]
|
30
|
+
|
31
|
+
# Development dependencies
|
32
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
33
|
+
spec.add_development_dependency 'rake-compiler', '~> 1.2'
|
34
|
+
end
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'fast_cuid2', '**', '*.rb')).each do |file|
|
4
|
+
require file
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'fast_cuid2/fast_cuid2'
|
9
|
+
rescue LoadError
|
10
|
+
puts 'Error loading fast_cuid2 extension'
|
11
|
+
end
|
12
|
+
|
13
|
+
module FastCuid2
|
14
|
+
class Error < StandardError; end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fast_cuid2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- sebi
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-02-10 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: rake
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '13.0'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '13.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake-compiler
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.2'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.2'
|
40
|
+
description: High-performance CUID2 (Collision-resistant Unique ID) generator implemented
|
41
|
+
in C for maximum speed while maintaining cryptographic security.
|
42
|
+
email:
|
43
|
+
- gore.sebyx@yahoo.com
|
44
|
+
executables: []
|
45
|
+
extensions:
|
46
|
+
- ext/fast_cuid2/extconf.rb
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- README.md
|
50
|
+
- ext/fast_cuid2/extconf.rb
|
51
|
+
- ext/fast_cuid2/fast_cuid2.c
|
52
|
+
- fast_cuid2.gemspec
|
53
|
+
- lib/fast_cuid2/fast_cuid2.so
|
54
|
+
- lib/gem/fast_cuid2.rb
|
55
|
+
- lib/gem/fast_cuid2/version.rb
|
56
|
+
homepage: https://github.com/sebyx07/fast_cuid2
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata:
|
60
|
+
homepage_uri: https://github.com/sebyx07/fast_cuid2
|
61
|
+
source_code_uri: https://github.com/sebyx07/fast_cuid2
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib/gem
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 3.0.0
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.6.3
|
78
|
+
specification_version: 4
|
79
|
+
summary: Fast CUID2 generator with C extension
|
80
|
+
test_files: []
|