/* Copyright David Abrahams 2004. Distributed under the Boost */ /* Software License, Version 1.0. (See accompanying */ /* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #include "jam.h" #include "strings.h" #include #include #include #ifndef NDEBUG # define JAM_STRING_MAGIC ((char)0xcf) # define JAM_STRING_MAGIC_SIZE 4 static void assert_invariants( string * self ) { int i; if ( self->value == 0 ) { assert( self->size == 0 ); assert( self->capacity == 0 ); assert( self->opt[ 0 ] == 0 ); return; } assert( self->size < self->capacity ); assert( ( self->capacity <= sizeof( self->opt ) ) == ( self->value == self->opt ) ); assert( self->value[ self->size ] == 0 ); /* String objects modified manually after construction to contain embedded * '\0' characters are considered structurally valid. */ assert( strlen( self->value ) <= self->size ); for ( i = 0; i < 4; ++i ) { assert( self->magic[ i ] == JAM_STRING_MAGIC ); assert( self->value[ self->capacity + i ] == JAM_STRING_MAGIC ); } } #else # define JAM_STRING_MAGIC_SIZE 0 # define assert_invariants(x) do {} while (0) #endif void string_new( string * s ) { s->value = s->opt; s->size = 0; s->capacity = sizeof( s->opt ); s->opt[ 0 ] = 0; #ifndef NDEBUG memset( s->magic, JAM_STRING_MAGIC, sizeof( s->magic ) ); #endif assert_invariants( s ); } void string_free( string * s ) { assert_invariants( s ); if ( s->value != s->opt ) BJAM_FREE( s->value ); string_new( s ); } static void string_reserve_internal( string * self, size_t capacity ) { if ( self->value == self->opt ) { self->value = (char *)BJAM_MALLOC_ATOMIC( capacity + JAM_STRING_MAGIC_SIZE ); self->value[ 0 ] = 0; strncat( self->value, self->opt, sizeof(self->opt) ); assert( strlen( self->value ) <= self->capacity && "Regression test" ); } else { self->value = (char *)BJAM_REALLOC( self->value, capacity + JAM_STRING_MAGIC_SIZE ); } #ifndef NDEBUG memcpy( self->value + capacity, self->magic, JAM_STRING_MAGIC_SIZE ); #endif self->capacity = capacity; } void string_reserve( string * self, size_t capacity ) { assert_invariants( self ); if ( capacity <= self->capacity ) return; string_reserve_internal( self, capacity ); assert_invariants( self ); } static void extend_full( string * self, char const * start, char const * finish ) { size_t new_size = self->capacity + ( finish - start ); size_t new_capacity = self->capacity; size_t old_size = self->capacity; while ( new_capacity < new_size + 1) new_capacity <<= 1; string_reserve_internal( self, new_capacity ); memcpy( self->value + old_size, start, new_size - old_size ); self->value[ new_size ] = 0; self->size = new_size; } static void maybe_reserve( string * self, size_t new_size ) { size_t capacity = self->capacity; if ( capacity <= new_size ) { size_t new_capacity = capacity; while ( new_capacity <= new_size ) new_capacity <<= 1; string_reserve_internal( self, new_capacity ); } } void string_append( string * self, char const * rhs ) { size_t rhs_size = strlen( rhs ); size_t new_size = self->size + rhs_size; assert_invariants( self ); maybe_reserve( self, new_size ); memcpy( self->value + self->size, rhs, rhs_size + 1 ); self->size = new_size; assert_invariants( self ); } void string_append_range( string * self, char const * start, char const * finish ) { size_t rhs_size = finish - start; size_t new_size = self->size + rhs_size; assert_invariants( self ); maybe_reserve( self, new_size ); memcpy( self->value + self->size, start, rhs_size ); self->size = new_size; self->value[ new_size ] = 0; assert_invariants( self ); } void string_copy( string * s, char const * rhs ) { string_new( s ); string_append( s, rhs ); } void string_truncate( string * self, size_t n ) { assert_invariants( self ); assert( n <= self->capacity ); self->value[ self->size = n ] = 0; assert_invariants( self ); } void string_pop_back( string * self ) { string_truncate( self, self->size - 1 ); } void string_push_back( string * self, char x ) { string_append_range( self, &x, &x + 1 ); } char string_back( string * self ) { assert_invariants( self ); return self->value[ self->size - 1 ]; } #ifndef NDEBUG void string_unit_test() { { string s[ 1 ]; int i; int const limit = sizeof( s->opt ) * 2 + 2; string_new( s ); assert( s->value == s->opt ); for ( i = 0; i < limit; ++i ) { string_push_back( s, (char)( i + 1 ) ); assert( s->size == i + 1 ); } assert( s->size == limit ); assert( s->value != s->opt ); for ( i = 0; i < limit; ++i ) assert( s->value[ i ] == (char)( i + 1 ) ); string_free( s ); } { char * const original = " \n\t\v Foo \r\n\v \tBar\n\n\r\r\t\n\v\t \t"; string copy[ 1 ]; string_copy( copy, original ); assert( !strcmp( copy->value, original ) ); assert( copy->size == strlen( original ) ); string_free( copy ); } } #endif