Native UUID Generation

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 :confused:

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 :wink:
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! :wink:

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. :wink:

Since the declares should work in most instances, I wouldn’t stress too much about it. :slight_smile:

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. :slight_smile:

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

This is the constructor of a UUID class I use

[code]Sub Constructor()
Self.mBytes = Crypto.GenerateRandomBytes(16)
Self.mBytes.LittleEndian = False

Dim Value As Byte = Self.mBytes.UInt8Value(6)
Value = Value And CType(&b00001111, UInt8)
Value = Value Or CType(&b01000000, UInt8)
Self.mBytes.UInt8Value(6) = Value

Value = Self.mBytes.UInt8Value(8)
Value = Value And CType(&b00111111, UInt8)
Value = Value Or CType(&b10000000, UInt8)
Self.mBytes.UInt8Value(8) = Value
End Sub[/code]

Edit: If I recall correctly, Kem originally wrote this code. Or at least inspired it.

[quote=259167:@Kem Tekinay]It’s there.

M_String.GenerateUUID [/quote]

Hmm. Really? The version I downloaded goes from Filter_MTC to HasHighASCII.

Just downloaded it 10 minutes ago…

[quote=259168:@Thom McGrath]This is the constructor of a UUID class I use

[code]Sub Constructor()
Self.mBytes = Crypto.GenerateRandomBytes(16)
Self.mBytes.LittleEndian = False

Dim Value As Byte = Self.mBytes.UInt8Value(6)
Value = Value And CType(&b00001111, UInt8)
Value = Value Or CType(&b01000000, UInt8)
Self.mBytes.UInt8Value(6) = Value

Value = Self.mBytes.UInt8Value(8)
Value = Value And CType(&b00111111, UInt8)
Value = Value Or CType(&b10000000, UInt8)
Self.mBytes.UInt8Value(8) = Value
End Sub[/code]

Edit: If I recall correctly, Kem originally wrote this code. Or at least inspired it.[/quote]

Thanks, Tom. But my question is will GenerateRandomBytes always return the same code for a particular machine? I’m not enough of an expert in Random number generation. But I would expect that to return different results each time when run… No?