Class: AutoC::String
- Includes:
- Redirecting
- Defined in:
- lib/autoc/string.rb
Overview
String is wrapper around the standard null-terminated C string which has the capabilities of both a plain string and a string builder optimized for appending and incremental building.
It is ought to be on par with the raw C strings performance-wise (or even exceed it since the string size is tracked).
Unlike the plain C string, this String type has value type semantics but it can be turned into the reference type with Reference.
The String’s default character type, CharType, is char although this can be changed.
String generally obeys the Vector interface with respect to working with its characters and resembles the List interface when using its building capabilities.
Generated C interface
String management
void typeCopy(Type * dst, Type * src) |
Create a new string dst filled with a copy the contents of src. NOTE: Previous contents of dst is overwritten. |
void typeCopyRange(Type * dst, Type * src, size_t first, size_t last) |
Create a new string dst filled with a part the contents of src lying in the range [first, last], that is including the character at position first and including the character at position last. NOTE: Previous contents of dst is overwritten. WARNING: first must not exceed last (that is, first ⇐ last) and both indices must be valid otherwise behavior is undefined. See typeWithin(). |
void typeCtor(Type * self, const CharType * chars) |
Create a new string self with a copy of the null-terminated C string chars. NULL value of chars is permitted; this case corresponds to an empty string "". NOTE: Previous contents of self is overwritten. |
void typeDtor(Type * self) |
Destroy string self. |
int typeEqual(Type * lt, Type * rt) |
Return non-zero value if strings lt and rt are considered equal by contents and zero value otherwise. |
size_t typeIdentify(Type * self) |
Return hash code for string self. |
Basic operations
const CharType * typeChars(Type * self) |
Return a read-only view of the string in a form of the standard C null-terminated string. NOTE: the returned value need not to be freed. WARNING: the returned value should be considered volatile and thus may be altered or invalidated by a subsequent call to any String method! |
int typeEmpty(Type * self) |
Return non-zero value if string self has zero length and zero value otherwise. |
CharType typeGet(Type * self, size_t index) |
Return a copy of the character stored in self at position index. WARNING: index must be a valid index otherwise the behavior is undefined. See typeWithin(). |
void typeSet(Type * self, size_t index, CharType value) |
Store a copy of the character value in string self at position index. WARNING: index must be a valid index otherwise the behavior is undefined. See typeWithin(). |
size_t typeSize(Type * self) |
Return number of characters stored in string self. Note that this does not include the null terminator. |
int typeWithin(Type * self, size_t index) |
Return non-zero value if index is a valid character index and zero value otherwise. A valid index lies between 0 and typeSize()-1 inclusively. |
String buffer operations
Functions which provide the string buffer functionality. This allows the incremental building of strings without excessive storage copying/reallocation.
void typePushChars(Type * self, const CharType* chars) |
Append a copy of the null-terminated C string chars to string self. |
int typePushChar(Type * self, CharType value) |
Append a copy of the character value to string self. Return non-zero value on success and zero value on conversion error. NOTE: this convenience function applies generic formatting rules and is currently implemented as a macro; for more precise control over the formatting process use the typePushFormat() function. |
int typePushInt(Type * self, int value) |
Append string representation of the integer value to string self. Return non-zero value on success and zero value on conversion error. NOTE: this convenience function applies generic formatting rules and is currently implemented as a macro; for more precise control over the formatting process use the typePushFormat() function. |
int typePushFloat(Type * self, double value) |
Append string representation of the floating-point value to string self. Return non-zero value on success and zero value on conversion error. NOTE: this convenience function applies generic formatting rules and is currently implemented as a macro; for more precise control over the formatting process use the typePushFormat() function. |
int typePushPtr(Type * self, void* value) |
Append string representation of the pointer value to string self. Return non-zero value on success and zero value on conversion error. NOTE: this convenience function applies generic formatting rules and is currently implemented as a macro; for more precise control over the formatting process use the typePushFormat() function. |
void typePushString(Type * self, Type * from) |
Append a copy of the contents of string from to string self. |
int typePushFormat(Type * self, const char* format, …); |
Append the ?sprintf()- formatted string to string self. Return non-zero value on successful formatting and zero value if the call to ?sprintf() failed. The latter usually happens due to the encoding error. This function tries to use the vsnprintf() standard C function if possible and falls back to unsafe vsprintf() function which is ought to be present in every ANSI-compliant standard C library. The former function is used on the platforms which are known to have it; the Autotools-compliant HAVE_VSNPRINTF macro is also taken into consideration. Note that the choice is obviously made at compile-time. If using the vsnprintf() and the allocated buffer is not large enough this function continuously expands the buffer to eventually accommodate the resulting string. On the contrary, when the unsafe vsprintf() is used, the buffer overrun causes this function to abort() in order to possible data corruption not to slip away uncaught. Current implementation operates on the heap-allocated buffer whose initial size is determined by the AUTOC_BUFFER_SIZE macro. If not explicitly set it defaults to 4096 bytes. |
Iteration over string’s characters
void itCtor(IteratorType * it, Type * self) |
Create a new forward iterator it on string self. NOTE: Previous contents of it is overwritten. |
void itCtorEx(IteratorType * it, Type * self, int forward) |
Create a new iterator it on string self. Non-zero value of forward specifies a forward iterator, zero value specifies a backward iterator. NOTE: Previous contents of it is overwritten. |
int itMove(IteratorType * it) |
Advance iterator position of it and return non-zero value if new position is valid and zero value otherwise. |
CharType itGet(IteratorType * it) |
Return a copy of the character pointed to by the iterator it. WARNING: current position must be valid otherwise the behavior is undefined. See itMove(). |
Instance Attribute Summary collapse
-
#it_ref ⇒ Object
readonly
Returns the value of attribute it_ref.
Attributes inherited from Type
Instance Method Summary collapse
- #char_type ⇒ Object
- #char_type_ref ⇒ Object
-
#constructible? ⇒ Boolean
No default constructor provided.
-
#initialize(type_name = :String, visibility = :public) ⇒ String
constructor
A new instance of String.
- #write_impls(stream, define) ⇒ Object
- #write_intf_decls(stream, declare, define) ⇒ Object
- #write_intf_types(stream) ⇒ Object
Methods inherited from Type
#==, #abort, #assert, #calloc, coerce, #comparable?, #copyable?, #destructible?, #entities, #extern, #free, #hash, #hashable?, #initializable?, #inline, #malloc, #method_missing, #orderable?, #prefix, #private?, #public?, #sortable?, #static, #static?, #write_decls, #write_defs, #write_intf
Methods inherited from Code
#attach, #entities, #priority, #source_size, #write_decls, #write_defs, #write_intf
Constructor Details
#initialize(type_name = :String, visibility = :public) ⇒ String
Returns a new instance of String
206 207 208 209 210 211 212 |
# File 'lib/autoc/string.rb', line 206 def initialize(type_name = :String, visibility = :public) super @it_ref = "#{it}*" @list = List.new(list, {:type => char_type_ref, :dtor => free}, :private) # List takes ownership over the strings put into it hence the custom element destructor initialize_redirectors @ctor = define_redirector(:ctor, Function::Signature.new([type_ref^:self, "const #{char_type_ref}"^:chars])) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class AutoC::Type
Instance Attribute Details
#it_ref ⇒ Object (readonly)
Returns the value of attribute it_ref
204 205 206 |
# File 'lib/autoc/string.rb', line 204 def it_ref @it_ref end |
Instance Method Details
#char_type ⇒ Object
200 |
# File 'lib/autoc/string.rb', line 200 def char_type; :char end |
#char_type_ref ⇒ Object
202 |
# File 'lib/autoc/string.rb', line 202 def char_type_ref; "#{char_type}*" end |
#constructible? ⇒ Boolean
No default constructor provided
215 |
# File 'lib/autoc/string.rb', line 215 def constructible?; false end |
#write_impls(stream, define) ⇒ Object
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/autoc/string.rb', line 322 def write_impls(stream, define) super [@list].each {|obj| obj.write_intf_decls(stream, static, inline) obj.write_impls(stream, static) } stream << %$ #include <stdio.h> #include <stdarg.h> #undef AUTOC_VSNPRINTF #if defined(_MSC_VER) #define AUTOC_VSNPRINTF _vsnprintf #elif defined(__DMC__) || defined (__LCC__) #define AUTOC_VSNPRINTF vsnprintf #elif defined(HAVE_VSNPRINTF) || __STDC_VERSION__ >= 199901L /* Be Autotools-friendly, C99 must have snprintf() */ #define AUTOC_VSNPRINTF vsnprintf #endif #ifndef AUTOC_VSNPRINTF /* #warning Using unsafe vsprintf() function */ #endif #{define} void #{_join}(#{type_ref} self) { #{@list.it} it; #{char_type_ref} string; size_t* size; /* size sizes cache to avoid excessive calls to strlen() */ size_t i, start = 0, total = 0; #{assert}(self); #{assert}(self->is_list); if(!#{@list.empty}(&self->data.list)) { size = (size_t*)malloc(#{@list.size}(&self->data.list)*sizeof(size_t)); #{assert}(size); #{@list.itCtor}(&it, &self->data.list); for(i = 0; #{@list.itMove}(&it); ++i) { total += (size[i] = strlen(#{@list.itGet}(&it))); } string = (#{char_type_ref})#{malloc}((total + 1)*sizeof(#{char_type})); #{assert}(string); #{@list.itCtor}(&it, &self->data.list); /* List is a LIFO structure therefore merging should be performed from right to left */ i = 0; start = total; while(#{@list.itMove}(&it)) { start -= size[i]; memcpy(&string[start], #{@list.itGet}(&it), size[i]*sizeof(#{char_type})); ++i; } string[total] = '\\0'; #{free}(size); } else { string = (#{char_type_ref})#{calloc}(1, sizeof(#{char_type})); #{assert}(string); } #{@list.dtor}(&self->data.list); self->size = total; self->data.string = string; self->is_list = 0; } #{define} void #{_split}(#{type_ref} self) { #{@list.type} list; #{assert}(self); #{assert}(!self->is_list); #{@list.ctor}(&list); #{@list.push}(&list, self->data.string); /* self->size = strlen(self->data.string); not needed since the size shouldn't have changed */ #{assert}(self->size == strlen(self->data.string)); self->data.list = list; self->is_list = 1; } #{define} #{ctor.definition} { #{assert}(self); if(chars) { size_t nbytes; self->size = strlen(chars); nbytes = (self->size + 1)*sizeof(#{char_type}); self->data.string = (#{char_type_ref})#{malloc}(nbytes); #{assert}(self->data.string); memcpy(self->data.string, chars, nbytes); self->is_list = 0; } else { /* NULL argument is permitted and corresponds to empty string */ self->size = 0; #{@list.ctor}(&self->data.list); self->is_list = 1; } } #{define} #{dtor.definition} { #{assert}(self); if(self->is_list) #{@list.dtor}(&self->data.list); else #{free}(self->data.string); } #{define} #{copy.definition} { #{assert}(src); #{assert}(dst); #{assert}(src != dst); #{ctor}(dst, #{chars}(src)); } #{define} void #{copyRange}(#{type_ref} dst, #{type_ref} src, size_t first, size_t last) { size_t size; #{char_type_ref} string; #{assert}(src); #{assert}(src != dst); #{assert}(first <= last); #{assert}(#{within}(src, first)); #{assert}(#{within}(src, last)); size = last - first + 1; string = (#{char_type_ref})#{malloc}((size + 1)*sizeof(#{char_type})); #{assert}(string); memcpy(string, &#{chars}(src)[first], size*sizeof(#{char_type})); string[size] = '\\0'; #{ctor}(dst, string); #{free}(string); } #{define} #{equal.definition} { #{assert}(lt); #{assert}(rt); return strcmp(#{chars}(lt), #{chars}(rt)) == 0; } #{define} #{identify.definition} { size_t index, result = 0; #{assert}(self); #{join}(self); for(index = 0; index < #{size}(self); ++index) { result ^= self->data.string[index]; result = AUTOC_RCYCLE(result); } return result; } #{define} int #{pushFormat}(#{type_ref} self, const char* format, ...) { va_list args; char* buffer; int i, c, buffer_size = #{_bufferSize}; #{assert}(self); #{assert}(format); do { buffer = (char*)#{malloc}(buffer_size*sizeof(char)); #{assert}(buffer); va_start(args, format); #ifdef AUTOC_VSNPRINTF i = AUTOC_VSNPRINTF(buffer, buffer_size, format, args); #else i = vsprintf(buffer, format, args); if(i >= buffer_size) #{abort}(); /* Since vsprintf() can not truncate its output this means the buffer overflow and there is no guarantee that some useful data is not corrupted so its better to crash right here than to let the corruption slip away uncaught */ #endif c = (i > 0 && !(i < buffer_size)); if(i > 0 && !c) #{pushChars}(self, buffer); va_end(args); #{free}(buffer); buffer_size *= 2; } while(c); return i >= 0; } #{define} void #{pushChars}(#{type_ref} self, const #{char_type_ref} chars) { #{char_type_ref} string; size_t size, nbytes; #{assert}(self); #{assert}(chars); #{split}(self); size = strlen(chars); nbytes = (size + 1)*sizeof(#{char_type}); string = (#{char_type_ref})#{malloc}(nbytes); #{assert}(string); memcpy(string, chars, nbytes); #{@list.push}(&self->data.list, string); self->size += size; } #{define} void #{pushString}(#{type_ref} self, #{type_ref} from) { #{assert}(self); #{assert}(from); #{pushChars}(self, #{chars}(from)); } #undef AUTOC_SNPRINTF $ end |
#write_intf_decls(stream, declare, define) ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/autoc/string.rb', line 242 def write_intf_decls(stream, declare, define) super write_redirectors(stream, declare, define) stream << %$ #include <string.h> #ifdef AUTOC_BUFFER_SIZE #define #{_bufferSize} AUTOC_BUFFER_SIZE #else #define #{_bufferSize} 4096 /* Stay in sync with the documentation! */ #endif #define #{join}(self) if(self->is_list) #{_join}(self); #define #{split}(self) if(!self->is_list) #{_split}(self); #{declare} void #{_join}(#{type_ref}); #{declare} void #{_split}(#{type_ref}); #{declare} #{ctor.declaration}; #{declare} #{dtor.declaration}; #{declare} #{copy.declaration}; #{declare} void #{copyRange}(#{type_ref}, #{type_ref}, size_t, size_t); #{declare} #{equal.declaration}; #{declare} #{identify.declaration}; #define #{empty}(self) (#{size}(self) == 0) #{define} size_t #{size}(#{type_ref} self) { #{assert}(self); /* #{join}(self); assuming the changes to the contents are reflected in the size */ #ifndef NDEBUG /* Type invariants which must hold true */ if(!self->is_list) #{assert}(self->size == strlen(self->data.string)); /* TODO self->is_list case */ #endif return self->size; } #{define} int #{within}(#{type_ref} self, size_t index) { #{assert}(self); /* Omitting excessive call to #{join}() */ return index < #{size}(self); } #{define} #{char_type} #{get}(#{type_ref} self, size_t index) { #{assert}(self); #{assert}(#{within}(self, index)); #{join}(self); return self->data.string[index]; } #{define} void #{set}(#{type_ref} self, size_t index, #{char_type} value) { #{assert}(self); #{assert}(#{within}(self, index)); #{join}(self); self->data.string[index] = value; } #{define} const #{char_type_ref} #{chars}(#{type_ref} self) { #{assert}(self); #{join}(self); return self->data.string; } #{declare} int #{pushFormat}(#{type_ref}, const char*, ...); #{declare} void #{pushChars}(#{type_ref}, const #{char_type_ref}); #{declare} void #{pushString}(#{type_ref}, #{type_ref}); #define #{pushChar}(self, c) #{pushFormat}(self, "%c", (char)(c)) #define #{pushInt}(self, i) #{pushFormat}(self, "%d", (int)(i)) #define #{pushFloat}(self, f) #{pushFormat}(self, "%e", (double)(f)) #define #{pushPtr}(self, p) #{pushFormat}(self, "%p", (void*)(p)) #define #{itCtor}(self, type) #{itCtorEx}(self, type, 1) #{define} void #{itCtorEx}(#{it_ref} self, #{type_ref} string, int forward) { #{assert}(self); #{assert}(string); self->string = string; self->forward = forward; self->index = forward ? -1 : #{size}(string); } #{define} int #{itMove}(#{it_ref} self) { #{assert}(self); if(self->forward) ++self->index; else --self->index; return #{within}(self->string, self->index); } #{define} #{char_type} #{itGet}(#{it_ref} self) { #{assert}(self); return #{get}(self->string, self->index); } $ end |
#write_intf_types(stream) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/autoc/string.rb', line 217 def write_intf_types(stream) stream << %$ /*** **** #{type}<#{char_type}> ***/ $ if public? [@list].each {|obj| obj.write_intf_types(stream)} # TODO : this should be handled by the entity dependencies system stream << %$ typedef struct #{type} #{type}; typedef struct #{it} #{it}; struct #{type} { size_t size; union data { #{char_type_ref} string; #{@list.type} list; } data; int is_list; }; struct #{it} { #{type_ref} string; int index, forward; }; $ end |