Use this descriptor to hold a string or binary data.
A resizable buffer descriptor is an RBuf type. Use the member functions of the base classes: TDes and TDesC to change the data in the descriptor. See Abstract base descriptor classes.
This buffer of the descriptor is put on the heap. The buffer is the place where the data is put. This is useful if 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:
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.
Creating an RBuf from an existing descriptor
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); ... }
Creating an RBuf by transferring ownership of a pre-existing buffer
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
.
Transferring ownership of an HBufC
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.
Transferring ownership of allocated memory
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.
Transferring ownership of the buffer owned by a different RBuf
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(); }