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