Merge branch 'develop' into feature/iterator_range_parsing
diff --git a/src/json.hpp b/src/json.hpp
index ddf37eb..03c1625 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -37,6 +37,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <cstdlib>
+#include <cstring>
 #include <functional>
 #include <initializer_list>
 #include <iomanip>
@@ -3960,7 +3961,7 @@
     @return Iterator following the last removed element. If the iterator @a
     pos refers to the last element, the `end()` iterator is returned.
 
-    @tparam InteratorType an @ref iterator or @ref const_iterator
+    @tparam IteratorType an @ref iterator or @ref const_iterator
 
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
@@ -3982,7 +3983,7 @@
     @liveexample{The example shows the result of `erase()` for different JSON
     types.,erase__IteratorType}
 
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
     the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
@@ -3991,13 +3992,13 @@
 
     @since version 1.0.0
     */
-    template <class InteratorType, typename
+    template <class IteratorType, typename
               std::enable_if<
-                  std::is_same<InteratorType, typename basic_json_t::iterator>::value or
-                  std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+                  std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+                  std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
                   , int>::type
               = 0>
-    InteratorType erase(InteratorType pos)
+    IteratorType erase(IteratorType pos)
     {
         // make sure iterator fits the current value
         if (this != pos.m_object)
@@ -4005,7 +4006,7 @@
             throw std::domain_error("iterator does not fit current value");
         }
 
-        InteratorType result = end();
+        IteratorType result = end();
 
         switch (m_type)
         {
@@ -4069,7 +4070,7 @@
     @return Iterator following the last removed element. If the iterator @a
     second refers to the last element, the `end()` iterator is returned.
 
-    @tparam InteratorType an @ref iterator or @ref const_iterator
+    @tparam IteratorType an @ref iterator or @ref const_iterator
 
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
@@ -4092,7 +4093,7 @@
     @liveexample{The example shows the result of `erase()` for different JSON
     types.,erase__IteratorType_IteratorType}
 
-    @sa @ref erase(InteratorType) -- removes the element at a given position
+    @sa @ref erase(IteratorType) -- removes the element at a given position
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
     @sa @ref erase(const size_type) -- removes the element from an array at
@@ -4100,13 +4101,13 @@
 
     @since version 1.0.0
     */
-    template <class InteratorType, typename
+    template <class IteratorType, typename
               std::enable_if<
-                  std::is_same<InteratorType, typename basic_json_t::iterator>::value or
-                  std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+                  std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+                  std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
                   , int>::type
               = 0>
-    InteratorType erase(InteratorType first, InteratorType last)
+    IteratorType erase(IteratorType first, IteratorType last)
     {
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
@@ -4114,7 +4115,7 @@
             throw std::domain_error("iterators do not fit current value");
         }
 
-        InteratorType result = end();
+        IteratorType result = end();
 
         switch (m_type)
         {
@@ -4186,8 +4187,8 @@
 
     @liveexample{The example shows the effect of `erase()`.,erase__key_type}
 
-    @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    @sa @ref erase(IteratorType) -- removes the element at a given position
+    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
     the given range
     @sa @ref erase(const size_type) -- removes the element from an array at
     the given index
@@ -4223,8 +4224,8 @@
 
     @liveexample{The example shows the effect of `erase()`.,erase__size_type}
 
-    @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    @sa @ref erase(IteratorType) -- removes the element at a given position
+    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
     the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
@@ -7504,32 +7505,25 @@
         /// the char type to use in the lexer
         using lexer_char_t = unsigned char;
 
-        /// constructor with a given buffer
-        explicit lexer(const string_t& s) noexcept
-            : m_stream(nullptr), m_buffer(s)
+        /// a lexer from a buffer with given length
+        lexer(const lexer_char_t* buff, const size_t len) noexcept
+            : m_content(buff)
         {
-            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
             m_start = m_cursor = m_content;
-            m_limit = m_content + s.size();
+            m_limit = m_content + len;
         }
 
-        /// constructor with a given stream
-        explicit lexer(std::istream* s) noexcept
-            : m_stream(s), m_buffer()
+        /// a lexer from an input stream
+        explicit lexer(std::istream& s)
+            : m_stream(&s), m_line_buffer()
         {
-            assert(m_stream != nullptr);
-            std::getline(*m_stream, m_buffer);
-            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
-            assert(m_content != nullptr);
-            m_start = m_cursor = m_content;
-            m_limit = m_content + m_buffer.size();
+            // fill buffer
+            fill_line_buffer();
         }
 
-        /// default constructor
-        lexer() = default;
-
         // switch off unwanted functions
+        lexer() = delete;
         lexer(const lexer&) = delete;
         lexer operator=(const lexer&) = delete;
 
@@ -7682,7 +7676,7 @@
         infinite sequence of whitespace or byte-order-marks. This contradicts
         the assumption of finite input, q.e.d.
         */
-        token_type scan() noexcept
+        token_type scan()
         {
             while (true)
             {
@@ -7734,7 +7728,7 @@
                     };
                     if ((m_limit - m_cursor) < 5)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yybm[0 + yych] & 32)
@@ -7868,7 +7862,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yybm[0 + yych] & 32)
@@ -7938,7 +7932,7 @@
                     m_marker = ++m_cursor;
                     if ((m_limit - m_cursor) < 3)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yybm[0 + yych] & 64)
@@ -8031,7 +8025,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
 basic_json_parser_32:
@@ -8068,7 +8062,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= 'e')
@@ -8212,7 +8206,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= '@')
@@ -8248,7 +8242,7 @@
                     m_marker = ++m_cursor;
                     if ((m_limit - m_cursor) < 3)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= 'D')
@@ -8289,7 +8283,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= '/')
@@ -8331,7 +8325,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= '@')
@@ -8385,7 +8379,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= '@')
@@ -8426,7 +8420,7 @@
                     ++m_cursor;
                     if (m_limit <= m_cursor)
                     {
-                        yyfill();    // LCOV_EXCL_LINE;
+                        fill_line_buffer();    // LCOV_EXCL_LINE;
                     }
                     yych = *m_cursor;
                     if (yych <= '@')
@@ -8464,30 +8458,57 @@
             return last_token_type;
         }
 
-        /// append data from the stream to the internal buffer
-        void yyfill() noexcept
+        /*!
+        @brief append data from the stream to the line buffer
+
+        This function is called by the scan() function when the end of the
+        buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
+        incremented without leaving the limits of the line buffer. Note re2c
+        decides when to call this function.
+
+        @pre
+            p p p p p p u u u u u x . . . . . .
+            ^           ^       ^   ^
+            m_content   m_start |   m_limit
+                                m_cursor
+
+        @post
+            u u u u u x x x x x x x . . . . . .
+            ^       ^               ^
+            |       m_cursor        m_limit
+            m_start
+            m_content
+        */
+        void fill_line_buffer()
         {
+            // no stream is used or end of file is reached
             if (m_stream == nullptr or not * m_stream)
             {
                 return;
             }
 
+            // number of processed characters (p)
             const auto offset_start = m_start - m_content;
+            // offset for m_marker wrt. to m_start
             const auto offset_marker = m_marker - m_start;
+            // number of unprocessed characters (u)
             const auto offset_cursor = m_cursor - m_start;
 
-            m_buffer.erase(0, static_cast<size_t>(offset_start));
+            // delete processed characters from line buffer
+            m_line_buffer.erase(0, static_cast<size_t>(offset_start));
+            // read next line from input stream
             std::string line;
-            assert(m_stream != nullptr);
             std::getline(*m_stream, line);
-            m_buffer += "\n" + line; // add line with newline symbol
+            // add line with newline symbol to the line buffer
+            m_line_buffer += "\n" + line;
 
-            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
+            // set pointers
+            m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str());
             assert(m_content != nullptr);
             m_start  = m_content;
             m_marker = m_start + offset_marker;
             m_cursor = m_start + offset_cursor;
-            m_limit  = m_start + m_buffer.size() - 1;
+            m_limit  = m_start + m_line_buffer.size() - 1;
         }
 
         /// return string representation of last read token
@@ -8829,8 +8850,8 @@
       private:
         /// optional input stream
         std::istream* m_stream = nullptr;
-        /// the buffer
-        string_t m_buffer;
+        /// line buffer buffer for m_stream
+        string_t m_line_buffer {};
         /// the buffer pointer
         const lexer_char_t* m_content = nullptr;
         /// pointer to the beginning of the current symbol
@@ -8853,25 +8874,48 @@
     class parser
     {
       public:
-        /// constructor for strings
-        parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
-            : callback(cb), m_lexer(s)
-        {
-            // read first token
-            get_token();
-        }
+        /// a parser reading from a string literal
+        parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr)
+            : callback(cb),
+              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff),
+                      strlen(buff))
+        {}
+
+        /// a parser reading from a string container
+        parser(const string_t& s, const parser_callback_t cb = nullptr)
+            : callback(cb),
+              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(s.c_str()), s.size())
+        {}
 
         /// a parser reading from an input stream
-        parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
-            : callback(cb), m_lexer(&_is)
+        parser(std::istream& is, const parser_callback_t cb = nullptr)
+            : callback(cb), m_lexer(is)
+        {}
+
+        /// a parser reading from a container with contiguous storage
+        template <class IteratorType, typename
+                  std::enable_if<
+                      std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
+                      , int>::type
+                  = 0>
+        parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
+            : callback(cb),
+              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
+                      static_cast<size_t>(std::distance(first, last)))
         {
-            // read first token
-            get_token();
+            int i = 0;
+            assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val)
+            {
+                return res and (val == *(std::next(std::addressof(*first), i++)));
+            }));
         }
 
         /// public parser interface
         basic_json parse()
         {
+            // read first token
+            get_token();
+
             basic_json result = parse_internal(true);
             result.assert_invariant();
 
@@ -9076,7 +9120,7 @@
         }
 
         /// get next token from lexer
-        typename lexer::token_type get_token() noexcept
+        typename lexer::token_type get_token()
         {
             last_token = m_lexer.scan();
             return last_token;
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 740c768..58724e9 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -37,6 +37,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <cstdlib>
+#include <cstring>
 #include <functional>
 #include <initializer_list>
 #include <iomanip>
@@ -3960,7 +3961,7 @@
     @return Iterator following the last removed element. If the iterator @a
     pos refers to the last element, the `end()` iterator is returned.
 
-    @tparam InteratorType an @ref iterator or @ref const_iterator
+    @tparam IteratorType an @ref iterator or @ref const_iterator
 
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
@@ -3982,7 +3983,7 @@
     @liveexample{The example shows the result of `erase()` for different JSON
     types.,erase__IteratorType}
 
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
     the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
@@ -3991,13 +3992,13 @@
 
     @since version 1.0.0
     */
-    template <class InteratorType, typename
+    template <class IteratorType, typename
               std::enable_if<
-                  std::is_same<InteratorType, typename basic_json_t::iterator>::value or
-                  std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+                  std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+                  std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
                   , int>::type
               = 0>
-    InteratorType erase(InteratorType pos)
+    IteratorType erase(IteratorType pos)
     {
         // make sure iterator fits the current value
         if (this != pos.m_object)
@@ -4005,7 +4006,7 @@
             throw std::domain_error("iterator does not fit current value");
         }
 
-        InteratorType result = end();
+        IteratorType result = end();
 
         switch (m_type)
         {
@@ -4069,7 +4070,7 @@
     @return Iterator following the last removed element. If the iterator @a
     second refers to the last element, the `end()` iterator is returned.
 
-    @tparam InteratorType an @ref iterator or @ref const_iterator
+    @tparam IteratorType an @ref iterator or @ref const_iterator
 
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
@@ -4092,7 +4093,7 @@
     @liveexample{The example shows the result of `erase()` for different JSON
     types.,erase__IteratorType_IteratorType}
 
-    @sa @ref erase(InteratorType) -- removes the element at a given position
+    @sa @ref erase(IteratorType) -- removes the element at a given position
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
     @sa @ref erase(const size_type) -- removes the element from an array at
@@ -4100,13 +4101,13 @@
 
     @since version 1.0.0
     */
-    template <class InteratorType, typename
+    template <class IteratorType, typename
               std::enable_if<
-                  std::is_same<InteratorType, typename basic_json_t::iterator>::value or
-                  std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+                  std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+                  std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
                   , int>::type
               = 0>
-    InteratorType erase(InteratorType first, InteratorType last)
+    IteratorType erase(IteratorType first, IteratorType last)
     {
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
@@ -4114,7 +4115,7 @@
             throw std::domain_error("iterators do not fit current value");
         }
 
-        InteratorType result = end();
+        IteratorType result = end();
 
         switch (m_type)
         {
@@ -4186,8 +4187,8 @@
 
     @liveexample{The example shows the effect of `erase()`.,erase__key_type}
 
-    @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    @sa @ref erase(IteratorType) -- removes the element at a given position
+    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
     the given range
     @sa @ref erase(const size_type) -- removes the element from an array at
     the given index
@@ -4223,8 +4224,8 @@
 
     @liveexample{The example shows the effect of `erase()`.,erase__size_type}
 
-    @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    @sa @ref erase(IteratorType) -- removes the element at a given position
+    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
     the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
@@ -7504,32 +7505,25 @@
         /// the char type to use in the lexer
         using lexer_char_t = unsigned char;
 
-        /// constructor with a given buffer
-        explicit lexer(const string_t& s) noexcept
-            : m_stream(nullptr), m_buffer(s)
+        /// a lexer from a buffer with given length
+        lexer(const lexer_char_t* buff, const size_t len) noexcept
+            : m_content(buff)
         {
-            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
             m_start = m_cursor = m_content;
-            m_limit = m_content + s.size();
+            m_limit = m_content + len;
         }
 
-        /// constructor with a given stream
-        explicit lexer(std::istream* s) noexcept
-            : m_stream(s), m_buffer()
+        /// a lexer from an input stream
+        explicit lexer(std::istream& s)
+            : m_stream(&s), m_line_buffer()
         {
-            assert(m_stream != nullptr);
-            std::getline(*m_stream, m_buffer);
-            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
-            assert(m_content != nullptr);
-            m_start = m_cursor = m_content;
-            m_limit = m_content + m_buffer.size();
+            // fill buffer
+            fill_line_buffer();
         }
 
-        /// default constructor
-        lexer() = default;
-
         // switch off unwanted functions
+        lexer() = delete;
         lexer(const lexer&) = delete;
         lexer operator=(const lexer&) = delete;
 
@@ -7682,7 +7676,7 @@
         infinite sequence of whitespace or byte-order-marks. This contradicts
         the assumption of finite input, q.e.d.
         */
-        token_type scan() noexcept
+        token_type scan()
         {
             while (true)
             {
@@ -7698,7 +7692,7 @@
                     re2c:define:YYCURSOR  = m_cursor;
                     re2c:define:YYLIMIT   = m_limit;
                     re2c:define:YYMARKER  = m_marker;
-                    re2c:define:YYFILL    = "yyfill(); // LCOV_EXCL_LINE";
+                    re2c:define:YYFILL    = "fill_line_buffer(); // LCOV_EXCL_LINE";
                     re2c:yyfill:parameter = 0;
                     re2c:indent:string    = "    ";
                     re2c:indent:top       = 1;
@@ -7761,30 +7755,57 @@
             return last_token_type;
         }
 
-        /// append data from the stream to the internal buffer
-        void yyfill() noexcept
+        /*!
+        @brief append data from the stream to the line buffer
+
+        This function is called by the scan() function when the end of the
+        buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
+        incremented without leaving the limits of the line buffer. Note re2c
+        decides when to call this function.
+
+        @pre
+            p p p p p p u u u u u x . . . . . .
+            ^           ^       ^   ^
+            m_content   m_start |   m_limit
+                                m_cursor
+
+        @post
+            u u u u u x x x x x x x . . . . . .
+            ^       ^               ^
+            |       m_cursor        m_limit
+            m_start
+            m_content
+        */
+        void fill_line_buffer()
         {
+            // no stream is used or end of file is reached
             if (m_stream == nullptr or not * m_stream)
             {
                 return;
             }
 
+            // number of processed characters (p)
             const auto offset_start = m_start - m_content;
+            // offset for m_marker wrt. to m_start
             const auto offset_marker = m_marker - m_start;
+            // number of unprocessed characters (u)
             const auto offset_cursor = m_cursor - m_start;
 
-            m_buffer.erase(0, static_cast<size_t>(offset_start));
+            // delete processed characters from line buffer
+            m_line_buffer.erase(0, static_cast<size_t>(offset_start));
+            // read next line from input stream
             std::string line;
-            assert(m_stream != nullptr);
             std::getline(*m_stream, line);
-            m_buffer += "\n" + line; // add line with newline symbol
+            // add line with newline symbol to the line buffer
+            m_line_buffer += "\n" + line;
 
-            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
+            // set pointers
+            m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str());
             assert(m_content != nullptr);
             m_start  = m_content;
             m_marker = m_start + offset_marker;
             m_cursor = m_start + offset_cursor;
-            m_limit  = m_start + m_buffer.size() - 1;
+            m_limit  = m_start + m_line_buffer.size() - 1;
         }
 
         /// return string representation of last read token
@@ -8126,8 +8147,8 @@
       private:
         /// optional input stream
         std::istream* m_stream = nullptr;
-        /// the buffer
-        string_t m_buffer;
+        /// line buffer buffer for m_stream
+        string_t m_line_buffer {};
         /// the buffer pointer
         const lexer_char_t* m_content = nullptr;
         /// pointer to the beginning of the current symbol
@@ -8150,25 +8171,48 @@
     class parser
     {
       public:
-        /// constructor for strings
-        parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
-            : callback(cb), m_lexer(s)
-        {
-            // read first token
-            get_token();
-        }
+        /// a parser reading from a string literal
+        parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr)
+            : callback(cb),
+              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff),
+                      strlen(buff))
+        {}
+
+        /// a parser reading from a string container
+        parser(const string_t& s, const parser_callback_t cb = nullptr)
+            : callback(cb),
+              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(s.c_str()), s.size())
+        {}
 
         /// a parser reading from an input stream
-        parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
-            : callback(cb), m_lexer(&_is)
+        parser(std::istream& is, const parser_callback_t cb = nullptr)
+            : callback(cb), m_lexer(is)
+        {}
+
+        /// a parser reading from a container with contiguous storage
+        template <class IteratorType, typename
+                  std::enable_if<
+                      std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
+                      , int>::type
+                  = 0>
+        parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
+            : callback(cb),
+              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
+                      static_cast<size_t>(std::distance(first, last)))
         {
-            // read first token
-            get_token();
+            int i = 0;
+            assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val)
+            {
+                return res and (val == *(std::next(std::addressof(*first), i++)));
+            }));
         }
 
         /// public parser interface
         basic_json parse()
         {
+            // read first token
+            get_token();
+
             basic_json result = parse_internal(true);
             result.assert_invariant();
 
@@ -8373,7 +8417,7 @@
         }
 
         /// get next token from lexer
-        typename lexer::token_type get_token() noexcept
+        typename lexer::token_type get_token()
         {
             last_token = m_lexer.scan();
             return last_token;
diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp
index 708a8cb..7cece21 100644
--- a/test/src/unit-class_lexer.cpp
+++ b/test/src/unit-class_lexer.cpp
@@ -38,43 +38,67 @@
     {
         SECTION("structural characters")
         {
-            CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array);
-            CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array);
-            CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object);
-            CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object);
-            CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator);
-            CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
+                              1).scan() == json::lexer::token_type::begin_array);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
+                              1).scan() == json::lexer::token_type::end_array);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
+                              1).scan() == json::lexer::token_type::begin_object);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
+                              1).scan() == json::lexer::token_type::end_object);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
+                              1).scan() == json::lexer::token_type::value_separator);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
+                              1).scan() == json::lexer::token_type::name_separator);
         }
 
         SECTION("literal names")
         {
-            CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null);
-            CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true);
-            CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
+                              4).scan() == json::lexer::token_type::literal_null);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
+                              4).scan() == json::lexer::token_type::literal_true);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
+                              5).scan() == json::lexer::token_type::literal_false);
         }
 
         SECTION("numbers")
         {
-            CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number);
-            CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
+                              1).scan() == json::lexer::token_type::value_number);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
+                              1).scan() == json::lexer::token_type::value_number);
         }
 
         SECTION("whitespace")
         {
             // result is end_of_input, because not token is following
-            CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input);
-            CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input);
-            CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input);
-            CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input);
-            CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
+                              1).scan() == json::lexer::token_type::end_of_input);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
+                              1).scan() == json::lexer::token_type::end_of_input);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
+                              1).scan() == json::lexer::token_type::end_of_input);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
+                              1).scan() == json::lexer::token_type::end_of_input);
+            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
+                              7).scan() == json::lexer::token_type::end_of_input);
         }
     }
 
@@ -100,7 +124,11 @@
     {
         for (int c = 1; c < 128; ++c)
         {
-            auto s = std::string(1, c);
+            // create string from the ASCII code
+            const auto s = std::string(1, c);
+            // store scan() result
+            const auto res = json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(s.c_str()),
+                                         1).scan();
 
             switch (c)
             {
@@ -122,7 +150,7 @@
                 case ('8'):
                 case ('9'):
                 {
-                    CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error);
+                    CHECK(res != json::lexer::token_type::parse_error);
                     break;
                 }
 
@@ -132,14 +160,14 @@
                 case ('\n'):
                 case ('\r'):
                 {
-                    CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input);
+                    CHECK(res == json::lexer::token_type::end_of_input);
                     break;
                 }
 
                 // anything else is not expected
                 default:
                 {
-                    CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error);
+                    CHECK(res == json::lexer::token_type::parse_error);
                     break;
                 }
             }
diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp
index fe00550..b6d6a61 100644
--- a/test/src/unit-class_parser.cpp
+++ b/test/src/unit-class_parser.cpp
@@ -32,6 +32,8 @@
 #include "json.hpp"
 using nlohmann::json;
 
+#include <valarray>
+
 TEST_CASE("parser class")
 {
     SECTION("parse")
@@ -743,11 +745,47 @@
         }
     }
 
-    SECTION("copy constructor")
+    SECTION("constructing from contiguous containers")
     {
-        json::string_t* s = new json::string_t("[1,2,3,4]");
-        json::parser p(*s);
-        delete s;
-        CHECK(p.parse() == json({1, 2, 3, 4}));
+        SECTION("from std::vector")
+        {
+            std::vector<uint8_t> v = {'t', 'r', 'u', 'e', '\0'};
+            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+        }
+
+        SECTION("from std::array")
+        {
+            std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e', '\0'} };
+            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+        }
+
+        SECTION("from array")
+        {
+            uint8_t v[] = {'t', 'r', 'u', 'e'};
+            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+        }
+
+        SECTION("from char literal")
+        {
+            CHECK(json::parser("true").parse() == json(true));
+        }
+
+        SECTION("from std::string")
+        {
+            std::string v = {'t', 'r', 'u', 'e'};
+            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+        }
+
+        SECTION("from std::initializer_list")
+        {
+            std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e', '\0'};
+            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+        }
+
+        SECTION("from std::valarray")
+        {
+            std::valarray<uint8_t> v = {'t', 'r', 'u', 'e', '\0'};
+            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+        }
     }
 }