Skip to content

Instantly share code, notes, and snippets.

@Frityet
Last active March 30, 2024 20:23
Show Gist options
  • Save Frityet/c5dfdfefa95abb0f7992c4c7bee0cf83 to your computer and use it in GitHub Desktop.
Save Frityet/c5dfdfefa95abb0f7992c4c7bee0cf83 to your computer and use it in GitHub Desktop.
Objective C Carray
#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
#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