Friday, June 17, 2005

 

Tip 9: Beware of calling MaxLength() on the TPtr returned from HBufC::Des()

There's an interesting side effect to calling Des() on an HBufC to return a modifiable descriptor.

Recall that HBufC derives from TDesC, and thus an HBufC object doesn’t have a maximum length word, because it is non-modifiable and doesn’t need one. But when you call Des() to return a modifiable descriptor, a TPtr which references the descriptor data, the modifiable descriptor must have a maximum length value. So where does it come from?

Well, in fact, the maximum length of the heap cell in which the HBufC was allocated is used to set the maximum length of the returned TPtr. But beware! This may not necessarily be the length specified when the heap descriptor was allocated. There are two reasons why this can occur, the first of which is the most common:

  1. If the length specified wasn't word-aligned (i.e. a multiple of 4 bytes) or
  2. Because the amount of space left over in the free cell from which the heap cell was allocated was insufficent to create any other heap cells, so was left spare. The minimum size required for a heap cell is approximately 12 bytes so, if there are fewer than 12 bytes in the remaining cell, your descriptor will end up with the surplus tacked on the end.

The result in either case is that the maximum length of a TPtr returned from HBufC::Des() may not be what you expect, given the size specified when the heap descriptor was created - it could be longer. For example:

const TInt KMaxBufLength = 9;
HBufC8* buf = HBufC8::NewL(KMaxBufLength);

TPtr8 ptr(buf->Des());

TInt maxLength = ptr.MaxLength(); // maxLength > 9 but may not be 12


Don’t expect that it is simply rounded up to the next word-aligned value. In the example above, the maximum length is guaranteed to be at least three extra bytes but it may be more, depending on the remaining size of the heap cell from which it was allocated.

So, if you cannot guarantee that the value of MaxLength() is what you expect, it's safest to use the integer value used to first allocate the HBufC. If the length of the heap buffer was set to its maximum on creation, you can alternatively use that:

const TInt KMaxBufLength = 9;

HBufC8* buf = HBufC8::NewL(KMaxBufLength);

TPtr8 ptr(buf->Des());

TInt unpredictable = ptr.MaxLength(); // maxLength > 9 but may not be 12

TInt bufLen = buf->Length();

bufLen will be 0, because the buffer was not set to its maximum length on creation.
This is done by calling NewMaxL():

HBufC8* buf = HBufC8::NewMaxL(KMaxBufLength);
TInt bufLen = buf->Length(); // This will be 9


So how does this matter?
Well, you can get caught out. Here's an example I used in my book. It illustrates the use of pointers to manipulate the contents of a descriptor and the use of an assertion statement to catch access beyond the descriptor length:

_LIT(KPanic, "TestPointer");
const TInt KBufferLength = 10;

void TestPointer()

{// Create a buffer with length KBufferLength = 10 bytes

HBufC8* myBuffer = HBufC8::NewMaxL(KBufferLength);

TPtr8 myPtr(myBuffer->Des());

myPtr.Fill('?'); // Fill with '?'


// Byte pointer to descriptor in memory

TUint8* ptr = (TUint8*)myPtr.Ptr();

TInt maxLength = myPtr.MaxLength();


for (TInt index = 0; index < maxLength; index++)

{// This fails at the end of the buffer (index = 10)

// because myPtr.MaxLength() > KBufferLength

__ASSERT_DEBUG(index<
KBufferLength, User::Panic(KPanic, KErrOverflow));
(*ptr) = '!'; // Replace the contents with '!'

++ptr;

}


}

This page is powered by Blogger. Isn't yours?

Google
WWW Top Tips for Descriptors