summaryrefslogtreecommitdiffstats
path: root/bin/genids.pl
blob: c94d8a2a37023414c15aa231b476405582c358e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/perl

use feature 'state';
use Math::BigInt try => 'GMP';
use Fcntl;

# Gets a random number from /dev/urandom, and adjust it to the range
# 0 <= value < $n without bias
sub get_random($) {
    my($n) = @_;
    state $fh;

    if (!defined($fh)) {
	sysopen($fh, '/dev/urandom', O_RDONLY) or die;
    }

    my $nbytes;
    while ($nbytes < 8 && $n > (1 << $nbytes)) {
	$nbytes++;
    }

    die if (sysread($fh, my $x, $nbytes) != $nbytes);

    # Eliminate bias by rejecting numbers at the bottom of the range
    # so that we end up with a whole number of

    my $xtoosmall = Math::BigInt::bmodpow(256, $nbytes, $n)->numify();

    do {
	$x = unpack('Q<', $x . ("\0" x (8 - $nbytes)));
    } while ($x < $xtoosmall);

    return $x % $n;
}

($prefix, $n, $start, $max) = @ARGV;

if ($n < 1 || !defined($prefix)) {
    die "Usage: $0 prefix count [start [max]]\n";
}

$start = 1 unless ( defined($start) );
$max = $start + $n - 1 unless ( defined($max) );
$max = $max + 0;
$digits = length($max);

$prefix = "\U$prefix";
if ($prefix !~ /^([A-Z0-9_-]+)(|[,\/:\s]([A-Z0-9_-]+))$/) {
    die "$0: invalid prefix: $prefix\n";
}

if ($2 ne '') {
    $prefix = $1;
    $suffix = $3;
}

$a = ord('A');

for ($i = 0; $i < $n; $i++) {
    undef $rid;
    for ($j = 0; $j < 2; $j++) {
	$rid .= chr($a + get_random(26));
    }
    printf "%s%s%s%0*d\n", $prefix, $rid, $suffix, $digits, $start + $i;
}