Browse Source

[2572] introduced a special source size of "unknown".

this will help handle special cases like using the lexer with stdin
associated with a pipe.
JINMEI Tatuya 12 years ago
parent
commit
4baf3a4dca

+ 10 - 0
src/lib/dns/master_lexer.cc

@@ -24,12 +24,22 @@
 
 #include <bitset>
 #include <cassert>
+#include <limits>
 #include <string>
 #include <vector>
 
 namespace isc {
 namespace dns {
 
+// The definition of SOURCE_SIZE_UNKNOWN.  Note that we initialize it using
+// a method of another library.  Technically, this could trigger a static
+// initialization fiasco.  But in this particular usage it's very unlikely
+// to happen because this value is expected to be used only as a return
+// value of a MasterLexer's method, and its constructor needs definitions
+// here.
+const size_t MasterLexer::SOURCE_SIZE_UNKNOWN =
+    std::numeric_limits<size_t>::max();
+
 namespace {
 typedef boost::shared_ptr<master_lexer_internal::InputSource> InputSourcePtr;
 } // end unnamed namespace

+ 16 - 0
src/lib/dns/master_lexer.h

@@ -331,6 +331,16 @@ public:
         const MasterToken token_;
     };
 
+    /// \brief Special value for input source size meaning "unknown".
+    ///
+    /// This constant value will be used as a return value of
+    /// \c getTotalSourceSize() when the size of one of the pushed sources
+    /// is unknown.  Note that this value itself is a valid integer in the
+    /// range of the type, so there's still a small possibility of
+    /// ambiguity.  In practice, however, the value should be sufficiently
+    /// large that should eliminate the possibility.
+    static const size_t SOURCE_SIZE_UNKNOWN;
+
     /// \brief Options for getNextToken.
     ///
     /// A compound option, indicating multiple options are set, can be
@@ -458,6 +468,12 @@ public:
     /// the size of the data available in the stream at the time of the
     /// source is pushed.
     ///
+    /// In some special cases, it's possible that the size of the file or
+    /// stream is unknown.  It happens, for example, if the standard input
+    /// is associated with a pipe from the output of another process and it's
+    /// specified as an input source.  If the size of some of the pushed
+    /// pushed source is unknown, this method returns SOURCE_SIZE_UNKNOWN.
+    ///
     /// If there is no source pushed in the lexer, it returns 0.
     ///
     /// \throw None

+ 13 - 2
src/lib/dns/master_lexer_inputsource.cc

@@ -37,16 +37,27 @@ createStreamName(const std::istream& input_stream) {
 size_t
 getStreamSize(std::istream& is) {
     is.seekg(0, std::ios_base::end);
-    if (is.fail() || is.bad()) {
+    if (is.bad()) {
+        // This means the istream has an integrity error.  It doesn't make
+        // sense to continue from this point, so we treat it as a fatal error.
         isc_throw(InputSource::OpenError,
                   "failed to seek end of input source");
+    } else if (is.fail()) {
+        // This is an error specific to seekg().  There can be several
+        // reasons, but the most likely cause in this context is that the
+        // stream is associated with a special type of file such as a pipe.
+        // In this case, it's more likely that other main operations of
+        // the input source work fine, so we continue with just setting
+        // the stream size to "unknown".
+        is.clear();   // clear this error not to confuse later ops.
+        return (MasterLexer::SOURCE_SIZE_UNKNOWN);
     }
     const std::streampos len = is.tellg();
     if (len == -1) {
         isc_throw(InputSource::OpenError, "failed to get input size");
     }
     is.seekg(0, std::ios::beg);
-    if (is.fail() || is.bad()) {
+    if (is.fail()) {
         isc_throw(InputSource::OpenError,
                   "failed to seek beginning of input source");
     }

+ 3 - 1
src/lib/dns/master_lexer_inputsource.h

@@ -89,8 +89,10 @@ public:
 
     /// \brief Returns the size of the input source in bytes.
     ///
+    /// If the size is unknown, it returns \c MasterLexer::SOURCE_SIZE_UNKNOWN.
+    ///
     /// See \c MasterLexer::getTotalSourceSize() for the definition of
-    /// the size of sources.
+    /// the size of sources and for when the size can be unknown.
     ///
     /// \throw None
     size_t getSize() const { return (input_size_); }

+ 10 - 2
src/lib/dns/tests/master_lexer_inputsource_unittest.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <dns/master_lexer_inputsource.h>
+#include <dns/master_lexer.h>
 #include <exceptions/exceptions.h>
 
 #include <gtest/gtest.h>
@@ -346,9 +347,16 @@ TEST_F(InputSourceTest, getSize) {
     istringstream iss("");
     EXPECT_EQ(0, InputSource(iss).getSize());
 
-    // Pretend there's an error in the stream.  The constructor will throw
-    // in the attempt of getting the input size.
+    // Pretend there's an error in seeking in the stream.  It will be
+    // considered a seek specific error, and getSize() returns "unknown".
     iss.setstate(std::ios_base::failbit);
+    EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, InputSource(iss).getSize());
+    // The fail bit should have been cleared.
+    EXPECT_FALSE(iss.fail());
+
+    // Pretend there's a *critical* error in the stream.  The constructor will
+    // throw in the attempt of getting the input size.
+    iss.setstate(std::ios_base::badbit);
     EXPECT_THROW(InputSource isrc(iss), InputSource::OpenError);
 
     // Check with input source from file name.  We hardcode the file size