The Battle for Wesnoth  1.15.2+dev
buffered_istream.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2012 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Helper class for buffering a @c std::istream.
18  */
19 
20 #pragma once
21 
22 #include <cstdio>
23 #include <sstream>
24 
25 /**
26  * Helper class for buffering a @c std::istream.
27  *
28  * This class is used to buffer a @c std::istream which is used for small
29  * reads; a character at a time. The @c std::istream needs to create a
30  * sentinel object for every read and profiling showed the @c std::istream
31  * class was causing a lot of overhead when parsing WML. This class helps by
32  * reading chunks from the @c std::stream and store them in an internal
33  * buffer. Then the next request can deliver data from this buffer.
34  *
35  * Since the class is only designed for small reads it only offers the @ref
36  * get() and the @ref peek() to get data and @ref eof() to signal the end of
37  * data. The original stream should not be used from, while being owned by this
38  * class.
39  */
41 {
42 public:
43 
44  explicit buffered_istream(std::istream& in)
45  : stream_(in)
46  , buffer_()
47  , buffer_size_(0)
48  , buffer_offset_(0)
49  , eof_(false)
50  {
51  }
52 
53  /**
54  * Gets and consumes a character from the buffer.
55  *
56  * @returns The character read.
57  * @retval EOF The end of input has been read.
58  */
59  int get()
60  {
61  fill_buffer();
62 
63  if(eof_) {
64  return EOF;
65  } else {
66  /*
67  * The data needs to be casted to an unsigned value before it
68  * is promoted to an int. The char might be signed and contain
69  * a negative value, resulting in a negative result, and cause
70  * problems. (Using gcc on x86 has this issue.)
71  */
72  unsigned char c = buffer_[buffer_offset_];
74  return c;
75  }
76  }
77 
78  /**
79  * Gets a character from the buffer.
80  *
81  * This version only gets a character, but doesn't consume it.
82  *
83  * @returns The character read.
84  * @retval EOF The end of input has been read.
85  */
86  int peek()
87  {
88  fill_buffer();
89 
90  if(eof_) {
91  return EOF;
92  } else {
93  /* See get() */
94  return static_cast<unsigned char>(buffer_[buffer_offset_]);
95  }
96  }
97 
98  /** Is the end of input reached? */
99  bool eof() const
100  {
101  return eof_;
102  }
103 
104  /** Returns the owned stream. */
105  std::istream& stream()
106  {
107  return stream_;
108  }
109 
110 private:
111 
112  /** The input to read from. */
113  std::istream& stream_;
114 
115  /**
116  * Buffer to store the data read from @c std::istream.
117  *
118  * Reading from @c std::istream isn't to fast, especially not a byte at a
119  * time. This buffer is used to buffer x bytes at a time. The size of the
120  * buffer is determined experimentally.
121  */
122  char buffer_[1024];
123 
124  /**
125  * The size of @ref buffer_.
126  *
127  * When buffering the data there might be less data in the stream as in the
128  * buffer. This variable contains the exact size of the buffer. For example
129  * the last chunk read from the stream is unlikely to have the same size a
130  * @ref buffer_.
131  */
132  unsigned buffer_size_;
133 
134  /**
135  * The offset of the current character in the buffer.
136  *
137  * @ref buffer_[buffer_offset_] is the current character, and can be peaked
138  * or consumed.
139  *
140  * @note the @ref buffer_offset_ may be beyond the @ref buffer_ so
141  * functions should test before directly using this variable.
142  */
143  unsigned buffer_offset_;
144 
145  /** Is the end of input reached? */
146  bool eof_;
147 
148  /**
149  * Fills the buffer.
150  *
151  * @warning This function must be called before @ref peek() and @ref get()
152  * to make sure the buffer state is valid before accessing it.
153  */
154  void fill_buffer()
155  {
156  if(buffer_offset_ >= buffer_size_) {
157  /*
158  * This does not only test for the EOF, but also makes sure the
159  * data is available in the buffer. Without it readsome will read
160  * nothing, after its first call, even if the EOF has not been
161  * reached.
162  */
163  if(stream_.rdbuf()->sgetc() == EOF) {
164  eof_ = true;
165  } else {
166  buffer_offset_ = 0;
167  buffer_size_ = static_cast<unsigned> (stream_.readsome(buffer_, sizeof(buffer_)));
168  }
169  }
170  }
171 };
unsigned buffer_offset_
The offset of the current character in the buffer.
std::istream & stream_
The input to read from.
void fill_buffer()
Fills the buffer.
int peek()
Gets a character from the buffer.
Helper class for buffering a std::istream.
char buffer_[1024]
Buffer to store the data read from std::istream.
unsigned in
If equal to search_counter, the node is off the list.
buffered_istream(std::istream &in)
unsigned buffer_size_
The size of buffer_.
std::istream & stream()
Returns the owned stream.
mock_char c
bool eof() const
Is the end of input reached?
bool eof_
Is the end of input reached?