The Data class encapsulates a buffer of bytes. It is similar in functionality to many common string classes, except that it operates on collections of bytes instead of collections of characters. In particular, Data is meant to operate on buffers that may or may not contain binary data. That said, there are a number of operations on Data which are written with manipulation of text in mind, and many methods go out of their way to ensure that the buffer is often null-terminated. It's a bit schizophrenic that way.
At any given time, a Data is associated with a single buffer which contains the bytes inside the Data. This buffer may be owned by the Data object itself, or owned by an external party. Additionally, data owned by an external party can be considered changeable or unchangeable.
|Sharing Style||Buffer Ownership||Buffer Mutable?|
|Share||External to Data class||Yes|
|Borrow||External to Data class||No|
|Take||Owned by Data class||Yes|
If a Data containing an immutable buffer (i.e. Sharing Mode is Borrow) is changed, then a new buffer is allocated by the Data, the contents of the existing (immutable) buffer are copied into it, and the modification is performed on the new buffer. This newly allocated buffer is owned by the Data.
If a modification is performed on a Data which requires more space than is available in the underlying buffer, then a larger buffer is allocated, the existing data is copied into the new buffer, and the modification is performed on the new buffer. If the old buffer was owned by the Data, it will be deallocated.
Note that all deallocation performed by the Data uses delete; consequently, any data owned by the Data (e.g. Sharing Mode is Take) must be allocated from the normal heap using new, lest you anger the portability gods.
Finally, users of Data should note that a Data created from a buffer with Share or Borrow sharing must outlive the Data itself.
Creating a Data
Data conveniently has fifteen non-deprecated constructors for your creation pleasure. The default constructor will ultimately cause the Data to allocate its own internal memory. The copy constuctor does pretty much what you expect it to do.
Creating from Another Type
Data will take a variety of different kinds of types in its constructor and convert them into the string representation of those types. Note that the data type passed to the constructor must be an exact match -- for example, you cannot call a Data constructor with a type of "float" as its parameter; you must first explicitly convert the float to a double (through explicit casting, for example).
Conversion from null-terminated c-style string:
char *mySignedCharPtr = "Hello"; resip::Data data1 = new resip::Data(mySignedCharPtr); // data1 contains "Hello"
Conversion from null-terminated c-style string of unsigned characters (for the criminally insane):
unsigned char *myUnsignedCharPtr = "Hej"; resip::Data data2 = new resip::Data(myUnsignedCharPtr); // data2 contains "Hej"
Conversion from STL string:
std::string myString = "Hola"; resip::Data data3 = new resip::Data(myString); // data3 contains "Hola"
Conversion from integer:
int myInt = -75; resip::Data data4 = new resip::Data(myInt); // data4 contains "-75"
Conversion from unsigned long integer:
unsigned long myUnsignedLong = 12345; resip::Data data5 = new resip::Data(myUnsignedLong); // data5 contains "12345"
Conversion from unsigned integer:
unsigned int myUnsignedInt = 75; resip::Data data6 = new resip::Data(myUnsignedLong); // data6 contains "-75"
Conversion from double takes an optional precision parameter which indicates the number of digits after the decimal point. Trailing zeros are omitted. If not specified, the default precision is 4.
double myDouble = 7.12345; resip::Data data7 = new resip::Data(myUnsignedLong); // data7 contains "7.1234" resip::Data data8 = new resip::Data(myUnsignedLong,2); // data8 contains "7.12" resip::Data data9 = new resip::Data(myUnsignedLong,6); // data9 contains "7.12345"
Conversion from boolean:
bool myBool = true; resip::Data data10 = new resip::Data(myBool); // data10 contains "true"
Conversion from character:
char myChar = 'a'; resip::Data data11 = new resip::Data(myChar); // data11 contains "true"
Creating from an Existing Buffer
The easiest way to create a Data from an existing buffer is to pass in a buffer and the number of bytes of meaningful data that exist in the buffer. This constructor operates in "Share" mode -- the Data will copy the passed-in buffer if and only if an attempt is made to change the contents of the Data.
char buffer; int bytes = read(fd, buffer, sizeof(buffer)); resip::Data myData = new resip::Data(buffer, bytes);
Alternately, you can explicitly indicate the sharing mode desired; once again, the length indictated is the number of meaninful bytes already present in the buffer, not the total length of the buffer. No constructor yet exists which allows you to indicate both the number of meaningful bytes and the total length of the buffer; if you have a need for such a constructor, though, it should be easy add one.
char buffer; int bytes = read(fd, buffer, sizeof(buffer)); resip::Data myData = new resip::Data(resip::Data::Borrow, buffer, bytes);
If you are creating a Data from a c-style null-terminated string, you are given the option of indicating a sharing mode also. Be very, very careful that your c-style string is null terminated if you use this constructor.
char myChar = "Hello"; resip::Data myData = new resip::Data(resip::Data::Borrow, myChar);
Finally, you can create a Data from another Data with an explicit allocation type. This is kind of copy-contstructor-ish, except that the compiler won't call it for you:
resip::Data myData = new resip::Data("I'm a teapot"); resip::Data myOtherData = new resip::Data(resip::Data::Share, myData);
equal, <, >, prefix, postfix, substr
hash, hex, escaping, md5