#pike __REAL_VERSION__ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 |
constant clk_offset = Calendar.ISO.Second(1582,10,15,0,0,0)-> |
distance(Calendar.ISO.Second("unix",0))->how_many(Calendar.Second) |
* 10000000; |
#else |
constant clk_offset = 0x01b21dd213814000; |
#endif |
|
protected string low_format_uuid (string bin_uuid) |
{ |
string ret = String.string2hex(bin_uuid); |
while( sizeof(ret)<32 ) ret = "0"+ret; |
return ret[..7] + "-" + ret[8..11] + "-" + ret[12..15] +"-" + |
ret[16..19] + "-" + ret[20..]; |
} |
|
|
|
class UUID { |
|
|
int timestamp; |
|
|
int posix_time() { |
return (timestamp-clk_offset)/10000000; |
} |
|
|
int version = 1; |
|
|
|
string str_version() { |
return ([ 1 : "Time-based", |
2 : "DCE security", |
3 : "Name-based (MD5)", |
4 : "Random", |
5 : "Name-based (SHA)"])[version] || "Unknown"; |
} |
|
|
int var = 1; |
|
|
|
string str_variant() { |
switch(var) { |
case 0: return "Reserved, NCS backward compatibility"; |
case 1: return "IETF draft variant"; |
case 2: return "Reserved, Microsoft Corporation backward compatibility"; |
default: return "Illegal variant"; |
} |
} |
|
|
|
int clk_seq; |
|
|
int node; |
|
|
void validate() { |
if(!timestamp && !version && !var && !clk_seq && !node) |
return; |
|
if( !(< 0,1,2 >)[var] ) error("Illegal variant %O.\n", var); |
|
if(timestamp & ~((1<<60)-1)) |
error("Timestamp %O out of range.\n", timestamp); |
if(version & ~15) |
error("Version %O out of range.\n", version); |
|
int seq_mask = ([ 0:~((1<<15)-1), |
1:~((1<<14)-1), |
2:~((1<<13)-1) ])[var]; |
if(clk_seq & seq_mask) |
error("Clock sequence %O out of range.\n", clk_seq); |
|
if(node & ~((1<<48)-1)) |
error("Node %012x out of range\n", node); |
} |
|
|
int time_low() { |
return timestamp & 0xFFffFFff; |
} |
|
|
int time_mid() { |
return (timestamp >> 32) & 0xFFff; |
} |
|
|
int time_hi_and_version() { |
return ((timestamp >> 32+16) << 4) | version; |
} |
|
|
string encode() { |
|
validate(); |
string ret = ""; |
|
|
|
int t = timestamp; |
ret += sprintf("%4c", t); |
t >>= 32; |
ret += sprintf("%2c", t); |
t >>= 16; |
t |= version<<12; |
ret += sprintf("%2c", t); |
|
int variant_mask = (1<<(16-var))-1; |
t = 0xffff & ~variant_mask; |
t |= clk_seq & (variant_mask >> 1); |
ret += sprintf("%2c", t); |
|
ret += sprintf("%6c", node); |
|
return ret; |
} |
|
|
string str() { |
return low_format_uuid (encode()); |
} |
|
|
string urn() { |
return "urn:uuid:"+str(); |
} |
|
|
void create(void|string in) { |
if(!in) return; |
sscanf(in, "urn:%s", in); |
sscanf(in, "uuid:%s", in); |
switch(sizeof(in)) { |
case 36: |
in -= "-"; |
if(sizeof(in)!=32) error("Illegal UUID.\n"); |
|
case 32: |
in = String.hex2string(in); |
|
case 16: |
int time_low, time_mid, time_hi_and_version; |
sscanf(in, "%4c%2c%2c%2c%6c", time_low, time_mid, time_hi_and_version, |
clk_seq, node); |
version = (time_hi_and_version & 0xf000)>>12; |
time_hi_and_version &= 0x0fff; |
timestamp = time_hi_and_version<<(6*8) | time_mid<<(4*8) | time_low; |
int var_mask = 1<<15; |
for (var = 0; clk_seq & var_mask; var++, var_mask>>=1) |
; |
clk_seq &= var_mask - 1; |
break; |
default: |
error("Illegal UUID.\n"); |
} |
validate(); |
} |
|
protected mixed cast(string to) { |
switch(to) { |
case "string": return str(); |
case "mapping": return ([ |
"time_low" : time_low(), |
"time_mid" : time_mid(), |
"time_hi_and_version" : time_hi_and_version(), |
]); |
} |
return UNDEFINED; |
} |
|
} |
|
|
|
protected class ClkSeq |
{ |
int clk_seq = random(1<<14); |
int last_time; |
|
System.Time clock = System.Time(); |
|
int time() { |
return clock->usec_full*10 + clk_offset; |
} |
|
array(int) get() { |
|
int timestamp = time(); |
|
if( timestamp <= last_time) { |
clk_seq++; |
clk_seq &= (1<<14)-1; |
} |
last_time = timestamp; |
|
return ({ timestamp, clk_seq }); |
} |
|
array(int) get_state() { |
return ({ last_time, clk_seq }); |
} |
|
void set_state(int _last_time, int _clk_seq) { |
last_time = _last_time; |
clk_seq = _clk_seq; |
} |
} |
|
protected ClkSeq clk_seq = ClkSeq(); |
|
|
|
|
|
array(int) get_clock_state() { |
return clk_seq->get_state(); |
} |
|
|
|
|
void set_clock_state(int last_time, int seq) { |
clk_seq->set_state(last_time, seq); |
} |
|
|
|
|
UUID make_version1(int node) { |
UUID u=UUID(); |
[ u->timestamp, u->clk_seq ] = clk_seq->get(); |
u->version = 1; |
if(node<0) |
u->node = random(1<<48) | 1<<40; |
else |
u->node = node; |
return u; |
} |
|
#if constant(Crypto.MD5.hash) |
|
|
|
UUID make_version3(string name, string|UUID namespace) { |
|
if(stringp(namespace)) |
namespace = UUID(namespace); |
namespace = namespace->encode(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 |
namespace = reverse(namespace[0..3]) + reverse(namespace[4..5]) + |
reverse(namespace[6..7]) + namespace[8..]; |
#endif |
|
|
string ret = Crypto.MD5.hash(namespace+name); |
|
#if 0 |
ret = reverse(ret[0..3]) + reverse(ret[4..5]) + |
reverse(ret[6..7]) + ret[8..]; |
#endif |
|
ret &= |
"\xff\xff\xff\xff" |
"\xff\xff" |
"\x0f\xff" |
"\x3f\xff" |
"\xff\xff\xff\xff\xff\xff"; |
|
ret |= |
"\x00\x00\x00\x00" |
"\x00\x00" |
"\x30\x00" |
"\x80\x00" |
"\x00\x00\x00\x00\x00\x00"; |
|
return UUID(ret); |
} |
|
#endif |
|
|
UUID make_version4() { |
UUID u=UUID(); |
u->timestamp = random(1<<60); |
u->version = 4; |
u->clk_seq = random(1<<14); |
u->node = random(1<<48); |
return u; |
} |
|
#if constant(Crypto.SHA1.hash) |
|
|
|
UUID make_version5(string name, string|UUID namespace) { |
|
if(stringp(namespace)) |
namespace = UUID(namespace); |
namespace = namespace->encode(); |
|
string ret = Crypto.SHA1.hash(namespace+name)[..15]; |
|
ret &= |
"\xff\xff\xff\xff" |
"\xff\xff" |
"\x0f\xff" |
"\x3f\xff" |
"\xff\xff\xff\xff\xff\xff"; |
|
ret |= |
"\x00\x00\x00\x00" |
"\x00\x00" |
"\x50\x00" |
"\x80\x00" |
"\x00\x00\x00\x00\x00\x00"; |
|
return UUID(ret); |
} |
|
#endif |
|
|
|
|
|
string format_uuid(string uuid) |
{ |
if (sizeof (uuid) == 16) |
return low_format_uuid (uuid); |
return UUID(uuid)->str(); |
} |
|
|
string parse_uuid(string uuid) |
{ |
if (sizeof (uuid) >= 32) { |
string s = uuid - "-"; |
if (sizeof (s) == 32) |
return String.hex2string (s); |
} |
return UUID(uuid)->encode(); |
} |
|
|
|
|
constant Nil_UUID = "00000000-0000-0000-0000-000000000000"; |
|
|
constant NameSpace_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; |
|
|
constant NameSpace_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"; |
|
|
constant NameSpace_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8"; |
|
|
constant NameSpace_X500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"; |
|
|
UUID make_null() { |
UUID u=UUID(); |
u->version = 0; |
u->var = 0; |
u->clk_seq = 0; |
return u; |
} |
|
|
|
#if constant(Crypto.MD5.hash) |
|
|
UUID make_dns(string name) { |
return make_version3(name, NameSpace_DNS); |
} |
|
|
UUID make_url(string name) { |
return make_version3(name, NameSpace_URL); |
} |
|
|
UUID make_oid(string name) { |
return make_version3(name, NameSpace_OID); |
} |
|
|
UUID make_x500(string name) { |
return make_version3(name, NameSpace_X500); |
} |
|
#elif constant(Crypto.SHA1.hash) |
|
|
UUID make_dns(string name) { |
return make_version5(name, NameSpace_DNS); |
} |
|
|
UUID make_url(string name) { |
return make_version5(name, NameSpace_URL); |
} |
|
|
UUID make_oid(string name) { |
return make_version5(name, NameSpace_OID); |
} |
|
|
UUID make_x500(string name) { |
return make_version5(name, NameSpace_X500); |
} |
|
#endif |
|
|