Is there a way do create on OS X UUID’s via “declare”?
Cocoa UUID Generation
Try the following (very basic, untested):
Function newUUID as Text
//requires OSX 10.8 or higher, bad things will happen on 10.7
#if TargetCocoa
declare function NSClassFromString lib "Cocoa" (clsName as cfstringref) as ptr
declare function UUID lib "Cocoa" selector "UUID" (clsRef as ptr) as ptr
declare function UUIDString lib "Cocoa" selector "UUIDString" (obj_id as ptr) as cfstringref
static NSUUID as ptr
if NSUUID = nil then
NSUUID = UUID(NSClassFromString("NSUUID"))
end if
return UUIDString(NSUUID)
#endif
end function
It could also be turned into a class with a small amount of effort if you are looking to control the initialization of the NSUUID instance.
[quote=150077:@Jason King]Try the following (very basic, untested):
Function newUUID as Text
//requires OSX 10.8 or higher, bad things will happen on 10.7
#if TargetCocoa
declare function NSClassFromString lib "Cocoa" (clsName as cfstringref) as ptr
declare function UUID lib "Cocoa" selector "UUID" (clsRef as ptr) as ptr
declare function UUIDString lib "Cocoa" selector "UUIDString" (obj_id as ptr) as cfstringref
static NSUUID as ptr
if NSUUID = nil then
NSUUID = UUID(NSClassFromString("NSUUID"))
end if
return UUIDString(NSUUID)
#endif
end function
It could also be turned into a class with a small amount of effort if you are looking to control the initialization of the NSUUID instance.[/quote]
First Generation will work, after a second round, the program terminates.
Try changing the lines of code inside of the “if NSUUID = nil then … end if” to
declare function retain lib "Cocoa" selector "retain" (obj_id as ptr) as ptr
NSUUID = retain(UUID(NSClassFromString("NSUUID")))
Nope, same thing
Sorry about that. This new version is tested. (Wasn’t at my computer before so I couldn’t check the crash log to diagnose the problem.)
Function newUUID() As text
//requires OSX 10.8 or higher, bad things will happen on 10.7
#if TargetCocoa
declare function NSClassFromString lib "Cocoa" (clsName as cfstringref) as ptr
declare function UUID lib "Cocoa" selector "UUID" (clsRef as ptr) as ptr
declare function UUIDString lib "Cocoa" selector "UUIDString" (obj_id as ptr) as cfstringref
dim NSUUID as ptr
NSUUID = UUID(NSClassFromString("NSUUID"))
return UUIDString(NSUUID)
#endif
End Function
[quote=150138:@Jason King]Sorry about that. This new version is tested. (Wasn’t at my computer before so I couldn’t check the crash log to diagnose the problem.)
[code]
Function newUUID() As text
//requires OSX 10.8 or higher, bad things will happen on 10.7
#if TargetCocoa
declare function NSClassFromString lib “Cocoa” (clsName as cfstringref) as ptr
declare function UUID lib “Cocoa” selector “UUID” (clsRef as ptr) as ptr
declare function UUIDString lib “Cocoa” selector “UUIDString” (obj_id as ptr) as cfstringref
dim NSUUID as ptr
NSUUID = UUID(NSClassFromString("NSUUID"))
return UUIDString(NSUUID)
#endif
End Function
[/code][/quote]
Thank you Jason. It works fine
The question is, are there solutions für the Linux and Windows Framework to? So i can implement via Targets.
OK i found a solution at the Real Software FORUM. Just Replace the old Carbon-Target Code with the new Cocoa Code.
Here is a function that will work across platforms:
Protected Function GenerateUUID() As String
// From http://www.cryptosys.net/pki/uuid-rfc4122.html
//
// Generate 16 random bytes (=128 bits)
// Adjust certain bits according to RFC 4122 section 4.4 as follows:
// set the four most significant bits of the 7th byte to 0100'B, so the high nibble is '4'
// set the two most significant bits of the 9th byte to 10'B, so the high nibble will be one of '8', '9', 'A', or 'B'.
// Convert the adjusted bytes to 32 hexadecimal digits
// Add four hyphen '-' characters to obtain blocks of 8, 4, 4, 4 and 12 hex digits
// Output the resulting 36-character string "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
dim randomBytes as MemoryBlock = Crypto.GenerateRandomBytes(16)
randomBytes.LittleEndian = false
//
// Adjust seventh byte
//
dim value as byte = randomBytes.Byte(6)
value = value and &b00001111 // Turn off the first four bits
value = value or &b01000000 // Turn on the second bit
randomBytes.Byte(6) = value
//
// Adjust ninth byte
//
value = randomBytes.Byte(8)
value = value and &b00111111 // Turn off the first two bits
value = value or &b10000000 // Turn on the first bit
randomBytes.Byte(8) = value
dim result as string = EncodeHex(randomBytes)
result = result.LeftB(8) + "-" + result.MidB(9, 4) + "-" + result.MidB(13, 4) + "-" + result.MidB(17, 4) + _
"-" + result.RightB(12)
return result
End Function
And a validator:
Protected Function ValidateUUID(s As String) As Boolean
// Validates a RFC-4122 random UUID like the ones generated by
// GenerateUUID
static rxValidator as RegEx
if rxValidator is nil then
rxValidator = new RegEx
rxValidator.SearchPattern = "(?mi-Us)\\A[[:xdigit:]]{8}-[[:xdigit:]]{4}-4[[:xdigit:]]{3}-[89AB][[:xdigit:]]{3}-[[:xdigit:]]{12}\\z"
end if
return rxValidator.Search(s) IsA RegExMatch
End Function
OK, here we go. Native UUID generation on all three platforms, with a native code fallback if the native functions fail or are unavailable. The native calls are about 10 times faster, so this is the best of all worlds:
Protected Function GenerateUUID() As String
// Tries to use declares to let the native system functions handle this.
// Otherwise, falls back to manual creation.
dim result as string
dim useDeclares as boolean = true
try
#if TargetCocoa
soft declare function NSClassFromString lib "Cocoa" ( clsName as cfstringref ) as ptr
soft declare function UUID lib "Cocoa" selector "UUID" ( clsRef as ptr ) as ptr
soft declare function UUIDString lib "Cocoa" selector "UUIDString" ( obj_id as ptr ) as cfstringref
dim classPtr as Ptr = NSClassFromString( "NSUUID" )
if classPtr = nil then
useDeclares = false
else
dim NSUUID as ptr = UUID( classPtr )
result = UUIDString( NSUUID )
end if
#elseif TargetWin32
const kLibName = "rpcrt4"
if not System.IsFunctionAvailable( "UuidCreate", kLibName ) then
useDeclares = false
elseif not System.IsFunctionAvailable( "UuidToStringA", kLibName ) then
useDeclares = false
else
soft declare function UUIDCreate lib kLibName alias "UuidCreate" ( ByRef uuid as WindowsUUID ) as Integer
soft declare function UUIDToString lib kLibName alias "UuidToStringA" ( ByRef inUUID as WindowsUUID, ByRef outString as CString ) As Integer
dim uuid as WindowsUUID
if UUIDCreate( uuid ) <> 0 then
useDeclares = false
else
dim out as CString
if UUIDToString( uuid, out ) <> 0 then
useDeclares = false
else
result = out
result = result.DefineEncoding( Encodings.UTF8 )
result = result.Uppercase
end if
end if
end if
#elseif TargetLinux
const kLibName = "uuid"
if not System.IsFunctionAvailable( "uuid_generate", kLibName ) then
useDeclares = false
elseif not System.IsFunctionAvailable( "uuid_unparse_upper", kLibName ) then
useDeclares = false
else
soft declare sub UUIDGenerate lib kLibName alias "uuid_generate" ( ByRef uuid as LinuxUUID )
soft declare sub UUIDUnparse lib kLibName alias "uuid_unparse_upper" ( ByRef uuid As LinuxUUID, ByRef out As LinuxUUIDString )
dim uuid as LinuxUUID
UUIDGenerate( uuid )
dim out as LinuxUUIDString
UUIDUnparse( uuid, out )
result = out.Data
result = result.DefineEncoding( Encodings.UTF8 )
end if
#endif
catch err as RuntimeException
useDeclares = false
if err IsA EndException or err IsA ThreadEndException then
raise err
end if
end try
if not useDeclares then
//
// Fallback to manual creation
//
// From http://www.cryptosys.net/pki/uuid-rfc4122.html
//
// Generate 16 random bytes (=128 bits)
// Adjust certain bits according to RFC 4122 section 4.4 as follows:
// set the four most significant bits of the 7th byte to 0100'B, so the high nibble is '4'
// set the two most significant bits of the 9th byte to 10'B, so the high nibble will be one of '8', '9', 'A', or 'B'.
// Convert the adjusted bytes to 32 hexadecimal digits
// Add four hyphen '-' characters to obtain blocks of 8, 4, 4, 4 and 12 hex digits
// Output the resulting 36-character string "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
//
#pragma BackgroundTasks False
#pragma BoundsChecking False
#pragma NilObjectChecking False
dim randomBytes as MemoryBlock = Crypto.GenerateRandomBytes( 16 )
randomBytes.LittleEndian = false
dim p as Ptr = randomBytes
//
// Adjust seventh byte
//
dim value as byte = p.Byte( 6 )
value = value and CType( &b00001111, Byte ) // Turn off the first four bits
value = value or CType( &b01000000, Byte ) // Turn on the second bit
p.Byte(6) = value
//
// Adjust ninth byte
//
value = p.Byte( 8 )
value = value and CType( &b00111111, Byte ) // Turn off the first two bits
value = value or CType( &b10000000, Byte ) // Turn on the first bit
p.Byte( 8 ) = value
result = EncodeHex( randomBytes )
result = result.LeftB( 8 ) + "-" + result.MidB( 9, 4 ) + "-" + result.MidB( 13, 4 ) + "-" + result.MidB( 17, 4 ) + _
"-" + result.RightB( 12 )
end if
return result
End Function
The structures are:
WindowsUUID
Data1 As UInt32
Data2 As UInt16
Data3 As UInt16
Data4 As String * 8
LinuxUUID
Bytes1 As String * 4
Bytes2 As String * 2
Bytes3 As String * 2
Bytes4 As String * 2
Bytes5 As String * 6
LinuxUIDString
Data As String * 36
TrailingNull As String * 1
This will all be in my M_String module, btw, and I’ll post the latest version soon.
http://www.mactechnologies.com/downloads/
(And that last structure should be “LinuxUUIDString”.)
FYI, #if TargetCocoa
should be #if TargetMacOS
or it won’t work on the command line.
Great work!
Kem, righteous job, sir. I’m borrowing your native routine for a project, and so far it’s working perfectly. For my particular needs, I modified it so that hyphenation is optional, as when using UUID as binary index in a database, the hyphens just get in the way.
Curious, though, as many system provided UUID implementations (from what I’ve read) incorporate machine and time identifying values, do you think this method will hold up as strongly as those under stress? (Referring to the native option; not counting the declares.)
It’s a little confusing, given that there’s 5 different versions currently for doing this. Your approach would appear to be ‘Version 4’., whereas version 5 goes back to the method used in version 3, but with a stronger hashing algorithm. So I’m scratching my head wondering why Version 4’s random approach would be any less desirable…(?)
As far as I can tell, if the crypto library ever generates a repeat value with 16 bytes of entropy, then I’d say computer science is bunk.
Since the declares should work in most instances, I wouldn’t stress too much about it.
Perhaps the engineers could speak to when RandomBytes has to resort to the pseudo random table. That’s the only time it would be a concern.
By the way, there are five trillion trillion trillion combinations, so I wouldn’t be that concerned anyway.
Some of the docs behind the GenerateRandomBytes method are here, but I couldn’t tell you specifically which mechanism is used behind the scenes. I think it’s the BlockingRng class, but don’t hold me to that.
So yes, this would be a good place for some specifics from the engineers. This was all implemented after I left the company. I only wrote the API proposal.
I wrote a cross-platform UUID class several years ago; the project is available here: https://github.com/declaresub/uuid
If there’s a need for it, I could throw into Xojo and bring it up to date.
I’ve rewritten my UUID code for Xojo 2015r1. You can find the project at https://github.com/declaresub/mojo .
[quote=173171:@Kem Tekinay]This will all be in my M_String module, btw, and I’ll post the latest version soon.
http://www.mactechnologies.com/downloads/
(And that last structure should be “LinuxUUIDString”.)[/quote]
Hi Kem,
I was just searching the forum here for a UUID generator and came across this thread. I always like your solutions and so downloaded your M_String module but don’t see this function there. Did it not get added?
It’s there.
M_String.GenerateUUID