Created
March 11, 2016 20:52
-
-
Save hausdorff/07c2d22bafb9c6bba7f2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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