add rnddelete function to xseg tool
[archipelago] / xseg / peers / user / xseg-tool.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <ctype.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11
12 #include <xseg/xseg.h>
13 #include <xseg/protocol.h>
14 int help(void)
15 {
16         printf("xseg <spec> [[[<src_port>]:[<dst_port>]] [<command> <arg>*] ]*\n"
17                 "spec:\n"
18                 "    <type:name:nr_ports:nr_requests:request_size:extra_size:page_shift>\n"
19                 "global commands:\n"
20                 "    reportall\n"
21                 "    create\n"
22                 "    destroy\n"
23                 "    bind <portno>\n"
24                 "    signal <portno>\n"
25                 "    bridge <portno1> <portno2> <logfile> {full|summary|stats}\n"
26                 "port commands:\n"
27                 "    report\n"
28                 "    alloc_requests (to source) <nr>\n"
29                 "    free_requests (from source) <nr>\n"
30                 "    put_requests (all from dest)\n"
31                 "    put_replies (all from dest)\n"
32                 "    wait        <nr_replies>\n"
33                 "    complete    <nr_requests>\n"
34                 "    fail        <nr_requests>\n"
35                 "    rndwrite    <nr_loops> <seed> <targetlen> <datalen> <objectsize>\n"
36                 "    rndread     <nr_loops> <seed> <targetlen> <datalen> <objectsize>\n"
37                 "    submit_reqs <nr_loops> <concurrent_reqs>\n"
38                 "    info        <target>\n"
39                 "    read        <target> <offset> <size>\n"
40                 "    write       <target> <offset> < data\n"
41                 "    truncate    <target> <size>\n"
42                 "    delete      <target>\n"
43                 "    acquire     <target>\n"
44                 "    release     <target>\n"
45                 "    copy        <src>  <dst>\n"
46                 "    clone       <src>  <dst>\n"
47         );
48         return 1;
49 }
50
51 char *namebuf;
52 char *chunk;
53 struct xseg_config cfg;
54 struct xseg *xseg;
55 uint32_t srcport, dstport;
56 uint64_t reqs;
57 #define mkname mkname_heavy
58 /* heavy distributes duplicates much more widely than light
59  * ./xseg-tool random 100000 | cut -d' ' -f2- | sort | uniq -d -c |wc -l
60  */
61
62 xport sport = NoPort;
63 static void init_local_signal() 
64 {
65         if (xseg && sport != srcport){
66                 xseg_init_local_signal(xseg, srcport);
67                 sport = srcport;
68         }
69 }
70
71 void mkname_heavy(char *name, uint32_t namelen, uint32_t seed)
72 {
73         int i;
74         char c;
75         for (i = 0; i < namelen; i += 1) {
76                 c = seed + (seed >> 8) + (seed >> 16) + (seed >> 24);
77                 c = '0' + ((c + (c >> 4)) & 0xf);
78                 if (c > '9')
79                         c += 'a'-'0'-10;
80                 name[i] = c;
81                 seed *= ((seed % 137911) | 1) * 137911;
82         }
83 }
84
85 void mkname_light(char *name, uint32_t namelen, uint32_t seed)
86 {
87         int i;
88         char c;
89         for (i = 0; i < namelen; i += 1) {
90                 c = seed;
91                 name[i] = 'A' + (c & 0xf);
92                 seed += 1;
93         }
94 }
95
96 uint64_t pick(uint64_t size)
97 {
98         return (uint64_t)((double)(RAND_MAX) / random());
99 }
100
101 void mkchunk(   char *chunk, uint32_t datalen,
102                 char *target, uint32_t targetlen, uint64_t offset)
103 {
104         long i, r, bufsize = targetlen + 16;
105         char buf[bufsize];
106         r = datalen % bufsize;
107         snprintf(buf, bufsize, "%016llx%s", (unsigned long long)offset, target);
108
109         for (i = 0; i <= (long)datalen - bufsize; i += bufsize)
110                 memcpy(chunk + i, buf, bufsize);
111
112         memcpy(chunk + datalen - r, buf, r);
113 }
114
115 int chkchunk(   char *chunk, uint32_t datalen,
116                 char *target, uint32_t targetlen, uint64_t offset)
117 {
118         long i, r;
119         int bufsize = targetlen + 16;
120         char buf[bufsize];
121         r = datalen % targetlen;
122         snprintf(buf, bufsize, "%016llx%s", (unsigned long long)offset, target);
123
124         for (i = 0; i <= (long)datalen - bufsize; i += bufsize)
125                 if (memcmp(chunk + i, buf, bufsize)) {
126                         /*printf("mismatch: '%*s'* vs '%*s'\n",
127                                 bufsize, buf, datalen, chunk);
128                         */
129                         return 0;
130                 }
131
132         if (memcmp(chunk + datalen - r, buf, r))
133                 return 0;
134
135         return 1;
136 }
137
138
139 #define ALLOC_MIN 4096
140 #define ALLOC_MAX 1048576
141
142 void inputbuf(FILE *fp, char **retbuf, uint64_t *retsize)
143 {
144         static uint64_t alloc_size;
145         static char *buf;
146         uint64_t size = 0;
147         char *p;
148         size_t r;
149
150         if (alloc_size < ALLOC_MIN)
151                 alloc_size = ALLOC_MIN;
152
153         if (alloc_size > ALLOC_MAX)
154                 alloc_size = ALLOC_MAX;
155
156         p = realloc(buf, alloc_size);
157         if (!p) {
158                 if (buf)
159                         free(buf);
160                 buf = NULL;
161                 goto out;
162         }
163
164         buf = p;
165
166         while (!feof(fp)) {
167                 r = fread(buf + size, 1, alloc_size - size, fp);
168                 if (!r)
169                         break;
170                 size += r;
171                 if (size >= alloc_size) {
172                         p = realloc(buf, alloc_size * 2);
173                         if (!p) {
174                                 if (buf)
175                                         free(buf);
176                                 buf = NULL;
177                                 size = 0;
178                                 goto out;
179                         }
180                         buf = p;
181                         alloc_size *= 2;
182                 }
183         }
184
185 out:
186         *retbuf = buf;
187         *retsize = size;
188 }
189
190 void report_request(struct xseg_request *req)
191 {
192         //uint32_t max = req->datalen;
193         //char *data = xseg_get_data(xseg, req);
194         //if (max > 128)
195         //      max = 128;
196         //data[max-1] = 0;
197         fprintf(stderr, "request %llu state %u\n", (unsigned long long)req->serial, req->state);
198 }
199
200 int cmd_info(char *target)
201 {
202         uint32_t targetlen = strlen(target);
203         size_t size = sizeof(uint64_t);
204         int r;
205         xport p;
206         struct xseg_request *req;
207         char *req_target;
208
209         req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
210         if (!req) {
211                 fprintf(stderr, "No request!\n");
212                 return -1;
213         }
214
215         r = xseg_prep_request(xseg, req, targetlen, size);
216         if (r < 0) {
217                 fprintf(stderr, "Cannot prepare request! (%lu, %lu)\n",
218                         (unsigned long) targetlen, (unsigned long) size);
219                 xseg_put_request(xseg, req, srcport);
220                 return -1;
221         }
222
223         req_target = xseg_get_target(xseg, req);
224         strncpy(req_target, target, targetlen);
225         req->offset = 0;
226         req->size = size;
227         req->op = X_INFO;
228
229         p = xseg_submit(xseg, req, srcport, X_ALLOC);
230         if (p == NoPort)
231                 return -1;
232
233         xseg_signal(xseg, p);
234
235         return 0;
236 }
237
238 int cmd_read(char *target, uint64_t offset, uint64_t size)
239 {
240         uint32_t targetlen = strlen(target);
241         int r;
242         xport p;
243         char *req_target;
244         struct xseg_request *req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
245         printf("%x\n", req);
246         if (!req) {
247                 fprintf(stderr, "No request\n");
248                 return -1;
249         }
250
251         r = xseg_prep_request(xseg, req, targetlen, size);
252         if (r < 0) {
253                 fprintf(stderr, "Cannot prepare request! (%lu, %llu)\n",
254                         (unsigned long)targetlen, (unsigned long long)size);
255                 xseg_put_request(xseg, req, srcport);
256                 return -1;
257         }
258
259         req_target = xseg_get_target(xseg, req);
260         strncpy(req_target, target, targetlen);
261         req->offset = offset;
262         req->size = size;
263         req->op = X_READ;
264         report_request(req);
265         p = xseg_submit(xseg, req, srcport, X_ALLOC);
266         if (p == NoPort)
267                 return -1;
268
269         xseg_signal(xseg, p);
270         return 0;
271 }
272
273 int cmd_write(char *target, uint64_t offset)
274 {
275         char *buf = NULL;
276         int r;
277         xport p;
278         uint64_t size = 0;
279         char *req_target, *req_data;
280         uint32_t targetlen = strlen(target);
281         struct xseg_request *req;
282
283         inputbuf(stdin, &buf, &size);
284         if (!size) {
285                 fprintf(stderr, "No input\n");
286                 return -1;
287         }
288
289         req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
290         if (!req) {
291                 fprintf(stderr, "No request\n");
292                 return -1;
293         }
294
295         r = xseg_prep_request(xseg, req, targetlen, size);
296         if (r < 0) {
297                 fprintf(stderr, "Cannot prepare request! (%lu, %llu)\n",
298                         (unsigned long)targetlen, (unsigned long long)size);
299                 xseg_put_request(xseg, req, srcport);
300                 return -1;
301         }
302
303         req_target = xseg_get_target(xseg, req);
304         strncpy(req_target, target, targetlen);
305         
306         req_data = xseg_get_data(xseg, req);
307         memcpy(req_data, buf, size);
308         req->offset = offset;
309         req->size = size;
310         req->op = X_WRITE;
311
312         p = xseg_submit(xseg, req, srcport, X_ALLOC);
313         if (p == NoPort) {
314                 fprintf(stderr, "Cannot submit\n");
315                 return -1;
316         }
317         xseg_signal(xseg, p);
318
319         return 0;
320 }
321
322 int cmd_truncate(char *target, uint64_t offset)
323 {
324         return 0;
325 }
326
327 int cmd_delete(char *target)
328 {
329         uint32_t targetlen = strlen(target);
330         int r;
331         struct xseg_request *req;
332         init_local_signal();
333         xseg_bind_port(xseg, srcport, NULL);
334
335         req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
336         if (!req) {
337                 fprintf(stderr, "No request!\n");
338                 return -1;
339         }
340
341         r = xseg_prep_request(xseg, req, targetlen, 0);
342         if (r < 0) {
343                 fprintf(stderr, "Cannot prepare request! (%lu, %lu)\n",
344                         (unsigned long) targetlen, (unsigned long) req->bufferlen - targetlen);
345                 xseg_put_request(xseg, req, srcport);
346                 return -1;
347         }
348
349         char *reqtarget = xseg_get_target(xseg, req);
350         strncpy(reqtarget, target, targetlen);
351         req->op = X_DELETE;
352
353         xport p = xseg_submit(xseg, req, srcport, X_ALLOC);
354         if (p == NoPort){
355                 fprintf(stderr, "Couldn't submit request\n");
356                 xseg_put_request(xseg, req, srcport);
357                 return -1;
358         }
359
360         xseg_signal(xseg, p);
361
362         return 0;
363 }
364
365 int cmd_acquire(char *target)
366 {
367         return 0;
368 }
369
370 int cmd_release(char *target)
371 {
372         return 0;
373 }
374
375 int cmd_copy(char *src, char *dst)
376 {
377         return 0;
378 }
379
380 int cmd_clone(char *src, char *dst)
381 {
382
383         uint32_t targetlen = strlen(dst);
384         uint32_t parentlen = strlen(src);
385         struct xseg_request *req;
386         struct xseg_request_clone *xclone;
387         xseg_bind_port(xseg, srcport, NULL);
388         req = xseg_get_request(xseg, srcport, dstport, X_ALLOC);
389         if (!req) {
390                 fprintf(stderr, "No request\n");
391                 return -1;
392         }
393
394         int r = xseg_prep_request(xseg, req, targetlen, sizeof(struct xseg_request_clone));
395         if (r < 0) {
396                 fprintf(stderr, "Cannot prepare request!\n");
397                 xseg_put_request(xseg, req, srcport);
398                 return -1;
399         }
400
401         char *target = xseg_get_target(xseg, req);
402         char *data = xseg_get_data(xseg, req);
403
404         strncpy(target, dst, targetlen);
405         xclone = (struct xseg_request_clone *) data;
406         strncpy(xclone->target, src, parentlen);
407         xclone->targetlen = parentlen;
408         xclone->size = -1;
409         req->offset = 0;
410         req->size = sizeof(struct xseg_request_clone);
411         req->op = X_CLONE;
412
413         xport p = xseg_submit(xseg, req, srcport, X_ALLOC);
414         if (p == NoPort){
415                 fprintf(stderr, "Cannot submit request\n");
416                 return -1;
417         }
418         xseg_signal(xseg, p);
419
420         return 0;
421 }
422
423 void log_req(int logfd, uint32_t portno2, uint32_t portno1, int op, int method,
424                 struct xseg_request *req)
425 {
426         FILE *logfp;
427         char target[64], data[64];
428         char *req_target, *req_data;
429         /* null terminate name in case of req->target is less than 63 characters,
430          * and next character after name (aka first byte of next buffer) is not
431          * null
432          */
433         unsigned int end = (req->targetlen > 63) ? 63 : req->targetlen;
434         
435         req_target = xseg_get_target(xseg, req);
436         req_data = xseg_get_data(xseg, req);
437
438         logfp = fdopen(logfd, "a");
439         if (!logfp)
440                 return;
441
442         switch(method) {
443         case 0:
444                 strncpy(target, req_target, end);
445                 target[end] = 0;
446                 strncpy(data, req_data, 63);
447                 data[63] = 0;
448
449                 fprintf(logfp,
450                         "src port: %u, dst port: %u,  op:%u offset: %llu size: %lu, reqstate: %u\n"
451                         "target[%u]: '%s', data[%llu]:\n%s------------------\n\n",
452                         (unsigned int)portno1,
453                         (unsigned int)portno2,
454                         (unsigned int)req->op,
455                         (unsigned long long)req->offset,
456                         (unsigned long)req->size,
457                         (unsigned int)req->state,
458                         (unsigned int)req->targetlen, target,
459                         (unsigned long long)req->datalen, data);
460                 break;
461         case 1:
462                 fprintf(logfp,
463                         "src port: %u, dst port: %u, op: %u\n",
464                         (unsigned int)portno1,
465                         (unsigned int)portno2,
466                         (unsigned int)req->op);
467                 break;
468         case 2:
469                 fprintf(logfp, "src port: %u, dst port: %u, reqs: %llu\n",
470                         (unsigned int)portno1,
471                         (unsigned int)portno2,
472                         (unsigned long long)++reqs);
473         }
474
475         fclose(logfp);
476         return;
477 }
478
479 #define LOG_ACCEPT  0
480 #define LOG_RECEIVE 1
481
482 int cmd_bridge(uint32_t portno1, uint32_t portno2, char *logfile, char *how)
483 {
484         struct xseg_request *req;
485         int logfd, method;
486         if (!strcmp(logfile, "-"))
487                 logfd = 1;
488         else {
489                 logfd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0600);
490                 if (logfd < 0) {
491                         perror(logfile);
492                         return -1;
493                 }
494         }
495
496         if (!strcmp(how, "full"))
497                 method = 0;
498         else if (!strcmp(how, "summary"))
499                 method = 1;
500         else
501                 method = 2;
502
503         for (;;) {
504                 int reloop = 0, active;
505                 xseg_prepare_wait(xseg, portno1);
506                 xseg_prepare_wait(xseg, portno2);
507                 req = NULL;
508
509                 for (;;) {
510                         active = 0;
511
512                         //FIXME
513                         req = xseg_accept(xseg, portno1, 0);
514                         if (req) {
515                                 xseg_submit(xseg, req, portno2, X_ALLOC);
516                                 log_req(logfd, portno1, portno2, LOG_ACCEPT, method, req);
517                                 active += 1;
518                         }
519
520                         req = xseg_accept(xseg, portno2, 0);
521                         if (req) {
522                                 xseg_submit(xseg, req, portno1, X_ALLOC);
523                                 log_req(logfd, portno2, portno1, LOG_ACCEPT, method, req);
524                                 active += 1;
525                         }
526
527                         req = xseg_receive(xseg, portno1, 0);
528                         if (req) {
529                                 xseg_respond(xseg, req, portno2, X_ALLOC);
530                                 log_req(logfd, portno1, portno2, LOG_RECEIVE, method, req);
531                                 active += 1;
532                         }
533
534                         req = xseg_receive(xseg, portno2, 0);
535                         if (req) {
536                                 xseg_respond(xseg, req, portno1, X_ALLOC);
537                                 log_req(logfd, portno2, portno1, LOG_RECEIVE, method, req);
538                                 active += 1;
539                         }
540
541                         if (active == 0) {
542                                 if (reloop)
543                                         break;
544                                 /* wait on multiple queues? */
545                                 xseg_wait_signal(xseg, 100000);
546                                 break;
547                         } else {
548                                 xseg_cancel_wait(xseg, portno1);        
549                                 xseg_cancel_wait(xseg, portno2);        
550                                 reloop = 1;
551                         }
552                 }
553         }
554
555         close(logfd);
556
557         return 0;
558 }
559
560 int cmd_rndwrite(long loops, int32_t seed, uint32_t targetlen, uint32_t chunksize, uint64_t size)
561 {
562         if (loops < 0)
563                 return help();
564
565         if (targetlen >= chunksize) {
566                 fprintf(stderr, "targetlen >= chunksize\n");
567                 return -1;
568         }
569
570         char *p = realloc(namebuf, targetlen+1);
571         if (!p) {
572                 fprintf(stderr, "Cannot allocate memory\n");
573                 return -1;
574         }
575         namebuf = p;
576
577         p = realloc(chunk, chunksize);
578         if (!p) {
579                 fprintf(stderr, "Cannot allocate memory\n");
580                 return -1;
581         }
582         chunk = p;
583         memset(chunk, 0, chunksize);
584
585         srandom(seed);
586
587         struct xseg_request *submitted = NULL, *received;
588         long nr_submitted = 0, nr_received = 0, nr_failed = 0;
589         int reported = 0, r;
590         uint64_t offset;
591         xport port;
592         char *req_data, *req_target;
593         seed = random();
594         init_local_signal();
595
596         for (;;) {
597                 xseg_prepare_wait(xseg, srcport);
598                 if (nr_submitted < loops &&
599                     (submitted = xseg_get_request(xseg, srcport, dstport, X_ALLOC))) {
600                         xseg_cancel_wait(xseg, srcport);
601                         r = xseg_prep_request(xseg, submitted, targetlen, chunksize);
602                         if (r < 0) {
603                                 fprintf(stderr, "Cannot prepare request! (%u, %u)\n",
604                                         targetlen, chunksize);
605                                 xseg_put_request(xseg, submitted, srcport);
606                                 return -1;
607                         }
608                         
609                         req_target = xseg_get_target(xseg, submitted);
610                         req_data = xseg_get_data(xseg, submitted);
611
612                         reported = 0;
613                         mkname(namebuf, targetlen, seed);
614                         namebuf[targetlen] = 0;
615                         //printf("%ld: %s\n", nr_submitted, namebuf);
616                         strncpy(req_target, namebuf, targetlen);
617                         offset = 0;// pick(size);
618                         mkchunk(req_data, chunksize, namebuf, targetlen, offset);
619
620                         submitted->offset = offset;
621                         submitted->size = chunksize;
622                         submitted->op = X_WRITE;
623                         submitted->flags |= XF_NOSYNC;
624
625                         port =  xseg_submit(xseg, submitted, srcport, X_ALLOC);
626                         if (port == NoPort) {
627                                 xseg_put_request(xseg, submitted, srcport);
628                         } else {
629                                 seed = random();
630                                 nr_submitted += 1;
631                                 xseg_signal(xseg, port);
632                         }
633                 }
634
635                 received = xseg_receive(xseg, srcport, 0);
636                 if (received) {
637                         xseg_cancel_wait(xseg, srcport);
638                         nr_received += 1;
639                         if (!(received->state & XS_SERVED)) {
640                                 nr_failed += 1;
641                                 report_request(received);
642                         }
643                         if (xseg_put_request(xseg, received, srcport))
644                                 fprintf(stderr, "Cannot put request at port %u\n", received->src_portno);
645                 }
646
647                 if (!submitted && !received)
648                         xseg_wait_signal(xseg, 1000000);
649
650                         if (nr_submitted % 1000 == 0 && !reported) {
651                                 reported = 1;
652                                 fprintf(stderr, "submitted %ld, received %ld, failed %ld\n",
653                                         nr_submitted, nr_received, nr_failed);
654                         }
655
656                         if (nr_received >= loops)
657                                 break;
658         }
659
660         fprintf(stderr, "submitted %ld, received %ld, failed %ld\n",
661                 nr_submitted, nr_received, nr_failed);
662         return 0;
663 }
664
665 int cmd_rnddelete(long loops, int32_t seed, uint32_t targetlen)
666 {
667         if (loops < 0)
668                 return help();
669         char *p = realloc(namebuf, targetlen+1);
670         if (!p) {
671                 fprintf(stderr, "Cannot allocate memory\n");
672                 return -1;
673         }
674         namebuf = p;
675
676         srandom(seed);
677
678         struct xseg_request *submitted = NULL, *received;
679         long nr_submitted = 0, nr_received = 0, nr_failed = 0;
680         int reported = 0, r;
681         xport port;
682         char *req_target;
683         seed = random();
684         init_local_signal();
685
686         for (;;) {
687                 xseg_prepare_wait(xseg, srcport);
688                 if (nr_submitted < loops &&
689                     (submitted = xseg_get_request(xseg, srcport, dstport, X_ALLOC))) {
690                         xseg_cancel_wait(xseg, srcport);
691                         r = xseg_prep_request(xseg, submitted, targetlen, 0);
692                         if (r < 0) {
693                                 fprintf(stderr, "Cannot prepare request! (%u, %u)\n",
694                                         targetlen, 0);
695                                 xseg_put_request(xseg, submitted, srcport);
696                                 return -1;
697                         }
698                         
699                         req_target = xseg_get_target(xseg, submitted);
700
701                         reported = 0;
702                         mkname(namebuf, targetlen, seed);
703                         namebuf[targetlen] = 0;
704                         //printf("%ld: %s\n", nr_submitted, namebuf);
705                         strncpy(req_target, namebuf, targetlen);
706                         submitted->offset = 0;
707                         submitted->size = 0;
708                         submitted->op = X_DELETE;
709                         submitted->flags = 0;
710
711                         port =  xseg_submit(xseg, submitted, srcport, X_ALLOC);
712                         if (port == NoPort) {
713                                 xseg_put_request(xseg, submitted, srcport);
714                         } else {
715                                 seed = random();
716                                 nr_submitted += 1;
717                                 xseg_signal(xseg, port);
718                         }
719                 }
720
721                 received = xseg_receive(xseg, srcport, 0);
722                 if (received) {
723                         xseg_cancel_wait(xseg, srcport);
724                         nr_received += 1;
725                         if (!(received->state & XS_SERVED)) {
726                                 nr_failed += 1;
727                                 report_request(received);
728                         }
729                         if (xseg_put_request(xseg, received, srcport))
730                                 fprintf(stderr, "Cannot put request at port %u\n", received->src_portno);
731                 }
732
733                 if (!submitted && !received)
734                         xseg_wait_signal(xseg, 1000000);
735
736                         if (nr_submitted % 1000 == 0 && !reported) {
737                                 reported = 1;
738                                 fprintf(stderr, "submitted %ld, received %ld, failed %ld\n",
739                                         nr_submitted, nr_received, nr_failed);
740                         }
741
742                         if (nr_received >= loops)
743                                 break;
744         }
745
746         fprintf(stderr, "submitted %ld, received %ld, failed %ld\n",
747                 nr_submitted, nr_received, nr_failed);
748         return 0;
749 }
750 /* note:
751  * prepare/wait rhythm,
752  * files are converted to independent chunk access patterns,
753 */
754
755 int cmd_rndread(long loops, int32_t seed, uint32_t targetlen, uint32_t chunksize, uint64_t size)
756 {
757         if (loops < 0)
758                 return help();
759
760         if (targetlen >= chunksize) {
761                 fprintf(stderr, "targetlen >= chunksize\n");
762                 return -1;
763         }
764
765         char *p = realloc(namebuf, targetlen+1);
766         if (!p) {
767                 fprintf(stderr, "Cannot allocate memory\n");
768                 return -1;
769         }
770         namebuf = p;
771
772         p = realloc(chunk, chunksize);
773         if (!p) {
774                 fprintf(stderr, "Cannot allocate memory\n");
775                 return -1;
776         }
777         chunk = p;
778         memset(chunk, 0, chunksize);
779
780         srandom(seed);
781
782         struct xseg_request *submitted = NULL, *received;
783         long nr_submitted = 0, nr_received = 0, nr_failed = 0, nr_mismatch = 0;
784         int reported = 0, r;
785         uint64_t offset;
786         xport port;
787         char *req_data, *req_target;
788         init_local_signal();
789
790         seed = random();
791         for (;;) {
792                 submitted = NULL;
793                 xseg_prepare_wait(xseg, srcport);
794                 if (nr_submitted < loops &&
795                     (submitted = xseg_get_request(xseg, srcport, dstport, X_ALLOC))) {
796                         xseg_cancel_wait(xseg, srcport);
797                         r = xseg_prep_request(xseg, submitted, targetlen, chunksize);
798                         if (r < 0) {
799                                 fprintf(stderr, "Cannot prepare request! (%u, %u)\n",
800                                         targetlen, chunksize);
801                                 xseg_put_request(xseg, submitted, srcport);
802                                 return -1;
803                         }
804
805                         req_target = xseg_get_target(xseg, submitted);
806                         reported = 0;
807                         mkname(namebuf, targetlen, seed);
808                         namebuf[targetlen] = 0;
809                         //printf("%ld: %s\n", nr_submitted, namebuf);
810                         offset = 0;//pick(size);
811
812                         strncpy(req_target, namebuf, targetlen);
813                         submitted->offset = offset;
814                         submitted->size = chunksize;
815                         submitted->op = X_READ;
816                         port = xseg_submit(xseg, submitted, srcport, X_ALLOC);
817                         if (port == NoPort) {
818                                 xseg_put_request(xseg, submitted, srcport);
819                         } else {
820                                 seed = random();
821                                 nr_submitted += 1;
822                                 xseg_signal(xseg, port);
823                         }
824                 }
825
826                 received = xseg_receive(xseg, srcport, 0);
827                 if (received) {
828                         xseg_cancel_wait(xseg, srcport);
829                         nr_received += 1;
830                         req_target = xseg_get_target(xseg, received);
831                         req_data = xseg_get_data(xseg, received);
832                         if (!(received->state & XS_SERVED)) {
833                                 nr_failed += 1;
834                                 report_request(received);
835                         } else if (!chkchunk(req_data, received->datalen,
836                                         req_target, received->targetlen, received->offset)) {
837         //                      report_request(received);
838                                 nr_mismatch += 1;
839                         }
840
841                         if (xseg_put_request(xseg, received, srcport))
842                                 fprintf(stderr, "Cannot put request at port %u\n", received->src_portno);
843                 }
844
845                 if (!submitted && !received)
846                         xseg_wait_signal(xseg, 1000000);
847
848                 if (nr_submitted % 1000 == 0 && !reported) {
849                         reported = 1;
850                         fprintf(stderr, "submitted %ld, received %ld, failed %ld, mismatched %ld\n",
851                         nr_submitted, nr_received, nr_failed, nr_mismatch);
852                 }
853
854                 if (nr_received >= loops)
855                         break;
856         }
857
858         fprintf(stderr, "submitted %ld, received %ld, failed %ld, mismatched %ld\n",
859                 nr_submitted, nr_received, nr_failed, nr_mismatch);
860         return 0;
861 }
862
863 int cmd_submit_reqs(long loops, long concurrent_reqs, int op)
864 {
865         if (loops < 0)
866                 return help();
867
868         struct xseg_request *submitted = NULL, *received;
869         long nr_submitted = 0, nr_received = 0, nr_failed = 0, nr_mismatch = 0, nr_flying = 0;
870         int reported = 0, r;
871         uint64_t offset;
872         uint32_t targetlen = 10, chunksize = 4096;
873         struct timeval tv1, tv2;
874         xport p;
875         char *req_data, *req_target;
876
877         xseg_bind_port(xseg, srcport, NULL);
878
879         gettimeofday(&tv1, NULL);
880         for (;;) {
881                 submitted = NULL;
882                 xseg_prepare_wait(xseg, srcport);
883                 if (nr_submitted < loops &&  nr_flying < concurrent_reqs &&
884                     (submitted = xseg_get_request(xseg, srcport, dstport, X_ALLOC))) {
885                         xseg_cancel_wait(xseg, srcport);
886                         r = xseg_prep_request(xseg, submitted, targetlen, chunksize);
887                         if (r < 0) {
888                                 fprintf(stderr, "Cannot prepare request! (%u, %u)\n",
889                                         targetlen, chunksize);
890                                 xseg_put_request(xseg, submitted, srcport);
891                                 return -1;
892                         }
893                         
894                         //FIXME
895                         ++nr_flying;
896                         nr_submitted += 1;
897                         reported = 0;
898                         offset = 0;//pick(size);
899
900                         submitted->offset = offset;
901                         submitted->size = chunksize;
902                         req_target = xseg_get_target(xseg, submitted);
903                         req_data = xseg_get_data(xseg, submitted);
904
905                         if (op == 0)
906                                 submitted->op = X_INFO;
907                         else if (op == 1)
908                                 submitted->op = X_READ;
909                         else if (op == 2) {
910                                 submitted->op = X_WRITE;
911                                 mkchunk(req_data, submitted->datalen, req_target, submitted->targetlen, submitted->offset);
912                         }
913
914                         p = xseg_submit(xseg, submitted, srcport, X_ALLOC);
915                         if ( p != NoPort){
916                                 if (xseg_signal(xseg, p) < 0)
917                                         perror("Cannot signal peer");
918                         }
919                 }
920                 received = xseg_receive(xseg, srcport, 0);
921                 if (received) {
922                         xseg_cancel_wait(xseg, srcport);
923                         --nr_flying;
924                         if (nr_received == 0)
925                                 fprintf(stderr, "latency (time for the first req to complete): %llu usecs\n",
926                                         (unsigned long long)received->elapsed);
927                         nr_received += 1;
928                         if (!(received->state & XS_SERVED)) {
929                                 nr_failed += 1;
930                                 //report_request(received);
931                         }
932
933                         if (xseg_put_request(xseg, received, srcport))
934                                 fprintf(stderr, "Cannot put request at port %u\n", received->src_portno);
935                 }
936
937                 if (!submitted && !received)
938                         xseg_wait_signal(xseg, 10000000L);
939
940                 if (nr_received >= loops)
941                         break;
942         }
943         gettimeofday(&tv2, NULL);
944
945         fprintf(stderr, "submitted %ld, received %ld, failed %ld, mismatched %ld\n",
946                 nr_submitted, nr_received, nr_failed, nr_mismatch);
947         long t = (tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec);
948         fprintf(stderr, "elpased time: %lf secs, throughput: %lf reqs/sec\n", (double) t / 1000000.0, (double) nr_submitted / (t / 1000000.0));
949
950         return 0;
951 }
952
953 int cmd_report(uint32_t portno)
954 {
955         struct xseg_port *port = xseg_get_port(xseg, portno);
956         if (!port) {
957                 printf("port %u is not assigned\n", portno);
958                 return 0;
959         }
960         struct xq *fq, *rq, *pq;
961         fq = xseg_get_queue(xseg, port, free_queue);
962         rq = xseg_get_queue(xseg, port, request_queue);
963         pq = xseg_get_queue(xseg, port, reply_queue);
964         fprintf(stderr, "port %u:\n"
965                 "   requests: %llu/%llu  src gw: %u  dst gw: %u\n"
966                 "       free_queue [%p] count : %u\n"
967                 "    request_queue [%p] count : %u\n"
968                 "      reply_queue [%p] count : %u\n",
969                 portno, port->alloc_reqs, port->max_alloc_reqs,
970                 xseg->src_gw[portno], xseg->dst_gw[portno],
971                 (void *)fq, xq_count(fq),
972                 (void *)rq, xq_count(rq),
973                 (void *)pq, xq_count(pq));
974         return 0;
975 }
976
977 int cmd_join(void)
978 {
979         if (xseg)
980                 return 0;
981
982         xseg = xseg_join(cfg.type, cfg.name, "posix", NULL);
983         if (!xseg) {
984                 fprintf(stderr, "cannot join segment!\n");
985                 return -1;
986         }
987         return 0;
988 }
989
990 int cmd_reportall(void)
991 {
992         uint32_t t;
993
994         if (cmd_join())
995                 return -1;
996
997
998         fprintf(stderr, "Heap usage: %llu / %llu\n", xseg->heap->cur, xseg->config.heap_size);
999         for (t = 0; t < xseg->config.nr_ports; t++)
1000                 cmd_report(t);
1001
1002         return 0;
1003 }
1004
1005 int cmd_create(void)
1006 {
1007         int r = xseg_create(&cfg);
1008         if (r) {
1009                 fprintf(stderr, "cannot create segment!\n");
1010                 return -1;
1011         }
1012
1013         fprintf(stderr, "Segment initialized.\n");
1014         return 0;
1015 }
1016
1017 int cmd_destroy(void)
1018 {
1019         if (!xseg && cmd_join())
1020                 return -1;
1021         xseg_leave(xseg);
1022         xseg_destroy(xseg);
1023         xseg = NULL;
1024         fprintf(stderr, "Segment destroyed.\n");
1025         return 0;
1026 }
1027
1028 int cmd_alloc_requests(unsigned long nr)
1029 {
1030         return xseg_alloc_requests(xseg, srcport, nr);
1031 }
1032
1033 int cmd_free_requests(unsigned long nr)
1034 {
1035         return xseg_free_requests(xseg, srcport, nr);
1036 }
1037
1038 int cmd_put_requests(void)
1039 {
1040         struct xseg_request *req;
1041
1042         for (;;) {
1043                 req = xseg_accept(xseg, dstport, 0);
1044                 if (!req)
1045                         break;
1046                 if (xseg_put_request(xseg, req, srcport))
1047                         fprintf(stderr, "Cannot put request at port %u\n", req->src_portno);
1048         }
1049
1050         return 0;
1051 }
1052
1053 int cmd_finish(unsigned long nr, int fail)
1054 {
1055         struct xseg_request *req;
1056         char *buf = malloc(sizeof(char) * 8128);
1057         char *req_target, *req_data;
1058         xseg_bind_port(xseg, srcport, NULL);
1059         xport p;
1060
1061         for (; nr--;) {
1062                 xseg_prepare_wait(xseg, srcport);
1063                 req = xseg_accept(xseg, srcport, 0);
1064                 if (req) {
1065                         req_target = xseg_get_target(xseg, req);
1066                         req_data = xseg_get_data(xseg, req);
1067                         xseg_cancel_wait(xseg, srcport);
1068                         if (fail == 1)
1069                                 req->state &= ~XS_SERVED;
1070                         else {
1071                                 if (req->op == X_READ)
1072                                         mkchunk(req_data, req->datalen, req_target, req->targetlen, req->offset);
1073                                 else if (req->op == X_WRITE) 
1074                                         memcpy(buf, req_data, (sizeof(*buf) > req->datalen) ? req->datalen : sizeof(*buf));
1075                                 else if (req->op == X_INFO)
1076                                         *((uint64_t *) req->data) = 4294967296;
1077                                 
1078                                 req->state |= XS_SERVED;
1079                                 req->serviced = req->size;
1080                         }
1081
1082                         p = xseg_respond(xseg, req, srcport, X_ALLOC);
1083                         xseg_signal(xseg, p);
1084                         continue;
1085                 }
1086                 ++nr;
1087                 xseg_wait_signal(xseg, 10000000L);
1088         }
1089
1090         free(buf);
1091
1092         return 0;
1093 }
1094
1095 void handle_reply(struct xseg_request *req)
1096 {
1097         char *req_data = xseg_get_data(xseg, req);
1098         char *req_target = xseg_get_target(xseg, req);
1099         if (!(req->state & XS_SERVED)) {
1100                 report_request(req);
1101                 goto put;
1102         }
1103
1104         switch (req->op) {
1105         case X_READ:
1106                 fwrite(req_data, 1, req->datalen, stdout);
1107                 break;
1108
1109         case X_WRITE:
1110                 fprintf(stdout, "wrote: ");
1111                 fwrite(req_data, 1, req->datalen, stdout);
1112                 break;
1113         case X_SYNC:
1114         case X_DELETE:
1115                 fprintf(stderr, "deleted %s\n", req_target);
1116                 break;
1117         case X_TRUNCATE:
1118         case X_COMMIT:
1119         case X_CLONE:
1120                 fprintf(stderr, "cloned %s\n", ((struct xseg_request_clone *)req_data)->target);
1121                 break;
1122         case X_INFO:
1123                 fprintf(stderr, "size: %llu\n", (unsigned long long)*((uint64_t *)req_data));
1124                 break;
1125
1126         default:
1127                 break;
1128         }
1129
1130 put:
1131         if (xseg_put_request(xseg, req, srcport))
1132                 fprintf(stderr, "Cannot put reply at port %u\n", req->src_portno);
1133 }
1134
1135 int cmd_wait(uint32_t nr)
1136 {
1137         struct xseg_request *req;
1138         long ret;
1139         init_local_signal(); 
1140
1141         for (;;) {
1142                 req = xseg_receive(xseg, srcport, 0);
1143                 if (req) {
1144                         handle_reply(req);
1145                         nr--;
1146                         if (nr == 0)
1147                                 break;
1148                         continue;
1149                 }
1150
1151                 ret = xseg_prepare_wait(xseg, srcport);
1152                 if (ret)
1153                         return -1;
1154
1155                 ret = xseg_wait_signal(xseg, 1000000);
1156                 ret = xseg_cancel_wait(xseg, srcport);
1157                 if (ret)
1158                         return -1;
1159         }
1160
1161         return 0;
1162 }
1163
1164 int cmd_put_replies(void)
1165 {
1166         struct xseg_request *req;
1167
1168         for (;;) {
1169                 req = xseg_receive(xseg, dstport, 0);
1170                 if (!req)
1171                         break;
1172                 fprintf(stderr, "request: %08llx%08llx\n"
1173                         "     op: %u\n"
1174                         "  state: %u\n",
1175                         0LL, (unsigned long long)req->serial,
1176                         req->op,
1177                         req->state);
1178                 report_request(req);
1179
1180                 //fwrite(req->buffer, 1, req->bufferlen, stdout);
1181
1182                 if (xseg_put_request(xseg, req, srcport))
1183                         fprintf(stderr, "Cannot put reply\n");
1184         }
1185
1186         return 0;
1187 }
1188
1189 int cmd_bind(long portno)
1190 {
1191         struct xseg_port *port = xseg_bind_port(xseg, portno, NULL);
1192         if (!port) {
1193                 fprintf(stderr, "failed to bind port %ld\n", portno);
1194                 return 1;
1195         }
1196
1197         fprintf(stderr, "bound port %u\n", xseg_portno(xseg, port));
1198         return 0;
1199 }
1200
1201 int cmd_signal(uint32_t portno)
1202 {
1203         return xseg_signal(xseg, portno);
1204 }
1205
1206 int parse_ports(char *str)
1207 {
1208         int ret = 0;
1209         char *s = str;
1210
1211         for (;;) {
1212                 if (*s == 0)
1213                         return 0;
1214
1215                 if (*s == ':') {
1216                         *s = 0;
1217                         if ((s > str) && isdigit(str[0])) {
1218                                 srcport = atol(str);
1219                                 ret ++;
1220                         }
1221                         break;
1222                 }
1223                 s ++;
1224         }
1225
1226         s += 1;
1227         str = s;
1228
1229         for (;;) {
1230                 if (*s == 0) {
1231                         if ((s > str) && isdigit(str[0])) {
1232                                 dstport = atol(str);
1233                                 ret ++;
1234                         }
1235                         break;
1236                 }
1237                 s ++;
1238         }
1239
1240         return ret;
1241 }
1242
1243 int main(int argc, char **argv)
1244 {
1245         int i, ret = 0;
1246         char *spec;
1247
1248         if (argc < 3)
1249                 return help();
1250
1251         srcport = -1;
1252         dstport = -1;
1253         spec = argv[1];
1254
1255         if (xseg_parse_spec(spec, &cfg)) {
1256                 fprintf(stderr, "Cannot parse spec\n");
1257                 return -1;
1258         }
1259
1260         if (xseg_initialize()) {
1261                 fprintf(stderr, "cannot initialize!\n");
1262                 return -1;
1263         }
1264
1265         for (i = 2; i < argc; i++) {
1266
1267                 if (!strcmp(argv[i], "create")) {
1268                         ret = cmd_create();
1269                         continue;
1270                 }
1271
1272                 if (!strcmp(argv[i], "join")) {
1273                         ret = cmd_join();
1274                         if (!ret)
1275                                 fprintf(stderr, "Segment joined.\n");
1276                         continue;
1277                 }
1278
1279                 if (!strcmp(argv[i], "destroy")) {
1280                         ret = cmd_destroy();
1281                         continue;
1282                 }
1283
1284                 if (cmd_join())
1285                         return -1;
1286
1287                 if (!strcmp(argv[i], "reportall")) {
1288                         ret = cmd_reportall();
1289                         continue;
1290                 }
1291
1292                 if (!strcmp(argv[i], "bind") && (i + 1 < argc)) {
1293                         ret = cmd_bind(atol(argv[i+1]));
1294                         i += 1;
1295                         continue;
1296                 }
1297
1298                 if (!strcmp(argv[i], "signal") && (i + 1 < argc)) {
1299                         ret = cmd_signal(atol(argv[i+1]));
1300                         i += 1;
1301                         continue;
1302                 }
1303
1304                 if (!strcmp(argv[i], "bridge") && (i + 4 < argc)) {
1305                         ret = cmd_bridge(atol(argv[i+1]),
1306                                          atol(argv[i+2]),
1307                                          argv[i+3],
1308                                          argv[i+4]);
1309                         i += 4;
1310                         continue;
1311                 }
1312
1313                 if (srcport == -1) {
1314                         if (!parse_ports(argv[i]))
1315                                 fprintf(stderr, "source port undefined: %s\n", argv[i]);
1316                         continue;
1317                 }
1318
1319                 if (dstport == -1) {
1320                         if (!parse_ports(argv[i]))
1321                                 fprintf(stderr, "destination port undefined: %s\n", argv[i]);
1322                         continue;
1323                 }
1324
1325                 if (!strcmp(argv[i], "report")) {
1326                         ret = cmd_report(dstport);
1327                         continue;
1328                 }
1329
1330                 if (!strcmp(argv[i], "alloc_requests") && (i + 1 < argc)) {
1331                         ret = cmd_alloc_requests(atol(argv[i+1]));
1332                         i += 1;
1333                         continue;
1334                 }
1335
1336                 if (!strcmp(argv[i], "free_requests") && (i + 1 < argc)) {
1337                         ret = cmd_free_requests(atol(argv[i+1]));
1338                         i += 1;
1339                         continue;
1340                 }
1341
1342                 if (!strcmp(argv[i], "put_requests")) {
1343                         ret = cmd_put_requests();
1344                         continue;
1345                 }
1346
1347                 if (!strcmp(argv[i], "put_replies")) {
1348                         ret = cmd_put_replies();
1349                         continue;
1350                 }
1351
1352                 if (!strcmp(argv[i], "complete") && (i + 1 < argc)) {
1353                         ret = cmd_finish(atol(argv[i+1]), 0);
1354                         i += 1;
1355                         continue;
1356                 }
1357
1358                 if (!strcmp(argv[i], "fail") && (i + 1 < argc)) {
1359                         ret = cmd_finish(atol(argv[i+1]), 1);
1360                         i += 1;
1361                         continue;
1362                 }
1363
1364                 if (!strcmp(argv[i], "wait") && (i + 1 < argc)) {
1365                         ret = cmd_wait(atol(argv[i+1]));
1366                         i += 1;
1367                         continue;
1368                 }
1369
1370                 if (!strcmp(argv[i], "rndwrite") && (i + 5 < argc)) {
1371                         long nr_loops = atol(argv[i+1]);
1372                         unsigned int seed = atoi(argv[i+2]);
1373                         unsigned int targetlen = atoi(argv[i+3]);
1374                         unsigned int chunksize = atoi(argv[i+4]);
1375                         unsigned long objectsize = atol(argv[i+5]);
1376                         ret = cmd_rndwrite(nr_loops, seed, targetlen, chunksize, objectsize);
1377                         i += 5;
1378                         continue;
1379                 }
1380                 
1381                 if (!strcmp(argv[i], "rnddelete") && (i + 3 < argc)) {
1382                         long nr_loops = atol(argv[i+1]);
1383                         unsigned int seed = atoi(argv[i+2]);
1384                         unsigned int targetlen = atoi(argv[i+3]);
1385                         ret = cmd_rnddelete(nr_loops, seed, targetlen);
1386                         i += 3;
1387                         continue;
1388                 }
1389
1390                 if (!strcmp(argv[i], "rndread") && (i + 5 < argc)) {
1391                         long nr_loops = atol(argv[i+1]);
1392                         unsigned int seed = atoi(argv[i+2]);
1393                         unsigned int targetlen = atoi(argv[i+3]);
1394                         unsigned int chunksize = atoi(argv[i+4]);
1395                         unsigned long objectsize = atol(argv[i+5]);
1396                         ret = cmd_rndread(nr_loops, seed, targetlen, chunksize, objectsize);
1397                         i += 5;
1398                         continue;
1399                 }
1400
1401                 if (!strcmp(argv[i], "submit_reqs") && (i + 3 < argc)) {
1402                         long nr_loops = atol(argv[i+1]);
1403                         long concurrent_reqs = atol(argv[i+2]);
1404                         int op = atoi(argv[i+3]);
1405                         ret = cmd_submit_reqs(nr_loops, concurrent_reqs, op);
1406                         i += 3;
1407                         continue;
1408                 }
1409
1410                 if (!strcmp(argv[i], "read") && (i + 3 < argc)) {
1411                         char *target = argv[i+1];
1412                         uint64_t offset = atol(argv[i+2]);
1413                         uint64_t size   = atol(argv[i+3]);
1414                         ret = cmd_read(target, offset, size);
1415                         i += 3;
1416                         continue;
1417                 }
1418
1419                 if (!strcmp(argv[i], "write") && (i + 2 < argc)) {
1420                         char *target = argv[i+1];
1421                         uint64_t offset = atol(argv[i+2]);
1422                         ret = cmd_write(target, offset);
1423                         i += 2;
1424                         continue;
1425                 }
1426
1427                 if (!strcmp(argv[i], "truncate") && (i + 2 < argc)) {
1428                         char *target = argv[i+1];
1429                         uint64_t offset = atol(argv[i+2]);
1430                         ret = cmd_truncate(target, offset);
1431                         i += 2;
1432                         continue;
1433                 }
1434
1435                 if (!strcmp(argv[i], "delete") && (i + 1 < argc)) {
1436                         char *target = argv[i+1];
1437                         ret = cmd_delete(target);
1438                         i += 1;
1439                         continue;
1440                 }
1441
1442                 if (!strcmp(argv[i], "acquire") && (i + 1 < argc)) {
1443                         char *target = argv[i+1];
1444                         ret = cmd_acquire(target);
1445                         i += 1;
1446                         continue;
1447                 }
1448
1449                 if (!strcmp(argv[i], "release") && (i + 1 < argc)) {
1450                         char *target = argv[i+1];
1451                         ret = cmd_release(target);
1452                         i += 1;
1453                         continue;
1454                 }
1455
1456                 if (!strcmp(argv[i], "copy") && (i + 2) < argc) {
1457                         char *src = argv[i+1];
1458                         char *dst = argv[i+2];
1459                         ret = cmd_copy(src, dst);
1460                         i += 2;
1461                         continue;
1462                 }
1463
1464                 if (!strcmp(argv[i], "clone") && (i + 2 < argc)) {
1465                         char *src = argv[i+1];
1466                         char *dst = argv[i+2];
1467                         ret = cmd_clone(src, dst);
1468                         i += 2;
1469                         continue;
1470                 }
1471
1472                 if (!strcmp(argv[i], "info") && (i + 1 < argc)) {
1473                         char *target = argv[i+1];
1474                         ret = cmd_info(target);
1475                         i += 1;
1476                         continue;
1477                 }
1478
1479
1480                 if (!parse_ports(argv[i]))
1481                         fprintf(stderr, "invalid argument: %s\n", argv[i]);
1482         }
1483
1484         /* xseg_leave(); */
1485         return ret;
1486 }