-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcarray_capi.h
352 lines (311 loc) · 12.5 KB
/
carray_capi.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
#ifndef CARRAY_CAPI_H
#define CARRAY_CAPI_H
#ifndef CARRAY_CAPI_HAVE_LONG_LONG
# include <limits.h>
# if defined(LLONG_MAX)
# define CARRAY_CAPI_HAVE_LONG_LONG 1
# else
# define CARRAY_CAPI_HAVE_LONG_LONG 0
# endif
#endif
#define CARRAY_CAPI_ID_STRING "_capi_carray"
#define CARRAY_CAPI_VERSION_MAJOR 1
#define CARRAY_CAPI_VERSION_MINOR 0
#define CARRAY_CAPI_VERSION_PATCH 0
#ifndef CARRAY_CAPI_IMPLEMENT_SET_CAPI
# define CARRAY_CAPI_IMPLEMENT_SET_CAPI 0
#endif
#ifndef CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI
# define CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI 0
#endif
#ifndef CARRAY_CAPI_IMPLEMENT_GET_CAPI
# define CARRAY_CAPI_IMPLEMENT_GET_CAPI 0
#endif
#ifdef __cplusplus
extern "C" {
struct carray_capi;
struct carray_info;
struct carray;
#else /* __cplusplus */
typedef struct carray_capi carray_capi;
typedef struct carray_info carray_info;
typedef struct carray carray;
typedef enum carray_type carray_type;
typedef enum carray_attr carray_attr;
#endif /* ! __cplusplus */
enum carray_type
{
CARRAY_UCHAR = 1,
CARRAY_SCHAR = 2,
CARRAY_SHORT = 3,
CARRAY_USHORT = 4,
CARRAY_INT = 5,
CARRAY_UINT = 6,
CARRAY_LONG = 7,
CARRAY_ULONG = 8,
CARRAY_FLOAT = 9,
CARRAY_DOUBLE = 10,
#if CARRAY_CAPI_HAVE_LONG_LONG
CARRAY_LLONG = 11,
CARRAY_ULLONG = 12,
#endif
};
enum carray_attr
{
CARRAY_DEFAULT = 0,
CARRAY_READONLY = 1
};
struct carray_info
{
carray_type elementType;
carray_attr attr;
size_t elementSize;
size_t elementCount;
size_t elementCapacity;
};
/**
* Carray C API.
*/
struct carray_capi
{
int version_major;
int version_minor;
int version_patch;
/**
* May point to another (incompatible) version of this API implementation.
* NULL if no such implementation exists.
*
* The usage of next_capi makes it possible to implement two or more
* incompatible versions of the C API.
*
* An API is compatible to another API if both have the same major
* version number and if the minor version number of the first API is
* greater or equal than the second one's.
*/
void* next_capi;
/**
* Creates new carray object which manages the underlying data.
*
* elementCount - number of elements
* data - if not NULL, contains after the call the pointer
* to the uninitialized array elements. the caller
* is responsible for initializing the elements.
* If NULL, the elements are initialized with zeros.
*
* Returns NULL pointer on parameter error.
* This function may also raise a Lua error.
*/
carray* (*newCarray)(lua_State* L, carray_type t, carray_attr attr, size_t elementCount, void** data);
/**
* Creates new carray object with a reference to underlying data
* managed by the caller.
*
* elementCount - number of elements
* dataRef - pointer to the element content. The caller guerantees that the content
* memory remains valid until the release callback is called.
* releaseCallback - is called by the carray object if the reference to the underlying
* data is not any longer needed. This callback may also be NULL
* for the case of static data.
*
* Returns NULL pointer on parameter error.
* This function may also raise a Lua error.
*/
carray* (*newCarrayRef)(lua_State* L, carray_type t, carray_attr attr, void* dataRef, size_t elementCount,
void (*releaseCallback)(void* dataRef, size_t elementCount));
/**
* Returns a valid pointer if the Lua object at the given stack
* index is a valid readable carray, otherwise returns NULL.
*
* info - contains information about the carray after the call
* May be NULL.
*
* The returned carray object is be valid as long as the Lua
* object at the given stack index remains valid.
* To keep the carray object beyond this call, the function
* retainConstCarray() should be called (see below).
*/
const carray* (*toReadableCarray)(lua_State* L, int index, carray_info* info);
/**
* Returns a valid pointer if the Lua object at the given stack
* index is a valid writable carray, otherwise returns NULL.
*
* info - contains information about the carray after the call
* May be NULL.
*
* The returned carray object is valid as long as the Lua
* object at the given stack index remains valid.
* To keep the carray object beyond this call, the function
* retainCarray() should be called (see below).
*/
carray* (*toWritableCarray)(lua_State* L, int index, carray_info* info);
/**
* Increase the reference counter of the carray object.
*
* This function must be called after the function toCarray()
* as long as the Lua object on the given stack index is
* valid (see above).
*/
void (*retainCarray)(const carray* a);
/**
* Decrease the reference counter of the carray object and
* destructs the carray object if no reference is left.
*/
void (*releaseCarray)(const carray* a);
/**
* Get pointer to elements.
* offset - index of the first element, 0 <= offset < elementCount
* count - number of elements, 0 <= offset + count <= elementCount
* Returns the pointer to the element in the array at the given offset.
* The caller may only read or write at most count elements at this pointer,
* otherwise behaviour may be undefined.
*/
void* (*getWritableElementPtr)(carray* a, size_t offset, size_t count);
/**
* Get pointer to elements.
* offset - index of the first element, 0 <= offset < elementCount
* count - number of elements, 0 <= offset + count <= elementCount
* Returns the pointer to the element in the array at the given offset.
* The caller may only read at most count elements at this pointer,
* otherwise behaviour may be undefined.
*/
const void* (*getReadableElementPtr)(const carray* a, size_t offset, size_t count);
/**
* Resizes the array.
*
* newElementCount - new number of elements. If this is larger than the current
* element count, the new elements are uninitialized.
*
* reservePercent - if < 0 the capacity is set to the new size,
* if == 0 the capacity remains if the new size is smaller
* than current capacity.
* if > 0 the capacity gets a reserve of reservePercent percent
* if capacity needs to be increased. E.g. if reservePercent == 100
* the capacity is doubled if newElementCount exceeds the current
* capacity.
*
* Returns pointer to the first element in the array. The caller may read
* or write newElementCount elements at this pointer.
* Returns NULL on failure or if newElementCount == 0.
*/
void* (*resizeCarray)(carray* a, size_t newElementCount, int reservePercent);
/**
* Insert Elements.
*
* offset - index of the first new element, 0 <= offset <= elementCount
* insertCount - number of elements to be inserted
*
* reservePercent - if < 0 the capacity is set to the new size,
* if == 0 the capacity remains if the new size is smaller
* than current capacity.
* if > 0 the capacity gets a reserve of reservePercent percent
* if capacity needs to be increased. E.g. if reservePercent == 100
* the capacity is doubled if new element count exceeds the current
* capacity.
*
* Returns pointer to the first new element in the array. The caller may write
* insertCount elements at this pointer.
* Returns NULL on failure or if insertCount == 0.
*/
void* (*insertElements)(carray* a, size_t offset, size_t insertCount, int reservePercent);
/**
* Insert Elements.
*
* offset - index of the first element to be removed, 0 <= offset < elementCount
* removeCount - number of elements to be removed
*
* reservePercent - if < 0 the capacity is set to the new size,
* if == 0 the capacity remains if the new size is smaller
* than current capacity.
* if > 0 the capacity gets a reserve of reservePercent percent
* if capacity needs to be increased. E.g. if reservePercent == 100
* the capacity is doubled if new element count exceeds the current
* capacity.
*/
void (*removeElements)(carray* a, size_t offset, size_t count, int reservePercent);
};
#if CARRAY_CAPI_IMPLEMENT_SET_CAPI
/**
* Sets the Carray C API into the metatable at the given index.
*
* index: index of the table that is be used as metatable for objects
* that are associated to the given capi.
*/
static int carray_set_capi(lua_State* L, int index, const carray_capi* capi)
{
lua_pushlstring(L, CARRAY_CAPI_ID_STRING, strlen(CARRAY_CAPI_ID_STRING)); /* -> key */
void** udata = (void**) lua_newuserdata(L, sizeof(void*) + strlen(CARRAY_CAPI_ID_STRING) + 1); /* -> key, value */
*udata = (void*)capi;
strcpy((char*)(udata + 1), CARRAY_CAPI_ID_STRING); /* -> key, value */
lua_rawset(L, (index < 0) ? (index - 2) : index); /* -> */
return 0;
}
#endif /* CARRAY_CAPI_IMPLEMENT_SET_CAPI */
#if CARRAY_CAPI_IMPLEMENT_GET_CAPI || CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI
/**
* Gives the associated Carray C API for the object at the given stack index.
* Returns NULL, if the object at the given stack index does not have an
* associated Carray C API or only has a Carray C API with incompatible version
* number. If errorReason is not NULL it receives the error reason in this case:
* 1 for incompatible version nummber and 2 for no associated C API at all.
*/
static const carray_capi* carray_get_capi(lua_State* L, int index, int* errorReason)
{
if (luaL_getmetafield(L, index, CARRAY_CAPI_ID_STRING) != LUA_TNIL) /* -> _capi */
{
const void** udata = (const void**) lua_touserdata(L, -1); /* -> _capi */
if ( udata
&& (lua_rawlen(L, -1) >= sizeof(void*) + strlen(CARRAY_CAPI_ID_STRING) + 1)
&& (memcmp((char*)(udata + 1), CARRAY_CAPI_ID_STRING,
strlen(CARRAY_CAPI_ID_STRING) + 1) == 0))
{
const carray_capi* capi = (const carray_capi*) *udata; /* -> _capi */
while (capi) {
if ( capi->version_major == CARRAY_CAPI_VERSION_MAJOR
&& capi->version_minor >= CARRAY_CAPI_VERSION_MINOR)
{ /* -> _capi */
lua_pop(L, 1); /* -> */
return capi;
}
capi = (const carray_capi*) capi->next_capi;
}
if (errorReason) {
*errorReason = 1;
}
} else { /* -> _capi */
if (errorReason) {
*errorReason = 2;
}
}
lua_pop(L, 1); /* -> */
} else { /* -> */
if (errorReason) {
*errorReason = 2;
}
}
return NULL;
}
#endif /* CARRAY_CAPI_IMPLEMENT_GET_CAPI || CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI */
#if CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI
static const carray_capi* carray_require_capi(lua_State* L)
{
if (luaL_loadstring(L, "return require('carray')") != 0) { /* -> chunk */
lua_error(L);
}
lua_call(L, 0, 1); /* -> carray */
int errorReason;
const carray_capi* capi = carray_get_capi(L, -1, &errorReason);
if (!capi) {
if (errorReason == 1) {
luaL_error(L, "carray capi version mismatch");
} else {
luaL_error(L, "carray capi not found");
}
}
lua_pop(L, 1);
return capi;
}
#endif /* CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* CARRAY_CAPI_H */