-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnotify_capi.h
173 lines (151 loc) · 5.94 KB
/
notify_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
#ifndef NOTIFY_CAPI_H
#define NOTIFY_CAPI_H
#define NOTIFY_CAPI_ID_STRING "_capi_notify"
#define NOTIFY_CAPI_VERSION_MAJOR 0
#define NOTIFY_CAPI_VERSION_MINOR 0
#define NOTIFY_CAPI_VERSION_PATCH 2
#ifndef NOTIFY_CAPI_IMPLEMENT_SET_CAPI
# define NOTIFY_CAPI_IMPLEMENT_SET_CAPI 0
#endif
#ifndef NOTIFY_CAPI_IMPLEMENT_GET_CAPI
# define NOTIFY_CAPI_IMPLEMENT_GET_CAPI 0
#endif
#ifdef __cplusplus
extern "C" {
struct notify_notifier;
struct notify_capi;
#else /* __cplusplus */
typedef struct notify_notifier notify_notifier;
typedef struct notify_capi notify_capi;
#endif /* ! __cplusplus */
/**
* Type for pointer to function that may be called if an error occurs.
* ehdata: void pointer that is given in notify method (see below)
* msg: detailed error message
* msglen: length of error message
*/
typedef void (*notifier_error_handler)(void* ehdata, const char* msg, size_t msglen);
/**
* Notify C API.
*/
struct notify_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;
/**
* Must return a valid pointer if the Lua object at the given stack
* index is a valid notifier, otherwise must return NULL.
*
* The returned notifier object must be valid as long as the Lua
* object at the given stack index remains valid.
* To keep the notifier object beyond this call, the function
* retainNotifier() should be called (see below).
*/
notify_notifier* (*toNotifier)(lua_State* L, int index);
/**
* Increase the reference counter of the notifier object.
* Must be thread safe.
*
* This function must be called after the function toNotifier()
* as long as the Lua object on the given stack index is
* valid (see above).
*/
void (*retainNotifier)(notify_notifier* n);
/**
* Decrease the reference counter of the notifier object and
* destructs the notifier object if no reference is left.
* Must be thread safe.
*/
void (*releaseNotifier)(notify_notifier* n);
/**
* Calls the notify method of the given notifier object.
* Must be thread safe.
*
* eh: error handling function, may be NULL
* ehdata: additional data that is given to error handling function.
* Returns 0 - on success otherwise an error code != 0.
* 1 - if notifier is closed. The caller is expected to release the notifier.
* Subsequent calls will always result this return code again.
* All other error codes are implementation specific.
*/
int (*notify)(notify_notifier* n, notifier_error_handler eh, void* ehdata);
};
#if NOTIFY_CAPI_IMPLEMENT_SET_CAPI
/**
* Sets the Notify 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 notify_set_capi(lua_State* L, int index, const notify_capi* capi)
{
lua_pushlstring(L, NOTIFY_CAPI_ID_STRING, strlen(NOTIFY_CAPI_ID_STRING)); /* -> key */
void** udata = (void**) lua_newuserdata(L, sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1); /* -> key, value */
*udata = (void*)capi;
strcpy((char*)(udata + 1), NOTIFY_CAPI_ID_STRING); /* -> key, value */
lua_rawset(L, (index < 0) ? (index - 2) : index); /* -> */
return 0;
}
#endif /* NOTIFY_CAPI_IMPLEMENT_SET_CAPI */
#if NOTIFY_CAPI_IMPLEMENT_GET_CAPI
/**
* Gives the associated Notify 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 Notify C API or only has a Notify 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 notify_capi* notify_get_capi(lua_State* L, int index, int* errorReason)
{
if (luaL_getmetafield(L, index, NOTIFY_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(NOTIFY_CAPI_ID_STRING) + 1)
&& (memcmp((char*)(udata + 1), NOTIFY_CAPI_ID_STRING,
strlen(NOTIFY_CAPI_ID_STRING) + 1) == 0))
{
const notify_capi* capi = (const notify_capi*) *udata; /* -> _capi */
while (capi) {
if ( capi->version_major == NOTIFY_CAPI_VERSION_MAJOR
&& capi->version_minor >= NOTIFY_CAPI_VERSION_MINOR)
{ /* -> _capi */
lua_pop(L, 1); /* -> */
return capi;
}
capi = (const notify_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 /* NOTIFY_CAPI_IMPLEMENT_GET_CAPI */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* NOTIFY_CAPI_H */