| 681 | | #if defined(DEBUG) |
| 682 | | // # define DUMP_STRSTRUCT_MEMUSE |
| 683 | | #endif // DEBUG |
| 684 | | |
| 685 | | |
| 686 | | struct GBS_strstruct { |
| 687 | | char *GBS_strcat_data; |
| 688 | | long GBS_strcat_data_size; |
| 689 | | long GBS_strcat_pos; |
| | 681 | class GBS_strstruct : Noncopyable { |
| | 682 | char *data; |
| | 683 | size_t buffer_size; |
| | 684 | size_t pos; |
| | 685 | |
| | 686 | void set_pos(size_t toPos) { |
| | 687 | pos = toPos; |
| | 688 | data[pos] = 0; |
| | 689 | } |
| | 690 | void inc_pos(size_t inc) { set_pos(pos+inc); } |
| | 691 | |
| | 692 | public: |
| | 693 | |
| | 694 | GBS_strstruct() |
| | 695 | : data(NULL) |
| | 696 | , buffer_size(0) |
| | 697 | , pos(0) |
| | 698 | {} |
| | 699 | ~GBS_strstruct() { free(data); } |
| | 700 | |
| | 701 | size_t get_buffer_size() const { return buffer_size; } |
| | 702 | size_t get_position() const { return pos; } |
| | 703 | |
| | 704 | const char *get_data() const { return data; } |
| | 705 | |
| | 706 | char *release_mem(size_t& size) { |
| | 707 | char *result = data; |
| | 708 | size = buffer_size; |
| | 709 | buffer_size = 0; |
| | 710 | data = 0; |
| | 711 | return result; |
| | 712 | } |
| | 713 | |
| | 714 | void reset_pos() { set_pos(0); } |
| | 715 | |
| | 716 | void assign_mem(char *block, size_t blocksize) { |
| | 717 | free(data); |
| | 718 | |
| | 719 | gb_assert(block && blocksize>0); |
| | 720 | |
| | 721 | data = block; |
| | 722 | buffer_size = blocksize; |
| | 723 | |
| | 724 | reset_pos(); |
| | 725 | } |
| | 726 | void reassign_mem(GBS_strstruct& from) { |
| | 727 | size_t size; |
| | 728 | char *block = from.release_mem(size); |
| | 729 | |
| | 730 | assign_mem(block, size); |
| | 731 | } |
| | 732 | |
| | 733 | |
| | 734 | void alloc_mem(size_t blocksize) { |
| | 735 | gb_assert(blocksize>0); |
| | 736 | gb_assert(!data); |
| | 737 | |
| | 738 | assign_mem((char*)malloc(blocksize), blocksize); |
| | 739 | } |
| | 740 | void realloc_mem(size_t newsize) { |
| | 741 | if (!data) alloc_mem(newsize); |
| | 742 | else { |
| | 743 | data = (char*)realloc(data, newsize); |
| | 744 | buffer_size = newsize; |
| | 745 | |
| | 746 | gb_assert(pos<newsize); |
| | 747 | } |
| | 748 | } |
| | 749 | |
| | 750 | void ensure_mem(size_t needed_size) { |
| | 751 | // ensures insertion of 'needed_size' bytes is ok |
| | 752 | size_t whole_needed_size = pos+needed_size+1; |
| | 753 | if (buffer_size<whole_needed_size) { |
| | 754 | size_t next_size = (whole_needed_size * 3) >> 1; |
| | 755 | realloc_mem(next_size); |
| | 756 | } |
| | 757 | } |
| | 758 | |
| | 759 | // -------------------- |
| | 760 | |
| | 761 | void cut_tail(size_t byte_count) { |
| | 762 | set_pos(pos<byte_count ? 0 : pos-byte_count); |
| | 763 | } |
| | 764 | |
| | 765 | void put(char c) { |
| | 766 | ensure_mem(1); |
| | 767 | data[pos] = c; |
| | 768 | inc_pos(1); |
| | 769 | } |
| | 770 | void nput(char c, size_t count) { |
| | 771 | ensure_mem(count); |
| | 772 | memset(data+pos, c, count); |
| | 773 | inc_pos(count); |
| | 774 | } |
| | 775 | |
| | 776 | void ncat(const char *from, size_t count) { |
| | 777 | if (count) { |
| | 778 | ensure_mem(count); |
| | 779 | memcpy(data+pos, from, count); |
| | 780 | inc_pos(count); |
| | 781 | } |
| | 782 | } |
| | 783 | void cat(const char *from) { ncat(from, strlen(from)); } |
| | 784 | |
| | 785 | void vprintf(size_t maxlen, const char *templat, va_list& parg) { |
| | 786 | ensure_mem(maxlen); |
| | 787 | |
| | 788 | char *buffer = data+pos; |
| | 789 | int printed; |
| | 790 | |
| | 791 | #ifdef LINUX |
| | 792 | printed = vsnprintf(buffer, maxlen, templat, parg); |
| | 793 | #else |
| | 794 | printed = vsprintf(buffer, templat, parg); |
| | 795 | #endif |
| | 796 | |
| | 797 | assert_or_exit(printed >= 0 && (size_t)printed <= maxlen); |
| | 798 | inc_pos(printed); |
| | 799 | } |
| 692 | | static GBS_strstruct *last_used = 0; |
| 693 | | |
| 694 | | GBS_strstruct *GBS_stropen(long init_size) { // opens a memory file |
| 695 | | GBS_strstruct *strstr; |
| 696 | | |
| 697 | | if (last_used && last_used->GBS_strcat_data_size >= init_size) { |
| 698 | | strstr = last_used; |
| 699 | | last_used = 0; |
| | 802 | static GBS_strstruct last_used; |
| | 803 | |
| | 804 | GBS_strstruct *GBS_stropen(long init_size) { |
| | 805 | /*! create a new memory file |
| | 806 | * @param init_size estimated used size |
| | 807 | */ |
| | 808 | |
| | 809 | GBS_strstruct *strstr = new GBS_strstruct; |
| | 810 | |
| | 811 | gb_assert(init_size>0); |
| | 812 | |
| | 813 | if (last_used.get_buffer_size() >= (size_t)init_size) { |
| | 814 | strstr->reassign_mem(last_used); |
| | 815 | |
| | 816 | static short oversized_counter = 0; |
| | 817 | |
| | 818 | if ((size_t)init_size*10 < strstr->get_buffer_size()) oversized_counter++; |
| | 819 | else oversized_counter = 0; |
| | 820 | |
| | 821 | if (oversized_counter>10) { // was oversized more than 10 times -> realloc |
| | 822 | size_t dummy; |
| | 823 | free(strstr->release_mem(dummy)); |
| | 824 | strstr->alloc_mem(init_size); |
| | 825 | } |
| 702 | | #if defined(DUMP_STRSTRUCT_MEMUSE) |
| 703 | | printf("allocating new GBS_strstruct (size = %li)\n", init_size); |
| 704 | | #endif // DUMP_STRSTRUCT_MEMUSE |
| 705 | | strstr = (GBS_strstruct *)malloc(sizeof(GBS_strstruct)); |
| 706 | | strstr->GBS_strcat_data_size = init_size; |
| 707 | | strstr->GBS_strcat_data = (char *)malloc((size_t)strstr->GBS_strcat_data_size); |
| 708 | | } |
| 709 | | |
| 710 | | strstr->GBS_strcat_pos = 0; |
| 711 | | strstr->GBS_strcat_data[0] = 0; |
| | 828 | strstr->alloc_mem(init_size); |
| | 829 | } |
| 731 | | if (last_used) { |
| 732 | | if (last_used->GBS_strcat_data_size < strstr->GBS_strcat_data_size) { // last_used is smaller -> keep this |
| 733 | | GBS_strstruct *tmp = last_used; |
| 734 | | last_used = strstr; |
| 735 | | strstr = tmp; |
| 736 | | } |
| 737 | | } |
| 738 | | else { |
| 739 | | static short oversized_counter = 0; |
| 740 | | |
| 741 | | if (strstr->GBS_strcat_pos*10 < strstr->GBS_strcat_data_size) oversized_counter++; |
| 742 | | else oversized_counter = 0; |
| 743 | | |
| 744 | | if (oversized_counter<10) { |
| 745 | | // keep strstruct for next call |
| 746 | | last_used = strstr; |
| 747 | | strstr = 0; |
| 748 | | } |
| 749 | | // otherwise the current strstruct was oversized 10 times -> free it |
| 750 | | } |
| 751 | | |
| 752 | | if (strstr) { |
| 753 | | #if defined(DUMP_STRSTRUCT_MEMUSE) |
| 754 | | printf("freeing GBS_strstruct (size = %li)\n", strstr->GBS_strcat_data_size); |
| 755 | | #endif // DUMP_STRSTRUCT_MEMUSE |
| 756 | | free(strstr->GBS_strcat_data); |
| 757 | | free(strstr); |
| 758 | | } |
| | 847 | size_t last_bsize = last_used.get_buffer_size(); |
| | 848 | size_t curr_bsize = strstr->get_buffer_size(); |
| | 849 | |
| | 850 | if (last_bsize < curr_bsize) { // last_used is smaller -> keep this |
| | 851 | last_used.reassign_mem(*strstr); |
| | 852 | } |
| | 853 | delete strstr; |
| 773 | | strstr->GBS_strcat_pos -= byte_count; |
| 774 | | if (strstr->GBS_strcat_pos < 0) strstr->GBS_strcat_pos = 0; |
| 775 | | strstr->GBS_strcat_data[strstr->GBS_strcat_pos] = 0; |
| 776 | | } |
| 777 | | |
| 778 | | static void gbs_strensure_mem(GBS_strstruct *strstr, long len) { |
| 779 | | if (strstr->GBS_strcat_pos + len + 2 >= strstr->GBS_strcat_data_size) { |
| 780 | | strstr->GBS_strcat_data_size = (strstr->GBS_strcat_pos+len+2)*3/2; |
| 781 | | strstr->GBS_strcat_data = (char *)realloc(strstr->GBS_strcat_data, strstr->GBS_strcat_data_size); |
| 782 | | #if defined(DUMP_STRSTRUCT_MEMUSE) |
| 783 | | printf("re-allocated GBS_strstruct to size = %li\n", strstr->GBS_strcat_data_size); |
| 784 | | #endif // DUMP_STRSTRUCT_MEMUSE |
| 785 | | } |
| | 868 | strstr->cut_tail(byte_count); |
| 814 | | gbs_strensure_mem(strstr, len+2); |
| 815 | | |
| 816 | | buffer = strstr->GBS_strcat_data+strstr->GBS_strcat_pos; |
| 817 | | |
| 818 | | #ifdef LINUX |
| 819 | | psize = vsnprintf(buffer, len, templat, parg); |
| 820 | | #else |
| 821 | | psize = vsprintf(buffer, templat, parg); |
| 822 | | #endif |
| 823 | | |
| 824 | | assert_or_exit(psize >= 0 && psize <= len); |
| 825 | | strstr->GBS_strcat_pos += psize; |
| | 887 | strstr->vprintf(len+2, templat, parg); |
| | 2023 | // -------------------------------------------------------------------------------- |
| | 2024 | |
| | 2025 | #if (UNIT_TESTS == 1) |
| | 2026 | |
| | 2027 | #include <test_unit.h> |
| | 2028 | |
| | 2029 | #define EXPECT_CONTENT(content) TEST_ASSERT_EQUAL(GBS_mempntr(strstr), content) |
| | 2030 | |
| | 2031 | void TEST_GBS_strstruct() { |
| | 2032 | { |
| | 2033 | GBS_strstruct *strstr = GBS_stropen(1000); EXPECT_CONTENT(""); |
| | 2034 | |
| | 2035 | GBS_chrncat(strstr, 'b', 3); EXPECT_CONTENT("bbb"); |
| | 2036 | GBS_intcat(strstr, 17); EXPECT_CONTENT("bbb17"); |
| | 2037 | GBS_chrcat(strstr, '_'); EXPECT_CONTENT("bbb17_"); |
| | 2038 | GBS_floatcat(strstr, 3.5); EXPECT_CONTENT("bbb17_3.500000"); |
| | 2039 | |
| | 2040 | TEST_ASSERT_EQUAL(GBS_memoffset(strstr), 14); |
| | 2041 | GBS_str_cut_tail(strstr, 13); EXPECT_CONTENT("b"); |
| | 2042 | GBS_strcat(strstr, "utter"); EXPECT_CONTENT("butter"); |
| | 2043 | GBS_strncat(strstr, "flying", 3); EXPECT_CONTENT("butterfly"); |
| | 2044 | |
| | 2045 | GBS_strnprintf(strstr, 200, "%c%s", ' ', "flutters"); |
| | 2046 | EXPECT_CONTENT("butterfly flutters"); |
| | 2047 | |
| | 2048 | free(GBS_strclose(strstr)); |
| | 2049 | } |
| | 2050 | { |
| | 2051 | // re-alloc smaller |
| | 2052 | GBS_strstruct *strstr = GBS_stropen(500); EXPECT_CONTENT(""); |
| | 2053 | GBS_strforget(strstr); |
| | 2054 | } |
| | 2055 | |
| | 2056 | // trigger downsize of oversized block |
| | 2057 | for (int i = 0; i<12; ++i) { |
| | 2058 | GBS_strstruct *strstr = GBS_stropen(10); |
| | 2059 | GBS_strforget(strstr); |
| | 2060 | } |
| | 2061 | |
| | 2062 | { |
| | 2063 | GBS_strstruct *strstr = GBS_stropen(10); |
| | 2064 | size_t oldbufsize = strstr->get_buffer_size(); |
| | 2065 | GBS_chrncat(strstr, 'x', 20); // trigger reallocation of buffer |
| | 2066 | |
| | 2067 | TEST_ASSERT(oldbufsize != strstr->get_buffer_size()); // did we reallocate? |
| | 2068 | EXPECT_CONTENT("xxxxxxxxxxxxxxxxxxxx"); |
| | 2069 | GBS_strforget(strstr); |
| | 2070 | } |
| | 2071 | } |
| | 2072 | |
| | 2073 | #endif |
| | 2074 | |