/* * Copyright (c) Facebook, Inc. and its affiliates. * * 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 FOLLY_GEN_FILE_H_ #error This file may only be included from folly/gen/File.h #endif #include #include namespace folly { namespace gen { namespace detail { class FileReader : public GenImpl { public: FileReader(File file, std::unique_ptr buffer) : file_(std::move(file)), buffer_(std::move(buffer)) { buffer_->clear(); } template bool apply(Body&& body) const { for (;;) { ssize_t n; do { n = ::read(file_.fd(), buffer_->writableTail(), buffer_->capacity()); } while (n == -1 && errno == EINTR); if (n == -1) { throw std::system_error(errno, std::system_category(), "read failed"); } if (n == 0) { return true; } if (!body(ByteRange(buffer_->tail(), size_t(n)))) { return false; } } } // Technically, there could be infinite files (e.g. /dev/random), but people // who open those can do so at their own risk. static constexpr bool infinite = false; private: File file_; std::unique_ptr buffer_; }; class FileWriter : public Operator { public: FileWriter(File file, std::unique_ptr buffer) : file_(std::move(file)), buffer_(std::move(buffer)) { if (buffer_) { buffer_->clear(); } } template void compose(const GenImpl& source) const { auto fn = [&](ByteRange v) { if (!this->buffer_ || v.size() >= this->buffer_->capacity()) { this->flushBuffer(); this->write(v); } else { if (v.size() > this->buffer_->tailroom()) { this->flushBuffer(); } memcpy(this->buffer_->writableTail(), v.data(), v.size()); this->buffer_->append(v.size()); } }; // Iterate source.foreach(std::move(fn)); flushBuffer(); file_.close(); } private: void write(ByteRange v) const { ssize_t n; while (!v.empty()) { do { n = ::write(file_.fd(), v.data(), v.size()); } while (n == -1 && errno == EINTR); if (n == -1) { throw std::system_error( errno, std::system_category(), "write() failed"); } v.advance(size_t(n)); } } void flushBuffer() const { if (buffer_ && buffer_->length() != 0) { write(ByteRange(buffer_->data(), buffer_->length())); buffer_->clear(); } } mutable File file_; std::unique_ptr buffer_; }; inline auto byLineImpl(File file, char delim, bool keepDelimiter) { // clang-format off return fromFile(std::move(file)) | eachAs() | resplit(delim, keepDelimiter); // clang-format on } } // namespace detail /** * Generator which reads lines from a file. * Note: This produces StringPieces which reference temporary strings which are * only valid during iteration. */ inline auto byLineFull(File file, char delim = '\n') { return detail::byLineImpl(std::move(file), delim, true); } inline auto byLineFull(int fd, char delim = '\n') { return byLineFull(File(fd), delim); } inline auto byLineFull(const char* f, char delim = '\n') { return byLineFull(File(f), delim); } inline auto byLine(File file, char delim = '\n') { return detail::byLineImpl(std::move(file), delim, false); } inline auto byLine(int fd, char delim = '\n') { return byLine(File(fd), delim); } inline auto byLine(const char* f, char delim = '\n') { return byLine(File(f), delim); } } // namespace gen } // namespace folly