1 /* -*- Mode: C; c-basic-offset:4 ; -*- */
2 /*
3 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2005 The University of Tennessee and The University
7 * of Tennessee Research Foundation. All rights
8 * reserved.
9 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
10 * University of Stuttgart. All rights reserved.
11 * Copyright (c) 2004-2005 The Regents of the University of California.
12 * All rights reserved.
13 * Copyright (c) 2012 Los Alamos National Security, Inc. All rights reserved.
14 * Copyright (c) 2013-2018 Intel, Inc. All rights reserved.
15 * Copyright (c) 2015 Research Organization for Information Science
16 * and Technology (RIST). All rights reserved.
17 * Copyright (c) 2016 Mellanox Technologies, Inc.
18 * All rights reserved.
19 * $COPYRIGHT$
20 *
21 * Additional copyrights may follow
22 *
23 * $HEADER$
24 */
25 /**
26 * @file
27 *
28 * Data packing subsystem.
29 */
30
31 #ifndef PMIX_BFROP_H_
32 #define PMIX_BFROP_H_
33
34 #include <src/include/pmix_config.h>
35 #include <pmix_common.h>
36 #include <src/include/types.h>
37
38 #include "src/mca/mca.h"
39
40 #include "bfrops_types.h"
41
42 BEGIN_C_DECLS
43
44 /* The overall objective of this framework is to provide seamless
45 * cross-version support for communications by allowing a process
46 * to communicate with a peer:
47 *
48 * (a) using a different version of the buffer operations. We are
49 * allowing changes in the structure compositions and/or data
50 * type definitions between versions. This specifically affects
51 * our ability to pack/unpack across versions.
52 *
53 * (b) using a different buffer type (described vs non-described).
54 * This resolves conflicts when one side was compiled with a
55 * debug option, while the other side has been "optimized".
56 *
57 * This is a mult-select framework - i.e., multiple components
58 * are selected and "active" at the same time. The intent is
59 * to have one component for each data type variation, with the
60 * expectation that the community will do its best not to revise
61 * existing data type definitions. Thus, new variations should be
62 * rare, and only a few components will exist.
63 *
64 * The framework itself reflects the fact that any given peer
65 * will utilize only one variation of the data type definitions.
66 * Thus, once a peer is identified, it will pass its version string
67 * to this framework's "assign_module" function, which will then
68 * pass it to each component until one returns a module capable of
69 * processing the given version. This module is then "attached" to
70 * the pmix_peer_t object so it can be used for all subsequent
71 * communication to/from that peer.
72 *
73 * Buffer type is included in the buffer metadata. Unfortunately,
74 * the metadata is not communicated at each exchange. Thus, the
75 * peers will indicate during the connection handshake the type
76 * of buffer they will use for all subsequent communications. The
77 * peer must then utilize that same buffer type for all messages
78 * sent to that remote proc, so we provide new macros for creating
79 * and constructing buffers that ensures the correct buffer type
80 * is marked.
81 *
82 * Accordingly, there are two levels of APIs defined for this
83 * framework:
84 *
85 * (a) component level - these allow for init/finalize of the
86 * component, and assignment of a module to a given peer
87 * based on the version that peer is using
88 *
89 * (b) module level - implement pack/unpack/copy/recv/etc. of
90 * the various datatypes. Note that the module only needs
91 * to provide those functions that differ from the base
92 * functions - they don't need to duplicate all that code!
93 */
94
95
96 /* The following functions are exposed to the user - they
97 * therefore are implemented in the bfrops/base functions
98 * as wrappers to the real functions.
99 *
100 * NOTE: THESE FUNCTIONS ARE NOT TO BE USED INTERNALLY -
101 * USE THE MACROS INSTEAD
102 */
103 bool pmix_value_cmp(pmix_value_t *p, pmix_value_t *p1);
104
105
106
107 /**** MODULE INTERFACE DEFINITION ****/
108
109 /* initialize the module - the module is expected
110 * to register its datatype functions at this time */
111 typedef pmix_status_t (*pmix_bfrop_init_fn_t)(void);
112
113 /* finalize the module */
114 typedef void (*pmix_bfrop_finalize_fn_t)(void);
115
116 /**
117 * Pack one or more values into a buffer.
118 *
119 * The pack function packs one or more values of a specified type into
120 * the specified buffer. The buffer must have already been
121 * initialized via an PMIX_NEW or PMIX_CONSTRUCT call - otherwise, the
122 * pack_value function will return an error. Providing an unsupported
123 * type flag will likewise be reported as an error.
124 *
125 * Note that any data to be packed that is not hard type cast (i.e.,
126 * not type cast to a specific size) may lose precision when unpacked
127 * by a non-homogeneous recipient. The BFROP will do its best to deal
128 * with heterogeneity issues between the packer and unpacker in such
129 * cases. Sending a number larger than can be handled by the recipient
130 * will return an error code (generated by the BFROP upon unpacking) -
131 * the BFROP cannot detect such errors during packing.
132 *
133 * @param *buffer A pointer to the buffer into which the value is to
134 * be packed.
135 *
136 * @param *src A void* pointer to the data that is to be packed. This
137 * is interpreted as a pointer to an array of that data type containing
138 * length num_values. Note that strings are of data type char*, and so
139 * they are to be passed as (char **) - i.e., the caller must
140 * pass the address of the pointer to the string as the void*. This
141 * allows the BFROP to use a single interface function, but still allow
142 * the caller to pass multiple strings in a single call.
143 *
144 * @param num_values An int32_t indicating the number of values that are
145 * to be packed, beginning at the location pointed to by src. A string
146 * value is counted as a single value regardless of length. The values
147 * must be contiguous in memory. Arrays of pointers (e.g., string
148 * arrays) should be contiguous, although (obviously) the data pointed
149 * to need not be contiguous across array entries.
150 *
151 * @param type The type of the data to be packed - must be one of the
152 * PMIX defined data types.
153 *
154 * @retval PMIX_SUCCESS The data was packed as requested.
155 *
156 * @retval PMIX_ERROR(s) An appropriate PMIX error code indicating the
157 * problem encountered. This error code should be handled
158 * appropriately.
159 *
160 * @code
161 * pmix_buffer_t *buffer;
162 * int32_t src;
163 *
164 * status_code = pmix_bfrop.pack(buffer, &src, 1, PMIX_INT32);
165 * @endcode
166 */
167 typedef pmix_status_t (*pmix_bfrop_pack_fn_t)(pmix_buffer_t *buffer,
168 const void *src,
169 int32_t num_values,
170 pmix_data_type_t type);
171
172 /**
173 * Unpack values from a buffer.
174 *
175 * The unpack function unpacks the next value (or values) of a
176 * specified type from the specified buffer.
177 *
178 * The buffer must have already been initialized via an PMIX_NEW or
179 * PMIX_CONSTRUCT call (and assumedly filled with some data) -
180 * otherwise, the unpack_value function will return an
181 * error. Providing an unsupported type flag will likewise be reported
182 * as an error, as will specifying a data type that DOES NOT match the
183 * type of the next item in the buffer. An attempt to read beyond the
184 * end of the stored data held in the buffer will also return an
185 * error.
186 *
187 * NOTE: it is possible for the buffer to be corrupted and that
188 * the BFROP will *think* there is a proper variable type at the
189 * beginning of an unpack region - but that the value is bogus (e.g., just
190 * a byte field in a string array that so happens to have a value that
191 * matches the specified data type flag). Therefore, the data type error check
192 * is NOT completely safe. This is true for ALL unpack functions.
193 *
194 * Warning: The caller is responsible for providing adequate memory
195 * storage for the requested data. As noted below, the user
196 * must provide a parameter indicating the maximum number of values that
197 * can be unpacked into the allocated memory. If more values exist in the
198 * buffer than can fit into the memory storage, then the bfrop will unpack
199 * what it can fit into that location and return an error code indicating
200 * that the buffer was only partially unpacked.
201 *
202 * Note that any data that was not hard type cast (i.e., not type cast
203 * to a specific size) when packed may lose precision when unpacked by
204 * a non-homogeneous recipient. The BFROP will do its best to deal with
205 * heterogeneity issues between the packer and unpacker in such
206 * cases. Sending a number larger than can be handled by the recipient
207 * will return an error code generated by the BFROP upon unpacking - the
208 * BFROP cannot detect such errors during packing.
209 *
210 * @param *buffer A pointer to the buffer from which the value will be
211 * extracted.
212 *
213 * @param *dest A void* pointer to the memory location into which the
214 * data is to be stored. Note that these values will be stored
215 * contiguously in memory. For strings, this pointer must be to (char
216 * **) to provide a means of supporting multiple string
217 * operations. The BFROP unpack function will allocate memory for each
218 * string in the array - the caller must only provide adequate memory
219 * for the array of pointers.
220 *
221 * @param type The type of the data to be unpacked - must be one of
222 * the BFROP defined data types.
223 *
224 * @retval *max_num_values The number of values actually unpacked. In
225 * most cases, this should match the maximum number provided in the
226 * parameters - but in no case will it exceed the value of this
227 * parameter. Note that if you unpack fewer values than are actually
228 * available, the buffer will be in an unpackable state - the bfrop will
229 * return an error code to warn of this condition.
230 *
231 * @note The unpack function will return the actual number of values
232 * unpacked in this location.
233 *
234 * @retval PMIX_SUCCESS The next item in the buffer was successfully
235 * unpacked.
236 *
237 * @retval PMIX_ERROR(s) The unpack function returns an error code
238 * under one of several conditions: (a) the number of values in the
239 * item exceeds the max num provided by the caller; (b) the type of
240 * the next item in the buffer does not match the type specified by
241 * the caller; or (c) the unpack failed due to either an error in the
242 * buffer or an attempt to read past the end of the buffer.
243 *
244 * @code
245 * pmix_buffer_t *buffer;
246 * int32_t dest;
247 * char **string_array;
248 * int32_t num_values;
249 *
250 * num_values = 1;
251 * status_code = pmix_bfrop.unpack(buffer, (void*)&dest, &num_values, PMIX_INT32);
252 *
253 * num_values = 5;
254 * string_array = malloc(num_values*sizeof(char *));
255 * status_code = pmix_bfrop.unpack(buffer, (void*)(string_array), &num_values, PMIX_STRING);
256 *
257 * @endcode
258 */
259 typedef pmix_status_t (*pmix_bfrop_unpack_fn_t)(pmix_buffer_t *buffer, void *dest,
260 int32_t *max_num_values,
261 pmix_data_type_t type);
262 /**
263 * Copy a payload from one buffer to another
264 * This function will append a copy of the payload in one buffer into
265 * another buffer. If the destination buffer is NOT empty, then the
266 * type of the two buffers MUST match or else an
267 * error will be returned. If the destination buffer IS empty, then
268 * its type will be set to that of the source buffer.
269 * NOTE: This is NOT a destructive procedure - the
270 * source buffer's payload will remain intact, as will any pre-existing
271 * payload in the destination's buffer.
272 */
273 typedef pmix_status_t (*pmix_bfrop_copy_payload_fn_t)(pmix_buffer_t *dest,
274 pmix_buffer_t *src);
275
276 /**
277 * Copy a data value from one location to another.
278 *
279 * Since registered data types can be complex structures, the system
280 * needs some way to know how to copy the data from one location to
281 * another (e.g., for storage in the registry). This function, which
282 * can call other copy functions to build up complex data types, defines
283 * the method for making a copy of the specified data type.
284 *
285 * @param **dest The address of a pointer into which the
286 * address of the resulting data is to be stored.
287 *
288 * @param *src A pointer to the memory location from which the
289 * data is to be copied.
290 *
291 * @param type The type of the data to be copied - must be one of
292 * the BFROP defined data types.
293 *
294 * @retval PMIX_SUCCESS The value was successfully copied.
295 *
296 * @retval PMIX_ERROR(s) An appropriate error code.
297 *
298 */
299 typedef pmix_status_t (*pmix_bfrop_copy_fn_t)(void **dest, void *src,
300 pmix_data_type_t type);
301
302 /**
303 * Print a data value.
304 *
305 * Since registered data types can be complex structures, the system
306 * needs some way to know how to print them (i.e., convert them to a string
307 * representation). Provided for debug purposes.
308 *
309 * @retval PMIX_SUCCESS The value was successfully printed.
310 *
311 * @retval PMIX_ERROR(s) An appropriate error code.
312 */
313 typedef pmix_status_t (*pmix_bfrop_print_fn_t)(char **output, char *prefix,
314 void *src, pmix_data_type_t type);
315
316 /**
317 * Transfer a value from one pmix_value_t to another. Ordinarily,
318 * this would be executed as a base function. However, it is
319 * possible that future versions may add new data types, and
320 * thus the xfer function may differ
321 *
322 * @retval PMIX_SUCCESS The value was successfully transferred
323 *
324 * @retval PMIX_ERROR(s) An appropriate error code
325 */
326 typedef pmix_status_t (*pmix_bfrop_value_xfer_fn_t)(pmix_value_t *dest,
327 const pmix_value_t *src);
328
329
330 /**
331 * Load data into a pmix_value_t object. Again, this is provided
332 * as a component function to support different data types
333 */
334 typedef void (*pmix_bfrop_value_load_fn_t)(pmix_value_t *v, const void *data,
335 pmix_data_type_t type);
336
337 /**
338 * Unload data from a pmix_value_t object
339 *
340 * @retval PMIX_SUCCESS The value was successfully unloaded
341 *
342 * @retval PMIX_ERROR(s) An appropriate error code
343 */
344 typedef pmix_status_t (*pmix_bfrop_value_unload_fn_t)(pmix_value_t *kv,
345 void **data, size_t *sz);
346
347 /**
348 * Compare two pmix_value_t structs
349 */
350 typedef pmix_value_cmp_t (*pmix_bfrop_value_cmp_fn_t)(pmix_value_t *p1, pmix_value_t *p2);
351
352 /* define a component-level API for registering a new
353 * datatype, providing all the required functions */
354 typedef pmix_status_t (*pmix_bfrop_base_register_fn_t)(const char *name, pmix_data_type_t type,
355 pmix_bfrop_pack_fn_t pack,
356 pmix_bfrop_unpack_fn_t unpack,
357 pmix_bfrop_copy_fn_t copy,
358 pmix_bfrop_print_fn_t print);
359
360 /* return the string name of a provided data type */
361 typedef const char* (*pmix_bfrop_data_type_string_fn_t)(pmix_data_type_t type);
362
363 /**
364 * Base structure for a BFROP module
365 */
366 typedef struct {
367 char *version;
368 pmix_bfrop_init_fn_t init;
369 pmix_bfrop_finalize_fn_t finalize;
370 pmix_bfrop_pack_fn_t pack;
371 pmix_bfrop_unpack_fn_t unpack;
372 pmix_bfrop_copy_fn_t copy;
373 pmix_bfrop_print_fn_t print;
374 pmix_bfrop_copy_payload_fn_t copy_payload;
375 pmix_bfrop_value_xfer_fn_t value_xfer;
376 pmix_bfrop_value_load_fn_t value_load;
377 pmix_bfrop_value_unload_fn_t value_unload;
378 pmix_bfrop_value_cmp_fn_t value_cmp;
379 pmix_bfrop_base_register_fn_t register_type;
380 pmix_bfrop_data_type_string_fn_t data_type_string;
381 } pmix_bfrops_module_t;
382
383
384 /* get a list of available versions - caller must free results
385 * when done */
386 PMIX_EXPORT char* pmix_bfrops_base_get_available_modules(void);
387
388 /* Select a bfrops module for a given version */
389 PMIX_EXPORT pmix_bfrops_module_t* pmix_bfrops_base_assign_module(const char *version);
390
391 /* provide a backdoor to access the framework debug output */
392 PMIX_EXPORT extern int pmix_bfrops_base_output;
393
394 /* MACROS FOR EXECUTING BFROPS FUNCTIONS */
395 #define PMIX_BFROPS_ASSIGN_TYPE(p, b) \
396 (b)->type = (p)->nptr->compat.type
397
398 #define PMIX_BFROPS_PACK(r, p, b, s, n, t) \
399 do { \
400 pmix_output_verbose(2, pmix_bfrops_base_output, \
401 "[%s:%d] PACK version %s", \
402 __FILE__, __LINE__, \
403 (p)->nptr->compat.bfrops->version); \
404 if (PMIX_BFROP_BUFFER_UNDEF == (b)->type) { \
405 (b)->type = (p)->nptr->compat.type; \
406 (r) = (p)->nptr->compat.bfrops->pack(b, s, n, t); \
407 } else if ((b)->type == (p)->nptr->compat.type) { \
408 (r) = (p)->nptr->compat.bfrops->pack(b, s, n, t); \
409 } else { \
410 (r) = PMIX_ERR_PACK_MISMATCH; \
411 } \
412 } while(0)
413
414 #define PMIX_BFROPS_UNPACK(r, p, b, d, m, t) \
415 do { \
416 pmix_output_verbose(2, pmix_bfrops_base_output, \
417 "[%s:%d] UNPACK version %s", \
418 __FILE__, __LINE__, \
419 (p)->nptr->compat.bfrops->version); \
420 if ((b)->type == (p)->nptr->compat.type) { \
421 (r) = (p)->nptr->compat.bfrops->unpack(b, d, m, t); \
422 } else { \
423 (r) = PMIX_ERR_UNPACK_FAILURE; \
424 } \
425 } while(0)
426
427 #define PMIX_BFROPS_COPY(r, p, d, s, t) \
428 (r) = (p)->nptr->compat.bfrops->copy(d, s, t)
429
430 #define PMIX_BFROPS_PRINT(r, p, o, pr, s, t) \
431 (r) = (p)->nptr->compat.bfrops->print(o, pr, s, t)
432
433 #define PMIX_BFROPS_COPY_PAYLOAD(r, p, d, s) \
434 do { \
435 if (PMIX_BFROP_BUFFER_UNDEF == (d)->type) { \
436 (d)->type = (p)->nptr->compat.type; \
437 (r) = (p)->nptr->compat.bfrops->copy_payload(d, s); \
438 } else if ((d)->type == (p)->nptr->compat.type) { \
439 (r) = (p)->nptr->compat.bfrops->copy_payload(d, s); \
440 } else { \
441 (r) = PMIX_ERR_PACK_MISMATCH; \
442 } \
443 } while(0)
444
445 #define PMIX_BFROPS_VALUE_XFER(r, p, d, s) \
446 (r) = (p)->nptr->compat.bfrops->value_xfer(d, s)
447
448 #define PMIX_BFROPS_VALUE_LOAD(p, v, d, t) \
449 (p)->nptr->compat.bfrops->value_load(v, d, t)
450
451 #define PMIX_BFROPS_VALUE_UNLOAD(r, p, k, d, s) \
452 (r) = (p)->nptr->compat.bfrops->value_unload(k,, d, s)
453
454 #define PMIX_BFROPS_VALUE_CMP(r, p, q, s) \
455 (r) = (p)->nptr->compat.bfrops->value_cmp(q, s)
456
457 #define PMIX_BFROPS_REGISTER(r, p, n, t, pk, u, c, pr) \
458 (r) = (p)->nptr->compat.bfrops->register_type(n, t, pk, u, c, pr)
459
460 #define PMIX_BFROPS_PRINT_TYPE(c, p, t) \
461 (c) = (p)->nptr->compat.bfrops->data_type_string(t)
462
463
464 /**** COMPONENT STRUCTURE DEFINITION ****/
465
466 /* define a component-level API for getting a module */
467 typedef pmix_bfrops_module_t* (*pmix_bfrop_base_component_assign_module_fn_t)(void);
468
469 /*
470 * the standard component data structure
471 */
472 struct pmix_bfrops_base_component_t {
473 pmix_mca_base_component_t base;
474 pmix_mca_base_component_data_t data;
475 int priority;
476 pmix_pointer_array_t types;
477 pmix_bfrop_base_component_assign_module_fn_t assign_module;
478 };
479 typedef struct pmix_bfrops_base_component_t pmix_bfrops_base_component_t;
480
481 /*
482 * Macro for use in components that are of type bfrops
483 */
484 #define PMIX_BFROPS_BASE_VERSION_1_0_0 \
485 PMIX_MCA_BASE_VERSION_1_0_0("bfrops", 1, 0, 0)
486
487 END_C_DECLS
488
489 #endif /* PMIX_BFROP_H */