Last active
March 30, 2024 20:23
-
-
Save Frityet/c5dfdfefa95abb0f7992c4c7bee0cf83 to your computer and use it in GitHub Desktop.
Objective C Carray
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <ObjFW/ObjFW.h> | |
OF_ASSUME_NONNULL_BEGIN | |
#define $_concat(a, b) a##b | |
#define $concat(...) $_concat(__VA_ARGS__) | |
#if !defined($carray_name) | |
# define $carray_name(T) $concat(T, Array) | |
#endif | |
#define $declare_carray(T) \ | |
@interface $carray_name(T) : OFObject \ | |
@property(readonly) size_t count; \ | |
@property(readonly) size_t capacity; \ | |
@property(readonly) T *items; \ | |
\ | |
- (instancetype)init; \ | |
- (instancetype)initWithCapacity: (size_t)capacity; \ | |
- (instancetype)initWithArray: (T *)array count: (size_t)count; \ | |
\ | |
- (void)addObject: (T)object [[clang::objc_direct]]; \ | |
- (void)addObjectsFromArray: (T *)array count: (size_t)count [[clang::objc_direct]];\ | |
- (void)insert: (T)object atIndex: (size_t)index [[clang::objc_direct]]; \ | |
- (void)removeObjectAtIndex: (size_t)index [[clang::objc_direct]]; \ | |
- (void)removeAllObjects [[clang::objc_direct]]; \ | |
\ | |
- (T)objectAtIndex: (size_t)index [[clang::objc_direct]]; \ | |
- (T)objectAtIndexedSubscript: (size_t)index [[clang::objc_direct]]; \ | |
@end | |
#define $define_carray(T) \ | |
@implementation $carray_name(T) \ | |
- (instancetype)init \ | |
{ \ | |
self = [super init]; \ | |
\ | |
_count = 0; \ | |
_capacity = 16; \ | |
_items = calloc(_capacity, sizeof(T)); \ | |
\ | |
return self; \ | |
} \ | |
\ | |
- (instancetype)initWithCapacity: (size_t)capacity \ | |
{ \ | |
self = [super init]; \ | |
\ | |
_count = 0; \ | |
_capacity = capacity; \ | |
_items = calloc(_capacity, sizeof(T)); \ | |
\ | |
return self; \ | |
} \ | |
\ | |
- (instancetype)initWithArray: (T *)array count: (size_t)count \ | |
{ \ | |
self = [super init]; \ | |
\ | |
_count = count; \ | |
_capacity = count; \ | |
_items = calloc(_capacity, sizeof(T)); \ | |
\ | |
memcpy(_items, array, sizeof(T) * count); \ | |
\ | |
return self; \ | |
} \ | |
\ | |
- (void)addObject: (T)object \ | |
{ \ | |
if (_count == _capacity) { \ | |
_capacity *= 2; \ | |
_items = realloc(_items, sizeof(T) * _capacity); \ | |
} \ | |
\ | |
_items[_count++] = object; \ | |
} \ | |
\ | |
- (void)addObjectsFromArray: (T *)array count: (size_t)count \ | |
{ \ | |
if (_count + count > _capacity) { \ | |
_capacity = _count + count; \ | |
_items = realloc(_items, sizeof(T) * _capacity); \ | |
} \ | |
\ | |
memcpy(_items + _count, array, sizeof(T) * count); \ | |
_count += count; \ | |
} \ | |
\ | |
- (void)insert: (T)object atIndex: (size_t)index \ | |
{ \ | |
if (index > _count) \ | |
@throw [OFOutOfRangeException exception]; \ | |
\ | |
if (_count == _capacity) { \ | |
_capacity *= 2; \ | |
_items = realloc(_items, sizeof(T) * _capacity); \ | |
} \ | |
\ | |
memmove(_items + index + 1, _items + index, sizeof(T) * (_count - index)); \ | |
_items[index] = object; \ | |
_count++; \ | |
} \ | |
\ | |
- (void)removeObjectAtIndex: (size_t)index \ | |
{ \ | |
if (index >= _count) \ | |
@throw [OFOutOfRangeException exception]; \ | |
\ | |
memmove(_items + index, _items + index + 1, sizeof(T) * (_count - index - 1)); \ | |
_count--; \ | |
} \ | |
\ | |
- (void)removeAllObjects \ | |
{ \ | |
_count = 0; \ | |
} \ | |
\ | |
- (T)objectAtIndex: (size_t)index \ | |
{ \ | |
if (index >= _count) \ | |
@throw [OFOutOfRangeException exception]; \ | |
\ | |
return _items[index]; \ | |
} \ | |
\ | |
- (T)objectAtIndexedSubscript: (size_t)index \ | |
{ \ | |
return [self objectAtIndex: index]; \ | |
} \ | |
\ | |
- (void)dealloc \ | |
{ \ | |
free(_items); \ | |
} \ | |
@end | |
typedef const char *CString; | |
typedef char *MutableCString; | |
typedef const char *WeakCString; | |
typedef char *MutableWeakCString; | |
//specialisation, the CStringArray will copy and store its memory in one `const char *` | |
@interface $carray_name(CString) : OFObject { | |
@private char *_items; | |
} | |
@property(readonly) size_t count; | |
@property(readonly) size_t capacity; | |
@property(readonly) CString items; //elements are seperated by '\0' | |
- (instancetype)init; | |
- (instancetype)initWithCapacity: (size_t)capacity; | |
- (instancetype)initWithArray: (CString *)array count: (size_t)count; | |
- (void)addObject: (CString)object [[clang::objc_direct]]; | |
- (void)addObjectsFromArray: (CString *)array count: (size_t)count [[clang::objc_direct]]; | |
- (void)insert: (CString)object atIndex: (size_t)index [[clang::objc_direct]]; | |
- (void)removeObjectAtIndex: (size_t)index [[clang::objc_direct]]; | |
- (void)removeAllObjects [[clang::objc_direct]]; | |
- (CString)objectAtIndex: (size_t)index [[clang::objc_direct]]; | |
- (CString)objectAtIndexedSubscript: (size_t)index [[clang::objc_direct]]; | |
@end | |
@interface $carray_name(CString)($concat($carray_name(CString), Extensions)) | |
- (CString (*)[])toArray [[clang::objc_direct]]; | |
- (MutableCString (*)[])copyToArray [[clang::objc_direct]]; | |
@end | |
$declare_carray(WeakCString); //this one doesn't copy | |
$declare_carray(MutableWeakCString); | |
OF_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "CArray.h" | |
OF_ASSUME_NONNULL_BEGIN | |
@implementation $carray_name(CString) | |
//split the `_items` into an array of strings (by splitting on '\0') | |
- (CString *)splitIntoArray: (CString *_Nonnull)out [[clang::objc_direct]] | |
{ | |
size_t count = 0; | |
char *ptr = _items; | |
for (size_t i = 0; i < _capacity; i++) { | |
if (_items[i] == '\0') { | |
out[count++] = ptr; | |
ptr = _items + i + 1; | |
} | |
} | |
return out; | |
} | |
- (instancetype)init | |
{ | |
self = [super init]; | |
_count = 0; | |
_capacity = 0; | |
_items = calloc(1, sizeof(char)); | |
return self; | |
} | |
- (instancetype)initWithCapacity: (size_t)capacity | |
{ | |
self = [super init]; | |
_count = 0; | |
_capacity = capacity; | |
_items = calloc(capacity, sizeof(char)); | |
return self; | |
} | |
- (instancetype)initWithArray: (CString *)array count: (size_t)count | |
{ | |
self = [super init]; | |
//yes this is a VLA | |
//shut up | |
size_t slens[count]; | |
size_t total = 0; | |
for (size_t i = 0; i < count; i++) { | |
slens[i] = strlen(array[i]); | |
total += slens[i]; | |
} | |
_count = count; //`_count` is always the number of strings | |
_capacity = total + count; //this is the actual size of the array, we add `count` to account for the '\0' characters | |
_items = calloc(_capacity, sizeof(char)); | |
char *ptr = _items; | |
for (size_t i = 0; i < count; i++) { | |
memcpy(ptr, array[i], slens[i]); | |
ptr += slens[i] + 1; //add 1 to account for the '\0' character, because we used `calloc` its already zeroed | |
} | |
return self; | |
} | |
//additions must require new alloc | |
- (void)addObject: (CString)object | |
{ | |
size_t slen = strlen(object); | |
_items = realloc(_items, _capacity + slen + 1); | |
memcpy(_items + _capacity, object, slen); | |
_capacity += slen; | |
_count++; | |
} | |
- (void)addObjectsFromArray: (CString *)array count: (size_t)count | |
{ | |
size_t total = 0; | |
for (size_t i = 0; i < count; i++) { | |
total += strlen(array[i]); | |
} | |
_items = realloc(_items, _capacity + total + 1); | |
for (size_t i = 0; i < count; i++) { | |
size_t slen = strlen(array[i]); | |
memcpy(_items + _capacity, array[i], slen); | |
_capacity += slen; | |
} | |
_count += count; | |
} | |
- (void)insert: (CString)object atIndex: (size_t)index | |
{ | |
if (index > _count) | |
@throw [OFOutOfRangeException exception]; | |
size_t slen = strlen(object); | |
_items = realloc(_items, _capacity + slen + 1); | |
memmove(_items + index + 1, _items + index, _capacity - index); | |
memcpy(_items + index, object, slen); | |
_capacity += slen; | |
_count++; | |
} | |
- (void)removeObjectAtIndex: (size_t)index | |
{ | |
if (index >= _count) | |
@throw [OFOutOfRangeException exception]; | |
size_t slen = strlen(_items + index); | |
memmove(_items + index, _items + index + 1, _capacity - index); | |
_capacity -= slen; | |
_count--; | |
} | |
- (void)removeAllObjects | |
{ | |
_count = 0; | |
_capacity = 0; | |
free(_items); | |
_items = NULL; | |
} | |
- (CString)objectAtIndex: (size_t)index | |
{ | |
if (index >= _count) | |
@throw [OFOutOfRangeException exception]; | |
return _items + index; | |
} | |
- (CString)objectAtIndexedSubscript:(size_t)index | |
{ | |
return [self objectAtIndex: index]; | |
} | |
- (void)dealloc | |
{ | |
free(_items); | |
} | |
@end | |
@implementation $carray_name(CString)($concat($carray_name(CString), Extensions)) | |
- (CString (*)[])toArray | |
{ | |
auto ptr = calloc(_count, sizeof(CString)); | |
if (!ptr) | |
@throw [OFOutOfMemoryException exception]; | |
return (CString (*)[])[self splitIntoArray: ptr]; | |
} | |
- (MutableCString (*)[])copyToArray | |
{ | |
MutableCString (*arr)[_count] = calloc(_count, sizeof(MutableCString)); | |
if (!arr) | |
@throw [OFOutOfMemoryException exception]; | |
for (size_t i = 0; i < _count; i++) { | |
size_t slen = strlen(_items + i); | |
(*arr)[i] = calloc(slen + 1, sizeof(char)); | |
if (!(*arr)[i]) | |
@throw [OFOutOfMemoryException exception]; | |
memcpy((*arr)[i], _items + i, slen); | |
} | |
return arr; | |
} | |
@end | |
$define_carray(WeakCString); | |
$define_carray(MutableWeakCString); | |
OF_ASSUME_NONNULL_END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment