This repository has been archived by the owner on Jun 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmanaged.h
394 lines (335 loc) · 11.2 KB
/
managed.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/**
* Copyright (C) 2023 Amrit Bhogal
*
* This file is part of ManagedC.
*
* ManagedC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ManagedC 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ManagedC. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MANAGEDC_MAIN)
#define MANAGEDC_MAIN
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) || defined (__linux__) || defined (MC_POSIX)
# define MC_POSIX 1
# include <unistd.h>
# include <pthread.h>
#else
# define MC_POSIX 0
#endif
#if defined(WIN32) || defined(MC_WIN32)
# define MC_WIN32 1
# include <windows.h>
#else
# define MC_WIN32 0
#endif
#if !defined(MC_NO_THREAD_SAFTEY)
# if MC_POSIX
typedef pthread_mutex_t mc_Mutex_t;
# define MC_MUTEX 1
# elif defined(MC_WIN32)
typedef HANDLE mc_mutex_t;
# define MC_MUTEX 1
# else
# pragma warning Mutex support disabled (unrecognized platform)
# define MC_MUTEX 0
# endif
#else
# define MC_MUTEX 0
#endif
#if !defined(MC_UINTPTR)
typedef unsigned long int mc_uintptr_t;
#endif
#if !defined(MC_GUARDPAGE_MAX)
# define MC_GUARDPAGE_MAX ((uintptr_t)0x1000)
#endif
#if !defined(MC_ALLOCATOR)
# define MC_ALLOCATOR(c, s) calloc(c, s)
#endif
#if !defined(MC_MEMCPY)
# define MC_MEMCPY(dst, src, nmemb) memcpy(dst, src, nmemb)
#endif
#if !defined(MC_MEMMOVE)
# define MC_MEMMOVE(dst, src, nmemb) memmove(dst, src, nmemb)
#endif
#if !defined(MC_FREE)
# define MC_FREE(ptr) free(ptr)
#endif
#if !defined(__GNUC__) || defined(__STRICT_ANSI__)
# define MC_ANSI 1
#else
# define MC_ANSI 0
#endif
#if defined(__clang__) && defined(__llvm__) && !MC_ANSI
# define MC_LLVM 1
#else
# define MC_LLVM 0
#endif
#if MC_ANSI
# define mc_nullable
# define mc_nonnull
# define mc_attribute(t)
# define mc_defer
# define mc_typeof(T)
# define MC_EXPAND(t)
# define mc_pragma(p)
# define mc_warning(w)
#else
# define MC_EXPAND(...) __VA_ARGS__
# define MC_concat2(x) _mc_##x##_deferexpr
# define MC_concat1(x) MC_concat2(x)
# define mc_typeof(...) __typeof__(__VA_ARGS__)
# define mc_attribute(...) __attribute__((__VA_ARGS__))
# define mc_pragma(...) _Pragma(#__VA_ARGS__)
# define mc_warning(...) mc_pragma(GCC warning "\"" __VA_ARGS__ "\"")
# if MC_LLVM
# define mc_nullable _Nullable
# define mc_nonnull _Nonnull
# define mc_nocapture __block
# define mc_defer mc_attribute(cleanup(_mc_rundefer)) void (^mc_nonnull MC_concat1(__LINE__))(void) = ^
static void _mc_rundefer(void (^mc_nonnull *mc_nonnull cb)(void)) { (*cb)(); }
# else
# define mc_nullable
# define mc_nonnull
# define mc_defer
# define mc_nocapture
# endif
#endif
#if MC_ANSI
# define mc_auto "Running in ANSI standard mode (no extensions). This macro does not automatically release the pointer!"
#else
static void managed_release(void *mc_nonnull ptr);
static void managed_release_ptr(void *mc_nonnull addr)
{
void **ptr = addr;
if (ptr != NULL && *ptr != NULL)
{
managed_release(*ptr);
*ptr = NULL;
}
}
# define mc_auto mc_attribute(cleanup(managed_release_ptr))
#endif
#if MC_MUTEX
static int mc_mutex_create(mc_Mutex_t *mc_nonnull mtx)
{
#if defined(MC_POSIX)
return pthread_mutex_init(mtx, NULL) == 0 ? 0 : 1;
# elif defined(MC_WIN32)
*mtx = CreateMutex(NULL, 0, NULL);
return 0;
#endif
}
static int mc_mutex_destroy(mc_Mutex_t *mc_nonnull mtx)
{
#if defined(MC_POSIX)
return pthread_mutex_destroy(mtx);
# elif defined(MC_WIN32)
CloseHandle(*mtx);
return 0;
#endif
}
static int mc_mutex_lock(mc_Mutex_t *mc_nonnull mtx)
{
#if defined(MC_POSIX)
return pthread_mutex_lock(mtx) == 0 ? 0 : 1;
# elif defined(MC_WIN32)
return WaitForSingleObject(*mtx, INFINITE) == WAIT_OBJECT_0 ? 0 : 1;
#endif
}
static int mc_mutex_unlock(mc_Mutex_t *mc_nonnull mtx)
{
#if defined(MC_POSIX)
return pthread_mutex_unlock(mtx) == 0 ? 0 : 1;
# elif defined(MC_WIN32)
return ReleaseMutex(*mtx) == 0 ? 1 : 0; /*Fuck MS*/
#endif
}
# define MC_MUTEX_CREATE(mtx) mc_mutex_create(mtx)
# define MC_MUTEX_DESTROY(mtx) mc_mutex_destroy(mtx)
# define MC_MUTEX_LOCK(mtx) mc_mutex_lock(mtx)
# define MC_MUTEX_UNLOCK(mtx) mc_mutex_unlock(mtx)
#else
# define MC_MUTEX_CREATE(mtx)
# define MC_MUTEX_DESTROY(mtx)
# define MC_MUTEX_LOCK(mtx)
# define MC_MUTEX_UNLOCK(mtx)
#endif
#define _mcinternal_ptrinfo(ptr) ((struct managed_PointerInfo *)managed_info_of(ptr))
typedef void managed_Free_f(void *mc_nonnull alloc);
struct managed_PointerInfo {
/**
* count: Number of used array elements.
* capacity: Number of allocated array elements. (Used for managed_Vector)
* typesize: Size of the type.
* reference_count: Number of references to this pointer.
*/
size_t count, capacity, typesize, reference_count;
/**
* Function to call on 0 references.
*/
managed_Free_f *mc_nullable free;
/**
* Pointer to the data, should be just in front of data.
*/
void *mc_nonnull data;
#if MC_MUTEX
/**
* Lock on the metadata, maps to an OS specific object.
*/
mc_Mutex_t lock;
#endif
};
/**
* Gets the metadata about the pointer
*/
static const struct managed_PointerInfo *mc_nullable managed_info_of(const void *mc_nonnull ptr)
{
struct managed_PointerInfo *info = NULL;
/*Check if the pointer is lesser than the max page, which *probably* means that it is not on the heap*/
if ((uintptr_t)ptr < MC_GUARDPAGE_MAX) return NULL;
info = (struct managed_PointerInfo *)ptr - 1;
if (info->data != ptr)
return NULL;
return info;
}
static long int mc_countof(const void *mc_nonnull ptr)
{
const struct managed_PointerInfo *info = managed_info_of(ptr);
size_t count = 0;
if (info == NULL) return -1;
if (MC_MUTEX_LOCK((mc_Mutex_t *)&info->lock) != 0) return -2;
count = info->count;
if (MC_MUTEX_UNLOCK((mc_Mutex_t *)&info->lock) != 0) return -3;
return (long int)count;
}
static long int mc_sizeof_type(const void *mc_nonnull ptr)
{
const struct managed_PointerInfo *info = managed_info_of(ptr);
size_t size = 0;
if (info == NULL) return -1;
if (MC_MUTEX_LOCK((mc_Mutex_t *)&info->lock) != 0) return -2;
size = info->typesize;
if (MC_MUTEX_UNLOCK((mc_Mutex_t *)&info->lock) != 0) return -3;
return (long int)size;
}
static long int mc_sizeof(const void *mc_nonnull ptr)
{
long int c = mc_countof(ptr), tsiz = mc_sizeof_type(ptr);
if (c < 0 || tsiz < 0) return -1; /*TODO: do the math to make this work properly*/
return c * tsiz;
}
#define mc_alloc(T, free) (T *)managed_allocate(1, sizeof(T), (managed_Free_f *)(free), NULL)
#define mc_array(T, count, free) (T *)managed_allocate(count, sizeof(T), (managed_Free_f *)(free), NULL)
/**
* Creates a new allocation with the managed pointer metadata. Define MC_ALLOCATOR(c, nmemb) to change the allocation function
*/
static void *mc_nullable managed_allocate(size_t count, size_t typesize, managed_Free_f *mc_nullable free, const void *mc_nullable data)
{
struct managed_PointerInfo *info = MC_ALLOCATOR(1, sizeof(struct managed_PointerInfo) + count * typesize);
if (info == NULL) return NULL;
info->capacity = info->count= count;
info->typesize = typesize;
info->free = free;
info->reference_count = 1;
/* The data must be right after the metadata */
info->data = info + 1;
if (MC_MUTEX_CREATE(&info->lock) != 0) {
MC_FREE(info);
return NULL;
}
if (data != NULL)
MC_MEMCPY(info->data, data, count * typesize);
return info->data;
}
/**
* Creates a copy of the managed pointer. This copies every byte of data in the allocation
*/
static void *mc_nullable managed_copy(const void *mc_nonnull ptr, long int count)
{
struct managed_PointerInfo *info = (void *)managed_info_of(ptr);
void *alloc = NULL;
if (info == NULL) return NULL;
if (MC_MUTEX_LOCK(&info->lock) != 0) return NULL;
if (count < 1) count = (long int)info->count;
alloc = managed_allocate((size_t)count, info->typesize, info->free,NULL);
if (alloc == NULL) return NULL;
/* Just in case count is larger than mc_countof(ptr) (sizing up an allocation), make sure you only copy the existing data */
MC_MEMCPY(alloc, ptr, (size_t)(info->typesize * info->count));
MC_MUTEX_UNLOCK(&info->lock); /*TODO: what should I even do if this fails?*/
return alloc;
}
static void *mc_nullable mc_dup(const void *mc_nonnull ptr)
{ return managed_copy(ptr, mc_countof(ptr)); }
#define mc_ref(ptr) MC_EXPAND((mc_typeof(ptr)))managed_reference(ptr)
/**
* Gets a reference to the ptr, and incrememnts it's reference count
*/
static void *mc_nullable managed_reference(void *mc_nonnull ptr)
{
struct managed_PointerInfo *info = (void *)managed_info_of(ptr);
if (info == NULL) return NULL;
if (MC_MUTEX_LOCK(&info->lock) != 0) return NULL;
info->reference_count++;
if (MC_MUTEX_UNLOCK(&info->lock) != 0) return NULL;
return ptr;
}
/*
* Releases a reference to the pointer, and if 0 references, frees the pointer
*/
static void managed_release(void *mc_nonnull ptr)
{
struct managed_PointerInfo *info = NULL;
size_t i = 0;
info = (struct managed_PointerInfo *)managed_info_of(ptr);
if (info == NULL) return;
if (MC_MUTEX_LOCK(&info->lock) != 0) return;
info->reference_count--;
if (info->reference_count < 1) {
if (info->free != NULL)
for (i = 0; i < info->count; i++) /* Free each item of the allocation individually */ {
info->free(((unsigned char *)ptr) + i * info->typesize);
}
/* Unlock before freeing! */
if (!MC_MUTEX_UNLOCK(&info->lock)) return;/*TODO: Maybe not the best?*/
MC_MUTEX_DESTROY(&info->lock);
MC_FREE(info);
}
MC_MUTEX_UNLOCK(&info->lock);
}
/**
* The compiler complains if void *const * is passed to void *, so use this hack
*/
static void mc_free(const void *mc_nonnull ptr)
{ managed_release((void *)ptr); }
/**
* Creates a new, non-managed, allocation and copies all the data to it
*/
static void *mc_nullable managed_to_unmanaged(const void *mc_nonnull ptr)
{
struct managed_PointerInfo *info = _mcinternal_ptrinfo(ptr);
void *unmanaged = NULL;
if (info == NULL) return NULL;
if (MC_MUTEX_LOCK(&info->lock) != 0) return NULL;
unmanaged = MC_ALLOCATOR(info->count + 1, info->typesize); /* +1 just in case it's a string */
if (unmanaged == NULL) return NULL;
MC_MEMCPY(unmanaged, ptr, info->count * info->typesize);
/* welp, too bad I guess if it fails! */
MC_MUTEX_UNLOCK(&info->lock);
return unmanaged;
}
#undef _mcinternal_ptrinfo
#undef MC_SOURCE_PRIVATE
#endif