|
| 1 | +#ifndef CARRAY_CAPI_H |
| 2 | +#define CARRAY_CAPI_H |
| 3 | + |
| 4 | +#ifndef CARRAY_CAPI_HAVE_LONG_LONG |
| 5 | +# include <limits.h> |
| 6 | +# if defined(LLONG_MAX) |
| 7 | +# define CARRAY_CAPI_HAVE_LONG_LONG 1 |
| 8 | +# else |
| 9 | +# define CARRAY_CAPI_HAVE_LONG_LONG 0 |
| 10 | +# endif |
| 11 | +#endif |
| 12 | + |
| 13 | +#define CARRAY_CAPI_ID_STRING "_capi_carray" |
| 14 | +#define CARRAY_CAPI_VERSION_MAJOR -1 |
| 15 | +#define CARRAY_CAPI_VERSION_MINOR 0 |
| 16 | +#define CARRAY_CAPI_VERSION_PATCH 1 |
| 17 | + |
| 18 | +typedef struct carray_capi carray_capi; |
| 19 | +typedef struct carray_info carray_info; |
| 20 | +typedef struct carray carray; |
| 21 | + |
| 22 | +typedef enum carray_type carray_type; |
| 23 | +typedef enum carray_attr carray_attr; |
| 24 | + |
| 25 | +#ifndef CARRAY_CAPI_IMPLEMENT_SET_CAPI |
| 26 | +# define CARRAY_CAPI_IMPLEMENT_SET_CAPI 0 |
| 27 | +#endif |
| 28 | + |
| 29 | +#ifndef CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI |
| 30 | +# define CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI 0 |
| 31 | +#endif |
| 32 | + |
| 33 | +#ifndef CARRAY_CAPI_IMPLEMENT_GET_CAPI |
| 34 | +# define CARRAY_CAPI_IMPLEMENT_GET_CAPI 0 |
| 35 | +#endif |
| 36 | + |
| 37 | +enum carray_type |
| 38 | +{ |
| 39 | + CARRAY_UCHAR = 1, |
| 40 | + CARRAY_SCHAR = 2, |
| 41 | + |
| 42 | + CARRAY_SHORT = 3, |
| 43 | + CARRAY_USHORT = 4, |
| 44 | + |
| 45 | + CARRAY_INT = 5, |
| 46 | + CARRAY_UINT = 6, |
| 47 | + |
| 48 | + CARRAY_LONG = 7, |
| 49 | + CARRAY_ULONG = 8, |
| 50 | + |
| 51 | + CARRAY_FLOAT = 9, |
| 52 | + CARRAY_DOUBLE = 10, |
| 53 | + |
| 54 | +#if CARRAY_CAPI_HAVE_LONG_LONG |
| 55 | + CARRAY_LLONG = 11, |
| 56 | + CARRAY_ULLONG = 12, |
| 57 | +#endif |
| 58 | +}; |
| 59 | + |
| 60 | +enum carray_attr |
| 61 | +{ |
| 62 | + CARRAY_DEFAULT = 0, |
| 63 | + CARRAY_READONLY = 1 |
| 64 | +}; |
| 65 | + |
| 66 | +struct carray_info |
| 67 | +{ |
| 68 | + carray_type type; |
| 69 | + carray_attr attr; |
| 70 | + size_t elementSize; |
| 71 | + size_t elementCount; |
| 72 | +}; |
| 73 | + |
| 74 | +/** |
| 75 | + * Carray C API. |
| 76 | + */ |
| 77 | +struct carray_capi |
| 78 | +{ |
| 79 | + int version_major; |
| 80 | + int version_minor; |
| 81 | + int version_patch; |
| 82 | + |
| 83 | + /** |
| 84 | + * May point to another (incompatible) version of this API implementation. |
| 85 | + * NULL if no such implementation exists. |
| 86 | + * |
| 87 | + * The usage of next_capi makes it possible to implement two or more |
| 88 | + * incompatible versions of the C API. |
| 89 | + * |
| 90 | + * An API is compatible to another API if both have the same major |
| 91 | + * version number and if the minor version number of the first API is |
| 92 | + * greater or equal than the second one's. |
| 93 | + */ |
| 94 | + void* next_capi; |
| 95 | + |
| 96 | + /** |
| 97 | + * Creates new carray object which manages the underlying data. |
| 98 | + * |
| 99 | + * elementCount - number of elements |
| 100 | + * data - if not NULL, contains after the call the pointer |
| 101 | + * to the uninitialized array elements. the caller |
| 102 | + * is responsible for initializing the elements. |
| 103 | + * If NULL, the elements are initialized with zeros. |
| 104 | + * |
| 105 | + * Returns NULL pointer on parameter error. |
| 106 | + * This function may also raise a Lua error. |
| 107 | + */ |
| 108 | + carray* (*newCarray)(lua_State* L, carray_type t, carray_attr attr, size_t elementCount, void** data); |
| 109 | + |
| 110 | + /** |
| 111 | + * Creates new carray object with a reference to underlying data |
| 112 | + * managed by the caller. |
| 113 | + * |
| 114 | + * elementCount - number of elements |
| 115 | + * dataRef - pointer to the element content. The caller guerantees that the content |
| 116 | + * memory remains valid until the release callback is called. |
| 117 | + * releaseCallback - is called by the carray object if the reference to the underlying |
| 118 | + * data is not any longer needed. This callback may also be NULL |
| 119 | + * for the case of static data. |
| 120 | + * |
| 121 | + * Returns NULL pointer on parameter error. |
| 122 | + * This function may also raise a Lua error. |
| 123 | + */ |
| 124 | + carray* (*newCarrayRef)(lua_State* L, carray_type t, carray_attr attr, void* dataRef, size_t elementCount, |
| 125 | + void (*releaseCallback)(void* dataRef, size_t elementCount)); |
| 126 | + |
| 127 | + /** |
| 128 | + * Returns a valid pointer if the Lua object at the given stack |
| 129 | + * index is a valid readable carray, otherwise returns NULL. |
| 130 | + * |
| 131 | + * info - contains information about the carray after the call |
| 132 | + * May be NULL. |
| 133 | + * |
| 134 | + * The returned carray object is be valid as long as the Lua |
| 135 | + * object at the given stack index remains valid. |
| 136 | + * To keep the carray object beyond this call, the function |
| 137 | + * retainConstCarray() should be called (see below). |
| 138 | + */ |
| 139 | + const carray* (*toReadableCarray)(lua_State* L, int index, carray_info* info); |
| 140 | + |
| 141 | + /** |
| 142 | + * Returns a valid pointer if the Lua object at the given stack |
| 143 | + * index is a valid writable carray, otherwise returns NULL. |
| 144 | + * |
| 145 | + * info - contains information about the carray after the call |
| 146 | + * May be NULL. |
| 147 | + * |
| 148 | + * The returned carray object is valid as long as the Lua |
| 149 | + * object at the given stack index remains valid. |
| 150 | + * To keep the carray object beyond this call, the function |
| 151 | + * retainCarray() should be called (see below). |
| 152 | + */ |
| 153 | + carray* (*toWritableCarray)(lua_State* L, int index, carray_info* info); |
| 154 | + |
| 155 | + /** |
| 156 | + * Increase the reference counter of the carray object. |
| 157 | + * |
| 158 | + * This function must be called after the function toCarray() |
| 159 | + * as long as the Lua object on the given stack index is |
| 160 | + * valid (see above). |
| 161 | + */ |
| 162 | + void (*retainCarray)(const carray* a); |
| 163 | + |
| 164 | + /** |
| 165 | + * Decrease the reference counter of the carray object and |
| 166 | + * destructs the carray object if no reference is left. |
| 167 | + */ |
| 168 | + void (*releaseCarray)(const carray* a); |
| 169 | + |
| 170 | + /** |
| 171 | + * Get pointer to elements. |
| 172 | + * offset - index of the first element, 0 <= offset < elementCount |
| 173 | + * count - number of elements, 0 <= offset + count <= elementCount |
| 174 | + * Returns the pointer to the element in the array at the given offset. |
| 175 | + * The caller may only read or write at most count elements at this pointer, |
| 176 | + * otherwise behaviour may be undefined. |
| 177 | + */ |
| 178 | + void* (*getWritableElementPtr)(carray* a, size_t offset, size_t count); |
| 179 | + |
| 180 | + /** |
| 181 | + * Get pointer to elements. |
| 182 | + * offset - index of the first element, 0 <= offset < elementCount |
| 183 | + * count - number of elements, 0 <= offset + count <= elementCount |
| 184 | + * Returns the pointer to the element in the array at the given offset. |
| 185 | + * The caller may only read at most count elements at this pointer, |
| 186 | + * otherwise behaviour may be undefined. |
| 187 | + */ |
| 188 | + const void* (*getReadableElementPtr)(const carray* a, size_t offset, size_t count); |
| 189 | +}; |
| 190 | + |
| 191 | +#if CARRAY_CAPI_IMPLEMENT_SET_CAPI |
| 192 | +/** |
| 193 | + * Sets the Carray C API into the metatable at the given index. |
| 194 | + * |
| 195 | + * index: index of the table that is be used as metatable for objects |
| 196 | + * that are associated to the given capi. |
| 197 | + */ |
| 198 | +static int carray_set_capi(lua_State* L, int index, const carray_capi* capi) |
| 199 | +{ |
| 200 | + lua_pushlstring(L, CARRAY_CAPI_ID_STRING, strlen(CARRAY_CAPI_ID_STRING)); /* -> key */ |
| 201 | + void** udata = lua_newuserdata(L, sizeof(void*) + strlen(CARRAY_CAPI_ID_STRING) + 1); /* -> key, value */ |
| 202 | + *udata = (void*)capi; |
| 203 | + strcpy((char*)(udata + 1), CARRAY_CAPI_ID_STRING); /* -> key, value */ |
| 204 | + lua_rawset(L, (index < 0) ? (index - 2) : index); /* -> */ |
| 205 | + return 0; |
| 206 | +} |
| 207 | +#endif /* CARRAY_CAPI_IMPLEMENT_SET_CAPI */ |
| 208 | + |
| 209 | +#if CARRAY_CAPI_IMPLEMENT_GET_CAPI || CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI |
| 210 | +/** |
| 211 | + * Gives the associated Carray C API for the object at the given stack index. |
| 212 | + * Returns NULL, if the object at the given stack index does not have an |
| 213 | + * associated Carray C API or only has a Carray C API with incompatible version |
| 214 | + * number. If errorReason is not NULL it receives the error reason in this case: |
| 215 | + * 1 for incompatible version nummber and 2 for no associated C API at all. |
| 216 | + */ |
| 217 | +static const carray_capi* carray_get_capi(lua_State* L, int index, int* errorReason) |
| 218 | +{ |
| 219 | + if (luaL_getmetafield(L, index, CARRAY_CAPI_ID_STRING) != LUA_TNIL) /* -> _capi */ |
| 220 | + { |
| 221 | + void** udata = lua_touserdata(L, -1); /* -> _capi */ |
| 222 | + |
| 223 | + if ( udata |
| 224 | + && (lua_rawlen(L, -1) >= sizeof(void*) + strlen(CARRAY_CAPI_ID_STRING) + 1) |
| 225 | + && (memcmp((char*)(udata + 1), CARRAY_CAPI_ID_STRING, |
| 226 | + strlen(CARRAY_CAPI_ID_STRING) + 1) == 0)) |
| 227 | + { |
| 228 | + const carray_capi* capi = *udata; /* -> _capi */ |
| 229 | + while (capi) { |
| 230 | + if ( capi->version_major == CARRAY_CAPI_VERSION_MAJOR |
| 231 | + && capi->version_minor >= CARRAY_CAPI_VERSION_MINOR) |
| 232 | + { /* -> _capi */ |
| 233 | + lua_pop(L, 1); /* -> */ |
| 234 | + return capi; |
| 235 | + } |
| 236 | + capi = capi->next_capi; |
| 237 | + } |
| 238 | + if (errorReason) { |
| 239 | + *errorReason = 1; |
| 240 | + } |
| 241 | + } else { /* -> _capi */ |
| 242 | + if (errorReason) { |
| 243 | + *errorReason = 2; |
| 244 | + } |
| 245 | + } |
| 246 | + lua_pop(L, 1); /* -> */ |
| 247 | + } else { /* -> */ |
| 248 | + if (errorReason) { |
| 249 | + *errorReason = 2; |
| 250 | + } |
| 251 | + } |
| 252 | + return NULL; |
| 253 | +} |
| 254 | +#endif /* CARRAY_CAPI_IMPLEMENT_GET_CAPI || CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI */ |
| 255 | + |
| 256 | +#if CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI |
| 257 | + |
| 258 | +static const carray_capi* carray_require_capi(lua_State* L) |
| 259 | +{ |
| 260 | + if (luaL_loadstring(L, "return require('carray')") != 0) { /* -> chunk */ |
| 261 | + lua_error(L); |
| 262 | + } |
| 263 | + lua_call(L, 0, 1); /* -> carray */ |
| 264 | + int errorReason; |
| 265 | + const carray_capi* capi = carray_get_capi(L, -1, &errorReason); |
| 266 | + if (!capi) { |
| 267 | + if (errorReason == 1) { |
| 268 | + luaL_error(L, "carray capi version mismatch"); |
| 269 | + } else { |
| 270 | + luaL_error(L, "carray capi not found"); |
| 271 | + } |
| 272 | + } |
| 273 | + lua_pop(L, 1); |
| 274 | + return capi; |
| 275 | +} |
| 276 | + |
| 277 | +#endif /* CARRAY_CAPI_IMPLEMENT_REQUIRE_CAPI */ |
| 278 | + |
| 279 | +#endif /* CARRAY_CAPI_H */ |
0 commit comments