source: doc/theses/mike_brooks_MMath/string.tex@ 6174ecc

Last change on this file since 6174ecc was 56ec508, checked in by Peter A. Buhr <pabuhr@…>, 6 months ago

more string API updates

  • Property mode set to 100644
File size: 69.6 KB
Line 
1\chapter{String}
2
3\vspace*{-20pt}
4This chapter presents my work on designing and building a modern string type in \CFA.
5The discussion starts with an overview of string API, then a number of interesting string problems, followed by how these issues are resolved in this work.
6
7
8\section{String Operations}
9
10\VRef[Figure]{f:StrApiCompare} shows a general comparison of string APIs for C, \CC, Java and \CFA.
11It provides a classic ``cheat sheet'', summarizing the names of the most-common closely-equivalent operations.
12The over-arching commonality is that operations work on groups of characters for assigning, copying, scanning, and updating.
13
14\begin{figure}[h]
15\begin{cquote}
16\begin{tabular}{@{}l|l|l|l@{}}
17C @char [ ]@ & \CC @string@ & Java @String@ & \CFA @string@ \\
18\hline
19@strcpy@, @strncpy@ & @=@ & @=@ & @=@ \\
20@strcat@, @strncat@ & @+@ & @+@ & @+@ \\
21@strcmp@, @strncmp@ & @==@, @!=@, @<@, @<=@, @>@, @>=@
22 & @equals@, @compareTo@ & @==@, @!=@, @<@, @<=@, @>@, @>=@ \\
23@strlen@ & @length@, @size@ & @length@ & @size@ \\
24@[ ]@ & @[ ]@ & @charAt@ & @[ ]@ \\
25@strncpy@ & @substr@ & @substring@ & @( )@ \\
26@strncpy@ & @replace@ & @replace@ & @=@ \emph{(on a substring)}\\
27@strstr@ & @find@ & @indexOf@ & @find@ \\
28@strcspn@ & @find_first_of@ & @matches@ & @include@ \\
29@strspn@ & @find_first_not_of@ & @matches@ & @exclude@ \\
30n/a & @c_str@, @data@ & n/a & @strcpy@, @strncpy@ \\
31\end{tabular}
32\end{cquote}
33\caption{Language comparison of string API}
34\label{f:StrApiCompare}
35\end{figure}
36
37As mentioned in \VRef{s:String}, a C string differs from other string types as it uses null termination rather than a length, which leads to explicit storage management;
38hence, most of its group operations are error prone and expensive.
39Most high-level string libraries use a separate length field and specialized storage management to implement group operations.
40Interestingly, \CC strings retain null termination in case it is needed to interface with C library functions.
41\begin{cfa}
42int open( @const char * pathname@, int flags );
43string fname{ "test.cc" );
44open( fname.@c_str()@, O_RDONLY ); // null terminated value of string
45\end{cfa}
46Here, the \CC @c_str@ function does not create a new null-terminated C string from the \CC string, as that requires passing ownership of the C string to the caller for eventual deletion.\footnote{
47C functions like \lstinline{strdup} do return allocated storage that must be freed by the caller.}
48% Instead, each \CC string is null terminated just in case it might be needed for this purpose.
49Providing this backwards compatibility with C has a ubiquitous performance and storage cost.
50
51
52\section{\CFA \lstinline{string} type}
53\label{s:stringType}
54
55The \CFA string type is for manipulation of dynamically-sized character-strings versus C @char *@ type for manipulation of statically-sized null-terminated character-strings.
56Hence, the amount of storage for a \CFA string changes dynamically at runtime to fit the string size, whereas the amount of storage for a C string is fixed at compile time.
57As a result, a @string@ declaration does not specify a maximum length, where a C string must.
58The maximum storage for a \CFA @string@ value is @size_t@ characters, which is $2^{32}$ or $2^{64}$ respectively.
59A \CFA string manages its length separately from the string, so there is no null (@'\0'@) terminating value at the end of a string value.
60Hence, a \CFA string cannot be passed to a C string manipulation routine, such as @strcat@.
61Like C strings, characters in a @string@ are numbered from the left starting at 0, and in \CFA numbered from the right starting at -1.
62\begin{cquote}
63\sf
64\begin{tabular}{@{}rrrrrl@{}}
65\small\tt a & \small\tt b & \small\tt c & \small\tt d & \small\tt e \\
660 & 1 & 2 & 3 & 4 & left to right index \\
67-5 & -4 & -3 & -2 & -1 & right to left index
68\end{tabular}
69\end{cquote}
70The following operations have been defined to manipulate an instance of type @string@.
71The discussion assumes the following declarations and assignment statements are executed.
72\begin{cfa}
73#include @<string.hfa>@
74@string@ s = "abcde", name = "MIKE", digit, alpha, punctuation, ifstmt;
75const char cs[] = "abc";
76int i;
77digit = "0123456789";
78punctuation = "().,";
79ifstmt = "IF (A > B) {";
80\end{cfa}
81Note, the include file @string.hfa@ to access type @string@.
82
83
84\subsection{Implicit String Conversions}
85
86The ability to convert from internal (machine) to external (human) format is useful in situations other than I/O.
87Hence, the basic types @char@, @char *@, @int@, @double@, @_Complex@, including any signness and size variations, implicitly convert to type @string@.
88\begin{cquote}
89\setlength{\tabcolsep}{15pt}
90\begin{tabular}{@{}l|ll|l@{}}
91\begin{cfa}
92// string s = 5;
93 s = 'x';
94 s = "abc";
95 s = cs;
96 s = 45hh;
97 s = 45h;
98\end{cfa}
99&
100\begin{cfa}
101
102"x"
103"abc"
104"abc"
105"45"
106"45"
107\end{cfa}
108&
109\begin{cfa}
110 s = (ssize_t)MIN;
111 s = (size_t)MAX;
112 s = 5.5;
113 s = 5.5L;
114 s = 5.5+3.4i;
115 s = 5.5L+3.4Li;
116\end{cfa}
117&
118\begin{cfa}
119"-9223372036854775808"
120"18446744073709551615"
121"5.5"
122"5.5"
123"5.5+3.4i"
124"5.5+3.4i"
125\end{cfa}
126\end{tabular}
127\end{cquote}
128Conversions can be explicitly specified using a compound literal.
129\begin{cfa}
130s = (string){ "abc" }; $\C{// converts char * to string}$
131s = (string){ 5 }; $\C{// converts int to string}$
132s = (string){ 5.5 }; $\C{// converts double to string}$
133\end{cfa}
134Conversions from @string@ to @char *@, attempt to be safe:
135either by requiring the maximum length of the @char *@ storage (@strncpy@) or allocating the @char *@ storage for the string characters (ownership), meaning the programmer must free the storage.
136Note, a C string is always null terminated, implying a minimum size of 1 character.
137\begin{cquote}
138\setlength{\tabcolsep}{15pt}
139\begin{tabular}{@{}l|l@{}}
140\begin{cfa}
141strncpy( cs, s, sizeof(cs) );
142char * cp = s;
143delete( cp );
144cp = s + ' ' + s;
145delete( cp );
146\end{cfa}
147&
148\begin{cfa}
149"abc\0", in place
150"abcde\0", malloc
151ownership
152"abcde abcde\0", malloc
153ownership
154\end{cfa}
155\end{tabular}
156\end{cquote}
157
158
159\subsection{Length}
160
161The @len@ operation (short for @strlen@) returns the length of a C or \CFA string.
162For consistency, @strlen@ also works with \CFA strings.
163\begin{cquote}
164\setlength{\tabcolsep}{15pt}
165\begin{tabular}{@{}l|l@{}}
166\begin{cfa}
167i = len( "" );
168i = len( "abc" );
169i = len( cs );
170i = strlen( cs );
171i = len( name );
172i = strlen( name );
173\end{cfa}
174&
175\begin{cfa}
1760
1773
1783
1793
1804
1814
182\end{cfa}
183\end{tabular}
184\end{cquote}
185
186
187\subsection{Comparison Operators}
188
189The binary relational, @<@, @<=@, @>@, @>=@, and equality, @==@, @!=@, operators compare strings using lexicographical ordering, where longer strings are greater than shorter strings.
190C strings use function @strcmp@, as the relational/equality operators compare C string pointers not their values, which does not match programmer expectation.
191
192
193\subsection{Concatenation}
194
195The binary operators @+@ and @+=@ concatenate characters, C strings and \CFA strings, creating the sum of the characters.
196\begin{cquote}
197\begin{tabular}{@{}l|l@{\hspace{25pt}}l|l@{\hspace{25pt}}l|l@{}}
198\begin{cfa}
199s = "";
200s = 'a' + 'b';
201s = 'a' + "b";
202s = "a" + 'b';
203s = "a" + "b";
204\end{cfa}
205&
206\begin{cfa}
207
208"ab"
209"ab"
210"ab"
211"ab"
212\end{cfa}
213&
214\begin{cfa}
215s = "";
216s = 'a' + 'b' + s;
217s = 'a' + 'b' + s;
218s = 'a' + "b" + s;
219s = "a" + 'b' + s;
220\end{cfa}
221&
222\begin{cfa}
223
224"ab"
225"abab"
226"ababab"
227"abababab"
228\end{cfa}
229&
230\begin{cfa}
231s = "";
232s = s + 'a' + 'b';
233s = s + 'a' + "b";
234s = s + "a" + 'b';
235s = s + "a" + "b";
236\end{cfa}
237&
238\begin{cfa}
239
240"ab"
241"abab"
242"ababab"
243"abababab"
244\end{cfa}
245\end{tabular}
246\end{cquote}
247For these operations to meet programmer expectations, \CFA introduces two C non-backward compatibilities.
248Note, subtracting pointers or characters has a low-level use case.
249\begin{cfa}
250ch - '0' $\C[2in]{// find character offset}$
251cp1 - cp2; $\C{// find pointer offset}\CRT$
252\end{cfa}
253However, there is no obvious use case for addition.
254\begin{cfa}
255ch + 'b' $\C[2in]{// add character values}$
256cp1 + 'a'; $\C{// move pointer cp1['a']}\CRT$
257\end{cfa}
258Adding character values or advancing a pointer with a character are unusual operations, and hence, unlikely to existing in C programs.
259Stealing these two cases for use with strings, allows all combinations of concatenation among @char@, @char *@, and @string@.
260Note, stealing only occurs if a program includes @string.hfa@, resulting is ambiguities in existing C code where there is no way to disambiguate.
261\begin{cfa}
262ch = 'a' + 'b'; $\C[2in]{// LHS disambiguate, add character values}$
263s = 'a' + 'b'; $\C{// LHS disambiguate, concatenation characters}$
264sout | 'a' + 'b'; $\C{// ambiguous with string.hfa, add or concatenate?}$
265sout | (char)'a' + 'b'; $\C{// disambiguate}$
266sout | "a" + "b"; $\C{// disambiguate}\CRT$
267\end{cfa}
268Again, the possibility of this scenario is extremely rare, as adding characters is meaningless.
269
270\CC cannot support this generality because it does not use the left-hand side of assignment in expression resolution.
271While it can special case some combinations:
272\begin{c++}
273s = 'a' + s; $\C[2in]{// compiles in C++}$
274s = "a" + s;
275\end{c++}
276it cannot generalize to any number of steps:
277\begin{c++}
278s = 'a' + 'b' + s; $\C{// does not compile in C++}\CRT$
279s = "a" + "b" + s;
280\end{c++}
281
282
283\subsection{Repetition}
284
285The binary operators @*@ and @*=@ repeat a string $N$ times.
286If $N = 0$, a zero length string, @""@, is returned.
287Like concatenation, multiplication is stolen for @char@;
288multiplication for pointers does not exist in C.
289\begin{cquote}
290\setlength{\tabcolsep}{15pt}
291\begin{tabular}{@{}l|l@{}}
292\begin{cfa}
293s = 'x' * 3;
294s = "abc" * 3;
295s = (name + ' ') * 3;
296\end{cfa}
297&
298\begin{cfa}
299"xxx"
300"abcabcabc"
301"MIKE MIKE MIKE "
302\end{cfa}
303\end{tabular}
304\end{cquote}
305
306
307\subsection{Substring}
308The substring operation returns a subset of a string starting at a position in the string and traversing a length or matching a pattern string.
309\begin{cquote}
310\setlength{\tabcolsep}{10pt}
311\begin{tabular}{@{}l|ll|l@{}}
312\begin{cfa}
313s = name( 2, 2 );
314s = name( 3, -2 );
315s = name( 2, 8 );
316s = name( 0, -1 );
317s = name( -1, -1 );
318s = name( -3 );
319\end{cfa}
320&
321\begin{cfa}
322"KE"
323"IK"
324"KE", clipped length to 2
325"", beyond string clipped to null
326"K"
327"IKE", to end of string
328\end{cfa}
329&
330\begin{cfa}
331s = name( "IK" );
332s = name( "WW" );
333
334
335
336
337\end{cfa}
338&
339\begin{cfa}
340"IK"
341""
342
343
344
345
346\end{cfa}
347\end{tabular}
348\end{cquote}
349A negative starting position is a specification from the right end of the string.
350A negative length means that characters are selected in the opposite (right to left) direction from the starting position.
351If the substring request extends beyond the beginning or end of the string, it is clipped (shortened) to the bounds of the string.
352If the substring request is completely outside of the original string, a null string is returned.
353The pattern form either returns the pattern string is the pattern matches or a null string if the pattern does not match.
354This mechanism is discussed next.
355
356The substring operation can also appear on the left side of an assignment and replaced by the string value on the right side.
357The length of the right string may be shorter, the same length, or longer than the length of left string.
358Hence, the left string may decrease, stay the same, or increase in length.
359\begin{cquote}
360\setlength{\tabcolsep}{15pt}
361\begin{tabular}{@{}l|l@{}}
362\begin{cfa}[escapechar={}]
363digit( 3, 3 ) = "";
364digit( 4, 3 ) = "xyz";
365digit( 7, 0 ) = "***";
366digit(-4, 3 ) = "$$$";
367digit( 5 ) = "LLL";
368\end{cfa}
369&
370\begin{cfa}[escapechar={}]
371"0126789"
372"0126xyz"
373"0126xyz"
374"012$$$z"
375"012$$LLL"
376\end{cfa}
377\end{tabular}
378\end{cquote}
379Pattern matching is useful on the left-hand side of the assignment.
380\begin{cquote}
381\setlength{\tabcolsep}{15pt}
382\begin{tabular}{@{}l|l@{}}
383\begin{cfa}[escapechar={}]
384digit( "$$" ) = "345";
385digit( "LLL") = "6789";
386\end{cfa}
387&
388\begin{cfa}
389"012345LLL"
390"0123456789"
391\end{cfa}
392\end{tabular}
393\end{cquote}
394Extending the pattern to a regular expression is a possible extension.
395
396
397\subsection{Searching}
398
399The @index@ operation
400\begin{cfa}
401int index( const string & key, int start = 1, occurrence occ = first );
402\end{cfa}
403returns the position of the first or last occurrence of the @key@ (depending on the occurrence indicator @occ@ that is either @first@ or @last@) in the current string starting the search at position @start@.
404If the @key@ does not appear in the current string, the length of the current string plus one is returned.
405%If the @key@ has zero length, the value 1 is returned regardless of what the current string contains.
406A negative starting position is a specification from the right end of the string.
407\begin{cquote}
408\setlength{\tabcolsep}{15pt}
409\begin{tabular}{@{}l|l@{}}
410\begin{cfa}
411i = find( digit, "567" );
412i = find( digit, "567", 7 );
413i = digit.index( "567", -1, last );
414i = name.index( "E", 5, last );
415\end{cfa}
416&
417\begin{cfa}
4185
41910
420
421
422\end{cfa}
423\end{tabular}
424\end{cquote}
425The next two string operations test a string to see if it is or is not composed completely of a particular class of characters.
426For example, are the characters of a string all alphabetic or all numeric?
427Use of these operations involves a two step operation.
428First, it is necessary to create an instance of type @strmask@ and initialize it to a string containing the characters of the particular character class, as in:
429\begin{cfa}
430strmask digitmask = digit;
431strmask alphamask = string( "abcdefghijklmnopqrstuvwxyz" );
432\end{cfa}
433Second, the character mask is used in the functions @include@ and @exclude@ to check a string for compliance of its characters with the characters indicated by the mask.
434
435The @include@ operation
436\begin{cfa}
437int include( const strmask &, int = 1, occurrence occ = first );
438\end{cfa}
439returns the position of the first or last character (depending on the occurrence indicator, which is either @first@ or @last@) in the current string that does not appear in the @mask@ starting the search at position @start@;
440hence it skips over characters in the current string that are included (in) the @mask@.
441The characters in the current string do not have to be in the same order as the @mask@.
442If all the characters in the current string appear in the @mask@, the length of the current string plus one is returned, regardless of which occurrence is being searched for.
443A negative starting position is a specification from the right end of the string.
444\begin{cfa}
445i = name.include( digitmask ); $\C{// i is assigned 1}$
446i = name.include( alphamask ); $\C{// i is assigned 6}$
447\end{cfa}
448
449The @exclude@ operation
450\begin{cfa}
451int exclude( string &mask, int start = 1, occurrence occ = first )
452\end{cfa}
453returns the position of the first or last character (depending on the occurrence indicator, which is either @first@ or @last@) in the current string that does appear in the @mask@ string starting the search at position @start@;
454hence it skips over characters in the current string that are excluded from (not in) in the @mask@ string.
455The characters in the current string do not have to be in the same order as the @mask@ string.
456If all the characters in the current string do NOT appear in the @mask@ string, the length of the current string plus one is returned, regardless of which occurrence is being searched for.
457A negative starting position is a specification from the right end of the string.
458\begin{cfa}
459i = name.exclude( digitmask ); $\C{// i is assigned 6}$
460i = ifstmt.exclude( strmask( punctuation ) ); $\C{// i is assigned 4}$
461\end{cfa}
462
463The @includeStr@ operation:
464\begin{cfa}
465string includeStr( strmask &mask, int start = 1, occurrence occ = first )
466\end{cfa}
467returns the longest substring of leading or trailing characters (depending on the occurrence indicator, which is either @first@ or @last@) of the current string that ARE included in the @mask@ string starting the search at position @start@.
468A negative starting position is a specification from the right end of the string.
469\begin{cfa}
470s = name.includeStr( alphamask ); $\C{// s is assigned "MIKE"}$
471s = ifstmt.includeStr( alphamask ); $\C{// s is assigned "IF"}$
472s = name.includeStr( digitmask ); $\C{// s is assigned ""}$
473\end{cfa}
474
475The @excludeStr@ operation:
476\begin{cfa}
477string excludeStr( strmask &mask, int start = 1, occurrence = first )
478\end{cfa}
479returns the longest substring of leading or trailing characters (depending on the occurrence indicator, which is either @first@ or @last@) of the current string that are excluded (NOT) in the @mask@ string starting the search at position @start@.
480A negative starting position is a specification from the right end of the string.
481\begin{cfa}
482s = name.excludeStr( digitmask); $\C{// s is assigned "MIKE"}$
483s = ifstmt.excludeStr( strmask( punctuation ) ); $\C{// s is assigned "IF "}$
484s = name.excludeStr( alphamask); $\C{// s is assigned ""}$
485\end{cfa}
486
487
488\subsection{Miscellaneous}
489
490The @trim@ operation
491\begin{cfa}
492string trim( string &mask, occurrence occ = first )
493\end{cfa}
494returns a string in that is the longest substring of leading or trailing characters (depending on the occurrence indicator, which is either @first@ or @last@) which ARE included in the @mask@ are removed.
495\begin{cfa}
496// remove leading blanks
497s = string( " ABC" ).trim( " " ); $\C{// s is assigned "ABC",}$
498// remove trailing blanks
499s = string( "ABC " ).trim( " ", last ); $\C{// s is assigned "ABC",}$
500\end{cfa}
501
502The @translate@ operation
503\begin{cfa}
504string translate( string &from, string &to )
505\end{cfa}
506returns a string that is the same length as the original string in which all occurrences of the characters that appear in the @from@ string have been translated into their corresponding character in the @to@ string.
507Translation is done on a character by character basis between the @from@ and @to@ strings; hence these two strings must be the same length.
508If a character in the original string does not appear in the @from@ string, then it simply appears as is in the resulting string.
509\begin{cfa}
510// upper to lower case
511name = name.translate( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz" );
512 // name is assigned "name"
513s = ifstmt.translate( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz" );
514 // ifstmt is assigned "if (a > b) {"
515// lower to upper case
516name = name.translate( "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
517 // name is assigned "MIKE"
518\end{cfa}
519
520The @replace@ operation
521\begin{cfa}
522string replace( string &from, string &to )
523\end{cfa}
524returns a string in which all occurrences of the @from@ string in the current string have been replaced by the @to@ string.
525\begin{cfa}
526s = name.replace( "E", "XX" ); $\C{// s is assigned "PXXTXXR"}$
527\end{cfa}
528The replacement is done left-to-right.
529When an instance of the @from@ string is found and changed to the @to@ string, it is NOT examined again for further replacement.
530
531
532\subsection{Returning N+1 on Failure}
533
534Any of the string search routines can fail at some point during the search.
535When this happens it is necessary to return indicating the failure.
536Many string types in other languages use some special value to indicate the failure.
537This value is often 0 or -1 (PL/I returns 0).
538This section argues that a value of N+1, where N is the length of the base string in the search, is a more useful value to return.
539The index-of function in APL returns N+1.
540These are the boundary situations and are often overlooked when designing a string type.
541
542The situation that can be optimized by returning N+1 is when a search is performed to find the starting location for a substring operation.
543For example, in a program that is extracting words from a text file, it is necessary to scan from left to right over whitespace until the first alphabetic character is found.
544\begin{cfa}
545line = line( line.exclude( alpha ) );
546\end{cfa}
547If a text line contains all whitespaces, the exclude operation fails to find an alphabetic character.
548If @exclude@ returns 0 or -1, the result of the substring operation is unclear.
549Most string types generate an error, or clip the starting value to 1, resulting in the entire whitespace string being selected.
550If @exclude@ returns N+1, the starting position for the substring operation is beyond the end of the string leaving a null string.
551
552The same situation occurs when scanning off a word.
553\begin{cfa}
554start = line.include(alpha);
555word = line(1, start - 1);
556\end{cfa}
557If the entire line is composed of a word, the include operation will fail to find a non-alphabetic character.
558In general, returning 0 or -1 is not an appropriate starting position for the substring, which must substring off the word leaving a null string.
559However, returning N+1 will substring off the word leaving a null string.
560
561
562\subsection{C Compatibility}
563
564To ease conversion from C to \CFA, there are companion @string@ routines for C strings.
565\VRef[Table]{t:CompanionStringRoutines} shows the C routines on the left that also work with @string@ and the rough equivalent @string@ operation of the right.
566Hence, it is possible to directly convert a block of C string operations into @string@ just by changing the
567
568\begin{table}
569\begin{cquote}
570\begin{tabular}{@{}l|l@{}}
571\multicolumn{1}{c|}{\lstinline{char []}} & \multicolumn{1}{c}{\lstinline{string}} \\
572\hline
573@strcpy@, @strncpy@ & @=@ \\
574@strcat@, @strncat@ & @+@ \\
575@strcmp@, @strncmp@ & @==@, @!=@, @<@, @<=@, @>@, @>=@ \\
576@strlen@ & @size@ \\
577@[]@ & @[]@ \\
578@strstr@ & @find@ \\
579@strcspn@ & @find_first_of@, @find_last_of@ \\
580@strspc@ & @find_fist_not_of@, @find_last_not_of@
581\end{tabular}
582\end{cquote}
583\caption{Companion Routines for \CFA \lstinline{string} to C Strings}
584\label{t:CompanionStringRoutines}
585\end{table}
586
587For example, this block of C code can be converted to \CFA by simply changing the type of variable @s@ from @char []@ to @string@.
588\begin{cfa}
589 char s[32];
590 //string s;
591 strcpy( s, "abc" ); PRINT( %s, s );
592 strncpy( s, "abcdef", 3 ); PRINT( %s, s );
593 strcat( s, "xyz" ); PRINT( %s, s );
594 strncat( s, "uvwxyz", 3 ); PRINT( %s, s );
595 PRINT( %zd, strlen( s ) );
596 PRINT( %c, s[3] );
597 PRINT( %s, strstr( s, "yzu" ) ) ;
598 PRINT( %s, strstr( s, 'y' ) ) ;
599\end{cfa}
600However, the conversion fails with I/O because @printf@ cannot print a @string@ using format code @%s@ because \CFA strings are not null terminated.
601
602
603\subsection{Parameter Passing}
604
605A substring is treated as a pointer into the base (substringed) string rather than creating a copy of the subtext.
606Hence, if the referenced item is changed, then the pointer sees the change.
607Pointers to the result value of a substring operation are defined to always start at the same location in their base string as long as that starting location exists, independent of changes to themselves or the base string.
608However, if the base string value changes, this may affect the values of one or more of the substrings to that base string.
609If the base string value shortens so that its end is before the starting location of a substring, resulting in the substring starting location disappearing, the substring becomes a null string located at the end of the base string.
610
611The following example illustrates passing the results of substring operations by reference and by value to a subprogram.
612Notice the side-effects to other reference parameters as one is modified.
613\begin{cfa}
614main() {
615 string x = "xxxxxxxxxxxxx";
616 test( x, x(1,3), x(3,3), x(5,5), x(9,5), x(9,5) );
617}
618
619// x, a, b, c, & d are substring results passed by reference
620// e is a substring result passed by value
621void test(string &x, string &a, string &b, string &c, string &d, string e) {
622 $\C{// x a b c d e}$
623 a( 1, 2 ) = "aaa"; $\C{// aaaxxxxxxxxxxx aaax axx xxxxx xxxxx xxxxx}$
624 b( 2, 12 ) = "bbb"; $\C{// aaabbbxxxxxxxxx aaab abbb bbxxx xxxxx xxxxx}$
625 c( 4, 5 ) = "ccc"; $\C{// aaabbbxcccxxxxxx aaab abbb bbxccc ccxxx xxxxx}$
626 c = "yyy"; $\C{// aaabyyyxxxxxx aaab abyy yyy xxxxx xxxxx}$
627 d( 1, 3 ) = "ddd"; $\C{// aaabyyyxdddxx aaab abyy yyy dddxx xxxxx}$
628 e( 1, 3 ) = "eee"; $\C{// aaabyyyxdddxx aaab abyy yyy dddxx eeexx}$
629 x = e; $\C{// eeexx eeex exx x eeexx}$
630}
631\end{cfa}
632
633
634\subsection{Input/Output Operators}
635
636Both the \CC operators @<<@ and @>>@ are defined on type @string@.
637However, input of a string value is different from input of a @char *@ value.
638When a string value is read, \emph{all} input characters from the current point in the input stream to either the end of line (@'\n'@) or the end of file are read.
639
640
641\section{Implementation}
642
643While \VRef[Figure]{f:StrApiCompare} emphasizes cross-language similarities, it elides many specific operational differences.
644For example, the @replace@ function selects a substring in the target and substitutes it with the source string, which can be smaller or larger than the substring.
645\CC performs the modification on the mutable receiver object
646\begin{cfa}
647string s1 = "abcde";
648s1.replace( 2, 3, "xy" ); $\C[2.25in]{// replace by position (zero origin) and length, mutable}\CRT$
649cout << s1 << endl;
650$\texttt{\small abxy}$
651\end{cfa}
652while Java allocates and returns a new string with the result, leaving the receiver unmodified.
653\label{p:JavaReplace}
654\begin{java}
655String s = "abcde";
656String r = s.replace( "cde", "xy" ); $\C[2.25in]{// replace by text, immutable}$
657System.out.println( s + ' ' + r );
658$\texttt{\small abcde abxy}$
659\end{java}
660% Generally, Java's @String@ type is immutable.
661Java provides a @StringBuffer@ near-analog that is mutable.
662\begin{java}
663StringBuffer sb = new StringBuffer( "abcde" );
664sb.replace( 2, 5, "xy" ); $\C[2.25in]{// replace by position, mutable}\CRT$
665System.out.println( sb );
666$\texttt{\small abxy}$
667\end{java}
668However, there are significant differences;
669\eg, @StringBuffer@'s @substring@ function returns a @String@ copy that is immutable.
670Finally, the operations between these type are asymmetric, \eg @String@ has @replace@ by text but not replace by position and vice versa for @StringBuffer@.
671
672More significant operational differences relate to storage management, often appearing through assignment (@target = source@), and are summarized in \VRef[Figure]{f:StrSemanticCompare}.
673% It calls out the consequences of each language taking a different approach on ``internal'' storage management.
674The following discussion justifies the figure's yes/no entries per language.
675
676\begin{figure}
677\setlength{\extrarowheight}{2pt}
678\begin{tabularx}{\textwidth}{@{}p{0.6in}XXcccc@{}}
679 & & & \multicolumn{4}{@{}c@{}}{\underline{Supports Helpful?}} \\
680 & Required & Helpful & C & \CC & Java & \CFA \\
681\hline
682Type abst'n
683 & Low-level: The string type is a varying amount of text communicated via a parameter or return.
684 & High-level: The string-typed relieves the user of managing memory for the text.
685 & no & yes & yes & yes \\
686\hline
687State
688 & \multirow{2}{2in}
689 {Fast Initialize: The target receives the characters of the source without copying the characters, resulting in an Alias or Snapshot.}
690 & Alias: The target name is within the source text; changes made in either variable are visible in both.
691 & yes & yes & no & yes \\
692\cline{3-7}
693 &
694 & Snapshot: The target is an alias within the source until the target changes (copy on write).
695 & no & no & yes & yes \\
696\hline
697Symmetry
698 & Laxed: The target's type is anything string-like; it may have a different status concerning ownership.
699 & Strict: The target's type is the same as the source; both strings are equivalent peers concerning ownership.
700 & -- & no & yes & yes \\
701\hline
702Referent
703 & Variable-Constrained: The target can accept the entire text of the source.
704 & Fragment: The target can accept an arbitrary substring of the source.
705 & no & no & yes & yes
706\end{tabularx}
707
708\noindent
709Notes
710\begin{itemize}[parsep=0pt]
711\item
712 All languages support Required in all criteria.
713\item
714 A language gets ``Supports Helpful'' in one criterion if it can do so without sacrificing the Required achievement on all other criteria.
715\item
716 The C ``string'' is actually @char []@, under the conventions that @<string.h>@ requires. Hence, there is no actual string type in C, so symmetry does not apply.
717\item
718 The Java @String@ class is analyzed; its @StringBuffer@ class behaves similarly to @C++@.
719\end{itemize}
720\caption{Comparison of languages' strings, storage management perspective.}
721\label{f:StrSemanticCompare}
722\end{figure}
723
724In C, the declaration
725\begin{cfa}
726char s[$\,$] = "abcde";
727\end{cfa}
728creates a second-class fixed-sized string-variable, as it can only be used in its lexical context;
729it cannot be passed by value to string operations or user functions as C array's cannot be copied because there is no string-length information passed to the function.
730Therefore, only pointers to strings are first-class, and discussed further.
731\begin{cfa}
732(const) char * s = "abcde"; $\C[2.25in]{// alias state, n/a symmetry, variable-constrained referent}$
733char * s1 = s; $\C{// alias state, n/a symmetry, variable-constrained referent}$
734char * s2 = s; $\C{// alias state, n/a symmetry, variable-constrained referent}$
735char * s3 = &s[1]; $\C{// alias state, n/a symmetry, variable-constrained referent}$
736char * s4 = &s3[1]; $\C{// alias state, n/a symmetry, variable-constrained referent}\CRT$
737printf( "%s %s %s %s %s\n", s, s1, s2, s3, s4 );
738$\texttt{\small abcde abcde abcde bcde cde}$
739\end{cfa}
740Note, all of these aliased strings rely on the single null termination character at the end of @s@.
741The issue of symmetry does not apply to C strings because the value and pointer strings are essentially different types, and so this feature is scored as not applicable for C.
742With the type not managing the text storage, there is no ownership question, \ie operations on @s1@ or @s2@ never leads to their memory becoming reusable.
743While @s3@ is a valid C-string that contains a proper substring of @s1@, the @s3@ technique does not constitute having a fragment referent because null termination implies the substring cannot be chosen arbitrarily; the technique works only for suffixes.
744
745In \CC, @string@ offers a high-level abstraction.
746\begin{cfa}
747string s = "abcde";
748string & s1 = s; $\C[2.25in]{// alias state, lax symmetry, variable-constrained referent}$
749string s2 = s; $\C{// copy (strict symmetry, variable-constrained referent)}$
750string s3 = s.substr( 1, 2 ); $\C{// copy (strict symmetry, fragment referent)}$
751string s4 = s3.substr( 1, 1 ); $\C{// copy (strict symmetry, fragment referent)}$
752cout << s << ' ' << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << endl;
753$\texttt{\small abcde abcde abcde bc c}$
754string & s5 = s.substr(2,4); $\C{// error: cannot point to temporary}\CRT$
755\end{cfa}
756The lax symmetry reflects how the validity of @s1@ depends on the content and lifetime of @s@.
757It is common practice in \CC to use the @s1@-style pass by reference, with the understanding that the callee only uses the referenced string for the duration of the call, \ie no side-effect using the parameter.
758So, when the called function is a constructor, it is typical to use an @s2@-style copy-initialization to string-object-typed member.
759Exceptions to this pattern are possible, but require the programmer to assure safety where the type system does not.
760The @s3@ initialization is constrained to copy the substring because @c_str@ always provides a null-terminated character, which may be different from the source string.
761@s3@ assignment could be fast by reference counting the text area and using copy-on-write, but would require an implementation upgrade.
762
763In Java, @String@ also offers a high-level abstraction:
764\begin{java}
765String s = "abcde";
766String s1 = s; $\C[2.25in]{// snapshot state, strict symmetry, variable-constrained referent}$
767String s2 = s.substring( 1, 3 ); $\C{// snapshot state (possible), strict symmetry, fragment referent}$
768String s3 = s2.substring( 1, 2 ); $\C{// snapshot state (possible), strict symmetry, fragment referent}\CRT$
769System.out.println( s + ' ' + s1 + ' ' + s2 + ' ' + s3 );
770System.out.println( (s == s1) + " " + (s == s2) + " " + (s2 == s3) );
771$\texttt{\small abcde abcde bc c}$
772$\texttt{\small true false false}$
773\end{java}
774Note, @substring@ takes a start and end position, rather than a start position and length.
775Here, facts about Java's implicit pointers and pointer equality can over complicate the picture, and so are ignored.
776Furthermore, Java's string immutability means string variables behave as simple values.
777The result in @s1@ is the pointer in @s@, and their pointer equality confirm no time is spent copying characters.
778With @s2@, the case for fast-copy is more subtle.
779Certainly, its value is not pointer-equal to @s@, implying at least a further allocation.
780\PAB{TODO: finish the fast-copy case.}
781Java does not meet the aliasing requirement because immutability make it impossible to modify.
782Java's @StringBuffer@ provides aliasing (see @replace@ example on \VPageref{p:JavaReplace}), though without supporting symmetric treatment of a fragment referent, \eg @substring@ of a @StringBuffer@ is a @String@;
783as a result, @StringBuffer@ scores as \CC.
784The easy symmetry that the Java string enjoys is aided by Java's garbage collection; Java's @s2@ is doing effectively the operation of \CC's @s3@, though without the consequence of complicating memory management.
785\PAB{What complex storage management is going on here?}
786
787Finally, in \CFA, @string@ also offers a high-level abstraction:
788\begin{cfa}
789string s = "abcde";
790string & s1 = s; $\C[2.25in]{// alias state, strict symmetry, variable-constrained referent}$
791string s2 = s; $\C{// snapshot state, strict symmetry, variable-constrained referent}$
792string s3 = s`share; $\C{// alias state, strict symmetry, variable-constrained referent}\CRT$
793string s4 = s( 1, 2 );
794string s5 = s4( 1, 1 );
795sout | s | s1 | s2 | s3 | s4 | s5;
796$\texttt{\small abcde abcde abcde abcde bc c}$
797\end{cfa}
798% all helpful criteria of \VRef[Figure]{f:StrSemanticCompare} are satisfied.
799The \CFA string manages storage, handles all assignments, including those of fragment referents with fast initialization, provides the choice between snapshot and alias semantics, and does so symmetrically with one type (which assures text validity according to the lifecycles of the string variables).
800The intended metaphor for \CFA stings is similar to a GUI text-editor or web browser.
801Select a consecutive block of text using the mouse generates an aliased substring in the file/dialog-box.
802Typing into the selected area is like assigning to an aliased substring, where the highlighted text is replaced with more or less text;
803depending on the text entered, the file/dialog-box content grows or shrinks.
804\PAB{Need to discuss the example, as for the other languages.}
805
806The remainder of this chapter explains how the \CFA string achieves this usage style.
807
808
809\section{Storage Management}
810
811This section discusses issues related to storage management of strings.
812Specifically, it is common for strings to logically overlap partially or completely.
813\begin{cfa}
814string s1 = "abcdef";
815string s2 = s1; $\C{// complete overlap, s2 == "abcdef"}$
816string s3 = s1.substr( 0, 3 ); $\C{// partial overlap, s3 == "abc"}$
817\end{cfa}
818This raises the question of how strings behave when an overlapping component is changed,
819\begin{cfa}
820s3[1] = 'w'; $\C{// what happens to s1 and s2?}$
821\end{cfa}
822which is restricted by a string's mutable or immutable property.
823For example, Java's immutable strings require copy-on-write when any overlapping string changes.
824Note, the notion of underlying string mutability is not specified by @const@; \eg in \CC:
825\begin{cfa}
826const string s1 = "abc";
827\end{cfa}
828the @const@ applies to the @s1@ pointer to @"abc"@, and @"abc"@ is an immutable constant that is \emph{copied} into the string's storage.
829Hence, @s1@ is not pointing at an immutable constant, meaning its underlying string can be mutable, unless some other designation is specified, such as Java's global immutable rule.
830
831
832\subsection{Logical overlap}
833
834\CFA provides a dynamic mechanism to indicate mutable or immutable using the attribute @`share@.
835This aliasing relationship is a sticky-property established at initialization.
836For example, here strings @s1@ and @s1a@ are in an aliasing relationship, while @s2@ is in a copy relationship.
837\input{sharing1.tex}
838Here, the aliasing (@`share@) causes partial changes (subscripting) to flow in both directions.
839\input{sharing2.tex}
840Similarly for complete changes.
841\input{sharing3.tex}
842
843Because string assignment copies the value, RHS aliasing is irrelevant.
844Hence, aliasing of the LHS is unaffected.
845\input{sharing4.tex}
846
847Now, consider string @s1_mid@ being an alias in the middle of @s1@, along with @s2@, made by a simple copy from the middle of @s1@.
848\input{sharing5.tex}
849Again, @`share@ passes changes in both directions; copy does not.
850As a result, the index values for the position of @b@ are 1 in the longer string @"abcd"@ and 0 in the shorter aliased string @"bc"@.
851This alternate positioning also applies to subscripting.
852\input{sharing6.tex}
853
854Finally, assignment flows through the aliasing relationship without affecting its structure.
855\input{sharing7.tex}
856In the @"ff"@ assignment, the result is straightforward to accept because the flow direction is from contained (small) to containing (large).
857The following rules explain aliasing substrings that flow in the opposite direction, large to small.
858
859Growth and shrinkage are natural extensions, as for the text-editor example mentioned earlier, where an empty substring is as real real as an empty string.
860\input{sharing8.tex}
861
862Multiple portions of a string can be aliased.
863% When there are several aliasing substrings at once, the text editor analogy becomes an online multi-user editor.
864%I should be able to edit a paragraph in one place (changing the document's length), without my edits affecting which letters are within a mouse-selection that you had made previously, somewhere else.
865\input{sharing9.tex}
866When @s1_bgn@'s size increases by 3, @s1_mid@'s starting location moves from 1 to 4 and @s1_end@'s from 3 to 6,
867
868When changes happens on an aliasing substring that overlap.
869\input{sharing10.tex}
870Strings @s1_crs@ and @s1_mid@ overlap at character 4, @j@ because the substrings are 3,2 and 4,2.
871When @s1_crs@'s size increases by 1, @s1_mid@'s starting location moves from 4 to 5, but the overlapping character remains, changing to @'+'@.
872
873\PAB{TODO: finish typesetting the demo}
874
875%\input{sharing-demo.tex}
876
877
878\subsection{RAII limitations}
879
880Earlier work on \CFA~\cite[ch.~2]{Schluntz17} implemented object constructors and destructors for all types (basic and user defined).
881A constructor is a user-defined function run implicitly \emph{after} an object's declaration-storage is created, and a destructor is a user-defined function run \emph{before} an object's declaration-storage is deleted.
882This feature, called RAII~\cite[p.~389]{Stroustrup94}, guarantees pre-invariants for users before accessing an object and post invariants for the programming environment after an object terminates.
883
884The purposes of these invariants goes beyond ensuring authentic values inside an object.
885Invariants can also track occurrences of managed objects in other data structures.
886For example, reference counting is a typical application of an invariant outside of the data values.
887With a reference-counting smart-pointer, the constructor and destructor \emph{of a pointer type} tracks the life cycle of the object it points to.
888Both \CC and \CFA RAII systems are powerful enough to achieve reference counting.
889
890In general, a lifecycle function has access to an object by location, \ie constructors and destructors receive a @this@ parameter providing an object's memory address.
891\begin{cfa}
892struct S { int * ip; };
893void ?{}( S & @this@ ) { this.ip = new(); } $\C[3in]{// default constructor}$
894void ?{}( S & @this@, int i ) { ?{}(this); *this.ip = i; } $\C{// initializing constructor}$
895void ?{}( S & @this@, S s ) { this = s; } $\C{// copy constructor}$
896void ^?{}( S & @this@ ) { delete( this.ip ); } $\C{// destructor}\CRT$
897\end{cfa}
898The lifecycle implementation can then add this object to a collection at creation and remove it at destruction.
899A module providing lifecycle semantics can traverse the collection at relevant times to keep the objects ``good.''
900Hence, declaring such an object not only ensures ``good'' authentic values, but also an implicit subscription to a service that keeps the value ``good'' across its lifetime.
901
902In many cases, the relationship between memory location and lifecycle is straightforward.
903For example, stack-allocated objects being used as parameters and returns, with a sender version in one stack frame and a receiver version in another, as opposed to assignment where sender and receiver are in the same stack frame.
904What is crucial for lifecycle management is knowing if the receiver is initialized or uninitialized, \ie an object is or is not currently associated with management.
905To provide this knowledge, languages differentiate between initialization and assignment to a left-hand side.
906\begin{cfa}
907Obj obj2 = obj1; $\C[1.5in]{// initialization, obj2 is initialized}$
908obj2 = obj1; $\C{// assignment, obj2 must be initialized for management to work}\CRT$
909\end{cfa}
910Initialization occurs at declaration by value, parameter by argument, return temporary by function call.
911Hence, it is necessary to have two kinds of constructors: by value or object.
912\begin{cfa}
913Obj obj1{ 1, 2, 3 }; $\C[1.5in]{// by value, management is initialized}$
914Obj obj2 = obj1; $\C{// by obj, management is updated}\CRT$
915\end{cfa}
916When no object management is required, initialization copies the right-hand value.
917Hence, the calling convention remains uniform, where the unmanaged case uses @memcpy@ as the initialization constructor and managed uses the specified initialization constructor.
918
919The \CFA RAII system supports lifecycle functions, except for returning a value from a function to a temporary.
920For example, in \CC:
921\begin{c++}
922struct S {...};
923S identity( S s ) { return s; }
924S s;
925s = identity( s ); // S temp = identity( s ); s = temp;
926\end{c++}
927the generated code for the function call created a temporary with initialization from the function call, and then assigns the temporary to the object.
928This two step approach means extra storage for the temporary and two copies to get the result into the object variable.
929\CC{17} introduced return value-optimization (RVO)~\cite{RVO20} to ``avoid copying an object that a function returns as its value, including avoiding creation of a temporary object''.
930\CFA uses C semantics for function return giving direct value-assignment, which eliminates unnecessary code, but skips an essential feature needed by lifetime management.
931The following discusses the consequences of this semantics with respect to lifetime management of \CFA strings.
932
933The present string-API contribution provides lifetime management with initialization semantics on function return.
934The workaround to achieve the full lifetime semantics does have a runtime performance penalty.
935An alternative API sacrifices return initialization semantics to recover full runtime performance.
936These APIs are layered, with the slower, friendlier High Level API (HL) wrapping the faster, more primitive Low Level API (LL).
937Both API present the same features, up to lifecycle management, with return initialization being disabled in LL and implemented with the workaround in HL.
938The intention is for most future code to target HL.
939When \CFA becomes a full compiler, it can provide return initialization with RVO optimizations.
940Then, programs written with the HL API will simply run faster.
941In the meantime, performance-critical sections of applications use LL.
942Subsequent performance experiments \see{\VRef{s:PerformanceAssessment}} with other string libraries has \CFA strings using the LL API.
943These measurement gives a fair estimate of the goal state for \CFA.
944
945
946\subsection{Memory management}
947
948A centrepiece of the string module is its memory manager.
949The management scheme defines a shared buffer for string text.
950Allocation in this buffer is via a bump-pointer;
951the buffer is compacted and/or relocated with growth when it fills.
952A string is a smart pointer into this buffer.
953
954This cycle of frequent cheap allocations, interspersed with infrequent expensive compactions, has obvious similarities to a general-purpose memory manager based on garbage collection (GC).
955A few differences are noteworthy.
956First, in a general purpose manager, the allocated objects may contain pointers to other objects, making the transitive reachability of these objects a crucial property.
957Here, the allocations are text, so one allocation never keeps another alive.
958Second, in a general purpose manager, the handle that keeps an allocation alive is just a lean pointer.
959For strings, a fatter representation is acceptable because there are fewer string head pointers versus chained pointers within nodes as for linked containers.
960
961\begin{figure}
962\includegraphics{memmgr-basic.pdf}
963\caption{String memory-management data structures}
964\label{f:memmgr-basic}
965\end{figure}
966
967\VRef[Figure]{f:memmgr-basic} shows the representation.
968The heap header and text buffer define a sharing context.
969Normally, one global sharing context is appropriate for an entire program;
970concurrent exceptions are discussed in \VRef{s:AvoidingImplicitSharing}.
971A string is a handle into the buffer and linked into a list.
972The list is doubly linked for $O(1)$ insertion and removal at any location.
973Strings are orders in the list by string-text address, where there is no overlapping, and approximately, where there is.
974The header maintains a next-allocation pointer, @alloc@, pointing to the last live allocation in the buffer.
975No external references point into the buffer and the management procedure relocates the text allocations as needed.
976A string handle references a containing string, while its string is contiguous and not null terminated.
977The length sets an upper limit on the string size, but is large (4 or 8 bytes).
978String handles can be allocated in the stack or heap, and represent the string variables in a program.
979Normal C life-time rules apply to guarantee correctness of the string linked-list.
980The text buffer is large enough with good management so that often only one dynamic allocation is necessary during program execution.
981% During this period, strings can vary in size dynamically.
982
983When the text buffer fills, \ie the next new string allocation causes @alloc@ to point beyond the end of the buffer, the strings are compacted.
984The linked handles define all live strings in the buffer, which indirectly defines the allocated and free space in the buffer.
985Since the string handles are in (roughly) sorted order, the handle list can be traversed copying the first text to the start of the buffer and subsequent strings after each over.
986After compaction, if the amount of free storage is still less than the new string allocation, a larger text buffer is heap allocated, the current buffer is copies into the new buffer, and the original buffer is freed.
987Note, the list of string handles is unaffected during a compaction;
988only the string pointers in the handles are modified to new buffer locations.
989
990Object lifecycle events are the \emph{subscription-management} triggers in such a service.
991There are two fundamental string-creation routines: importing external text like a C-string or reading a string, and initialization from an existing \CFA string.
992When importing, storage comes from the end of the buffer, into which the text is copied.
993The new string handle is inserted at the end of the handle list because the new text is at the end of the buffer.
994When initializing from text already in the text buffer, the new handle is a second reference into the original run of characters.
995In this case, the new handle's linked-list position is after the original handle.
996Both string initialization styles preserve the string module's internal invariant that the linked-list order matches the buffer order.
997For string destruction, handles are removed from the list.
998
999Certain string operations can results in a subset (substring) of another string.
1000The resulting handle is then placed in the correct sorted position in the list, possible with a short linear search to locate the position.
1001For string operations resulting in a new string, that string is allocated at the end of the buffer.
1002For shared-edit strings, handles that originally referenced containing locations need to see the new value at the new buffer location.
1003These strings are moved to appropriate locations at the end of the list \see{[xref: TBD]}.
1004For nonshared-edit strings, a containing string can be moved and the nonshared strings can remain in the same position.
1005String assignment words similarly to string initialization, maintain the invariant of linked-list order matching buffer order.
1006
1007At the level of the memory manager, these modifications can always be explained as assignments and appendment;
1008for example, an append is an assignment into the empty substring at the end of the buffer.
1009Favourable conditions allow for in-place editing: where there is room for the resulting value in the original buffer location, and where all handles referring to the original buffer location see the new value.
1010However, the general case requires a new buffer allocation: where the new value does not fit in the old place, or if other handles are still using the old value.
1011
1012
1013\subsection{Sharing implementation}
1014
1015The \CFA string module has two mechanisms to handle the case when string handles share a string of text.
1016
1017The first type of sharing is the user requests both string handles be views of the same logical, modifiable string.
1018This state is typically produced by the substring operation.
1019\begin{cfa}
1020string s = "abcde";
1021string s1 = s( 1, 2 )@`share@; $\C[2.25in]{// explicit sharing}$
1022s[1] = 'x'; $\C{// change s and s1}\CRT$
1023sout | s | s1;
1024$\texttt{\small axcde xc}$
1025\end{cfa}
1026In a typical substring call, the source string-handle is referencing an entire string, and the resulting, newly made, string handle is referencing a portion of the original.
1027In this state, a subsequent modification made by either is visible in both.
1028
1029The second type of sharing happens when the system implicitly delays the physical execution of a logical \emph{copy} operation, as part of its copy-on-write optimization.
1030This state is typically produced by constructing a new string, using an original string as its initialization source.
1031\begin{cfa}
1032string s = "abcde";
1033string s1 = s( 1, 2 )@@; $\C[2.25in]{// no sharing}$
1034s[1] = 'x'; $\C{// copy-on-write s1}\CRT$
1035sout | s | s1;
1036$\texttt{\small axcde bc}$
1037\end{cfa}
1038In this state, a subsequent modification done on one handle triggers the deferred copy action, leaving the handles referencing different text within the buffer, holding distinct values.
1039
1040A further abstraction, in the string module's implementation, helps distinguish the two senses of sharing.
1041A share-edit set (SES) is an equivalence class over string handles, being the reflexive, symmetric and transitive closure of the relationship of one string being constructed from another, with the ``share'' opt-in given.
1042The SES is represented by a second linked list among the handles.
1043A string that shares edits with no other is in a SES by itself.
1044Inside a SES, a logical modification of one substring portion may change the logical value in another, depending on whether the two actually overlap.
1045Conversely, no logical value change can flow outside of a SES.
1046Even if a modification on one string handle does not reveal itself \emph{logically} to anther handle in the same SES (because they do not overlap), if the modification is length-changing, completing the modification requires visiting the second handle to adjust its location in the sliding text.
1047
1048
1049\subsection{Avoiding implicit sharing}
1050\label{s:AvoidingImplicitSharing}
1051
1052There are tradeoffs associated with the copy-on-write mechanism.
1053Several qualitative matters are detailed in \VRef{s:PerformanceAssessment} and the qualitative issue of multi-threaded support is introduced here.
1054The \CFA string library provides a switch to disable threads allocating from the string buffer, when string sharing is unsafe.
1055When toggled, string management is moved to the storage allocator, specifically @malloc@/@free@, where the storage allocator is assumed to be thread-safe.
1056
1057In detail, string sharing has inter-linked string handles, so any participant managing one string is also managing, directly, the neighbouring strings, and from there, a data structure of the ``set of all strings.''
1058This string structure is intended for sequential access.
1059Hence, multiple threads using shared strings need to avoid modifying (concurrently) an instance of this structure (like Java immutable strings).
1060A positive consequence of this approach is that independent threads can use the sharing buffer without locking overhead.
1061
1062When the string library is running with sharing disabled, it runs without implicit thread-safety challenges, which is the same as the \CC STL, and with performance goals similar to the STL.
1063Running with sharing disabled can be thought of as a STL-emulation mode.
1064Hence, concurrent users of string objects must still bring their own mutual exclusion, but the string library does not add any cross thread uses that are not apparent in a user's code.
1065
1066The \CFA string library provides the type @string_sharectx@ to control an ambient sharing context for a current thread.
1067It allows two adjustments: to opt out of sharing entirely or to begin sharing within a private context.
1068Either way, the chosen mode applies only to the current thread, for the duration of the lifetime of the created @string_sharectx@ object, up to being suspended by child lifetimes of different contexts.
1069\VRef[Figure]{fig:string-sharectx} illustrates its behaviour.
1070Executing the example does not produce an interesting outcome.
1071But the comments indicate when the logical copy operation runs with
1072\begin{description}
1073 \item[share:] the copy being deferred, as described through the rest of this section (fast), or
1074 \item[copy:] the copy performed eagerly (slow).
1075\end{description}
1076Only eager copies can cross @string_sharectx@ boundaries.
1077The intended use is with stack-managed lifetimes, in which the established context lasts until the current function returns, and affects all functions called that do not create their own contexts.
1078In this example, the single-letter functions are called in alphabetic order.
1079The functions @a@, @b@ and @g@ share string character ranges with each other, because they occupy a common sharing-enabled context.
1080The function @e@ shares within itself (because its is in a sharing-enabled context), but not with the rest of the program (because its context is not occupied by any of the rest of the program).
1081The functions @c@, @d@ and @f@ never share anything, because they are in a sharing-disabled context.
1082
1083
1084\begin{figure}
1085 \begin{tabular}{ll}
1086 \lstinputlisting[language=CFA, firstline=10, lastline=55]{sharectx.run.cfa}
1087 &
1088 \raisebox{-0.17\totalheight}{\includegraphics{string-sharectx.pdf}} % lower
1089 \end{tabular}
1090 \caption{Controlling copying vs sharing of strings using \lstinline{string_sharectx}.}
1091 \label{fig:string-sharectx}
1092\end{figure}
1093
1094
1095[ TODO: true up with ``is thread local'' (implement that and expand this discussion to give a concurrent example, or adjust this wording) ]
1096
1097
1098\subsection{Future work}
1099
1100To discuss: Unicode
1101
1102To discuss: Small-string optimization
1103
1104
1105\section{Performance assessment}
1106\label{s:PerformanceAssessment}
1107
1108I assessed the \CFA string library's speed and memory usage against strings in \CC STL.
1109The results are presented in even equivalent cases, due to either micro-optimizations foregone, or fundamental costs of the added functionality.
1110They also show the benefits and tradeoffs, as >100\% effects, of switching to \CFA, with the tradeoff points quantified.
1111The final test shows the overall win of the \CFA text-sharing mechanism.
1112It exercises several operations together, showing \CFA enabling clean user code to achieve performance that STL requires less-clean user code to achieve.
1113
1114To discuss: general goal of ...
1115while STL makes you think about memory management, all the time, and if you do, your performance can be great ...
1116\CFA sacrifices this advantage modestly in exchange for big wins when you're not thinking about memory management.
1117[Does this position cover all of it?]
1118
1119To discuss: revisit HL v LL APIs
1120
1121To discuss: revisit no-sharing as STL emulation modes
1122
1123
1124\subsection{Methodology}
1125
1126These tests use a \emph{corpus} of strings (string content is immaterial).
1127For varying-length strings, the mean length comes from a geometric distribution, which implies that lengths much longer than the mean occur frequently.
1128The string sizes are:
1129\begin{description}
1130 \item [Fixed-size] all string lengths are of the stated size.
1131 \item [Varying from 1 to N] means the string lengths are drawn from the geometric distribution with a stated mean and all lengths occur.
1132 \item [Varying from 16 to N] means string lengths are drawn from the geometric distribution with the stated mean, but only lengths 16 and above occur; thus, the stated mean is above 16.
1133\end{description}
1134The means for the geometric distribution are the X-axis values in experiments.
1135The special treatment of length 16 deals with the short-string optimization (SSO) in STL @string@, currently not implemented in \CFA.
1136When an STL string can fit into a heap pointer, the optimization uses the pointer storage to eliminate using the heap.
1137\begin{c++}
1138class string {
1139 union {
1140 struct { $\C{// long string, string storage in heap}$
1141 size_t size;
1142 char * strptr;
1143 } lstr;
1144 char sstr[sizeof(lstr)]; $\C{// short string 8-16 characters, in situ}$
1145 };
1146 bool tag; $\C{// string kind, short or long}$
1147 ... $\C{// other storage}$
1148};
1149\end{c++}
1150
1151When success is illustrated, notwithstanding SSO, a fixed-size or from-16 distribution ensures that extra-optimized cases are not part of the mix on the STL side.
1152In all experiments that use a corpus, its text is generated and loaded into the system under test before the timed phase begins.
1153
1154To discuss: vocabulary for reused case variables
1155
1156To discuss: common approach to iteration and quoted rates
1157
1158To discuss: hardware and such
1159
1160To ensure comparable results, a common memory allocator is used for \CFA and \CC.
1161The llheap allocator~\cite{Zulfiqar22} is embedded into \CFA and is used standalone with \CC.
1162
1163
1164\subsection{Test: Append}
1165
1166This test measures the speed of appending randomly-size text onto a growing string.
1167\begin{cquote}
1168\setlength{\tabcolsep}{20pt}
1169\begin{tabular}{@{}ll@{}}
1170% \multicolumn{1}{c}{\textbf{fresh}} & \multicolumn{1}{c}{\textbf{reuse}} \\
1171\begin{cfa}
1172
1173for ( ... ) {
1174 @string x;@ // fresh
1175 for ( ... )
1176 x @+=@ ...
1177}
1178\end{cfa}
1179&
1180\begin{cfa}
1181string x;
1182for ( ... ) {
1183 @x = "";@ $\C[1in]{// reuse}$
1184 for ( ... )
1185 x @+=@ ... $\C{// append, alternative x = x + ...}\CRT$
1186}
1187\end{cfa}
1188\end{tabular}
1189\end{cquote}
1190The benchmark's outer loop executes ``until a sample-worthy amount of execution has happened'' and an inner loop for ``building up the desired-length string.''
1191Its subcases include,
1192\begin{enumerate}[leftmargin=*]
1193\item
1194\CFA nosharing/sharing \vs \CC nosharing.
1195\item
1196Difference between the logically equivalent operations @x += ...@ \vs @x = x + ...@.
1197For numeric types, the generated code is equivalence, giving identical performance.
1198However, for string types there can be a significant difference in performance, especially if this code appears in a loop iterating a large number of times.
1199This difference might not be intuitive to beginners.
1200\item
1201Coding practice where the user's logical allocation is fresh \vs reused.
1202Here, \emph{reusing a logical allocation}, means that the program variable, into which the user is concatenating, previously held a long string.
1203In general, a user should not have to care about this difference, yet the STL performs differently in these cases.
1204Furthermore, if a routine takes a string by reference, if cannot use the fresh approach.
1205Concretely, both cases incur the cost of copying characters into the target string, but only the allocation-fresh case incurs a further reallocation cost, which is generally paid at points of doubling the length.
1206For the STL, this cost includes obtaining a fresh buffer from the memory allocator and copying older characters into the new buffer, while \CFA-sharing hides such a cost entirely.
1207%The fresh \vs reuse distinction is only relevant in the \emph{append} tests.
1208\end{enumerate}
1209
1210\begin{figure}
1211\centering
1212 \includegraphics{string-graph-peq-cppemu.pdf}
1213% \includegraphics[width=\textwidth]{string-graph-peq-cppemu.png}
1214 \caption{Average time per iteration (lower is better) with one \lstinline{x += y} invocation, comparing \CFA with STL implementations (given \CFA running in STL emulation mode), and comparing the ``fresh'' with ``reused'' reset styles, at various string sizes.}
1215 \label{fig:string-graph-peq-cppemu}
1216\end{figure}
1217
1218This tests use the varying-from-1 corpus construction, \ie it assumes the STL's advantage of small-string optimization.
1219\PAB{To discuss: any other case variables introduced in the performance intro}
1220\VRef[Figure]{fig:string-graph-peq-cppemu} shows this behaviour, by the STL and by \CFA in STL emulation mode.
1221\CFA reproduces STL's performance, up to a 15\% penalty averaged over the cases shown, diminishing with larger strings, and 50\% in the worst case.
1222This penalty characterizes the amount of implementation fine tuning done with STL and not done with \CFA in present state.
1223There is a larger penalty for redeclaring the string each loop iteration (fresh) versus hosting it out of the loop and reseting it to the null string (reuse).
1224The cost is 40\% averaged over the cases shown and minimally 24\%, and shows up consistently between the \CFA and STL implementations, and increases with larger strings.
1225
1226\begin{figure}
1227\centering
1228 \includegraphics{string-graph-peq-sharing.pdf}
1229% \includegraphics[width=\textwidth]{string-graph-peq-sharing.png}
1230 \caption{Average time per iteration (lower is better) with one \lstinline{x += y} invocation, comparing \CFA (having implicit sharing activated) with STL, and comparing the ``fresh'' with ``reused'' reset styles, at various string sizes.}
1231 \label{fig:string-graph-peq-sharing}
1232\end{figure}
1233
1234In sharing mode, \CFA makes the fresh/reuse difference disappear, as shown in \VRef[Figure]{fig:string-graph-peq-sharing}.
1235At append lengths 5 and above, \CFA not only splits the two baseline STL cases, but its slowdown of 16\% over (STL with user-managed reuse) is close to the \CFA-v-STL implementation difference seen with \CFA in STL-emulation mode.
1236
1237\begin{figure}
1238\centering
1239 \includegraphics{string-graph-pta-sharing.pdf}
1240% \includegraphics[width=\textwidth]{string-graph-pta-sharing.png}
1241 \caption{Average time per iteration (lower is better) with one \lstinline{x = x + y} invocation, comparing \CFA (having implicit sharing activated) with STL.
1242For context, the results from \VRef[Figure]{fig:string-graph-peq-sharing} are repeated as the bottom bands.
1243While not a design goal, and not graphed out, \CFA in STL-emulation mode outperformed STL in this case; user-managed allocation reuse did not affect any of the implementations in this case.}
1244 \label{fig:string-graph-pta-sharing}
1245\end{figure}
1246
1247When the user takes a further step beyond the STL's optimal zone, by running @x = x + y@, as in \VRef[Figure]{fig:string-graph-pta-sharing}, the STL's penalty is above $15 \times$ while \CFA's (with sharing) is under $2 \times$, averaged across the cases shown here.
1248Moreover, the STL's gap increases with string size, while \CFA's converges.
1249
1250
1251\subsubsection{Test: Pass argument}
1252
1253STL has a penalty for passing a string by value, which indirectly forces users to think about memory management when communicating values to a function.
1254\begin{cfa}
1255void foo( string s );
1256string s = "abc";
1257foo( s );
1258\end{cfa}
1259With implicit sharing active, \CFA treats this operation as normal and supported.
1260This test illustrates a main advantage of the \CFA sharing algorithm.
1261It also has a case in which STL's small-string optimization provides a successful mitigation.
1262
1263\begin{figure}
1264\centering
1265 \includegraphics{string-graph-pbv.pdf}
1266% \includegraphics[width=\textwidth]{string-graph-pbv.png}
1267 \caption{Average time per iteration (lower is better) with one call to a function that takes a by-value string argument, comparing \CFA (having implicit sharing activated) with STL.
1268(a) With \emph{Varying-from-1} corpus construction, in which the STL-only benefit of small-string optimization occurs, in varying degrees, at all string sizes.
1269(b) With \emph{Fixed-size} corpus construction, in which this benefit applies exactly to strings with length below 16.
1270[TODO: show version (b)]}
1271 \label{fig:string-graph-pbv}
1272\end{figure}
1273
1274\VRef[Figure]{fig:string-graph-pbv} shows the costs for calling a function that receives a string argument by value.
1275STL's performance worsens as string length increases, while \CFA has the same performance at all sizes.
1276
1277The \CFA cost to pass a string is nontrivial.
1278The contributor is adding and removing the callee's string handle from the global list.
1279This cost is $1.5 \times$ to $2 \times$ over STL's when small-string optimization applies, though this cost should be avoidable in the same case, given a \CFA realization of this optimization.
1280At the larger sizes, when STL has to manage storage for the string, STL runs more than $3 \times$ slower, mainly due to time spent in the general-purpose memory allocator.
1281
1282
1283\subsubsection{Test: Allocate}
1284
1285This test directly compares the allocation schemes of the \CFA string with sharing, compared with the STL string.
1286It treats the \CFA scheme as a form of garbage collection, and the STL scheme as an application of malloc-free.
1287The test shows that \CFA enables faster speed at a cost in memory usage.
1288
1289A garbage collector, afforded the freedom of managed memory, often runs faster than malloc-free (in an amortized analysis, even though it must occasionally stop to collect) because it is able to use its collection time to move objects.
1290(In the case of the mini-allocator powering the \CFA string library, objects are runs of text.) Moving objects lets fresh allocations consume from a large contiguous store of available memory; the ``bump pointer'' book-keeping for such a scheme is very light.
1291A malloc-free implementation without the freedom to move objects must, in the general case, allocate in the spaces between existing objects; doing so entails the heavier book-keeping to navigate and maintain a linked structure.
1292
1293A garbage collector keeps allocations around for longer than the using program can reach them.
1294By contrast, a program using malloc-free (correctly) releases allocations exactly when they are no longer reachable.
1295Therefore, the same harness will use more memory while running under garbage collection.
1296A garbage collector can minimize the memory overhead by searching for these dead allocations aggressively, that is, by collecting more often.
1297Tuned in this way, it spends a lot of time collecting, easily so much as to overwhelm its speed advantage from bump-pointer allocation.
1298If it is tuned to collect rarely, then it leaves a lot of garbage allocated (waiting to be collected) but gains the advantage of little time spent doing collection.
1299
1300[TODO: find citations for the above knowledge]
1301
1302The speed for memory tradeoff is, therefore, standard for comparisons like \CFA--STL string allocations.
1303The test verifies that it is so and quantifies the returns available.
1304
1305These tests manipulate a tuning knob that controls how much extra space to use.
1306Specific values of this knob are not user-visible and are not presented in the results here.
1307Instead, its two effects (amount of space used and time per operation) are shown.
1308The independent variable is the liveness target, which is the fraction of the text buffer that is in use at the end of a collection.
1309The allocator will expand its text buffer during a collection if the actual fraction live exceeds this target.
1310
1311This experiment's driver allocates strings by constructing a string handle as a local variable then looping over recursive calls.
1312The time measurement is of nanoseconds per such allocating call.
1313The arrangement of recursive calls and their fan-out (iterations per recursion level) makes some of the strings long-lived and some of them short-lived.
1314String lifetime (measured in number of subsequent string allocations) is ?? distributed, because each node in the call tree survives as long as its descendent calls.
1315The run presented in this section used a call depth of 1000 and a fan-out of 1.006, which means that approximately one call in 167 makes two recursive calls, while the rest make one.
1316This sizing was chosen to keep the amounts of consumed memory within the machine's last-level cache.
1317
1318\begin{figure}
1319\centering
1320 \includegraphics{string-graph-allocn.pdf}
1321% \includegraphics[width=\textwidth]{string-graph-allocn.png}
1322 \caption{Space and time performance, under varying fraction-live targets, for the five string lengths shown, at (\emph{Fixed-size} corpus construction.
1323[MISSING] The identified clusters are for the default fraction-live target, which is 30\%.
1324MISSING: STL results, typically just below the 0.5--0.9 \CFA segment.
1325All runs keep an average of 836 strings live, and the median string lifetime is ?? allocations.}
1326 \label{fig:string-graph-allocn}
1327\end{figure}
1328
1329\VRef[Figure]{fig:string-graph-allocn} shows the results of this experiment.
1330At all string sizes, varying the liveness threshold gives offers speed-for-space tradeoffs relative to STL.
1331At the default liveness threshold, all measured string sizes see a ??\%--??\% speedup for a ??\%--??\% increase in memory footprint.
1332
1333
1334\subsubsection{Test: Normalize}
1335
1336This test is more applied than the earlier ones.
1337It combines the effects of several operations.
1338It also demonstrates a case of the \CFA API allowing user code to perform well, while being written without overt memory management, while achieving similar performance in STL requires adding memory-management complexity.
1339
1340To motivate: edits being rare
1341
1342The program is doing a specialized find-replace operation on a large body of text.
1343In the program under test, the replacement is just to erase a magic character.
1344But in the larger software problem represented, the rewrite logic belongs to a module that was originally intended to operate on simple, modest-length strings.
1345The challenge is to apply this packaged function across chunks taken from the large body.
1346Using the \CFA string library, the most natural way to write the helper module's function also works well in the adapted context.
1347Using the STL string, the most natural ways to write the helper module's function, given its requirements in isolation, slow down when it is driven in the adapted context.
1348
1349\begin{lstlisting}
1350void processItem( string & item ) {
1351 // find issues in item and fix them
1352}
1353\end{lstlisting}
Note: See TracBrowser for help on using the repository browser.