![Symbian Developer Library](../../../../../../../a_stock/images/mainheading.gif)
![]() |
![]() |
|
RBuf
A resizable buffer descriptor is an RBuf
type. This
descriptor is a recent addition to the family of descriptors, and like all of
the other descriptor types, it is used to contain strings and binary data. You
can also use this descriptor type to manipulate and change the data using the
functions provided by the iom
base classes (See
Abstract base descriptor classes).
This descriptor is implemented so that its buffer, i.e. the location where the data actually resides, is on the heap. This makes it useful when you do not know the maximum size of the data until run time.
The resizable buffer descriptors are similar to what are called
Heap descriptors, or
HBufC
types, but the API provided by RBuf
is
easier to use. The RBuf
API also makes it easier to change the
maximum size of the buffer containing the data, a task often referred to as
reallocating the buffer.
The general guidelines are: use HBufC
descriptors to
contain data that rarely changes; use RBuf
descriptors to
contain data that changes frequently. However, in practice, RBuf
types are equally suitable for both cases, and you should always
consider using an RBuf
in preference to an
HBufC
.
For cases where your code uses APIs that return an HBufC
type, you can convert this to an RBuf
. In effect,
RBuf
can act as a wrapper around the HBufC
; access
and manipulation of the data is then done through the member functions of
RBuf
and its base classes TDes
and
TDesC
Some key points about resizable buffer descriptors:
For text data, it is usual to construct an RBuf
type and
allow the appropriate variant, either a RBuf8
or a
RBuf16
to be selected at build time.
For binary data, an explicit RBuf8
is used.
It is rare to use an explicit RBuf16
.
Data can be changed through RBuf
as well as replaced
using its assignment operators. Like all descriptors, RBuf
is
derived from:
You can pass an RBuf
to a function that takes a
TDesC&
parameter, and a TDes&
parameter.
Memory that has already been allocated by your code can be
transferred to an RBuf
. This allows any data that may be in that
location to be managed through the descriptor.
Ownership of an existing HBufC
can be transferred to an
RBuf
. This allows the data contained within the HBufC
to be managed through the RBuf
API.
Although the following notes refer to the build independent types, they are equally valid for the explicit 8-bit and 16-bit types.
An RBuf
object behaves like a handle to a
resource. In this case, the resource is the buffer that contains the data.
While this might seem to be an implementation issue, you need to understand
that much of the interface provided by the RBuf
class itself is
involved in allocating, freeing and resizing this buffer.
An RBuf
object can:
be placed on the program stack.
be a member of another 'C' type class.
An RBuf object cannot be a member of a 'T' type class.
See also Class types.
The default constructor does not allocate a buffer, and means that, by default, the object represents no data. In descriptor terminology, its length is zero and its maximum length is zero.
Before an RBuf
can hold any data, you need to create the
buffer.
The simplest way to create the buffer is to use the
Create()
or the CreateL()
member functions of
RBuf
[See RBuf16::Create()
and
RBuf16::CreateL()
].
For example, the following code fragment constructs a resizable buffer descriptor that can hold up to 15 data items. In descriptor terminology, this means that the maximum length is set to 15. The current length of the descriptor is set to zero, i.e. it contains no data.
RBuf buf;
...
buf.CreateL(15);
...
Note that CreateL()
is similar to Create()
,
but leaves if memory cannot be allocated.
A variation on the simple creation technique is to use the
CreateMax()
or the CreateMaxL()
member functions of
RBuf
[See RBuf16::Create()
and
RBuf16::CreateL()
].
These functions not only allocate the buffer, but set the current length of the data to be the same as the maximum length. For example:
RBuf buf;
...
buf.CreateMaxL(15);
...
No data has been assigned to the descriptor, and in effect it contains "arbitrary" data. However, this can be useful in cases where the descriptor needs to have a current length before calling another function. For example, you might want a buffer of 16 bytes initialised to binary zeroes:
RBuf8 buf;
...
buf.CreateMaxL(15);
buf.Fillz();
...
FillZ()
provided by the TDes8
base
class, sets the data to binary zeroes for the length of the descriptor, 15
bytes in this example. Note that we have explicitly used the 8-bit variant
here.
There are other ways of achieving this, but this is an economical technique.
A common requirement is to create an RBuf
and to
copy the data from an existing descriptor of any type. RBuf
provides variants of Create()
and CreateL()
to do
this.
In the following code fragment, a TBuf
descriptor is passed to a function, which then passes it to this
CreateL()
variant. The source descriptor has a maximum length of
15, which means that the RBuf
buffer is allocated so that the
RBuf
maximum length is also 15.
The content of the source descriptor is copied into the
RBuf
, and the length of the RBuf
(i.e. the length of
data that the descriptor represents) is set to be the same as that of the
source descriptor - in this case 11.
_LIT(KSampleText,"Hello World");
...
TBuf<15> sampletext(KSampleText);
FuncL(sampletext);
...
void FuncL(TDesC& aSource)
{
RBuf buf;
buf.CreateL(aSource);
...
}
The following code fragment constructs a resizable buffer descriptor and copies data from a source descriptor. The length of data copied is limited by the value of the second parameter. If this is less than the length of the source descriptor, then only this length of data is copied. The buffer allocated is large enough to contain data of length specified by this second parameter.
This is often used in cases where the length of the source data is
not easily predictable, and it is important to limit the size of the
RBuf
buffer created.
There are two possibilities in this code fragment :
if the length of aSource
is 20, then the maximum
length of buf
is limited to 10, only half the data in
aSource
is copied, and the length of buf
is set to
10.
if the length of aSource
is 8, then the maximum
length of buf
is still set to 10; all 8 items are copied, and the
length of buf
is set to 8.
void FuncL(TDesC& aSource)
{
RBuf buf;
...
buf.CreateL(aSource,10);
...
}
An important technique is to create an RBuf
and
transfer ownership of existing allocated memory to it. This allows any data in
that block of memory to be managed through the RBuf
.
There are three main cases to consider:
transferring ownership of a preexisting heap descriptor, an
HBufC
type.
transferring ownership of allocated memory
transferring ownership of the buffer owned by a different
RBuf
.
This is a mechanism you would use if an existing API returned an
HBufC
, and you wanted to deal with its data through an
RBuf
instead.
The following code fragment shows how this could be done. Note that
the HBufC
pointer is passed to the RBuf
constructor.
Following construction, ownership of the heap descriptor has been
passed to the RBuf
object. In descriptor terminology, the maximum
length of the RBuf
is 15, and its length is 11, reflecting the
state of the original HBufC
object.
You can now manipulate any data that may have been assigned to the
original HBufC
, through the functions in the RBuf
base classes.
HBufC* hptr;
_LIT(KSampleText,"Hello World");
...
hptr = HBufC::NewL(15); // Creates a heap descriptor which can hold up
... // to 15 data items. The current length is zero.
*hptr = KSampleText; // Assigns some data to the heap descriptor.
... // The current length of the heap descriptor is now 11.
RBuf buf(hptr); // Ownership of the heap descriptor is passed
... // to the RBuf during construction of the RBuf.
There is an alternative technique that allows you to assign
ownership of the HBufC
after construction of the
RBuf
using the Assign()
function. This allows you to
reuse the same RBuf
object. For example:
HBufC* hptr;
_LIT(KSampleText,"Hello World");
...
hptr = HBufC::NewL(15); // Creates a heap descriptor which can hold up
... // to 15 data items. The current length is zero.
*hptr = KSampleText; // Assigns some data to the heap descriptor.
... // The current length of the heap descriptor is now 11.
RBuf buf;
...
buf.Assign(hptr); // Ownership of the heap descriptor is passed
... // to the RBuf after construction of the RBuf.
Once ownership has been transferred, you must not access the data
through the original HBufC
pointer.
There is no mechanism for reversing the transfer of ownership. The
freeing of the buffer can only be done through the RBuf
. See
Freeing the buffer.
In the following code fragment, ptr
is assumed to
contain the address of memory previously allocated. The code fragment shows how
ownership of this memory can be transferred to the RBuf
.
TUint16* ptr;
TInt length(32);
... // Assume memory of length 32 has been allocated
// and its address stored in ptr.
RBuf buf;
...
buf.Assign(ptr,length);
... // The memory passed to the descriptor becomes the
// descriptor's buffer. In descriptor terminology,
// the maximum length of the RBuf is 32; its length
// is zero, meaning that the descriptor represents
// no data.
In the following code fragment, an RBuf
is created, a
buffer created for it, and then ownership is transferred to another
RBuf
.
RBuf bufSource;
RBuf bufTarget;
...
bufSource.CreateL(15); // Creates memory buffer for the descriptor
// that can hold up to 15 data items. In descriptor
// terminology, the maximum length is set to 15 and
// the current length is set to 0.
bufTarget.Assign(bufSource); // Transfers ownership of the memory buffer
// to the bufTarget descriptor.
...
You can change the size of the memory buffer, by using the
ReAlloc()
or ReAllocL()
member functions of
RBuf
.
It does not matter how the original buffer was allocated, whether
directly via Create()
, CreateL()
,
CreateMax()
or CReateMaxL()
, or by transfer of
ownership of an existing buffer (and this includes transfer from an
HBufC
). You do this if you intend to increase (or significantly
decrease) the amount of data that the descriptor is to represent. Remember that
the amount of memory allocated to the buffer is not automatically changed in
response to changes in the amount of data.
In the following code fragment, an RBuf
is created by
copying from an existing descriptor. It is then reallocated so that its maximum
length is doubled. Error conditions, such as out-of-memory conditions are
ignored.
_LIT(KSampleText,"Hello World");
...
TBuf<15> sampletext(KSampleText);
FuncL(sampletext);
...
void FuncL(TDesC& aSource)
{
RBuf buf;
Tint max;
buf.CreateL(aSource); // max size is 15, length is 11.
max = buf.MaxLength() * 2;
buf.ReallocL(max); // max size is now 30, but length is still 11.
...
}
You can free the memory buffer by calling ReAlloc()
or
ReAllocL()
and passing a zero value.
_LIT(KSampleText,"Hello World");
...
TBuf<15> sampletext(KSampleText);
FuncL(sampletext);
...
void FuncL(TDesC& aSource)
{
RBuf buf;
buf.CreateL(aSource); // max size is 15, length is 11.
buf.ReallocL(0); // max size is now 0, length is 0, the memory
// buffer has been freed, and data
// has been thrown away.
...
}
You can also use the Close()
member function of
RBuf
. For example:
_LIT(KSampleText,"Hello World");
...
TBuf<15> sampletext(KSampleText);
FuncL(sampletext);
...
void FuncL(TDesC& aSource)
{
RBuf buf;
buf.CreateL(aSource); // max size is 15, length is 11.
buf.Close(); // max size is now 0, length is 0, the memory
// buffer has been freed, and data
// has been thrown away.
...
}
To avoid memory leaks when the RBuf
object is placed
on the stack, you should use the following programming pattern:
{
RBuf buf;
buf.CleanupClosePushL();
... // Use the RBuf
CleanupStack::PopAndDestroy() //remove from cleanup stack
// and free the buffer to avoid memory
}
The CleanupClosePushL()
function puts a cleanup item
onto the cleanup stack. The effect of this is to cause the class's
Close()
function to be called in the event of a leave
occurring. In its turn, Close()
simply frees off the buffer.
If an RBuf
is a member of a class, then that class's
destructor must remember to call either : ReAllocL(0)
or
Close()
.
An RBuf
can be reused, but you must remember to call
either : ReAllocL(0)
or Close()
before you assign
other memory or another HBufC
. Failure to do this will result in a
memory leak.
You should not use an RBuf
as a member of a 'T' type
class. See Class types.
Data in an RBuf
descriptor can be replaced. It can also be
modified using the standard functionality provided by the
TDes
base class.
_LIT(KSampleText,"Hello World");
...
TBuf<15> sampletext(KSampleText);
FuncL(sampletext);
...
void FuncL(TDesC& aSource)
{
RBuf buf;
buf.CreateL(aSource.MaxLength()); // Create the RBuf.
buf = aSource; // Copy "Hello World" using the assignment
// operator.
buf.Delete(1,1); // Delete the 1st character.
...
buf.Close();
}