98981ef9f143a9a709dd8e79c068a980b5ee5847
[folly.git] / folly / gen / File-inl.h
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_GEN_FILE_H_
18 #error This file may only be included from folly/gen/File.h
19 #endif
20
21 #include <system_error>
22
23 #include <folly/gen/String.h>
24
25 namespace folly {
26 namespace gen {
27 namespace detail {
28
29 class FileReader : public GenImpl<ByteRange, FileReader> {
30  public:
31   FileReader(File file, std::unique_ptr<IOBuf> buffer)
32     : file_(std::move(file)),
33       buffer_(std::move(buffer)) {
34     buffer_->clear();
35   }
36
37   template <class Body>
38   bool apply(Body&& body) const {
39     for (;;) {
40       ssize_t n;
41       do {
42         n = ::read(file_.fd(), buffer_->writableTail(), buffer_->capacity());
43       } while (n == -1 && errno == EINTR);
44       if (n == -1) {
45         throw std::system_error(errno, std::system_category(), "read failed");
46       }
47       if (n == 0) {
48         return true;
49       }
50       if (!body(ByteRange(buffer_->tail(), n))) {
51         return false;
52       }
53     }
54   }
55
56   // Technically, there could be infinite files (e.g. /dev/random), but people
57   // who open those can do so at their own risk.
58   static constexpr bool infinite = false;
59
60  private:
61   File file_;
62   std::unique_ptr<IOBuf> buffer_;
63 };
64
65 class FileWriter : public Operator<FileWriter> {
66  public:
67   FileWriter(File file, std::unique_ptr<IOBuf> buffer)
68     : file_(std::move(file)),
69       buffer_(std::move(buffer)) {
70     if (buffer_) {
71       buffer_->clear();
72     }
73   }
74
75   template <class Source, class Value>
76   void compose(const GenImpl<Value, Source>& source) const {
77     auto fn = [&](ByteRange v) {
78       if (!this->buffer_ || v.size() >= this->buffer_->capacity()) {
79         this->flushBuffer();
80         this->write(v);
81       } else {
82         if (v.size() > this->buffer_->tailroom()) {
83           this->flushBuffer();
84         }
85         memcpy(this->buffer_->writableTail(), v.data(), v.size());
86         this->buffer_->append(v.size());
87       }
88     };
89
90     // Iterate
91     source.foreach(std::move(fn));
92
93     flushBuffer();
94     file_.close();
95   }
96
97  private:
98   void write(ByteRange v) const {
99     ssize_t n;
100     while (!v.empty()) {
101       do {
102         n = ::write(file_.fd(), v.data(), v.size());
103       } while (n == -1 && errno == EINTR);
104       if (n == -1) {
105         throw std::system_error(errno, std::system_category(),
106                                 "write() failed");
107       }
108       v.advance(n);
109     }
110   }
111
112   void flushBuffer() const {
113     if (buffer_ && buffer_->length() != 0) {
114       write(ByteRange(buffer_->data(), buffer_->length()));
115       buffer_->clear();
116     }
117   }
118
119   mutable File file_;
120   std::unique_ptr<IOBuf> buffer_;
121 };
122
123 }  // !detail
124
125 /**
126  * Generator which reads lines from a file.
127  * Note: This produces StringPieces which reference temporary strings which are
128  * only valid during iteration.
129  */
130 inline auto byLine(File file, char delim = '\n')
131     -> decltype(fromFile(std::move(file))
132                 | eachAs<StringPiece>()
133                 | resplit(delim)) {
134   return fromFile(std::move(file))
135        | eachAs<StringPiece>()
136        | resplit(delim);
137 }
138
139 inline auto byLine(int fd, char delim = '\n')
140   -> decltype(byLine(File(fd), delim)) { return byLine(File(fd), delim); }
141
142 inline auto byLine(const char* f, char delim = '\n')
143   -> decltype(byLine(File(f), delim)) { return byLine(File(f), delim); }
144
145 }}  // !folly::gen