Full size Banner

Descriptor and Binary Data

In Symbian OS, descriptor is used for representing text and binary data. A charecter can be 8 bits (narrow) and 16 bits (Unicode or wide). For binary data alwasy 8 bits are used. There are three variations of each type. Neutral, 8 bit and 16 bit. Neutral means it can be 16 bit or 8 bit depending on if the UNICODE flag is defined in the SDK. For example, a buffer decriptor can be TBuf (neutral), TBuf8 (8 bit) and TBuf16 (16 bit).

Descriptors are not like C strings; they protect against buffer overrun and dont rely on NULL terminators to determine the length of the string. So NULL can be part of the descriptor. Programmer should take care of memory allocation and cleanup.

Two main reasons why Symbian in introduced descriptors.
  • Symbian descriptor does not rely on NULL termination like C equivalent
  • Binary data and charecter string can be expressed by same API

It is impossible to construct a descriptor with a length which exceeds the buffers capacity (panic will occur). Descriptors are classes with base class as TDesC. All instantiable classes are derived indirectly from TDesC. Different classes are used for different purposes. For example some are not modifiable and some are modifiable. Some can be declared on the stack and some must be constructed by allocating memory on the heap. Following picture shows the class hierarchy.

TDesC class has a member variable iLength (32 bit long) and that can be retrived by Length() method. This Length() methods gets the number of data items. This method is never overriden by any of the derived classes.

First 4 bits of the iLength is actually used to identify the type of derived classes and rest 28 bits are used for actual length.There are currently six derived descriptor classes, each of which sets the identifying bits as appropriate upon construction.

Whenever a descriptor class name contains the trailing C that means the string is constant. If the string is constant it can't be modified, but it may be possible to modify it directly depending on the descriptor. For example TDesC is not modifiable on the other hand TDes is modifiable.

Access to the descriptor data for all descriptors goes through the nonvirtual Ptr() method of the base class, TDesC, which uses a switch statement to check the 4 bits, identify the type of descriptor and return the correct address for the beginning of its data. Following table shows descriptor tyes, where it is instantiated and equivalent C statement.

Before going to individual instantiable classes let's take a closer look to two most important base classes and most widely used descripto macro (_LIT). Those are TDesC and TDes. The trailing C means constant (not modifiable).

TDesC

TDesC is the most parent class in descriptor inheritence hierarchy. This class has many non modifiable methods that can be used to access, compare and so on. These methods does not alter the contents of the descriptor itself. There is two variation of of this class TDesC16 (Unicode, 16 bits) and TDesC8 (non unicode, 8 bits). The class declation can be found in your include folder. Following class declation is taken for 16 bit (short version)

class TDesC16
{
inline TInt Length() const;
IMPORT_C const TUint16 *Ptr() const;
IMPORT_C TInt Compare(const TDesC16 &aDes) const;
IMPORT_C TInt CompareF(const TDesC16 &aDes) const;
IMPORT_C TInt CompareC(const TDesC16 &aDes) const;

IMPORT_C TInt Match(const TDesC16 &aDes) const;
IMPORT_C TInt MatchF(const TDesC16 &aDes) const;
IMPORT_C TInt MatchC(const TDesC16 &aDes) const;
IMPORT_C TInt Locate(TChar aChar) const;
IMPORT_C TInt LocateReverse(TChar aChar) const;
IMPORT_C TInt LocateF(TChar aChar) const;
IMPORT_C TInt LocateReverseF(TChar aChar) const;
IMPORT_C TInt FindF(const TDesC16 &aDes) const;
IMPORT_C HBufC16 *AllocLC() const;
protected:
inline TDesC16() {}
inline TDesC16(TInt aType,TInt aLength);

private:
TUint iLength;

};


As we can see that most of them are non modifiable methods. Also we noticed that there is member variable iLength but there is no member variables for type. The first 4 bits are of the iLength is used for type and rest are used for actual length. The Length() returns iLength. Derive classes use TDesC16(TInt aType,TInt aLength) constructor to construct the TDesC class where type and length are passed to constructor. There are many operations in the class. When you need to use some class you could consult with the SDK help file.
Here is an example how this descriptor can be used.
When we know that a method is not going to modify the descriptor then we can pass it as TDesC. Also we need to pass it by references.

void StringRead(const TDesC& aString)
{
_LIT(KFormat5,"\"%S\"; %d; %d\n");
console->Printf(KFormat5,&aString,aString.Length(),aString.Size());
// Access but does not modify it
}


_LIT

_LIT stands for literal in Symbian OS. A literal string is one which is included in program binaries. A program binary consists of consists of compiled and linked DLLs and EXEs that make up aprogram. If program binary contains any literal that will be read only also.

In C, we can express the literal as follows:
static const char RTextHelloWorld[] = "Hello World"; In Symbian OS, literal string are included in program binary by using TLitC (two version, 8 bits and 16 bits). But in practice, we use _LIT macro to create TLitC type class.
The macro is used in the following way which generates a TLitC type.
_LIT(KHello, "Hello World");
which is expnaded to TLitC by following macro.
#define _LIT8( name,s ) const static TLitC8 < sizeof( s ) > name={sizeof(s)-1,s}
TLitC for 8 bit is declared as follows in the SDK header file.
template
class TLitC8
{
public:
inline const TDesC8* operator&() const;
inline operator const TDesC8&() const;
inline const TDesC8& operator()() const;
TText8 iBuf[__Align8(S)];
};
From the above class description, we can see that we can access the literal by using some operator.

TDes
The class encapsulates the data member containing the maximum length of data represented by an 8 or 16 bits descriptor. It also provides member functions through which the data can be modified. The class adds to the behaviour provided by TDesC (both 16 bits and 8 bits) This class cannot be instantiated as it is intended to form part of a class hierarchy; it provides a well defined part of descriptor behaviour. It can, however, be passed as an argument type for functions which need to both modify and access descriptor data. TDes for 8 bit is declared as follows in the SDK header file (shortcut version).
class TDes8 : public TDesC8
{
public:
inline TDes8& operator=(const TUint8 *aString);
inline TDes8& operator=(const TDesC8 &aDes);
inline TDes8& operator=(const TDes8 &aDes);
inline TInt MaxLength() const;
inline TInt MaxSize() const;
IMPORT_C void AppendNumFixedWidthUC(TUint aVal,TRadix aRadix,TInt aWidth);
IMPORT_C void Format(TRefByValue aFmt,...);
...
protected:
TInt iMaxLength;
};
Here is an example how this descriptor can be used. When we think that some function can modify the content then we can pass it as TDes type.
LOCAL_C void StringWrite(TDes& aString)
{
_LIT(KTxtModStringWrite," modified by StringWrite");
aString.Append(KTxtModStringWrite);
_LIT(KFormat3,"\"%S\"; %d; %d; %d\n");
console->Printf(KFormat3,
&aString,
aString.Length(),
aString.Size(),
aString.MaxLength()
);
}

_LIT(KDataTPtr,"A TPtr descriptor");
TText area[48];
TPtr ptr(&area[0],48); // See later section for TPtr
ptr = KDataTPtr; // "A TPtr descriptor"
StringWrite(ptr);


Now let's take a closer look to individual descriptor classes that can be instantiated, at the end of this page you can find source code examples and power point in pdf format that can help you.

TBuf<n> Stack based descriptor

Tbuf is stack based buffer descriptor, which has nonconstant buffer determined by the integer value passed to the templated class. The passed integer is maximum allowed length of the buffer. It derives indirectly from TDes,which itself derives from TDesC, thus inheriting the full range of descriptor operations in TDes and TDesC. TBuf defines a number of constructors and assignment operators, similar to those offered by its non-modifiable counterpart. As with all descriptors, its memory management is your responsibility and, although the buffer is modifiable, it cannot be extended beyond the initial maximum length set on construction. If the contents of the buffer need to expand, it is up to you to make sure that you either make the original allocation large enough at compile time.

TBuf16 is declared in SDK like this. You can see iBuf member variable is just an array of TUint16 determined by the template parameter S. Length is stored in the base class and 4 bits of the length is used to store the type. This class inherited those private and protected methods from it's base classes.

template <TInt S>
class TBuf16 : public TBufBase16
{
public:
...
inline TBuf16(const TUint16* aString);
inline TBuf16(const TDesC16& aDes);
inline TBuf16<S>& operator=(const TUint16* aString);
inline TBuf16<S>& operator=(const TDesC16& aDes);
inline TBuf16<S>& operator=(const TBuf16<S>& aDes);
protected:
TUint16 iBuf[__Align16(S)];
};

Here is some example how this descriptor can be used.
_LIT(KDataTBuf,"Hello Symbian");
TBuf<40> buf1(KDataTBuf); // Constructed from literal descriptor
TBuf<40> buf2(buf1); // Constructed from constant buffer descriptor
TBuf8<40> buf3((TText8*)"So what?"); // from C string
_LIT(KDataAboutme,"about me");
// Appending new data to existing one, if the total length is more than 40 it will panic
buf3.Append(KDataAboutme);
TBuf<40> buf4; // Constructed empty, length = 0, maximum length = 40
// Illustrate copy and replace
buf4 = buf2; // buf2 copied into buf4, updating length and max length
buf3 = (TText8*)"Murder for a jar of red rum"; // updated from C string
The stack-based buffer descriptors, TBuf<n> and TBufC<n>, are useful for fixed-size or relatively small strings, say up to the length 256. When the length of the string is higher or