• 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 "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         strncat( self->value, self->opt, sizeof(self->opt) );
78         assert( strlen( self->value ) <= self->capacity && "Regression test" );
79     }
80     else
81     {
82         self->value = (char *)BJAM_REALLOC( self->value, capacity +
83             JAM_STRING_MAGIC_SIZE );
84     }
85 #ifndef NDEBUG
86     memcpy( self->value + capacity, self->magic, JAM_STRING_MAGIC_SIZE );
87 #endif
88     self->capacity = capacity;
89 }
90 
91 
string_reserve(string * self,size_t capacity)92 void string_reserve( string * self, size_t capacity )
93 {
94     assert_invariants( self );
95     if ( capacity <= self->capacity )
96         return;
97     string_reserve_internal( self, capacity );
98     assert_invariants( self );
99 }
100 
101 
extend_full(string * self,char const * start,char const * finish)102 static void extend_full( string * self, char const * start, char const * finish )
103 {
104     size_t new_size = self->capacity + ( finish - start );
105     size_t new_capacity = self->capacity;
106     size_t old_size = self->capacity;
107     while ( new_capacity < new_size + 1)
108         new_capacity <<= 1;
109     string_reserve_internal( self, new_capacity );
110     memcpy( self->value + old_size, start, new_size - old_size );
111     self->value[ new_size ] = 0;
112     self->size = new_size;
113 }
114 
maybe_reserve(string * self,size_t new_size)115 static void maybe_reserve( string * self, size_t new_size )
116 {
117     size_t capacity = self->capacity;
118     if ( capacity <= new_size )
119     {
120         size_t new_capacity = capacity;
121         while ( new_capacity <= new_size )
122             new_capacity <<= 1;
123         string_reserve_internal( self, new_capacity );
124     }
125 }
126 
127 
string_append(string * self,char const * rhs)128 void string_append( string * self, char const * rhs )
129 {
130     size_t rhs_size = strlen( rhs );
131     size_t new_size = self->size + rhs_size;
132     assert_invariants( self );
133 
134     maybe_reserve( self, new_size );
135 
136     memcpy( self->value + self->size, rhs, rhs_size + 1 );
137     self->size = new_size;
138 
139     assert_invariants( self );
140 }
141 
142 
string_append_range(string * self,char const * start,char const * finish)143 void string_append_range( string * self, char const * start, char const * finish )
144 {
145     size_t rhs_size = finish - start;
146     size_t new_size = self->size + rhs_size;
147     assert_invariants( self );
148 
149     maybe_reserve( self, new_size );
150 
151     if ( start != finish )
152         memcpy( self->value + self->size, start, rhs_size );
153     self->size = new_size;
154     self->value[ new_size ] = 0;
155 
156     assert_invariants( self );
157 }
158 
159 
string_copy(string * s,char const * rhs)160 void string_copy( string * s, char const * rhs )
161 {
162     string_new( s );
163     string_append( s, rhs );
164 }
165 
string_truncate(string * self,size_t n)166 void string_truncate( string * self, size_t n )
167 {
168     assert_invariants( self );
169     assert( n <= self->capacity );
170     self->value[ self->size = n ] = 0;
171     assert_invariants( self );
172 }
173 
174 
string_pop_back(string * self)175 void string_pop_back( string * self )
176 {
177     string_truncate( self, self->size - 1 );
178 }
179 
180 
string_push_back(string * self,char x)181 void string_push_back( string * self, char x )
182 {
183     string_append_range( self, &x, &x + 1 );
184 }
185 
186 
string_back(string * self)187 char string_back( string * self )
188 {
189     assert_invariants( self );
190     return self->value[ self->size - 1 ];
191 }
192 
string_rtrim(string * self)193 void string_rtrim( string * self )
194 {
195     char *p;
196     assert_invariants( self );
197     p = self->value + self->size - 1;
198     for ( ; p >= self->value && ( *p == '\0' || isspace( *p ) ); *p-- = 0 );
199 }
200 
201 #ifndef NDEBUG
string_unit_test()202 void string_unit_test()
203 {
204     {
205         string s[ 1 ];
206         int i;
207         int const limit = sizeof( s->opt ) * 2 + 2;
208         string_new( s );
209         assert( s->value == s->opt );
210         for ( i = 0; i < limit; ++i )
211         {
212             string_push_back( s, (char)( i + 1 ) );
213             assert( s->size == i + 1 );
214         }
215         assert( s->size == limit );
216         assert( s->value != s->opt );
217         for ( i = 0; i < limit; ++i )
218             assert( s->value[ i ] == (char)( i + 1 ) );
219         string_free( s );
220     }
221 
222     {
223         const char * const original = "  \n\t\v  Foo \r\n\v \tBar\n\n\r\r\t\n\v\t \t";
224         string copy[ 1 ];
225         string_copy( copy, original );
226         assert( !strcmp( copy->value, original ) );
227         assert( copy->size == strlen( original ) );
228         string_free( copy );
229     }
230 
231     {
232         const char * const foo = "Foo    ";
233         string foo_copy[ 1 ];
234         string_copy( foo_copy, foo );
235         string_rtrim( foo_copy );
236         assert( !strcmp( foo_copy->value, "Foo" ) );
237 
238         string_rtrim( foo_copy );
239         assert( !strcmp( foo_copy->value, "Foo" ) );
240     }
241     {
242         const char * const bar = "Bar\0\0\0";
243         string bar_copy[ 1 ];
244         string_copy( bar_copy, bar );
245         string_rtrim( bar_copy );
246         assert( !strcmp( bar_copy->value, "Bar" ) );
247 
248         string_rtrim( bar_copy );
249         assert( !strcmp( bar_copy->value, "Bar" ) );
250     }
251 }
252 #endif
253