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:
Richard Frith-MacDonald 2013-12-20 12:26:22 +00:00
parent 9944f1823f
commit 9b99122b0f
6 changed files with 506 additions and 0 deletions

View 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 */

View file

@ -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
View 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
View 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;
}

View file

56
Tests/base/NSUUID/basic.m Normal file
View 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;
}