Line data Source code
1 : /*
2 : * Copyright (C) 2014-2017 Fulvio Benini
3 :
4 : * This file is part of Scid (Shane's Chess Information Database).
5 : *
6 : * Scid is free software: you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation.
9 : *
10 : * Scid is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with Scid. If not, see <http://www.gnu.org/licenses/>.
17 : */
18 :
19 : /** @file
20 : * Extends the std:filebuf class with performance improvements.
21 : */
22 :
23 : #ifndef FILEBUF_H
24 : #define FILEBUF_H
25 :
26 : #include "common.h"
27 : #include <climits>
28 : #include <fstream>
29 :
30 : /**
31 : * Adds some helper functions to std::filebuf:
32 : * - getline()
33 : * - read and write of unsigned integers with size of 32/24/16/8 bit.
34 : */
35 193 : class Filebuf : public std::filebuf {
36 : public:
37 : /**
38 : * Opens a file.
39 : * @param filename: path to the file to be opened.
40 : * @param fmode: open the file for reading, writing, or both.
41 : * @returns OK on success, an @e errorT code on failure.
42 : */
43 87 : errorT Open(const char* filename, fileModeT fmode) {
44 87 : std::ios::openmode mode = std::ios::binary;
45 87 : switch (fmode) {
46 33 : case FMODE_ReadOnly:
47 33 : mode |= std::ios::in;
48 33 : break;
49 15 : case FMODE_WriteOnly:
50 15 : mode |= std::ios::out;
51 15 : break;
52 15 : case FMODE_Both:
53 15 : mode |= std::ios::in | std::ios::out;
54 15 : break;
55 20 : case FMODE_Create:
56 20 : mode |= std::ios::in | std::ios::out | std::ios::trunc;
57 20 : break;
58 4 : default:
59 4 : return ERROR_FileMode;
60 : }
61 :
62 83 : return (open(filename, mode) != 0) ? OK : ERROR_FileOpen;
63 : }
64 :
65 : /**
66 : * Equivalent to std::fstream::getline, but faster (no sentry [27.7.2.1.3]).
67 : *
68 : * Extracts characters from @e *this and stores them in successive locations
69 : * of the array whose first element is pointed to by @p str, until the end
70 : * of line ('\\n' char) or the end of file condition occurs.
71 : * The '\\n' char is not stored into @p str, and a null character, even in
72 : * case of errors, is appended.
73 : *
74 : * Typical usage: @code
75 : * while (file.getline(buf, sizeof buf)) {
76 : * // use buf
77 : * }
78 : * if (file.sgetc() != EOF) // error
79 : * @endcode
80 : * @param str: pointer to the character string to store the characters to
81 : * @param count: size of character string pointed to by @p str
82 : * @returns
83 : * - the number of characters read, including the '\\n' char
84 : * - 0 on failure or if the buffer is too small.
85 : */
86 491 : size_t getline(char* str, size_t count) {
87 491 : ASSERT(str != 0);
88 491 : ASSERT(count != 0);
89 :
90 491 : size_t n = 0;
91 9220 : for (int ch = sgetc(); ch != EOF; ch = snextc()) {
92 9206 : ++n;
93 9206 : if (ch == '\n') {
94 217 : sbumpc();
95 217 : break;
96 : }
97 8989 : if (n >= count) {
98 260 : n = 0; // Fail: buffer too small
99 260 : break;
100 : }
101 8729 : *str++ = static_cast<char>(ch);
102 : }
103 491 : *str = 0;
104 491 : return n;
105 : }
106 :
107 : /**
108 : * Reads a 8-bit unsigned integer.
109 : * This function do not check for errors or EOF.
110 : */
111 299025 : byte ReadOneByte() { return static_cast<byte>(sbumpc()); }
112 :
113 : /**
114 : * Reads a 16-bit unsigned integer.
115 : * This function do not check for errors or EOF.
116 : */
117 134495 : uint16_t ReadTwoBytes() { return static_cast<uint16_t>(read<2>()); }
118 :
119 : /**
120 : * Reads a 24-bit unsigned integer.
121 : * This function do not check for errors or EOF.
122 : */
123 3081 : uint32_t ReadThreeBytes() { return read<3>(); }
124 :
125 : /**
126 : * Reads a 32-bit unsigned integer.
127 : * This function do not check for errors or EOF.
128 : */
129 3028 : uint32_t ReadFourBytes() { return read<4>(); }
130 :
131 : /**
132 : * Writes a 8-bit unsigned integer.
133 : * @returns the number of characters successfully written.
134 : */
135 148910 : int WriteOneByte(byte value) {
136 148910 : int_type ch = sputc(static_cast<char_type>(value));
137 148910 : return (ch != traits_type::eof()) ? 1 : 0;
138 : }
139 :
140 : /**
141 : * Writes a 16-bit unsigned integer.
142 : * @returns the number of characters successfully written.
143 : */
144 66582 : int WriteTwoBytes(uint32_t value) {
145 66582 : return WriteOneByte(static_cast<byte>(value >> 8)) +
146 66582 : WriteOneByte(static_cast<byte>(value));
147 : }
148 : /**
149 : * Writes a 24-bit unsigned integer.
150 : * @returns the number of characters successfully written.
151 : */
152 2120 : int WriteThreeBytes(uint32_t value) {
153 4240 : return WriteOneByte(static_cast<byte>(value >> 16)) +
154 2120 : WriteOneByte(static_cast<byte>(value >> 8)) +
155 2120 : WriteOneByte(static_cast<byte>(value));
156 : }
157 : /**
158 : * Writes a 32-bit unsigned integer.
159 : * @returns the number of characters successfully written.
160 : */
161 2027 : int WriteFourBytes(uint32_t value) {
162 4054 : return WriteOneByte(static_cast<byte>(value >> 24)) +
163 4054 : WriteOneByte(static_cast<byte>(value >> 16)) +
164 2027 : WriteOneByte(static_cast<byte>(value >> 8)) +
165 2027 : WriteOneByte(static_cast<byte>(value));
166 : }
167 :
168 : private:
169 140604 : template <int nBytes> uint32_t read() {
170 140604 : uint32_t res = 0;
171 : if (nBytes > 3)
172 3028 : res += ReadOneByte() << 24;
173 : if (nBytes > 2)
174 6109 : res += ReadOneByte() << 16;
175 : if (nBytes > 1)
176 140604 : res += ReadOneByte() << 8;
177 140604 : return res + ReadOneByte();
178 : }
179 : };
180 :
181 : /**
182 : * Optimizes std::filebuf for random-access reading.
183 : */
184 22 : class FilebufAppend : protected Filebuf {
185 : std::streamoff fileSz_;
186 : std::streamoff filePos_;
187 :
188 : public:
189 22 : FilebufAppend() : fileSz_(0), filePos_(-1) {}
190 :
191 : /**
192 : * Opens a file and store its size.
193 : * @param filename: path to the file to be opened.
194 : * @param fmode: open the file for reading, writing, or both.
195 : * @returns OK on success, an @e errorT code on failure.
196 : */
197 21 : errorT open(const std::string& filename, fileModeT fmode) {
198 21 : errorT res = Open(filename.c_str(), fmode);
199 21 : if (res != OK)
200 1 : return res;
201 20 : fileSz_ = pubseekoff(0, std::ios::end);
202 20 : if (fileSz_ == -1)
203 0 : return ERROR_FileSeek;
204 20 : return OK;
205 : }
206 :
207 : /**
208 : * Returns the size of the file.
209 : */
210 10030 : unsigned long long size() const { return fileSz_; }
211 :
212 : /**
213 : * Invokes std::filebuf::sync() to write all pending output to the file.
214 : * @returns 0 in case of success, -1 in case of failure.
215 : */
216 6 : int pubsync() { return sync(); }
217 :
218 : /**
219 : * Writes, at the end of the file, @p count characters from the character
220 : * array whose first element is pointed to by @p s.
221 : * @returns OK in case of success, an error code otherwise.
222 : */
223 4805 : errorT append(const char_type* s, std::streamsize count) {
224 4805 : assert(s != 0);
225 :
226 4805 : if (filePos_ != -2) { // Seek to end of file, if necessary.
227 13 : filePos_ = seekpos(fileSz_);
228 13 : if (filePos_ == -1)
229 0 : return ERROR_FileSeek;
230 13 : filePos_ = -2;
231 : }
232 :
233 4805 : std::streamsize n = xsputn(s, count);
234 4805 : fileSz_ += n;
235 4805 : return (n == count) ? OK : ERROR_FileWrite;
236 : }
237 :
238 : /**
239 : * Invoke filebuf::xsgetn() and update @e filePos_.
240 : */
241 6010 : std::streamsize sgetn(char_type* s, std::streamsize count) {
242 6010 : std::streamsize res = xsgetn(s, count);
243 6010 : filePos_ += res;
244 6010 : return res;
245 : }
246 :
247 : /**
248 : * Repositions the internal buffer or invoke filebuf::seekpos().
249 : * In the standard implementation, the buffer must be abandoned to ensure
250 : * consistency when transitioning from reading to writing.
251 : */
252 6010 : std::streamoff pubseekpos(std::streamoff pos) {
253 6010 : if (filePos_ < 0 || pos < filePos_)
254 1022 : return filePos_ = seekpos(pos);
255 :
256 4988 : if (filePos_ != pos) {
257 1606 : const std::streamsize avail = egptr() - gptr();
258 1606 : if (avail >= pos - filePos_) {
259 40 : ASSERT(pos - filePos_ <= INT_MAX);
260 40 : gbump(static_cast<int>(pos - filePos_));
261 40 : filePos_ = pos;
262 : } else {
263 1566 : filePos_ = seekpos(pos);
264 : }
265 : }
266 4988 : return filePos_;
267 : }
268 : };
269 :
270 : #endif // FILEBUF_H
|