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