1 /*
2 * Copyright (c) 2019 IBM Corporation. All rights reserved.
3 * Copyright (c) 2019 Mellanox Technologies, Inc.
4 * All rights reserved.
5 *
6 * $COPYRIGHT$
7 *
8 * Additional copyrights may follow
9 *
10 * $HEADER$
11 */
12
13 #include <src/include/pmix_config.h>
14
15 #include <pmix_common.h>
16
17 #include "src/include/pmix_socket_errno.h"
18 #include "src/include/pmix_globals.h"
19 #include "src/util/argv.h"
20 #include "src/util/error.h"
21 #include "src/util/output.h"
22
23 #include <unistd.h>
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27
28 #include "src/mca/psquash/base/base.h"
29 #include "psquash_flex128.h"
30
31 /* Flexible packing constants */
32 #define FLEX_BASE7_MAX_BUF_SIZE (SIZEOF_SIZE_T+1)
33 #define FLEX_BASE7_MASK ((1<<7) - 1)
34 #define FLEX_BASE7_SHIFT 7
35 #define FLEX_BASE7_CONT_FLAG (1<<7)
36
37 /**
38 * Packing conversion of a signed integer value to a flexible representation.
39 * The main idea is to split a signed negative value onto an absolute value
40 * and a sign bit stored in the special location.
41 * This allows efficient representetion of negative values in the
42 * flexible form.
43 *
44 * type - type (pmix_data_type_t) of integer value
45 * ptr - pointer to the signed integer value
46 * with the type defined as (type)
47 * out - flexible representation of *ptr,
48 * extended to uint64_t if needed
49 * (see a comment to `pmix_bfrops_pack_flex` for additional details)
50 */
51 #define FLEX128_PACK_CONVERT_SIGNED(type, ptr, out) \
52 do { \
53 type __tbuf = 0; \
54 size_t __tmp; \
55 int __sign = 0; \
56 memcpy(&__tbuf, (ptr), sizeof(type)); \
57 __tmp = __tbuf; \
58 (out) = (size_t)__tmp; \
59 if (__tmp & (1UL << (sizeof(__tmp)*CHAR_BIT-1))) { \
60 __sign = 1; \
61 out = ~(out); \
62 } \
63 (out) = ((out) << 1) + __sign; \
64 } while (0)
65
66 /**
67 * Packing conversion of a signed integer value to a flexible representation.
68 * For unsigned types it is reduced to a memcopy.
69 *
70 * type - usual integer C-type of integer value
71 * ptr - pointer to the signed integer value
72 * with the type defined as (type)
73 * out - flexible representation of *ptr,
74 * extended to uint64_t if needed
75 * (see a comment to `pmix_bfrops_pack_flex` for additional details)
76 */
77 #define FLEX128_PACK_CONVERT_UNSIGNED(type, ptr, out) \
78 do { \
79 type __tbuf = 0; \
80 memcpy(&__tbuf, (ptr), sizeof(type)); \
81 out = __tbuf; \
82 } while (0)
83
84 /**
85 * Packing conversion from integer value to a flexible representation.
86 *
87 * r - return status code
88 * t - type (pmix_data_type_t) of integer value, it is determines
89 * which type of integer is converted
90 * s - pointer to the integer value with the type defined as (t)
91 * d - flexible representation output value (uin64_t)
92 * (see a comment to `pmix_bfrops_pack_flex` for additional details)
93 */
94 #define FLEX128_PACK_CONVERT(r, t, s, d) \
95 do { \
96 (r) = PMIX_SUCCESS; \
97 switch (t) { \
98 case PMIX_INT16: \
99 FLEX128_PACK_CONVERT_SIGNED(int16_t, s, d); \
100 break; \
101 case PMIX_UINT16: \
102 FLEX128_PACK_CONVERT_UNSIGNED(uint16_t, s, d); \
103 break; \
104 case PMIX_INT: \
105 case PMIX_INT32: \
106 FLEX128_PACK_CONVERT_SIGNED(int32_t, s, d); \
107 break; \
108 case PMIX_UINT: \
109 case PMIX_UINT32: \
110 FLEX128_PACK_CONVERT_UNSIGNED(uint32_t, s, d); \
111 break; \
112 case PMIX_INT64: \
113 FLEX128_PACK_CONVERT_SIGNED(int64_t, s, d); \
114 break; \
115 case PMIX_SIZE: \
116 FLEX128_PACK_CONVERT_UNSIGNED(size_t, s, d); \
117 break; \
118 case PMIX_UINT64: \
119 FLEX128_PACK_CONVERT_UNSIGNED(uint64_t, s, d); \
120 break; \
121 default: \
122 (r) = PMIX_ERR_BAD_PARAM; \
123 } \
124 } while(0)
125
126 /**
127 * Unpacking conversion from a flexible representation to a
128 * signed integer value.
129 *
130 * type - C-type of a signed integer value
131 * val - flexible representation (uint64_t)
132 * ptr - pointer to a 64-bit output buffer for the upacked value
133 * (see a comment to `pmix_bfrops_pack_flex` for additional details)
134 */
135 #define FLEX128_UNPACK_CONVERT_SIGNED(type, val, ptr) \
136 do { \
137 type __tbuf = 0; \
138 size_t __tmp = val; \
139 int sign = (__tmp) & 1; \
140 __tmp >>= 1; \
141 if (sign) { \
142 __tmp = ~__tmp; \
143 } \
144 __tbuf = (type)__tmp; \
145 memcpy(ptr, &__tbuf, sizeof(type)); \
146 } while (0)
147
148 /**
149 * Unpacking conversion of a flexible representation value
150 * to an unsigned integer.
151 *
152 * type - C-type of unsigned integer value
153 * val - flexible representation value (uint64_t)
154 * ptr - pointer to a 64-bit output buffer for the upacked value
155 * (see a comment to `pmix_bfrops_pack_flex` for additional details)
156 */
157 #define FLEX128_UNPACK_CONVERT_UNSIGNED(type, val, ptr) \
158 do { \
159 type __tbuf = 0; \
160 __tbuf = (type)val; \
161 memcpy(ptr, &__tbuf, sizeof(type)); \
162 } while (0)
163
164 /**
165 * Unpacking conversion of a flexible representation value
166 * to an integer.
167 *
168 * r - return status code
169 * t - type (pmix_data_type_t) of integer value, it is determines
170 * which type of integer is converted
171 * s - flex-representation value (uin64_t)
172 * d - pointer to a 64-bit output buffer for the upacked value
173 * (see a comment to `pmix_bfrops_pack_flex` for additional details)
174 */
175 #define FLEX128_UNPACK_CONVERT(r, t, s, d) \
176 do { \
177 (r) = PMIX_SUCCESS; \
178 switch (t) { \
179 case PMIX_INT16: \
180 FLEX128_UNPACK_CONVERT_SIGNED(int16_t, s, d); \
181 break; \
182 case PMIX_UINT16: \
183 FLEX128_UNPACK_CONVERT_UNSIGNED(uint16_t, s, d); \
184 break; \
185 case PMIX_INT: \
186 case PMIX_INT32: \
187 FLEX128_UNPACK_CONVERT_SIGNED(int32_t, s, d); \
188 break; \
189 case PMIX_UINT: \
190 case PMIX_UINT32: \
191 FLEX128_UNPACK_CONVERT_UNSIGNED(uint32_t, s, d); \
192 break; \
193 case PMIX_INT64: \
194 FLEX128_UNPACK_CONVERT_SIGNED(int64_t, s, d); \
195 break; \
196 case PMIX_SIZE: \
197 FLEX128_UNPACK_CONVERT_UNSIGNED(size_t, s, d); \
198 break; \
199 case PMIX_UINT64: \
200 FLEX128_UNPACK_CONVERT_UNSIGNED(uint64_t, s, d); \
201 break; \
202 default: \
203 (r) = PMIX_ERR_BAD_PARAM; \
204 } \
205 } while(0)
206
207 static pmix_status_t flex128_init(void);
208
209 static void flex128_finalize(void);
210
211 static pmix_status_t flex128_get_max_size(pmix_data_type_t type, size_t *size);
212
213 static pmix_status_t flex128_encode_int(pmix_data_type_t type, void *src,
214 void *dst, size_t *size);
215
216 static pmix_status_t flex128_decode_int(pmix_data_type_t type, void *src,
217 size_t src_len, void *dest,
218 size_t *dst_size);
219
220 static size_t flex_pack_integer(size_t val,
221 uint8_t out_buf[FLEX_BASE7_MAX_BUF_SIZE]);
222
223 static size_t flex_unpack_integer(const uint8_t in_buf[], size_t buf_size,
224 size_t *out_val, size_t *out_val_size);
225
226 pmix_psquash_base_module_t pmix_flex128_module = {
227 .name = "flex128",
228 .int_type_is_encoded = true,
229 .init = flex128_init,
230 .finalize = flex128_finalize,
231 .get_max_size = flex128_get_max_size,
232 .encode_int = flex128_encode_int,
233 .decode_int = flex128_decode_int
234 };
235
236
237 static pmix_status_t flex128_init(void)
238 {
239 pmix_output_verbose(2, pmix_globals.debug_output,
240 "psquash: flex128 init");
241 return PMIX_SUCCESS;
242 }
243
244 static void flex128_finalize(void)
245 {
246 pmix_output_verbose(2, pmix_globals.debug_output,
247 "psquash: flex128 finalize");
248 }
249
250 static pmix_status_t flex128_get_max_size(pmix_data_type_t type, size_t *size)
251 {
252 pmix_status_t rc;
253 PMIX_SQUASH_TYPE_SIZEOF(rc, type, *size);
254 /* the size of the packed value can be 1B larger
255 * because of continuation flags */
256 *size += 1;
257 return rc;
258 }
259
260 static pmix_status_t flex128_encode_int(pmix_data_type_t type, void *src,
261 void *dst, size_t *size)
262 {
263 pmix_status_t rc = PMIX_SUCCESS;
264 uint8_t tmp_buf[FLEX_BASE7_MAX_BUF_SIZE];
265 uint64_t tmp;
266
267 FLEX128_PACK_CONVERT(rc, type, (uint8_t*)src, tmp);
268 if (PMIX_SUCCESS != rc) {
269 PMIX_ERROR_LOG(rc);
270 return rc;
271 }
272 *size = flex_pack_integer(tmp, tmp_buf);
273 memcpy(dst, tmp_buf, *size);
274
275 return rc;
276 }
277
278 static pmix_status_t flex128_decode_int(pmix_data_type_t type, void *src,
279 size_t src_len, void *dest, size_t *dst_size)
280 {
281 pmix_status_t rc = PMIX_SUCCESS;
282 size_t tmp;
283 size_t val_size, unpack_val_size;
284
285 PMIX_SQUASH_TYPE_SIZEOF(rc, type, val_size);
286 if (PMIX_SUCCESS != rc) {
287 PMIX_ERROR_LOG(rc);
288 return rc;
289 }
290 *dst_size = flex_unpack_integer(src, src_len, &tmp, &unpack_val_size);
291
292 if( val_size < unpack_val_size ) { // sanity check
293 rc = PMIX_ERR_UNPACK_FAILURE;
294 PMIX_ERROR_LOG(rc);
295 return rc;
296 }
297 FLEX128_UNPACK_CONVERT(rc, type, tmp, (uint8_t*)dest);
298 if (PMIX_SUCCESS != rc) {
299 PMIX_ERROR_LOG(rc);
300 return rc;
301 }
302
303 return rc;
304 }
305
306 /*
307 * Typical representation of a number in computer systems is:
308 * A[0]*B^0 + A[1]*B^1 + A[2]*B^2 + ... + A[n]*B^n
309 * where B called a base and B == 256 (one byte)
310 *
311 * This encoding changes the default representation by introducing an additional
312 * bit per each byte to store a "continuation flag". So integers are now encoded
313 * with the same representation, but the base B = 128 and the remaning bit is
314 * used to indicate whether or not the next byte contains more bits of this value.
315 */
316 static size_t flex_pack_integer(size_t val,
317 uint8_t out_buf[FLEX_BASE7_MAX_BUF_SIZE])
318 {
319 size_t tmp = val;
320 size_t idx = 0;
321
322 do {
323 uint8_t val = tmp & FLEX_BASE7_MASK;
324 tmp >>= FLEX_BASE7_SHIFT;
325 if (PMIX_UNLIKELY(tmp)) {
326 val |= FLEX_BASE7_CONT_FLAG;
327 }
328 out_buf[idx++] = val;
329 } while(tmp && idx < SIZEOF_SIZE_T);
330
331 /* If we have leftover (VERY unlikely) */
332 if (PMIX_UNLIKELY(SIZEOF_SIZE_T == idx && tmp)) {
333 out_buf[idx++] = tmp;
334 }
335
336 return idx;
337 }
338
339 /*
340 * See a comment to `pmix_bfrops_pack_flex` for additional details.
341 */
342 static size_t flex_unpack_integer(const uint8_t in_buf[], size_t buf_size,
343 size_t *out_val, size_t *out_val_size)
344 {
345 size_t value = 0, shift = 0, shift_last = 0;
346 size_t idx = 0;
347 uint8_t val = 0, val_last = 0;
348 uint8_t hi_bit = 0;
349 size_t flex_size = buf_size;
350
351 /* restrict the buf size to max flex size */
352 if (buf_size > FLEX_BASE7_MAX_BUF_SIZE) {
353 flex_size = FLEX_BASE7_MAX_BUF_SIZE;
354 }
355
356 do {
357 val = in_buf[idx++];
358 val_last = val;
359 shift_last = shift;
360 value = value + (((uint64_t)val & FLEX_BASE7_MASK) << shift);
361 shift += FLEX_BASE7_SHIFT;
362 } while(PMIX_UNLIKELY((val & FLEX_BASE7_CONT_FLAG) &&
363 (idx < (flex_size-1))));
364 /* If we have leftover (VERY unlikely) */
365 if (PMIX_UNLIKELY((flex_size-1) == idx &&
366 (val & FLEX_BASE7_CONT_FLAG))) {
367 val = in_buf[idx++];
368 val_last = val;
369 value = value + ((uint64_t)val << shift);
370 shift_last = shift;
371 }
372 /* compute the most significant bit of val */
373 while (val_last != 0) {
374 val_last >>= 1;
375 hi_bit++;
376 }
377 /* compute the real val size */
378 *out_val_size = (hi_bit + shift_last)/CHAR_BIT +
379 !!((hi_bit + shift_last) & (CHAR_BIT - 1));
380 *out_val = value;
381
382 return idx;
383 }