Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdls_fdstreambuf
[Package bdls]

Provide a stream buffer initialized with a file descriptor. More...

Namespaces

namespace  bdls

Detailed Description

Outline
Purpose:
Provide a stream buffer initialized with a file descriptor.
Classes:
bdls::FdStreamBuf stream buffer constructed with file descriptor
See also:
<bsl::streambuf>
Description:
This component implements a class, bdls::FdStreamBuf, derived from the C++ standard library's bsl::streambuf that can be associated with a file descriptor. Except for the pubimbue function, all of the actions that can be performed on an bsl::streambuf can be performed on a bdls::FdStreamBuf. An bsl::streambuf provides public methods for reading from and writing to a stream of data, which are implemented in terms of protected virtual functions. A bdls::FdStreamBuf provides an implementation of these protected virtual members such that they operate on a given file descriptor. The file descriptor can represent a file, a pipe, or other device, and it can be associated with the bdls::FdStreamBuf at construction, or by calling the reset method. Note that a bsl::stream can be initialized with a bdls::FdStreamBuf, making it possible to associate the stream with a file descriptor.
Note that the pubimbue function may be called, but not with any value other than bsl::locale(). Furthermore, when called with this value, it has no effect.
The file descriptor type bdls::FilesystemUtil::FileDescriptor used in this component is, on Unix, an int type returned by open, and on Windows, a HANDLE type returned by CreateFile. Ideally, a user would open the file and obtain the platform-independent bdls::FilesystemUtil::FileDescriptor by calling bdls::FilesystemUtil::open, which will call the appropriate routine for the platform and return a bdls::FilesystemUtil::FileDescriptor. A value of bdls::FilesystemUtil::k_INVALID_FD is used to represent an invalid file handle on both platforms.
On Windows for a file in text mode, the byte 0x1a (ctrl-Z) is recognized as an end of file marker. If it is encountered, it is not returned in the buffer and subsequent reads will indicate that no more input is available. The behavior is undefined if it is not the last byte in the file. Other types of files are not required to end with 0x1a. For files on Unix and files opened in binary mode on Windows, 0x1a is treated like any other byte.
Note that the public methods of the bsl::streambuf class used in the usage example are not described here. See documentation in "The C++ Programming Language, Third Edition", by Bjarne Stroustrup, Section 21.6.4, and on the web at:
 http://www.cplusplus.com/reference/iostream/streambuf
Note that the bdls::FdStreamBuf and bdls::FdStreamBuf_FileHandler classes here are based on STLPort's implementation of filebuf and _Filebuf_Base respectively, with copyright notice as follows:
 ----------------------------------------------------------------------------
 Copyright (c) 1999
 Silicon Graphics Computer Systems, Inc.

 Copyright (c) 1999
 Boris Fomitchev

 This material is provided "as is", with absolutely no warranty expressed
 or implied.  Any use is at your own risk.

 Permission to use or copy this software for any purpose is hereby granted
 without fee, provided the above notices are retained on all copies.
 Permission to modify the code and to distribute modified code is granted,
 provided the above notices are retained, and a notice that the code was
 modified is included with the above copyright notice.
 ----------------------------------------------------------------------------
Usage:
This section illustrates intended use of this component.
Example 1: Stream Initialization:
The most common usage of this component is to initialize a stream. In this case, the bdls::FdStreamBuf will be used for either input or output, but not both.
First we create a suitable file name, and make sure that no file of that name already exists:
  char fileNameBuffer[100];
  bsl::sprintf(fileNameBuffer,
#ifdef BSLS_PLATFORM_OS_UNIX
               "/tmp/bdls_FdStreamBuf.usage.1.%d.txt",
#else // windows
               "C:\\TEMP\\bdls_FdStreamBuf.usage.1.%d.txt";
#endif
               bdls::ProcessUtil::getProcessId());

  bdls::FilesystemUtil::remove(fileNameBuffer);
  assert(0 == bdls::FilesystemUtil::exists(fileNameBuffer));
Then we create the file and open a file descriptor to it; the boolean flags indicate that the file is to be writable, and not previously existing (and therefore must be created):
  typedef bdls::FilesystemUtil::FileDescriptor FdType;

  FdType fd = bdls::FilesystemUtil::open(fileNameBuffer,
                                         bdls::FilesystemUtil::e_CREATE,
                                         bdls::FilesystemUtil::e_READ_WRITE);
  assert(bdls::FilesystemUtil::k_INVALID_FD != fd);
Next we create a bdls::FdStreamBuf associated with file descriptor fd; the false argument indicates that streamBuffer will not assume ownership of fd, meaning that when streamBuffer is destroyed fd will remain open:
Note also that the stream buffer defaults to being in text mode on Windows, and binary mode on Unix.
  {
      bdls::FdStreamBuf streamBuffer(fd,
                                     true,    // writable
                                     false);  // 'fd' won't be closed
                                              // when 'streamBuffer' is
                                              // destroyed

      bsl::ostream os(&streamBuffer);

      os << "Five times nine point five = " << 5 * 9.5 << bsl::endl;
  }
Note also that the stream buffer defaults to being in text mode on Windows, and binary mode on Unix.
Now create a new stream buffer to read the file back, in this case using binary mode so we can see exactly what was written. The new stream buf is used to initialize an input stream.
  {
      // read it in binary mode

      bdls::FdStreamBuf streamBuffer(fd,
                                     false,  // not writable
                                     false,  // 'streamBuffer' does not
                                             // own 'fd'
                                     true);  // binary mode

      streamBuffer.pubseekpos(0);

      char buf[100];
      bsl::memset(buf, 0, sizeof(buf));

      bsl::istream is(&streamBuffer);
      char *pc = buf;
      do {
          is >> bsl::noskipws >> *pc++;
      } while ('\n' != pc[-1]);

#ifdef BSLS_PLATFORM_OS_UNIX
      assert(!bsl::strcmp("Five times nine point five = 47.5\n", buf));
#else
      //On Windows we see a CRLF ('\r\n') instead of a simple LF '\n'
      assert(!bsl::strcmp("Five times nine point five = 47.5\r\n", buf));
#endif
  }
Finally, read the file back a second time, this time in text mode. Note how, on Windows, the \r\n is translated back to \n
  {
      // read it back in text mode

      bdls::FdStreamBuf streamBuffer(fd,
                                     false);  // not writable
                                              // 'fd' will be closed when
                                              // streamBuffer is destroyed.
                                              // Mode will be binary on
                                              // Unix, text on Dos.
      streamBuffer.pubseekpos(0);

      char buf[100];
      bsl::memset(buf, 0, sizeof(buf));

      bsl::istream is(&streamBuffer);
      char *pc = buf;
      do {
          is >> bsl::noskipws >> *pc++;
      } while ('\n' != pc[-1]);

      assert(!bsl::strcmp("Five times nine point five = 47.5\n", buf));
  }
And finally, we clean up:
  bdls::FilesystemUtil::remove(fileNameBuffer);
Example 2: Streambuf:
For our second example we will create a bdls::FdStreamBuf associated with a temporary file, and then use the public methods of the base class interface, including sputn, sgetn and pubseekpos, to do some I/O and seeking on it.
  const char line1[] = "To be or not to be, that is the question.\n";
  const char line2[] =
                     "There are more things in heaven and earth,\n"
                     "Horatio, than are dreamt of in your philosophy.\n";
  const char line3[] = "Wherever you go, there you are.  B Banzai\n";

  const int lengthLine1 = sizeof(line1) - 1;
  const int lengthLine2 = sizeof(line2) - 1;
  const int lengthLine3 = sizeof(line3) - 1;
We start by selecting a file name for our (temporary) file.
  char fileNameBuffer[100];
  bsl::sprintf(fileNameBuffer,
#ifdef BSLS_PLATFORM_OS_UNIX
               "/tmp/bdls_FdStreamBuf.usage.2.%d.txt",
#else // windows
               "C:\\TEMP\\bdls_FdStreamBuf.usage.2.%d.txt",
#endif
               bdls::ProcessUtil::getProcessId());
Then, make sure the file does not already exist:
  bdls::FilesystemUtil::remove(fileNameBuffer);
  assert(false == bdls::FilesystemUtil::exists(fileNameBuffer));
Next, Create the file and open a file descriptor to it. The boolean flags indicate that the file is writable, and not previously existing (and therefore must be created):
  typedef bdls::FilesystemUtil::FileDescriptor FdType;

  FdType fd = bdls::FilesystemUtil::open(fileNameBuffer,
                                         bdls::FilesystemUtil::e_CREATE,
                                         bdls::FilesystemUtil::e_READ_WRITE);
  assert(bdls::FilesystemUtil::k_INVALID_FD != fd);
Now, we create a bdls::FdStreamBuf object named streamBuffer associated with the file descriptor fd. Note that streamBuffer defaults to assuming ownership of fd, meaning that when streamBuffer is cleared, reset, or destroyed, fd will be closed. Note that FdStreamBuf implements streambuf, which provides the public methods used in this example:
  bdls::FdStreamBuf streamBuffer(fd, true);

  assert(streamBuffer.fileDescriptor() == fd);
  assert(streamBuffer.isOpened());
Next we use the sputn method to write two lines to the file:
  streamBuffer.sputn(line1, lengthLine1);
  streamBuffer.sputn(line2, lengthLine2);
Then we seek back to the start of the file.
  bsl::streamoff status = streamBuffer.pubseekpos(0);
  assert(0 == status);
Next, we read the first lengthLine1 characters of the file into buf, with the method sgetn.
  char buf[1000];
  bsl::memset(buf, 0, sizeof(buf));
  status = streamBuffer.sgetn(buf, lengthLine1);
  assert(lengthLine1 == status);
  assert(!bsl::strcmp(line1, buf));
Next we try to read 2 * lengthLine2 characters when only lengthLine2 characters are available in the file to read, so the sgetn method will stop after reading lengthLine2 characters. The sgetn method will return the number of chars successfully read:
  bsl::memset(buf, 0, sizeof(buf));
  status =  streamBuffer.sgetn(buf, 2 * lengthLine2);
  assert(lengthLine2 == status);
  assert(!bsl::strcmp(line2, buf));
Trying to read past the end of the file invalidated the current cursor position in the file, so we must seek from the end or the beginning of the file in order to establish a new cursor position. Note the pubseekpos method always seeks relative to the beginning. We seek back to the start of the file:
  status = streamBuffer.pubseekpos(0);
  assert(0 == status);
Note that line1 and line3 are the same length:
  assert(lengthLine1 == lengthLine3);
Then we write, replacing line1 in the file with line3:
  status = streamBuffer.sputn(line3, lengthLine3);
  assert(lengthLine3 == status);
Now we seek back to the beginning of the file:
  status = streamBuffer.pubseekpos(0);
Next we verify we were returned to the start of the file:
  assert(0 == status);
Then we read and verify the first line, which now contains the text of line3:
  bsl::memset(buf, 0, sizeof(buf));
  status = streamBuffer.sgetn(buf, lengthLine3);
  assert(lengthLine3 == status);
  assert(!bsl::strcmp(line3, buf));
Now we read and verify the second line, still line2:
  bsl::memset(buf, 0, sizeof(buf));
  status = streamBuffer.sgetn(buf, lengthLine2);
  assert(lengthLine2 == status);
  assert(!bsl::strcmp(line2, buf));
Next we close fd and disconnect streamBuffer from fd:
  status = streamBuffer.clear();
  assert(0 == status);
Note that streamBuffer is now no longer open, and is not associated with a file descriptor:
  assert(!streamBuffer.isOpened());
  assert(bdls::FilesystemUtil::k_INVALID_FD ==
         streamBuffer.fileDescriptor());
Finally, we clean up the file:
  bdls::FilesystemUtil::remove(fileNameBuffer);