source: doc/theses/mike_brooks_MMath/string.tex@ 810c2c5

Last change on this file since 810c2c5 was 810c2c5, checked in by Michael Brooks <mlbrooks@…>, 4 days ago

address two string-section oustanding items: remove confusing sentences; add attribution plot overlay for separate compilation overhead

  • Property mode set to 100644
File size: 105.3 KB
RevLine 
[bbf6a180]1\chapter{String}
2
[195f43d]3\vspace*{-20pt}
[cef5bfc]4This chapter presents my work on designing and building a modern string type in \CFA.
[d9aee90]5The discussion starts with an overview of the string API, then a number of interesting string problems, followed by how these issues are resolved in this work.
[16915b1]6
7
[c01a2fd]8\section{String Operations}
[174a11a]9
[931f1b4]10% https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(string_functions)
11
[56ec508]12\VRef[Figure]{f:StrApiCompare} shows a general comparison of string APIs for C, \CC, Java and \CFA.
13It provides a classic ``cheat sheet'', summarizing the names of the most-common closely-equivalent operations.
[195f43d]14The over-arching commonality is that operations work on groups of characters for assigning, copying, scanning, and updating.
[5e8d75bb]15
[195f43d]16\begin{figure}[h]
[174a11a]17\begin{cquote}
[5e8d75bb]18\begin{tabular}{@{}l|l|l|l@{}}
[5d300ba]19C @char [ ]@ & \CC @string@ & Java @String@ & \CFA @string@ \\
[174a11a]20\hline
[411aa65]21@strcpy@, @strncpy@ & @=@ & @=@ & @=@ \\
22@strcat@, @strncat@ & @+@, @+=@ & @+@, @+=@ & @+@, @+=@ \\
[5e8d75bb]23@strcmp@, @strncmp@ & @==@, @!=@, @<@, @<=@, @>@, @>=@
[411aa65]24 & @equals@, @compareTo@
25 & @==@, @!=@, @<@, @<=@, @>@, @>=@ \\
26@strlen@ & @length@, @size@ & @length@ & @size@ \\
27@[ ]@ & @[ ]@ & @charAt@ & @[ ]@ \\
28@strncpy@ & @substr@ & @substring@ & @( )@, on RHS of @=@ \\
29@strncpy@ & @replace@ & @replace@ & @( )@, on LHS of @=@ \\
30@strstr@ & @find@ & @indexOf@ & @find@ \\
[31be464]31@strcspn@ & @find_first_of@ & @matches@ & @exclude@ \\
32@strspn@ & @find_first_not_of@ & @matches@ & @include@ \\
[411aa65]33N/A & @c_str@, @data@ & N/A & @strcpy@, @strncpy@ \\
[174a11a]34\end{tabular}
35\end{cquote}
[56ec508]36\caption{Language comparison of string API}
[5e8d75bb]37\label{f:StrApiCompare}
38\end{figure}
39
[b0296dba]40As mentioned in \VRef{s:String}, a C string uses null termination rather than a length, which leads to explicit storage management;
41hence, most of its group operations are error prone and expensive due to copying.
[195f43d]42Most high-level string libraries use a separate length field and specialized storage management to implement group operations.
[06ffa95]43Interestingly, \CC strings retain null termination in case it is needed to interface with C library functions.
[c01a2fd]44\begin{cfa}
[195f43d]45int open( @const char * pathname@, int flags );
[b766c9b7]46string fname{ "test.cc" );
[56ec508]47open( fname.@c_str()@, O_RDONLY ); // null terminated value of string
[c01a2fd]48\end{cfa}
[e78d969]49Here, the \CC @c_str@ member 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{
[b766c9b7]50C functions like \lstinline{strdup} do return allocated storage that must be freed by the caller.}
[195f43d]51% Instead, each \CC string is null terminated just in case it might be needed for this purpose.
[b766c9b7]52Providing this backwards compatibility with C has a ubiquitous performance and storage cost.
[c01a2fd]53
[fc8ec54]54
[411aa65]55\section{\CFA \lstinline{string} Type}
[fc8ec54]56\label{s:stringType}
57
58The \CFA string type is for manipulation of dynamically-sized character-strings versus C @char *@ type for manipulation of statically-sized null-terminated character-strings.
[780727f]59Therefore, 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.
60As a result, a @string@ declaration does not specify a maximum length, where a C string array does.
61For \CFA, as a @string@ dynamically grows and shrinks in size, so does its underlying storage.
[5d300ba]62For C, as a string dynamically grows and shrinks in size, its underlying storage does not.
[fc8ec54]63The maximum storage for a \CFA @string@ value is @size_t@ characters, which is $2^{32}$ or $2^{64}$ respectively.
64A \CFA string manages its length separately from the string, so there is no null (@'\0'@) terminating value at the end of a string value.
[602ac05]65Hence, a \CFA string cannot be passed to a C string manipulation function, such as @strcat@.
[b0296dba]66Like C strings, characters in a @string@ are numbered from the left starting at 0 (because subscripting is zero-origin), and in \CFA numbered from the right starting at -1.
[f8913b7c]67\begin{cquote}
[b0296dba]68\rm
[931f1b4]69\begin{tabular}{@{}rrrrll@{}}
70\small\tt "a & \small\tt b & \small\tt c & \small\tt d & \small\tt e" \\
[f8913b7c]710 & 1 & 2 & 3 & 4 & left to right index \\
72-5 & -4 & -3 & -2 & -1 & right to left index
73\end{tabular}
74\end{cquote}
[b0296dba]75The following operations manipulate an instance of type @string@, where the discussion assumes the following declarations.
[fc8ec54]76\begin{cfa}
77#include @<string.hfa>@
[931f1b4]78@string@ s = "abcde", name = "MIKE", digit = "0123456789";
[b0296dba]79const char cs[$\,$] = "abc";
[fc8ec54]80int i;
81\end{cfa}
[931f1b4]82Note, the include file @<string.hfa>@ to access type @string@.
[fc8ec54]83
84
85\subsection{Implicit String Conversions}
86
87The ability to convert from internal (machine) to external (human) format is useful in situations other than I/O.
[d9aee90]88Hence, the basic types @char@, @char *@, @int@, @double@, @_Complex@, including any signness and size variations, implicitly convert to type @string@ (as in Java).
[fc8ec54]89\begin{cquote}
[5d300ba]90\setlength{\tabcolsep}{10pt}
91\begin{tabular}{@{}llll@{}}
[fc8ec54]92\begin{cfa}
[780727f]93string s = 5;
[b0296dba]94s = 'x';
95s = "abc";
[e78d969]96s = 42hh; // signed char
97s = 42h; // short int
[780727f]98s = 0xff;
[fc8ec54]99\end{cfa}
100&
101\begin{cfa}
[780727f]102"5"
[fc8ec54]103"x"
104"abc"
[780727f]105"42"
106"42"
107"255"
[56ec508]108\end{cfa}
109&
110\begin{cfa}
[780727f]111s = (ssize_t)MIN;
112s = (size_t)MAX;
113s = 5.5;
114s = 5.5L;
115s = 5.5+3.4i;
116s = 5.5L+3.4Li;
[56ec508]117\end{cfa}
118&
119\begin{cfa}
[fc8ec54]120"-9223372036854775808"
121"18446744073709551615"
122"5.5"
123"5.5"
124"5.5+3.4i"
125"5.5+3.4i"
126\end{cfa}
127\end{tabular}
[56ec508]128\end{cquote}
129Conversions can be explicitly specified using a compound literal.
130\begin{cfa}
[5d300ba]131s = (string){ 5 }; s = (string){ "abc" }; s = (string){ 5.5 };
[56ec508]132\end{cfa}
[602ac05]133
[780727f]134Conversions from @string@ to @char *@ attempt to be safe.
[5d300ba]135The overloaded @strncpy@ function is safe, if the length of the C string is correct.
[e78d969]136The assignment operator and constructor both allocate storage and return its address, meaning the programmer must free it.
[780727f]137Note, a C string is always null terminated, implying storage is always necessary for the null.
[56ec508]138\begin{cquote}
[5d300ba]139\begin{tabular}{@{}ll@{}}
[56ec508]140\begin{cfa}
[780727f]141string s = "abcde";
142char cs[4];
[56ec508]143strncpy( cs, s, sizeof(cs) );
[5d300ba]144char * cp = s; // ownership
[56ec508]145delete( cp );
[780727f]146cp = s + ' ' + s; // ownership
[56ec508]147delete( cp );
148\end{cfa}
149&
150\begin{cfa}
[780727f]151
152
[56ec508]153"abc\0", in place
154"abcde\0", malloc
[780727f]155
[56ec508]156"abcde abcde\0", malloc
[780727f]157
[56ec508]158\end{cfa}
159\end{tabular}
160\end{cquote}
[fc8ec54]161
162
163\subsection{Length}
164
[5d300ba]165The @len@ operation (short for @strlen@) returns the length of a C (not including the terminating null) or \CFA string.
166For compatibility, an overloaded @strlen@ works with \CFA strings.
[fc8ec54]167\begin{cquote}
[5d300ba]168\begin{tabular}{@{}ll@{}}
[fc8ec54]169\begin{cfa}
[56ec508]170i = len( "" );
171i = len( "abc" );
172i = len( cs );
173i = strlen( cs );
174i = len( name );
175i = strlen( name );
[fc8ec54]176\end{cfa}
177&
178\begin{cfa}
1790
1803
1813
[56ec508]1823
1834
[fc8ec54]1844
185\end{cfa}
186\end{tabular}
187\end{cquote}
188
189
190\subsection{Comparison Operators}
191
[780727f]192The binary relational, @<@, @<=@, @>@, @>=@, and equality, @==@, @!=@, operators compare \CFA strings using lexicographical ordering, where longer strings are greater than shorter strings.
[602ac05]193In C, these operators compare the C string pointer not its value, which does not match programmer expectation.
[b0296dba]194C strings use function @strcmp@ to lexicographically compare the string value.
[5d300ba]195Java has the same issue with @==@ (reference) and @.equals@ (value) comparison.
[fc8ec54]196
197
198\subsection{Concatenation}
199
[b0296dba]200The binary operators @+@ and @+=@ concatenate C @char@, @char *@ and \CFA strings, creating the sum of the characters.
[780727f]201\begin{cquote}
[5d300ba]202\setlength{\tabcolsep}{5pt}
203\begin{tabular}{@{}ll|ll|ll@{}}
[fc8ec54]204\begin{cfa}
[56ec508]205s = "";
206s = 'a' + 'b';
207s = 'a' + "b";
208s = "a" + 'b';
209s = "a" + "b";
210\end{cfa}
211&
212\begin{cfa}
213
214"ab"
215"ab"
216"ab"
217"ab"
218\end{cfa}
219&
220\begin{cfa}
221s = "";
[fc8ec54]222s = 'a' + 'b' + s;
[56ec508]223s = 'a' + 'b' + s;
224s = 'a' + "b" + s;
225s = "a" + 'b' + s;
[fc8ec54]226\end{cfa}
227&
228\begin{cfa}
229
[56ec508]230"ab"
231"abab"
232"ababab"
233"abababab"
234\end{cfa}
235&
236\begin{cfa}
237s = "";
238s = s + 'a' + 'b';
239s = s + 'a' + "b";
240s = s + "a" + 'b';
241s = s + "a" + "b";
242\end{cfa}
243&
244\begin{cfa}
[fc8ec54]245
[56ec508]246"ab"
247"abab"
248"ababab"
249"abababab"
[fc8ec54]250\end{cfa}
251\end{tabular}
[780727f]252\end{cquote}
[b0296dba]253However, including @<string.hfa>@ can result in ambiguous uses of the overloaded @+@ operator.\footnote{Combining multiple packages in any programming language can result in name clashes or ambiguities.}
[780727f]254For example, subtracting characters or pointers has valid use-cases:
[56ec508]255\begin{cfa}
[780727f]256ch - '0' $\C[2in]{// find character offset}$
257cs - cs2; $\C{// find pointer offset}\CRT$
[56ec508]258\end{cfa}
[b0296dba]259addition is less obvious
[56ec508]260\begin{cfa}
[780727f]261ch + 'b' $\C[2in]{// add character values}$
262cs + 'a'; $\C{// move pointer cs['a']}\CRT$
[b0296dba]263\end{cfa}
[d9aee90]264There are legitimate use cases for arithmetic with @signed@/@unsigned@ characters (bytes), and these types are treated differently from @char@ in \CC and \CFA.
265However, backwards compatibility makes it impossible to restrict or remove addition on type @char@.
[b0296dba]266Similarly, it is impossible to restrict or remove addition on type @char *@ because (unfortunately) it is subscripting: @cs + 'a'@ implies @cs['a']@ or @'a'[cs]@.
267
[5d300ba]268The prior \CFA concatenation examples show complex mixed-mode interactions among @char@, @char *@, and @string@ constants work correctly (@string@ variables are the same).
[b0296dba]269The reason is that the \CFA type-system handles this kind of overloading well using the left-hand assignment-type and complex conversion costs.
270Hence, the type system correctly handles all uses of addition (explicit or implicit) for @char *@.
271\begin{cfa}
272printf( "%s %s %s %c %c\n", "abc", cs, cs + 3, cs['a'], 'a'[cs] );
[56ec508]273\end{cfa}
[b0296dba]274Only @char@ addition can result in ambiguities, and only when there is no left-hand information.
[56ec508]275\begin{cfa}
[780727f]276ch = ch + 'b'; $\C[2in]{// LHS disambiguate, add character values}$
277s = 'a' + 'b'; $\C{// LHS disambiguate, concatenate characters}$
[411aa65]278printf( "%c\n", @'a' + 'b'@ ); $\C{// no LHS information, ambiguous}$
279printf( "%c\n", @(return char)@('a' + 'b') ); $\C{// disambiguate with ascription cast}\CRT$
[56ec508]280\end{cfa}
[b0296dba]281The ascription cast, @(return T)@, disambiguates by stating a (LHS) type to use during expression resolution (not a conversion).
282Fortunately, character addition without LHS information is rare in C/\CFA programs, so repurposing the operator @+@ for @string@ types is not a problem.
[780727f]283Note, other programming languages that repurpose @+@ for concatenation, can have similar ambiguity issues.
[56ec508]284
[b0296dba]285Interestingly, \CC cannot support this generality because it does not use the left-hand side of assignment in expression resolution.
[e78d969]286While it can special-case some combinations:
[56ec508]287\begin{c++}
288s = 'a' + s; $\C[2in]{// compiles in C++}$
289s = "a" + s;
290\end{c++}
291it cannot generalize to any number of steps:
292\begin{c++}
293s = 'a' + 'b' + s; $\C{// does not compile in C++}\CRT$
294s = "a" + "b" + s;
295\end{c++}
[fc8ec54]296
297
298\subsection{Repetition}
299
300The binary operators @*@ and @*=@ repeat a string $N$ times.
[e78d969]301If $N = 0$, a zero-length string results, @""@.
[fc8ec54]302\begin{cquote}
[5d300ba]303\begin{tabular}{@{}ll@{}}
[fc8ec54]304\begin{cfa}
[b0296dba]305s = 'x' * 0;
[fc8ec54]306s = 'x' * 3;
307s = "abc" * 3;
[780727f]308s = ("MIKE" + ' ') * 3;
[fc8ec54]309\end{cfa}
310&
311\begin{cfa}
[780727f]312""
[56ec508]313"xxx"
314"abcabcabc"
315"MIKE MIKE MIKE "
[fc8ec54]316\end{cfa}
317\end{tabular}
318\end{cquote}
[b0296dba]319Like concatenation, there is a potential ambiguity with multiplication of characters;
[780727f]320multiplication of pointers does not exist in C.
[b0296dba]321\begin{cfa}
[5d300ba]322ch = ch * 3; $\C[2in]{// LHS disambiguate, multiply character value}$
[780727f]323s = 'a' * 3; $\C{// LHS disambiguate, concatenate characters}$
[411aa65]324printf( "%c\n", @'a' * 3@ ); $\C{// no LHS information, ambiguous}$
325printf( "%c\n", @(return char)@('a' * 3) ); $\C{// disambiguate with ascription cast}\CRT$
[b0296dba]326\end{cfa}
327Fortunately, character multiplication without LHS information is even rarer than addition, so repurposing the operator @*@ for @string@ types is not a problem.
[fc8ec54]328
329
330\subsection{Substring}
[780727f]331
332The substring operation returns a subset of a string starting at a position in the string and traversing a length, or matching a pattern string.
[fc8ec54]333\begin{cquote}
[5d300ba]334\setlength{\tabcolsep}{8pt}
335\begin{tabular}{@{}ll|ll@{}}
[780727f]336\multicolumn{2}{@{}c}{\textbf{length}} & \multicolumn{2}{c@{}}{\textbf{pattern}} \\
337\multicolumn{4}{@{}l}{\lstinline{string name = "PETER"}} \\
338\begin{cfa}
339s = name( 0, 4 );
340s = name( 1, 4 );
341s = name( 2, 4 );
342s = name( 4, -2 );
343s = name( 8, 2 );
344s = name( 0, -2 );
345s = name( -1, -2 );
[fc8ec54]346s = name( -3 );
347\end{cfa}
348&
349\begin{cfa}
[780727f]350"PETE"
351"ETER"
352"TER" // clip length to 3
353"ER"
[5d300ba]354"" // clip, beyond right
355"" // clip, beyond left
[780727f]356"ER"
357"TER" // to end of string
[56ec508]358\end{cfa}
359&
360\begin{cfa}
[780727f]361s = name( "ET" );
[56ec508]362s = name( "WW" );
363
364
365
366
[780727f]367
368
[56ec508]369\end{cfa}
370&
371\begin{cfa}
[780727f]372"ET"
373"" // does not occur
374
375
[56ec508]376
377
378
379
[fc8ec54]380\end{cfa}
381\end{tabular}
382\end{cquote}
[780727f]383For the length form, a negative starting position is a specification from the right end of the string.
[fc8ec54]384A negative length means that characters are selected in the opposite (right to left) direction from the starting position.
385If the substring request extends beyond the beginning or end of the string, it is clipped (shortened) to the bounds of the string.
[f8913b7c]386If the substring request is completely outside of the original string, a null string is returned.
[780727f]387For the pattern-form, it returns the pattern string if the pattern matches or a null string if the pattern does not match.
[602ac05]388The usefulness of this mechanism is discussed next.
[fc8ec54]389
[b0296dba]390The substring operation can appear on the left side of assignment, where it defines a replacement substring.
391The length of the right string may be shorter, the same, or longer than the length of left string.
[f8913b7c]392Hence, the left string may decrease, stay the same, or increase in length.
393\begin{cquote}
[5d300ba]394\begin{tabular}{@{}ll@{}}
[56ec508]395\begin{cfa}[escapechar={}]
[f8913b7c]396digit( 3, 3 ) = "";
397digit( 4, 3 ) = "xyz";
398digit( 7, 0 ) = "***";
[56ec508]399digit(-4, 3 ) = "$$$";
400digit( 5 ) = "LLL";
[fc8ec54]401\end{cfa}
[f8913b7c]402&
[56ec508]403\begin{cfa}[escapechar={}]
404"0126789"
405"0126xyz"
406"0126xyz"
407"012$$$z"
408"012$$LLL"
[f8913b7c]409\end{cfa}
410\end{tabular}
411\end{cquote}
[5d300ba]412Here, substring pattern matching is useful on the left-hand side of assignment.
[56ec508]413\begin{cquote}
[5d300ba]414\begin{tabular}{@{}ll@{}}
[56ec508]415\begin{cfa}[escapechar={}]
416digit( "$$" ) = "345";
417digit( "LLL") = "6789";
[fc8ec54]418\end{cfa}
[56ec508]419&
[fc8ec54]420\begin{cfa}
[56ec508]421"012345LLL"
422"0123456789"
[fc8ec54]423\end{cfa}
[56ec508]424\end{tabular}
425\end{cquote}
[5d300ba]426Supporting a regular-expression pattern is a possible extension.
[fc8ec54]427
[780727f]428The replace operation extends substring to substitute all occurrences.
[f8913b7c]429\begin{cquote}
[5d300ba]430\begin{tabular}{@{}ll@{}}
[fc8ec54]431\begin{cfa}
[931f1b4]432s = replace( "PETER", "E", "XX" );
433s = replace( "PETER", "ET", "XX" );
434s = replace( "PETER", "W", "XX" );
[fc8ec54]435\end{cfa}
[f8913b7c]436&
437\begin{cfa}
[931f1b4]438"PXXTXXR"
439"PXXER"
440"PETER"
[f8913b7c]441\end{cfa}
442\end{tabular}
443\end{cquote}
[931f1b4]444The replacement is done left-to-right and substituted text is not examined for replacement.
445
446
447\subsection{Searching}
[fc8ec54]448
[780727f]449The @find@ operation returns the position of the first occurrence of a key in a string.
[b0296dba]450If the key does not appear in the string, the length of the string is returned.
[931f1b4]451\begin{cquote}
[5d300ba]452\begin{tabular}{@{}ll@{}}
[fc8ec54]453\begin{cfa}
[931f1b4]454i = find( digit, '3' );
[602ac05]455i = find( digit, "45" );
[b0296dba]456i = find( digit, "abc" );
[fc8ec54]457\end{cfa}
[931f1b4]458&
[fc8ec54]459\begin{cfa}
[931f1b4]4603
4614
[e78d969]46210 // not found, string length
[fc8ec54]463\end{cfa}
[931f1b4]464\end{tabular}
465\end{cquote}
[b0296dba]466
[d9aee90]467A character-class operation indicates if a string is composed completely of a particular class of characters, \eg, alphabetic, numeric, vowels, \etc.
[931f1b4]468\begin{cquote}
[5d300ba]469\begin{tabular}{@{}ll@{}}
[fc8ec54]470\begin{cfa}
[931f1b4]471charclass vowels{ "aeiouy" };
472i = include( "aabiuyoo", vowels );
[e78d969]473i = include( "aaeiuyoo", vowels );
[fc8ec54]474\end{cfa}
[931f1b4]475&
476\begin{cfa}
[fc8ec54]477
[931f1b4]4782 // b non-compliant
[e78d969]4798 // compliant, string length
[931f1b4]480\end{cfa}
481\end{tabular}
482\end{cquote}
[b0296dba]483@vowels@ defines a character class and function @include@ checks if all characters in the string appear in the class (compliance).
[e78d969]484The position of the first non-compliant character is returned or the position of the last character if the string is compliant.
[931f1b4]485There is no relationship between the order of characters in the two strings.
486Function @exclude@ is the reverse of @include@, checking if all characters in the string are excluded from the class (compliance).
487\begin{cquote}
[5d300ba]488\begin{tabular}{@{}ll@{}}
[fc8ec54]489\begin{cfa}
[931f1b4]490i = exclude( "cdbfghmk", vowels );
491i = exclude( "cdyfghmk", vowels );
[fc8ec54]492\end{cfa}
[931f1b4]493&
[fc8ec54]494\begin{cfa}
[e78d969]4958 // compliant, string length
[931f1b4]4962 // y non-compliant
[fc8ec54]497\end{cfa}
[931f1b4]498\end{tabular}
499\end{cquote}
500Both forms can return the longest substring of compliant characters.
501\begin{cquote}
[5d300ba]502\begin{tabular}{@{}ll@{}}
[fc8ec54]503\begin{cfa}
[931f1b4]504s = include( "aabiuyoo", vowels );
[e78d969]505s = include( "aaeiuyoo", vowels );
[931f1b4]506s = exclude( "cdbfghmk", vowels );
507s = exclude( "cdyfghmk", vowels );
[fc8ec54]508\end{cfa}
[931f1b4]509&
[fc8ec54]510\begin{cfa}
[931f1b4]511"aaeiuyoo"
512"aa"
513"cdbfghmk"
514"cd"
[fc8ec54]515\end{cfa}
[931f1b4]516\end{tabular}
517\end{cquote}
[fc8ec54]518
[31be464]519There are also versions of @include@ and @exclude@, returning a position or string, taking a validation function, like one of the C character-class functions.\footnote{It is part of the hereditary of C that these function take and return an \lstinline{int} rather than a \lstinline{bool}, which affects the function type.}
[931f1b4]520\begin{cquote}
[5d300ba]521\begin{tabular}{@{}ll@{}}
[fc8ec54]522\begin{cfa}
[b0296dba]523i = include( "1FeC34aB", @isxdigit@ );
524i = include( ".,;'!\"", @ispunct@ );
525i = include( "XXXx", @isupper@ );
[fc8ec54]526\end{cfa}
[931f1b4]527&
[fc8ec54]528\begin{cfa}
[931f1b4]5298 // compliant
5306 // compliant
5313 // non-compliant
[fc8ec54]532\end{cfa}
[931f1b4]533\end{tabular}
534\end{cquote}
[d9aee90]535These operations perform an \emph{apply} of the validation function to each character, where the function returns a boolean indicating a stopping condition for the search.
[e78d969]536The position of the first non-compliant character is returned or the position of the last character if the string is compliant (and vice versa for @exclude@).
[fc8ec54]537
[931f1b4]538The translate operation returns a string with each character transformed by one of the C character transformation functions.
539\begin{cquote}
[5d300ba]540\begin{tabular}{@{}ll@{}}
[fc8ec54]541\begin{cfa}
[931f1b4]542s = translate( "abc", @toupper@ );
543s = translate( "ABC", @tolower@ );
544int tospace( int c ) { return isspace( c ) ? ' ' : c; }
545s = translate( "X X\tX\nX", @tospace@ );
[fc8ec54]546\end{cfa}
[931f1b4]547&
[fc8ec54]548\begin{cfa}
[931f1b4]549"ABC"
550"abc"
551
552"X X X X"
[fc8ec54]553\end{cfa}
[931f1b4]554\end{tabular}
555\end{cquote}
[fc8ec54]556
[56ec508]557
[b0296dba]558\subsection{Returning N on Search Failure}
[fc8ec54]559
[b0296dba]560Some of the prior string operations are composite, \eg string operations returning the longest substring of compliant characters (@include@) are built using a search and then substring the appropriate text.
561However, string search can fail, which is reported as an alternate search outcome, possibly an exception.
562Many string libraries use a return code to indicate search failure, with a failure value of @0@ or @-1@ (PL/I~\cite{PLI} returns @0@).
[5d300ba]563This semantics leads to an awkward pattern, which can appear many times in a string library or user code.
[602ac05]564\begin{cfa}
[b0296dba]565i = exclude( s, alpha );
566if ( i != -1 ) return s( 0, i );
567else return "";
[602ac05]568\end{cfa}
[5d300ba]569The problem is that substring does the wrong thing or fails with the failure return-code in most string libraries, so it has to be special cased.
[b0296dba]570
[d9aee90]571\CFA adopts a return code but the failure value is taken from the index-of function in APL~\cite{apl}, which returns the length of the target string $N$ (or $N+1$ for 1 origin).
[b0296dba]572This semantics allows many search and substring functions to be written without conditions, \eg:
[602ac05]573\begin{cfa}
[b0296dba]574string include( const string & s, int (*f)( int ) ) { return @s( 0, include( s, f ) )@; }
575string exclude( const string & s, int (*f)( int ) ) { return @s( 0, exclude( s, f ) )@; }
[602ac05]576\end{cfa}
577In string systems with an $O(1)$ length operator, checking for failure is low cost.
[fc8ec54]578\begin{cfa}
[602ac05]579if ( include( line, alpha ) == len( line ) ) ... // not found, 0 origin
[fc8ec54]580\end{cfa}
[b0296dba]581\VRef[Figure]{f:ExtractingWordsText} compares \CC and \CFA string code for extracting words from a line of text, repeatedly removing non-word text and then a word until the line is empty.
582The \CFA code is simpler solely because of the choice for indicating search failure.
[d9aee90]583(A simplification of the \CC version is to concatenate a sentinel character at the end of the line so the call to @find_first_not_of@ does not fail.)
[b0296dba]584
585\begin{figure}
586\begin{cquote}
[5d300ba]587\begin{tabular}{@{}ll@{}}
[b0296dba]588\multicolumn{1}{c}{\textbf{\CC}} & \multicolumn{1}{c}{\textbf{\CFA}} \\
589\begin{cfa}
590for ( ;; ) {
591 string::size_type posn = line.find_first_of( alpha );
592 if ( posn == string::npos ) break;
593 line = line.substr( posn );
594 posn = line.find_first_not_of( alpha );
595 if ( posn != string::npos ) {
596 cout << line.substr( 0, posn ) << endl;
597 line = line.substr( posn );
598 } else {
599 cout << line << endl;
600 line = "";
601 }
602}
603\end{cfa}
604&
605\begin{cfa}
[411aa65]606for () {
[b0296dba]607 size_t posn = exclude( line, alpha );
608 if ( posn == len( line ) ) break;
609 line = line( posn );
610 posn = include( line, alpha );
611
612 sout | line( 0, posn );
613 line = line( posn );
614
615
616
617
618}
619\end{cfa}
620\end{tabular}
621\end{cquote}
622\caption{Extracting Words from Line of Text}
623\label{f:ExtractingWordsText}
624\end{figure}
[fc8ec54]625
626
627\subsection{C Compatibility}
628
[602ac05]629To ease conversion from C to \CFA, \CFA provides companion C @string@ functions.
630Hence, it is possible to convert a block of C string operations to \CFA strings just by changing the type @char *@ to @string@.
[d9aee90]631\begin{cquote}
632\begin{tabular}{@{}ll@{}}
[5d300ba]633\multicolumn{2}{@{}l@{}}{\lstinline{char s[32]; // changing s's type to string works}} \\
[602ac05]634\begin{cfa}
[b0296dba]635strlen( s );
636strnlen( s, 3 );
637strcmp( s, "abc" );
638strncmp( s, "abc", 3 );
[d9aee90]639\end{cfa}
640&
641\begin{cfa}
[602ac05]642strcpy( s, "abc" );
643strncpy( s, "abcdef", 3 );
644strcat( s, "xyz" );
645strncat( s, "uvwxyz", 3 );
[fc8ec54]646\end{cfa}
[d9aee90]647\end{tabular}
648\end{cquote}
[fc8ec54]649However, the conversion fails with I/O because @printf@ cannot print a @string@ using format code @%s@ because \CFA strings are not null terminated.
[602ac05]650Nevertheless, this capability does provide a useful starting point for conversion to safer \CFA strings.
[fc8ec54]651
652
[602ac05]653\subsection{I/O Operators}
654
[5d300ba]655The ability to input and output a string is as essential as for any other type.
656The goal for character I/O is to work with groups rather than individual characters.
[602ac05]657A comparison with \CC string I/O is presented as a counterpoint to \CFA string I/O.
658
659The \CC output @<<@ and input @>>@ operators are defined on type @string@.
660\CC output for @char@, @char *@, and @string@ are similar.
661The \CC manipulators are @setw@, and its associated width controls @left@, @right@ and @setfill@.
662\begin{cquote}
[5d300ba]663\begin{tabular}{@{}ll@{}}
[602ac05]664\begin{c++}
665string s = "abc";
666cout << setw(10) << left << setfill( 'x' ) << s << endl;
667\end{c++}
668&
669\begin{c++}
670
671"abcxxxxxxx"
672\end{c++}
673\end{tabular}
674\end{cquote}
675
676The \CFA input/output operator @|@ is defined on type @string@.
[b0296dba]677\CFA output for @char@, @char *@, and @string@ are similar.
[602ac05]678The \CFA manipulators are @bin@, @oct@, @hex@, @wd@, and its associated width control and @left@.
679\begin{cquote}
[5d300ba]680\begin{tabular}{@{}ll@{}}
[602ac05]681\begin{cfa}
682string s = "abc";
683sout | bin( s ) | nl
684 | oct( s ) | nl
685 | hex( s ) | nl
686 | wd( 10, s ) | nl
687 | wd( 10, 2, s ) | nl
688 | left( wd( 10, s ) );
[56ec508]689\end{cfa}
[602ac05]690&
691\begin{cfa}
692
693"0b1100001 0b1100010 0b1100011"
694"0141 0142 0143"
695"0x61 0x62 0x63"
696" abc"
697" ab"
698"abc "
699\end{cfa}
700\end{tabular}
701\end{cquote}
[8136df1]702\CC @setfill@ is not considered an important string manipulator.
[602ac05]703
[b0296dba]704\CC input matching for @char@, @char *@, and @string@ are similar, where \emph{all} input characters are read from the current point in the input stream to the end of the type size, format width, whitespace, end of line (@'\n'@), or end of file.
[602ac05]705The \CC manipulator is @setw@ to restrict the size.
706Reading into a @char@ is safe as the size is 1, @char *@ is unsafe without using @setw@ to constraint the length (which includes @'\0'@), @string@ is safe as its grows dynamically as characters are read.
707\begin{cquote}
[5d300ba]708\begin{tabular}{@{}ll@{}}
[602ac05]709\begin{c++}
710char ch, c[10];
711string s;
712cin >> ch >> setw( 5 ) >> c >> s;
[b0296dba]713@abcde fg@
[602ac05]714\end{c++}
715&
716\begin{c++}
717
[56ec508]718
[602ac05]719'a' "bcde" "fg"
[56ec508]720
[602ac05]721\end{c++}
722\end{tabular}
723\end{cquote}
[411aa65]724Input text can be \emph{gulped}, including whitespace, from the current point to an arbitrary delimiter character using @getline@.
[5d300ba]725\begin{cquote}
726\setlength{\tabcolsep}{10pt}
727\begin{tabular}{@{}ll@{}}
728\multicolumn{1}{c}{\textbf{\CC}} & \multicolumn{1}{c}{\textbf{\CFA}} \\
729\begin{cfa}
730string s1, s2, s3;
731getline( cin, s1, 'a' );
732getline( cin, s2, 'w' );
733getline( cin, s3 );
734cout << s1 << ' ' << s2 << ' ' << s3 << endl;
735@bbbad ddwxyz@
736\end{cfa}
737&
738\begin{cfa}
739
740sin | getline( s1, 'a' )
741 | getline( s2, 'w' )
742 | getline( s3 );
743sout | s1 | s2 | s3; "bbb" "d dd" "xyz"
744
745\end{cfa}
746\end{tabular}
747
748\end{cquote}
[602ac05]749
[b0296dba]750The \CFA philosophy for input is that, for every constant type in C, these constants should be usable as input.
[602ac05]751For example, the complex constant @3.5+4.1i@ can appear as input to a complex variable.
752\CFA input matching for @char@, @char *@, and @string@ are similar.
753C-strings may only be read with a width field, which should match the string size.
754Certain input manipulators support a scanset, which is a simple regular expression from @printf@.
[b0296dba]755The \CFA manipulators for these types are @wdi@,\footnote{Due to an overloading issue in the type-resolver, the input width name must be temporarily different from the output, \lstinline{wdi} versus \lstinline{wd}.} and its associated width control and @left@, @quote@, @incl@, @excl@, and @getline@.
[602ac05]756\begin{cquote}
757\setlength{\tabcolsep}{10pt}
[5d300ba]758\begin{tabular}{@{}ll@{}}
[602ac05]759\begin{c++}
760char ch, c[10];
761string s;
762sin | ch | wdi( 5, c ) | s;
[b0296dba]763@abcde fg@
[5d300ba]764sin | @quote@( ch ) | @quote@( wdi( sizeof(c), c ) ) | @quote@( s, '[', ']' ) | nl;
765@'a' "bcde" [fg]@
[602ac05]766sin | incl( "a-zA-Z0-9 ?!&\n", s ) | nl;
[b0296dba]767@x?&000xyz TOM !.@
[602ac05]768sin | excl( "a-zA-Z0-9 ?!&\n", s );
[b0296dba]769@<>{}{}STOP@
[602ac05]770\end{c++}
771&
772\begin{c++}
773
774
[b0296dba]775'a' "bcde" "fg"
[602ac05]776
[b0296dba]777'a' "bcde" "fg"
[602ac05]778
779"x?&000xyz TOM !"
780
781"<>{}{}"
782
783\end{c++}
784\end{tabular}
785\end{cquote}
[411aa65]786Note, the ability to read in quoted strings with whitespace to match with program string constants.
[b0296dba]787The @nl@ at the end of an input ignores the rest of the line.
[fc8ec54]788
789
[602ac05]790\subsection{Assignment}
[fc8ec54]791
[195f43d]792While \VRef[Figure]{f:StrApiCompare} emphasizes cross-language similarities, it elides many specific operational differences.
[b0296dba]793For example, the \CC @replace@ function selects a substring in the target and substitutes it with the source string, which can be smaller or larger than the substring.
794\CC modifies the mutable receiver object, replacing by position (zero origin) and length.
795\begin{c++}
[195f43d]796string s1 = "abcde";
[5d300ba]797s1.replace( 2, 3, "xy" ); "abxy"
[b0296dba]798\end{c++}
799Java cannot modify the receiver (immutable strings) so it returns a new string, replacing by text.
[06ffa95]800\label{p:JavaReplace}
[195f43d]801\begin{java}
802String s = "abcde";
[5d300ba]803String r = s.replace( "cde", "xy" ); "abxy"
[b0296dba]804\end{java}
805Java also provides a mutable @StringBuffer@, replacing by position (zero origin) and length.
[06ffa95]806\begin{java}
[195f43d]807StringBuffer sb = new StringBuffer( "abcde" );
[5d300ba]808sb.replace( 2, 5, "xy" ); "abxy"
[b0296dba]809\end{java}
810However, there are anomalies.
811@StringBuffer@'s @substring@ returns a @String@ copy that is immutable rather than modifying the receiver.
812As well, the operations are asymmetric, \eg @String@ has @replace@ by text but not replace by position and vice versa for @StringBuffer@.
813
[d9aee90]814More significant operational differences relate to storage management, often appearing through assignment (@target = source@), and are summarized in \VRef[Figure]{f:StrSemanticCompare}, which defines properties type abstraction, state, symmetry, and referent.
[195f43d]815The following discussion justifies the figure's yes/no entries per language.
[5e8d75bb]816
817\begin{figure}
[195f43d]818\setlength{\extrarowheight}{2pt}
[5d300ba]819\setlength{\tabcolsep}{5pt}
820\begin{tabularx}{\textwidth}{@{}p{0.8in}XXcccc@{}}
[195f43d]821 & & & \multicolumn{4}{@{}c@{}}{\underline{Supports Helpful?}} \\
[5e8d75bb]822 & Required & Helpful & C & \CC & Java & \CFA \\
823\hline
[5d300ba]824Type Abstraction
[195f43d]825 & Low-level: The string type is a varying amount of text communicated via a parameter or return.
826 & High-level: The string-typed relieves the user of managing memory for the text.
827 & no & yes & yes & yes \\
[5e8d75bb]828\hline
[195f43d]829State
[5e8d75bb]830 & \multirow{2}{2in}
[195f43d]831 {Fast Initialize: The target receives the characters of the source without copying the characters, resulting in an Alias or Snapshot.}
[f85de47]832 & Alias: The target variable names the source text; changes made in either variable are visible in both.
[195f43d]833 & yes & yes & no & yes \\
[5e8d75bb]834\cline{3-7}
835 &
[f85de47]836 & Snapshot: Subsequent operations on either the source or target variable do not affect the other.
[195f43d]837 & no & no & yes & yes \\
[5e8d75bb]838\hline
839Symmetry
[195f43d]840 & Laxed: The target's type is anything string-like; it may have a different status concerning ownership.
841 & Strict: The target's type is the same as the source; both strings are equivalent peers concerning ownership.
[411aa65]842 & N/A & no & yes & yes \\
[5e8d75bb]843\hline
844Referent
[195f43d]845 & Variable-Constrained: The target can accept the entire text of the source.
846 & Fragment: The target can accept an arbitrary substring of the source.
847 & no & no & yes & yes
848\end{tabularx}
[5e8d75bb]849
850\noindent
851Notes
[195f43d]852\begin{itemize}[parsep=0pt]
[5e8d75bb]853\item
854 All languages support Required in all criteria.
855\item
856 A language gets ``Supports Helpful'' in one criterion if it can do so without sacrificing the Required achievement on all other criteria.
857\item
[f85de47]858 The C ``string'' is @char *@, under the conventions of @<string.h>@. Because this type does not manage a text allocation, symmetry does not apply.
[5e8d75bb]859\item
[411aa65]860 The Java @String@ class is analyzed; its @StringBuffer@ class behaves similarly to \CC.
[5e8d75bb]861\end{itemize}
[195f43d]862\caption{Comparison of languages' strings, storage management perspective.}
[5e8d75bb]863\label{f:StrSemanticCompare}
864\end{figure}
865
[411aa65]866In C, these declarations are very different.
[5e8d75bb]867\begin{cfa}
[f85de47]868char x[$\,$] = "abcde";
869char * y = "abcde";
[5e8d75bb]870\end{cfa}
[411aa65]871Both associate the declared name with the fixed, six contiguous bytes: @{'a', 'b', 'c', 'd', 'e', 0}@.
872But @x@ is allocated on the stack (with values filled at the declaration), while @y@ refers to the executable's read-only data-section.
[f85de47]873With @x@ representing an allocation, it offers information in @sizeof(x)@ that @y@ does not.
[411aa65]874But this extra information is second-class, as it can only be used in the immediate lexical context, \ie it cannot be passed to string operations or user functions.
[f85de47]875Only pointers to text buffers are first-class, and discussed further.
[5e8d75bb]876\begin{cfa}
[f85de47]877char * s = "abcde";
[411aa65]878char * s1 = s; $\C[2.25in]{// alias state, N/A symmetry, variable-constrained referent}$
879char * s2 = &s[1]; $\C{// alias state, N/A symmetry, variable-constrained referent}$
880char * s3 = &s2[1]; $\C{// alias state, N/A symmetry, variable-constrained referent}\CRT$
[f85de47]881printf( "%s %s %s %s\n", s, s1, s2, s3 );
882$\texttt{\small abcde abcde bcde cde}$
[5e8d75bb]883\end{cfa}
[06ffa95]884Note, all of these aliased strings rely on the single null termination character at the end of @s@.
[f85de47]885The issue of symmetry does not apply to a low-level API because no management of a text buffer is provided.
886With the @char *@ type not managing the text storage, there is no ownership question, \ie operations on @s1@ or @s2@ never lead to their memory becoming reusable.
[195f43d]887While @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.
[5e8d75bb]888
[195f43d]889In \CC, @string@ offers a high-level abstraction.
890\begin{cfa}
891string s = "abcde";
892string & s1 = s; $\C[2.25in]{// alias state, lax symmetry, variable-constrained referent}$
[f85de47]893string s2 = s; $\C{// no fast-initialize (strict symmetry, variable-constrained referent)}$
894string s3 = s.substr( 1, 2 ); $\C{// no fast-initialize (strict symmetry, fragment referent)}$
895string s4 = s3.substr( 1, 1 ); $\C{// no fast-initialize (strict symmetry, fragment referent)}$
[195f43d]896cout << s << ' ' << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << endl;
897$\texttt{\small abcde abcde abcde bc c}$
898string & s5 = s.substr(2,4); $\C{// error: cannot point to temporary}\CRT$
899\end{cfa}
[411aa65]900The @s1@ lax symmetry reflects how its validity depends on the lifetime of @s@.
[f85de47]901It is common practice in \CC to use the @s1@-style for a by-reference function parameter.
902Doing so assumes that the callee only uses the referenced string for the duration of the call, \ie no storing the parameter (as a reference) for later.
903So, when the called function is a constructor, its definition typically uses an @s2@-style copy-initialization.
[195f43d]904Exceptions to this pattern are possible, but require the programmer to assure safety where the type system does not.
[411aa65]905The @s3@ initialization must copy the substring to support a subsequent @c_str@ call, which provides null-termination, generally at a different position than the source string's.
[f85de47]906@s2@ assignment could be made fast, by reference-counting the text area and using copy-on-write, but would require an implementation upgrade.
[195f43d]907
908In Java, @String@ also offers a high-level abstraction:
[06ffa95]909\begin{java}
[195f43d]910String s = "abcde";
911String s1 = s; $\C[2.25in]{// snapshot state, strict symmetry, variable-constrained referent}$
912String s2 = s.substring( 1, 3 ); $\C{// snapshot state (possible), strict symmetry, fragment referent}$
913String s3 = s2.substring( 1, 2 ); $\C{// snapshot state (possible), strict symmetry, fragment referent}\CRT$
914System.out.println( s + ' ' + s1 + ' ' + s2 + ' ' + s3 );
915$\texttt{\small abcde abcde bc c}$
[5d300ba]916System.out.println( (s == s1) + " " + (s == s2) + " " + (s2 == s3) );
[195f43d]917$\texttt{\small true false false}$
[06ffa95]918\end{java}
[f85de47]919Note, Java's @substring@ takes a start and end position, rather than a start position and length.
[195f43d]920Here, facts about Java's implicit pointers and pointer equality can over complicate the picture, and so are ignored.
921Furthermore, Java's string immutability means string variables behave as simple values.
922The result in @s1@ is the pointer in @s@, and their pointer equality confirm no time is spent copying characters.
923With @s2@, the case for fast-copy is more subtle.
924Certainly, its value is not pointer-equal to @s@, implying at least a further allocation.
[411aa65]925But because Java is \emph{not} constrained to use a null-terminated representation, a standard-library implementation is free to refer to the source characters in-place.
[f85de47]926Java does not meet the aliasing requirement because immutability makes it impossible to modify.
[06ffa95]927Java'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@;
928as a result, @StringBuffer@ scores as \CC.
[f85de47]929The easy symmetry that the Java string enjoys is aided by Java's garbage collection; Java's @s1@ is doing effectively the operation of \CC's @s1@, though without the consequence of complicating memory management.
930
931% former \PAB{What complex storage management is going on here?}
932% answer: the variable names in the sentence were out of date; fixed
[06ffa95]933
934Finally, in \CFA, @string@ also offers a high-level abstraction:
[5e8d75bb]935\begin{cfa}
[195f43d]936string s = "abcde";
[f85de47]937string & s1 = s; $\C[2.25in]{// alias state, lax symmetry, variable-constrained referent}$
[195f43d]938string s2 = s; $\C{// snapshot state, strict symmetry, variable-constrained referent}$
[f85de47]939string s3 = s`share; $\C{// alias state, strict symmetry, variable-constrained referent}$
940string s4 = s( 1, 2 ); $\C{// snapshot state, strict symmetry, fragment referent}$
[5d300ba]941string s5 = s4( 1, 1 )`share; $\C{// alias state, strict symmetry, fragment referent}\CRT$
[195f43d]942sout | s | s1 | s2 | s3 | s4 | s5;
943$\texttt{\small abcde abcde abcde abcde bc c}$
[5e8d75bb]944\end{cfa}
[195f43d]945% all helpful criteria of \VRef[Figure]{f:StrSemanticCompare} are satisfied.
946The \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).
[f85de47]947The @s1@ case is the same in \CFA as in \CC.
948But the \CFA @s2@ delivers a fast snapshot, while the \CC @s2@ does not.
949\CFA offers the snapshot-vs-alias choice in @s2@-vs-@s3@ and @s4@-vs-@s5@.
950Regardless of snapshot-vs-alias and fragment-vs-whole, the target is always of the same @string@ type.
[5e8d75bb]951
[f85de47]952% former \PAB{Need to discuss the example, as for the other languages.}
953% answer: done
[5e8d75bb]954
955
[411aa65]956\subsection{Logical Overlap}
[c01a2fd]957
[f85de47]958It may be unfamiliar to combine \VRef[Figure]{f:StrSemanticCompare}'s alias state and fragment referent in one API, or at the same time.
959This section shows the capability in action.
[c01a2fd]960
[411aa65]961\begin{comment}
962The metaphor of a GUI text-editor is used to illustrate combining these features.
963Most editors allow selecting a consecutive block of text (highlighted) to define an aliased substring within a document.
964Typing in this area overwrites the prior text, replacing the selected text with less, same, or more text.
965Importantly, the document also changes size, not just the selection.
966%This alias model is assigning to an aliased substring for two strings overlapping by total containment: one is the selected string, the other is the document.
967Extend the metaphor to two selected areas, where one area can be drag-and-dropped into another, changing the text in the drop area and correspondingly changing the document.
[a17f496]968When the selected areas are independent, the semantics of the drag-and-drop are straightforward.
[411aa65]969However, for overlapping selections, either partial or full, there are multiple useful semantics.
970For example, two areas overlap at the top, or bottom, or a block at a corner, where one areas is dropped into the other.
971For selecting a smaller area within a larger, and dropping the smaller area into the larger to replace it.
972In both cases, meaningful semantics must be constructed or the operation precluded.
973However, without this advanced capability, certain operations become multi-step, possible requiring explicit temporaries.
974\end{comment}
975
976A GUI text-editor provides a metaphor.
977Selecting a block of text using the mouse defines an aliased substring within a document.
978Typing in this area overwrites what was there, replacing the originally selected text with more or less text.
979But the \emph{containing document} also grows or shrinks, not just the selection.
980This action models assigning to an aliased substring when one string is completely contained in the other.
981
982Extend the metaphor to a multi-user editor.
983If Alice selects a range of text at the bottom, while Bob is rewriting a paragraph at the top, Alice's selection holds onto the characters initially selected, unaffected by Bob making the document grow/shrink even though Alice's start index in the document is changing.
984This action models assigning to an aliased substring when the two strings do not overlap.
985
986Logically, Alice's and Bob's actions on the whole document are like two single-user-edit cases, giving the semantics of the individual edits flowing into a whole.
987But, there is no need to have two separate document strings.
988Even if a third selection removes all the text, both Alice's and Bob's strings remain.
989The independence of their selections assumes that the editor API does not allow the selection to be enlarged, \ie adding text from the containing environment, which may have disappeared.
990
991This leaves the ``Venn-diagram overlap'' cases, where Alice's and Bob's selections overlap at the top, bottom, or corner.
992In this case, the selection areas are dependent, and so, changes in content and size in one may have an affect in the other.
993There are multiple possible semantics for this case.
994The remainder of this section shows the chosen semantics for all of the cases.
995
996String sharing is expressed using the @`share@ marker to indicate aliasing (mutations shared) \vs snapshot (not quite an immutable result, but one with subsequent mutations isolated).
[5d300ba]997This aliasing relationship is a \newterm{sticky property} established at initialization.
[06ffa95]998For example, here strings @s1@ and @s1a@ are in an aliasing relationship, while @s2@ is in a copy relationship.
[489d3ba]999\input{sharing1.tex}
[06ffa95]1000Here, the aliasing (@`share@) causes partial changes (subscripting) to flow in both directions.
[411aa65]1001(In the following examples, note how @s1@ and @s1a@ change together, and @s2@ is independent.)
[489d3ba]1002\input{sharing2.tex}
[06ffa95]1003Similarly for complete changes.
[489d3ba]1004\input{sharing3.tex}
[06ffa95]1005Because string assignment copies the value, RHS aliasing is irrelevant.
1006Hence, aliasing of the LHS is unaffected.
[489d3ba]1007\input{sharing4.tex}
1008
[411aa65]1009Now, consider string @s1_mid@ being an alias in the middle of @s1@, along with @s2@, made by a copy from the middle of @s1@.
[489d3ba]1010\input{sharing5.tex}
[06ffa95]1011Again, @`share@ passes changes in both directions; copy does not.
1012As 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"@.
1013This alternate positioning also applies to subscripting.
[489d3ba]1014\input{sharing6.tex}
1015
[06ffa95]1016Finally, assignment flows through the aliasing relationship without affecting its structure.
[489d3ba]1017\input{sharing7.tex}
[06ffa95]1018In the @"ff"@ assignment, the result is straightforward to accept because the flow direction is from contained (small) to containing (large).
1019The following rules explain aliasing substrings that flow in the opposite direction, large to small.
[bbf6a180]1020
[f85de47]1021Growth and shrinkage are natural extensions, as for the text-editor analogy, where an empty substring is as real as an empty string.
[489d3ba]1022\input{sharing8.tex}
[bbf6a180]1023
[06ffa95]1024Multiple portions of a string can be aliased.
1025% When there are several aliasing substrings at once, the text editor analogy becomes an online multi-user editor.
1026%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.
[489d3ba]1027\input{sharing9.tex}
[06ffa95]1028When @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,
[489d3ba]1029
[e78d969]1030Changes can happen on an aliasing substring that overlaps.
[489d3ba]1031\input{sharing10.tex}
[5d300ba]1032Strings @s1_crs@ and @s1_mid@ overlap at character 4, @'j'@, because the substrings are 3,2 and 4,2.
[8f250e0]1033When @s1_crs@'s size increases by 1, @s1_mid@'s starting location moves from 4 to 5, but the overlapping character remains, changing to @'+'@.
[489d3ba]1034
[b0296dba]1035\begin{figure}
1036\begin{cfa}
1037// x, a, b, c, & d are substring results passed by reference
1038// e is a substring result passed by value
1039void test( string & x, string & a, string & b, string & c, string & d, string e ) {
1040\end{cfa}
1041\begin{cquote}
1042\setlength{\tabcolsep}{2pt}
1043\begin{tabular}{@{}ll@{}}
1044\begin{cfa}
1045
1046 a( 0, 2 ) = "aaa";
1047 b( 1, 12 ) = "bbb";
[e78d969]1048 c( 3, 5 ) = "ccc";
[b0296dba]1049 c = "yyy";
1050 d( 0, 3 ) = "ddd";
1051 e( 0, 3 ) = "eee";
1052 x = e;
1053}
1054\end{cfa}
1055&
1056\sf
1057\setlength{\extrarowheight}{-0.5pt}
1058\begin{tabular}{@{}llllll@{}}
[e78d969]1059x & a & b & c & d & e \\
1060@"ssttttuuuww"@ & @"sst"@ & @"ttt"@ & @"ttuuu"@ & @"uww"@ & @"uww"@ \\
1061@"aaattttuuuww"@ & @"aaat"@ & @"ttt"@ & @"ttuuu"@ & @"uww"@ & @"uww"@ \\
1062@"aaatbbbtuuuww"@ & @"aaat"@ & @"tbbb"@ & @"tuuu"@ & @"uww"@ & @"uww"@ \\
1063@"aaatbbbtuucccww"@ & @"aaat"@ & @"tbbb"@ & @"tuuccc"@& @"cccww"@ & @"uww"@ \\
1064@"aaatbbbyyyww"@ & @"aaat"@ & @"tbbb"@ & @"yyy"@ & @"ww"@ & @"uww"@ \\
1065@"aaatbbbyyyddd"@ & @"aaat"@ & @"tbbb"@ & @"yyy"@ & @"ddd"@ & @"uww"@ \\
1066@"aaatbbbyyyddd"@ & @"aaat"@ & @"tbbb"@ & @"yyy"@ & @"ddd"@ & @"uww"@ \\
1067@"eee"@ & @""@ & @""@ & @""@ & @""@ & @"eee"@ \\
1068 &
[b0296dba]1069\end{tabular}
1070\end{tabular}
1071\end{cquote}
1072\begin{cfa}
1073int main() {
1074 string x = "xxxxxxxxxxx";
1075 test( x, x(0, 3), x(2, 3), x(4, 5), x(8, 5), x(8, 5) );
1076}
1077\end{cfa}
1078\caption{Parameter Passing}
1079\label{f:ParameterPassing}
1080\end{figure}
1081
[e78d969]1082\begin{figure}
1083\vspace*{-5pt}
1084\setlength{\tabcolsep}{1.5pt}
1085\setlength{\columnsep}{60pt}
1086\setlength{\columnseprule}{0.2pt}
1087\setlength{\extrarowheight}{-1pt}
1088
1089\begin{multicols}{2}
1090\sf
1091\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1092\begin{tabular}{@{}*{12}{c}@{}}
1093\multicolumn{12}{@{}l@{}}{function entry} \\
1094x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 \\
1095 & {\tt s} & {\tt s} & {\tt t} & {\tt t} & {\tt t} & {\tt t} & {\tt u} & {\tt u} & {\tt u} & {\tt w} & {\tt w} \\
1096a & 0 & 1 & 2 \\
1097 & {\tt s} & {\tt s} & {\tt t} \\
1098b & & & 0 & 1 & 2 \\
1099 & & & {\tt t} & {\tt t} & {\tt t} \\
1100c & & & & & 0 & 1 & 2 & 3 & 4 \\
1101 & & & & & {\tt t} & {\tt t} & {\tt u} & {\tt u} & {\tt u} \\
1102d & & & & & & & & & 0 & 1 & 2 \\
1103 & & & & & & & & & {\tt u} & {\tt w} & {\tt w}
1104\end{tabular}
1105&
1106\begin{tabular}{@{}*{5}{c}@{}}
1107 & \\
1108 & e & 0 & 1 & 2 \\
1109 & & {\tt u} & {\tt w} & {\tt w} \\
1110 & & & & \\
1111 & & & & \\
1112 & & & & \\
1113 & & & & \\
1114 & & & & \\
1115 & & & & \\
1116 & & & & \\
1117 & & & &
1118\end{tabular}
1119\end{tabular}
1120
1121\bigskip
1122@a( 0, 2 ) = "aaa";@ \\
1123\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1124\begin{tabular}{@{}*{13}{c}@{}}
1125x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 \\
1126 & {\tt a} & {\tt a} & {\tt a} & {\tt t} & {\tt t} & {\tt t} & {\tt t} & {\tt u} & {\tt u} & {\tt u} & {\tt w} & {\tt w} \\
1127a & 0 & 1 & 2 & 3 \\
1128 & {\tt a} & {\tt a} & {\tt a} & {\tt t} \\
1129b & & & & 0 & 1 & 2 \\
1130 & & & & {\tt t} & {\tt t} & {\tt t} \\
1131c & & & & & & 0 & 1 & 2 & 3 & 4 \\
1132 & & & & & & {\tt t} & {\tt t} & {\tt u} & {\tt u} & {\tt u} \\
1133d & & & & & & & & & & 0 & 1 & 2 \\
1134 & & & & & & & & & & {\tt u} & {\tt w} & {\tt w}
1135\end{tabular}
1136&
1137\begin{tabular}{@{}*{5}{c}@{}}
1138 & e & 0 & 1 & 2 \\
1139 & & {\tt u} & {\tt w} & {\tt w} \\
1140 & & & & \\
1141 & & & & \\
1142 & & & & \\
1143 & & & & \\
1144 & & & & \\
1145 & & & & \\
1146 & & & & \\
1147 & & & &
1148\end{tabular}
1149\end{tabular}
1150
1151\bigskip
1152
1153@b( 1, 12 ) = "bbb";@ \\
1154\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1155\begin{tabular}{@{}*{14}{c}@{}}
1156x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12\\
1157 & {\tt a} & {\tt a} & {\tt a} & {\tt t} & {\tt b} & {\tt b} & {\tt b} & {\tt t} & {\tt u} & {\tt u} & {\tt u} & {\tt w} & {\tt w} \\
1158a & 0 & 1 & 2 & 3 \\
1159 & {\tt a} & {\tt a} & {\tt a} & {\tt t} \\
1160b & & & & 0 & 1 & 2 & 3 \\
1161 & & & & {\tt t} & {\tt b} & {\tt b} & {\tt b} \\
1162c & & & & & & & & 0 & 1 & 2 & 3 \\
1163 & & & & & & & & {\tt t} & {\tt u} & {\tt u} & {\tt u} \\
1164d & & & & & & & & & & & 0 & 1 & 2 \\
1165 & & & & & & & & & & & {\tt u} & {\tt w} & {\tt w}
1166\end{tabular}
1167&
1168\begin{tabular}{@{}*{5}{c}@{}}
1169 & e & 0 & 1 & 2 \\
1170 & & {\tt u} & {\tt w} & {\tt w} \\
1171 & & & & \\
1172 & & & & \\
1173 & & & & \\
1174 & & & & \\
1175 & & & & \\
1176 & & & & \\
1177 & & & & \\
1178 & & & &
1179\end{tabular}
1180\end{tabular}
1181
1182\bigskip
1183
1184@c( 3, 5 ) = "ccc";@ \\
1185\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1186\begin{tabular}{@{}*{16}{c}@{}}
1187x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 \\
1188 & {\tt a} & {\tt a} & {\tt a} & {\tt t} & {\tt b} & {\tt b} & {\tt b} & {\tt t} & {\tt u} & {\tt u} & {\tt c} & {\tt c} & {\tt c} & {\tt w} & {\tt w} \\
1189a & 0 & 1 & 2 & 3 \\
1190 & {\tt a} & {\tt a} & {\tt a} & {\tt t} \\
1191b & & & & 0 & 1 & 2 & 3 \\
1192 & & & & {\tt t} & {\tt b} & {\tt b} & {\tt b} \\
1193c & & & & & & & & 0 & 1 & 2 & 3 & 4 & 5 \\
1194 & & & & & & & & {\tt t} & {\tt u} & {\tt u} & {\tt c} & {\tt c} & {\tt c} \\
1195d & & & & & & & & & & & 0 & 1 & 2 & 3 & 4 \\
1196 & & & & & & & & & & & {\tt c} & {\tt c} & {\tt c} & {\tt w} & {\tt w}
1197\end{tabular}
1198&
1199\begin{tabular}{@{}*{5}{c}@{}}
1200 & e & 0 & 1 & 2 \\
1201 & & {\tt u} & {\tt w} & {\tt w} \\
1202 & & & & \\
1203 & & & & \\
1204 & & & & \\
1205 & & & & \\
1206 & & & & \\
1207 & & & & \\
1208 & & & & \\
1209 & & & &
1210\end{tabular}
1211\end{tabular}
1212
1213@c = "yyy";@ \\
1214\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1215\begin{tabular}{@{}*{13}{c}@{}}
1216x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 \\
1217 & {\tt a} & {\tt a} & {\tt a} & {\tt t} & {\tt b} & {\tt b} & {\tt b} & {\tt y} & {\tt y} & {\tt y} & {\tt w} & {\tt w} \\
1218a & 0 & 1 & 2 & 3 \\
1219 & {\tt a} & {\tt a} & {\tt a} & {\tt t} \\
1220b & & & & 0 & 1 & 2 & 3 \\
1221 & & & & {\tt t} & {\tt b} & {\tt b} & {\tt b} \\
1222c & & & & & & & & 0 & 1 & 2 \\
1223 & & & & & & & & {\tt y} & {\tt y} & {\tt y} \\
1224d & & & & & & & & & & & 0 & 1 \\
1225 & & & & & & & & & & & {\tt w} & {\tt w}
1226\end{tabular}
1227&
1228\begin{tabular}{@{}*{5}{c}@{}}
1229 & e & 0 & 1 & 2 \\
1230 & & {\tt u} & {\tt w} & {\tt w} \\
1231 & & & & \\
1232 & & & & \\
1233 & & & & \\
1234 & & & & \\
1235 & & & & \\
1236 & & & & \\
1237 & & & & \\
1238 & & & &
1239\end{tabular}
1240\end{tabular}
1241
1242\bigskip
1243@d( 0, 3 ) = "ddd";@ \\
1244\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1245\begin{tabular}{@{}*{14}{c}@{}}
1246x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 \\
1247 & {\tt a} & {\tt a} & {\tt a} & {\tt t} & {\tt b} & {\tt b} & {\tt b} & {\tt y} & {\tt y} & {\tt y} & {\tt d} & {\tt d} & {\tt d} \\
1248a & 0 & 1 & 2 & 3 \\
1249 & {\tt a} & {\tt a} & {\tt a} & {\tt t} \\
1250b & & & & 0 & 1 & 2 & 3 \\
1251 & & & & {\tt t} & {\tt b} & {\tt b} & {\tt b} \\
1252c & & & & & & & & 0 & 1 & 2 \\
1253 & & & & & & & & {\tt y} & {\tt y} & {\tt y} \\
1254d & & & & & & & & & & & 0 & 1 & 2 \\
1255 & & & & & & & & & & & {\tt d} & {\tt d} & {\tt d}
1256\end{tabular}
1257&
1258\begin{tabular}{@{}*{5}{c}@{}}
1259 & e & 0 & 1 & 2 \\
1260 & & {\tt u} & {\tt w} & {\tt w} \\
1261 & & & & \\
1262 & & & & \\
1263 & & & & \\
1264 & & & & \\
1265 & & & & \\
1266 & & & & \\
1267 & & & & \\
1268 & & & &
1269\end{tabular}
1270\end{tabular}
1271
1272\bigskip
1273
1274@e( 0, 3 ) = "eee";@ \\
1275\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1276\begin{tabular}{@{}*{14}{c}@{}}
1277x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 \\
1278 & {\tt a} & {\tt a} & {\tt a} & {\tt t} & {\tt b} & {\tt b} & {\tt b} & {\tt y} & {\tt y} & {\tt y} & {\tt d} & {\tt d} & {\tt d} \\
1279a & 0 & 1 & 2 & 3 \\
1280 & {\tt a} & {\tt a} & {\tt a} & {\tt t} \\
1281b & & & & 0 & 1 & 2 & 3 \\
1282 & & & & {\tt t} & {\tt b} & {\tt b} & {\tt b} \\
1283c & & & & & & & & 0 & 1 & 2 \\
1284 & & & & & & & & {\tt y} & {\tt y} & {\tt y} \\
1285d & & & & & & & & & & & 0 & 1 & 2 \\
1286 & & & & & & & & & & & {\tt d} & {\tt d} & {\tt d}
1287\end{tabular}
1288&
1289\begin{tabular}{@{}*{5}{c}@{}}
1290 & e & 0 & 1 & 2 \\
1291 & & {\tt e} & {\tt e} & {\tt e} \\
1292 & & & & \\
1293 & & & & \\
1294 & & & & \\
1295 & & & & \\
1296 & & & & \\
1297 & & & & \\
1298 & & & & \\
1299 & & & &
1300\end{tabular}
1301\end{tabular}
1302
1303\bigskip
1304
1305@x = e;@ \\
1306\begin{tabular}{@{}l@{\hspace{10pt}}l@{}}
1307\begin{tabular}{@{}*{16}{c}@{}}
1308x & 0 & 1 & 2 \\
1309 & {\tt e} & {\tt e} & {\tt e} \\
1310a & \\
1311 & "" \\
1312b & \\
1313 & "" \\
1314c & \\
1315 & "" \\
1316d & \\
1317 & ""
1318\end{tabular}
1319&
1320\begin{tabular}{@{}*{5}{c}@{}}
1321 & e & 0 & 1 & 2 \\
1322 & & {\tt e} & {\tt e} & {\tt e} \\
1323 & & & & \\
1324 & & & & \\
1325 & & & & \\
1326 & & & & \\
1327 & & & & \\
1328 & & & & \\
1329 & & & & \\
1330 & & & &
1331\end{tabular}
1332\end{tabular}
1333\end{multicols}
1334
1335\caption{Execution of Function \lstinline{test}}
1336\label{f:ParametersPassingResults}
1337\end{figure}
1338
1339\VRef[Figure]{f:ParameterPassing} shows similar relationships when passing the results of substring operations by reference and by value to a subprogram.
1340Because this example is complex, \VRef[Figure]{f:ParametersPassingResults} shows line-by-line changes to the reference parameters @x@, @a@, @b@, @c@, @d@, and value parameter @e@ as function @test@ executes (read top to bottom, left to right).
1341The assignment to variable @a@ changes its substring @"ss"@ to @"aaa"@ increasing @a@ to @"aaat"@ (3 to 4 characters), increasing @x@ from 11 to 12 characters, and pushing @b@, @c@ and @d@ right one character in @x@ because there is no overlap with the changed characters.
1342Similarly, the assignment to variable @b@ changes its substring @"tt"@ to @"bbb"@ increasing @b@ to @"tbbb"@ (3 to 4 characters), increasing @x@ from 12 to 13 characters, and pushing @c@ and @d@ right one character in @x@ because there is no overlap with the changed characters.
1343The assignment to variable @c@ changes its substring @"u"@ (at the end) to @"ccc"@ increasing @c@ to @"tuuccc"@ (4 to 6 characters), increasing @x@ from 13 to 15 characters, and @d@ remains in the same position because it overlaps with the changed characters and its @"u"@ grows to @"ccc"@ giving @"cccww"@.
1344The second assignment to variable @c@ changes its substring @"tuuccc"@ to @"yyy"@ decreasing @c@ to @"yyy"@ (6 to 3 characters), decreasing @x@ from 15 to 12 characters, and the 3 character overlapping with @d@ are removed leaving @"ww"@.
1345The assignment to variable @d@ changes its substring @"ww"@ to @"ddd"@ increasing @d@ from 2 to 3 characters, increasing @x@ from 11 to 12 characters.
1346The assignment to variable @e@ changes its substring @"uww"@ to @"eee"@ with no change in length.
1347Finally, the assignment to @x@ sets it pointing at @e@, and all prior overlapping strings with @x@ become empty (null) strings.
1348
[489d3ba]1349
[411aa65]1350\section{Storage Management}
[489d3ba]1351
[e78d969]1352This section discusses the implementation of the prior string semantics, specifically, when strings overlap partially or completely.
1353% First, the issue of a string's mutable or immutable property is clarified.
1354% Java's immutable strings require copy-on-write when any overlapping string changes.
1355% However, \CFA/\CC strings are always mutability, even if specified with @const@.
1356% \begin{cfa}
1357% const string s1 = "abc";
1358% \end{cfa}
1359% Here, @const@ applies to the implicit @s1@ pointer to @"abc"@, and @"abc"@ is an immutable constant that is \emph{copied} into the string's storage.
1360% Hence, @s1@ is never pointing at an immutable constant and its underlying string is always mutable, without some addition mechanism as for explicit pointers/references.
1361% \begin{cfa}
1362% const int * const cip; // pointer and its referent are const
1363% const string s1 @const@ = "abc"; // second const applies to implicit referent
1364% \end{cfa}
[bbf6a180]1365
1366
[411aa65]1367\subsection{General Implementation}
[f85de47]1368\label{string-general-impl}
[bbf6a180]1369
[e78d969]1370The centrepiece of the string module is its memory manager.
[1b39705]1371The management scheme defines a shared buffer for string text.
[f85de47]1372Allocation in this buffer is always via a bump-pointer;
1373the buffer is compacted and/or relocated (to grow) when it fills.
[1b39705]1374A string is a smart pointer into this buffer.
[bbf6a180]1375
[411aa65]1376This cycle of frequent cheap allocations, interspersed with infrequent expensive compactions, has obvious similarities to a general-purpose memory-manager based on garbage collection (GC).
[1b39705]1377A few differences are noteworthy.
[8f250e0]1378First, in a general purpose manager, the allocated objects may contain pointers to other objects, making the transitive reachability of these objects a crucial property.
[1b39705]1379Here, the allocations are text, so one allocation never keeps another alive.
[f85de47]1380Second, in a general purpose manager, the handle that keeps an allocation alive is a bare pointer.
[411aa65]1381For strings, a fatter representation is acceptable because this pseudo-pointer is only used for entry into the string-heap, not for general data-substructure linking around the general heap.
[bbf6a180]1382
[0dffe91]1383\begin{figure}
[a7d93cb]1384\includegraphics{memmgr-basic.pdf}
[0dffe91]1385\caption{String memory-management data structures}
1386\label{f:memmgr-basic}
1387\end{figure}
[bbf6a180]1388
[1b39705]1389\VRef[Figure]{f:memmgr-basic} shows the representation.
[8f250e0]1390The heap header and text buffer define a sharing context.
[411aa65]1391Normally, one global context is appropriate for an entire program;
1392concurrency is discussed in \VRef{s:ControllingImplicitSharing}.
[e78d969]1393A string is a handle to a node in a linked list containing information about string text in the buffer.
[1b39705]1394The list is doubly linked for $O(1)$ insertion and removal at any location.
[f85de47]1395Strings are ordered in the list by text start address.
[411aa65]1396The heap header maintains a next-allocation pointer, @alloc@, pointing to the last live allocation in the buffer.
[1b39705]1397No external references point into the buffer and the management procedure relocates the text allocations as needed.
[8f250e0]1398A string handle references a containing string, while its string is contiguous and not null terminated.
[1b39705]1399The length sets an upper limit on the string size, but is large (4 or 8 bytes).
[8f250e0]1400String handles can be allocated in the stack or heap, and represent the string variables in a program.
1401Normal C life-time rules apply to guarantee correctness of the string linked-list.
[411aa65]1402The text buffer is large enough with good management so that often only one dynamic allocation is necessary during program execution, but not so large as to cause program bloat.
[8f250e0]1403% During this period, strings can vary in size dynamically.
[1b39705]1404
1405When the text buffer fills, \ie the next new string allocation causes @alloc@ to point beyond the end of the buffer, the strings are compacted.
1406The linked handles define all live strings in the buffer, which indirectly defines the allocated and free space in the buffer.
[411aa65]1407The string handles are maintained in sorted order, so the handle list can be traversed, copying the first live text to the start of the buffer, and subsequent strings after each other.
[5d300ba]1408After compaction, if free storage is still less than the new string allocation, a larger text buffer is heap-allocated, the current buffer is copied into the new buffer, and the original buffer is freed.
[f85de47]1409Note, the list of string handles is structurally unaffected during a compaction;
1410only the text pointers in the handles are modified to new buffer locations.
[1b39705]1411
[8f250e0]1412Object lifecycle events are the \emph{subscription-management} triggers in such a service.
[602ac05]1413There are two fundamental string-creation functions: importing external text like a C-string or reading a string, and initialization from an existing \CFA string.
[1b39705]1414When importing, storage comes from the end of the buffer, into which the text is copied.
[5d300ba]1415To maintain sorted order, the new string handle is inserted at the end of the handle list because the new text is at the end of the buffer.
[f85de47]1416When initializing from text already in the buffer, the new handle is a second reference into the original run of characters.
[5d300ba]1417To maintain sorted order, the new handle's linked-list position is after the original handle.
[1b39705]1418Both string initialization styles preserve the string module's internal invariant that the linked-list order matches the buffer order.
1419For string destruction, handles are removed from the list.
[411aa65]1420Once the last handle using a run of buffer characters is destroyed, that buffer space is excluded from use until the next compaction.
[1b39705]1421
[411aa65]1422Certain string operations result in a substring of another string.
1423The resulting handle is then placed in the correct sorted position in the list, possible requiring a short linear search to locate the position.
[1b39705]1424For string operations resulting in a new string, that string is allocated at the end of the buffer.
[e78d969]1425For shared-edit strings, handles that originally reference containing locations need to see the new value at the new buffer location.
[f85de47]1426These strings are moved to appropriate locations at the end of the list \see{details in \VRef{sharing-impl}}.
1427For nonshared-edit strings, a containing string can be moved and the other strings can remain in the same position.
1428String assignment works similarly to string initialization, maintaining the invariant of linked-list order matching buffer order.
[bbf6a180]1429
[f85de47]1430At the level of the memory manager, these modifications can always be explained as assignment and appendment.
1431For example, an append is an assignment into the empty substring at the end of the buffer.
[8f250e0]1432Favourable 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.
[f85de47]1433One notable example of favourable conditions occurs because the most recently written string is often the last in the buffer, after which a large amount of free space occurs.
[5d300ba]1434Now, repeated appends (reading) can occur without copying previously accumulated characters.
[8f250e0]1435However, 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.
[bbf6a180]1436
1437
[411aa65]1438\subsection{RAII Limitations}
[f85de47]1439\label{string-raii-limit}
1440
1441Earlier work on \CFA~\cite[ch.~2]{Schluntz17} implemented object constructors and destructors for all types (basic and user defined).
[411aa65]1442A constructor is a user-defined function run implicitly \emph{after} an object's storage is allocated, and a destructor is a user-defined function run \emph{before} an object's storage is deallocated.
[f85de47]1443This feature, called Resource Acquisition Is Initialization (RAII)~\cite[p.~389]{Stroustrup94}, helps guarantee invariants for users before accessing an object and for the programming environment after an object terminates.
1444
1445The purposes of these invariants goes beyond ensuring authentic values inside an object.
1446Invariants can also track data about managed objects in other data structures.
1447Reference counting is an example application of an invariant outside of the immediate data value.
1448With a reference-counting smart-pointer, the constructor and destructor \emph{of a pointer type} track the lifecycle of the object it points to.
1449Both \CC and \CFA RAII systems are powerful enough to achieve reference counting.
1450
[5d300ba]1451In general, a lifecycle function has access to an object by location, \ie constructors and destructors receive a parameter providing an object's memory address.\footnote{
1452Historically in \CC, the type of \lstinline[language=C++]{this} is a pointer versus a reference;
1453in \CFA, the first parameter of a constructor or destructor must be a reference.}
[f85de47]1454\begin{cfa}
1455struct S { int * ip; };
1456void ?{}( S & @this@ ) { this.ip = new(); } $\C[3in]{// default constructor}$
[5d300ba]1457void ?{}( S & @this@, int i ) { this{}; *this.ip = i; } $\C{// initializing constructor}$
1458void ?{}( S & @this@, S s ) { this{ *s.ip }; } $\C{// copy constructor}$
[f85de47]1459void ^?{}( S & @this@ ) { delete( this.ip ); } $\C{// destructor}\CRT$
1460\end{cfa}
1461Such basic examples use the @this@ address only to gain access to the values being managed.
[e78d969]1462But lifecycle logic can use the \emph{address}, too, \eg add the @this@ object to a collection at creation and remove it at destruction.
[f85de47]1463\begin{cfa}
[e78d969]1464struct Thread { $\C[3in]{// list node}$
[f85de47]1465 // private
[e78d969]1466 inline dlink( Thread );
[f85de47]1467};
[e78d969]1468static dlist( Thread ) @ready_queue@;
1469void ?{}( Thread & this ) { /* initialize thread */ insert_last( ready_queue, @this@ ); }
1470void ^?{}( Thread & this ) { remove( @this@ ); /* de-initialize thread */ } $\C{// implicitly use ready\_queue}\CRT$
1471\end{cfa}
1472A module providing the @Thread@ (node) type can traverse @ready_queue@ to manipulate the objects.
1473Hence, declaring a @Thread@ not only ensures that it begins with an initially ``good'' value, but it also provides an implicit subscription to a service (scheduler) that keeps the value ``good'' during its lifetime.
[f85de47]1474Again, both \CFA and \CC support this usage style.
1475
[e78d969]1476A third capability concerns \emph{implicit} copying.
1477For passing and returning by value in function call, the parameters and return results do not require construction because after allocation the argument or return value is copied into the corresponding object.
1478In the parameter direction, the language's function-call arranges copy-constructor calls at the control transfer into the callee.
1479In the return direction, the roles are reversed and the copy-constructor calls happen at the return to the caller.
[5d300ba]1480\CC supports this capability. % without qualification.
[411aa65]1481\CFA offers limited support;
[5d300ba]1482simple examples work, but implicit copying does not combine successfully with other RAII capabilities.
[411aa65]1483
1484\CC also offers move constructors and return-value optimization~\cite{RVO20}.
[e78d969]1485These features help reduce unhelpful copy-constructor calls, which, for types like the initial @S@ example, lead to extra memory allocations.
1486\CFA does not currently have these features;
1487adding similarly-intended features to \CFA is desirable.
[411aa65]1488However, this section is about a problem in the realization of features that \CFA already supports.
[e78d969]1489Hence, the comparison continues with the classic version of \CC that treats such copy-constructor calls as necessary.
[f85de47]1490
1491To summarize the unsupported combinations, the relevant features are:
1492\begin{enumerate}
1493\item
[5d300ba]1494\label{p:feature1}
[f85de47]1495 Object provider implements lifecycle functions to manage a resource outside of the object.
1496\item
[5d300ba]1497\label{p:feature2}
1498 Object provider implements lifecycle functions to store references to the object, often originating from outside of it.
[f85de47]1499\item
[5d300ba]1500\label{p:feature3}
[f85de47]1501 Object user expects to pass (in either direction) an object by value for function calls.
1502\end{enumerate}
[5d300ba]1503\CC supports all three simultaneously.
1504\CFA does not currently support \ref{p:feature2} and \ref{p:feature3} on the same object, though \ref{p:feature1} works along with either one of \ref{p:feature2} or \ref{p:feature3}.
1505\CFA needs to be fixed to support all three simultaneously.
[f85de47]1506
[5d300ba]1507The reason \CFA does not support \ref{p:feature2} with \ref{p:feature3} is a holdover from how \CFA lowered function calls to C, before \CFA got references and RAII.
1508At that time, adhering to a principal of minimal intervention, this code was treated as a passthrough:
[f85de47]1509\begin{cfa}
[411aa65]1510struct U { ... };
[e78d969]1511// add RAII ctor/dtor function
1512void f( U u ) { ... /* access fields of u */ ... }
[f85de47]1513U x;
1514f( x );
1515\end{cfa}
[e78d969]1516However, adding RAII constructors/destructor changes things.
[5d300ba]1517
1518\VRef[Figure]{f:CodeLoweringRAII} shows the common \CC lowering~\cite[Sec. 3.1.2.3]{cxx:raii-abi} (right) proceeds differently than the present \CFA lowering (left).
1519The current \CFA scheme is still using a by-value C call.
1520C does a @memcpy@ on structures passed by value.
[e78d969]1521And so, the body of @f@ sees the bits of @__u_for_f@ occurring at an address that has never been presented to the @U@ lifecycle functions.
1522If @U@ is trying to have a style-\ref{p:feature2} invariant, it shows up broken in @f@: references supposedly to @u@ are actually to @__u_for_f@.
[5d300ba]1523The \CC scheme does not have this problem because it constructs the @u@ copy in the correct location within @f@.
1524Yet, the current \CFA scheme is sufficient to deliver style-\ref{p:feature1} invariants (in this style-\ref{p:feature3} use case) because this scheme still does the correct number of lifecycle calls, using correct values, at correct times.
1525So, reference-counting or simple ownership applications get their invariants respected under call/return-by-value.
1526
1527\begin{figure}
1528\centering
[411aa65]1529\begin{tabular}{@{}l|l@{}}
[5d300ba]1530\multicolumn{1}{@{}c|}{\CFA today} & \multicolumn{1}{c@{}}{\CC, \CFA future} \\
[f85de47]1531\begin{cfa}
1532struct U {...};
1533// RAII elided
[5d300ba]1534void f( U u ) {
1535
[e78d969]1536 ... /* access fields of u */ ...
[5d300ba]1537
[f85de47]1538}
1539U x; // call default ctor
[5d300ba]1540{
1541 @U __u_for_f = x;@ // call copy ctor
1542 f( __u_for_f );
1543 // call dtor, __u_for_f
1544}
[f85de47]1545// call dtor, x
1546\end{cfa}
1547&
1548\begin{cfa}
1549struct U {...};
1550// RAII elided
[5d300ba]1551void f( U * __u_orig ) {
1552 @U u = * __u_orig;@ // call copy ctor
[e78d969]1553 ... /* access fields of u */ ...
[5d300ba]1554 // call dtor, u
[f85de47]1555}
1556U x; // call default ctor
[5d300ba]1557
1558
1559f( &x ) ;
1560
1561
[f85de47]1562// call dtor, x
1563\end{cfa}
1564\end{tabular}
1565
[5d300ba]1566\caption{Code Lowering for RAII}
1567\label{f:CodeLoweringRAII}
1568\end{figure}
[f85de47]1569
1570% [Mike is not currently seeing how distinguishing initialization from assignment is relevant]
1571% as opposed to assignment where sender and receiver are in the same stack frame.
1572% What 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.
1573% To provide this knowledge, languages differentiate between initialization and assignment to a left-hand side.
1574% \begin{cfa}
1575% Obj obj2 = obj1; $\C[1.5in]{// initialization, obj2 is initialized}$
1576% obj2 = obj1; $\C{// assignment, obj2 must be initialized for management to work}\CRT$
1577% \end{cfa}
1578% Initialization occurs at declaration by value, parameter by argument, return temporary by function call.
1579% Hence, it is necessary to have two kinds of constructors: by value or object.
1580% \begin{cfa}
1581% Obj obj1{ 1, 2, 3 }; $\C[1.5in]{// by value, management is initialized}$
1582% Obj obj2 = obj1; $\C{// by obj, management is updated}\CRT$
1583% \end{cfa}
1584% When no object management is required, initialization copies the right-hand value.
1585% Hence, the calling convention remains uniform, where the unmanaged case uses @memcpy@ as the initialization constructor and managed uses the specified initialization constructor.
1586
1587% [Mike is currently seeing RVO as orthogonal, addressed with the footnote]
1588% to a temporary.
1589% For example, in \CC:
1590% \begin{c++}
1591% struct S {...};
1592% S identity( S s ) { return s; }
1593% S s;
1594% s = identity( s ); // S temp = identity( s ); s = temp;
1595% \end{c++}
1596% the generated code for the function call created a temporary with initialization from the function call, and then assigns the temporary to the object.
1597% This two step approach means extra storage for the temporary and two copies to get the result into the object variable.
1598% \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''.
1599% \CFA uses C semantics for function return giving direct value-assignment, which eliminates unnecessary code, but skips an essential feature needed by lifetime management.
1600% The following discusses the consequences of this semantics with respect to lifetime management of \CFA strings.
1601
[5d300ba]1602The string API offers style \ref{p:feature3}'s pass-by-value, \eg in the return of @"a" + "b"@.
1603Its implementation uses the style-\ref{p:feature2} invariant of the string handles being linked to each other, helping to achieve high performance.
[411aa65]1604Since these two RAII styles cannot coexist, a workaround splits the API into two layers: one that provides pass-by-value, built upon the other with inter-linked handles.
[f85de47]1605The layer with pass-by-value incurs a performance penalty, while the layer without delivers the desired runtime performance.
[411aa65]1606The slower, friendlier High Level API (HL, type @string@) wraps the faster, more primitive Low Level API (LL, type @string_res@, abbreviating ``resource'').
[f85de47]1607Both APIs present the same features, up to return-by-value operations being unavailable in LL and implemented via the workaround in HL.
1608The intention is for most future code to target HL.
[5d300ba]1609When the RAII issue is fixed, the full HL feature set is achievable using the LL-style lifetime management.
1610Then, HL can be removed, LL's type renamed to @string@, and programs generated with the current HL will run faster.
[f85de47]1611In the meantime, performance-critical sections of applications must use LL.
1612Subsequent performance experiments \see{\VRef{s:PerformanceAssessment}} use the LL API when comparing \CFA to other languages.
1613This measurement gives a fair estimate of the goal state for \CFA.
1614A separate measure of the HL overhead is also included.
[5d300ba]1615Hence, \VRef[Section]{string-general-impl} is describing the goal state for \CFA.
1616In present state, the internal type @string_res@ replaces @string@ for an inter-linked handle.
[f85de47]1617
[411aa65]1618To use LL, a programmer rewrites invocations using pass-by-value APIs into invocations where resourcing is more explicit.
1619Many invocations are unaffected, notably assignment and comparison.
[5d300ba]1620\VRef[Figure]{f:HL_LL_Lowering} shows, of the capabilities listed in \VRef[Figure]{f:StrApiCompare}, only three cases need revisions.
1621The actual HL workaround wraps @string@ as a pointer to a uniquely owned, heap-allocated @string_res@.
1622This arrangement has @string@ using style-\ref{p:feature1} RAII, which is compatible with pass-by-value.
1623
1624\begin{figure}
1625\centering
1626\begin{tabular}{@{}ll@{}}
[f85de47]1627HL & LL \\
1628\hline
1629\begin{cfa}
[411aa65]1630
[f85de47]1631string s = "a" + "b";
1632\end{cfa}
1633&
1634\begin{cfa}
1635string_res sr = "a";
1636sr += b;
1637\end{cfa}
1638\\
1639\hline
1640\begin{cfa}
1641string s = "abcde";
[5d300ba]1642string s2 = s(2, 3); // s2 == "cde"
[411aa65]1643
[5d300ba]1644s(2,3) = "x"; // s == "abx" && s2 == "cde"
[f85de47]1645\end{cfa}
1646&
1647\begin{cfa}
1648string_res sr = "abcde";
[5d300ba]1649string_res sr2 = {sr, 2, 3}; // sr2 == "cde"
[f85de47]1650string_res sr_mid = { sr, 2, 3, SHARE };
[5d300ba]1651sr_mid = "x"; // sr == "abx" && sr2 == "cde"
[f85de47]1652\end{cfa}
1653\\
1654\hline
1655\begin{cfa}
1656string s = "abcde";
[411aa65]1657
[5d300ba]1658s[2] = "xxx"; // s == "abxxxde"
[f85de47]1659\end{cfa}
1660&
1661\begin{cfa}
1662string_res sr = "abcde";
1663string_res sr_mid = { sr, 2, 1, SHARE };
[5d300ba]1664mid = "xxx"; // sr == "abxxxde"
[f85de47]1665\end{cfa}
1666\end{tabular}
[5d300ba]1667
1668\caption{HL to LL Lowering}
1669\label{f:HL_LL_Lowering}
1670\end{figure}
[f85de47]1671
1672
[411aa65]1673\subsection{Sharing Implementation}
[f85de47]1674\label{sharing-impl}
[bbf6a180]1675
[411aa65]1676The \CFA string module has two mechanisms to deal with string handles sharing text.
[f85de47]1677In the first type of sharing, the user requests that both string handles be views of the same logical, modifiable string.
[8f250e0]1678This state is typically produced by the substring operation.
1679\begin{cfa}
1680string s = "abcde";
1681string s1 = s( 1, 2 )@`share@; $\C[2.25in]{// explicit sharing}$
1682s[1] = 'x'; $\C{// change s and s1}\CRT$
1683sout | s | s1;
1684$\texttt{\small axcde xc}$
1685\end{cfa}
[411aa65]1686Here, the source string-handle is referencing an entire string, and the resulting, newly made, string handle is referencing a contained portion of the original.
1687In this state, a modification made in the overlapping area is visible in both strings.
[bbf6a180]1688
[8f250e0]1689The 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.
1690This state is typically produced by constructing a new string, using an original string as its initialization source.
1691\begin{cfa}
1692string s = "abcde";
1693string s1 = s( 1, 2 )@@; $\C[2.25in]{// no sharing}$
1694s[1] = 'x'; $\C{// copy-on-write s1}\CRT$
1695sout | s | s1;
1696$\texttt{\small axcde bc}$
1697\end{cfa}
1698In 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.
[bbf6a180]1699
[411aa65]1700A further abstraction helps distinguish the two senses of sharing.
[e78d969]1701A 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, when the ``share'' option is given.
[8f250e0]1702The SES is represented by a second linked list among the handles.
[e78d969]1703A string with no sharing is in a SES by itself.
1704Inside a SES, a modification of one substring portion may change the value in another substring portion, depending on whether the two overlap.
1705Conversely, no value change can flow outside of a SES.
1706Even if a modification on one string handle does not reveal itself \emph{logically} to another 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.
[bbf6a180]1707
1708
[411aa65]1709\subsection{Controlling Implicit Sharing}
[b0296dba]1710\label{s:ControllingImplicitSharing}
[62b5940]1711
[b0296dba]1712There are tradeoffs associated with sharing and its implicit copy-on-write mechanism.
1713Several qualitative matters are detailed in \VRef{s:PerformanceAssessment}.
1714In detail, string sharing has inter-linked string handles, so managing one string is also managing the neighbouring strings, and from there, a data structure of the ``set of all strings.''
1715Therefore, it is useful to toggle this capability on or off when it is not providing any application benefit.
[62b5940]1716
[b0296dba]1717The \CFA string library provides the type @string_sharectx@ to control an ambient sharing context.
1718It allows two adjustments: to opt out of sharing entirely or to begin sharing within a private context.
1719Running with sharing disabled can be thought of as a \CC STL-emulation mode, where each string is dynamically allocated.
1720The chosen mode applies for the duration of the lifetime of the created @string_sharectx@ object, up to being suspended by child lifetimes of different contexts.
1721\VRef[Figure]{fig:string-sharectx} illustrates this behaviour by showing the stack frames of a program in execution.
1722In this example, the single-letter functions are called in alphabetic order.
1723The functions @a@, @b@ and @g@ share string character ranges with each other, because they occupy a common sharing-enabled context.
1724The 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).
1725The functions @c@, @d@ and @f@ never share anything, because they are in a sharing-disabled context.
1726Executing the example does not produce an interesting outcome, but the comments in the picture indicate when the logical copy operation runs with
[e78d969]1727\begin{description}[itemsep=0pt,parsep=0pt]
[411aa65]1728 \item[share:] the copy being deferred, as described through the rest of this section (fast), or
1729 \item[copy:] the copy performed eagerly (slow).
[b0296dba]1730\end{description}
1731Only eager copies can cross @string_sharectx@ boundaries.
1732The 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.
[bbf6a180]1733
[411aa65]1734\begin{figure}
1735 \begin{tabular}{ll}
1736 \lstinputlisting[language=CFA, firstline=10, lastline=55]{sharectx.run.cfa}
1737 &
1738 \raisebox{-0.17\totalheight}{\includegraphics{string-sharectx.pdf}} % lower
1739 \end{tabular}
1740 \caption{Controlling copying vs sharing of strings using \lstinline{string_sharectx}.}
1741 \label{fig:string-sharectx}
1742\end{figure}
[bbf6a180]1743
1744
[411aa65]1745\subsection{Sharing and Threading}
[b0296dba]1746
1747The \CFA string library provides no thread safety, the same as \CC string, providing similar performance goals.
1748Threads can create their own string buffers and avoid passing these strings to other threads, or require that shared strings be immutable, as concurrent reading is safe.
1749A positive consequence of this approach is that independent threads can use the sharing buffer without locking overhead.
1750When string sharing amongst threads is required, program-wide string-management can toggled to non-sharing using @malloc@/@free@, where the storage allocator is assumed to be thread-safe.
1751Finally, concurrent users of string objects can provide their own mutual exclusion.
1752
1753
[411aa65]1754\subsection{Short-String Optimization}
[bbf6a180]1755
[411aa65]1756\CC implements a short-string ($\le$16) optimization (SSO).
1757As a string header contains a pointer to the string text, this pointer can be tagged and used to contain a short string, removing a dynamic memory allocation/deallocation.
1758\begin{c++}
1759class string {
1760 union {
1761 struct { $\C{// long string, text in heap}$
1762 size_t size;
1763 char * strptr;
1764 } lstr;
1765 char sstr[sizeof(lstr)]; $\C{// short string <16 characters, text in situ}$
1766 };
1767 // some tagging for short or long strings
1768};
1769\end{c++}
1770However, there is now a conditional check required on the fast-path to switch between short and long string operations.
[bbf6a180]1771
[e78d969]1772It might be possible to pack 16- or 32-bit Unicode characters within the same string buffer as for 8-bit characters.
[b0296dba]1773Again, locations for identification flags must be found and checked along the fast path to select the correct actions.
[5d300ba]1774Handling utf8 (variable length) is more problematic because simple pointer arithmetic cannot be used to stride through the variable-length characters.
[b0296dba]1775Trying to use a secondary array of fixed-sized pointers/offsets to the characters is possible, but raises the question of storage management for the utf8 characters themselves.
[bbf6a180]1776
1777
[411aa65]1778\section{Performance Assessment}
[1b39705]1779\label{s:PerformanceAssessment}
[bbf6a180]1780
[e78d969]1781I assessed the \CFA string library's speed and memory usage against strings in the \CC STL.
1782Overall, this analysis shows common features in both APIs comes at no substantial cost in performance.
[5d300ba]1783Moreover, the comparison shows that \CFA's high-level string features simplify text processing because the STL requires users to think more about memory management.
[f85de47]1784When the user does, and is successful, STL's performance can be very good.
[5d300ba]1785But if a user does understand the consequences of the STL representation, performance becomes poor.
[411aa65]1786The \CFA string lets the user work at the level of just putting the right text into the right variables, with corresponding performance degradations reduced or eliminated.
[bbf6a180]1787
[f85de47]1788% The final test shows the overall win of the \CFA text-sharing mechanism.
1789% It exercises several operations together, showing \CFA enabling clean user code to achieve performance that STL requires less-clean user code to achieve.
[bbf6a180]1790
[8f250e0]1791
1792\subsection{Methodology}
1793
[f85de47]1794These tests use a \emph{corpus} of strings.
[e78d969]1795The lengths are important not the contents.
[5d300ba]1796In a result graph, a corpus's mean string-length is often the independent variable on the x-axis.
[e0350e0]1797When a corpus contains strings of different lengths, the lengths are drawn from a lognormal distribution.
[411aa65]1798Therefore, strings much longer than the mean occur less often and strings slightly shorter than the mean occur most often.
[f85de47]1799A corpus's string sizes are one of:
[bbf6a180]1800\begin{description}
[fc8ec54]1801 \item [Fixed-size] all string lengths are of the stated size.
[e0350e0]1802 \item [Varying 1 and up] the string lengths are drawn from the lognormal distribution with a stated mean and all lengths occur.
1803 \item [Varying 16 and up] string lengths are drawn from the lognormal distribution with the stated mean, but only lengths 16 and above occur; thus, the stated mean is above 16.
[bbf6a180]1804\end{description}
[411aa65]1805The special treatment of length 16 deals with the SSO in STL @string@, currently not implemented in \CFA.
[f85de47]1806A fixed-size or from-16 distribution ensures that \CC's extra-optimized cases are isolated within, or removed from, the comparison.
[e78d969]1807In all experiments that use a corpus, text is generated and loaded before the timing phase begins.
[f85de47]1808To ensure comparable results, a common memory allocator is used for \CFA and \CC.
[411aa65]1809\CFA runs the llheap allocator~\cite{Zulfiqar22}, which is also plugged into \CC.
[5d300ba]1810The llheap allocator is significantly better than the standard @glibc@ allocator.
[f85de47]1811
[e78d969]1812The operations being measured take only dozens of nanoseconds, so a succession of many invocations is run and timed as a group.
[411aa65]1813The experiments run for a fixed duration (5 seconds), as determined by re-checking @clock()@ every 10,000 invocations, which is never more often than once per 80 ms.
1814Timing outcomes report mean nanoseconds per invocation, which includes harness overhead and the targeted string API execution.
[bbf6a180]1815
[411aa65]1816As discussed in \VRef[Section]{string-raii-limit}, general performance comparisons are made using \CFA's faster, low-level string API, named @string_res@.
1817\VRef{s:ControllingImplicitSharing} presents an operational mode where \CFA string sharing is turned off.
1818In this mode, the \CFA string operates similarly to \CC's, by using a heap allocation for string text.
1819Some experiments include measurements in this mode for baselining purposes, called ``\CC emulation mode'' or ``nosharing''.
[8136df1]1820See~\VRef{s:ExperimentalEnvironment} for a description of the hardware environment.
1821
[bbf6a180]1822
[8f250e0]1823\subsection{Test: Append}
[bbf6a180]1824
[411aa65]1825These tests measure the speed of appending strings from the corpus onto a larger, growing string.
1826They show \CFA performing comparably to \CC overall, though with penalties for simple API misuses.
[f85de47]1827The basic harness is:
1828\begin{cfa}
[411aa65]1829// set alarm duration
1830for ( ... ) { $\C[1.5in]{// loop for duration}$
[e78d969]1831 // reset accum to empty
[411aa65]1832 for ( i; N ) { $\C{// perform multiple appends (concatenations)}$
1833 accum += corpus[ f( i ) ];
[f85de47]1834 }
[411aa65]1835 count += N; $\C{// count number of appends}\CRT$
[f85de47]1836}
1837\end{cfa}
[411aa65]1838The harness's outer loop executes for the experiment duration.
[e78d969]1839The string is reset to empty before appending (see below).
[411aa65]1840The inner loop builds up a growing-length string with successive appends.
1841Each run targets a specific (mean) corpus string length and produces one data point on the result graph.
[f85de47]1842Three specific comparisons are made with this harness.
1843Each picks its own independent-variable basis of comparison.
[e78d969]1844All three comparisons use the varying-from-1 corpus construction, \ie they allow the STL to show its SSO advantage.
[f85de47]1845
1846
1847\subsubsection{Fresh vs Reuse in \CC, Emulation Baseline}
1848
[e78d969]1849The first experiment compares \CFA with \CC in nosharing mode using @malloc@/@free@.
[411aa65]1850% This experiment establishes a baseline for other experiments.
1851This experiment also introduces the first \CC coding pitfall, which the next experiment shows is helped by turning on \CFA sharing.
1852% This pitfall shows, a \CC programmer must pay attention to string variable reuse.
[e78d969]1853In the following, both programs start with @accum@ empty and build it up by appending @N@ strings (type @string@ in \CC and the faster @string_res@ in \CFA).
[8f250e0]1854\begin{cquote}
[411aa65]1855\setlength{\tabcolsep}{40pt}
[8f250e0]1856\begin{tabular}{@{}ll@{}}
[fc8ec54]1857% \multicolumn{1}{c}{\textbf{fresh}} & \multicolumn{1}{c}{\textbf{reuse}} \\
[8f250e0]1858\begin{cfa}
[bbf6a180]1859
[8f250e0]1860for ( ... ) {
[411aa65]1861 @string_res accum;@ $\C[1.5in]{// fresh}$
1862 for ( N )
1863 accum @+=@ ... $\C{// append}\CRT$
[8f250e0]1864}
1865\end{cfa}
1866&
1867\begin{cfa}
[f85de47]1868string_res accum;
[8f250e0]1869for ( ... ) {
[411aa65]1870 @accum = "";@ $\C[1.5in]{// reuse}$
1871 for ( N )
1872 accum @+=@ ... $\C{// append}\CRT$
[8f250e0]1873}
1874\end{cfa}
1875\end{tabular}
1876\end{cquote}
[411aa65]1877The difference is creating a new or reusing an existing string variable.
1878The pitfall is that most programmers do not consider this difference.
1879However, creating a new variable implies deallocating the previous string storage and allocating new empty storage.
1880As the string grows, further deallocations/allocations are required to release the previous and extend the current string storage.
1881So, the fresh version is constantly restarting with zero string storage, while the reuse version benefits from having its prior large storage from the last append sequence.
[8f250e0]1882
[bbf6a180]1883\begin{figure}
[a7d93cb]1884\centering
[f85de47]1885 \includegraphics{plot-string-peq-cppemu.pdf}
[a7d93cb]1886% \includegraphics[width=\textwidth]{string-graph-peq-cppemu.png}
[411aa65]1887 \caption{Fresh vs Reuse in \CC, Emulation Baseline.
1888 Average time per iteration with one \lstinline{x += y} invocation (lower is better).
1889 Comparing \CFA's STL emulation mode with STL implementations, and comparing the fresh with reused reset styles.}
[8f250e0]1890 \label{fig:string-graph-peq-cppemu}
[411aa65]1891 \bigskip
1892 \bigskip
[2410424]1893 \includegraphics{plot-string-peq-sharing.pdf}
[411aa65]1894% \includegraphics[width=\textwidth]{string-graph-peq-sharing.png}
1895 \caption{\CFA Compromise for Fresh \vs Reuse.
1896 Average time per iteration with one \lstinline{x += y} invocation (lower is better).
1897 Comparing \CFA's sharing mode with STL, and comparing the fresh with reused reset styles.
1898 The \CC results are repeated from \VRef[Figure]{fig:string-graph-peq-cppemu}.}
1899 \label{fig:string-graph-peq-sharing}
[bbf6a180]1900\end{figure}
1901
[f85de47]1902\VRef[Figure]{fig:string-graph-peq-cppemu} shows the resulting performance.
[e78d969]1903The two fresh (solid lines) and the two reuse (dash lines) are identical, except for lengths $\le$10, where the \CC SSO has a 40\% average and minimally 24\% advantage.
[411aa65]1904The gap between the fresh and reuse lines is the removal of the dynamic memory allocates and reuse of prior storage, \eg 100M allocations for fresh \vs 100 allocations for reuse across all experiments.
1905While allocation reduction is huge, data copying dominates the cost, so the lines are still reasonably close together.
[f85de47]1906
1907
[411aa65]1908\subsubsection{\CFA's Sharing Mode}
[f85de47]1909
[411aa65]1910This comparison is the same as the last one, except the \CFA implementation is using sharing mode.
1911Hence, both \CFA's fresh and reuse versions have no memory allocations, and as before, only for reuse does \CC have no memory allocations.
1912\VRef[Figure]{fig:string-graph-peq-sharing} shows the resulting performance.
1913For fresh at append lengths 5 and above, \CFA is now closer to the \CC reuse performance, because of removing the dynamic allocations.
1914However, for reuse, \CFA has slowed down slightly, to performance matching the new fresh version, as the two versions are now implemented virtually the same.
1915The reason for the \CFA reuse slow-down is the overhead of managing the sharing scheme (primarily maintaining the list of handles), without gaining any benefit.
[f85de47]1916
[411aa65]1917\begin{comment}
1918FIND A HOME!!!
1919The potential benefits of the sharing scheme do not give \CFA an edge over \CC when appending onto a reused string, though the first one helps \CFA win at going onto a fresh string. These abilities are:
1920\begin{itemize}
1921\item
1922To grow a text allocation repeatedly without copying it elsewhere.
1923This ability is enabled by \CFA's most-recently modified string being located immediately before the text buffer's \emph{shared} bump-pointer area, \ie often a very large greenfield, relative to the \emph{individual} string being grown.
1924With \CC-reuse, this benefit is already reaped by the user's reuse of a pre-stretched allocation.
1925Yet \CC-fresh pays the higher cost because its room to grow for free is at most a constant times the original string's length.
1926\item
1927To share an individual text allocation across multiple related strings.
1928This ability is not applicable to appending with @+=@.
1929It in play in [xref sub-experiment pta] and [xref experiment pbv].
1930\item
1931To share a text arena across unrelated strings, sourcing disparate allocations from a common place.
1932That is, always allocating from a bump pointer, and never maintaining free lists.
1933This ability is not relevant to running any append scenario on \CFA with sharing, because appending modifies an existing allocation and is not driving several allocations.
1934This ability is assessed in [xref experiment allocn].
1935\end{itemize}
1936This cost, of slowing down append-with-reuse, is \CFA paying the piper for other scenarios done well.
1937\CFA prioritizes the fresh use case because it is more natural.
1938The \emph{user-invoked} reuse scheme is an unnatural programming act because it deliberately misuses lexical scope: a variable (@accum@) gets its lifetime extended beyond the scope in which it is used.
[bbf6a180]1939
[411aa65]1940A \CFA user needing the best performance on an append scenario can still access the \CC-like speed by invoking noshare.
1941This (indirect) resource management is memory-safe, as compared to that required in \CC to use @string&@, where knowledge of another string's lifetime comes into play.
1942This abstraction opt-out is also different from invoking the LL API-level option.
1943In fact, these considerations are orthogonal.
1944But the key difference is that invoking the LL API would be a temporary measure, to use a workaround of a known \CFA language issue; choosing to exempt a string from sharing is a permanent act of program tuning.
1945Beyond these comparisons, opting for noshare actually provides program ``eye candy,'' indicating that under-the-hood thinking is becoming relevant here.
1946\end{comment}
[f85de47]1947
1948
[411aa65]1949\subsubsection{Misusing Concatenation}
[f85de47]1950
[411aa65]1951A further pitfall occurs writing the apparently equivalent @x = x + y@ \vs @x += y@.
[f85de47]1952For numeric types, the generated code is equivalent, giving identical performance.
1953However, for string types there can be a significant difference.
[411aa65]1954This pitfall is a particularly likely for beginners.
[f85de47]1955
1956In earlier experiments, the choice of \CFA API among HL and LL had no impact on the functionality being tested.
1957Here, however, the @+@ operation, which returns its result by value, is only available in HL.
[5d300ba]1958The \CFA @+=@ is obtained by inlining the HL implementation of @+@, which is done using LL's @+=@, into the test harness, while omitting the HL-inherent extra dynamic allocation. The HL-upon-LL @+@ implementation, is:
1959\begin{cquote}
1960\setlength{\tabcolsep}{20pt}
1961\begin{tabular}{@{}ll@{}}
[f85de47]1962\begin{cfa}
[5d300ba]1963struct string { // simple ownership
1964 string_res * inner; // RAII manages malloc/free
[f85de47]1965};
1966void ?+=?( string & s, string s2 ) {
1967 (*s.inner) += (*s2.inner);
1968}
[5d300ba]1969\end{cfa}
1970&
1971\begin{cfa}
[f85de47]1972string @?+?@( string lhs, string rhs ) {
1973 string ret = lhs;
1974 ret @+=@ rhs;
1975 return ret;
1976}
[5d300ba]1977
[f85de47]1978\end{cfa}
[5d300ba]1979\end{tabular}
1980\end{cquote}
[f85de47]1981This @+@ implementation is also the goal implementation of @+@ once the HL/LL workaround is no longer needed. Inlining the induced LL steps into the test harness gives:
1982\begin{cquote}
1983\setlength{\tabcolsep}{20pt}
1984\begin{tabular}{@{}ll@{}}
1985\multicolumn{1}{c}{\textbf{Goal}} & \multicolumn{1}{c}{\textbf{Measured}} \\
1986\begin{cfa}
1987
1988for ( ... ) {
1989 string accum;
1990 for ( ... ) {
1991 accum = @accum + ...@
1992
1993
1994 }
1995}
1996\end{cfa}
1997&
1998\begin{cfa}
1999string_res pta_ll_temp;
2000for ( ... ) {
2001 string_res accum;
2002 for ( ... ) {
2003 pta_ll_temp = @accum@;
2004 pta_ll_temp @+= ...@;
2005 accum = pta_ll_temp;
2006 }
2007}
2008\end{cfa}
2009\end{tabular}
2010\end{cquote}
[411aa65]2011Note, the goal code functions today in HL but with slower performance.
[bbf6a180]2012
2013\begin{figure}
[a7d93cb]2014\centering
[2410424]2015 \includegraphics{plot-string-pta-sharing.pdf}
[a7d93cb]2016% \includegraphics[width=\textwidth]{string-graph-pta-sharing.png}
[411aa65]2017 \caption{CFA's low overhead for misusing concatenation. Average time per iteration with one \lstinline{x += y} invocation (lower is better). Comparing \CFA (having implicit sharing activated) with STL, and comparing the \lstinline{+}-then-\lstinline{=} with the \lstinline{+=} append styles. The \lstinline{+=} results are repeated from \VRef[Figure]{fig:string-graph-peq-sharing}.}
[8f250e0]2018 \label{fig:string-graph-pta-sharing}
[bbf6a180]2019\end{figure}
2020
[411aa65]2021\VRef[Figure]{fig:string-graph-pta-sharing} gives the outcome, where the Y-axis is log scale because of the large differences.
2022The STL's penalty is $8 \times$ while \CFA's is only $2 \times$, averaged across the cases shown here.
[8f250e0]2023Moreover, the STL's gap increases with string size, while \CFA's converges.
[f85de47]2024So again, \CFA helps users who just want to treat strings as values, and not think about the resource management under the covers.
[bbf6a180]2025
2026
[411aa65]2027\subsection{Test: Pass Argument}
[f85de47]2028
2029STL has a penalty for passing a string by value, which forces users to think about memory management when communicating values with a function.
[411aa65]2030The key \CFA value-add is that a user can think of a string simply as a value; this test shows that \CC charges a stiff penalty for thinking this way, while \CFA does not.
[f85de47]2031This test illustrates a main advantage of the \CFA sharing algorithm (in one case).
[411aa65]2032It shows STL's SSO providing a successful mitigation (in the other case).
[f85de47]2033
2034The basic operation considered is:
[b1c220a]2035\begin{cfa}
2036void foo( string s );
2037string s = "abc";
2038foo( s );
2039\end{cfa}
[8f250e0]2040With implicit sharing active, \CFA treats this operation as normal and supported.
[5d300ba]2041Again, an HL-LL difference requires a mockup as LL does not directly support pass-by-value.
[f85de47]2042\begin{cquote}
2043\setlength{\tabcolsep}{20pt}
2044\begin{tabular}{@{}ll@{}}
2045\multicolumn{1}{c}{\textbf{Goal}} & \multicolumn{1}{c}{\textbf{Measured}} \\
2046\begin{cfa}
2047void helper( string q ) {
2048
2049}
[411aa65]2050for ( ... ) { // loop for duration
2051 helper( corpus[ f( i ) ] );
2052 count += 1;
[f85de47]2053}
2054\end{cfa}
2055&
2056\begin{cfa}
2057void helper( string_res & qref ) {
2058 string_res q = { qref, COPY_VALUE };
2059}
2060
2061
2062
2063
2064\end{cfa}
2065\end{tabular}
2066\end{cquote}
[411aa65]2067The goal (HL) version gives the modified test harness, with a single loop.
2068Each iteration uses a corpus item as the argument to the function call.
[5d300ba]2069These corpus items are imported to the string heap before beginning the timed run.
[f85de47]2070
[bbf6a180]2071\begin{figure}
[a7d93cb]2072\centering
[2410424]2073 \includegraphics{plot-string-pbv.pdf}
[a7d93cb]2074% \includegraphics[width=\textwidth]{string-graph-pbv.png}
[2410424]2075 \begin{tabularx}{\linewidth}{>{\centering\arraybackslash}X >{\centering\arraybackslash}X} (a) & (b) \end{tabularx}
[a7d93cb]2076 \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.
[e0350e0]2077(a) With \emph{Varying 1 and up} corpus construction, in which the STL-only benefit of SSO optimization occurs, in varying degrees, at all string sizes.
[2410424]2078(b) With \emph{Fixed-size} corpus construction, in which this benefit applies exactly to strings with length below 16.}
[8f250e0]2079 \label{fig:string-graph-pbv}
[bbf6a180]2080\end{figure}
2081
[a7d93cb]2082\VRef[Figure]{fig:string-graph-pbv} shows the costs for calling a function that receives a string argument by value.
[5d300ba]2083STL's performance worsens uniformly as string length increases, except for short strings due to SSO, while \CFA has the same performance at all sizes.
[f85de47]2084While improved, the \CFA cost to pass a string is still nontrivial.
[8f250e0]2085The contributor is adding and removing the callee's string handle from the global list.
[411aa65]2086This cost is $1.5 \times$ to $2 \times$ over STL's when SSO applies, but is avoidable once \CFA realizes this optimization.
2087At the larger sizes, the STL runs more than $3 \times$ slower, because it has to allocation/deallocate storage for the parameter and copy the argument string to the parameter.
2088If the \CC string is passed by reference, the results are better and flat across string lengths like \CFA.
[bbf6a180]2089
2090
[f85de47]2091\subsection{Test: Allocate}
[bbf6a180]2092
[f85de47]2093This test directly compares the allocation schemes of the \CFA string (sharing active), with the STL string.
2094It treats the \CFA scheme as a form of garbage collection (GC), and the STL scheme as an application of malloc-free.
2095The test shows that \CFA enables faster speed in exchange for greater memory usage.
[bbf6a180]2096
[a17f496]2097A precise garbage-collector, which knows all pointers and may modify them, often runs faster than malloc-free in an amortized analysis, even though it must occasionally stop to collect (latency issues).
2098The speedup happens indirectly because GC is able to move objects.
[411aa65]2099(In the case of the mini-allocator powering the \CFA string library, objects are runs of text.)
[a17f496]2100Moving objects creates a large contiguous store of available memory;
2101fresh allocations consume from this area using light-weight \emph{bump-allocation}.
2102A malloc-free implementation cannot move objects because the location of allocated objects is unknown;
2103hence, free-space management is more complex managing linked structures of freed allocations and possibly coalescing freed blocks.
2104
2105GC occurs lazily, so the lifetime of unreachable allocations is unknown, \eg GC may never be triggered.
2106A garbage collector can minimize the memory footprint for unreachable allocations by collecting eagerly.
2107However, this approach can negate GC's speed advantage.
2108Alternatively, collecting infrequently can consume large amounts of resident and virtual memory.
2109In contrast, a program using malloc-free tries to release an allocation as soon as it is unreachable.
2110This approach reduces the memory footprint (depending on the allocator), but has a per allocation cost for each free.
2111% [TODO: find citations for the above knowledge]
2112Therefore, a speed \vs memory tradeoff is a good comparator for \CFA--STL string allocations.
2113%The test verifies that it is so and quantifies the returns available.
[bbf6a180]2114
[8f250e0]2115These tests manipulate a tuning knob that controls how much extra space to use.
[a17f496]2116Specific values of this knob are not user-visible and are not presented in the results.
[8f250e0]2117Instead, its two effects (amount of space used and time per operation) are shown.
[a17f496]2118The independent variable is the liveness target, which is the fraction of the text buffer in use at the end of a collection.
2119The allocator expands its text buffer during a collection if the actual live fraction exceeds this target.
[bbf6a180]2120
[f85de47]2121\begin{figure}
2122\centering
2123 \includegraphics{string-perf-alloc.pdf}
[e0350e0]2124 \caption{Memory-allocation test's harness and its resulting pattern of memory usage under a bump-pointer-only scheme.}
[f85de47]2125 \label{fig:string-perf-alloc}
2126\end{figure}
2127
2128\VRef[Figure]{fig:string-perf-alloc} shows the memory-allocation pattern that the test harness drives.
2129Note how this pattern creates few long-lived strings and many short-lived strings.
2130To extend this pattern to the scale of a performance test, the harness is actually recursive, to achieve the nesting (three levels in the figure), and iterative, to achieve the fan-out (factor two / binary tree in the figure).
2131\begin{cfa}
2132void f( int depth ) {
2133 if (depth == 0) return;
2134 string_res x = corpus[...]; // importing from char * here
2135 COUNT_ONE_OP_DONE
2136 for ( fanOut ) {
2137 // elided: terminate fixed-duration experiment
2138 f( depth - 1 );
2139 }
2140}
2141START_TIMER
2142f( launchDepth );
2143STOP_TIMER
2144\end{cfa}
2145The runs presented use 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.
[a17f496]2146This sizing keeps an average of 836 strings live and keeps the amounts of consumed memory within the machine's last-level cache.
2147The time measurement is nanoseconds per @f@-call, for internal or leaf strings.
[f85de47]2148
2149% String lifetime (measured in number of subsequent string allocations) is ?? distributed, because each node in the call tree survives as long as its descendent calls.
2150
2151
[bbf6a180]2152\begin{figure}
[a7d93cb]2153\centering
[2410424]2154 \includegraphics{plot-string-allocn.pdf}
[a17f496]2155 \begin{tabularx}{\linewidth}{>{\centering\arraybackslash}X >{\centering\arraybackslash}X} (a) Vertical, Lower is better. \\ Horizontal, leftward is better. & (b) \hspace*{5pt} STL CFA \hspace*{20pt} STL \hspace*{10pt} CFA \hspace*{10pt} \end{tabularx}
2156 \caption{Space and time performance, under varying fraction-live targets, for the five string lengths shown, at \emph{Varying 16 and up} corpus construction.}
[8f250e0]2157 \label{fig:string-graph-allocn}
[bbf6a180]2158\end{figure}
2159
[a17f496]2160\VRef[Figure]{fig:string-graph-allocn} shows the experimental results.
2161In plot (a), there is one labeled curve for each of the five chosen string sizes, 20, 50, 100, 200, 500.
[e0350e0]2162A labeled curve shows the \CFA speed and space combinations achievable by varying the fraction-live tuning parameter.
[a17f496]2163Stepping along the curve from top-left toward bottom-right means the string library has a smaller fraction of its heap remain live after collection, \ie it doubles its size more often.
2164Hence, memory approximately doubles between points.
2165The additional memory means less time is spent collecting, so the curve shows lower (better) times per operation (y-axis).
2166This doubling benefit diminishes, so the curves begin to flatten.
2167
2168The point chosen as \CFA's default liveness threshold (20\%) is marked with a circle.
2169For most string lengths, this point occurs as the doubling benefit becomes subjectively negligible.
[e78d969]2170At len-500 (top line in plot (a)), the amount of space needed to achieve 20\% liveness is so much ($>$~4M) that last-level cache misses begin occurring generating a further slowdown.
[a17f496]2171This effect is an anomaly of the experimental setup trying to compare such varied sizes in one view;
2172the len-500 default point is included only to provide a holistic picture of the STL comparisons (discussed next).
2173
2174Staying within plot (a), each \emph{tradeoff} line (green) connects the default-tuned \CFA performance point with its length-equivalent STL performance point.
2175STL always uses less memory (occurs to the left of) than its \CFA equivalent.
2176Ideally, \CFA results would be faster (STL occurs above it);
2177however, this inversion only occurs for smaller string lengths.
2178That is, the STL-to-\CFA tradeoff line goes down and to the right for small strings, but for longer strings, it goes up and to the right.
[e0350e0]2179
2180Plot (b) offers insight into why larger lengths are not currently showing the anticipated tradeoff, and suggests possible remedies.
2181This plot breaks down the time spent, comparing STL--\CFA tradeoffs, at successful len-50 with unsuccessful len-200.
[a17f496]2182Data are sourced from running the experiment under \emph{perf}, recording samples of the call stack, and imposing a mutually-exclusive classification on these call stacks.
2183Reading a stacked bar from the top down, \emph{text import} captures samples where a routine like @memcpy@ is running, so that the string library is copying characters from the corpus source into its allocated working space.
2184\emph{Malloc-free} means literally one of those routines (taking nontrivial time only for STL), while \emph{gc} is the direct \CFA(-only) equivalent.
2185All of the attributions so far occur while a string is live; further time spent in a string's lifecycle management functions is attributed \emph{ctor-dtor}.
[e0350e0]2186Skipping to the bottom-most attribution, \emph{harness-leaf} represents samples where the youngest live stack frame is simply the recursive @helper@ routine of \VRef[Figure]{fig:string-perf-alloc}.
2187The skipped attribution \emph{Other} has samples that meet none of the criteria above.
2188
2189Beside \CFA's principal bars, a bar for separate-compilation overhead (\emph{sep.\ comp.}) shows the benefit that STL enjoys by using monolithic compilation.
2190\CFA currently forgoes this benefit.
[e78d969]2191This overhead figure is obtained by hacking a version of the \CFA string library to have a header-only implementation and measuring the resulting speed.
[e0350e0]2192The difference between separately compiled (normal) and header-only (hacked) versions is the reported overhead.
[a17f496]2193It represents how much \CFA could speed up if it switched to a header-only implementation to match STL.
[e0350e0]2194The difference jumps noticeably between the different string sizes, but has little correlation with string size ($r=0.3$).
2195Associating this potential improvement with the \emph{other} attribution is a stylistic choice.
2196
2197Analyzing the stacked bars from the bottom up, the first two categories, \emph{harness-leaf} and \emph{other} both give baseline times that should not be matters of STL-\CFA difference.
2198The \emph{harness-leaf} category is called out as a quality check; this attribution is equal across all four setups, ruling out mysterious ``everything is X\% slower'' effects.
2199But with the \emph{other} attribution, STL beats \CFA by an amount that matches the STL's benefit from avoiding separate compilation.
2200In the \emph{ctor-dtor} category, \CFA is spending more time than STL to inter-link its string handles.
2201Comparing STL's \emph{malloc-free} with \CFA's \emph{gc}, \CFA's is considerably shorter, in spite of its copying, because \emph{gc} only spends time on live strings, while \emph{malloc-free} works on all strings.
2202Most of the every-string bookkeeping occurs under STL's \emph{malloc-free} and \CFA's \emph{ctor-dtor}.
2203So the critical comparison is STL's $(\textit{ctor-dtor} + \textit{malloc-free})$ \vs \CFA's $(\textit{ctor-dtor} + \textit{gc})$.
2204Here, \CFA is a clear winner.
[a17f496]2205Moreover, the discussion so far holds for both the successful and unsuccessful string lengths, albeit with the length-correlated duration of \emph{gc} (due to copying) encroaching on \CFA's advantage, though not eliminating it.
[e0350e0]2206The total attributions so far see \CFA ahead, possibly by more than is shown, depending on how one views the separate-compilation difference.
[a17f496]2207Under a \CFA-generous separate-compilation stance, \CFA is equal or ahead in every important category so far.
[e0350e0]2208
2209The remaining attribution, \emph{text-import}, is thus a major reason that \CFA is currently unsuccessful at delivering improved speed with long strings. \PAB{From Mike: need to finish this point.}
[bbf6a180]2210
[f85de47]2211% \subsection{Test: Normalize}
[bbf6a180]2212
[f85de47]2213% This test is more applied than the earlier ones.
2214% It combines the effects of several operations.
2215% It 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.
[bbf6a180]2216
[f85de47]2217% To motivate: edits being rare
[bbf6a180]2218
[f85de47]2219% The program is doing a specialized find-replace operation on a large body of text.
2220% In the program under test, the replacement is just to erase a magic character.
2221% But in the larger software problem represented, the rewrite logic belongs to a module that was originally intended to operate on simple, modest-length strings.
2222% The challenge is to apply this packaged function across chunks taken from the large body.
2223% Using the \CFA string library, the most natural way to write the helper module's function also works well in the adapted context.
2224% Using 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.
[bbf6a180]2225
2226\begin{lstlisting}
2227void processItem( string & item ) {
[8f250e0]2228 // find issues in item and fix them
[bbf6a180]2229}
2230\end{lstlisting}
[e78d969]2231
2232
2233\section{Future Work}
2234
2235\CFA strings provide an interesting set of operations for manipulating sets of characters.
2236However, experience is needed to determine which operations are superfluous and which are missing.
2237It is hard to find a strong string API in other general-purpose programming-languages to compare with, as most have simple string APIs, especially I/O.
2238Supporting regular-expressions, as in text-editors and specialize string languages, \eg AWK~\cite{AWK}, Ruby~\cite{Ruby}, SNOBOL~\cite{SNOBOL}, is an important extension.
2239String sharing also needs experience to determine if the rules for overlapping behaviour provide the right features for complex editing applications.
2240
2241Extending \CFA strings to non-ASCII characters is also a significant upgrade required for non-Latin languages.
2242Working with fixed-size 16 and 32-bit characters should be a straightforward extension from 8-bit characters.
2243Variable-sized UTF-8/16/32 characters require a significant rethink for implementation.
2244For example, high-performance manipulation may require a companion array of pointers to the variable-sized characters.
2245
2246Finally, adding the \CC SSO trick and merging the two-part string-implementation, once the RAII problem in \CFA is fixed, will provide even better performance.
Note: See TracBrowser for help on using the repository browser.