Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -40,10 +40,17 @@ size_max="SIZE_T_MAX"], [ AC_MSG_RESULT(no) size_max="((size_t)-1)"]) AC_DEFINE_UNQUOTED(SIZE_MAX, $size_max, [Maximum value for size_t])]) +AC_CHECK_HEADER(objc/runtime.h, + [AC_DEFINE(HAVE_OBJC_RUNTIME_H, 1, [Whether we have objc/runtime.h])]) +AC_CHECK_LIB(objc, sel_get_name, + [AC_DEFINE(HAVE_SEL_GET_NAME, 1, [Whether we have sel_get_name])]) +AC_CHECK_LIB(objc, sel_getName, + [AC_DEFINE(HAVE_SEL_GETNAME, 1, [Whether we have sel_getName])]) + AC_CHECK_FUNC(asprintf, [ have_asprintf="yes" AC_DEFINE(HAVE_ASPRINTF, 1, "Whether we have asprintf")], [ have_asprintf="no" AC_SUBST(ASPRINTF, "asprintf.c")]) Index: src/OFArray.h ================================================================== --- src/OFArray.h +++ src/OFArray.h @@ -8,17 +8,18 @@ * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ #import "OFObject.h" +#import "OFComparable.h" /** * The OFArray class provides a class for storing dynamically sized arrays. * If you plan to store large hunks of data, you should consider using * OFBigArray, which allocates the memory in pages and not in bytes. */ -@interface OFArray: OFObject +@interface OFArray: OFObject { char *data; size_t itemsize; size_t items; } @@ -96,10 +97,17 @@ * Removes a specified amount of the last items from the OFArray. * * \param nitems The number of items to remove */ - removeNItems: (size_t)nitems; + +/** + * Clones the OFArray, creating a new one. + * + * \return A new autoreleased copy of the OFArray + */ +- (id)copy; @end @interface OFBigArray: OFArray { size_t size; Index: src/OFArray.m ================================================================== --- src/OFArray.m +++ src/OFArray.m @@ -114,10 +114,59 @@ items -= nitems; return self; } + +- (id)copy +{ + OFArray *new = [OFArray arrayWithItemSize: itemsize]; + + [new addNItems: items + fromCArray: data]; + + return new; +} + +- (BOOL)isEqual: (id)obj +{ + if (![obj isKindOf: [OFArray class]]) + return NO; + if ([obj items] != items || [obj itemsize] != itemsize) + return NO; + if (memcmp([obj data], data, items * itemsize)) + return NO; + + return YES; +} + +- (int)compare: (id)obj +{ + int ret; + + if (![obj isKindOf: [OFArray class]]) + @throw [OFInvalidArgumentException newWithClass: [self class] + andSelector: _cmd]; + if ([obj itemsize] != itemsize) + @throw [OFInvalidArgumentException newWithClass: [self class] + andSelector: _cmd]; + + if ([obj items] == items) + return memcmp(data, [obj data], items * itemsize); + + if (items > [obj items]) { + if ((ret = memcmp(data, [obj data], [obj items] * itemsize))) + return ret; + + return *(char*)[self item: [obj items]]; + } else { + if ((ret = memcmp(data, [obj data], items * itemsize))) + return ret; + + return *(char*)[obj item: [self items]] * -1; + } +} @end @implementation OFBigArray - initWithItemSize: (size_t)is { @@ -186,6 +235,16 @@ items -= nitems; size = nsize; return self; } + +- (id)copy +{ + OFArray *new = [OFArray bigArrayWithItemSize: itemsize]; + + [new addNItems: items + fromCArray: data]; + + return new; +} @end ADDED src/OFComparable.h Index: src/OFComparable.h ================================================================== --- src/OFComparable.h +++ src/OFComparable.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008 - 2009 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE included in + * the packaging of this file. + */ + +#import "OFObject.h" + +/** + * The OFComparable protocol provides functions to compare two objects. + */ +@protocol OFComparable +/** + * \param obj The object which is tested for equality + * \return A boolean whether the object is equal to the other object + */ +- (BOOL)isEqual: (id)obj; + +/** + * Compares the object to another object. + * + * \param obj An object to compare with + * \return An integer which is the result of the comparison, see for example + * strcmp + */ +- (int)compare: (id)obj; +@end Index: src/OFExceptions.h ================================================================== --- src/OFExceptions.h +++ src/OFExceptions.h @@ -117,10 +117,37 @@ * An OFException indicating the given value is out of range. */ @interface OFOutOfRangeException: OFException {} @end +/** + * An OFException indicating that the argument is invalid for this method. + */ +@interface OFInvalidArgumentException: OFException +{ + SEL selector; +} + +/** + * \param class The class of the object which caused the exception + * \param selector The selector which doesn't accept the argument + * \return A new invalid argument exception + */ ++ newWithClass: (Class)class + andSelector: (SEL)selector; + +/** + * Initializes an already allocated invalid argument exception + * + * \param class The class of the object which caused the exception + * \param selector The selector which doesn't accept the argument + * \return An initialized invalid argument exception + */ +- initWithClass: (Class)class + andSelector: (SEL)selector; +@end + /** * An OFException indicating that the encoding is invalid for this object. */ @interface OFInvalidEncodingException: OFException {} @end Index: src/OFExceptions.m ================================================================== --- src/OFExceptions.m +++ src/OFExceptions.m @@ -27,12 +27,24 @@ #define GET_ERR GetLastError() #define GET_SOCK_ERR WSAGetLastError() #define ERRFMT "Error code was: %d" #define ERRPARAM err #endif + +#ifdef HAVE_OBJC_RUNTIME_H +#import +#endif #import "OFExceptions.h" + +#if defined(HAVE_SEL_GET_NAME) +#define SEL_NAME(x) sel_get_name(x) +#elif defined(HAVE_SEL_GETNAME) +#define SEL_NAME(x) sel_getName(x) +#else +#error "You need either sel_get_name() or sel_getName()!" +#endif #ifndef HAVE_ASPRINTF #import "asprintf.h" #endif @@ -150,18 +162,47 @@ asprintf(&string, "Value out of range in class %s!", [class name]); return string; } @end + +@implementation OFInvalidArgumentException ++ newWithClass: (Class)class_ + andSelector: (SEL)selector_ +{ + return [[self alloc] initWithClass: class_ + andSelector: selector_]; +} + +- initWithClass: (Class)class_ + andSelector: (SEL)selector_ +{ + if ((self = [super initWithClass: class_])) + selector = selector_; + + return self; +} + +- (const char*)cString +{ + if (string != NULL) + return string; + + asprintf(&string, "The argument for method %s of class %s is invalid!", + SEL_NAME(selector), [class name]); + + return string; +} +@end @implementation OFInvalidEncodingException - (const char*)cString { if (string != NULL) return string; - asprintf(&string, "The encoding is invalid for classs %s!", + asprintf(&string, "The encoding is invalid for class %s!", [class name]); return string; } @end @@ -170,11 +211,11 @@ - (const char*)cString { if (string != NULL) return string; - asprintf(&string, "The format is invalid for classs %s!", [class name]); + asprintf(&string, "The format is invalid for class %s!", [class name]); return string; } @end Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -11,15 +11,16 @@ #import #import #import "OFObject.h" +#import "OFComparable.h" /** * A class for storing and modifying strings. */ -@interface OFString: OFObject +@interface OFString: OFObject { char *string; size_t length; BOOL is_utf8; } @@ -105,27 +106,19 @@ /** * Clones the OFString, creating a new one. * * \return A new autoreleased copy of the OFString */ -- (OFString*)clone; +- (id)copy; /** * Sets the OFString to the specified OFString. * * \param str An OFString to set the OFString to. */ - setTo: (OFString*)str; -/** - * Compares the OFString to another OFString. - * - * \param str An OFString to compare with - * \return An integer which is the result of the comparison, see wcscmp - */ -- (int)compareTo: (OFString*)str; - /** * Append another OFString to the OFString. * * \param str An OFString to append */ Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -235,11 +235,11 @@ - (size_t)length { return length; } -- (OFString*)clone +- (id)copy { return [OFString stringWithCString: string]; } - setTo: (OFString*)str @@ -268,13 +268,26 @@ memcpy(string, [str cString], length + 1); return self; } -- (int)compareTo: (OFString*)str +- (BOOL)isEqual: (id)obj +{ + if (![obj isKindOf: [OFString class]]) + return NO; + if (strcmp(string, [obj cString])) + return NO; + + return YES; +} + +- (int)compare: (id)obj { - return strcmp(string, [str cString]); + if (![obj isKindOf: [OFString class]]) + @throw [OFInvalidArgumentException newWithClass: [self class]]; + + return strcmp(string, [obj cString]); } - append: (OFString*)str { return [self appendCString: [str cString]]; Index: tests/OFArray/OFArray.m ================================================================== --- tests/OFArray/OFArray.m +++ tests/OFArray/OFArray.m @@ -16,10 +16,11 @@ #import #import #import "OFArray.h" #import "OFExceptions.h" +#import "OFAutoreleasePool.h" #define CATCH_EXCEPTION(code, exception) \ @try { \ code; \ \ @@ -115,14 +116,57 @@ main() { id a; void *p, *q; size_t i; + OFArray *x, *y; + OFAutoreleasePool *pool; puts("== TESTING OFArray =="); TEST(OFArray) puts("== TESTING OFBigArray =="); TEST(OFBigArray) + + pool = [OFAutoreleasePool new]; + x = [OFArray arrayWithItemSize: 1]; + y = [OFArray bigArrayWithItemSize: 1]; + + if (![x isEqual: y]) { + puts("FAIL 1!"); + return 1; + } + + [x add: "x"]; + if ([x isEqual: y]) { + puts("FAIL 2!"); + return 2; + } + [pool releaseObjects]; + + x = [OFArray arrayWithItemSize: 2]; + y = [OFArray bigArrayWithItemSize: 4]; + + if ([x isEqual: y]) { + puts("FAIL 3!"); + return 1; + } + [pool releaseObjects]; + + x = [OFArray arrayWithItemSize: 1]; + [x addNItems: 3 + fromCArray: "abc"]; + y = [x copy]; + if ([x compare: y]) { + puts("FAIL 4!"); + return 1; + } + + [y add: "de"]; + if ([x compare: y] != -100) { + puts("FAIL 5!"); + return 1; + } + [pool release]; return 0; } Index: tests/OFString/OFString.m ================================================================== --- tests/OFString/OFString.m +++ tests/OFString/OFString.m @@ -23,11 +23,11 @@ #define ZD "%zd" #else #define ZD "%u" #endif -#define NUM_TESTS 12 +#define NUM_TESTS 13 #define SUCCESS \ printf("\r\033[1;%dmTests successful: " ZD "/%d\033[0m", \ (i == NUM_TESTS - 1 ? 32 : 33), i + 1, NUM_TESTS); \ fflush(stdout); #define FAIL \ @@ -59,18 +59,19 @@ OFString *s1 = [OFString stringWithCString: "test"]; OFString *s2 = [OFString stringWithCString: ""]; OFString *s3; OFString *s4 = [OFString string]; - s3 = [s1 clone]; + s3 = [s1 copy]; - CHECK(![s1 compareTo: s3]) + CHECK([s1 isEqual: s3]) + CHECK(![s1 isEqual: [OFObject new]]); [s2 appendCString: "123"]; [s4 setTo: s2]; - CHECK(![s2 compareTo: s4]) + CHECK(![s2 compare: s4]) CHECK(!strcmp([[s1 append: s2] cString], "test123")) CHECK(strlen([s1 cString]) == [s1 length] && [s1 length] == 7) CHECK(!strcmp([[s1 reverse] cString], "321tset")) CHECK(!strcmp([[s1 upper] cString], "321TSET")) CHECK(!strcmp([[s1 lower] cString], "321tset"))