Basic:
Descriptors are Symbian OS strings. They are known as 'Descriptors', because they are self describing. Each descriptor object holds the length of the string of data it represents as well as its 'type', which identifies the underlying memory layout of the data it holds. They can also be used to store binary data.
There are two types, 8-bit and 16-bit descriptors. The 8-bit variant is used to store ASCII characters and raw binary data. The smallest data unit in these is a "narrow" 8 bit (1 byte) character. The 16-bit type is used to store Unicode characters; for these, the data unit is a "wide" 16-bit (2 byte) character.8 & 16 bit descripters are differentiated by the following representation TDesC8 or TDesC16.
Descriptors are Symbian OS strings. They are known as 'Descriptors', because they are self describing. Each descriptor object holds the length of the string of data it represents as well as its 'type', which identifies the underlying memory layout of the data it holds. They can also be used to store binary data.
There are two types, 8-bit and 16-bit descriptors. The 8-bit variant is used to store ASCII characters and raw binary data. The smallest data unit in these is a "narrow" 8 bit (1 byte) character. The 16-bit type is used to store Unicode characters; for these, the data unit is a "wide" 16-bit (2 byte) character.8 & 16 bit descripters are differentiated by the following representation TDesC8 or TDesC16.
Different type of Descriptors and their purpose & usage:
Layout of non-modifable descriptors:
First, let’s distinguish between descriptor literals, which are constant and can be built into ROM because their contents is fixed at build time, and non-modifable descriptors, whose contents are constant but not fixed at build time. Symbian OS, literals are treated a bit differently to the other descriptors, and I’ll discuss them separately in What are literal descriptors?.
All (non-literal) descriptors derive from the base class TDesC (typedef’d to TDesC16 in e32std.h and defined in e32des16.h). The ‘C’ at the end of the class name indicates that the descriptor is non-modifiable. TDesC class has methods for determining the length of the descriptor and accessing its data, and implements other descriptor methods which access the descriptor data but do not modify it, such as data access, comparison and search functionality.It is worth pointing out that all non-modifiable descriptor methods are implemented in this generic base class rather than overridden by each non-modifiable descriptor subclass (TBufC, TPtrC and HBufC).
This is for memory efficiency. It’s done this way, rather than by use of virtual functions, because these would require each derived descriptor object to occupy an extra 4 bytes by adding a virtual pointer (vptr) for access to the virtual function table. This memory size overhead is undesirable, particularly for smaller strings, where the 4 bytes becomes a large percentage of the overall size.
And by keeping the descriptor methods in the base classes, the amount of code to implement a full set of string functionality is minimized too. This reuse is a benefit in terms of ROM size, and an illustration of C++ best practice for testing and maintenance. The subclasses only implement specific methods for construction and copy assignment.
The first 4 bytes of every descriptor object is the same: these hold the length of the data it currently contains. Well, actually, only 28 of these 32 bits are used to hold the length of the descriptor data; the top 4 bits are used to indicate the type of descriptor. The use of 4 bits to identify the type limits the number of different types of descriptor to 2^4 (=16), but since only six types have been necessary in all previous releases of Symbian OS (TBufC, TBuf, TPtrC, TPtr, HBufC and RBuf) , it seems unlikely that the range will need to be extended significantly in future. The use of the other 28 bits to store the data length, also means that the maximum number of byes a descriptor may occupy is limited to 2^28 bytes = 256 MB.
The rest of the layout of a descriptor object depends upon the implementation of each of the subclasses. Access to the descriptor data of each type goes through the (nonvirtual) Ptr() method of the TDesC base class, which uses a switch statement to identify the type of descriptor (using the top 4 bits of the beginning of the descriptor object) and return the correct address for the beginning of its data.
Of course, this requires that the TDesC base class has knowledge of the memory layout of its subclasses hardcoded into Ptr(). This means that you can’t create your own descriptor class, deriving from TDesC, and expect it to work.
Layout of modifiable descriptors:
Modifiable descriptors all derive from the base class TDes, which itself is a subclass of TDesC. TDes has an additional 4-byte member variable, which is used to store the maximum length of data currently allocated to the descriptor.
TDes defines a range of methods for modifying string data. Like the non-modifiable descriptors, all the manipulation is inherited by the derived classes, and works regardless of their type. The subclasses only implement specific methods for construction and copy assignment.
Apart from the allocation methods of the heap buffer descriptors, no descriptor functions allocate any memory. So any modification which extends the data of the descriptor, eg Append(), will check that there is sufficient memory available before proceeding. The contents of the descriptor can shrink and expand at will, as long as the length does not exceed the maximum. If the length of the descriptor contents is less than the maximum length, the end of the block of memory allocated to the descriptor is simply unused.
Descriptor code uses assertion statements to ensure that the maximum length of the descriptor is sufficient, before proceeding. The checks are made in both debug and release builds, and panic if an overflow would occur. This avoids the hard-to-trace memory scribbles and buffer overflows typical of C style strings.
0 comments:
Post a Comment