Index: src/OFMethodSignature.h ================================================================== --- src/OFMethodSignature.h +++ src/OFMethodSignature.h @@ -84,7 +84,29 @@ * @returns The offset on the stack frame of the argument at the * specified index */ - (size_t)argumentOffsetAtIndex: (size_t)index; @end + +#ifdef __cplusplus +extern "C" { +#endif +/*! + * @brief Returns the size for the specified type encoding. + * + * @param type The type encoding to return the size for + * @return The size for the specified type encoding + */ +extern size_t of_sizeof_type_encoding(const char *type); + +/*! + * @brief Returns the alignment for the specified type encoding. + * + * @param type The type encoding to return the alignment for + * @return The alignment for the specified type encoding + */ +extern size_t of_alignof_type_encoding(const char *type); +#ifdef __cplusplus +} +#endif OF_ASSUME_NONNULL_END Index: src/OFMethodSignature.m ================================================================== --- src/OFMethodSignature.m +++ src/OFMethodSignature.m @@ -14,17 +14,504 @@ * file. */ #include "config.h" +#include #include #import "OFMethodSignature.h" #import "OFData.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" +#import "OFOutOfRangeException.h" + +size_t sizeofEncoding(const char **type, size_t *length); +size_t alignofEncoding(const char **type, size_t *length); + +size_t +sizeofArray(const char **type, size_t *length) +{ + size_t count = 0; + size_t size; + + assert(*length > 0); + + (*type)++; + (*length)--; + + while (*length > 0 && isdigit(**type)) { + count = count * 10 + **type - '0'; + + (*type)++; + (*length)--; + } + + size = sizeofEncoding(type, length); + + if (*length == 0 || **type != ']') + @throw [OFInvalidFormatException exception]; + + (*type)++; + (*length)--; + + if (SIZE_MAX / count < size) + @throw [OFOutOfRangeException exception]; + + return count * size; +} + +size_t +sizeofStruct(const char **type, size_t *length) +{ + size_t size = 0; + + assert(*length > 0); + + (*type)++; + (*length)--; + + /* Skip name */ + while (*length > 0 && **type != '=') { + (*type)++; + (*length)--; + } + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + /* Skip '=' */ + (*type)++; + (*length)--; + + while (*length > 0 && **type != '}') { + const char *typeCopy = *type; + size_t lengthCopy = *length; + size_t fieldSize = sizeofEncoding(type, length); + size_t fieldAlign = alignofEncoding(&typeCopy, &lengthCopy); + + if (size % fieldAlign != 0) { + size_t padding = fieldAlign - (size % fieldAlign); + + if (SIZE_MAX - size < padding) + @throw [OFOutOfRangeException exception]; + + size += padding; + } + + if (SIZE_MAX - size < fieldSize) + @throw [OFOutOfRangeException exception]; + + size += fieldSize; + } + + if (*length == 0 || **type != '}') + @throw [OFInvalidFormatException exception]; + + (*type)++; + (*length)--; + + return size; +} + +size_t +sizeofUnion(const char **type, size_t *length) +{ + size_t size = 0; + + assert(*length > 0); + + (*type)++; + (*length)--; + + /* Skip name */ + while (*length > 0 && **type != '=') { + (*type)++; + (*length)--; + } + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + /* Skip '=' */ + (*type)++; + (*length)--; + + while (*length > 0 && **type != ')') { + size_t fieldSize = sizeofEncoding(type, length); + + if (fieldSize > size) + size = fieldSize; + } + + if (*length == 0 || **type != ')') + @throw [OFInvalidFormatException exception]; + + (*type)++; + (*length)--; + + return size; +} + +size_t +sizeofEncoding(const char **type, size_t *length) +{ + size_t size; + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + if (**type == 'r') { + (*type)++; + (*length)--; + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + } + + switch (**type) { + case 'c': + case 'C': + size = sizeof(char); + break; + case 'i': + case 'I': + size = sizeof(int); + break; + case 's': + case 'S': + size = sizeof(short); + break; + case 'l': + case 'L': + size = sizeof(long); + break; + case 'q': + case 'Q': + size = sizeof(long long); + break; +#ifdef __SIZEOF_INT128__ + case 't': + case 'T': + size = sizeof(__int128); + break; +#endif + case 'f': + size = sizeof(float); + break; + case 'd': + size = sizeof(double); + break; + case 'D': + size = sizeof(long double); + break; + case 'B': + size = sizeof(_Bool); + break; + case 'v': + size = 0; + break; + case '*': + size = sizeof(char *); + break; + case '@': + size = sizeof(id); + break; + case '#': + size = sizeof(Class); + break; + case ':': + size = sizeof(SEL); + break; + case '[': + return sizeofArray(type, length); + case '{': + return sizeofStruct(type, length); + case '(': + return sizeofUnion(type, length); + case '^': + /* Just to skip over the rest */ + (*type)++; + (*length)--; + sizeofEncoding(type, length); + + return sizeof(void *); +#ifndef __STDC_NO_COMPLEX__ + case 'j': + (*type)++; + (*length)--; + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + switch (**type) { + case 'f': + size = sizeof(float _Complex); + break; + case 'd': + size = sizeof(double _Complex); + break; + case 'D': + size = sizeof(long double _Complex); + break; + default: + @throw [OFInvalidFormatException exception]; + } +#endif + default: + @throw [OFInvalidFormatException exception]; + } + + (*type)++; + (*length)--; + + return size; +} + +size_t +alignofArray(const char **type, size_t *length) +{ + size_t align; + + assert(*length > 0); + + (*type)++; + (*length)--; + + while (*length > 0 && isdigit(**type)) { + (*type)++; + (*length)--; + } + + align = alignofEncoding(type, length); + + if (*length == 0 || **type != ']') + @throw [OFInvalidFormatException exception]; + + (*type)++; + (*length)--; + + return align; +} + +size_t +alignofStruct(const char **type, size_t *length) +{ + size_t align = 0; + + assert(*length > 0); + + (*type)++; + (*length)--; + + /* Skip name */ + while (*length > 0 && **type != '=') { + (*type)++; + (*length)--; + } + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + /* Skip '=' */ + (*type)++; + (*length)--; + + while (*length > 0 && **type != '}') { + size_t fieldAlign = alignofEncoding(type, length); + + if (fieldAlign > align) + align = fieldAlign; + } + + if (*length == 0 || **type != '}') + @throw [OFInvalidFormatException exception]; + + (*type)++; + (*length)--; + + return align; +} + +size_t +alignofUnion(const char **type, size_t *length) +{ + size_t align = 0; + + assert(*length > 0); + + (*type)++; + (*length)--; + + /* Skip name */ + while (*length > 0 && **type != '=') { + (*type)++; + (*length)--; + } + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + /* Skip '=' */ + (*type)++; + (*length)--; + + while (*length > 0 && **type != ')') { + size_t fieldAlign = alignofEncoding(type, length); + + if (fieldAlign > align) + align = fieldAlign; + } + + if (*length == 0 || **type != ')') + @throw [OFInvalidFormatException exception]; + + (*type)++; + (*length)--; + + return align; +} + +size_t +alignofEncoding(const char **type, size_t *length) +{ + size_t align; + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + if (**type == 'r') { + (*type)++; + (*length)--; + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + } + + switch (**type) { + case 'c': + case 'C': + align = OF_ALIGNOF(char); + break; + case 'i': + case 'I': + align = OF_ALIGNOF(int); + break; + case 's': + case 'S': + align = OF_ALIGNOF(short); + break; + case 'l': + case 'L': + align = OF_ALIGNOF(long); + break; + case 'q': + case 'Q': + align = OF_ALIGNOF(long long); + break; +#ifdef __SIZEOF_INT128__ + case 't': + case 'T': + align = OF_ALIGNOF(__int128); + break; +#endif + case 'f': + align = OF_ALIGNOF(float); + break; + case 'd': + align = OF_ALIGNOF(double); + break; + case 'D': + align = OF_ALIGNOF(long double); + break; + case 'B': + align = OF_ALIGNOF(_Bool); + break; + case 'v': + align = 0; + break; + case '*': + align = OF_ALIGNOF(char *); + break; + case '@': + align = OF_ALIGNOF(id); + break; + case '#': + align = OF_ALIGNOF(Class); + break; + case ':': + align = OF_ALIGNOF(SEL); + break; + case '[': + return alignofArray(type, length); + case '{': + return alignofStruct(type, length); + case '(': + return alignofUnion(type, length); + case '^': + /* Just to skip over the rest */ + (*type)++; + (*length)--; + alignofEncoding(type, length); + + return OF_ALIGNOF(void *); +#ifndef __STDC_NO_COMPLEX__ + case 'j': + (*type)++; + (*length)--; + + if (*length == 0) + @throw [OFInvalidFormatException exception]; + + switch (**type) { + case 'f': + align = OF_ALIGNOF(float _Complex); + break; + case 'd': + align = OF_ALIGNOF(double _Complex); + break; + case 'D': + align = OF_ALIGNOF(long double _Complex); + break; + default: + @throw [OFInvalidFormatException exception]; + } +#endif + default: + @throw [OFInvalidFormatException exception]; + } + + (*type)++; + (*length)--; + + return align; +} + +size_t +of_sizeof_type_encoding(const char *type) +{ + size_t length = strlen(type); + size_t ret = sizeofEncoding(&type, &length); + + if (length > 0) + @throw [OFInvalidFormatException exception]; + + return ret; +} + +size_t +of_alignof_type_encoding(const char *type) +{ + size_t length = strlen(type); + size_t ret = alignofEncoding(&type, &length); + + if (length > 0) + @throw [OFInvalidFormatException exception]; + + return ret; +} @implementation OFMethodSignature + (instancetype)signatureWithObjCTypes: (const char*)types { return [[[self alloc] initWithObjCTypes: types] autorelease]; Index: src/macros.h ================================================================== --- src/macros.h +++ src/macros.h @@ -89,13 +89,15 @@ #else # define OF_BYTE_ORDER_NATIVE OF_BYTE_ORDER_LITTLE_ENDIAN #endif #if __STDC_VERSION__ >= 201112L +# define OF_ALIGNOF(type) _Alignof(type) # define OF_ALIGNAS(type) _Alignas(type) #else -# define OF_ALIGNAS(type) __attribute__((__aligned__(sizeof(type)))) +# define OF_ALIGNOF(type) __alignof__(type) +# define OF_ALIGNAS(type) __attribute__((__aligned__(__alignof__(type)))) #endif #if __STDC_VERSION__ >= 201112L && defined(OF_HAVE_MAX_ALIGN_T) # define OF_BIGGEST_ALIGNMENT _Alignof(max_align_t) #else Index: tests/OFMethodSignatureTests.m ================================================================== --- tests/OFMethodSignatureTests.m +++ tests/OFMethodSignatureTests.m @@ -24,10 +24,46 @@ #import "OFInvalidFormatException.h" #import "TestsAppDelegate.h" static OFString *module = @"OFMethodSignature"; + +struct test1_struct { + char c; + int i; +}; + +struct test2_struct { + char c; + struct { + short s; + int i; + } st; + union { + char c; + int i; + } u; + long double d; +}; + +union test3_union { + char c; + int i; + long double d; +}; + +union test4_union { + char c; + struct { + short x, y; + } st; + int i; + union { + float f; + long double d; + } u; +}; @implementation TestsAppDelegate (OFMethodSignatureTests) - (void)methodSignatureTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; @@ -75,8 +111,48 @@ EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #6", OFInvalidFormatException, [OFMethodSignature signatureWithObjCTypes: "{{}0"]) + TEST(@"of_sizeof_type_encoding() #1", + of_sizeof_type_encoding(@encode(struct test1_struct)) == + sizeof(struct test1_struct)) + + TEST(@"of_sizeof_type_encoding() #2", + of_sizeof_type_encoding(@encode(struct test2_struct)) == + sizeof(struct test2_struct)) + + TEST(@"of_sizeof_type_encoding() #3", + of_sizeof_type_encoding(@encode(union test3_union)) == + sizeof(union test3_union)) + + TEST(@"of_sizeof_type_encoding() #4", + of_sizeof_type_encoding(@encode(union test4_union)) == + sizeof(union test4_union)) + + TEST(@"of_sizeof_type_encoding() #5", + of_sizeof_type_encoding(@encode(struct test1_struct [5])) == + sizeof(struct test1_struct [5])) + + TEST(@"of_alignof() #1", + of_alignof_type_encoding(@encode(struct test1_struct)) == + OF_ALIGNOF(struct test1_struct)) + + TEST(@"of_alignof_type_encoding() #2", + of_alignof_type_encoding(@encode(struct test2_struct)) == + OF_ALIGNOF(struct test2_struct)) + + TEST(@"of_alignof_type_encoding() #3", + of_alignof_type_encoding(@encode(union test3_union)) == + OF_ALIGNOF(union test3_union)) + + TEST(@"of_alignof_type_encoding() #4", + of_alignof_type_encoding(@encode(union test4_union)) == + OF_ALIGNOF(union test4_union)) + + TEST(@"of_alignof_type_encoding() #5", + of_alignof_type_encoding(@encode(struct test1_struct [5])) == + OF_ALIGNOF(struct test1_struct [5])) + [pool drain]; } @end