Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 invipal
Copyright (c) 2024 sirsegv, invipal

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

This package is V implementation of [NanoID](https://github.com/ai/nanoid)

Generated from [Go Nanoid](https://github.com/matoous/go-nanoid)
It was forked from [invipal's implemenation](https://github.com/invipal/nanoid), which itself was generated from [Go Nanoid](https://github.com/matoous/go-nanoid)

**Safe.** It uses cryptographically strong random generator.

Expand All @@ -23,21 +23,21 @@ and has the same number of unique options in just 22 symbols instead of 36.
Via vpm

```bash
$ v install invipal.nanoid
$ v install squidink7.nanoid
```

## Usage

Generate ID

```v
id := nanoid.new() or { 'error' }
id := nanoid.new()
```

Generate ID with a custom alphabet and length

```v
id := nanoid.generate('erzurum', 25) or { 'error' }
id := nanoid.generate('erzurum', 25)
```

## Contribution
Expand Down
65 changes: 30 additions & 35 deletions nanoid.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ module nanoid
import math
import crypto.rand

const (
// default_alphabet is the alphabet used for ID characters by default.
default_alphabet = '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.runes()
// default length for ID
default_size = 21
)
// default_alphabet is the alphabet used for ID characters by default.
const default_alphabet = '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.runes()

// get_mask generates bit mask used to obtain bits from the random bytes that are used to get index of random character
// from the alphabet. Example: if the alphabet has 6 = (110)_2 characters it is sufficient to use mask 7 = (111)_2
fn get_mask(alphabet_size int) int {
for i := 1; i <= 8; i++ {
for i in 1..9 {
mask := (2 << u64(i)) - 1
if mask >= alphabet_size - 1 {
return mask
Expand All @@ -22,8 +18,8 @@ fn get_mask(alphabet_size int) int {
return 0
}

// generate is a low-level function to change alphabet and ID size.
pub fn generate(alphabet string, size int) ?string {
// generate_opt is a low-level function to change alphabet and ID size.
pub fn generate_opt(alphabet string, size int) !string {
chars := alphabet.runes()

if alphabet.len == 0 || alphabet.len > 255 {
Expand All @@ -40,10 +36,12 @@ pub fn generate(alphabet string, size int) ?string {
step := int(math.ceil(ceil_arg))

mut id := []rune{len: size}
// bytes := []byte{len: step}

bytes := rand.read(step) or { return error(err.msg()) }
for j := 0; true; {
for i := 0; i < step; i++ {

mut j := 0
for true {
for i in 0..step {
curr_byte := bytes[i] & u8(mask)
if curr_byte < u8(chars.len) {
id[j] = chars[curr_byte]
Expand All @@ -55,42 +53,39 @@ pub fn generate(alphabet string, size int) ?string {
}
}

return error('could not generated')
return error('could not generate')
}

// generate is the same as generate_opt but panics on error.
pub fn generate(alphabet string, size int) string {
return generate_opt(alphabet, size) or { panic(err.msg()) }
}

// must_generate is the same as generate but panics on error.
pub fn must_generate(alphabet string, size int) string {
id := generate(alphabet, size) or { panic(err.msg()) }
return id
// Allows the id size to be passed as a parameter
@[params]
pub struct NanoIDParams {
size int = 21
}

// new generates secure URL-friendly unique ID.
// new_opt generates secure URL-friendly unique ID.
// Accepts optional parameter - length of the ID to be generated (21 by default).
pub fn new(l ...int) ?string {
mut size := int(0)
if l.len == 0 {
size = default_size
} else if l.len == 1 {
size = l[0]
if size <= 0 {
return error('size must be positive integer')
}
} else {
return error('unexpected parameter')
pub fn new_opt(p NanoIDParams) !string {
mut size := p.size
if size <= 0 {
return error('size must be positive integer')
}

bytes := rand.read(size) or { return error(err.msg()) }
bytes := rand.read(size)!

mut id := []rune{len: size}
for i := 0; i < size; i++ {
for i in 0..size {
id[i] = default_alphabet[bytes[i] & 63]
}

return id[..size].string()
}

// must is the same as new but panics on error.
pub fn must(l ...int) string {
id := new(...l) or { panic(err.msg()) }
return id
// new is the same as new_opt but panics on error.
pub fn new(p NanoIDParams) string {
return new_opt(p) or { panic(err.msg()) }
}