]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/s3select/include/csvparser/csv.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / s3select / include / csvparser / csv.h
diff --git a/ceph/src/s3select/include/csvparser/csv.h b/ceph/src/s3select/include/csvparser/csv.h
new file mode 100644 (file)
index 0000000..c5cb5bc
--- /dev/null
@@ -0,0 +1,1273 @@
+// Copyright: (2012-2015) Ben Strasser <code@ben-strasser.net>
+// License: BSD-3
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CSV_H
+#define CSV_H
+
+#include <vector>
+#include <string>
+#include <cstring>
+#include <algorithm>
+#include <utility>
+#include <cstdio>
+#include <exception>
+#ifndef CSV_IO_NO_THREAD
+#include <mutex>
+#include <thread>
+#include <condition_variable>
+#endif
+#include <memory>
+#include <cassert>
+#include <cerrno>
+#include <istream>
+#include <limits>
+
+namespace io{
+        ////////////////////////////////////////////////////////////////////////////
+        //                                 LineReader                             //
+        ////////////////////////////////////////////////////////////////////////////
+
+        namespace error{
+                struct base : std::exception{
+                        virtual void format_error_message()const = 0;
+
+                        const char*what()const noexcept override{
+                                format_error_message();
+                                return error_message_buffer;
+                        }
+
+                        mutable char error_message_buffer[512];
+                };
+
+                const int max_file_name_length = 255;
+
+                struct with_file_name{
+                        with_file_name(){
+                                std::memset(file_name, 0, sizeof(file_name));
+                        }
+
+                        void set_file_name(const char*file_name){
+                                if(file_name != nullptr){
+                                        // This call to strncpy has parenthesis around it
+                                        // to silence the GCC -Wstringop-truncation warning
+                                        (strncpy(this->file_name, file_name, sizeof(this->file_name)));
+                                        this->file_name[sizeof(this->file_name)-1] = '\0';
+                                }else{
+                                        this->file_name[0] = '\0';
+                                }
+                        }
+
+                        char file_name[max_file_name_length+1];
+                };
+
+                struct with_file_line{
+                        with_file_line(){
+                                file_line = -1;
+                        }
+
+                        void set_file_line(int file_line){
+                                this->file_line = file_line;
+                        }
+
+                        int file_line;
+                };
+
+                struct with_errno{
+                        with_errno(){
+                                errno_value = 0;
+                        }
+
+                        void set_errno(int errno_value){
+                                this->errno_value = errno_value;
+                        }
+
+                        int errno_value;
+                };
+
+                struct can_not_open_file :
+                        base,
+                        with_file_name,
+                        with_errno{
+                        void format_error_message()const override{
+                                if(errno_value != 0)
+                                        std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                                "Can not open file \"%s\" because \"%s\"."
+                                                , file_name, std::strerror(errno_value));
+                                else
+                                        std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                                "Can not open file \"%s\"."
+                                                , file_name);
+                        }
+                };
+
+                struct line_length_limit_exceeded :
+                        base,
+                        with_file_name,
+                        with_file_line{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        "Line number %d in file \"%s\" exceeds the maximum length of 2^24-1."
+                                        , file_line, file_name);
+                        }
+                };
+        }
+
+        class ByteSourceBase{
+        public:
+                virtual int read(char*buffer, int size)=0;
+                virtual ~ByteSourceBase(){}
+        };
+
+        namespace detail{
+
+                class OwningStdIOByteSourceBase : public ByteSourceBase{
+                public:
+                        explicit OwningStdIOByteSourceBase(FILE*file):file(file){
+                                // Tell the std library that we want to do the buffering ourself.
+                                std::setvbuf(file, 0, _IONBF, 0);
+                        }
+
+                        int read(char*buffer, int size){
+                                return std::fread(buffer, 1, size, file);
+                        }
+
+                        ~OwningStdIOByteSourceBase(){
+                                std::fclose(file);
+                        }
+
+                private:
+                        FILE*file;
+                };
+
+                class NonOwningIStreamByteSource : public ByteSourceBase{
+                public:
+                        explicit NonOwningIStreamByteSource(std::istream&in):in(in){}
+
+                        int read(char*buffer, int size){
+                                in.read(buffer, size);
+                                return in.gcount();
+                        }
+
+                        ~NonOwningIStreamByteSource(){}
+
+                private:
+                       std::istream&in;
+                };
+
+                class NonOwningStringByteSource : public ByteSourceBase{
+                public:
+                        NonOwningStringByteSource(const char*str, long long size):str(str), remaining_byte_count(size){}
+
+                        int read(char*buffer, int desired_byte_count){
+                                int to_copy_byte_count = desired_byte_count;
+                                if(remaining_byte_count < to_copy_byte_count)
+                                        to_copy_byte_count = remaining_byte_count;
+                                std::memcpy(buffer, str, to_copy_byte_count);
+                                remaining_byte_count -= to_copy_byte_count;
+                                str += to_copy_byte_count;
+                                return to_copy_byte_count;
+                        }
+
+                        ~NonOwningStringByteSource(){}
+
+                private:
+                        const char*str;
+                        long long remaining_byte_count;
+                };
+
+                #ifndef CSV_IO_NO_THREAD
+                class AsynchronousReader{
+                public:
+                        void init(std::unique_ptr<ByteSourceBase>arg_byte_source){
+                                std::unique_lock<std::mutex>guard(lock);
+                                byte_source = std::move(arg_byte_source);
+                                desired_byte_count = -1;
+                                termination_requested = false;
+                                worker = std::thread(
+                                        [&]{
+                                                std::unique_lock<std::mutex>guard(lock);
+                                                try{
+                                                        for(;;){
+                                                                read_requested_condition.wait(
+                                                                        guard,
+                                                                        [&]{
+                                                                                return desired_byte_count != -1 || termination_requested;
+                                                                        }
+                                                                );
+                                                                if(termination_requested)
+                                                                        return;
+
+                                                                read_byte_count = byte_source->read(buffer, desired_byte_count);
+                                                                desired_byte_count = -1;
+                                                                if(read_byte_count == 0)
+                                                                        break;
+                                                                read_finished_condition.notify_one();
+                                                        }
+                                                }catch(...){
+                                                        read_error = std::current_exception();
+                                                }
+                                                read_finished_condition.notify_one();
+                                        }
+                                );
+                        }
+
+                        bool is_valid()const{
+                                return byte_source != nullptr;
+                        }
+
+                        void start_read(char*arg_buffer, int arg_desired_byte_count){
+                                std::unique_lock<std::mutex>guard(lock);
+                                buffer = arg_buffer;
+                                desired_byte_count = arg_desired_byte_count;
+                                read_byte_count = -1;
+                                read_requested_condition.notify_one();
+                        }
+
+                        int finish_read(){
+                                std::unique_lock<std::mutex>guard(lock);
+                                read_finished_condition.wait(
+                                        guard,
+                                        [&]{
+                                                return read_byte_count != -1 || read_error;
+                                        }
+                                );
+                                if(read_error)
+                                        std::rethrow_exception(read_error);
+                                else
+                                        return read_byte_count;
+                        }
+
+                        ~AsynchronousReader(){
+                                if(byte_source != nullptr){
+                                        {
+                                                std::unique_lock<std::mutex>guard(lock);
+                                                termination_requested = true;
+                                        }
+                                        read_requested_condition.notify_one();
+                                        worker.join();
+                                }
+                        }
+
+                private:
+                        std::unique_ptr<ByteSourceBase>byte_source;
+
+                        std::thread worker;
+
+                        bool termination_requested;
+                        std::exception_ptr read_error;
+                        char*buffer;
+                        int desired_byte_count;
+                        int read_byte_count;
+
+                        std::mutex lock;
+                        std::condition_variable read_finished_condition;
+                        std::condition_variable read_requested_condition;
+                };
+                #endif
+
+                class SynchronousReader{
+                public:
+                        void init(std::unique_ptr<ByteSourceBase>arg_byte_source){
+                                byte_source = std::move(arg_byte_source);
+                        }
+
+                        bool is_valid()const{
+                                return byte_source != nullptr;
+                        }
+
+                        void start_read(char*arg_buffer, int arg_desired_byte_count){
+                                buffer = arg_buffer;
+                                desired_byte_count = arg_desired_byte_count;
+                        }
+
+                        int finish_read(){
+                                return byte_source->read(buffer, desired_byte_count);
+                        }
+                private:
+                        std::unique_ptr<ByteSourceBase>byte_source;
+                        char*buffer;
+                        int desired_byte_count;
+                };
+        }
+
+        class LineReader{
+        private:
+                static const int block_len = 1<<20;
+                std::unique_ptr<char[]>buffer; // must be constructed before (and thus destructed after) the reader!
+                #ifdef CSV_IO_NO_THREAD
+                detail::SynchronousReader reader;
+                #else
+                detail::AsynchronousReader reader;
+                #endif
+                int data_begin;
+                int data_end;
+
+                char file_name[error::max_file_name_length+1];
+                unsigned file_line;
+
+                static std::unique_ptr<ByteSourceBase> open_file(const char*file_name){
+                        // We open the file in binary mode as it makes no difference under *nix
+                        // and under Windows we handle \r\n newlines ourself.
+                        FILE*file = std::fopen(file_name, "rb");
+                        if(file == 0){
+                                int x = errno; // store errno as soon as possible, doing it after constructor call can fail.
+                                error::can_not_open_file err;
+                                err.set_errno(x);
+                                err.set_file_name(file_name);
+                                throw err;
+                        }
+                        return std::unique_ptr<ByteSourceBase>(new detail::OwningStdIOByteSourceBase(file));
+                }
+
+                void init(std::unique_ptr<ByteSourceBase>byte_source){
+                        file_line = 0;
+
+                        buffer = std::unique_ptr<char[]>(new char[3*block_len]);
+                        data_begin = 0;
+                        data_end = byte_source->read(buffer.get(), 2*block_len);
+
+                        // Ignore UTF-8 BOM
+                        if(data_end >= 3 && buffer[0] == '\xEF' && buffer[1] == '\xBB' && buffer[2] == '\xBF')
+                                data_begin = 3;
+
+                        if(data_end == 2*block_len){
+                                reader.init(std::move(byte_source));
+                                reader.start_read(buffer.get() + 2*block_len, block_len);
+                        }
+                }
+
+        public:
+                LineReader() = delete;
+                LineReader(const LineReader&) = delete;
+                LineReader&operator=(const LineReader&) = delete;
+
+                explicit LineReader(const char*file_name){
+                        set_file_name(file_name);
+                        init(open_file(file_name));
+                }
+
+                explicit LineReader(const std::string&file_name){
+                        set_file_name(file_name.c_str());
+                        init(open_file(file_name.c_str()));
+                }
+
+                LineReader(const char*file_name, std::unique_ptr<ByteSourceBase>byte_source){
+                        set_file_name(file_name);
+                        init(std::move(byte_source));
+                }
+
+                LineReader(const std::string&file_name, std::unique_ptr<ByteSourceBase>byte_source){
+                        set_file_name(file_name.c_str());
+                        init(std::move(byte_source));
+                }
+
+                LineReader(const char*file_name, const char*data_begin, const char*data_end){
+                        set_file_name(file_name);
+                        init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningStringByteSource(data_begin, data_end-data_begin)));
+                }
+
+                LineReader(const std::string&file_name, const char*data_begin, const char*data_end){
+                        set_file_name(file_name.c_str());
+                        init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningStringByteSource(data_begin, data_end-data_begin)));
+                }
+
+                LineReader(const char*file_name, FILE*file){
+                        set_file_name(file_name);
+                        init(std::unique_ptr<ByteSourceBase>(new detail::OwningStdIOByteSourceBase(file)));
+                }
+
+                LineReader(const std::string&file_name, FILE*file){
+                        set_file_name(file_name.c_str());
+                        init(std::unique_ptr<ByteSourceBase>(new detail::OwningStdIOByteSourceBase(file)));
+                }
+
+                LineReader(const char*file_name, std::istream&in){
+                        set_file_name(file_name);
+                        init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningIStreamByteSource(in)));
+                }
+
+                LineReader(const std::string&file_name, std::istream&in){
+                        set_file_name(file_name.c_str());
+                        init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningIStreamByteSource(in)));
+                }
+
+                void set_file_name(const std::string&file_name){
+                        set_file_name(file_name.c_str());
+                }
+
+                void set_file_name(const char*file_name){
+                        if(file_name != nullptr){
+                                strncpy(this->file_name, file_name, sizeof(this->file_name));
+                                this->file_name[sizeof(this->file_name)-1] = '\0';
+                        }else{
+                                this->file_name[0] = '\0';
+                        }
+                }
+
+                const char*get_truncated_file_name()const{
+                        return file_name;
+                }
+
+                void set_file_line(unsigned file_line){
+                        this->file_line = file_line;
+                }
+
+                unsigned get_file_line()const{
+                        return file_line;
+                }
+
+                char*next_line(){
+                        if(data_begin == data_end)
+                                return nullptr;
+
+                        ++file_line;
+
+                        assert(data_begin < data_end);
+                        assert(data_end <= block_len*2);
+
+                        if(data_begin >= block_len){
+                                std::memcpy(buffer.get(), buffer.get()+block_len, block_len);
+                                data_begin -= block_len;
+                                data_end -= block_len;
+                                if(reader.is_valid())
+                                {
+                                        data_end += reader.finish_read();
+                                        std::memcpy(buffer.get()+block_len, buffer.get()+2*block_len, block_len);
+                                        reader.start_read(buffer.get() + 2*block_len, block_len);
+                                }
+                        }
+
+                        int line_end = data_begin;
+                        while(line_end != data_end && buffer[line_end] != '\n'){
+                                ++line_end;
+                        }
+
+                        if(line_end - data_begin + 1 > block_len){
+                                error::line_length_limit_exceeded err;
+                                err.set_file_name(file_name);
+                                err.set_file_line(file_line);
+                                throw err;
+                        }
+
+                        if(line_end != data_end && buffer[line_end] == '\n'){
+                                buffer[line_end] = '\0';
+                        }else{
+                                // some files are missing the newline at the end of the
+                                // last line
+                                ++data_end;
+                                buffer[line_end] = '\0';
+                        }
+
+                        // handle windows \r\n-line breaks
+                        if(line_end != data_begin && buffer[line_end-1] == '\r')
+                                buffer[line_end-1] = '\0';
+
+                        char*ret = buffer.get() + data_begin;
+                        data_begin = line_end+1;
+                        return ret;
+                }
+        };
+
+
+        ////////////////////////////////////////////////////////////////////////////
+        //                                 CSV                                    //
+        ////////////////////////////////////////////////////////////////////////////
+
+        namespace error{
+                const int max_column_name_length = 63;
+                struct with_column_name{
+                        with_column_name(){
+                                std::memset(column_name, 0, max_column_name_length+1);
+                        }
+
+                        void set_column_name(const char*column_name){
+                                if(column_name != nullptr){
+                                        std::strncpy(this->column_name, column_name, max_column_name_length);
+                                        this->column_name[max_column_name_length] = '\0';
+                                }else{
+                                        this->column_name[0] = '\0';
+                                }
+                        }
+
+                        char column_name[max_column_name_length+1];
+                };
+
+
+                const int max_column_content_length = 63;
+
+                struct with_column_content{
+                        with_column_content(){
+                                std::memset(column_content, 0, max_column_content_length+1);
+                        }
+
+                        void set_column_content(const char*column_content){
+                                if(column_content != nullptr){
+                                        std::strncpy(this->column_content, column_content, max_column_content_length);
+                                        this->column_content[max_column_content_length] = '\0';
+                                }else{
+                                        this->column_content[0] = '\0';
+                                }
+                        }
+
+                        char column_content[max_column_content_length+1];
+                };
+
+
+                struct extra_column_in_header :
+                        base,
+                        with_file_name,
+                        with_column_name{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(Extra column "%s" in header of file "%s".)"
+                                        , column_name, file_name);
+                        }
+                };
+
+                struct missing_column_in_header :
+                        base,
+                        with_file_name,
+                        with_column_name{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(Missing column "%s" in header of file "%s".)"
+                                        , column_name, file_name);
+                        }
+                };
+
+                struct duplicated_column_in_header :
+                        base,
+                        with_file_name,
+                        with_column_name{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(Duplicated column "%s" in header of file "%s".)"
+                                        , column_name, file_name);
+                        }
+                };
+
+                struct header_missing :
+                        base,
+                        with_file_name{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        "Header missing in file \"%s\"."
+                                        , file_name);
+                        }
+                };
+
+                struct too_few_columns :
+                        base,
+                        with_file_name,
+                        with_file_line{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        "Too few columns in line %d in file \"%s\"."
+                                        , file_line, file_name);
+                        }
+                };
+
+                struct too_many_columns :
+                        base,
+                        with_file_name,
+                        with_file_line{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        "Too many columns in line %d in file \"%s\"."
+                                        , file_line, file_name);
+                        }
+                };
+
+                struct escaped_string_not_closed :
+                        base,
+                        with_file_name,
+                        with_file_line{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        "Escaped string was not closed in line %d in file \"%s\"."
+                                        , file_line, file_name);
+                        }
+                };
+
+                struct integer_must_be_positive :
+                        base,
+                        with_file_name,
+                        with_file_line,
+                        with_column_name,
+                        with_column_content{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(The integer "%s" must be positive or 0 in column "%s" in file "%s" in line "%d".)"
+                                        , column_content, column_name, file_name, file_line);
+                        }
+                };
+
+                struct no_digit :
+                        base,
+                        with_file_name,
+                        with_file_line,
+                        with_column_name,
+                        with_column_content{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(The integer "%s" contains an invalid digit in column "%s" in file "%s" in line "%d".)"
+                                        , column_content, column_name, file_name, file_line);
+                        }
+                };
+
+                struct integer_overflow :
+                        base,
+                        with_file_name,
+                        with_file_line,
+                        with_column_name,
+                        with_column_content{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(The integer "%s" overflows in column "%s" in file "%s" in line "%d".)"
+                                        , column_content, column_name, file_name, file_line);
+                        }
+                };
+
+                struct integer_underflow :
+                        base,
+                        with_file_name,
+                        with_file_line,
+                        with_column_name,
+                        with_column_content{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(The integer "%s" underflows in column "%s" in file "%s" in line "%d".)"
+                                        , column_content, column_name, file_name, file_line);
+                        }
+                };
+
+                struct invalid_single_character :
+                        base,
+                        with_file_name,
+                        with_file_line,
+                        with_column_name,
+                        with_column_content{
+                        void format_error_message()const override{
+                                std::snprintf(error_message_buffer, sizeof(error_message_buffer),
+                                        R"(The content "%s" of column "%s" in file "%s" in line "%d" is not a single character.)"
+                                        , column_content, column_name, file_name, file_line);
+                        }
+                };
+        }
+
+        using ignore_column = unsigned int;
+        static const ignore_column ignore_no_column = 0;
+        static const ignore_column ignore_extra_column = 1;
+        static const ignore_column ignore_missing_column = 2;
+
+        template<char ... trim_char_list>
+        struct trim_chars{
+        private:
+                constexpr static bool is_trim_char(char){
+                        return false;
+                }
+
+                template<class ...OtherTrimChars>
+                constexpr static bool is_trim_char(char c, char trim_char, OtherTrimChars...other_trim_chars){
+                        return c == trim_char || is_trim_char(c, other_trim_chars...);
+                }
+
+        public:
+                static void trim(char*&str_begin, char*&str_end){
+                        while(str_begin != str_end && is_trim_char(*str_begin, trim_char_list...))
+                                ++str_begin;
+                        while(str_begin != str_end && is_trim_char(*(str_end-1), trim_char_list...))
+                                --str_end;
+                        *str_end = '\0';
+                }
+        };
+
+
+        struct no_comment{
+                static bool is_comment(const char*){
+                        return false;
+                }
+        };
+
+        template<char ... comment_start_char_list>
+        struct single_line_comment{
+        private:
+                constexpr static bool is_comment_start_char(char){
+                        return false;
+                }
+
+                template<class ...OtherCommentStartChars>
+                constexpr static bool is_comment_start_char(char c, char comment_start_char, OtherCommentStartChars...other_comment_start_chars){
+                        return c == comment_start_char || is_comment_start_char(c, other_comment_start_chars...);
+                }
+
+        public:
+
+                static bool is_comment(const char*line){
+                        return is_comment_start_char(*line, comment_start_char_list...);
+                }
+        };
+
+        struct empty_line_comment{
+                static bool is_comment(const char*line){
+                        if(*line == '\0')
+                                return true;
+                        while(*line == ' ' || *line == '\t'){
+                                ++line;
+                                if(*line == 0)
+                                        return true;
+                        }
+                        return false;
+                }
+        };
+
+        template<char ... comment_start_char_list>
+        struct single_and_empty_line_comment{
+                static bool is_comment(const char*line){
+                        return single_line_comment<comment_start_char_list...>::is_comment(line) || empty_line_comment::is_comment(line);
+                }
+        };
+
+        template<char sep>
+        struct no_quote_escape{
+                static const char*find_next_column_end(const char*col_begin){
+                        while(*col_begin != sep && *col_begin != '\0')
+                                ++col_begin;
+                        return col_begin;
+                }
+
+                static void unescape(char*&, char*&){
+
+                }
+        };
+
+        template<char sep, char quote>
+        struct double_quote_escape{
+                static const char*find_next_column_end(const char*col_begin){
+                        while(*col_begin != sep && *col_begin != '\0')
+                                if(*col_begin != quote)
+                                        ++col_begin;
+                                else{
+                                        do{
+                                                ++col_begin;
+                                                while(*col_begin != quote){
+                                                        if(*col_begin == '\0')
+                                                                throw error::escaped_string_not_closed();
+                                                        ++col_begin;
+                                                }
+                                                ++col_begin;
+                                        }while(*col_begin == quote);
+                                }
+                        return col_begin;
+                }
+
+                static void unescape(char*&col_begin, char*&col_end){
+                        if(col_end - col_begin >= 2){
+                                if(*col_begin == quote && *(col_end-1) == quote){
+                                        ++col_begin;
+                                        --col_end;
+                                        char*out = col_begin;
+                                        for(char*in = col_begin; in!=col_end; ++in){
+                                                if(*in == quote && (in+1) != col_end && *(in+1) == quote){
+                                                         ++in;
+                                                }
+                                                *out = *in;
+                                                ++out;
+                                        }
+                                        col_end = out;
+                                        *col_end = '\0';
+                                }
+                        }
+
+                }
+        };
+
+        struct throw_on_overflow{
+                template<class T>
+                static void on_overflow(T&){
+                        throw error::integer_overflow();
+                }
+
+                template<class T>
+                static void on_underflow(T&){
+                        throw error::integer_underflow();
+                }
+        };
+
+        struct ignore_overflow{
+                template<class T>
+                static void on_overflow(T&){}
+
+                template<class T>
+                static void on_underflow(T&){}
+        };
+
+        struct set_to_max_on_overflow{
+                template<class T>
+                static void on_overflow(T&x){
+                        // using (std::numeric_limits<T>::max) instead of std::numeric_limits<T>::max
+                        // to make code including windows.h with its max macro happy
+                        x = (std::numeric_limits<T>::max)();
+                }
+
+                template<class T>
+                static void on_underflow(T&x){
+                        x = (std::numeric_limits<T>::min)();
+                }
+        };
+
+
+        namespace detail{
+                template<class quote_policy>
+                void chop_next_column(
+                        char*&line, char*&col_begin, char*&col_end
+                ){
+                        assert(line != nullptr);
+
+                        col_begin = line;
+                        // the col_begin + (... - col_begin) removes the constness
+                        col_end = col_begin + (quote_policy::find_next_column_end(col_begin) - col_begin);
+
+                        if(*col_end == '\0'){
+                                line = nullptr;
+                        }else{
+                                *col_end = '\0';
+                                line = col_end + 1;
+                        }
+                }
+
+                template<class trim_policy, class quote_policy>
+                void parse_line(
+                        char*line,
+                        char**sorted_col,
+                        const std::vector<int>&col_order
+                ){
+                        for (int i : col_order) {
+                                if(line == nullptr)
+                                        throw ::io::error::too_few_columns();
+                                char*col_begin, *col_end;
+                                chop_next_column<quote_policy>(line, col_begin, col_end);
+
+                                if (i != -1) {
+                                        trim_policy::trim(col_begin, col_end);
+                                        quote_policy::unescape(col_begin, col_end);
+
+                                        sorted_col[i] = col_begin;
+                                }
+                        }
+                        if(line != nullptr)
+                                throw ::io::error::too_many_columns();
+                }
+
+                template<unsigned column_count, class trim_policy, class quote_policy>
+                void parse_header_line(
+                        char*line,
+                        std::vector<int>&col_order,
+                        const std::string*col_name,
+                        ignore_column ignore_policy
+                ){
+                        col_order.clear();
+
+                        bool found[column_count];
+                        std::fill(found, found + column_count, false);
+                        while(line){
+                                char*col_begin,*col_end;
+                                chop_next_column<quote_policy>(line, col_begin, col_end);
+
+                                trim_policy::trim(col_begin, col_end);
+                                quote_policy::unescape(col_begin, col_end);
+
+                                for(unsigned i=0; i<column_count; ++i)
+                                        if(col_begin == col_name[i]){
+                                                if(found[i]){
+                                                        error::duplicated_column_in_header err;
+                                                        err.set_column_name(col_begin);
+                                                        throw err;
+                                                }
+                                                found[i] = true;
+                                                col_order.push_back(i);
+                                                col_begin = 0;
+                                                break;
+                                        }
+                                if(col_begin){
+                                        if(ignore_policy & ::io::ignore_extra_column)
+                                                col_order.push_back(-1);
+                                        else{
+                                                error::extra_column_in_header err;
+                                                err.set_column_name(col_begin);
+                                                throw err;
+                                        }
+                                }
+                        }
+                        if(!(ignore_policy & ::io::ignore_missing_column)){
+                                for(unsigned i=0; i<column_count; ++i){
+                                        if(!found[i]){
+                                                error::missing_column_in_header err;
+                                                err.set_column_name(col_name[i].c_str());
+                                                throw err;
+                                        }
+                                }
+                        }
+                }
+
+                template<class overflow_policy>
+                void parse(char*col, char &x){
+                        if(!*col)
+                                throw error::invalid_single_character();
+                        x = *col;
+                        ++col;
+                        if(*col)
+                                throw error::invalid_single_character();
+                }
+
+                template<class overflow_policy>
+                void parse(char*col, std::string&x){
+                        x = col;
+                }
+
+                template<class overflow_policy>
+                void parse(char*col, const char*&x){
+                        x = col;
+                }
+
+                template<class overflow_policy>
+                void parse(char*col, char*&x){
+                        x = col;
+                }
+
+                template<class overflow_policy, class T>
+                void parse_unsigned_integer(const char*col, T&x){
+                        x = 0;
+                        while(*col != '\0'){
+                                if('0' <= *col && *col <= '9'){
+                                        T y = *col - '0';
+                                        if(x > ((std::numeric_limits<T>::max)()-y)/10){
+                                                overflow_policy::on_overflow(x);
+                                                return;
+                                        }
+                                        x = 10*x+y;
+                                }else
+                                        throw error::no_digit();
+                                ++col;
+                        }
+                }
+
+                template<class overflow_policy>void parse(char*col, unsigned char &x)
+                        {parse_unsigned_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, unsigned short &x)
+                        {parse_unsigned_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, unsigned int &x)
+                        {parse_unsigned_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, unsigned long &x)
+                        {parse_unsigned_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, unsigned long long &x)
+                        {parse_unsigned_integer<overflow_policy>(col, x);}
+
+                template<class overflow_policy, class T>
+                void parse_signed_integer(const char*col, T&x){
+                        if(*col == '-'){
+                                ++col;
+
+                                x = 0;
+                                while(*col != '\0'){
+                                        if('0' <= *col && *col <= '9'){
+                                                T y = *col - '0';
+                                                if(x < ((std::numeric_limits<T>::min)()+y)/10){
+                                                        overflow_policy::on_underflow(x);
+                                                        return;
+                                                }
+                                                x = 10*x-y;
+                                        }else
+                                                throw error::no_digit();
+                                        ++col;
+                                }
+                                return;
+                        }else if(*col == '+')
+                                ++col;
+                        parse_unsigned_integer<overflow_policy>(col, x);
+                }
+
+                template<class overflow_policy>void parse(char*col, signed char &x)
+                        {parse_signed_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, signed short &x)
+                        {parse_signed_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, signed int &x)
+                        {parse_signed_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, signed long &x)
+                        {parse_signed_integer<overflow_policy>(col, x);}
+                template<class overflow_policy>void parse(char*col, signed long long &x)
+                        {parse_signed_integer<overflow_policy>(col, x);}
+
+                template<class T>
+                void parse_float(const char*col, T&x){
+                        bool is_neg = false;
+                        if(*col == '-'){
+                                is_neg = true;
+                                ++col;
+                        }else if(*col == '+')
+                                ++col;
+
+                        x = 0;
+                        while('0' <= *col && *col <= '9'){
+                                int y = *col - '0';
+                                x *= 10;
+                                x += y;
+                                ++col;
+                        }
+
+                        if(*col == '.'|| *col == ','){
+                                ++col;
+                                T pos = 1;
+                                while('0' <= *col && *col <= '9'){
+                                        pos /= 10;
+                                        int y = *col - '0';
+                                        ++col;
+                                        x += y*pos;
+                                }
+                        }
+
+                        if(*col == 'e' || *col == 'E'){
+                                ++col;
+                                int e;
+
+                                parse_signed_integer<set_to_max_on_overflow>(col, e);
+
+                                if(e != 0){
+                                        T base;
+                                        if(e < 0){
+                                                base = T(0.1);
+                                                e = -e;
+                                        }else{
+                                                base = T(10);
+                                        }
+
+                                        while(e != 1){
+                                                if((e & 1) == 0){
+                                                        base = base*base;
+                                                        e >>= 1;
+                                                }else{
+                                                        x *= base;
+                                                        --e;
+                                                }
+                                        }
+                                        x *= base;
+                                }
+                        }else{
+                                if(*col != '\0')
+                                        throw error::no_digit();
+                        }
+
+                        if(is_neg)
+                                x = -x;
+                }
+
+                template<class overflow_policy> void parse(char*col, float&x) { parse_float(col, x); }
+                template<class overflow_policy> void parse(char*col, double&x) { parse_float(col, x); }
+                template<class overflow_policy> void parse(char*col, long double&x) { parse_float(col, x); }
+
+                template<class overflow_policy, class T>
+                void parse(char*col, T&x){
+                        // Mute unused variable compiler warning
+                        (void)col;
+                        (void)x;
+                        // GCC evalutes "false" when reading the template and
+                        // "sizeof(T)!=sizeof(T)" only when instantiating it. This is why
+                        // this strange construct is used.
+                        static_assert(sizeof(T)!=sizeof(T),
+                                "Can not parse this type. Only buildin integrals, floats, char, char*, const char* and std::string are supported");
+                }
+
+        }
+
+        template<unsigned column_count,
+                class trim_policy = trim_chars<' ', '\t'>,
+                class quote_policy = no_quote_escape<','>,
+                class overflow_policy = throw_on_overflow,
+                class comment_policy = no_comment
+        >
+        class CSVReader{
+        private:
+                LineReader in;
+
+                char*row[column_count];
+                std::string column_names[column_count];
+
+                std::vector<int>col_order;
+
+                template<class ...ColNames>
+                void set_column_names(std::string s, ColNames...cols){
+                        column_names[column_count-sizeof...(ColNames)-1] = std::move(s);
+                        set_column_names(std::forward<ColNames>(cols)...);
+                }
+
+                void set_column_names(){}
+
+
+        public:
+                CSVReader() = delete;
+                CSVReader(const CSVReader&) = delete;
+                CSVReader&operator=(const CSVReader&);
+
+                template<class ...Args>
+                explicit CSVReader(Args&&...args):in(std::forward<Args>(args)...){
+                        std::fill(row, row+column_count, nullptr);
+                        col_order.resize(column_count);
+                        for(unsigned i=0; i<column_count; ++i)
+                                col_order[i] = i;
+                        for(unsigned i=1; i<=column_count; ++i)
+                                column_names[i-1] = "col"+std::to_string(i);
+                }
+
+               char*next_line(){
+                       return in.next_line();
+               }
+
+                template<class ...ColNames>
+                void read_header(ignore_column ignore_policy, ColNames...cols){
+                        static_assert(sizeof...(ColNames)>=column_count, "not enough column names specified");
+                        static_assert(sizeof...(ColNames)<=column_count, "too many column names specified");
+                        try{
+                                set_column_names(std::forward<ColNames>(cols)...);
+
+                                char*line;
+                                do{
+                                        line = in.next_line();
+                                        if(!line)
+                                                throw error::header_missing();
+                                }while(comment_policy::is_comment(line));
+
+                                detail::parse_header_line
+                                        <column_count, trim_policy, quote_policy>
+                                        (line, col_order, column_names, ignore_policy);
+                        }catch(error::with_file_name&err){
+                                err.set_file_name(in.get_truncated_file_name());
+                                throw;
+                        }
+                }
+
+                template<class ...ColNames>
+                void set_header(ColNames...cols){
+                        static_assert(sizeof...(ColNames)>=column_count,
+                                "not enough column names specified");
+                        static_assert(sizeof...(ColNames)<=column_count,
+                                "too many column names specified");
+                        set_column_names(std::forward<ColNames>(cols)...);
+                        std::fill(row, row+column_count, nullptr);
+                        col_order.resize(column_count);
+                        for(unsigned i=0; i<column_count; ++i)
+                                col_order[i] = i;
+                }
+
+                bool has_column(const std::string&name) const {
+                        return col_order.end() != std::find(
+                                col_order.begin(), col_order.end(),
+                                        std::find(std::begin(column_names), std::end(column_names), name)
+                                - std::begin(column_names));
+                }
+
+                void set_file_name(const std::string&file_name){
+                        in.set_file_name(file_name);
+                }
+
+                void set_file_name(const char*file_name){
+                        in.set_file_name(file_name);
+                }
+
+                const char*get_truncated_file_name()const{
+                        return in.get_truncated_file_name();
+                }
+
+                void set_file_line(unsigned file_line){
+                        in.set_file_line(file_line);
+                }
+
+                unsigned get_file_line()const{
+                        return in.get_file_line();
+                }
+
+        private:
+                void parse_helper(std::size_t){}
+
+                template<class T, class ...ColType>
+                void parse_helper(std::size_t r, T&t, ColType&...cols){
+                        if(row[r]){
+                                try{
+                                        try{
+                                                ::io::detail::parse<overflow_policy>(row[r], t);
+                                        }catch(error::with_column_content&err){
+                                                err.set_column_content(row[r]);
+                                                throw;
+                                        }
+                                }catch(error::with_column_name&err){
+                                        err.set_column_name(column_names[r].c_str());
+                                        throw;
+                                }
+                        }
+                        parse_helper(r+1, cols...);
+                }
+
+
+        public:
+                template<class ...ColType>
+                bool read_row(ColType& ...cols){
+                        static_assert(sizeof...(ColType)>=column_count,
+                                "not enough columns specified");
+                        static_assert(sizeof...(ColType)<=column_count,
+                                "too many columns specified");
+                        try{
+                                try{
+
+                                        char*line;
+                                        do{
+                                                line = in.next_line();
+                                                if(!line)
+                                                        return false;
+                                        }while(comment_policy::is_comment(line));
+
+                                        detail::parse_line<trim_policy, quote_policy>
+                                                (line, row, col_order);
+
+                                        parse_helper(0, cols...);
+                                }catch(error::with_file_name&err){
+                                        err.set_file_name(in.get_truncated_file_name());
+                                        throw;
+                                }
+                        }catch(error::with_file_line&err){
+                                err.set_file_line(in.get_file_line());
+                                throw;
+                        }
+
+                        return true;
+                }
+        };
+}
+#endif
+