• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright David Abrahams 2004. Distributed under the Boost */
2 /* Software License, Version 1.0. (See accompanying */
3 /* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
4 
5 #include "jam.h"
6 #include "jam_strings.h"
7 
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 
13 #ifndef NDEBUG
14 # define JAM_STRING_MAGIC ((char)0xcf)
15 # define JAM_STRING_MAGIC_SIZE 4
assert_invariants(string * self)16 static void assert_invariants( string * self )
17 {
18     int i;
19 
20     if ( self->value == 0 )
21     {
22         assert( self->size == 0 );
23         assert( self->capacity == 0 );
24         assert( self->opt[ 0 ] == 0 );
25         return;
26     }
27 
28     assert( self->size < self->capacity );
29     assert( ( self->capacity <= sizeof( self->opt ) ) == ( self->value == self->opt ) );
30     assert( self->value[ self->size ] == 0 );
31     /* String objects modified manually after construction to contain embedded
32      * '\0' characters are considered structurally valid.
33      */
34     assert( strlen( self->value ) <= self->size );
35 
36     for ( i = 0; i < 4; ++i )
37     {
38         assert( self->magic[ i ] == JAM_STRING_MAGIC );
39         assert( self->value[ self->capacity + i ] == JAM_STRING_MAGIC );
40     }
41 }
42 #else
43 # define JAM_STRING_MAGIC_SIZE 0
44 # define assert_invariants(x) do {} while (0)
45 #endif
46 
47 
string_new(string * s)48 void string_new( string * s )
49 {
50     s->value = s->opt;
51     s->size = 0;
52     s->capacity = sizeof( s->opt );
53     s->opt[ 0 ] = 0;
54 #ifndef NDEBUG
55     memset( s->magic, JAM_STRING_MAGIC, sizeof( s->magic ) );
56 #endif
57     assert_invariants( s );
58 }
59 
60 
string_free(string * s)61 void string_free( string * s )
62 {
63     assert_invariants( s );
64     if ( s->value != s->opt )
65         BJAM_FREE( s->value );
66     string_new( s );
67 }
68 
69 
string_reserve_internal(string * self,size_t capacity)70 static void string_reserve_internal( string * self, size_t capacity )
71 {
72     if ( self->value == self->opt )
73     {
74         self->value = (char *)BJAM_MALLOC_ATOMIC( capacity +
75             JAM_STRING_MAGIC_SIZE );
76         self->value[ 0 ] = 0;
77         size_t opt_size = sizeof(self->opt); // Workaround sizeof in strncat warning.
78         strncat( self->value, self->opt, opt_size );
79         assert( strlen( self->value ) <= self->capacity && "Regression test" );
80     }
81     else
82     {
83         self->value = (char *)BJAM_REALLOC( self->value, capacity +
84             JAM_STRING_MAGIC_SIZE );
85     }
86 #ifndef NDEBUG
87     memcpy( self->value + capacity, self->magic, JAM_STRING_MAGIC_SIZE );
88 #endif
89     self->capacity = capacity;
90 }
91 
92 
string_reserve(string * self,size_t capacity)93 void string_reserve( string * self, size_t capacity )
94 {
95     assert_invariants( self );
96     if ( capacity <= self->capacity )
97         return;
98     string_reserve_internal( self, capacity );
99     assert_invariants( self );
100 }
101 
102 
maybe_reserve(string * self,size_t new_size)103 static void maybe_reserve( string * self, size_t new_size )
104 {
105     size_t capacity = self->capacity;
106     if ( capacity <= new_size )
107     {
108         size_t new_capacity = capacity;
109         while ( new_capacity <= new_size )
110             new_capacity <<= 1;
111         string_reserve_internal( self, new_capacity );
112     }
113 }
114 
115 
string_append(string * self,char const * rhs)116 void string_append( string * self, char const * rhs )
117 {
118     size_t rhs_size = strlen( rhs );
119     size_t new_size = self->size + rhs_size;
120     assert_invariants( self );
121 
122     maybe_reserve( self, new_size );
123 
124     memcpy( self->value + self->size, rhs, rhs_size + 1 );
125     self->size = new_size;
126 
127     assert_invariants( self );
128 }
129 
130 
string_append_range(string * self,char const * start,char const * finish)131 void string_append_range( string * self, char const * start, char const * finish )
132 {
133     size_t rhs_size = finish - start;
134     size_t new_size = self->size + rhs_size;
135     assert_invariants( self );
136 
137     maybe_reserve( self, new_size );
138 
139     if ( start != finish )
140         memcpy( self->value + self->size, start, rhs_size );
141     self->size = new_size;
142     self->value[ new_size ] = 0;
143 
144     assert_invariants( self );
145 }
146 
147 
string_copy(string * s,char const * rhs)148 void string_copy( string * s, char const * rhs )
149 {
150     string_new( s );
151     string_append( s, rhs );
152 }
153 
string_truncate(string * self,size_t n)154 void string_truncate( string * self, size_t n )
155 {
156     assert_invariants( self );
157     assert( n <= self->capacity );
158     self->value[ self->size = n ] = 0;
159     assert_invariants( self );
160 }
161 
162 
string_pop_back(string * self)163 void string_pop_back( string * self )
164 {
165     string_truncate( self, self->size - 1 );
166 }
167 
168 
string_push_back(string * self,char x)169 void string_push_back( string * self, char x )
170 {
171     string_append_range( self, &x, &x + 1 );
172 }
173 
174 
string_back(string * self)175 char string_back( string * self )
176 {
177     assert_invariants( self );
178     return self->value[ self->size - 1 ];
179 }
180 
string_rtrim(string * self)181 void string_rtrim( string * self )
182 {
183     char *p;
184     assert_invariants( self );
185     p = self->value + self->size - 1;
186     for ( ; p >= self->value && ( *p == '\0' || isspace( *p ) ); *p-- = 0 );
187 }
188 
189 #ifndef NDEBUG
string_unit_test()190 void string_unit_test()
191 {
192     {
193         string s[ 1 ];
194         unsigned long i;
195         unsigned long const limit = sizeof( s->opt ) * 2 + 2;
196         string_new( s );
197         assert( s->value == s->opt );
198         for ( i = 0; i < limit; ++i )
199         {
200             string_push_back( s, (char)( i + 1 ) );
201             assert( s->size == i + 1 );
202         }
203         assert( s->size == limit );
204         assert( s->value != s->opt );
205         for ( i = 0; i < limit; ++i )
206             assert( s->value[ i ] == (char)( i + 1 ) );
207         string_free( s );
208     }
209 
210     {
211         const char * const original = "  \n\t\v  Foo \r\n\v \tBar\n\n\r\r\t\n\v\t \t";
212         string copy[ 1 ];
213         string_copy( copy, original );
214         assert( !strcmp( copy->value, original ) );
215         assert( copy->size == strlen( original ) );
216         string_free( copy );
217     }
218 
219     {
220         const char * const foo = "Foo    ";
221         string foo_copy[ 1 ];
222         string_copy( foo_copy, foo );
223         string_rtrim( foo_copy );
224         assert( !strcmp( foo_copy->value, "Foo" ) );
225 
226         string_rtrim( foo_copy );
227         assert( !strcmp( foo_copy->value, "Foo" ) );
228     }
229     {
230         const char * const bar = "Bar\0\0\0";
231         string bar_copy[ 1 ];
232         string_copy( bar_copy, bar );
233         string_rtrim( bar_copy );
234         assert( !strcmp( bar_copy->value, "Bar" ) );
235 
236         string_rtrim( bar_copy );
237         assert( !strcmp( bar_copy->value, "Bar" ) );
238     }
239 }
240 #endif
241