aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmdsclient/proto-util.h
blob: 2d384412bc66b6c32f75368174a8e27fbd892c6d (plain) (blame)
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
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
/**
 * mds — A micro-display server
 * Copyright © 2014, 2015, 2016  Mattias Andrée (maandree@member.fsf.org)
 * 
 * This program 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.
 * 
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef MDS_LIBMDSCLIENT_PROTO_UTIL_H
#define MDS_LIBMDSCLIENT_PROTO_UTIL_H



#include <stddef.h>
#include <stdarg.h>
#include <stdint.h>



/**
 * Optimisations `libmds_headers_cherrypick` MAY use
 */
typedef enum libmds_cherrypick_optimisation
{
  /**
   * No optimisation is allowed, in particular this
   * means that the array of headers may not be reordered.
   * The function may still create a copy of the header
   * array and sort the copy.
   * 
   * Cannot be combined with `SORT` or `SORTED`
   * 
   * This option is guaranteed to always have the value 0
   */
  DO_NOT_SORT = 0,
  
  /**
   * `libmds_headers_cherrypick` is allowed to
   * sort the header array. There is no guarantee
   * that the header array will be sorted.
   * 
   * Cannot be combined with `DO_NOT_SORT` or `SORTED`
   */
  SORT = 1,
  
  /**
   * Informs `libmds_headers_cherrypick` that the header
   * array already is sorted. `libmds_headers_cherrypick`
   * can use this information to optimise its procedure.
   * But this also means that the header array will not
   * be reordered.
   * 
   * Cannot be combined with `DO_NOT_SORT` or `SORT`
   */
  SORTED = 2,
  
  
  /**
   * The list of requested headers is not sorted
   * in ascending order
   * 
   * Cannot be combined with `ARGS_SORTED`
   * 
   * This option is guaranteed to always have the value 0
   */
  ARGS_UNSORTED = 0,
  
  /**
   * The list of requested headers is sorted in
   * ascending order
   * 
   * Cannot be combined with `ARGS_UNSORTED`
   */
  ARGS_SORTED = 4,
  
  
} libmds_cherrypick_optimisation_t;


/**
 * Cherrypick headers from a message
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   found         Output parameter for the number of found headers of those that
 *                        were requested.
 * @param   optimisation  Optimisations the function may use, only one value please
 * @param   ...           The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                Zero on success, -1 on error, `errno` will have been set
 *                        accordingly on error.
 * 
 * @throws  ENOMEM        Out of memory. Possibly, the process hit the RLIMIT_AS or
 *                        RLIMIT_DATA limit described in getrlimit(2).
 */
__attribute__((sentinel))
int libmds_headers_cherrypick(char** restrict headers, size_t header_count, size_t* restrict found,
			      libmds_cherrypick_optimisation_t optimisation, ...);

/**
 * Cherrypick headers from a message,
 * linear search will be used without optimisations
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   ...           The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
__attribute__((sentinel))
size_t libmds_headers_cherrypick_linear_unsorted(char** restrict headers, size_t header_count, ...);
#define libmds_headers_cherrypick_linear_unsorted  libmds_headers_cherrypick_linear_unsorted

/**
 * Cherrypick headers from a message,
 * linear search will be used with optimisation based
 * on that the array is sorted as well as the list of
 * requested headers (`...`)
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   ...           The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
__attribute__((sentinel))
size_t libmds_headers_cherrypick_linear_sorted(char** restrict headers, size_t header_count, ...);
#define libmds_headers_cherrypick_linear_sorted  libmds_headers_cherrypick_linear_sorted

/**
 * Cherrypick headers from a message,
 * binary search will be used
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   ...           The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
__attribute__((sentinel))
size_t libmds_headers_cherrypick_binary_unsorted(char** restrict headers, size_t header_count, ...);
#define libmds_headers_cherrypick_binary_unsorted  libmds_headers_cherrypick_binary_unsorted

/**
 * Cherrypick headers from a message,
 * binary search will be used with optimisation based on
 * that list of requested headers (`...`)  is sorted
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   ...           The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
__attribute__((sentinel))
size_t libmds_headers_cherrypick_binary_sorted(char** restrict headers, size_t header_count, ...);
#define libmds_headers_cherrypick_binary_unsorted  libmds_headers_cherrypick_binary_unsorted

/**
 * Cherrypick headers from a message
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   found         Output parameter for the number of found headers of those that
 *                        were requested. `NULL` is permitted.
 * @param   optimisation  Optimisations the function may use
 * @param   args          The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                Zero on success, -1 on error, `errno` will have been set
 *                        accordingly on error.
 * 
 * @throws  ENOMEM        Out of memory. Possibly, the process hit the RLIMIT_AS or
 *                        RLIMIT_DATA limit described in getrlimit(2).
 */
int libmds_headers_cherrypick_v(char** restrict headers, size_t header_count, size_t* restrict found,
				libmds_cherrypick_optimisation_t optimisation, va_list args);

/**
 * Cherrypick headers from a message,
 * linear search will be used without optimisation
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   args          The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
size_t libmds_headers_cherrypick_linear_unsorted_v(char** restrict headers, size_t header_count, va_list args);
#define libmds_headers_cherrypick_linear_unsorted_v  libmds_headers_cherrypick_linear_unsorted_v

/**
 * Cherrypick headers from a message,
 * linear search will be used with optimisation based
 * on that the array is sorted as well as the list of
 * requested headers (`args`)
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   args          The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
size_t libmds_headers_cherrypick_linear_sorted_v(char** restrict headers, size_t header_count, va_list args);
#define libmds_headers_cherrypick_linear_sorted_v  libmds_headers_cherrypick_linear_sorted_v

/**
 * Cherrypick headers from a message,
 * binary search will be used
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   args          The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
size_t libmds_headers_cherrypick_binary_unsorted_v(char** restrict headers, size_t header_count, va_list args);
#define libmds_headers_cherrypick_binary_unsorted_v  libmds_headers_cherrypick_binary_unsorted_v

/**
 * Cherrypick headers from a message,
 * binary search will be used with optimisation based on
 * that list of requested headers (`args`)  is sorted
 * 
 * @param   headers       The headers in the message
 * @param   header_count  The number of headers
 * @param   args          The first argument should be the name of a header, it should
 *                        have the type `const char*`. The second argument should be
 *                        a pointer to the location where the header named by the first
 *                        argument in the argument list names. It should have the type
 *                        `char**`. If the header is found, its value will be stored,
 *                        and it will be a NUL-terminated string. If the header is not
 *                        found, `NULL` will be stored.  The next two arguments is
 *                        interpreted analogously to the first two arguments, and the
 *                        following two in the same way, and so on. When there are no
 *                        more headers in the list, it should be terminated with a `NULL`.
 * @return                The number of found headers of those that were requested
 */
size_t libmds_headers_cherrypick_binary_sorted_v(char** restrict headers, size_t header_count, va_list args);
#define libmds_headers_cherrypick_binary_sorted_v  libmds_headers_cherrypick_binary_sorted_v

/**
 * Sort the a header array, this is what `libmds_headers_cherrypick`
 * uses to optimise its procedure.
 * 
 * @param  headers      The array of headers
 * @param  headr_count  The number of elements in `headers`
 */
void libmds_headers_sort(char** restrict headers, size_t header_count);

/**
 * Compose a message
 * 
 * @param   buffer          Pointer to the buffer where the message should be written,
 *                          may point to `NULL` if `buffer_size` points to zero, but the
 *                          pointer itself must not be `NULL`. The buffer may be reallocated.
 *                          Will be updated with the new buffer it is allocated.
 *                          The buffer the pointer points to will be filled with the
 *                          message, however there is no guarantee it will be NUL-terminated.
 * @param   buffer_size     The allocation size of, in `char` `*buffer`, if and only if `buffer`
 *                          points to `NULL`, this point should point to zero, and vice versa.
 *                          Must not be `NULL`. Will be update with the new allocation size.
 *                          It is guaranteed that there will be remove to NUL-terminate
 *                          the message even if it was already NUL-terminate (terminate it
 *                          doubly in that case.)
 * @param   length          Output parameter for the length of the constructed message.
 *                          Must not be `NULL`.
 * @param   payload         The payload that should be added to the message, it should end
 *                          with a LF. `NULL` if the message should not have a payload.
 * @param   payload_length  Pointer to the length of the payload. If `NULL`, `payload`
 *                          must be NUL-terminated, and if it is `NULL` this NUL-termination
 *                          will be used to find the length of the payload. This variable is
 *                          not used if `payload` is `NULL`.
 * @param   ...             The first argument should be a line describing the first header,
 *                          it should be a printf(3)-formatted line that fully describes the
 *                          header line, that is, the header name, colon, blank space and then
 *                          the header's value. No LF should be included. The following
 *                          arguments should be the argument to format the header line.
 *                          If a format for a line begins with a question mark, the remainder
 *                          of the line used, but only if the next argument is non-zero
 *                          (it should be of type `int`,) that argument will not be used
 *                          for the formatting. This may be be iterated any number of this.
 *                          The last argument should be `NULL` to specify that there are no
 *                          more headers. The `Length`-header should not be included, it is
 *                          added automatically. A header may not have a length larger than
 *                          2¹⁵, otherwise the behaviour of this function is undefined.
 * @return                  Zero on success, -1 on error, `errno` will have been set
 *                          accordingly on error.
 * 
 * @throws  ENOMEM          Out of memory. Possibly, the process hit the RLIMIT_AS or
 *                          RLIMIT_DATA limit described in getrlimit(2).
 */
__attribute__((nonnull(1, 2, 3), sentinel))
int libmds_compose(char** restrict buffer, size_t* restrict buffer_size, size_t* restrict length,
		   const char* restrict payload, const size_t* restrict payload_length, ...);

/**
 * Compose a message
 * 
 * @param   buffer          Pointer to the buffer where the message should be written,
 *                          may point to `NULL` if `buffer_size` points to zero, but the
 *                          pointer itself must not be `NULL`. The buffer may be reallocated.
 *                          Will be updated with the new buffer it is allocated.
 *                          The buffer the pointer points to will be filled with the
 *                          message, however there is no guarantee it will be NUL-terminated.
 * @param   buffer_size     The allocation size, in `char` of `*buffer`, if and only if `buffer`
 *                          points to `NULL`, this point should point to zero, and vice versa.
 *                          Must not be `NULL`. Will be update with the new allocation size.
 *                          It is guaranteed that there will be remove to NUL-terminate
 *                          the message even if it was already NUL-terminate (terminate it
 *                          doubly in that case.)
 * @param   length          Output parameter for the length of the constructed message.
 *                          Must not be `NULL`.
 * @param   payload         The payload that should be added to the message, it should end
 *                          with a LF. `NULL` if the message should not have a payload.
 * @param   payload_length  Pointer to the length of the payload. If `NULL`, `payload`
 *                          must be NUL-terminated, and if it is `NULL` this NUL-termination
 *                          will be used to find the length of the payload. This variable is
 *                          not used if `payload` is `NULL`.
 * @param   args            The first argument should be a line describing the first header,
 *                          it should be a printf(3)-formatted line that fully describes the
 *                          header line, that is, the header name, colon, blank space and then
 *                          the header's value. No LF should be included. The following
 *                          arguments should be the argument to format the header line.
 *                          If a format for a line begins with a question mark, the remainder
 *                          of the line used, but only if the next argument is non-zero
 *                          (it should be of type `int`,) that argument will not be used
 *                          for the formatting. This may be be iterated any number of this.
 *                          The last argument should be `NULL` to specify that there are no
 *                          more headers. The `Length`-header should not be included, it is
 *                          added automatically. A header may not have a length larger than
 *                          2¹⁵, otherwise the behaviour of this function is undefined.
 * @return                  Zero on success, -1 on error, `errno` will have been set
 *                          accordingly on error.
 * 
 * @throws  ENOMEM          Out of memory. Possibly, the process hit the RLIMIT_AS or
 *                          RLIMIT_DATA limit described in getrlimit(2).
 */
__attribute__((nonnull(1, 2, 3)))
int libmds_compose_v(char** restrict buffer, size_t* restrict buffer_size, size_t* restrict length,
		     const char* restrict payload, const size_t* restrict payload_length, va_list args);

/**
 * Increase the message ID counter
 * 
 * @param   message_id  Pointer to the current message ID, will be update with
 *                      the next free message ID. Must not be `NULL`.
 * @param   test        Function that tests whether an message ID is free,
 *                      it takes the message ID to test as its first argument,
 *                      and `data` as its second argument, and returns zero if
 *                      it is in used, a positive integer if it is free, and a
 *                      negative integer if an error occurred. `NULL` if there
 *                      should be no testing.
 * @param   data        Argument to pass to `test` so that it can deal with
 *                      threading or any other issues. Unused if `test` is `NULL`.
 * @return              Zero on success, -1 on error, `errno` will have been set
 *                      accordingly on error.
 * 
 * @throws  EAGAIN      If there are no free message ID to be used.
 *                      It is advisable to make `test` throw this immediately if
 *                      there are no free message ID:s.
 * @throws              Any error that `test` may throw.
 */
__attribute__((nonnull(1)))
int libmds_next_message_id(uint32_t* restrict message_id, int (*test)(uint32_t message_id, void* data),
			   void* data);


#endif