This source file includes following definitions.
- opal_pmix_base_set_evbase
- opal_pmix_base_register_handler
- opal_pmix_base_evhandler
- opal_pmix_base_deregister_handler
- opal_pmix_base_notify_event
- opal_pmix_base_exchange
- opal_pmix_base_store_encoded
- opal_pmix_base_commit_packed
- opal_pmix_base_partial_commit_packed
- opal_pmix_base_get_packed
- opal_pmix_base_cache_keys_locally
- setup_key
- pmi_base64_encsym
- pmi_base64_decsym
- pmi_base64_encode_block
- pmi_base64_decode_block
- pmi_encode
- pmi_decode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #include "opal_config.h"
20 #include "opal/constants.h"
21
22
23 #include <regex.h>
24
25 #include <time.h>
26 #include <string.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #include "opal_stdint.h"
32 #include "opal/class/opal_pointer_array.h"
33 #include "opal/util/argv.h"
34 #include "opal/util/output.h"
35 #include "opal/util/proc.h"
36 #include "opal/util/show_help.h"
37
38 #include "opal/mca/pmix/base/base.h"
39 #include "opal/mca/pmix/base/pmix_base_fns.h"
40 #include "opal/mca/pmix/base/pmix_base_hash.h"
41
42 #define OPAL_PMI_PAD 10
43
44 void opal_pmix_base_set_evbase(opal_event_base_t *evbase)
45 {
46 opal_pmix_base.evbase = evbase;
47 }
48
49
50
51
52 static opal_pmix_notification_fn_t evhandler = NULL;
53
54 void opal_pmix_base_register_handler(opal_list_t *event_codes,
55 opal_list_t *info,
56 opal_pmix_notification_fn_t err,
57 opal_pmix_evhandler_reg_cbfunc_t cbfunc,
58 void *cbdata)
59 {
60 evhandler = err;
61 if (NULL != cbfunc) {
62 cbfunc(OPAL_SUCCESS, 0, cbdata);
63 }
64 }
65
66 void opal_pmix_base_evhandler(int status,
67 const opal_process_name_t *source,
68 opal_list_t *info, opal_list_t *results,
69 opal_pmix_notification_complete_fn_t cbfunc, void *cbdata)
70 {
71 if (NULL != evhandler) {
72 evhandler(status, source, info, results, cbfunc, cbdata);
73 }
74 }
75
76 void opal_pmix_base_deregister_handler(size_t errid,
77 opal_pmix_op_cbfunc_t cbfunc,
78 void *cbdata)
79 {
80 evhandler = NULL;
81 if (NULL != cbfunc) {
82 cbfunc(OPAL_SUCCESS, cbdata);
83 }
84 }
85
86 int opal_pmix_base_notify_event(int status,
87 const opal_process_name_t *source,
88 opal_pmix_data_range_t range,
89 opal_list_t *info,
90 opal_pmix_op_cbfunc_t cbfunc, void *cbdata)
91 {
92 return OPAL_SUCCESS;
93 }
94
95 int opal_pmix_base_exchange(opal_value_t *indat,
96 opal_pmix_pdata_t *outdat,
97 int timeout)
98 {
99 int rc;
100 opal_list_t ilist, mlist;
101 opal_value_t *info;
102 opal_pmix_pdata_t *pdat;
103
104
105 opal_dss.copy((void**)&info, indat, OPAL_VALUE);
106 OBJ_CONSTRUCT(&ilist, opal_list_t);
107 opal_list_append(&ilist, &info->super);
108
109 info = OBJ_NEW(opal_value_t);
110 info->key = strdup(OPAL_PMIX_PERSISTENCE);
111 info->type = OPAL_PERSIST;
112 info->data.integer = OPAL_PMIX_PERSIST_FIRST_READ;
113 opal_list_append(&ilist, &info->super);
114
115
116 rc = opal_pmix.publish(&ilist);
117 OPAL_LIST_DESTRUCT(&ilist);
118 if (OPAL_SUCCESS != rc) {
119 return rc;
120 }
121
122
123
124
125
126 pdat = OBJ_NEW(opal_pmix_pdata_t);
127 pdat->value.key = strdup(outdat->value.key);
128 pdat->value.type = outdat->value.type;
129
130 OBJ_CONSTRUCT(&mlist, opal_list_t);
131
132 info = OBJ_NEW(opal_value_t);
133 info->key = strdup(OPAL_PMIX_WAIT);
134 info->type = OPAL_BOOL;
135 info->data.flag = true;
136 opal_list_append(&mlist, &info->super);
137
138
139
140 info = OBJ_NEW(opal_value_t);
141 info->key = strdup(OPAL_PMIX_TIMEOUT);
142 info->type = OPAL_INT;
143 if (0 < opal_pmix_base.timeout) {
144
145 info->data.integer = opal_pmix_base.timeout;
146 } else {
147 info->data.integer = timeout;
148 }
149 opal_list_append(&mlist, &info->super);
150
151
152
153 OBJ_CONSTRUCT(&ilist, opal_list_t);
154 opal_list_append(&ilist, &pdat->super);
155 rc = opal_pmix.lookup(&ilist, &mlist);
156 OPAL_LIST_DESTRUCT(&mlist);
157 if (OPAL_SUCCESS != rc) {
158 OPAL_LIST_DESTRUCT(&ilist);
159 return rc;
160 }
161
162
163 outdat->proc = pdat->proc;
164 free(outdat->value.key);
165 rc = opal_value_xfer(&outdat->value, &pdat->value);
166 OPAL_LIST_DESTRUCT(&ilist);
167 return rc;
168 }
169
170
171
172
173 static char* setup_key(const opal_process_name_t* name, const char *key, int pmix_keylen_max);
174 static char *pmi_encode(const void *val, size_t vallen);
175 static uint8_t *pmi_decode (const char *data, size_t *retlen);
176
177 int opal_pmix_base_store_encoded(const char *key, const void *data,
178 opal_data_type_t type, char** buffer, int* length)
179 {
180 opal_byte_object_t *bo;
181 size_t data_len = 0;
182 size_t needed;
183
184 int pmi_packed_data_off = *length;
185 char* pmi_packed_data = *buffer;
186
187 switch (type) {
188 case OPAL_STRING:
189 {
190 char *ptr = *(char **)data;
191 data_len = ptr ? strlen(ptr) + 1 : 0;
192 data = ptr;
193 break;
194 }
195 case OPAL_INT:
196 case OPAL_UINT:
197 data_len = sizeof (int);
198 break;
199 case OPAL_INT16:
200 case OPAL_UINT16:
201 data_len = sizeof (int16_t);
202 break;
203 case OPAL_INT32:
204 case OPAL_UINT32:
205 data_len = sizeof (int32_t);
206 break;
207 case OPAL_INT64:
208 case OPAL_UINT64:
209 data_len = sizeof (int64_t);
210 break;
211 case OPAL_BYTE_OBJECT:
212 bo = (opal_byte_object_t *) data;
213 data = bo->bytes;
214 data_len = bo->size;
215 }
216
217 needed = 10 + data_len + strlen (key);
218
219 if (NULL == pmi_packed_data) {
220 pmi_packed_data = calloc (needed, 1);
221 } else {
222
223 pmi_packed_data = realloc (pmi_packed_data, pmi_packed_data_off + needed);
224 }
225
226
227 if (NULL == data) {
228 data_len = 0xffff;
229 }
230
231
232 pmi_packed_data_off += sprintf (pmi_packed_data + pmi_packed_data_off,
233 "%s%c%02x%c%04x%c", key, '\0', type, '\0',
234 (int) data_len, '\0');
235 if (NULL != data) {
236 memmove (pmi_packed_data + pmi_packed_data_off, data, data_len);
237 pmi_packed_data_off += data_len;
238 }
239
240 *length = pmi_packed_data_off;
241 *buffer = pmi_packed_data;
242 return OPAL_SUCCESS;
243 }
244
245 int opal_pmix_base_commit_packed( char** data, int* data_offset,
246 char** enc_data, int* enc_data_offset,
247 int max_key, int* pack_key, kvs_put_fn fn)
248 {
249 int rc;
250 char *pmikey = NULL, *tmp;
251 char tmp_key[32];
252 char *encoded_data;
253 int encoded_data_len;
254 int data_len;
255 int pkey;
256
257 pkey = *pack_key;
258
259 if (NULL == (tmp = malloc(max_key))) {
260 OPAL_ERROR_LOG(OPAL_ERR_OUT_OF_RESOURCE);
261 return OPAL_ERR_OUT_OF_RESOURCE;
262 }
263 data_len = *data_offset;
264 if (NULL == (encoded_data = pmi_encode(*data, data_len))) {
265 OPAL_ERROR_LOG(OPAL_ERR_OUT_OF_RESOURCE);
266 free(tmp);
267 return OPAL_ERR_OUT_OF_RESOURCE;
268 }
269 *data = NULL;
270 *data_offset = 0;
271
272 encoded_data_len = (int)strlen(encoded_data);
273 while (encoded_data_len+*enc_data_offset > max_key - 2) {
274 memcpy(tmp, *enc_data, *enc_data_offset);
275 memcpy(tmp+*enc_data_offset, encoded_data, max_key-*enc_data_offset-1);
276 tmp[max_key-1] = 0;
277
278 sprintf (tmp_key, "key%d", pkey);
279
280 if (NULL == (pmikey = setup_key(&OPAL_PROC_MY_NAME, tmp_key, max_key))) {
281 OPAL_ERROR_LOG(OPAL_ERR_BAD_PARAM);
282 rc = OPAL_ERR_BAD_PARAM;
283 break;
284 }
285
286 rc = fn(pmikey, tmp);
287 free(pmikey);
288 if (OPAL_SUCCESS != rc) {
289 *pack_key = pkey;
290 free(tmp);
291 free(encoded_data);
292 return rc;
293 }
294
295 pkey++;
296 memmove(encoded_data, encoded_data+max_key-1-*enc_data_offset, encoded_data_len - max_key + *enc_data_offset + 2);
297 *enc_data_offset = 0;
298 encoded_data_len = (int)strlen(encoded_data);
299 }
300 memcpy(tmp, *enc_data, *enc_data_offset);
301 memcpy(tmp+*enc_data_offset, encoded_data, encoded_data_len+1);
302 tmp[*enc_data_offset+encoded_data_len+1] = '\0';
303 tmp[*enc_data_offset+encoded_data_len] = '-';
304 free(encoded_data);
305
306 sprintf (tmp_key, "key%d", pkey);
307
308 if (NULL == (pmikey = setup_key(&OPAL_PROC_MY_NAME, tmp_key, max_key))) {
309 OPAL_ERROR_LOG(OPAL_ERR_BAD_PARAM);
310 rc = OPAL_ERR_BAD_PARAM;
311 free(tmp);
312 return rc;
313 }
314
315 rc = fn(pmikey, tmp);
316 free(pmikey);
317 if (OPAL_SUCCESS != rc) {
318 *pack_key = pkey;
319 free(tmp);
320 return rc;
321 }
322
323 pkey++;
324 free(*data);
325 *data = NULL;
326 *data_offset = 0;
327 free(tmp);
328 if (NULL != *enc_data) {
329 free(*enc_data);
330 *enc_data = NULL;
331 *enc_data_offset = 0;
332 }
333 *pack_key = pkey;
334 return OPAL_SUCCESS;
335 }
336
337 int opal_pmix_base_partial_commit_packed( char** data, int* data_offset,
338 char** enc_data, int* enc_data_offset,
339 int max_key, int* pack_key, kvs_put_fn fn)
340 {
341 int rc;
342 char *pmikey = NULL, *tmp;
343 char tmp_key[32];
344 char *encoded_data;
345 int encoded_data_len;
346 int data_len;
347 int pkey;
348
349 pkey = *pack_key;
350
351 if (NULL == (tmp = malloc(max_key))) {
352 OPAL_ERROR_LOG(OPAL_ERR_OUT_OF_RESOURCE);
353 return OPAL_ERR_OUT_OF_RESOURCE;
354 }
355 data_len = *data_offset - (*data_offset%3);
356 if (NULL == (encoded_data = pmi_encode(*data, data_len))) {
357 OPAL_ERROR_LOG(OPAL_ERR_OUT_OF_RESOURCE);
358 free(tmp);
359 return OPAL_ERR_OUT_OF_RESOURCE;
360 }
361 if (*data_offset == data_len) {
362 *data = NULL;
363 *data_offset = 0;
364 } else {
365 memmove(*data, *data+data_len, *data_offset - data_len);
366 *data = realloc(*data, *data_offset - data_len);
367 *data_offset -= data_len;
368 }
369
370 encoded_data_len = (int)strlen(encoded_data);
371 while (encoded_data_len+*enc_data_offset > max_key - 2) {
372 memcpy(tmp, *enc_data, *enc_data_offset);
373 memcpy(tmp+*enc_data_offset, encoded_data, max_key-*enc_data_offset-1);
374 tmp[max_key-1] = 0;
375
376 sprintf (tmp_key, "key%d", pkey);
377
378 if (NULL == (pmikey = setup_key(&OPAL_PROC_MY_NAME, tmp_key, max_key))) {
379 OPAL_ERROR_LOG(OPAL_ERR_BAD_PARAM);
380 rc = OPAL_ERR_BAD_PARAM;
381 break;
382 }
383
384 rc = fn(pmikey, tmp);
385 free(pmikey);
386 if (OPAL_SUCCESS != rc) {
387 *pack_key = pkey;
388 free(tmp);
389 free(encoded_data);
390 return rc;
391 }
392
393 pkey++;
394 memmove(encoded_data, encoded_data+max_key-1-*enc_data_offset, encoded_data_len - max_key + *enc_data_offset + 2);
395 *enc_data_offset = 0;
396 encoded_data_len = (int)strlen(encoded_data);
397 }
398 free(tmp);
399 if (NULL != *enc_data) {
400 free(*enc_data);
401 }
402 *enc_data = realloc(encoded_data, strlen(encoded_data)+1);
403 *enc_data_offset = strlen(encoded_data);
404 *pack_key = pkey;
405 return OPAL_SUCCESS;
406 }
407
408 int opal_pmix_base_get_packed(const opal_process_name_t* proc, char **packed_data,
409 size_t *len, int vallen, kvs_get_fn fn)
410 {
411 char *tmp_encoded = NULL, *pmikey, *pmi_tmp;
412 int remote_key, size;
413 size_t bytes_read;
414 int rc = OPAL_ERR_NOT_FOUND;
415
416
417 *packed_data = NULL;
418 *len = 0;
419
420 pmi_tmp = calloc (vallen, 1);
421 if (NULL == pmi_tmp) {
422 return OPAL_ERR_OUT_OF_RESOURCE;
423 }
424
425
426 for (remote_key = 0, bytes_read = 0 ; ; ++remote_key) {
427 char tmp_key[32];
428
429 sprintf (tmp_key, "key%d", remote_key);
430
431 if (NULL == (pmikey = setup_key(proc, tmp_key, vallen))) {
432 rc = OPAL_ERR_OUT_OF_RESOURCE;
433 OPAL_ERROR_LOG(rc);
434 free(pmi_tmp);
435 if (NULL != tmp_encoded) {
436 free(tmp_encoded);
437 }
438 return rc;
439 }
440
441 OPAL_OUTPUT_VERBOSE((10, opal_pmix_base_framework.framework_output,
442 "GETTING KEY %s", pmikey));
443
444 rc = fn(pmikey, pmi_tmp, vallen);
445 free (pmikey);
446 if (OPAL_SUCCESS != rc) {
447 break;
448 }
449
450 size = strlen (pmi_tmp);
451
452 if (NULL == tmp_encoded) {
453 tmp_encoded = malloc (size + 1);
454 } else {
455 tmp_encoded = realloc (tmp_encoded, bytes_read + size + 1);
456 }
457
458 strcpy (tmp_encoded + bytes_read, pmi_tmp);
459 bytes_read += size;
460
461
462 if ('-' == tmp_encoded[bytes_read-1]) {
463 break;
464 }
465 }
466
467 free (pmi_tmp);
468
469 OPAL_OUTPUT_VERBOSE((10, opal_pmix_base_framework.framework_output,
470 "Read data %s\n",
471 (NULL == tmp_encoded) ? "NULL" : tmp_encoded));
472
473 if (NULL != tmp_encoded) {
474 *packed_data = (char *) pmi_decode (tmp_encoded, len);
475 free (tmp_encoded);
476 if (NULL == *packed_data) {
477 return OPAL_ERR_OUT_OF_RESOURCE;
478 }
479 }
480
481 return rc;
482 }
483
484 int opal_pmix_base_cache_keys_locally(const opal_process_name_t* id, const char* key,
485 opal_value_t **out_kv, char* kvs_name,
486 int vallen, kvs_get_fn fn)
487 {
488 char *tmp, *tmp2, *tmp3, *tmp_val;
489 opal_data_type_t stored_type;
490 size_t len, offset;
491 int rc, size;
492 opal_value_t *kv, *knew;
493 opal_list_t values;
494
495
496 *out_kv = NULL;
497
498
499 OBJ_CONSTRUCT(&values, opal_list_t);
500 rc = opal_pmix_base_fetch(id, key, &values);
501 if (OPAL_SUCCESS == rc) {
502 kv = (opal_value_t*)opal_list_get_first(&values);
503
504 if (OPAL_SUCCESS != (rc = opal_dss.copy((void**)&knew, kv, OPAL_VALUE))) {
505 OPAL_ERROR_LOG(rc);
506 } else {
507 *out_kv = knew;
508 }
509 OPAL_LIST_DESTRUCT(&values);
510 return rc;
511 }
512 OPAL_LIST_DESTRUCT(&values);
513
514 OPAL_OUTPUT_VERBOSE((1, opal_pmix_base_framework.framework_output,
515 "pmix: get all keys for proc %s in KVS %s",
516 OPAL_NAME_PRINT(*id), kvs_name));
517
518 rc = opal_pmix_base_get_packed(id, &tmp_val, &len, vallen, fn);
519 if (OPAL_SUCCESS != rc) {
520 return rc;
521 }
522
523
524 for (offset = 0 ; offset < len ; ) {
525
526 tmp = tmp_val + offset + strlen (tmp_val + offset) + 1;
527
528 tmp2 = tmp + strlen (tmp) + 1;
529
530 tmp3 = tmp2 + strlen (tmp2) + 1;
531
532 stored_type = (opal_data_type_t) strtol (tmp, NULL, 16);
533 size = strtol (tmp2, NULL, 16);
534
535 kv = OBJ_NEW(opal_value_t);
536 kv->key = strdup(tmp_val + offset);
537 kv->type = stored_type;
538
539 switch (stored_type) {
540 case OPAL_BYTE:
541 kv->data.byte = *tmp3;
542 break;
543 case OPAL_STRING:
544 kv->data.string = strdup(tmp3);
545 break;
546 case OPAL_PID:
547 kv->data.pid = strtoul(tmp3, NULL, 10);
548 break;
549 case OPAL_INT:
550 kv->data.integer = strtol(tmp3, NULL, 10);
551 break;
552 case OPAL_INT8:
553 kv->data.int8 = strtol(tmp3, NULL, 10);
554 break;
555 case OPAL_INT16:
556 kv->data.int16 = strtol(tmp3, NULL, 10);
557 break;
558 case OPAL_INT32:
559 kv->data.int32 = strtol(tmp3, NULL, 10);
560 break;
561 case OPAL_INT64:
562 kv->data.int64 = strtol(tmp3, NULL, 10);
563 break;
564 case OPAL_UINT:
565 kv->data.uint = strtoul(tmp3, NULL, 10);
566 break;
567 case OPAL_UINT8:
568 kv->data.uint8 = strtoul(tmp3, NULL, 10);
569 break;
570 case OPAL_UINT16:
571 kv->data.uint16 = strtoul(tmp3, NULL, 10);
572 break;
573 case OPAL_UINT32:
574 kv->data.uint32 = strtoul(tmp3, NULL, 10);
575 break;
576 case OPAL_UINT64:
577 kv->data.uint64 = strtoull(tmp3, NULL, 10);
578 break;
579 case OPAL_BYTE_OBJECT:
580 if (size == 0xffff) {
581 kv->data.bo.bytes = NULL;
582 kv->data.bo.size = 0;
583 size = 0;
584 } else {
585 kv->data.bo.bytes = malloc(size);
586 memcpy(kv->data.bo.bytes, tmp3, size);
587 kv->data.bo.size = size;
588 }
589 break;
590 default:
591 opal_output(0, "UNSUPPORTED TYPE %d", stored_type);
592 return OPAL_ERROR;
593 }
594
595 if (OPAL_SUCCESS != (rc = opal_pmix_base_store(id, kv))) {
596 OPAL_ERROR_LOG(rc);
597 }
598
599 offset = (size_t) (tmp3 - tmp_val) + size;
600 if (0 == strcmp(kv->key, key)) {
601
602 if (OPAL_SUCCESS != (rc = opal_dss.copy((void**)&knew, kv, OPAL_VALUE))) {
603 OPAL_ERROR_LOG(rc);
604 } else {
605 *out_kv = knew;
606 }
607 }
608 }
609 free (tmp_val);
610
611
612
613 if (OPAL_SUCCESS == rc && NULL == *out_kv) {
614 return OPAL_ERR_NOT_FOUND;
615 }
616 return rc;
617 }
618
619 static char* setup_key(const opal_process_name_t* name, const char *key, int pmix_keylen_max)
620 {
621 char *pmi_kvs_key;
622
623 if (pmix_keylen_max <= asprintf(&pmi_kvs_key, "%" PRIu32 "-%" PRIu32 "-%s",
624 name->jobid, name->vpid, key)) {
625 free(pmi_kvs_key);
626 return NULL;
627 }
628
629 return pmi_kvs_key;
630 }
631
632
633 static inline unsigned char pmi_base64_encsym (unsigned char value) {
634 assert (value < 64);
635
636 if (value < 26) {
637 return 'A' + value;
638 } else if (value < 52) {
639 return 'a' + (value - 26);
640 } else if (value < 62) {
641 return '0' + (value - 52);
642 }
643
644 return (62 == value) ? '+' : '/';
645 }
646
647 static inline unsigned char pmi_base64_decsym (unsigned char value) {
648 if ('+' == value) {
649 return 62;
650 } else if ('/' == value) {
651 return 63;
652 } else if (' ' == value) {
653 return 64;
654 } else if (value <= '9') {
655 return (value - '0') + 52;
656 } else if (value <= 'Z') {
657 return (value - 'A');
658 } else if (value <= 'z') {
659 return (value - 'a') + 26;
660 }
661 return 64;
662 }
663
664 static inline void pmi_base64_encode_block (const unsigned char in[3], char out[4], int len) {
665 out[0] = pmi_base64_encsym (in[0] >> 2);
666 out[1] = pmi_base64_encsym (((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4));
667
668 out[2] = 1 < len ? pmi_base64_encsym(((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)) : ' ';
669 out[3] = 2 < len ? pmi_base64_encsym(in[2] & 0x3f) : ' ';
670 }
671
672 static inline int pmi_base64_decode_block (const char in[4], unsigned char out[3]) {
673 char in_dec[4];
674
675 in_dec[0] = pmi_base64_decsym (in[0]);
676 in_dec[1] = pmi_base64_decsym (in[1]);
677 in_dec[2] = pmi_base64_decsym (in[2]);
678 in_dec[3] = pmi_base64_decsym (in[3]);
679
680 out[0] = in_dec[0] << 2 | in_dec[1] >> 4;
681 if (64 == in_dec[2]) {
682 return 1;
683 }
684
685 out[1] = in_dec[1] << 4 | in_dec[2] >> 2;
686 if (64 == in_dec[3]) {
687 return 2;
688 }
689
690 out[2] = ((in_dec[2] << 6) & 0xc0) | in_dec[3];
691 return 3;
692 }
693
694
695
696 static char *pmi_encode(const void *val, size_t vallen)
697 {
698 char *outdata, *tmp;
699 size_t i;
700
701 outdata = calloc (((2 + vallen) * 4) / 3 + 2, 1);
702 if (NULL == outdata) {
703 return NULL;
704 }
705
706 for (i = 0, tmp = outdata ; i < vallen ; i += 3, tmp += 4) {
707 pmi_base64_encode_block((unsigned char *) val + i, tmp, vallen - i);
708 }
709
710 tmp[0] = (unsigned char)'\0';
711
712 return outdata;
713 }
714
715 static uint8_t *pmi_decode (const char *data, size_t *retlen)
716 {
717 size_t input_len = strlen (data) / 4;
718 unsigned char *ret;
719 int out_len;
720 size_t i;
721
722
723 *retlen = 0;
724
725 ret = calloc (1, 3 * input_len);
726 if (NULL == ret) {
727 return ret;
728 }
729 for (i = 0, out_len = 0 ; i < input_len ; i++, data += 4) {
730 out_len += pmi_base64_decode_block(data, ret + 3 * i);
731 }
732 *retlen = out_len;
733 return ret;
734 }