puts a data record into the buffer and returns a token for record.
PRECONDITION: caller holds a bufferManager monitor.
The caller must set the sync parameter true if the thread
will ultimately call sync() after a successful put().
This strategy allows the waitingThreads counter to be
incremented while the current thread holds the bufferManager
monitor.
Implementations should return a token that can be used
later for replay, and for debugging purposes.
The data record is passed as a byte[][] allowing
callers to construct data records from individual bits
of information.
The arrays are concatenated into a single log record whose size
is the sum of the individual array sizes.
Each array is preceded by the size of the individual array.
The entire record is preceded by the size of all arrays
including the individual array size fields.
The record format is as follows:
+------+------------+----------------+---------+ +----------------+---------+
| type | total size | data[0].length | data[0] | . . . | data[n].length | data[n] |
+------+------------+----------------+---------+ +----------------+---------+
During replay the entire record is returned as a single
byte[]. The ReplayListener is responsible for
decomposing the record into the original array of byte[].