Many systems, components and applications deal with pre-defined, well known string constants. For example, parsing and manipulating text containing structure mostly requires comparisons against standard string constants.
In a complex system, composed of a large number of objects, there may also be a need to pass strings between objects, and to route processing depending on the value of some string. The implementation of the HTTP transport framework and the XML framework are examples within the Symbian platform where such intense string handling is required.
To improve efficiency, the Symbian platform uses the idea of the string pool.
A string pool is referenced through an RStringPool
, which is a handle like object. The following diagram illustrates
the basic idea:
Figure: String pool representation
A string pool is a mechanism for storing strings in a particular way that makes the comparison of strings a very fast operation. It is particularly efficient at handling strings that can be set up at program compile time. For example, strings that identify lexical elements in a structured text. Typically, they are well known strings that are likely to be used very often in a given context.
Such strings are organised into tables, and each string within a table can be referenced by an index value, which can be symbolised by an enum. Such tables are referred to as static string tables (See Static string tables). The basic algorithm used internally ensures that the pool contains only one string of any particular value, and uses a reference counting mechanism to keep track of usage.
The advantages of representing string constants in such a way are:
avoiding a proliferation of duplicate strings throughout a component or an application; typically there is one string pool per thread, and one copy of a string
allowing string constants to be represented by integer values
allowing strings
to be passed between objects by passing integer values, wrapped in
a class (any one of the RString
and RStringF
classes)
allowing strings to be compared by comparing the integer values.
Internally, a string pool uses hash tables to reference strings.
Static string tables and string constants can be added dynamically to the string pool, for example, at run time. However, there is always a performance penalty while adding either a static or a dynamic string to the string pool as the hash tables need to be updated. This means that it is better to add static string tables at string pool initialisation time, as this is the best time to absorb the overhead.
The string pool
as supplied by the Symbian platform supports any strings that can
be represented by a TDes8
descriptor; this includes
ASCII or UTF-8 encoded strings. Note that a TDes8
type can represent any type of data , including binary data, and
means that a string pool can easily handle the extended characters
of another encoding.
Within the string pool, strings are of two types - case sensitive and case insensitive. This affects the way strings are compared. Case insensitivity implies that strings are folded for the purpose of comparison.
A string pool can contain up to 4,096 static string tables, and each table can represent up 26,2144 strings.
Static string tables are defined and built
at compile time. They are represented by a TStringTable
object. A string table can be added to the string pool by passing
the string table reference to a call to:
void RStringTable::OpenL( const TStringTable& aTable );
The following diagram illustrates a general picture. Note that the strings in any given string table are deemed to be either case sensitive or case insensitive, and this governs how comparisons are made.
As the name implies, a static string table is declared as
a const TStringTable
data member of a class
with a user-defined name. The class name is defined in a header file
while the table itself is implemented in a .cpp
C++
source file. Both the header file and the C++ source file are normally
included in the project definition. Typically, a set of enum values
are also defined within the scope of the class, and each value is
associated with the strings in the table; code in other parts of the
program access the strings in the string pool using these enum values.
The Perl script, stringtable.pl
, located in ...\syslibs\bafl\stringtools\
, can be used to generate
these .cpp
and .h
files from a simple
text definition. The text definition file simply lists the strings
and the enum symbols to be associated with them; the file itself is
given a .st
file type.
Following is a simple
example of ExampleStringTable.st
file:
# Example String Table fstringtable ExampleStringTable !// Some types of fruit # This comment won't appear in the .h file, but the one above will. EApple apple EOrange orange EBanana banana # Some animals ECat cat EDog dog
The main points to note are:
the keyword fstringtable is used to define the name of the class that contains
the string table declaration and the enum value symbols. The class
name itself follows the keyword. For example, ExampleStringTable
.
Note that you can include underscore characters in the
class name. For example, Example_StringTable
.
the symbols EApple
and EOrange
form the enum value
symbols that correspond to the strings apple
and orange
respectively.
all statements
starting with a # are comments and are completely ignored.
However # characters can appear in a string. For example ap#ple
is a valid string and is not interpreted as a comment.
all statements starting with a ! are comments that are inserted into the generated header file.
Running the Perl script with ExampleStringTable.st
as source generates the header file ExampleStringTable.h
and the C++ source file ExampleStringTable.cpp
as
illustrated below:
// Autogenerated from epoc32\build\generated\example\ExampleStringTable.st by the stringtable tool - Do not edit #ifndef STRINGTABLE_ExampleStringTable #define STRINGTABLE_ExampleStringTable #include "StringPool.h" struct TStringTable; /** A String table */ class ExampleStringTable { public: enum TStrings { // Some types of fruit /** apple */ EApple, /** orange */ EOrange, /** banana */ EBanana, /** cat */ ECat, /** dog */ EDog }; static const TStringTable Table; }; #endif // STRINGTABLE_ExampleStringTable
// Autogenerated from epoc32\build\generated\example\ExampleStringTable.st by the stringtable tool - Do not edit #include <e32std.h> #include "StringPool.h" #include "StringTableSupport.h" #include "ExampleStringTable.h" #ifdef _DEBUG #undef _DEBUG #endif _STLIT8( K1, "apple" ); _STLIT8( K2, "orange" ); _STLIT8( K3, "banana" ); _STLIT8( K4, "cat" ); _STLIT8( K5, "dog" ); // Intermediate const void * const KStringPointers[] = { ( const void* )&K1, ( const void* )&K2, ( const void* )&K3, ( const void* )&K4, ( const void* )&K5 }; const TStringTable ExampleStringTable::Table = {5, KStringPointers, EFalse};
The table itself is the static data member Table
of class ExampleStringTable
.