mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
iUUID for unix by leeg
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@37480 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
9944f1823f
commit
9b99122b0f
6 changed files with 506 additions and 0 deletions
56
Headers/Foundation/NSUUID.h
Normal file
56
Headers/Foundation/NSUUID.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* Interface for NSUUID for GNUStep
|
||||
Copyright (C) 2013 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Graham Lee <graham@iamleeg.com>
|
||||
Created: 2013
|
||||
|
||||
This file is part of the GNUstep Base Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02111 USA.
|
||||
*/
|
||||
|
||||
#ifndef __NSUUID_h_GNUSTEP_BASE_INCLUDE
|
||||
#define __NSUUID_h_GNUSTEP_BASE_INCLUDE
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned char uuid_t[16];
|
||||
|
||||
@class NSString;
|
||||
|
||||
@interface NSUUID : NSObject <NSCopying, NSCoding>
|
||||
{
|
||||
@private
|
||||
uuid_t uuid;
|
||||
}
|
||||
|
||||
+ (id)UUID;
|
||||
- (id)initWithUUIDString:(NSString *)string;
|
||||
- (id)initWithUUIDBytes:(uuid_t)bytes;
|
||||
- (NSString *)UUIDString;
|
||||
- (void)getUUIDBytes:(uuid_t)bytes;
|
||||
|
||||
@end
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NSUUID_h_GNUSTEP_BASE_INCLUDE */
|
|
@ -291,6 +291,7 @@ NSURLResponse.m \
|
|||
NSTextCheckingResult.m\
|
||||
NSURLHandle.m \
|
||||
NSUserDefaults.m \
|
||||
NSUUID.m \
|
||||
NSValue.m \
|
||||
NSValueTransformer.m \
|
||||
NSXMLDocument.m \
|
||||
|
@ -451,6 +452,7 @@ NSURLRequest.h \
|
|||
NSURLResponse.h \
|
||||
NSUserDefaults.h \
|
||||
NSUtilities.h \
|
||||
NSUUID.h \
|
||||
NSValue.h \
|
||||
NSValueTransformer.h \
|
||||
NSXMLDocument.h \
|
||||
|
|
280
Source/NSUUID.m
Normal file
280
Source/NSUUID.m
Normal file
|
@ -0,0 +1,280 @@
|
|||
/** NSUUID - Representation of universally unique identifiers.
|
||||
Copyright (C) 2013 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Graham Lee <graham@iamleeg.com>
|
||||
Created: November 2013
|
||||
|
||||
This file is part of the GNUstep Base Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02111 USA.
|
||||
|
||||
*/
|
||||
|
||||
#import "common.h"
|
||||
#import "Foundation/NSCoder.h"
|
||||
#import "Foundation/NSUUID.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
static int uuid_from_string(const char *string, unsigned char *uuid);
|
||||
static void string_from_uuid(const unsigned char *uuid, char *string);
|
||||
static int random_uuid(unsigned char *uuid);
|
||||
|
||||
static const int kUUIDStringLength = 36;
|
||||
static const int kUnformattedUUIDStringLength = 32;
|
||||
static const int kUUIDByteCount = 16;
|
||||
|
||||
|
||||
/**
|
||||
* A representation of universally unique identifiers (UUIDs).
|
||||
*/
|
||||
@implementation NSUUID
|
||||
|
||||
+ (id) UUID
|
||||
{
|
||||
id u;
|
||||
|
||||
u = [[self alloc] init];
|
||||
return AUTORELEASE(u);
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
uuid_t localUUID;
|
||||
int result;
|
||||
|
||||
result = random_uuid(localUUID);
|
||||
if (result != 0)
|
||||
{
|
||||
DESTROY(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithUUIDBytes: localUUID];
|
||||
}
|
||||
|
||||
- (id) initWithUUIDString: (NSString *)string
|
||||
{
|
||||
uuid_t localUUID;
|
||||
const char *cString;
|
||||
int parseResult;
|
||||
|
||||
cString = [string cStringUsingEncoding: NSASCIIStringEncoding];
|
||||
parseResult = uuid_from_string(cString, localUUID);
|
||||
if (parseResult != 0)
|
||||
{
|
||||
DESTROY(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithUUIDBytes: localUUID];
|
||||
}
|
||||
|
||||
- (id) initWithUUIDBytes: (uuid_t)bytes
|
||||
{
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
memcpy(self->uuid, bytes, kUUIDByteCount);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)UUIDString
|
||||
{
|
||||
char uuidChars[kUUIDStringLength + 1];
|
||||
NSString *string;
|
||||
|
||||
string_from_uuid(uuid, uuidChars);
|
||||
string = [[NSString alloc] initWithCString: uuidChars
|
||||
encoding: NSASCIIStringEncoding];
|
||||
return AUTORELEASE(string);
|
||||
}
|
||||
|
||||
- (void) getUUIDBytes: (uuid_t)bytes
|
||||
{
|
||||
memcpy(bytes, uuid, kUUIDByteCount);
|
||||
}
|
||||
|
||||
- (BOOL) isEqual: (NSUUID *)other
|
||||
{
|
||||
if (![other isKindOfClass: [NSUUID class]])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
int comparison = memcmp(self->uuid, other->uuid, kUUIDByteCount);
|
||||
return (comparison == 0) ? YES : NO;
|
||||
}
|
||||
|
||||
- (NSUInteger) hash
|
||||
{
|
||||
// more expensive than casting but that's not alignment-safe
|
||||
NSUInteger uintegerArray[kUUIDByteCount/sizeof(NSUInteger)];
|
||||
NSUInteger hash = 0;
|
||||
|
||||
memcpy(uintegerArray, uuid, kUUIDByteCount);
|
||||
for (int i = 0; i < kUUIDByteCount/sizeof(NSUInteger); i++)
|
||||
{
|
||||
hash ^= uintegerArray[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
- (id) copyWithZone: (NSZone *)zone
|
||||
{
|
||||
return RETAIN(self);
|
||||
}
|
||||
|
||||
static NSString *uuidKey = @"uuid";
|
||||
|
||||
- (void) encodeWithCoder: (NSCoder *)aCoder
|
||||
{
|
||||
if ([aCoder allowsKeyedCoding])
|
||||
{
|
||||
[aCoder encodeBytes: uuid length: kUUIDByteCount forKey: uuidKey];
|
||||
}
|
||||
else
|
||||
{
|
||||
[aCoder encodeBytes: uuid length: kUUIDByteCount];
|
||||
}
|
||||
}
|
||||
|
||||
- (id) initWithCoder: (NSCoder *)aDecoder
|
||||
{
|
||||
if (nil != (self = [super init]))
|
||||
{
|
||||
NSUInteger decodedLength = 0;
|
||||
const uint8_t *decodedUUID;
|
||||
|
||||
if ([aDecoder allowsKeyedCoding])
|
||||
{
|
||||
decodedUUID = [aDecoder decodeBytesForKey: uuidKey
|
||||
returnedLength: &decodedLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
decodedUUID
|
||||
= [aDecoder decodeBytesWithReturnedLength: &decodedLength];
|
||||
}
|
||||
if (decodedLength == kUUIDByteCount)
|
||||
{
|
||||
memcpy(uuid, decodedUUID, kUUIDByteCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
DESTROY(self);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
static int uuid_from_string(const char *string, unsigned char *uuid)
|
||||
{
|
||||
char unformatted[kUnformattedUUIDStringLength];
|
||||
|
||||
if (strlen(string) != kUUIDStringLength)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < kUUIDStringLength; i++)
|
||||
{
|
||||
char c = string[i];
|
||||
|
||||
if ((i == 8) || (i == 13) || (i == 18) || (i == 23))
|
||||
{
|
||||
if (c != '-')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isxdigit(c))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
strncpy(unformatted, string, 8);
|
||||
strncpy(unformatted+8, string+9, 4);
|
||||
strncpy(unformatted+12, string+14, 4);
|
||||
strncpy(unformatted+16, string+19, 4);
|
||||
strncpy(unformatted+20, string+24, 12);
|
||||
|
||||
for (int i = 0; i < kUUIDByteCount; i++)
|
||||
{
|
||||
{
|
||||
char thisDigit[3];
|
||||
thisDigit[0] = unformatted[2*i];
|
||||
thisDigit[1] = unformatted[2*i+1];
|
||||
thisDigit[2] = 0;
|
||||
uuid[i] = strtoul(thisDigit, NULL, kUUIDByteCount);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void string_from_uuid(const unsigned char *uuid, char *string)
|
||||
{
|
||||
char unformatted[kUnformattedUUIDStringLength];
|
||||
|
||||
for (int i = 0; i < kUUIDByteCount; i++)
|
||||
{
|
||||
unsigned char byte = uuid[i];
|
||||
char thisPair[3];
|
||||
snprintf(thisPair, 3, "%02X", byte);
|
||||
strncpy(unformatted + 2*i, thisPair, 2);
|
||||
}
|
||||
strncpy(string, unformatted, 8);
|
||||
string[8] = '-';
|
||||
strncpy(string + 9, unformatted + 8, 4);
|
||||
string[13] = '-';
|
||||
strncpy(string + 14, unformatted + 12, 4);
|
||||
string[18] = '-';
|
||||
strncpy(string + 19, unformatted + 16, 4);
|
||||
string[23] = '-';
|
||||
strncpy(string + 24, unformatted + 20, 12);
|
||||
string[kUUIDStringLength] = '\0';
|
||||
}
|
||||
|
||||
static int random_uuid(unsigned char *uuid)
|
||||
{
|
||||
/*Only supporting Version 4 UUIDs (see RFC4412, section 4.4),
|
||||
*consistent with Apple. Other variants suffer from privacy
|
||||
*problems (and are more work...)
|
||||
*/
|
||||
int devUrandom = open("/dev/urandom", O_RDONLY);
|
||||
if (devUrandom == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ssize_t bytesRead = read(devUrandom, uuid, kUUIDByteCount);
|
||||
close(devUrandom);
|
||||
if (bytesRead != kUUIDByteCount)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// as required by the RFC, bits 48-51 should contain 0b0100 (4)
|
||||
// and bits 64-65 should contain 0b01 (1)
|
||||
unsigned char timeByte = uuid[6];
|
||||
timeByte = (4 << 8) + (timeByte & 0x0f);
|
||||
uuid[7] = timeByte;
|
||||
|
||||
unsigned char sequenceByte = uuid[8];
|
||||
sequenceByte = (1 << 6) + (sequenceByte & 0x3f);
|
||||
uuid[8] = sequenceByte;
|
||||
|
||||
return 0;
|
||||
}
|
112
Tests/base/NSTask/notify.m
Normal file
112
Tests/base/NSTask/notify.m
Normal file
|
@ -0,0 +1,112 @@
|
|||
#import <Foundation/NSTask.h>
|
||||
#import <Foundation/NSFileHandle.h>
|
||||
#import <Foundation/NSFileManager.h>
|
||||
#import <Foundation/NSData.h>
|
||||
#import <Foundation/NSNotification.h>
|
||||
#import <Foundation/NSRunLoop.h>
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
|
||||
#import "ObjectTesting.h"
|
||||
|
||||
@interface TaskHandler : NSObject
|
||||
{
|
||||
NSString *path;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TaskHandler
|
||||
|
||||
static BOOL taskTerminationNotificationReceived;
|
||||
|
||||
- (void) setLaunchPath: (NSString*)s
|
||||
{
|
||||
ASSIGNCOPY(path, s);
|
||||
}
|
||||
|
||||
- (void) taskDidTerminate: (NSNotification *)notification
|
||||
{
|
||||
NSLog(@"Received NSTaskDidTerminateNotification %@", notification);
|
||||
taskTerminationNotificationReceived = YES;
|
||||
}
|
||||
|
||||
- (void) testNSTaskNotifications
|
||||
{
|
||||
NSDate *deadline;
|
||||
BOOL earlyTermination = NO;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
NSTask *task = [NSTask new];
|
||||
|
||||
[task setLaunchPath: path];
|
||||
[task setArguments: [NSArray arrayWithObjects:
|
||||
@"-c", @"echo Child starting; sleep 10; echo Child exiting", nil]];
|
||||
taskTerminationNotificationReceived = NO;
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: self
|
||||
selector: @selector(taskDidTerminate:)
|
||||
name: NSTaskDidTerminateNotification
|
||||
object: task];
|
||||
[task launch];
|
||||
NSLog(@"Launched pid %d", [task processIdentifier]);
|
||||
if (earlyTermination)
|
||||
{
|
||||
NSLog(@"Running run loop for 5 seconds");
|
||||
deadline = [NSDate dateWithTimeIntervalSinceNow:5.0];
|
||||
while ([deadline timeIntervalSinceNow] > 0.0)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
||||
NSLog(@"Run loop finished, will now call -[NSTask terminate]");
|
||||
[task terminate];
|
||||
NSLog(@"Terminate returned, waiting for termination");
|
||||
[task waitUntilExit];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Running run loop for 15 seconds");
|
||||
deadline = [NSDate dateWithTimeIntervalSinceNow: 15.0];
|
||||
while ([deadline timeIntervalSinceNow] > 0.0
|
||||
&& !taskTerminationNotificationReceived)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
||||
}
|
||||
}
|
||||
[task release];
|
||||
NSAssert(taskTerminationNotificationReceived,
|
||||
@"termination notification not received");
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
removeObserver: self name: NSTaskDidTerminateNotification object: nil];
|
||||
if (earlyTermination)
|
||||
break;
|
||||
earlyTermination = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
TaskHandler *h;
|
||||
NSFileManager *mgr;
|
||||
NSString *helpers;
|
||||
NSString *lp;
|
||||
|
||||
START_SET("notify");
|
||||
mgr = [NSFileManager defaultManager];
|
||||
helpers = [mgr currentDirectoryPath];
|
||||
helpers = [helpers stringByAppendingPathComponent: @"Helpers"];
|
||||
helpers = [helpers stringByAppendingPathComponent: @"obj"];
|
||||
|
||||
lp = [helpers stringByAppendingPathComponent: @"testecho"];
|
||||
|
||||
h = [TaskHandler new];
|
||||
[h setLaunchPath: lp];
|
||||
[h testNSTaskNotifications];
|
||||
[h release];
|
||||
|
||||
END_SET("notify");
|
||||
return 0;
|
||||
}
|
0
Tests/base/NSUUID/TestInfo
Normal file
0
Tests/base/NSUUID/TestInfo
Normal file
56
Tests/base/NSUUID/basic.m
Normal file
56
Tests/base/NSUUID/basic.m
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include <Foundation/NSAutoreleasePool.h>
|
||||
#include <Foundation/NSString.h>
|
||||
#include <Foundation/NSUUID.h>
|
||||
#include "Testing.h"
|
||||
#include "ObjectTesting.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
||||
NSUUID *uuid1, *uuid2;
|
||||
NSString *uuidString = @"2139CD2E-15E6-4C37-84DA-1E18EEBFAB4A";
|
||||
|
||||
uuid_t uuidBytes = { 0x80, 0x1f, 0x3a, 0x01, 0x95, 0x7c, 0x45, 0x0f,
|
||||
0xaf, 0xf2, 0x1b, 0xe9, 0x59, 0xf5, 0x89, 0x54 };
|
||||
|
||||
TEST_FOR_CLASS(@"NSUUID", [NSUUID alloc],
|
||||
"+[NSUUID alloc] returns a NSUUID");
|
||||
TEST_FOR_CLASS(@"NSUUID", [NSUUID UUID],
|
||||
"+[NSUUID UUID] returns a UUID");
|
||||
|
||||
uuid1 = [[NSUUID alloc] initWithUUIDString:@"test"];
|
||||
PASS(uuid1 == nil, "Don't create a UUID from an invalid string");
|
||||
|
||||
uuid1 = [[NSUUID alloc] initWithUUIDString:uuidString];
|
||||
PASS(uuid1 != nil, "Create a UUID from a valid string");
|
||||
PASS_EQUAL([uuid1 UUIDString], uuidString,
|
||||
"Derive a stable UUID string value");
|
||||
|
||||
uuid2 = [[NSUUID alloc] initWithUUIDString:uuidString];
|
||||
PASS_EQUAL(uuid1, uuid2, "UUIDs representing the same value are considered equal");
|
||||
PASS([uuid1 hash] == [uuid2 hash], "Equal objects have equal hashes");
|
||||
|
||||
DESTROY(uuid2);
|
||||
uuid2 = [[NSUUID alloc] initWithUUIDBytes:uuidBytes];
|
||||
PASS(![uuid1 isEqual:uuid2], "UUIDs representing different values should not be considered equal");
|
||||
|
||||
uuid_t otherBytes = {0};
|
||||
[uuid2 getUUIDBytes:otherBytes];
|
||||
|
||||
int comparison = memcmp(uuidBytes, otherBytes, 16);
|
||||
PASS(comparison == 0, "Get a stable value for the UUID bytes");
|
||||
DESTROY(uuid2);
|
||||
|
||||
uuid2 = [uuid1 copy];
|
||||
PASS_EQUAL(uuid1, uuid2, "-[NSUUID copy] returns an identical object");
|
||||
DESTROY(uuid2);
|
||||
|
||||
NSData *coded = [NSKeyedArchiver archivedDataWithRootObject:uuid1];
|
||||
uuid2 = [NSKeyedUnarchiver unarchiveObjectWithData:coded];
|
||||
PASS_EQUAL(uuid1, uuid2, "UUID survives a round-trip through archiver");
|
||||
DESTROY(uuid1);
|
||||
|
||||
[arp release];
|
||||
arp = nil;
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue