yaSSL uses a simulated streaming input/output system defined in buffer.hpp. The buffers behave like a smart c style array for use with overloaded << and >> and provide a checking option, input is designed to be read from and output is for writing. std::vector is not used because of a desire to have checked []access, offset, and the ability to read/write to the buffer bulk wise while maintaining the correct size and offset. The buffers themselves use a checking policy.
The checking policy is Check by default but may be turned off at compile time to use NoCheck. Check merely ensures that range errors are caught and is especially useful for debugging and testing, though it is a simple inlined test, some users may prefer to avoid runtime buffer flow checks.
One other feature worth noting about the buffers is that since they know their current offset, an index is not required for operator[]. By passing the constant AUTO, the user is freed from making silly index tracking errors, easing the burden of simple, but still error-prone, input/output programming. For example, compare the following two implementations:
A) // input operator for ServerHello
input_buffer& operator>>(input_buffer& input, ServerHello& hello)
{
// Protocol
hello.server_version_.major_ = input[AUTO];
hello.server_version_.minor_ = input[AUTO];
// Random
input.read(hello.random_, RAN_LEN);
// Session
hello.id_len_ = input[AUTO];
input.read(hello.session_id_, ID_LEN);
// Suites
hello.cipher_suite_[0] = input[AUTO];
hello.cipher_suite_[1] = input[AUTO];
// Compression
hello.compression_method_ = CompressionMethod(input[AUTO]);
return input;
}
B) // input operator for ServerHello
input_buffer& operator>>(input_buffer& input, ServerHello& hello)
{
size_t i = input.get_current();
// Protocol
hello.server_version_.major_ = input[i++];
hello.server_version_.minor_ = input[i++];
// Random
input.read(hello.random_, RAN_LEN);
i += RAN_LEN;
// Session
hello.id_len_ = input[i++];
input.read(hello.session_id_, ID_LEN);
i += ID_LEN;
// Suites
hello.cipher_suite_[0] = input[i++];
hello.cipher_suite_[1] = input[i++];
// Compression
hello.compression_method_ =
CompressionMethod(input[i++]);
input.set_current(i);
return input;
}
While B is not much more difficult to implement, the chances for simple errors to occur are increased. Not to mention having to remember to get/set the current offset before passing the buffer to handlers and in the event of exceptions, there is no guarantee that the index is correctly positioned, making recovery nearly impossible.