zhangmeng
2021-12-10 5990bac28af438a914165441b3c33a370b320d16
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
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
//
// Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 Devolutions <info@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt).  A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
 
#ifndef CORE_PLATFORM_H
#define CORE_PLATFORM_H
 
// We require some standard C header files.  The only one of these that might
// be problematic is <stdint.h>, which is required for C99.  Older versions
// of the Windows compilers might not have this.  However, latest versions of
// MS Studio have a functional <stdint.h>.  If this impacts you, just upgrade
// your tool chain.
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
 
// These are the APIs that a platform must implement to support nng.
 
// A word about fork-safety: This library is *NOT* fork safe, in that
// functions may not be called in the child process without an intervening
// exec().  The library attempts to detect this situation, and crashes the
// process with an error message if it encounters it.  (See nn_platform_init
// below.)
//
// Additionally, some file descriptors may leak across fork even to
// child processes.  We make every reasonable effort to ensure that this
// does not occur, but on some platforms there are unavoidable race
// conditions between file creation and marking the file close-on-exec.
//
// Forkers should use posix_spawn() if possible, and as much as possible
// arrange for file close on exec by posix_spawn, or close the descriptors
// they do not need in the child.  (Note that posix_spawn() does *NOT*
// arrange for pthread_atfork() handlers to be called on some platforms.)
 
//
// Debugging Support
//
 
// nni_plat_abort crashes the system; it should do whatever is appropriate
// for abnormal programs on the platform, such as calling abort().
extern void nni_plat_abort(void);
 
// nni_plat_println is used to emit debug messages.  Typically this is used
// during core debugging, or to emit panic messages.  Message content will
// not contain newlines, but the output will add them.
extern void nni_plat_println(const char *);
 
// nni_plat_printf is like printf.  It should conform to C99 standard printf,
// but is a function to allow platform ports to redirect.  It should go to
// the same place that nni_plat_println does.
extern void nni_plat_printf(const char *, ...);
 
// nni_plat_strerror allows the platform to use additional error messages
// for additional error codes.  The err code passed in should be the
// equivalent of errno or GetLastError, without the NNG_ESYSERR component.
// The platform should make sure that the returned value will be valid
// after the call returns.  (If necessary, thread-local storage can be
// used.)
extern const char *nni_plat_strerror(int);
 
//
// Memory Management
//
 
// nni_alloc allocates memory.  In most cases this can just be malloc().
// However, you may provide a different allocator, for example it is
// possible to use a slab allocator or somesuch.  It is permissible for this
// to return NULL if memory cannot be allocated.
extern void *nni_alloc(size_t);
 
// nni_zalloc is just like nni_alloc, but ensures that memory is
// initialized to zero.  It is a separate function because some platforms
// can use a more efficient zero-based allocation.
extern void *nni_zalloc(size_t);
 
// nni_free frees memory allocated with nni_alloc or nni_zalloc. It takes
// a size because some allocators do not track size, or can operate more
// efficiently if the size is provided with the free call.  Examples of this
// are slab allocators like this found in Solaris/illumos (see libumem).
// This routine does nothing if supplied with a NULL pointer and zero size.
// Most implementations can just call free() here.
extern void nni_free(void *, size_t);
 
typedef struct nni_plat_mtx    nni_plat_mtx;
typedef struct nni_plat_rwlock nni_plat_rwlock;
typedef struct nni_plat_cv     nni_plat_cv;
typedef struct nni_plat_thr    nni_plat_thr;
typedef struct nni_rwlock      nni_rwlock;
 
//
// Threading & Synchronization Support
//
 
// nni_plat_mtx_init initializes a mutex structure.  An initialized mutex must
// be distinguishable from zeroed memory.
extern void nni_plat_mtx_init(nni_plat_mtx *);
 
// nni_plat_mtx_fini destroys the mutex and releases any resources allocated
// for it's use.  If the mutex is zeroed memory, this should do nothing.
extern void nni_plat_mtx_fini(nni_plat_mtx *);
 
// nni_plat_mtx_lock locks the mutex.  This is not recursive -- a mutex can
// only be entered once.
extern void nni_plat_mtx_lock(nni_plat_mtx *);
 
// nni_plat_mtx_unlock unlocks the mutex.  This can only be performed by the
// thread that owned the mutex.
extern void nni_plat_mtx_unlock(nni_plat_mtx *);
 
// read/write locks - these work like mutexes except that multiple readers
// can acquire the lock.  These are not safe for recursive use, and it is
// unspecified whether any measures are provided to prevent starvation.
extern void nni_rwlock_init(nni_rwlock *);
extern void nni_rwlock_fini(nni_rwlock *);
extern void nni_rwlock_rdlock(nni_rwlock *);
extern void nni_rwlock_wrlock(nni_rwlock *);
extern void nni_rwlock_unlock(nni_rwlock *);
 
// nni_plat_cv_init initializes a condition variable.  We require a mutex be
// supplied with it, and that mutex must always be held when performing any
// operations on the condition variable (other than fini.)  As with mutexes, an
// initialized mutex should be distinguishable from zeroed memory.
extern void nni_plat_cv_init(nni_plat_cv *, nni_plat_mtx *);
 
// nni_plat_cv_fini releases all resources associated with condition variable.
// If the cv points to just zeroed memory (was never initialized), it does
// nothing.
extern void nni_plat_cv_fini(nni_plat_cv *);
 
// nni_plat_cv_wake wakes all waiters on the condition.  This should be
// called with the lock held.
extern void nni_plat_cv_wake(nni_plat_cv *);
 
// nni_plat_cv_wake1 wakes only a single waiter.  Use with caution
// to avoid losing the wakeup when multiple waiters may be present.
extern void nni_plat_cv_wake1(nni_plat_cv *);
 
// nni_plat_cv_wait waits for a wake up on the condition variable.  The
// associated lock is atomically released and reacquired upon wake up.
// Callers can be spuriously woken.  The associated lock must be held.
extern void nni_plat_cv_wait(nni_plat_cv *);
 
// nni_plat_cv_until waits for a wakeup on the condition variable, or
// until the system time reaches the specified absolute time.  (It is an
// absolute form of nni_cond_timedwait.)  Early wakeups are possible, so
// check the condition.  It will return either NNG_ETIMEDOUT, or 0.
extern int nni_plat_cv_until(nni_plat_cv *, nni_time);
 
// nni_plat_thr_init creates a thread that runs the given function. The
// thread receives a single argument.  The thread starts execution
// immediately.
extern int nni_plat_thr_init(nni_plat_thr *, void (*)(void *), void *);
 
// nni_plat_thr_fini waits for the thread to exit, and then releases any
// resources associated with the thread.  After this returns, it
// is an error to reference the thread in any further way.
extern void nni_plat_thr_fini(nni_plat_thr *);
 
// nni_plat_thr_is_self returns true if the caller is the thread
// identified, and false otherwise.  (This allows some deadlock
// prevention in callbacks, for example.)
extern bool nni_plat_thr_is_self(nni_plat_thr *);
 
// nni_plat_thr_set_name is used to set the thread name, which
// should be a short ASCII string.  It may or may not be supported --
// this is intended to facilitate debugging.
extern void nni_plat_thr_set_name(nni_plat_thr *, const char *);
 
//
// Atomics support.  This will evolve over time.
//
 
// nni_atomic_flag supports only test-and-set and reset operations.
// This can be implemented without locks on any reasonable system, and
// it corresponds to C11 atomic flag.
typedef struct nni_atomic_flag nni_atomic_flag;
 
extern bool nni_atomic_flag_test_and_set(nni_atomic_flag *);
extern void nni_atomic_flag_reset(nni_atomic_flag *);
 
// nni_atomic_bool is for boolean flags that need to be checked without
// changing their value.  This might require a lock on some systems.
typedef struct nni_atomic_bool nni_atomic_bool;
 
extern void nni_atomic_init_bool(nni_atomic_bool *);
extern void nni_atomic_set_bool(nni_atomic_bool *, bool);
extern bool nni_atomic_get_bool(nni_atomic_bool *);
extern bool nni_atomic_swap_bool(nni_atomic_bool *, bool);
 
typedef struct nni_atomic_u64 nni_atomic_u64;
 
extern void     nni_atomic_init64(nni_atomic_u64 *);
extern void     nni_atomic_add64(nni_atomic_u64 *, uint64_t);
extern void     nni_atomic_sub64(nni_atomic_u64 *, uint64_t);
extern uint64_t nni_atomic_get64(nni_atomic_u64 *);
extern void     nni_atomic_set64(nni_atomic_u64 *, uint64_t);
extern uint64_t nni_atomic_swap64(nni_atomic_u64 *, uint64_t);
extern uint64_t nni_atomic_dec64_nv(nni_atomic_u64 *);
extern void     nni_atomic_inc64(nni_atomic_u64 *);
 
// nni_atomic_cas64 is a compare and swap.  The second argument is the
// value to compare against, and the third is the new value. Returns
// true if the value was set.
extern bool nni_atomic_cas64(nni_atomic_u64 *, uint64_t, uint64_t);
 
// In a lot of circumstances, we want a simple atomic reference count,
// or atomic tunable values for integers like queue lengths or TTLs.
// These native integer forms should be preferred over the 64 bit versions
// unless larger bit sizes are truly needed.  They will be more efficient
// on many platforms.
typedef struct nni_atomic_int nni_atomic_int;
 
extern void nni_atomic_init(nni_atomic_int *);
extern void nni_atomic_add(nni_atomic_int *, int);
extern void nni_atomic_sub(nni_atomic_int *, int);
extern int  nni_atomic_get(nni_atomic_int *);
extern void nni_atomic_set(nni_atomic_int *, int);
extern int  nni_atomic_swap(nni_atomic_int *, int);
extern int  nni_atomic_dec_nv(nni_atomic_int *);
extern void nni_atomic_dec(nni_atomic_int *);
extern void nni_atomic_inc(nni_atomic_int *);
 
// nni_atomic_cas is a compare and swap.  The second argument is the
// value to compare against, and the third is the new value. Returns
// true if the value was set.
extern bool nni_atomic_cas(nni_atomic_int *, int, int);
 
//
// Clock Support
//
 
// nn_clock returns a number of milliseconds since some arbitrary time
// in the past.  The values returned by nni_clock must use the same base
// as the times used in nni_plat_cond_until.  The nni_clock() must
// return values > 0, and must return values smaller than 2^63.  (We could
// relax this last constraint, but there is no reason to, and leaves us the
// option of using negative values for other purposes in the future.)
extern nni_time nni_clock(void);
 
// nni_msleep sleeps for the specified number of milliseconds (at least).
extern void nni_msleep(nni_duration);
 
//
// Entropy Support
//
 
// nni_random returns a cryptographically secure random uint32.
uint32_t nni_random(void);
 
// nni_plat_init is called to allow the platform the chance to
// do any necessary initialization.  This routine MUST be idempotent,
// and thread-safe, and will be called before any other API calls, and
// may be called at any point thereafter.  It is permitted to return
// an error if some critical failure initializing the platform occurs,
// but once this succeeds, all future calls must succeed as well, unless
// nni_plat_fini has been called.
//
// The function argument should be called if the platform has not initialized
// (i.e. exactly once), and its result passed back to the caller.  If it
// does not return 0 (success), then it may be called again to try to
// initialize the platform again at a later date.
extern int nni_plat_init(int (*)(void));
 
// nni_plat_fini is called to clean up resources.  It is intended to
// be called as the last thing executed in the library, and no other functions
// will be called until nni_platform_init is called.
extern void nni_plat_fini(void);
 
// nni_plat_ncpu returns the number of logical CPUs on the system.  This is
// used to scale the number of independent threads started.
extern int nni_plat_ncpu(void);
 
//
// TCP Support.
//
typedef struct nni_tcp_conn     nni_tcp_conn;
typedef struct nni_tcp_dialer   nni_tcp_dialer;
typedef struct nni_tcp_listener nni_tcp_listener;
 
// nni_tcp_dialer_init creates a new dialer object.
extern int nni_tcp_dialer_init(nni_tcp_dialer **);
 
// nni_tcp_dialer_fini finalizes the dialer, closing it and freeing
// all resources.
extern void nni_tcp_dialer_fini(nni_tcp_dialer *);
 
// nni_tcp_dialer_close closes the dialer.
// Further operations on it should return NNG_ECLOSED.
// Any in-progress connection will be aborted.
extern void nni_tcp_dialer_close(nni_tcp_dialer *);
 
// nni_tcp_dial attempts to create an outgoing connection,
// asynchronously, to the address in the aio. On success, the first (and only)
// output will be an nni_tcp_conn * associated with the remote server.
extern void nni_tcp_dial(nni_tcp_dialer *, const nng_sockaddr *, nni_aio *);
 
// nni_tcp_dialer_set sets an option on the dialer.
extern int nni_tcp_dialer_set(
    nni_tcp_dialer *, const char *, const void *, size_t, nni_type);
 
// nni_tcp_dialer_get gets an option on the dialer.
extern int nni_tcp_dialer_get(
    nni_tcp_dialer *, const char *, void *, size_t *, nni_type);
 
// nni_tcp_listener_init creates a new listener object, unbound.
extern int nni_tcp_listener_init(nni_tcp_listener **);
 
// nni_tcp_listener_fini frees the listener and all associated resources.
// It implicitly closes the listener as well.
extern void nni_tcp_listener_fini(nni_tcp_listener *);
 
// nni_tcp_listener_close closes the listener.  This will unbind
// any bound socket, and further operations will result in NNG_ECLOSED.
extern void nni_tcp_listener_close(nni_tcp_listener *);
 
// nni_tcp_listener_listen creates the socket in listening mode, bound
// to the specified address.
extern int nni_tcp_listener_listen(nni_tcp_listener *, const nni_sockaddr *);
 
// nni_tcp_listener_accept accepts in incoming connect, asynchronously.
// On success, the first (and only) output will be an nni_tcp_conn *
// associated with the remote peer.
extern void nni_tcp_listener_accept(nni_tcp_listener *, nni_aio *);
 
// nni_tcp_listener_set sets an option on the listener.
extern int nni_tcp_listener_set(
    nni_tcp_listener *, const char *, const void *, size_t, nni_type);
 
// nni_tcp_listener_get gets an option from the listener.  The most common
// use for this is to retrieve the setting of the NNG_OPT_TCP_LOCADDR
// address after binding to wild card port (0).
extern int nni_tcp_listener_get(
    nni_tcp_listener *, const char *, void *, size_t *, nni_type);
 
// nni_resolv_ip resolves a DNS host and service name asynchronously.
// The family should be one of NNG_AF_INET, NNG_AF_INET6, or NNG_AF_UNSPEC.
// The first two constrain the name to those families, while the third will
// return names of either family.  The passive flag indicates that the
// name will be used for bind(), otherwise the name will be used with
// connect().  The host part may be NULL only if passive is true.
// Symbolic service names will be looked up assuming SOCK_STREAM, so
// they may not work with UDP.
extern void nni_resolv_ip(
    const char *, const char *, int, bool, nng_sockaddr *sa, nni_aio *);
 
// nni_parse_ip parses an IP address, without a port.
extern int nni_parse_ip(const char *, nng_sockaddr *);
 
// nni_parse_ip_port parses an IP address with an optional port appended.
extern int nni_parse_ip_port(const char *, nng_sockaddr *);
 
//
// IPC (UNIX Domain Sockets & Named Pipes) Support.
//
 
typedef struct nni_ipc_conn     nni_ipc_conn;
typedef struct nni_ipc_dialer   nni_ipc_dialer;
typedef struct nni_ipc_listener nni_ipc_listener;
 
// IPC is so different from platform to platform.  The following should
// be implemented.  If IPC isn't supported, all of these functions should
// be stubs that just return NNG_ENOTSUP.
extern int nni_ipc_dialer_alloc(nng_stream_dialer **, const nng_url *);
extern int nni_ipc_listener_alloc(nng_stream_listener **, const nng_url *);
 
//
// UDP support. UDP is not connection oriented, and only has the notion
// of being bound, sendto, and recvfrom.  (It is possible to set up a
// connect call that semantically acts as a filter on recvfrom, but we
// don't use that.)  Outbound packets will include the destination address
// in the AIO, and inbound packets include the source address in the AIO.
// For now we don't have more sophisticated options like setting the TTL.
//
typedef struct nni_plat_udp nni_plat_udp;
 
// nni_plat_udp_open initializes a UDP socket, binding to the local
// address specified specified in the AIO.  The remote address is
// not used.  The resulting nni_plat_udp structure is returned in the
// the aio's a_pipe.
extern int nni_plat_udp_open(nni_plat_udp **, nni_sockaddr *);
 
// nni_plat_udp_close closes the underlying UDP socket.
extern void nni_plat_udp_close(nni_plat_udp *);
 
// nni_plat_udp_send sends the data in the aio to the the
// destination specified in the nni_aio.  The iovs are the
// UDP payload.
extern void nni_plat_udp_send(nni_plat_udp *, nni_aio *);
 
// nni_plat_udp_pipe_recv recvs a message, storing it in the iovs
// from the UDP payload.  If the UDP payload will not fit, then
// NNG_EMSGSIZE results.
extern void nni_plat_udp_recv(nni_plat_udp *, nni_aio *);
 
//
// Notification Pipe Pairs
//
 
// nni_plat_pipe creates a pair of linked file descriptors that are
// suitable for notification via SENDFD/RECVFD.  These are platform
// specific and exposed to applications for integration into event loops.
// The first pipe is written to by nng to notify, and the second pipe is
// generally read from to clear the event.   The implementation is not
// obliged to provide two pipes -- for example eventfd can be used with
// just a single file descriptor.  In such a case the implementation may
// just provide the same value twice.
extern int nni_plat_pipe_open(int *, int *);
 
// nni_plat_pipe_raise pushes a notification to the pipe.  Usually this
// will just be a non-blocking attempt to write a single byte.  It may
// however use any other underlying system call that is appropriate.
extern void nni_plat_pipe_raise(int);
 
// nni_plat_pipe_clear clears all notifications from the pipe.  Usually this
// will just be a non-blocking read.  (The call should attempt to read
// all data on a pipe, for example.)
extern void nni_plat_pipe_clear(int);
 
// nni_plat_pipe_close closes both pipes that were provided by the open
// routine.
extern void nni_plat_pipe_close(int, int);
 
extern int nni_plat_udp_sockname(nni_plat_udp *, nni_sockaddr *);
 
//
// File/Store Support
//
// Some transports require a persistent storage for things like
// key material, etc.  Generally, these are all going to be relatively
// small objects (such as certificates), so we only require a synchronous
// implementation from platforms.  This Key-Value API is intended to
// to support using the Key's as filenames, and keys will consist of
// only these characters: [0-9a-zA-Z._-].  The directory used should be
// determined by using an environment variable (NNG_STATE_DIR), or
// using some other application-specific method.
//
// We also support listing keys, for the case where a key must be looked
// up -- for example to get a list of certificates, or some such.
//
 
// nni_plat_file_put writes the named file, with the provided data,
// and the given size.  If the file already exists it is overwritten.
// The permissions on the file should be limited to read and write
// access by the entity running the application only.
extern int nni_plat_file_put(const char *, const void *, size_t);
 
// nni_plat_file_get reads the entire named file, allocating storage
// to receive the data and returning the data and the size in the
// reference arguments.  The data pointer should be freed with nni_free
// using the supplied size when no longer needed.
extern int nni_plat_file_get(const char *, void **, size_t *);
 
// nni_plat_file_delete deletes the named file.  If the name refers to
// a directory, then that will be removed only if empty.
extern int nni_plat_file_delete(const char *);
 
// nni_plat_file_check checks the file path to determine its type.
// If the path does not exist, then NNG_ENOENT is returned.
enum nni_plat_file_type_val {
    NNI_PLAT_FILE_TYPE_FILE,  // normal file
    NNI_PLAT_FILE_TYPE_DIR,   // normal directory
    NNI_PLAT_FILE_TYPE_OTHER, // something else (pipe, device node, etc.)
};
extern int nni_plat_file_type(const char *, int *);
 
enum nni_plat_file_walk_result {
    NNI_PLAT_FILE_WALK_CONTINUE,
    NNI_PLAT_FILE_WALK_STOP,        // stop walking (all done)
    NNI_PLAT_FILE_WALK_PRUNE_SIB,   // skip siblings and their children
    NNI_PLAT_FILE_WALK_PRUNE_CHILD, // skip children
};
 
enum nni_plat_file_walk_flags {
    NNI_PLAT_FILE_WALK_DEPTH_FIRST   = 0, // get children first
    NNI_PLAT_FILE_WALK_BREADTH_FIRST = 1, // get siblings first (later)
    NNI_PLAT_FILE_WALK_SHALLOW = 2, // do not descend into subdirectories
    NNI_PLAT_FILE_WALK_FILES_ONLY = 4, // directory names are not reported
};
 
// nni_plat_file_walker is called for each pathname found by walking a
// directory tree.  It returns one of the nni_plat_file_walk_result values.
typedef int (*nni_plat_file_walker)(const char *, void *);
 
// nni_plat_file_walk walks a directory tree, calling the walker function
// with the path name, and the supplied void * argument.
extern int nni_plat_file_walk(const char *, nni_plat_file_walker, void *, int);
 
typedef struct nni_plat_flock nni_plat_flock;
 
// nni_plat_file_lock locks the file.  This usually means open it (creating
// if it does not exist) and doing a lock operation.  The nni_plat_flock
// is our handle for the lock, to unlock.  Usually its just a file descriptor,
// and we can unlock by doing close().  Note that this is a "try-lock"
// operation -- if the file is already locked then NNG_EBUSY is returned.
extern int nni_plat_file_lock(const char *path, nni_plat_flock *);
 
// nni_plat_file_unlock unlocks the previously locked file.
extern void nni_plat_file_unlock(nni_plat_flock *);
 
// nni_plat_dir_open attempts to "open a directory" for listing.  The
// handle for further operations is returned in the first argument, and
// the directory name is supplied in the second.
extern int nni_plat_dir_open(void **, const char *);
 
// nni_plat_dir_next gets the next directory entry.  Each call returns
// a new entry (arbitrary order).  When no more entries exist, it returns
// NNG_ENOENT.  The returned name is valid until the next call to this
// function, or until the directory is closed.  Only files are returned,
// subdirectories are not reported.
extern int nni_plat_dir_next(void *, const char **);
 
// nni_plat_dir_close closes the directory handle, freeing all
// resources associated with it.
extern void nni_plat_dir_close(void *);
 
// nni_plat_dir_create creates a directory.  Any parent directories must
// already exist.  If the directory already exists, 0 is returned.
extern int nni_plat_dir_create(const char *);
 
// nni_plat_dir_remove removes a directory, which must already be empty.
// If it does not exist, 0 is returned.
extern int nni_plat_dir_remove(const char *);
 
// nni_plat_temp_dir returns a temporary/scratch directory for the platform
// The result should be freed with nni_strfree().
extern char *nni_plat_temp_dir(void);
 
// nni_plat_join_dir joins to path components to make a path name.
// For example. on UNIX systems nni_plat_join_dir("/tmp", "a") returns
// "/tmp/a".  The pathname returned should be freed with nni_strfree().
extern char *nni_plat_join_dir(const char *, const char *);
 
// nni_plat_file_basename returns the "file" part of the file name.
// The returned pointer will usually reference the end of the supplied
// string, and may not be altered.
extern const char *nni_plat_file_basename(const char *);
 
//
// Actual platforms we support.  This is included up front so that we can
// get the specific types that are supplied by the platform.
#if defined(NNG_PLATFORM_POSIX)
#include "platform/posix/posix_impl.h"
#elif defined(NNG_PLATFORM_WINDOWS)
#include "platform/windows/win_impl.h"
#else
#error "unknown platform"
#endif
 
#endif // CORE_PLATFORM_H