[29] | 1 | /* |
---|
| 2 | * Copyright 1993, 1995 Christopher Seiwald. |
---|
| 3 | * |
---|
| 4 | * This file is part of Jam - see jam.c for Copyright information. |
---|
| 5 | */ |
---|
| 6 | |
---|
| 7 | # include "jam.h" |
---|
| 8 | # include "newstr.h" |
---|
| 9 | # include "hash.h" |
---|
| 10 | # include "compile.h" |
---|
| 11 | # include <stddef.h> |
---|
| 12 | # include <stdlib.h> |
---|
| 13 | |
---|
| 14 | /* |
---|
| 15 | * newstr.c - string manipulation routines |
---|
| 16 | * |
---|
| 17 | * To minimize string copying, string creation, copying, and freeing |
---|
| 18 | * is done through newstr. |
---|
| 19 | * |
---|
| 20 | * External functions: |
---|
| 21 | * |
---|
| 22 | * newstr() - return a dynamically allocated copy of a string |
---|
| 23 | * copystr() - return a copy of a string previously returned by newstr() |
---|
| 24 | * freestr() - free a string returned by newstr() or copystr() |
---|
| 25 | * donestr() - free string tables |
---|
| 26 | * |
---|
| 27 | * Once a string is passed to newstr(), the returned string is readonly. |
---|
| 28 | * |
---|
| 29 | * This implementation builds a hash table of all strings, so that multiple |
---|
| 30 | * calls of newstr() on the same string allocate memory for the string once. |
---|
| 31 | * Strings are never actually freed. |
---|
| 32 | */ |
---|
| 33 | |
---|
| 34 | typedef char *STRING; |
---|
| 35 | |
---|
| 36 | static struct hash *strhash = 0; |
---|
| 37 | static int strtotal = 0; |
---|
| 38 | static int strcount_in = 0; |
---|
| 39 | static int strcount_out = 0; |
---|
| 40 | |
---|
| 41 | /* |
---|
| 42 | * Immortal string allocator implementation speeds string allocation |
---|
| 43 | * and cuts down on internal fragmentation |
---|
| 44 | */ |
---|
| 45 | |
---|
| 46 | # define STRING_BLOCK 4096 |
---|
| 47 | typedef struct strblock |
---|
| 48 | { |
---|
| 49 | struct strblock* next; |
---|
| 50 | char data[STRING_BLOCK]; |
---|
| 51 | } strblock; |
---|
| 52 | |
---|
| 53 | static strblock* strblock_chain = 0; |
---|
| 54 | |
---|
| 55 | /* Storage remaining in the current strblock */ |
---|
| 56 | static char* storage_start = 0; |
---|
| 57 | static char* storage_finish = 0; |
---|
| 58 | |
---|
| 59 | /* */ |
---|
| 60 | #define SIMPLE_ALLOC 0 |
---|
| 61 | /*/ |
---|
| 62 | #define SIMPLE_ALLOC 1 |
---|
| 63 | /* */ |
---|
| 64 | |
---|
| 65 | /* |
---|
| 66 | * allocate() - Allocate n bytes of immortal string storage |
---|
| 67 | */ |
---|
| 68 | static char* allocate(size_t n) |
---|
| 69 | { |
---|
| 70 | #if SIMPLE_ALLOC |
---|
| 71 | return (char*)malloc(n); |
---|
| 72 | #else |
---|
| 73 | /* See if we can grab storage from an existing block */ |
---|
| 74 | size_t remaining = storage_finish - storage_start; |
---|
| 75 | if ( remaining >= n ) |
---|
| 76 | { |
---|
| 77 | char* result = storage_start; |
---|
| 78 | storage_start += n; |
---|
| 79 | return result; |
---|
| 80 | } |
---|
| 81 | else /* Must allocate a new block */ |
---|
| 82 | { |
---|
| 83 | strblock* new_block; |
---|
| 84 | size_t nalloc = n; |
---|
| 85 | if ( nalloc < STRING_BLOCK ) |
---|
| 86 | nalloc = STRING_BLOCK; |
---|
| 87 | |
---|
| 88 | /* allocate a new block and link into the chain */ |
---|
| 89 | new_block = (strblock*)malloc( offsetof( strblock, data[0] ) + nalloc * sizeof(new_block->data[0]) ); |
---|
| 90 | if ( new_block == 0 ) |
---|
| 91 | return 0; |
---|
| 92 | new_block->next = strblock_chain; |
---|
| 93 | strblock_chain = new_block; |
---|
| 94 | |
---|
| 95 | /* Take future allocations out of the larger remaining space */ |
---|
| 96 | if ( remaining < nalloc - n ) |
---|
| 97 | { |
---|
| 98 | storage_start = new_block->data + n; |
---|
| 99 | storage_finish = new_block->data + nalloc; |
---|
| 100 | } |
---|
| 101 | return new_block->data; |
---|
| 102 | } |
---|
| 103 | #endif |
---|
| 104 | } |
---|
| 105 | |
---|
| 106 | /* |
---|
| 107 | * newstr() - return a dynamically allocated copy of a string |
---|
| 108 | */ |
---|
| 109 | |
---|
| 110 | char * |
---|
| 111 | newstr( char *string ) |
---|
| 112 | { |
---|
| 113 | STRING str, *s = &str; |
---|
| 114 | |
---|
| 115 | if( !strhash ) |
---|
| 116 | strhash = hashinit( sizeof( STRING ), "strings" ); |
---|
| 117 | |
---|
| 118 | *s = string; |
---|
| 119 | |
---|
| 120 | if( hashenter( strhash, (HASHDATA **)&s ) ) |
---|
| 121 | { |
---|
| 122 | int l = strlen( string ); |
---|
| 123 | char *m = (char *)allocate( l + 1 ); |
---|
| 124 | |
---|
| 125 | strtotal += l + 1; |
---|
| 126 | memcpy( m, string, l + 1 ); |
---|
| 127 | *s = m; |
---|
| 128 | |
---|
| 129 | if ( DEBUG_PROFILE ) |
---|
| 130 | profile_memory( l+1 ); |
---|
| 131 | } |
---|
| 132 | |
---|
| 133 | strcount_in += 1; |
---|
| 134 | return *s; |
---|
| 135 | } |
---|
| 136 | |
---|
| 137 | /* |
---|
| 138 | * copystr() - return a copy of a string previously returned by newstr() |
---|
| 139 | */ |
---|
| 140 | |
---|
| 141 | char * |
---|
| 142 | copystr( char *s ) |
---|
| 143 | { |
---|
| 144 | strcount_in += 1; |
---|
| 145 | return s; |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | /* |
---|
| 149 | * freestr() - free a string returned by newstr() or copystr() |
---|
| 150 | */ |
---|
| 151 | |
---|
| 152 | void |
---|
| 153 | freestr( char *s ) |
---|
| 154 | { |
---|
| 155 | strcount_out += 1; |
---|
| 156 | } |
---|
| 157 | |
---|
| 158 | /* |
---|
| 159 | * donestr() - free string tables |
---|
| 160 | */ |
---|
| 161 | |
---|
| 162 | void |
---|
| 163 | donestr() |
---|
| 164 | { |
---|
| 165 | /* Reclaim string blocks */ |
---|
| 166 | while ( strblock_chain != 0 ) |
---|
| 167 | { |
---|
| 168 | strblock* n = strblock_chain->next; |
---|
| 169 | free(strblock_chain); |
---|
| 170 | strblock_chain = n; |
---|
| 171 | } |
---|
| 172 | |
---|
| 173 | hashdone( strhash ); |
---|
| 174 | |
---|
| 175 | if( DEBUG_MEM ) |
---|
| 176 | printf( "%dK in strings\n", strtotal / 1024 ); |
---|
| 177 | |
---|
| 178 | /* printf( "--- %d strings of %d dangling\n", strcount_in-strcount_out, strcount_in ); */ |
---|
| 179 | } |
---|