Author: mordante
Date: Sat Mar 24 09:28:27 2012
New Revision: 53624

URL: http://svn.gna.org/viewcvs/wesnoth?rev=53624&view=rev
Log:
Add a new buffered std::istream wrapper.

This class increases the speed of the starting up code. The code is
game.cpp:374 -- 442. After converting the preprocessor and tokenizer to
use this new class the time of that part of the code went from 978 ms to
866 ms.

Added:
    trunk/src/buffered_istream.hpp   (with props)

Added: trunk/src/buffered_istream.hpp
URL: 
http://svn.gna.org/viewcvs/wesnoth/trunk/src/buffered_istream.hpp?rev=53624&view=auto
==============================================================================
--- trunk/src/buffered_istream.hpp (added)
+++ trunk/src/buffered_istream.hpp Sat Mar 24 09:28:27 2012
@@ -1,0 +1,174 @@
+/* $Id$ */
+/*
+   Copyright (C) 2012 by Mark de Wever <[email protected]>
+   Part of the Battle for Wesnoth Project http://www.wesnoth.org/
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY.
+
+   See the COPYING file for more details.
+*/
+
+/**
+ * @file
+ * Helper class for buffering a @ref std::istream.
+ */
+
+#ifndef BUFFERED_ISTREAM_HPP_INCLUDED
+#define BUFFERED_ISTREAM_HPP_INCLUDED
+
+#include "util.hpp"
+
+/**
+ * Helper class for buffering a @ref std::istream.
+ *
+ * This class is used to buffer a @ref std::istream which is used for small
+ * reads; a character at a time. The @ref std::istream needs to create a
+ * sentinel object for every read and profiling showed the @ref std::istream
+ * class was causing a lot of overhead when parsing WML. This class helps by
+ * reading chunks from the @ref std::stream and store them in an internal
+ * buffer. Then the next request can deliver data from this buffer.
+ *
+ * Since the class is only designed for small reads it only offers the @ref
+ * get() and the @ref peek() to get data and @ref eof() to signal the end of
+ * data. The original stream should not be used from, while being owned by this
+ * class.
+ */
+class buffered_istream
+{
+public:
+
+       explicit buffered_istream(std::istream& in)
+               : stream_(in)
+               , buffer_()
+               , buffer_size_(0)
+               , buffer_offset_(0)
+               , eof_(false)
+       {
+       }
+
+       /**
+        * Gets and consumes a character from the buffer.
+        *
+        * @returns                   The character read.
+        * @retval EOF                The end of input has been read.
+        */
+       int get()
+       {
+               fill_buffer();
+
+               if(UNLIKELY(eof_)) {
+                       return EOF;
+               } else {
+                       /*
+                        * The data needs to be casted to an unsigned value 
before it
+                        * is promoted to an int. The char might be signed and 
contain
+                        * a negative value, resulting in a negative result, 
and cause
+                        * problems. (Using gcc on x86 has this issue.)
+                        */
+                       unsigned char c = buffer_[buffer_offset_];
+                       ++buffer_offset_;
+                       return c;
+               }
+       }
+
+       /**
+        * Gets a character from the buffer.
+        *
+        * This version only gets a character, but doesn't consume it.
+        *
+        * @returns                   The character read.
+        * @retval EOF                The end of input has been read.
+        */
+       int peek()
+       {
+               fill_buffer();
+
+               if(UNLIKELY(eof_)) {
+                       return EOF;
+               } else {
+                       /* See get() */
+                       return static_cast<unsigned 
char>(buffer_[buffer_offset_]);
+               }
+       }
+
+       /** Is the end of input reached? */
+       bool eof() const
+       {
+               return eof_;
+       }
+
+       /** Returns the owned stream. */
+       std::istream& stream()
+       {
+               return stream_;
+       }
+
+private:
+
+       /** The input to read from. */
+       std::istream& stream_;
+
+       /**
+        * Buffer to store the data read from @ref std::istream.
+        *
+        * Reading from @ref std::istream isn't to fast, especially not a byte 
at a
+        * time. This buffer is used to buffer x bytes at a time. The size of 
the
+        * buffer is determined experimentally.
+        */
+       char buffer_[1024];
+
+       /**
+        * The size of @ref buffer_.
+        *
+        * When buffering the data there might be less data in the stream as in 
the
+        * buffer. This variable contains the exact size of the buffer. For 
example
+        * the last chunk read from the stream is unlikely to have the same 
size a
+        * @ref buffer_.
+        */
+       unsigned buffer_size_;
+
+       /**
+        * The offset of the current character in the buffer.
+        *
+        * @ref buffer_[buffer_offset_] is the current character, and can be 
peaked
+        * or consumed.
+        *
+        * @note the @ref buffer_offset_ may be beyond the @ref buffer_ so
+        * functions should test before directly using this variable.
+        */
+       unsigned buffer_offset_;
+
+       /** Is the end of input reached? */
+       bool eof_;
+
+       /**
+        * Fills the buffer.
+        *
+        * @warning This function must be called before @ref peek() and @ref 
get()
+        * to make sure the buffer state is valid before accessing it.
+        */
+       void fill_buffer()
+       {
+               if(UNLIKELY(buffer_offset_ >= buffer_size_)) {
+                       /*
+                        * This does not only test for the EOF, but also makes 
sure the
+                        * data is available in the buffer. Without it readsome 
will read
+                        * nothing, after its first call, even if the EOF has 
not been
+                        * reached.
+                        */
+                       if(UNLIKELY(stream_.rdbuf()->sgetc() == EOF)) {
+                               eof_ = true;
+                       } else {
+                               buffer_offset_ = 0;
+                               buffer_size_ = stream_.readsome(buffer_, 
sizeof(buffer_));
+                       }
+               }
+       }
+};
+
+#endif

Propchange: trunk/src/buffered_istream.hpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/src/buffered_istream.hpp
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision


_______________________________________________
Wesnoth-commits mailing list
[email protected]
https://mail.gna.org/listinfo/wesnoth-commits

Reply via email to