Skip to content

Instantly share code, notes, and snippets.

@hausdorff
Created March 11, 2016 20:52
Show Gist options
  • Save hausdorff/07c2d22bafb9c6bba7f2 to your computer and use it in GitHub Desktop.
Save hausdorff/07c2d22bafb9c6bba7f2 to your computer and use it in GitHub Desktop.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __STOUT_OS_POSIX_READ_HPP__
#define __STOUT_OS_POSIX_READ_HPP__
#include <stdio.h>
#ifndef __WINDOWS__
#include <unistd.h>
#endif // __WINDOWS__
#include <string>
#if defined(__sun) || defined(__WINDOWS__)
#include <fstream>
#endif // __sun || __WINDOWS__
#include <stout/error.hpp>
#include <stout/result.hpp>
#include <stout/try.hpp>
#ifdef __WINDOWS__
#include <stout/windows.hpp>
#include <stout/os/socket.hpp>
#endif // __WINDOWS__
namespace os {
#ifdef __WINDOWS__
inline ssize_t read(int fd, void* data, size_t size)
{
// TODO(benh): Map any Windows specific return code semantics from
// either `recv` or `_read` into POSIX semantics (i.e., what the
// callee will be checking for).
if (net::isSocket(fd)) {
return recv(fd, (char*)data, size, 0);
}
return ::_read(fd, data, size);
}
#else
inline ssize_t read(int fd, void* data, size_t size)
{
return ::read(fd, data, size);
}
#endif // __WINDOWS__
#ifdef __WINDOWS__
inline bool read_interrupted()
{
return WSAGetLastError() == WSAEWOULDBLOCK;
}
#else
inline bool read_interrupted()
{
return errno == EINTR;
}
#endif // __WINDOWS__
// Reads 'size' bytes from a file from its current offset.
// If EOF is encountered before reading 'size' bytes then the result
// will contain the bytes read and a subsequent read will return None.
inline Result<std::string> read(int fd, size_t size)
{
char* buffer = new char[size];
size_t offset = 0;
while (offset < size) {
ssize_t length = os::read(fd, buffer + offset, size - offset);
if (length < 0) {
// TODO(bmahler): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
if (read_interrupted()) {
continue;
}
ErrnoError error; // Constructed before 'delete' to capture errno.
delete[] buffer;
return error;
} else if (length == 0) {
// Reached EOF before expected! Only return as much data as
// available or None if we haven't read anything yet.
if (offset > 0) {
std::string result(buffer, offset);
delete[] buffer;
return result;
}
delete[] buffer;
return None();
}
offset += length;
}
std::string result(buffer, size);
delete[] buffer;
return result;
}
// Returns the contents of the file. NOTE: getline is not available on Solaris
// or Windows, so we use STL.
#if defined(__sun) || defined(__WINDOWS__)
inline Try<std::string> read(const std::string& path)
{
std::ifstream file(path.c_str());
if (!file.is_open()) {
// Does ifstream actually set errno?
return ErrnoError("Failed to open file '" + path + "'");
}
return std::string((std::istreambuf_iterator<char>(file)),
(std::istreambuf_iterator<char>()));
}
#else
inline Try<std::string> read(const std::string& path)
{
FILE* file = fopen(path.c_str(), "r");
if (file == NULL) {
return ErrnoError("Failed to open file '" + path + "'");
}
// Initially the 'line' is NULL and length 0, getline() allocates
// ('malloc') a buffer for reading the line.
// In subsequent iterations, if the buffer is not large enough to
// hold the line, getline() resizes it with 'realloc' and updates
// 'line' and 'length' as necessary. See:
// - http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html
// - http://man7.org/linux/man-pages/man3/getline.3.html
std::string result;
char* line = NULL;
size_t length = 0;
ssize_t read;
while ((read = getline(&line, &length, file)) != -1) {
result.append(line, read);
}
// getline() requires the line buffer to be freed by the caller.
free(line);
if (ferror(file)) {
ErrnoError error;
// NOTE: We ignore the error from fclose(). This is because
// users calling this function are interested in the return value
// of read(). Also an unsuccessful fclose() does not affect the
// read.
fclose(file);
return error;
}
fclose(file);
return result;
}
#endif // __sun
} // namespace os {
#endif // __STOUT_OS_POSIX_READ_HPP__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment