Component of the Week #36: bdlsb - I/O streambufs for memory buffers
- Summary:
Provides stream buffer classes for memory-based I/O operations.
Offers both fixed-size and dynamic memory buffer solutions.
Includes high-performance non-virtual stream buffer alternatives.
Supports both input and output operations on memory buffers.
The bdlsb package
provides a collection of stream buffer classes that implement the
bsl::basic_streambuf protocol using memory buffers instead of files or
other I/O devices. This package is particularly useful for testing,
serialization, and high-performance streaming operations where data is
processed entirely in memory.
Package Components
This package contains components that fall into two categories. Components with names ending in “streambuf” define classes derived from bsl::streambuf. The other components define classes with similar interfaces but without inheritance from bsl::streambuf. These non-derived classes can be passed as template parameters, making member function calls faster since they avoid virtual function overhead. The package contains the following components:
bdlsb_fixedmeminput: Basic input stream buffer using a client-supplied buffer. Does not derive from
bsl::streambuffor better performance.bdlsb_fixedmeminstreambuf: Input stream buffer using a client-supplied buffer, deriving from
bsl::streambuffor compatibility with standard streams.bdlsb_fixedmemoutput: Basic output stream buffer using a client-supplied buffer. Does not derive from
bsl::streambuffor better performance.bdlsb_fixedmemoutstreambuf: Output stream buffer using a client-supplied buffer, deriving from
bsl::streambuffor compatibility with standard streams.bdlsb_memoutstreambuf: Output stream buffer using allocator-managed memory that can grow dynamically.
bdlsb_overflowmemoutput: Output stream buffer that starts with a client buffer but overflows to an allocator-managed buffer when needed.
bdlsb_overflowmemoutstreambuf: Stream buffer version of
bdlsb_overflowmemoutputthat derives frombsl::streambuf.
Usage Examples
Fixed Memory Input Stream Buffer
bdlsb::FixedMemInStreamBuf is designed for reading from a fixed memory buffer with full bsl::streambuf compatibility. Here we show using it with a standard bsl::istream:
#include <bdlsb_fixedmeminstreambuf.h>
#include <bdlb_doublecompareutil.h>
#include <bsl_algorithm.h>
#include <bsl_cmath.h>
#include <bsl_iostream.h>
#include <bsl_string.h>
#include <cassert>
using namespace BloombergLP;
using bsl::cout;
using bsl::endl;
int main() {
typedef bdlb::DoubleCompareUtil CompUtil;
// First, prepare some text data in a buffer
const char testData[] = "42 3.14159 Hello World!\n"
"100 2.71828 Testing 123\n";
// Create a fixed memory input stream buffer from the test data
bdlsb::FixedMemInStreamBuf streamBuffer(testData,
sizeof(testData) - 1);
bsl::istream inputStream(&streamBuffer);
// Read structured data from the buffer
int intValue1, intValue2;
double doubleValue1, doubleValue2;
bsl::string text1, text2, text3, text4;
inputStream >> intValue1 >> doubleValue1 >> text1 >> text2;
inputStream >> intValue2 >> doubleValue2 >> text3 >> text4;
if (inputStream.fail()) {
cout << "Error reading from stream" << endl;
return -1;
}
// Verify the parsed data
assert(intValue1 == 42);
assert(0 == CompUtil::fuzzyCompare(doubleValue1, 3.14159, 0.01));
assert(text1 == "Hello");
assert(text2 == "World!");
assert(intValue2 == 100);
assert(0 == CompUtil::fuzzyCompare(doubleValue2, 2.71828, 0.01));
assert(text3 == "Testing");
assert(text4 == "123");
cout << "All values parsed and verified successfully!" << endl;
return 0;
}
Fixed Memory Output Stream Buffer
bdlsb::FixedMemOutStreamBuf is designed for writing to a fixed memory buffer with full bsl::streambuf compatibility. Here we show using it with a standard bsl::ostream:
#include <bdlsb_fixedmemoutstreambuf.h>
#include <bslma_defaultallocatorguard.h>
#include <bslma_testallocator.h>
#include <bsl_iostream.h>
#include <bsl_string.h>
#include <cassert>
using namespace BloombergLP;
using bsl::cout;
using bsl::endl;
using bsl::ends;
int main() {
// Set up test allocator as default to verify no default allocations
bslma::TestAllocator defaultAllocator("default", false);
bslma::DefaultAllocatorGuard guard(&defaultAllocator);
enum { k_BUFFER_SIZE = 200 };
char buffer[k_BUFFER_SIZE];
// Create a fixed memory output stream buffer
bdlsb::FixedMemOutStreamBuf streamBuf(buffer, k_BUFFER_SIZE);
bsl::ostream outputStream(&streamBuf);
// Write formatted text and numerical data
outputStream << "Test Results Summary" << endl;
outputStream << "===================" << endl;
outputStream << "Tests run: " << 150 << endl;
outputStream << "Success rate: " << 98.67 << "%" << endl;
outputStream << "Average time: " << 2.345 << " seconds" << endl;
outputStream << ends; // null-terminate the buffer
if (outputStream.fail()) {
cout << "Error writing to stream" << endl;
return -1;
}
// Check what was written
cout << "Bytes written: " << streamBuf.length() << endl;
cout << "Buffer capacity: " << streamBuf.capacity() << endl;
cout << endl;
// Display the formatted output
cout << "Generated output:" << endl;
cout << "=================" << endl;
// Output the buffer directly
cout << buffer;
// Verify no default allocations occurred
assert(0 == defaultAllocator.numAllocations());
return 0;
}
Expected output:
Bytes written: 85
Buffer capacity: 200
Generated output:
=================
Test Results Summary
===================
Tests run: 150
Success rate: 98.67%
Average time: 2.345 seconds
Dynamic Memory Output Stream Buffer
bdlsb::MemOutStreamBuf provides a stream buffer that can grow dynamically and is particularly useful for testing stream operations:
#include <bdlsb_memoutstreambuf.h>
#include <bslma_defaultallocatorguard.h>
#include <bslma_testallocator.h>
#include <bsl_iostream.h>
#include <bsl_string.h>
#include <cassert>
using namespace BloombergLP;
using bsl::cout;
using bsl::endl;
using bsl::ends;
int main() {
// Set up test allocator as default to verify no default allocations
bslma::TestAllocator defaultAllocator("default", false);
bslma::DefaultAllocatorGuard guard(&defaultAllocator);
bslma::TestAllocator ta("test", false);
// Create a memory output stream buffer
// One advantage: doesn't use default allocator like
// `bsl::ostringstream`.
bdlsb::MemOutStreamBuf streamBuf(&ta);
bsl::ostream os(&streamBuf);
// Write formatted output directly to the stream
os << "=== Test Results ===" << endl;
os << "Tests Passed: " << 42 << endl;
os << "Tests Failed: " << 3 << endl;
os << ends; // Add terminating null
// Get the formatted output without using default allocator
cout << "Formatted output:" << endl;
cout << streamBuf.data() << endl;
cout << "Allocator used: " << ta.numBytesInUse() << " bytes" << endl;
// Reading the contents of a `bsl::ostringstream` with the 'str()'
// accessor would unavoidably use the default allocator.
cout << "Note: `bsl::ostringstream.str()` would use the default\n"
<< "allocator, but `bdlsb::MemOutStreamBuf` allows us to\n"
<< "use an allocator of our choice." << endl;
// Verify no default allocations occurred
assert(0 == defaultAllocator.numAllocations());
return 0;
}
Expected output:
Formatted output:
=== Test Results ===
Tests Passed: 42
Tests Failed: 3
Allocator used: 256 bytes
Note: `bsl::ostringstream.str()` would use the default
allocator, but `bdlsb::MemOutStreamBuf` allows us to
use an allocator of our choice.
Key Features
Performance Optimization
* Non-virtual versions (FixedMemInput, FixedMemOutput, OverFlowMemOutput) avoid virtual function call overhead, useful in calls that take a ‘streambuf-like’ class as a template arguement rather than as a streambuf *
* Designed for high-performance scenarios where minimal overhead is crucial
Memory Management
* Fixed-size buffers for predictable memory usage
* Dynamic buffers that grow as needed
* Overflow buffers that start fixed but expand when necessary
* Control over allocator usage (unlike bsl::ostringstream)
Testing Benefits * Output classes that inherit from bsl::streambuf can be used to initialize bsl::ostream`s and test output functions without using the default allocator, which is not possible with `bsl::ostringstream. * Direct access to raw buffer contents for verification * No file I/O overhead during testing
Flexibility
* Both bsl::streambuf-derived and non-derived versions
* Support for both input and output operations
* Compatible with standard C++ streams and text/numerical I/O
Common Use Cases
The bdlsb package is particularly useful for:
Text and numerical I/O: Parsing and formatting data in memory buffers
Testing: Capturing stream output for verification without file I/O
Memory-constrained environments: Fixed-size buffers with predictable memory usage
Performance-critical code: Non-virtual versions for minimal overhead
Allocator control: Avoiding default allocator usage in output stream testing
- For more details, see:
bsl::streambuf documentation for stream buffer concepts
documentation for bdls::FdStreamBuf for file descriptor-based stream buffers for stream-based access to already-opened files, pipes, and devices.