docs/src/modules-ucx2.md

changeset 628
1e2be40f0cb5
parent 390
d345541018fa
child 717
aa17be68fc66
equal deleted inserted replaced
627:cc8cbabd27cd 628:1e2be40f0cb5
73 // make the array as small as possible 73 // make the array as small as possible
74 ucx_array_shrink(result); 74 ucx_array_shrink(result);
75 return result; 75 return result;
76 } 76 }
77 77
78 /* ... */ 78 // ...
79 79
80 sstr_t* array = /* some standard array of strings */ 80 sstr_t* array = // some standard array of strings
81 size_t arrlen = /* the length of the array */ 81 size_t arrlen = // the length of the array
82 82
83 UcxArray* result = create_unique(array,arrlen); 83 UcxArray* result = create_unique(array,arrlen);
84 84
85 /* Iterate over the array and print the elements */ 85 // Iterate over the array and print the elements
86 sstr_t* unique = result->data; 86 sstr_t* unique = result->data;
87 for (size_t i = 0 ; i < result->size ; i++) { 87 for (size_t i = 0 ; i < result->size ; i++) {
88 printf("%" PRIsstr "\n", SFMT(unique[i])); 88 printf("%" PRIsstr "\n", SFMT(unique[i]));
89 } 89 }
90 90
91 /* Free the array. */ 91 // Free the array.
92 ucx_array_free(result); 92 ucx_array_free(result);
93 ``` 93 ```
94 ### Preventing out of bounds writes 94 ### Preventing out of bounds writes
95 95
96 The functions `ucx_array_reserve()`, `ucx_array_resize()`, `ucx_array_grow()`, 96 The functions `ucx_array_reserve()`, `ucx_array_resize()`, `ucx_array_grow()`,
126 126
127 Suppose you have a list of items which contain a `time_t` value and your task 127 Suppose you have a list of items which contain a `time_t` value and your task
128 is to find all items within a time window `[t_start, t_end]`. 128 is to find all items within a time window `[t_start, t_end]`.
129 With AVL Trees this is easy: 129 With AVL Trees this is easy:
130 ```C 130 ```C
131 /* --------------------- 131 // Somewhere in a header
132 * Somewhere in a header
133 */
134 typedef struct { 132 typedef struct {
135 time_t ts; 133 time_t ts;
136 /* other important data */ 134 // other important data
137 } MyObject; 135 } MyObject;
138 136
139 /* ----------- 137 // Source code
140 * Source code
141 */
142
143 UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint); 138 UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint);
144 /* ... populate tree with objects, use '& MyObject.ts' as key ... */ 139 // ... populate tree with objects, use '& MyObject.ts' as key ...
145 140
146 141
147 /* Now find every item, with 30 <= ts <= 70 */ 142 // Now find every item, with 30 <= ts <= 70
148 time_t ts_start = 30; 143 time_t ts_start = 30;
149 time_t ts_end = 70; 144 time_t ts_end = 70;
150 145
151 printf("Values in range:\n"); 146 printf("Values in range:\n");
152 for ( 147 for (
210 205
211 const size_t chunksize = 256; 206 const size_t chunksize = 256;
212 207
213 UcxBuffer* linebuf = 208 UcxBuffer* linebuf =
214 ucx_buffer_new( 209 ucx_buffer_new(
215 NULL, /* the buffer should manage the memory area for us */ 210 NULL, // the buffer should manage the memory area for us
216 2*chunksize, /* initial size should be twice the chunk size */ 211 2*chunksize, // initial size should be twice the chunk size
217 UCX_BUFFER_AUTOEXTEND); /* the buffer will grow when necessary */ 212 UCX_BUFFER_AUTOEXTEND); // the buffer will grow when necessary
218 213
219 size_t lineno = 1; 214 size_t lineno = 1;
220 do { 215 do {
221 /* read line chunk */ 216 // read line chunk
222 size_t read = ucx_stream_ncopy( 217 size_t read = ucx_stream_ncopy(
223 input, linebuf, fread, ucx_buffer_write, chunksize); 218 input, linebuf, fread, ucx_buffer_write, chunksize);
224 if (read == 0) break; 219 if (read == 0) break;
225 220
226 /* handle line endings */ 221 // handle line endings
227 do { 222 do {
228 sstr_t bufstr = ucx_buffer_to_sstr(linebuf); 223 sstr_t bufstr = ucx_buffer_to_sstr(linebuf);
229 sstr_t nl = sstrchr(bufstr, '\n'); 224 sstr_t nl = sstrchr(bufstr, '\n');
230 if (nl.length == 0) break; 225 if (nl.length == 0) break;
231 226
232 size_t linelen = bufstr.length - nl.length; 227 size_t linelen = bufstr.length - nl.length;
233 sstr_t linestr = sstrsubsl(bufstr, 0, linelen); 228 sstr_t linestr = sstrsubsl(bufstr, 0, linelen);
234 229
235 printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr)); 230 printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr));
236 231
237 /* shift the buffer to the next line */ 232 // shift the buffer to the next line
238 ucx_buffer_shift_left(linebuf, linelen+1); 233 ucx_buffer_shift_left(linebuf, linelen+1);
239 } while(1); 234 } while(1);
240 235
241 } while(1); 236 } while(1);
242 237
243 /* print the 'noeol' line, if any */ 238 // print the 'noeol' line, if any
244 sstr_t lastline = ucx_buffer_to_sstr(linebuf); 239 sstr_t lastline = ucx_buffer_to_sstr(linebuf);
245 if (lastline.length > 0) { 240 if (lastline.length > 0) {
246 printf("%zu: %" PRIsstr, lineno, SFMT(lastline)); 241 printf("%zu: %" PRIsstr, lineno, SFMT(lastline));
247 } 242 }
248 243
284 } 279 }
285 } 280 }
286 return list; 281 return list;
287 } 282 }
288 283
289 /* we will need this function to clean up the list contents later */ 284 // we will need this function to clean up the list contents later
290 void free_sstr(void* ptr) { 285 void free_sstr(void* ptr) {
291 sstr_t* s = ptr; 286 sstr_t* s = ptr;
292 free(s->ptr); 287 free(s->ptr);
293 free(s); 288 free(s);
294 } 289 }
295 290
296 /* ... */ 291 // ...
297 292
298 sstr_t* array = /* some array of strings */ 293 sstr_t* array = // some array of strings
299 size_t arrlen = /* the length of the array */ 294 size_t arrlen = // the length of the array
300 295
301 UcxList* list = remove_duplicates(array,arrlen); 296 UcxList* list = remove_duplicates(array,arrlen);
302 297
303 /* Iterate over the list and print the elements */ 298 // Iterate over the list and print the elements
304 UCX_FOREACH(elem, list) { 299 UCX_FOREACH(elem, list) {
305 sstr_t s = *((sstr_t*)elem->data); 300 sstr_t s = *((sstr_t*)elem->data);
306 printf("%" PRIsstr "\n", SFMT(s)); 301 printf("%" PRIsstr "\n", SFMT(s));
307 } 302 }
308 303
309 /* Use our free function to free the duplicated strings. */ 304 // Use our free function to free the duplicated strings.
310 ucx_list_free_content(list, free_sstr); 305 ucx_list_free_content(list, free_sstr);
311 ucx_list_free(list); 306 ucx_list_free(list);
312 ``` 307 ```
313 308
314 ## Logging 309 ## Logging
390 } 385 }
391 } else if(option) { 386 } else if(option) {
392 ucx_map_cstr_put(options, option, arg); 387 ucx_map_cstr_put(options, option, arg);
393 option = NULL; 388 option = NULL;
394 } else { 389 } else {
395 /* ... handle argument that is not an option ... */ 390 // ... handle argument that is not an option ...
396 } 391 }
397 } 392 }
398 if(option) { 393 if(option) {
399 fprintf(stderr, 394 fprintf(stderr,
400 "Missing argument for option -%c\n", optchar); 395 "Missing argument for option -%c\n", optchar);
461 FILE *f = fopen("test.csv", "r"); 456 FILE *f = fopen("test.csv", "r");
462 if (!f) { 457 if (!f) {
463 perror("Cannot open file"); 458 perror("Cannot open file");
464 return 1; 459 return 1;
465 } 460 }
466 /* close the file automatically at pool destruction*/ 461 // close the file automatically at pool destruction
467 ucx_mempool_reg_destr(pool, f, (ucx_destructor) fclose); 462 ucx_mempool_reg_destr(pool, f, (ucx_destructor) fclose);
468 463
469 /* create a buffer and register it at the memory pool for destruction */ 464 // create a buffer and register it at the memory pool for destruction
470 UcxBuffer* content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); 465 UcxBuffer* content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
471 ucx_mempool_reg_destr(pool, content, (ucx_destructor) ucx_buffer_free); 466 ucx_mempool_reg_destr(pool, content, (ucx_destructor) ucx_buffer_free);
472 467
473 /* read the file and split it by lines first */ 468 // read the file and split it by lines first
474 ucx_stream_copy(f, content, fread, ucx_buffer_write); 469 ucx_stream_copy(f, content, fread, ucx_buffer_write);
475 sstr_t contentstr = ucx_buffer_to_sstr(content); 470 sstr_t contentstr = ucx_buffer_to_sstr(content);
476 ssize_t lc = 0; 471 ssize_t lc = 0;
477 sstr_t* lines = sstrsplit_a(pool->allocator, contentstr, S("\n"), &lc); 472 sstr_t* lines = sstrsplit_a(pool->allocator, contentstr, S("\n"), &lc);
478 473
479 /* skip the header and parse the remaining data */ 474 // skip the header and parse the remaining data
480 UcxList* datalist = NULL; 475 UcxList* datalist = NULL;
481 for (size_t i = 1 ; i < lc ; i++) { 476 for (size_t i = 1 ; i < lc ; i++) {
482 if (lines[i].length == 0) continue; 477 if (lines[i].length == 0) continue;
483 ssize_t fc = 3; 478 ssize_t fc = 3;
484 sstr_t* fields = sstrsplit_a(pool->allocator, lines[i], S(";"), &fc); 479 sstr_t* fields = sstrsplit_a(pool->allocator, lines[i], S(";"), &fc);
492 data->column_b = fields[1]; 487 data->column_b = fields[1];
493 data->column_c = fields[2]; 488 data->column_c = fields[2];
494 datalist = ucx_list_append_a(pool->allocator, datalist, data); 489 datalist = ucx_list_append_a(pool->allocator, datalist, data);
495 } 490 }
496 491
497 /* control output */ 492 // control output
498 UCX_FOREACH(elem, datalist) { 493 UCX_FOREACH(elem, datalist) {
499 CSVData* data = elem->data; 494 CSVData* data = elem->data;
500 printf("Column A: %" PRIsstr " | " 495 printf("Column A: %" PRIsstr " | "
501 "Column B: %" PRIsstr " | " 496 "Column B: %" PRIsstr " | "
502 "Column C: %" PRIsstr "\n", 497 "Column C: %" PRIsstr "\n",
503 SFMT(data->column_a), SFMT(data->column_b), SFMT(data->column_c) 498 SFMT(data->column_a), SFMT(data->column_b), SFMT(data->column_c)
504 ); 499 );
505 } 500 }
506 501
507 /* cleanup everything, no manual free() needed */ 502 // cleanup everything, no manual free() needed
508 ucx_mempool_destroy(pool); 503 ucx_mempool_destroy(pool);
509 504
510 return 0; 505 return 0;
511 } 506 }
512 ``` 507 ```
517 memory is not supposed to be freed with a simple call to `free()`. 512 memory is not supposed to be freed with a simple call to `free()`.
518 In this case, you can overwrite the default destructor as follows: 513 In this case, you can overwrite the default destructor as follows:
519 ```C 514 ```C
520 MyObject* obj = ucx_mempool_malloc(pool, sizeof(MyObject)); 515 MyObject* obj = ucx_mempool_malloc(pool, sizeof(MyObject));
521 516
522 /* some special initialization with own resource management */ 517 // some special initialization with own resource management
523 my_object_init(obj); 518 my_object_init(obj);
524 519
525 /* register destructor function */ 520 // register destructor function
526 ucx_mempool_set_destr(obj, (ucx_destructor) my_object_destroy); 521 ucx_mempool_set_destr(obj, (ucx_destructor) my_object_destroy);
527 ``` 522 ```
528 Be aware, that your destructor function should not free any memory, that is 523 Be aware, that your destructor function should not free any memory, that is
529 also managed by the pool. 524 also managed by the pool.
530 Otherwise you might be risking a double-free. 525 Otherwise you might be risking a double-free.
542 The key/value pairs are stored within an UCX Map. 537 The key/value pairs are stored within an UCX Map.
543 538
544 ### Example: Loading properties from a file 539 ### Example: Loading properties from a file
545 540
546 ```C 541 ```C
547 /* Open the file as usual */ 542 // Open the file as usual
548 FILE* file = fopen("myprops.properties", "r"); 543 FILE* file = fopen("myprops.properties", "r");
549 if (!file) { 544 if (!file) {
550 // error handling 545 // error handling
551 return 1; 546 return 1;
552 } 547 }
553 548
554 /* Load the properties from the file */ 549 // Load the properties from the file
555 UcxMap* myprops = ucx_map_new(16); 550 UcxMap* myprops = ucx_map_new(16);
556 if (ucx_properties_load(myprops, file)) { 551 if (ucx_properties_load(myprops, file)) {
557 /* ... error handling ... */ 552 // ... error handling ...
558 fclose(file); 553 fclose(file);
559 ucx_map_free(myprops); 554 ucx_map_free(myprops);
560 return 1; 555 return 1;
561 } 556 }
562 557
563 /* Print out the key/value pairs */ 558 // Print out the key/value pairs
564 char* propval; 559 char* propval;
565 UcxMapIterator propiter = ucx_map_iterator(myprops); 560 UcxMapIterator propiter = ucx_map_iterator(myprops);
566 UCX_MAP_FOREACH(key, propval, propiter) { 561 UCX_MAP_FOREACH(key, propval, propiter) {
567 printf("%s = %s\n", (char*)key.data, propval); 562 printf("%s = %s\n", (char*)key.data, propval);
568 } 563 }
569 564
570 /* Don't forget to free the values before freeing the map */ 565 // Don't forget to free the values before freeing the map
571 ucx_map_free_content(myprops, NULL); 566 ucx_map_free_content(myprops, NULL);
572 ucx_map_free(myprops); 567 ucx_map_free(myprops);
573 fclose(file); 568 fclose(file);
574 ``` 569 ```
575 570
597 int i = 42; 592 int i = 42;
598 float f = 3.14f; 593 float f = 3.14f;
599 const char* str = "Hello!"; 594 const char* str = "Hello!";
600 size_t strn = 7; 595 size_t strn = 7;
601 596
602 /* push the integer */ 597 // push the integer
603 ucx_stack_push(&stack, sizeof(int), &i); 598 ucx_stack_push(&stack, sizeof(int), &i);
604 599
605 /* push the float and rember the address */ 600 // push the float and rember the address
606 float* remember = ucx_stack_push(&stack, sizeof(float), &f); 601 float* remember = ucx_stack_push(&stack, sizeof(float), &f);
607 602
608 /* push the string with zero terminator */ 603 // push the string with zero terminator
609 ucx_stack_push(&stack, strn, str); 604 ucx_stack_push(&stack, strn, str);
610 605
611 /* if we forget, how big an element was, we can ask the stack */ 606 // if we forget, how big an element was, we can ask the stack
612 printf("Length of string: %zu\n", ucx_stack_topsize(&stack)-1); 607 printf("Length of string: %zu\n", ucx_stack_topsize(&stack)-1);
613 608
614 /* retrieve the string as sstr_t, without zero terminator! */ 609 // retrieve the string as sstr_t, without zero terminator!
615 sstr_t s; 610 sstr_t s;
616 s.length = ucx_stack_topsize(&stack)-1; 611 s.length = ucx_stack_topsize(&stack)-1;
617 s.ptr = malloc(s.length); 612 s.ptr = malloc(s.length);
618 ucx_stack_popn(&stack, s.ptr, s.length); 613 ucx_stack_popn(&stack, s.ptr, s.length);
619 printf("%" PRIsstr "\n", SFMT(s)); 614 printf("%" PRIsstr "\n", SFMT(s));
620 615
621 /* print the float directly from the stack and free it */ 616 // print the float directly from the stack and free it
622 printf("Float: %f\n", *remember); 617 printf("Float: %f\n", *remember);
623 ucx_stack_free(&stack, remember); 618 ucx_stack_free(&stack, remember);
624 619
625 /* the last element is the integer */ 620 // the last element is the integer
626 int j; 621 int j;
627 ucx_stack_pop(&stack, &j); 622 ucx_stack_pop(&stack, &j);
628 printf("Integer: %d\n", j); 623 printf("Integer: %d\n", j);
629 ``` 624 ```
630 625
645 ### Initialization 640 ### Initialization
646 641
647 There are several ways to create an `sstr_t`: 642 There are several ways to create an `sstr_t`:
648 643
649 ```C 644 ```C
650 /* (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated */ 645 // (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated
651 sstr_t a = sstr(cstr); 646 sstr_t a = sstr(cstr);
652 647
653 /* (2) cstr does not need to be zero-terminated, if length is specified */ 648 // (2) cstr does not need to be zero-terminated, if length is specified
654 sstr_t b = sstrn(cstr, len); 649 sstr_t b = sstrn(cstr, len);
655 650
656 /* (3) S() macro creates sstr_t from a string using sizeof() and using sstrn(). 651 // (3) S() macro creates sstr_t from a string using sizeof() and using sstrn().
657 This version is especially useful for function arguments */ 652 // This version is especially useful for function arguments
658 sstr_t c = S("hello"); 653 sstr_t c = S("hello");
659 654
660 /* (4) SC() macro works like S(), but makes the string immutable using scstr_t. 655 // (4) SC() macro works like S(), but makes the string immutable using scstr_t.
661 (available since UCX 2.0) */ 656 // (available since UCX 2.0)
662 scstr_t d = SC("hello"); 657 scstr_t d = SC("hello");
663 658
664 /* (5) ST() macro creates sstr_t struct literal using sizeof() */ 659 // (5) ST() macro creates sstr_t struct literal using sizeof()
665 sstr_t e = ST("hello"); 660 sstr_t e = ST("hello");
666 ``` 661 ```
667 662
668 You should not use the `S()`, `SC()`, or `ST()` macro with string of unknown 663 You should not use the `S()`, `SC()`, or `ST()` macro with string of unknown
669 origin, since the `sizeof()` call might not coincide with the string length in 664 origin, since the `sizeof()` call might not coincide with the string length in
714 709
715 ```C 710 ```C
716 sstr_t test = ST("here::are::some::strings"); 711 sstr_t test = ST("here::are::some::strings");
717 sstr_t delim = ST("::"); 712 sstr_t delim = ST("::");
718 713
719 ssize_t count = 0; /* no limit */ 714 ssize_t count = 0; // no limit
720 UcxMempool* pool = ucx_mempool_new_default(); 715 UcxMempool* pool = ucx_mempool_new_default();
721 716
722 sstr_t* result = sstrsplit_a(pool->allocator, test, delim, &count); 717 sstr_t* result = sstrsplit_a(pool->allocator, test, delim, &count);
723 for (ssize_t i = 0 ; i < count ; i++) { 718 for (ssize_t i = 0 ; i < count ; i++) {
724 /* don't forget to specify the length via the %*s format specifier */ 719 // don't forget to specify the length via the %*s format specifier
725 printf("%*s\n", result[i].length, result[i].ptr); 720 printf("%*s\n", result[i].length, result[i].ptr);
726 } 721 }
727 722
728 ucx_mempool_destroy(pool); 723 ucx_mempool_destroy(pool);
729 ``` 724 ```
760 define test subroutines. 755 define test subroutines.
761 756
762 You should declare test cases and subroutines in a header file per test unit 757 You should declare test cases and subroutines in a header file per test unit
763 and implement them as you would implement normal functions. 758 and implement them as you would implement normal functions.
764 ```C 759 ```C
765 /* myunit.h */ 760 // myunit.h
766 UCX_TEST(function_name); 761 UCX_TEST(function_name);
767 UCX_TEST_SUBROUTINE(subroutine_name, paramlist); /* optional */ 762 UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
768 763
769 764
770 /* myunit.c */ 765 // myunit.c
771 UCX_TEST_SUBROUTINE(subroutine_name, paramlist) { 766 UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
772 /* ... reusable tests with UCX_TEST_ASSERT() ... */ 767 // ... reusable tests with UCX_TEST_ASSERT() ...
773 } 768 }
774 769
775 UCX_TEST(function_name) { 770 UCX_TEST(function_name) {
776 /* ... resource allocation and other test preparation ... */ 771 // ... resource allocation and other test preparation ...
777 772
778 /* mandatory marker for the start of the tests */ 773 // mandatory marker for the start of the tests
779 UCX_TEST_BEGIN 774 UCX_TEST_BEGIN
780 775
781 /* ... verifications with UCX_TEST_ASSERT() ... 776 // ... verifications with UCX_TEST_ASSERT() ...
782 * (and/or calls with UCX_TEST_CALL_SUBROUTINE()) 777 // (and/or calls with UCX_TEST_CALL_SUBROUTINE())
783 */ 778
784 779 // mandatory marker for the end of the tests
785 /* mandatory marker for the end of the tests */
786 UCX_TEST_END 780 UCX_TEST_END
787 781
788 /* ... resource cleanup ... 782 // ... resource cleanup ...
789 * (all code after UCX_TEST_END is always executed) 783 // (all code after UCX_TEST_END is always executed)
790 */
791 } 784 }
792 ``` 785 ```
793 If you want to use the `UCX_TEST_ASSERT()` macro in a function, you are 786 If you want to use the `UCX_TEST_ASSERT()` macro in a function, you are
794 *required* to use a `UCX_TEST_SUBROUTINE`. 787 *required* to use a `UCX_TEST_SUBROUTINE`.
795 Otherwise the testing framework does not know where to jump, when the assertion 788 Otherwise the testing framework does not know where to jump, when the assertion
798 After implementing the tests, you can easily build a test suite and execute it: 791 After implementing the tests, you can easily build a test suite and execute it:
799 ```C 792 ```C
800 UcxTestSuite* suite = ucx_test_suite_new(); 793 UcxTestSuite* suite = ucx_test_suite_new();
801 ucx_test_register(suite, testMyTestCase01); 794 ucx_test_register(suite, testMyTestCase01);
802 ucx_test_register(suite, testMyTestCase02); 795 ucx_test_register(suite, testMyTestCase02);
803 /* ... */ 796 // ...
804 ucx_test_run(suite, stdout); /* stdout, or any other FILE stream */ 797 ucx_test_run(suite, stdout); // stdout, or any other FILE stream
805 ``` 798 ```
806 799
807 ## Utilities 800 ## Utilities
808 801
809 *Header file:* [utils.h](api-2.1/utils_8h.html) 802 *Header file:* [utils.h](api-2.1/utils_8h.html)
829 if (argc != 3) { 822 if (argc != 3) {
830 fprintf(stderr, "Use %s <src> <dest>", argv[0]); 823 fprintf(stderr, "Use %s <src> <dest>", argv[0]);
831 return 1; 824 return 1;
832 } 825 }
833 826
834 FILE *srcf = fopen(argv[1], "r"); /* insert error handling on your own */ 827 FILE *srcf = fopen(argv[1], "r"); // insert error handling on your own
835 FILE *destf = fopen(argv[2], "w"); 828 FILE *destf = fopen(argv[2], "w");
836 829
837 size_t n = ucx_stream_copy(srcf, destf, fread, fwrite); 830 size_t n = ucx_stream_copy(srcf, destf, fread, fwrite);
838 printf("%zu bytes copied.\n", n); 831 printf("%zu bytes copied.\n", n);
839 832
864 for (unsigned int i = 2 ; i < 100 ; i++) { 857 for (unsigned int i = 2 ; i < 100 ; i++) {
865 ucx_bprintf(strbuffer, "Integer %d is %s\n", 858 ucx_bprintf(strbuffer, "Integer %d is %s\n",
866 i, prime(i) ? "prime" : "not prime"); 859 i, prime(i) ? "prime" : "not prime");
867 } 860 }
868 861
869 /* print the result to stdout */ 862 // print the result to stdout
870 printf("%s", (char*)strbuffer->space); 863 printf("%s", (char*)strbuffer->space);
871 864
872 ucx_buffer_free(strbuffer); 865 ucx_buffer_free(strbuffer);
873 ``` 866 ```

mercurial