<commit_before>/*
 * A very bad streaming TOML parser
 */
#define NOLOG
#include "toml.h"
#include "debug.h"
#include <cassert>
#include <string>

struct Token
{
    enum class Type
    {
        Eof,
        SquareOpen,
        SquareClose,
        BraceOpen,
        BraceClose,
        Assign,
        Newline,
        Comma,
        Dot,

        Ident,
        String,
        Integer,
    };

    Type    m_type;
    ::std::string   m_data;
    int64_t m_intval = 0;

    Token(Type ty):
        m_type(ty)
    {
    }
    Token(Type ty, ::std::string s):
        m_type(ty),
        m_data(s)
    {
    }
    Token(Type ty, int64_t i):
        m_type(ty),
        m_intval(i)
    {
    }

    static Token lex_from(::std::ifstream& is);
    static Token lex_from_inner(::std::ifstream& is);

    const ::std::string& as_string() const {
        assert(m_type == Type::Ident || m_type == Type::String);
        return m_data;
    }

    friend ::std::ostream& operator<<(::std::ostream& os, const Token& x) {
        switch(x.m_type)
        {
        case Type::Eof:   os << "Eof";    break;
        case Type::SquareOpen:  os << "SquareOpen"; break;
        case Type::SquareClose: os << "SquareClose"; break;
        case Type::BraceOpen:   os << "BraceOpen"; break;
        case Type::BraceClose:  os << "BraceClose"; break;
        case Type::Assign:      os << "Assign";   break;
        case Type::Newline:     os << "Newline";  break;
        case Type::Comma:       os << "Comma";    break;
        case Type::Dot:         os << "Dot";      break;
        case Type::Ident:  os << "Ident(" << x.m_data << ")";  break;
        case Type::String: os << "String(" << x.m_data << ")"; break;
        case Type::Integer: os << "Integer(" << x.m_intval << ")"; break;
        }
        return os;
    }
};

TomlFile::TomlFile(const ::std::string& filename):
    m_if(filename)
{
    if( !m_if.is_open() ) {
        throw ::std::runtime_error("Unable to open file '" + filename + "'");
    }
}
TomlFileIter TomlFile::begin()
{
    TomlFileIter rv { *this };
    ++rv;
    return rv;
}
TomlFileIter TomlFile::end()
{
    return TomlFileIter { *this };
}

TomlKeyValue TomlFile::get_next_value()
{
    auto t = Token::lex_from(m_if);

    if(m_current_composite.empty())
    {
        while( t.m_type == Token::Type::Newline )
            t = Token::lex_from(m_if);

        // Expect '[', a string, or an identifier
        switch(t.m_type)
        {
        case Token::Type::Eof:
            // Empty return indicates the end of the list
            return TomlKeyValue {};
        case Token::Type::SquareOpen:
            m_current_block.clear();
            do
            {
                t = Token::lex_from(m_if);
                bool is_array = false;
                if(t.m_type == Token::Type::SquareOpen)
                {
                    is_array = true;
                    t = Token::lex_from(m_if);
                }
                assert(t.m_type == Token::Type::Ident || t.m_type == Token::Type::String);
                m_current_block.push_back(t.as_string());
                if(is_array)
                {
                    m_current_block.push_back(::format(m_array_counts[t.as_string()]++));
                    t = Token::lex_from(m_if);
                    assert(t.m_type == Token::Type::SquareClose);
                }

                t = Token::lex_from(m_if);
            } while(t.m_type == Token::Type::Dot);
            if( t.m_type != Token::Type::SquareClose )
            {
                throw ::std::runtime_error(::format("Unexpected token in block header - ", t));
            }
            t = Token::lex_from(m_if);
            if (t.m_type != Token::Type::Newline)
            {
                throw ::std::runtime_error(::format("Unexpected token after block block - ", t));
            }
            DEBUG("Start block " << m_current_block);
            // Recurse!
            return get_next_value();
        default:
            break;
        }
    }
    else
    {
        // Expect a string or an identifier
        if( t.m_type == Token::Type::Eof )
        {
            // EOF isn't allowed here
            throw ::std::runtime_error(::format("Unexpected EOF in composite"));
        }
    }
    switch (t.m_type)
    {
    case Token::Type::String:
    case Token::Type::Ident:
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token for key - ", t));
    }
    ::std::string   key_name = t.as_string();
    t = Token::lex_from(m_if);

    if(t.m_type != Token::Type::Assign)
        throw ::std::runtime_error(::format("Unexpected token after key - ", t));
    t = Token::lex_from(m_if);

    TomlKeyValue    rv;
    switch(t.m_type)
    {
    case Token::Type::String:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value = TomlValue { t.m_data };
        break;
    case Token::Type::SquareOpen:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value.m_type = TomlValue::Type::List;
        while( (t = Token::lex_from(m_if)).m_type != Token::Type::SquareClose )
        {
            while( t.m_type == Token::Type::Newline )
                t = Token::lex_from(m_if);
            if( t.m_type == Token::Type::SquareClose )
                break;

            // TODO: Recurse parse a value
            switch(t.m_type)
            {
            case Token::Type::String:
                rv.value.m_sub_values.push_back(TomlValue { t.as_string() });
                break;
            default:
                throw ::std::runtime_error(::format("Unexpected token in array value position - ", t));
            }

            t = Token::lex_from(m_if);
            if(t.m_type != Token::Type::Comma)
                break;
        }
        if(t.m_type != Token::Type::SquareClose)
            throw ::std::runtime_error(::format("Unexpected token after array - ", t));
        break;
    case Token::Type::BraceOpen:
        m_current_composite.push_back(key_name);
        DEBUG("Enter composite block " << m_current_block << ", " << m_current_composite);
        // Recurse to restart parse
        return get_next_value();
    case Token::Type::Integer:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);
        rv.value = TomlValue { t.m_intval };
        return rv;
    case Token::Type::Ident:
        if( t.m_data == "true" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);
            rv.value = TomlValue { true };
        }
        else if( t.m_data == "false" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);

            rv.value = TomlValue { false };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected identifier in value position - ", t));
        }
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token in value position - ", t));
    }

    t = Token::lex_from(m_if);
    while (!m_current_composite.empty() && t.m_type == Token::Type::BraceClose)
    {
        DEBUG("Leave composite block " << m_current_block << ", " << m_current_composite);
        m_current_composite.pop_back();
        t = Token::lex_from(m_if);
    }
    if( m_current_composite.empty() )
    {
        // TODO: Allow EOF?
        if(t.m_type != Token::Type::Newline)
            throw ::std::runtime_error(::format("Unexpected token in TOML file after entry - ", t));
    }
    else
    {
        if( t.m_type != Token::Type::Comma )
            throw ::std::runtime_error(::format("Unexpected token in TOML file after composite entry - ", t));
    }
    return rv;
}

Token Token::lex_from(::std::ifstream& is)
{
    auto rv = Token::lex_from_inner(is);
    //DEBUG("lex_from: " << rv);
    return rv;
}
Token Token::lex_from_inner(::std::ifstream& is)
{
    int c;
    do
    {
        c = is.get();
    } while( c != EOF && c != '\n' && isspace(c) );

    ::std::string   str;
    switch(c)
    {
    case EOF:   return Token { Type::Eof };
    case '[':   return Token { Type::SquareOpen };
    case ']':   return Token { Type::SquareClose };
    case '{':   return Token { Type::BraceOpen };
    case '}':   return Token { Type::BraceClose };
    case ',':   return Token { Type::Comma };
    case '.':   return Token { Type::Dot };
    case '=':   return Token { Type::Assign };
    case '\n':  return Token { Type::Newline };
    case '#':
        while(c != '\n')
        {
            c = is.get();
            if(c == EOF)
                return Token { Type::Eof };
        }
        return Token { Type::Newline };
    case '\'':
        c = is.get();
        while (c != '\'')
        {
            if (c == EOF)
                throw ::std::runtime_error("Unexpected EOF in single-quoted string");
            if (c == '\\')
            {
                // TODO: Escaped strings
                throw ::std::runtime_error("TODO: Escaped sequences in strings (single)");
            }
            str += (char)c;
            c = is.get();
        }
        return Token { Type::String, str };
    case '"':
        c = is.get();
        if(c == '"')
        {
            c = is.get();
            if( c != '"' )
            {
                is.putback(c);
                return Token { Type::String, "" };
            }
            else
            {
                // Keep reading until """
                for(;;)
                {
                    c = is.get();
                    if(c == '"')
                    {
                        c = is.get();
                        if(c == '"')
                        {
                            c = is.get();
                            if(c == '"')
                            {
                                break;
                            }
                            str += '"';
                        }
                        str += '"';
                    }
                    if( c == EOF )
                        throw ::std::runtime_error("Unexpected EOF in triple-quoted string");
                    if(c == '\\')
                    {
                        // TODO: Escaped strings
                        throw ::std::runtime_error("TODO: Escaped sequences in strings (triple)");
                    }
                    str += (char)c;
                }
            }
        }
        else
        {
            while(c != '"')
            {
                if (c == EOF)
                    throw ::std::runtime_error("Unexpected EOF in double-quoted string");
                if (c == '\\')
                {
                    // TODO: Escaped strings
                    c = is.get();
                    switch(c)
                    {
                    case '"':  str += '"'; break;
                    case 'n':  str += '\n'; break;
                    default:
                        throw ::std::runtime_error("TODO: Escaped sequences in strings");
                    }
                    c = is.get();
                    continue ;
                }
                str += (char)c;
                c = is.get();
            }
        }
        return Token { Type::String, str };
    default:
        if(isalpha(c))
        {
            // Identifier
            while(isalnum(c) || c == '-' || c == '_')
            {
                str += (char)c;
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Ident, str };
        }
        else if( isdigit(c) )
        {
            int64_t val = 0;
            while(isdigit(c))
            {
                val *= 10;
                val += c - '0';
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Integer, val };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected chracter '", (char)c, "' in file"));
        }
    }
}
<commit_msg>Toml - Slightly better commenting<commit_after>/*
 * A very bad streaming TOML parser
 */
#define NOLOG
#include "toml.h"
#include "debug.h"
#include <cassert>
#include <string>

struct Token
{
    enum class Type
    {
        Eof,
        SquareOpen,
        SquareClose,
        BraceOpen,
        BraceClose,
        Assign,
        Newline,
        Comma,
        Dot,

        Ident,
        String,
        Integer,
    };

    Type    m_type;
    ::std::string   m_data;
    int64_t m_intval = 0;

    Token(Type ty):
        m_type(ty)
    {
    }
    Token(Type ty, ::std::string s):
        m_type(ty),
        m_data(s)
    {
    }
    Token(Type ty, int64_t i):
        m_type(ty),
        m_intval(i)
    {
    }

    static Token lex_from(::std::ifstream& is);
    static Token lex_from_inner(::std::ifstream& is);

    const ::std::string& as_string() const {
        assert(m_type == Type::Ident || m_type == Type::String);
        return m_data;
    }

    friend ::std::ostream& operator<<(::std::ostream& os, const Token& x) {
        switch(x.m_type)
        {
        case Type::Eof:   os << "Eof";    break;
        case Type::SquareOpen:  os << "SquareOpen"; break;
        case Type::SquareClose: os << "SquareClose"; break;
        case Type::BraceOpen:   os << "BraceOpen"; break;
        case Type::BraceClose:  os << "BraceClose"; break;
        case Type::Assign:      os << "Assign";   break;
        case Type::Newline:     os << "Newline";  break;
        case Type::Comma:       os << "Comma";    break;
        case Type::Dot:         os << "Dot";      break;
        case Type::Ident:  os << "Ident(" << x.m_data << ")";  break;
        case Type::String: os << "String(" << x.m_data << ")"; break;
        case Type::Integer: os << "Integer(" << x.m_intval << ")"; break;
        }
        return os;
    }
};

TomlFile::TomlFile(const ::std::string& filename):
    m_if(filename)
{
    if( !m_if.is_open() ) {
        throw ::std::runtime_error("Unable to open file '" + filename + "'");
    }
}
TomlFileIter TomlFile::begin()
{
    TomlFileIter rv { *this };
    ++rv;
    return rv;
}
TomlFileIter TomlFile::end()
{
    return TomlFileIter { *this };
}

TomlKeyValue TomlFile::get_next_value()
{
    auto t = Token::lex_from(m_if);

    if(m_current_composite.empty())
    {
        while( t.m_type == Token::Type::Newline )
            t = Token::lex_from(m_if);

        // Expect '[', a string, or an identifier
        switch(t.m_type)
        {
        case Token::Type::Eof:
            // Empty return indicates the end of the list
            return TomlKeyValue {};
        case Token::Type::SquareOpen:
            m_current_block.clear();
            do
            {
                t = Token::lex_from(m_if);
                bool is_array = false;
                if(t.m_type == Token::Type::SquareOpen)
                {
                    is_array = true;
                    t = Token::lex_from(m_if);
                }
                assert(t.m_type == Token::Type::Ident || t.m_type == Token::Type::String);
                m_current_block.push_back(t.as_string());
                if(is_array)
                {
                    m_current_block.push_back(::format(m_array_counts[t.as_string()]++));
                    t = Token::lex_from(m_if);
                    assert(t.m_type == Token::Type::SquareClose);
                }

                t = Token::lex_from(m_if);
            } while(t.m_type == Token::Type::Dot);
            if( t.m_type != Token::Type::SquareClose )
            {
                throw ::std::runtime_error(::format("Unexpected token in block header - ", t));
            }
            t = Token::lex_from(m_if);
            if (t.m_type != Token::Type::Newline)
            {
                throw ::std::runtime_error(::format("Unexpected token after block block - ", t));
            }
            DEBUG("Start block " << m_current_block);
            // Recurse!
            return get_next_value();
        default:
            break;
        }
    }
    else
    {
        // Expect a string or an identifier
        if( t.m_type == Token::Type::Eof )
        {
            // EOF isn't allowed here
            throw ::std::runtime_error(::format("Unexpected EOF in composite"));
        }
    }
    switch (t.m_type)
    {
    case Token::Type::String:
    case Token::Type::Ident:
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token for key - ", t));
    }
    ::std::string   key_name = t.as_string();
    t = Token::lex_from(m_if);

    if(t.m_type != Token::Type::Assign)
        throw ::std::runtime_error(::format("Unexpected token after key - ", t));
    t = Token::lex_from(m_if);

    // --- Value ---
    TomlKeyValue    rv;
    switch(t.m_type)
    {
    // String: Return the string value
    case Token::Type::String:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value = TomlValue { t.m_data };
        break;
    // Array: Parse the entire list and return as Type::List
    case Token::Type::SquareOpen:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);

        rv.value.m_type = TomlValue::Type::List;
        while( (t = Token::lex_from(m_if)).m_type != Token::Type::SquareClose )
        {
            while( t.m_type == Token::Type::Newline )
                t = Token::lex_from(m_if);
            if( t.m_type == Token::Type::SquareClose )
                break;

            // TODO: Recursively parse a value
            // TODO: OR, support other value types
            switch(t.m_type)
            {
            case Token::Type::String:
                rv.value.m_sub_values.push_back(TomlValue { t.as_string() });
                break;
            default:
                throw ::std::runtime_error(::format("Unexpected token in array value position - ", t));
            }

            t = Token::lex_from(m_if);
            if(t.m_type != Token::Type::Comma)
                break;
        }
        if(t.m_type != Token::Type::SquareClose)
            throw ::std::runtime_error(::format("Unexpected token after array - ", t));
        break;
    case Token::Type::BraceOpen:
        m_current_composite.push_back(key_name);
        DEBUG("Enter composite block " << m_current_block << ", " << m_current_composite);
        // Recurse to restart parse
        return get_next_value();
    case Token::Type::Integer:
        rv.path = m_current_block;
        rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
        rv.path.push_back(key_name);
        rv.value = TomlValue { t.m_intval };
        return rv;
    case Token::Type::Ident:
        if( t.m_data == "true" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);
            rv.value = TomlValue { true };
        }
        else if( t.m_data == "false" )
        {
            rv.path = m_current_block;
            rv.path.insert(rv.path.end(), m_current_composite.begin(), m_current_composite.end());
            rv.path.push_back(key_name);

            rv.value = TomlValue { false };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected identifier in value position - ", t));
        }
        break;
    default:
        throw ::std::runtime_error(::format("Unexpected token in value position - ", t));
    }

    t = Token::lex_from(m_if);
    while (!m_current_composite.empty() && t.m_type == Token::Type::BraceClose)
    {
        DEBUG("Leave composite block " << m_current_block << ", " << m_current_composite);
        m_current_composite.pop_back();
        t = Token::lex_from(m_if);
    }
    if( m_current_composite.empty() )
    {
        // TODO: Allow EOF?
        if(t.m_type != Token::Type::Newline)
            throw ::std::runtime_error(::format("Unexpected token in TOML file after entry - ", t));
    }
    else
    {
        if( t.m_type != Token::Type::Comma )
            throw ::std::runtime_error(::format("Unexpected token in TOML file after composite entry - ", t));
    }
    return rv;
}

Token Token::lex_from(::std::ifstream& is)
{
    auto rv = Token::lex_from_inner(is);
    //DEBUG("lex_from: " << rv);
    return rv;
}
Token Token::lex_from_inner(::std::ifstream& is)
{
    int c;
    do
    {
        c = is.get();
    } while( c != EOF && c != '\n' && isspace(c) );

    ::std::string   str;
    switch(c)
    {
    case EOF:   return Token { Type::Eof };
    case '[':   return Token { Type::SquareOpen };
    case ']':   return Token { Type::SquareClose };
    case '{':   return Token { Type::BraceOpen };
    case '}':   return Token { Type::BraceClose };
    case ',':   return Token { Type::Comma };
    case '.':   return Token { Type::Dot };
    case '=':   return Token { Type::Assign };
    case '\n':  return Token { Type::Newline };
    case '#':
        while(c != '\n')
        {
            c = is.get();
            if(c == EOF)
                return Token { Type::Eof };
        }
        return Token { Type::Newline };
    case '\'':
        c = is.get();
        while (c != '\'')
        {
            if (c == EOF)
                throw ::std::runtime_error("Unexpected EOF in single-quoted string");
            if (c == '\\')
            {
                // TODO: Escaped strings
                throw ::std::runtime_error("TODO: Escaped sequences in strings (single)");
            }
            str += (char)c;
            c = is.get();
        }
        return Token { Type::String, str };
    case '"':
        c = is.get();
        if(c == '"')
        {
            c = is.get();
            if( c != '"' )
            {
                is.putback(c);
                return Token { Type::String, "" };
            }
            else
            {
                // Keep reading until """
                for(;;)
                {
                    c = is.get();
                    if(c == '"')
                    {
                        c = is.get();
                        if(c == '"')
                        {
                            c = is.get();
                            if(c == '"')
                            {
                                break;
                            }
                            str += '"';
                        }
                        str += '"';
                    }
                    if( c == EOF )
                        throw ::std::runtime_error("Unexpected EOF in triple-quoted string");
                    if(c == '\\')
                    {
                        // TODO: Escaped strings
                        throw ::std::runtime_error("TODO: Escaped sequences in strings (triple)");
                    }
                    str += (char)c;
                }
            }
        }
        else
        {
            while(c != '"')
            {
                if (c == EOF)
                    throw ::std::runtime_error("Unexpected EOF in double-quoted string");
                if (c == '\\')
                {
                    // TODO: Escaped strings
                    c = is.get();
                    switch(c)
                    {
                    case '"':  str += '"'; break;
                    case 'n':  str += '\n'; break;
                    default:
                        throw ::std::runtime_error("TODO: Escaped sequences in strings");
                    }
                    c = is.get();
                    continue ;
                }
                str += (char)c;
                c = is.get();
            }
        }
        return Token { Type::String, str };
    default:
        if(isalpha(c))
        {
            // Identifier
            while(isalnum(c) || c == '-' || c == '_')
            {
                str += (char)c;
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Ident, str };
        }
        else if( isdigit(c) )
        {
            int64_t val = 0;
            while(isdigit(c))
            {
                val *= 10;
                val += c - '0';
                c = is.get();
            }
            is.putback(c);
            return Token { Type::Integer, val };
        }
        else
        {
            throw ::std::runtime_error(::format("Unexpected chracter '", (char)c, "' in file"));
        }
    }
}
<|endoftext|>


<commit_before>#include "uploader.h"
#include "scope_guard.h"
#include "errors.h"
#include "context.h"

#include <iostream>
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <boost/bind.hpp>
#include "workaround.hpp"
#include "compressor.h"

#define MIN_PART_SIZE (16*1024*1024)
#define MIN_ALLOWED_PART_SIZE (16*1024*1024)
#define MAX_PART_NUM 10000

using namespace es3;
using namespace boost::filesystem;

struct es3::upload_content
{
	upload_content() : num_parts_(), num_completed_() {}
	context_ptr conn_;
	std::string upload_id_;
	std::string remote_;
	uint64_t orig_size_;
	time_t mtime_;

	std::mutex lock_;
	size_t num_parts_;
	size_t num_completed_;
	std::vector<std::string> etags_;
};

class part_upload_task : public sync_task
{
	size_t num_;
	upload_content_ptr content_;

	segment_ptr segment_;
public:
	part_upload_task(size_t num, upload_content_ptr content,
					 segment_ptr segment)
		: num_(num), content_(content), segment_(segment)
	{
	}

	virtual void operator()(agenda_ptr agenda)
	{
		VLOG(2) << "Starting upload of a part " << num_ << " of "
				<< content_->remote_;

		struct sched_param param;
		param.sched_priority = 1+num_*90/content_->num_parts_;
		pthread_setschedparam(pthread_self(), SCHED_RR, &param);

		std::string part_path=content_->remote_+
				"?partNumber="+int_to_string(num_+1)
				+"&uploadId="+content_->upload_id_;

		s3_connection up(content_->conn_);
		std::string etag=up.upload_data(part_path,
										&segment_->data_[0],
										segment_->data_.size());
		if (etag.empty())
			err(errWarn) << "Failed to upload a part " << num_;

		//Check if the upload is completed
		guard_t g(content_->lock_);
		content_->num_completed_++;
		content_->etags_.at(num_) = etag;

		VLOG(2) << "Uploaded part " << num_ << " of "<< content_->remote_
				<< " with etag=" << etag
				<< ", total=" << content_->num_parts_
				<< ", sent=" << content_->num_completed_ << ".";

		if (content_->num_completed_ == content_->num_parts_)
		{
			VLOG(2) << "Assembling "<< content_->remote_ <<".";
			//We've completed the upload!
			s3_connection up2(content_->conn_);
			up2.complete_multipart(content_->remote_+
								   "?uploadId="+content_->upload_id_,
								   content_->etags_);
		}
	}
};

class file_pump : public sync_task,
		public boost::enable_shared_from_this<file_pump>
{
	upload_content_ptr content_;
	zip_result_ptr files_;
	size_t cur_segment_, number_of_segments_;
public:
	file_pump(upload_content_ptr content,
		zip_result_ptr files, size_t cur_segment, size_t number_of_segments) :
		content_(content), files_(files),
		cur_segment_(cur_segment), number_of_segments_(number_of_segments)
	{
	}

	virtual std::string get_class() const
	{
		return "pump"+int_to_string(get_class_limit());
	}

	virtual int get_class_limit() const
	{
		return content_->conn_->max_readers_;
	}

	virtual void operator()(agenda_ptr agenda)
	{
		context_ptr ctx = content_->conn_;
		uint64_t start_offset = ctx->segment_size_*cur_segment_;

		//First, skip to our piece
		uint64_t skipped_so_far=0, offset_within_=0;
		int cur_piece=0;
		for(int f=0;f<files_->sizes_.size();++f)
		{
			uint64_t cur_size = files_->sizes_.at(f);
			if (start_offset<(skipped_so_far+cur_size))
			{
				cur_piece = f;
				offset_within_ = start_offset-skipped_so_far;
				assert(offset_within_<cur_size);
				break;
			}
			skipped_so_far+=cur_size;
		}

		//Now read data!!!
		for(int f=0;f<number_of_segments_;++f)
		{
			segment_ptr seg = ctx->get_segment();
			seg->data_.resize(ctx->segment_size_, 0);

			uint64_t segment_read_so_far=0;
			while(segment_read_so_far<seg->data_.size())
			{
				//Note that we're duplicating the handle because other
				//file pumps might be using it
				std::cerr<<"Opening " <<files_->files_.at(cur_piece).c_str() << std::endl;
				handle_t cur_fl(open(
									files_->files_.at(cur_piece).c_str(), O_RDONLY));
				std::cerr<<"Opened " <<files_->files_.at(cur_piece).c_str() << std::endl;
				lseek64(cur_fl.get(), offset_within_, SEEK_SET) | libc_die;
				std::cerr<<"Seeked " <<files_->files_.at(cur_piece).c_str() << std::endl;
				uint64_t cur_piece_size=files_->sizes_.at(cur_piece);

				uint64_t remaining_size =
						std::min(ctx->segment_size_-segment_read_so_far,
								 cur_piece_size-offset_within_);
				while(remaining_size>0)
				{
					char buf[65536*8];
					size_t chunk=std::min(remaining_size,
										  uint64_t(sizeof(buf)));
					size_t res=read(cur_fl.get(), buf, chunk) | libc_die;
					assert(res!=0);
					memcpy(&seg->data_[segment_read_so_far], buf, res);

					segment_read_so_far+=res;
					offset_within_+=res;
					remaining_size-=res;
				}

				//We've run out of file piece, switch to the next one
				if (cur_piece==files_->sizes_.size()-1)
				{
					//No other pieces and this is the last segment
					seg->data_.resize(segment_read_so_far);
				} else
				{
					if (cur_piece_size==offset_within_)
					{
						cur_piece++;
						offset_within_=0;
					} else
					{
						assert(segment_read_so_far==ctx->segment_size_);
					}
				}
			}
			assert(segment_read_so_far==ctx->segment_size_
				   || f==number_of_segments_-1);

			sync_task_ptr task(new part_upload_task(cur_segment_+f,
													content_, seg));
			agenda->schedule(task);
		}
	}
};

void file_uploader::operator()(agenda_ptr agenda)
{
	uint64_t file_sz=file_size(path_);
	time_t mtime=last_write_time(path_);

	//Check the modification date of the file locally and on the
	//remote side
	s3_connection up(conn_);
	std::pair<time_t, uint64_t> mod=up.find_mtime_and_size(remote_);
	if (mod.first==mtime && mod.second==file_sz)
		return; //TODO: add an optional MD5 check?

	//Woohoo! We need to upload the file.
	upload_content_ptr up_data(new upload_content());
	up_data->conn_ = conn_;
	up_data->mtime_ = mtime;
	up_data->remote_ = remote_;
	up_data->orig_size_=file_sz;

	VLOG(2) << "Starting upload of " << path_ << " as "
			  << remote_;

	bool do_compress = should_compress(path_, file_sz);
	if (do_compress)
	{
		zipped_callback on_finish=boost::bind(
					&file_uploader::start_upload, shared_from_this(),
					agenda, up_data, _1, true);

		sync_task_ptr task(new file_compressor(path_, conn_, on_finish));
		agenda->schedule(task);
	} else
	{
		handle_t fl(open(path_.c_str(), O_RDONLY) | libc_die);
		zip_result_ptr files(new compressed_result(path_, fl.size()));
		start_upload(agenda, up_data, files, false);
	}
}

//void file_uploader::simple_upload(agenda_ptr ag, upload_content_ptr content)
//{
//	//Simple upload
//	header_map_t hmap;
//	hmap["x-amz-meta-compressed"] = "false";
//	hmap["Content-Type"] = "application/x-binary";
//	hmap["x-amz-meta-last-modified"] = int_to_string(content->mtime_);
//	s3_connection up(conn_);

////	handle_t file(open(path_.c_str(), O_RDONLY));
////	int64_t sz=lseek64(file.get(), 0, SEEK_END);
////	hmap["x-amz-meta-size"] = int_to_string(sz);
////	std::string etag=up.upload_data(remote_,
////									file, sz, 0, hmap);
//}

void file_uploader::start_upload(agenda_ptr ag,
								 upload_content_ptr content,
								 zip_result_ptr files,
								 bool compressed)
{
	uint64_t size = 0;
	for(auto iter=files->sizes_.begin();iter!=files->sizes_.end();++iter)
		size+=*iter;

	size_t number_of_segments= size/conn_->segment_size_ +
			((size%conn_->segment_size_)==0 ? 0:1);
	if (number_of_segments>MAX_PART_NUM)
		err(errFatal) << "File "<<remote_ <<" is too big";

	content->num_parts_ = number_of_segments;
	content->etags_.resize(number_of_segments);

	header_map_t hmap;
	hmap["x-amz-meta-compressed"] = compressed ? "true" : "false";
	hmap["Content-Type"] = "application/x-binary";
	hmap["x-amz-meta-last-modified"] = int_to_string(content->mtime_);
	hmap["x-amz-meta-size"] = content->orig_size_;
	s3_connection up(conn_);
	content->upload_id_=up.initiate_multipart(remote_, hmap);

	//Now create file pumps
	size_t num_per_pump = number_of_segments / conn_->max_readers_ + 1;
	for(int f=0;f<number_of_segments;f+=num_per_pump)
	{
		size_t num_cur = number_of_segments-f;
		if (num_cur > num_per_pump)
			num_cur = num_per_pump;

		sync_task_ptr task(new file_pump(content, files, f, num_cur));
		ag->schedule(task);
	}
}

void file_deleter::operator()(agenda_ptr agenda)
{
	VLOG(2) << "Removing " << remote_;
	s3_connection up(conn_);
	up.read_fully("DELETE", remote_);
}
<commit_msg>Preparation work<commit_after>#include "uploader.h"
#include "scope_guard.h"
#include "errors.h"
#include "context.h"

#include <iostream>
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <boost/bind.hpp>
#include "workaround.hpp"
#include "compressor.h"

#define MIN_PART_SIZE (16*1024*1024)
#define MIN_ALLOWED_PART_SIZE (16*1024*1024)
#define MAX_PART_NUM 10000

using namespace es3;
using namespace boost::filesystem;

struct es3::upload_content
{
	upload_content() : num_parts_(), num_completed_() {}
	context_ptr conn_;
	std::string upload_id_;
	std::string remote_;
	uint64_t orig_size_;
	time_t mtime_;

	std::mutex lock_;
	size_t num_parts_;
	size_t num_completed_;
	std::vector<std::string> etags_;
};

class part_upload_task : public sync_task
{
	size_t num_;
	upload_content_ptr content_;

	segment_ptr segment_;
public:
	part_upload_task(size_t num, upload_content_ptr content,
					 segment_ptr segment)
		: num_(num), content_(content), segment_(segment)
	{
	}

	virtual void operator()(agenda_ptr agenda)
	{
		VLOG(2) << "Starting upload of a part " << num_ << " of "
				<< content_->remote_;

		struct sched_param param;
		param.sched_priority = 1+num_*90/content_->num_parts_;
		pthread_setschedparam(pthread_self(), SCHED_RR, &param);

		std::string part_path=content_->remote_+
				"?partNumber="+int_to_string(num_+1)
				+"&uploadId="+content_->upload_id_;

		s3_connection up(content_->conn_);
		std::string etag=up.upload_data(part_path,
										&segment_->data_[0],
										segment_->data_.size());
		if (etag.empty())
			err(errWarn) << "Failed to upload a part " << num_;

		//Check if the upload is completed
		guard_t g(content_->lock_);
		content_->num_completed_++;
		content_->etags_.at(num_) = etag;

		VLOG(2) << "Uploaded part " << num_ << " of "<< content_->remote_
				<< " with etag=" << etag
				<< ", total=" << content_->num_parts_
				<< ", sent=" << content_->num_completed_ << ".";

		if (content_->num_completed_ == content_->num_parts_)
		{
			VLOG(2) << "Assembling "<< content_->remote_ <<".";
			//We've completed the upload!
			s3_connection up2(content_->conn_);
			up2.complete_multipart(content_->remote_+
								   "?uploadId="+content_->upload_id_,
								   content_->etags_);
		}
	}
};

class file_pump : public sync_task,
		public boost::enable_shared_from_this<file_pump>
{
	upload_content_ptr content_;
	zip_result_ptr files_;
	size_t cur_segment_, number_of_segments_;
public:
	file_pump(upload_content_ptr content,
		zip_result_ptr files, size_t cur_segment, size_t number_of_segments) :
		content_(content), files_(files),
		cur_segment_(cur_segment), number_of_segments_(number_of_segments)
	{
	}

	virtual std::string get_class() const
	{
		return "pump"+int_to_string(get_class_limit());
	}

	virtual int get_class_limit() const
	{
		return content_->conn_->max_readers_;
	}

	virtual void operator()(agenda_ptr agenda)
	{
		context_ptr ctx = content_->conn_;
		uint64_t start_offset = ctx->segment_size_*cur_segment_;

		//First, skip to our piece
		uint64_t skipped_so_far=0, offset_within_=0;
		int cur_piece=0;
		for(int f=0;f<files_->sizes_.size();++f)
		{
			uint64_t cur_size = files_->sizes_.at(f);
			if (start_offset<(skipped_so_far+cur_size))
			{
				cur_piece = f;
				offset_within_ = start_offset-skipped_so_far;
				assert(offset_within_<cur_size);
				break;
			}
			skipped_so_far+=cur_size;
		}

		//Now read data!!!
		for(int f=0;f<number_of_segments_;++f)
		{
			segment_ptr seg = ctx->get_segment();
			seg->data_.resize(ctx->segment_size_, 0);

			uint64_t segment_read_so_far=0;
			while(segment_read_so_far<seg->data_.size())
			{
				//Note that we're duplicating the handle because other
				//file pumps might be using it
				std::cerr<<"Opening " <<files_->files_.at(cur_piece).c_str() << std::endl;
				handle_t cur_fl(open(
									files_->files_.at(cur_piece).c_str(), O_RDONLY));
				std::cerr<<"Opened " <<files_->files_.at(cur_piece).c_str() << std::endl;
				lseek64(cur_fl.get(), offset_within_, SEEK_SET) | libc_die;
				std::cerr<<"Seeked " <<files_->files_.at(cur_piece).c_str() << std::endl;
				uint64_t cur_piece_size=files_->sizes_.at(cur_piece);

				uint64_t remaining_size =
						std::min(ctx->segment_size_-segment_read_so_far,
								 cur_piece_size-offset_within_);
				while(remaining_size>0)
				{
					char buf[65536*8];
					size_t chunk=std::min(remaining_size,
										  uint64_t(sizeof(buf)));
					size_t res=read(cur_fl.get(), buf, chunk) | libc_die;
					assert(res!=0);
					memcpy(&seg->data_[segment_read_so_far], buf, res);

					segment_read_so_far+=res;
					offset_within_+=res;
					remaining_size-=res;
				}

				//We've run out of file piece, switch to the next one
				if (cur_piece==files_->sizes_.size()-1)
				{
					//No other pieces and this is the last segment
					seg->data_.resize(segment_read_so_far);
				} else
				{
					if (cur_piece_size==offset_within_)
					{
						cur_piece++;
						offset_within_=0;
					} else
					{
						assert(segment_read_so_far==ctx->segment_size_);
					}
				}
			}
			assert(segment_read_so_far==ctx->segment_size_
				   || f==number_of_segments_-1);

			sync_task_ptr task(new part_upload_task(cur_segment_+f,
													content_, seg));
			agenda->schedule(task);
		}
	}
};

void file_uploader::operator()(agenda_ptr agenda)
{
	uint64_t file_sz=file_size(path_);
	time_t mtime=last_write_time(path_);

	//Check the modification date of the file locally and on the
	//remote side
	s3_connection up(conn_);
	std::pair<time_t, uint64_t> mod=up.find_mtime_and_size(remote_);
	if (mod.first==mtime && mod.second==file_sz)
		return; //TODO: add an optional MD5 check?

	//Woohoo! We need to upload the file.
	upload_content_ptr up_data(new upload_content());
	up_data->conn_ = conn_;
	up_data->mtime_ = mtime;
	up_data->remote_ = remote_;
	up_data->orig_size_=file_sz;

	VLOG(2) << "Starting upload of " << path_ << " as "
			  << remote_;

	bool do_compress = should_compress(path_, file_sz);
	//Prepare upload
	header_map_t hmap;
	hmap["x-amz-meta-compressed"] = do_compress ? "true" : "false";
	hmap["Content-Type"] = "application/x-binary";
	hmap["x-amz-meta-last-modified"] = int_to_string(up_data->mtime_);
	hmap["x-amz-meta-size"] = int_to_string(up_data->orig_size_);
	s3_connection up_prep(conn_);
	up_data->upload_id_=up_prep.initiate_multipart(remote_, hmap);

	if (do_compress)
	{
		zipped_callback on_finish=boost::bind(
					&file_uploader::start_upload, shared_from_this(),
					agenda, up_data, _1, true);

		sync_task_ptr task(new file_compressor(path_, conn_, on_finish));
		agenda->schedule(task);
	} else
	{
		handle_t fl(open(path_.c_str(), O_RDONLY) | libc_die);
		zip_result_ptr files(new compressed_result(path_, fl.size()));
		start_upload(agenda, up_data, files, false);
	}
}

//void file_uploader::simple_upload(agenda_ptr ag, upload_content_ptr content)
//{
//	//Simple upload
//	header_map_t hmap;
//	hmap["x-amz-meta-compressed"] = "false";
//	hmap["Content-Type"] = "application/x-binary";
//	hmap["x-amz-meta-last-modified"] = int_to_string(content->mtime_);
//	s3_connection up(conn_);

////	handle_t file(open(path_.c_str(), O_RDONLY));
////	int64_t sz=lseek64(file.get(), 0, SEEK_END);
////	hmap["x-amz-meta-size"] = int_to_string(sz);
////	std::string etag=up.upload_data(remote_,
////									file, sz, 0, hmap);
//}

void file_uploader::start_upload(agenda_ptr ag,
								 upload_content_ptr content,
								 zip_result_ptr files,
								 bool compressed)
{
	uint64_t size = 0;
	for(auto iter=files->sizes_.begin();iter!=files->sizes_.end();++iter)
		size+=*iter;

	size_t number_of_segments= size/conn_->segment_size_ +
			((size%conn_->segment_size_)==0 ? 0:1);
	if (number_of_segments>MAX_PART_NUM)
		err(errFatal) << "File "<<remote_ <<" is too big";

	content->num_parts_ = number_of_segments;
	content->etags_.resize(number_of_segments);

	//Now create file pumps
	size_t num_per_pump = number_of_segments / conn_->max_readers_ + 1;
	for(int f=0;f<number_of_segments;f+=num_per_pump)
	{
		size_t num_cur = number_of_segments-f;
		if (num_cur > num_per_pump)
			num_cur = num_per_pump;

		sync_task_ptr task(new file_pump(content, files, f, num_cur));
		ag->schedule(task);
	}
}

void file_deleter::operator()(agenda_ptr agenda)
{
	VLOG(2) << "Removing " << remote_;
	s3_connection up(conn_);
	up.read_fully("DELETE", remote_);
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkImageAccumulate.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkImageAccumulate.h"

#include "vtkImageData.h"
#include "vtkImageStencilData.h"
#include "vtkImageStencilIterator.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"

#include <math.h>

vtkStandardNewMacro(vtkImageAccumulate);

//----------------------------------------------------------------------------
// Constructor sets default values
vtkImageAccumulate::vtkImageAccumulate()
{
  for (int idx = 0; idx < 3; ++idx)
    {
    this->ComponentSpacing[idx] = 1.0;
    this->ComponentOrigin[idx] = 0.0;
    this->ComponentExtent[idx*2] = 0;
    this->ComponentExtent[idx*2+1] = 0;
    }
  this->ComponentExtent[1] = 255;

  this->ReverseStencil = 0;

  this->Min[0] = this->Min[1] = this->Min[2] = 0.0;
  this->Max[0] = this->Max[1] = this->Max[2] = 0.0;
  this->Mean[0] = this->Mean[1] = this->Mean[2] = 0.0;
  this->StandardDeviation[0] = this->StandardDeviation[1] =
    this->StandardDeviation[2] = 0.0;
  this->VoxelCount = 0;
  this->IgnoreZero = 0;

  // we have the image input and the optional stencil input
  this->SetNumberOfInputPorts(2);
}

//----------------------------------------------------------------------------
vtkImageAccumulate::~vtkImageAccumulate()
{
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::SetComponentExtent(int extent[6])
{
  int idx, modified = 0;

  for (idx = 0; idx < 6; ++idx)
    {
    if (this->ComponentExtent[idx] != extent[idx])
      {
      this->ComponentExtent[idx] = extent[idx];
      modified = 1;
      }
    }
  if (modified)
    {
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::SetComponentExtent(int minX, int maxX,
                                            int minY, int maxY,
                                            int minZ, int maxZ)
{
  int extent[6];

  extent[0] = minX;  extent[1] = maxX;
  extent[2] = minY;  extent[3] = maxY;
  extent[4] = minZ;  extent[5] = maxZ;
  this->SetComponentExtent(extent);
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::GetComponentExtent(int extent[6])
{
  for (int idx = 0; idx < 6; ++idx)
    {
    extent[idx] = this->ComponentExtent[idx];
    }
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::SetStencilData(vtkImageStencilData *stencil)
{
  this->SetInputData(1, stencil);
}

//----------------------------------------------------------------------------
vtkImageStencilData *vtkImageAccumulate::GetStencil()
{
  if (this->GetNumberOfInputConnections(1) < 1)
    {
    return 0;
    }
  return vtkImageStencilData::SafeDownCast(
    this->GetExecutive()->GetInputData(1, 0));
}

//----------------------------------------------------------------------------
// This templated function executes the filter for any type of data.
template <class T>
void vtkImageAccumulateExecute(vtkImageAccumulate *self,
                               vtkImageData *inData, T *,
                               vtkImageData *outData, vtkIdType *outPtr,
                               double min[3], double max[3],
                               double mean[3],
                               double standardDeviation[3],
                               vtkIdType *voxelCount,
                               int* updateExtent)
{
  // variables used to compute statistics (filter handles max 3 components)
  double sum[3];
  sum[0] = sum[1] = sum[2] = 0.0;
  double sumSqr[3];
  sumSqr[0] = sumSqr[1] = sumSqr[2] = 0.0;
  min[0] = min[1] = min[2] = VTK_DOUBLE_MAX;
  max[0] = max[1] = max[2] = VTK_DOUBLE_MIN;
  standardDeviation[0] = standardDeviation[1] = standardDeviation[2] = 0.0;
  *voxelCount = 0;

  // input's number of components is used as output dimensionality
  int numC = inData->GetNumberOfScalarComponents();

  // get information for output data
  int outExtent[6];
  outData->GetExtent(outExtent);
  vtkIdType outIncs[3];
  outData->GetIncrements(outIncs);
  double origin[3];
  outData->GetOrigin(origin);
  double spacing[3];
  outData->GetSpacing(spacing);

  // zero count in every bin
  vtkIdType size = 1;
  size *= (outExtent[1] - outExtent[0] + 1);
  size *= (outExtent[3] - outExtent[2] + 1);
  size *= (outExtent[5] - outExtent[4] + 1);
  for (vtkIdType j = 0; j < size; j++)
    {
    outPtr[j] = 0;
    }

  vtkImageStencilData *stencil = self->GetStencil();
  bool reverseStencil = (self->GetReverseStencil() != 0);
  bool ignoreZero = (self->GetIgnoreZero() != 0);

  vtkImageStencilIterator<T> inIter(inData, stencil, updateExtent, self);

  while (!inIter.IsAtEnd())
    {
    if (inIter.IsInStencil() ^ reverseStencil)
      {
      T *inPtr = inIter.BeginSpan();
      T *spanEndPtr = inIter.EndSpan();

      while (inPtr != spanEndPtr)
        {
        // find the bin for this pixel.
        bool outOfBounds = false;
        vtkIdType *outPtrC = outPtr;
        for (int idxC = 0; idxC < numC; ++idxC)
          {
          double v = static_cast<double>(*inPtr++);
          if (!ignoreZero || v != 0)
            {
            // gather statistics
            sum[idxC] += v;
            sumSqr[idxC] += v*v;
            if (v > max[idxC])
              {
              max[idxC] = v;
              }
            if (v < min[idxC])
              {
              min[idxC] = v;
              }
            (*voxelCount)++;
            }

          // compute the index
          int outIdx = vtkMath::Floor((v - origin[idxC]) / spacing[idxC]);

          // verify that it is in range
          if (outIdx >= outExtent[idxC*2] && outIdx <= outExtent[idxC*2+1])
            {
            outPtrC += (outIdx - outExtent[idxC*2]) * outIncs[idxC];
            }
          else
            {
            outOfBounds = true;
            }
          }

        // increment the bin
        if (!outOfBounds)
          {
          ++(*outPtrC);
          }
        }
      }

    inIter.NextSpan();
    }

  // initialize the statistics
  mean[0] = 0;
  mean[1] = 0;
  mean[2] = 0;

  standardDeviation[0] = 0;
  standardDeviation[1] = 0;
  standardDeviation[2] = 0;

  if (*voxelCount != 0) // avoid the div0
    {
    double n = static_cast<double>(*voxelCount);
    mean[0] = sum[0]/n;
    mean[1] = sum[1]/n;
    mean[2] = sum[2]/n;

    if (*voxelCount - 1 != 0) // avoid the div0
      {
      double m = static_cast<double>(*voxelCount - 1);
      standardDeviation[0] = sqrt((sumSqr[0] - mean[0]*mean[0]*n)/m);
      standardDeviation[1] = sqrt((sumSqr[1] - mean[1]*mean[1]*n)/m);
      standardDeviation[2] = sqrt((sumSqr[2] - mean[2]*mean[2]*n)/m);
      }
    }

}

//----------------------------------------------------------------------------
// This method is passed a input and output Data, and executes the filter
// algorithm to fill the output from the input.
// It just executes a switch statement to call the correct function for
// the Datas data types.
int vtkImageAccumulate::RequestData(
  vtkInformation* vtkNotUsed( request ),
  vtkInformationVector** inputVector,
  vtkInformationVector* outputVector)
{
  void *inPtr;
  void *outPtr;

  // get the input
  vtkInformation* in1Info = inputVector[0]->GetInformationObject(0);
  vtkImageData *inData = vtkImageData::SafeDownCast(
    in1Info->Get(vtkDataObject::DATA_OBJECT()));
  int *uExt = in1Info->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT());

  // get the output
  vtkInformation *outInfo = outputVector->GetInformationObject(0);
  vtkImageData *outData = vtkImageData::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkDebugMacro(<<"Executing image accumulate");

  // We need to allocate our own scalars since we are overriding
  // the superclasses "Execute()" method.
  outData->SetExtent(outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()));
  outData->AllocateScalars(outInfo);

  vtkDataArray *inArray = this->GetInputArrayToProcess(0,inputVector);
  inPtr = inData->GetArrayPointerForExtent(inArray, uExt);
  outPtr = outData->GetScalarPointer();

  // Components turned into x, y and z
  if (inData->GetNumberOfScalarComponents() > 3)
    {
    vtkErrorMacro("This filter can handle up to 3 components");
    return 0;
    }

  // this filter expects that output is type int.
  if (outData->GetScalarType() != VTK_ID_TYPE)
    {
    vtkErrorMacro(<< "Execute: out ScalarType " << outData->GetScalarType()
                  << " must be vtkIdType\n");
    return 0;
    }

  switch (inData->GetScalarType())
    {
    vtkTemplateMacro(vtkImageAccumulateExecute( this,
                                                inData,
                                                static_cast<VTK_TT *>(inPtr),
                                                outData,
                                                static_cast<vtkIdType *>(outPtr),
                                                this->Min, this->Max,
                                                this->Mean,
                                                this->StandardDeviation,
                                                &this->VoxelCount,
                                                uExt ));
    default:
      vtkErrorMacro(<< "Execute: Unknown ScalarType");
      return 0;
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkImageAccumulate::RequestInformation (
  vtkInformation* vtkNotUsed(request),
  vtkInformationVector** vtkNotUsed(inputVector),
  vtkInformationVector* outputVector)
{
  // get the info objects
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(),
               this->ComponentExtent,6);
  outInfo->Set(vtkDataObject::ORIGIN(),this->ComponentOrigin,3);
  outInfo->Set(vtkDataObject::SPACING(),this->ComponentSpacing,3);

  vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_ID_TYPE, 1);
  return 1;
}

//----------------------------------------------------------------------------
// Get ALL of the input.
int vtkImageAccumulate::RequestUpdateExtent (
  vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector,
  vtkInformationVector* vtkNotUsed( outputVector ))
{
  // get the info objects
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* stencilInfo = 0;
  if(inputVector[1]->GetNumberOfInformationObjects() > 0)
    {
    stencilInfo = inputVector[1]->GetInformationObject(0);
    }

  // Use the whole extent of the first input as the update extent for
  // both inputs.  This way the stencil will be the same size as the
  // input.
  int extent[6] = {0,-1,0,-1,0,-1};
  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent, 6);
  if(stencilInfo)
    {
    stencilInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
                     extent, 6);
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkImageAccumulate::FillInputPortInformation(
  int port, vtkInformation* info)
{
  if (port == 1)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageStencilData");
    // the stencil input is optional
    info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
    }
  else
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
    }
  return 1;
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Mean: ("
     << this->Mean[0] << ", "
     << this->Mean[1] << ", "
     << this->Mean[2] << ")\n";
  os << indent << "Min: ("
     << this->Min[0] << ", "
     << this->Min[1] << ", "
     << this->Min[2] << ")\n";
  os << indent << "Max: ("
     << this->Max[0] << ", "
     << this->Max[1] << ", "
     << this->Max[2] << ")\n";
  os << indent << "StandardDeviation: ("
     << this->StandardDeviation[0] << ", "
     << this->StandardDeviation[1] << ", "
     << this->StandardDeviation[2] << ")\n";
  os << indent << "VoxelCount: " << this->VoxelCount << "\n";
  os << indent << "Stencil: " << this->GetStencil() << "\n";
  os << indent << "ReverseStencil: " << (this->ReverseStencil ?
                                         "On\n" : "Off\n");
  os << indent << "IgnoreZero: " << (this->IgnoreZero ? "On" : "Off") << "\n";

  os << indent << "ComponentOrigin: ( "
     << this->ComponentOrigin[0] << ", "
     << this->ComponentOrigin[1] << ", "
     << this->ComponentOrigin[2] << " )\n";

  os << indent << "ComponentSpacing: ( "
     << this->ComponentSpacing[0] << ", "
     << this->ComponentSpacing[1] << ", "
     << this->ComponentSpacing[2] << " )\n";

  os << indent << "ComponentExtent: ( "
     << this->ComponentExtent[0] << "," << this->ComponentExtent[1] << " "
     << this->ComponentExtent[2] << "," << this->ComponentExtent[3] << " "
     << this->ComponentExtent[4] << "," << this->ComponentExtent[5] << " }\n";
}

<commit_msg>vtkImageAccumulate: handle errors in the actual algorithm<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkImageAccumulate.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkImageAccumulate.h"

#include "vtkImageData.h"
#include "vtkImageStencilData.h"
#include "vtkImageStencilIterator.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"

#include <math.h>

vtkStandardNewMacro(vtkImageAccumulate);

//----------------------------------------------------------------------------
// Constructor sets default values
vtkImageAccumulate::vtkImageAccumulate()
{
  for (int idx = 0; idx < 3; ++idx)
    {
    this->ComponentSpacing[idx] = 1.0;
    this->ComponentOrigin[idx] = 0.0;
    this->ComponentExtent[idx*2] = 0;
    this->ComponentExtent[idx*2+1] = 0;
    }
  this->ComponentExtent[1] = 255;

  this->ReverseStencil = 0;

  this->Min[0] = this->Min[1] = this->Min[2] = 0.0;
  this->Max[0] = this->Max[1] = this->Max[2] = 0.0;
  this->Mean[0] = this->Mean[1] = this->Mean[2] = 0.0;
  this->StandardDeviation[0] = this->StandardDeviation[1] =
    this->StandardDeviation[2] = 0.0;
  this->VoxelCount = 0;
  this->IgnoreZero = 0;

  // we have the image input and the optional stencil input
  this->SetNumberOfInputPorts(2);
}

//----------------------------------------------------------------------------
vtkImageAccumulate::~vtkImageAccumulate()
{
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::SetComponentExtent(int extent[6])
{
  int idx, modified = 0;

  for (idx = 0; idx < 6; ++idx)
    {
    if (this->ComponentExtent[idx] != extent[idx])
      {
      this->ComponentExtent[idx] = extent[idx];
      modified = 1;
      }
    }
  if (modified)
    {
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::SetComponentExtent(int minX, int maxX,
                                            int minY, int maxY,
                                            int minZ, int maxZ)
{
  int extent[6];

  extent[0] = minX;  extent[1] = maxX;
  extent[2] = minY;  extent[3] = maxY;
  extent[4] = minZ;  extent[5] = maxZ;
  this->SetComponentExtent(extent);
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::GetComponentExtent(int extent[6])
{
  for (int idx = 0; idx < 6; ++idx)
    {
    extent[idx] = this->ComponentExtent[idx];
    }
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::SetStencilData(vtkImageStencilData *stencil)
{
  this->SetInputData(1, stencil);
}

//----------------------------------------------------------------------------
vtkImageStencilData *vtkImageAccumulate::GetStencil()
{
  if (this->GetNumberOfInputConnections(1) < 1)
    {
    return 0;
    }
  return vtkImageStencilData::SafeDownCast(
    this->GetExecutive()->GetInputData(1, 0));
}

//----------------------------------------------------------------------------
// This templated function executes the filter for any type of data.
template <class T>
int vtkImageAccumulateExecute(vtkImageAccumulate *self,
                              vtkImageData *inData, T *,
                              vtkImageData *outData, vtkIdType *outPtr,
                              double min[3], double max[3],
                              double mean[3],
                              double standardDeviation[3],
                              vtkIdType *voxelCount,
                              int* updateExtent)
{
  // variables used to compute statistics (filter handles max 3 components)
  double sum[3];
  sum[0] = sum[1] = sum[2] = 0.0;
  double sumSqr[3];
  sumSqr[0] = sumSqr[1] = sumSqr[2] = 0.0;
  min[0] = min[1] = min[2] = VTK_DOUBLE_MAX;
  max[0] = max[1] = max[2] = VTK_DOUBLE_MIN;
  standardDeviation[0] = standardDeviation[1] = standardDeviation[2] = 0.0;
  *voxelCount = 0;

  // input's number of components is used as output dimensionality
  int numC = inData->GetNumberOfScalarComponents();
  if (numC > 3)
    {
    return 0;
    }

  // get information for output data
  int outExtent[6];
  outData->GetExtent(outExtent);
  vtkIdType outIncs[3];
  outData->GetIncrements(outIncs);
  double origin[3];
  outData->GetOrigin(origin);
  double spacing[3];
  outData->GetSpacing(spacing);

  // zero count in every bin
  vtkIdType size = 1;
  size *= (outExtent[1] - outExtent[0] + 1);
  size *= (outExtent[3] - outExtent[2] + 1);
  size *= (outExtent[5] - outExtent[4] + 1);
  for (vtkIdType j = 0; j < size; j++)
    {
    outPtr[j] = 0;
    }

  vtkImageStencilData *stencil = self->GetStencil();
  bool reverseStencil = (self->GetReverseStencil() != 0);
  bool ignoreZero = (self->GetIgnoreZero() != 0);

  vtkImageStencilIterator<T> inIter(inData, stencil, updateExtent, self);

  while (!inIter.IsAtEnd())
    {
    if (inIter.IsInStencil() ^ reverseStencil)
      {
      T *inPtr = inIter.BeginSpan();
      T *spanEndPtr = inIter.EndSpan();

      while (inPtr != spanEndPtr)
        {
        // find the bin for this pixel.
        bool outOfBounds = false;
        vtkIdType *outPtrC = outPtr;
        for (int idxC = 0; idxC < numC; ++idxC)
          {
          double v = static_cast<double>(*inPtr++);
          if (!ignoreZero || v != 0)
            {
            // gather statistics
            sum[idxC] += v;
            sumSqr[idxC] += v*v;
            if (v > max[idxC])
              {
              max[idxC] = v;
              }
            if (v < min[idxC])
              {
              min[idxC] = v;
              }
            (*voxelCount)++;
            }

          // compute the index
          int outIdx = vtkMath::Floor((v - origin[idxC]) / spacing[idxC]);

          // verify that it is in range
          if (outIdx >= outExtent[idxC*2] && outIdx <= outExtent[idxC*2+1])
            {
            outPtrC += (outIdx - outExtent[idxC*2]) * outIncs[idxC];
            }
          else
            {
            outOfBounds = true;
            }
          }

        // increment the bin
        if (!outOfBounds)
          {
          ++(*outPtrC);
          }
        }
      }

    inIter.NextSpan();
    }

  // initialize the statistics
  mean[0] = 0;
  mean[1] = 0;
  mean[2] = 0;

  standardDeviation[0] = 0;
  standardDeviation[1] = 0;
  standardDeviation[2] = 0;

  if (*voxelCount != 0) // avoid the div0
    {
    double n = static_cast<double>(*voxelCount);
    mean[0] = sum[0]/n;
    mean[1] = sum[1]/n;
    mean[2] = sum[2]/n;

    if (*voxelCount - 1 != 0) // avoid the div0
      {
      double m = static_cast<double>(*voxelCount - 1);
      standardDeviation[0] = sqrt((sumSqr[0] - mean[0]*mean[0]*n)/m);
      standardDeviation[1] = sqrt((sumSqr[1] - mean[1]*mean[1]*n)/m);
      standardDeviation[2] = sqrt((sumSqr[2] - mean[2]*mean[2]*n)/m);
      }
    }

  return 1;
}

//----------------------------------------------------------------------------
// This method is passed a input and output Data, and executes the filter
// algorithm to fill the output from the input.
// It just executes a switch statement to call the correct function for
// the Datas data types.
int vtkImageAccumulate::RequestData(
  vtkInformation* vtkNotUsed( request ),
  vtkInformationVector** inputVector,
  vtkInformationVector* outputVector)
{
  void *inPtr;
  void *outPtr;

  // get the input
  vtkInformation* in1Info = inputVector[0]->GetInformationObject(0);
  vtkImageData *inData = vtkImageData::SafeDownCast(
    in1Info->Get(vtkDataObject::DATA_OBJECT()));
  int *uExt = in1Info->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT());

  // get the output
  vtkInformation *outInfo = outputVector->GetInformationObject(0);
  vtkImageData *outData = vtkImageData::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkDebugMacro(<<"Executing image accumulate");

  // We need to allocate our own scalars since we are overriding
  // the superclasses "Execute()" method.
  outData->SetExtent(outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()));
  outData->AllocateScalars(outInfo);

  vtkDataArray *inArray = this->GetInputArrayToProcess(0,inputVector);
  inPtr = inData->GetArrayPointerForExtent(inArray, uExt);
  outPtr = outData->GetScalarPointer();

  // Components turned into x, y and z
  if (inData->GetNumberOfScalarComponents() > 3)
    {
    vtkErrorMacro("This filter can handle up to 3 components");
    return 0;
    }

  // this filter expects that output is type int.
  if (outData->GetScalarType() != VTK_ID_TYPE)
    {
    vtkErrorMacro(<< "Execute: out ScalarType " << outData->GetScalarType()
                  << " must be vtkIdType\n");
    return 0;
    }

  int retVal = 0;
  switch (inData->GetScalarType())
    {
    vtkTemplateMacro(retVal = vtkImageAccumulateExecute( this,
                                                inData,
                                                static_cast<VTK_TT *>(inPtr),
                                                outData,
                                                static_cast<vtkIdType *>(outPtr),
                                                this->Min, this->Max,
                                                this->Mean,
                                                this->StandardDeviation,
                                                &this->VoxelCount,
                                                uExt ));
    default:
      vtkErrorMacro(<< "Execute: Unknown ScalarType");
      return 0;
    }

  return retVal;
}

//----------------------------------------------------------------------------
int vtkImageAccumulate::RequestInformation (
  vtkInformation* vtkNotUsed(request),
  vtkInformationVector** vtkNotUsed(inputVector),
  vtkInformationVector* outputVector)
{
  // get the info objects
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(),
               this->ComponentExtent,6);
  outInfo->Set(vtkDataObject::ORIGIN(),this->ComponentOrigin,3);
  outInfo->Set(vtkDataObject::SPACING(),this->ComponentSpacing,3);

  vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_ID_TYPE, 1);
  return 1;
}

//----------------------------------------------------------------------------
// Get ALL of the input.
int vtkImageAccumulate::RequestUpdateExtent (
  vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector,
  vtkInformationVector* vtkNotUsed( outputVector ))
{
  // get the info objects
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* stencilInfo = 0;
  if(inputVector[1]->GetNumberOfInformationObjects() > 0)
    {
    stencilInfo = inputVector[1]->GetInformationObject(0);
    }

  // Use the whole extent of the first input as the update extent for
  // both inputs.  This way the stencil will be the same size as the
  // input.
  int extent[6] = {0,-1,0,-1,0,-1};
  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent, 6);
  if(stencilInfo)
    {
    stencilInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
                     extent, 6);
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkImageAccumulate::FillInputPortInformation(
  int port, vtkInformation* info)
{
  if (port == 1)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageStencilData");
    // the stencil input is optional
    info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
    }
  else
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
    }
  return 1;
}

//----------------------------------------------------------------------------
void vtkImageAccumulate::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Mean: ("
     << this->Mean[0] << ", "
     << this->Mean[1] << ", "
     << this->Mean[2] << ")\n";
  os << indent << "Min: ("
     << this->Min[0] << ", "
     << this->Min[1] << ", "
     << this->Min[2] << ")\n";
  os << indent << "Max: ("
     << this->Max[0] << ", "
     << this->Max[1] << ", "
     << this->Max[2] << ")\n";
  os << indent << "StandardDeviation: ("
     << this->StandardDeviation[0] << ", "
     << this->StandardDeviation[1] << ", "
     << this->StandardDeviation[2] << ")\n";
  os << indent << "VoxelCount: " << this->VoxelCount << "\n";
  os << indent << "Stencil: " << this->GetStencil() << "\n";
  os << indent << "ReverseStencil: " << (this->ReverseStencil ?
                                         "On\n" : "Off\n");
  os << indent << "IgnoreZero: " << (this->IgnoreZero ? "On" : "Off") << "\n";

  os << indent << "ComponentOrigin: ( "
     << this->ComponentOrigin[0] << ", "
     << this->ComponentOrigin[1] << ", "
     << this->ComponentOrigin[2] << " )\n";

  os << indent << "ComponentSpacing: ( "
     << this->ComponentSpacing[0] << ", "
     << this->ComponentSpacing[1] << ", "
     << this->ComponentSpacing[2] << " )\n";

  os << indent << "ComponentExtent: ( "
     << this->ComponentExtent[0] << "," << this->ComponentExtent[1] << " "
     << this->ComponentExtent[2] << "," << this->ComponentExtent[3] << " "
     << this->ComponentExtent[4] << "," << this->ComponentExtent[5] << " }\n";
}

<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

ll N, Q;

vector<ll> waru(ll x) {
  vector<ll> ans;
  ans.push_back(x);
  while (x >= 0) {
    x = (x-1)/N;
    ans.push_back(x);
  }
  reverse(ans.begin(), ans.end());
  return ans;
}

ll solve(ll x, ll y) {
  vector<ll> V = waru(x);
  vector<ll> W = waru(y);
  int ind = 0;
  ll ans = 0;
  while (ind < (int)V.size() && ind < (int)W.size()) {
    if (V[ind] == W[ind]) {
      ans = V[ind];
      ind++;
    } else {
      break;
    }
  }
  return ans;
}

int main () {
  cin >> N >> Q;
  for (auto i = 0; i < Q; ++i) {
    ll v, w;
    cin >> v >> w;
    v--;
    w--;
    cout << solve(v, w) + 1 << endl;
  }
}
<commit_msg>submit B.cpp to 'B - Evergrowing Tree' (cf17-relay-open) [C++14 (GCC 5.4.1)]<commit_after>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

ll N, Q;

vector<ll> waru(ll x) {
  vector<ll> ans;
  ans.push_back(x);
  while (x > 0) {
    x = (x-1)/N;
    ans.push_back(x);
  }
  reverse(ans.begin(), ans.end());
  return ans;
}

ll solve(ll x, ll y) {
  vector<ll> V = waru(x);
  vector<ll> W = waru(y);
  int ind = 0;
  ll ans = 0;
  while (ind < (int)V.size() && ind < (int)W.size()) {
    if (V[ind] == W[ind]) {
      ans = V[ind];
      ind++;
    } else {
      break;
    }
  }
  return ans;
}

int main () {
  cin >> N >> Q;
  for (auto i = 0; i < Q; ++i) {
    ll v, w;
    cin >> v >> w;
    v--;
    w--;
    cout << solve(v, w) + 1 << endl;
  }
}
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id: AliAnalysisTaskPIDResponse.cxx 43811 2010-09-23 14:13:31Z wiechula $ */
#include <TFile.h>
#include <TChain.h>

#include <AliAnalysisManager.h>
#include <AliInputEventHandler.h>
#include <AliVEventHandler.h>
#include <AliVEvent.h>
#include <AliVParticle.h>
#include <AliVTrack.h>
#include <AliLog.h>
#include <AliPIDResponse.h>
#include <AliESDpid.h>
#include <AliProdInfo.h>

#include "AliAnalysisTaskPIDResponse.h"

ClassImp(AliAnalysisTaskPIDResponse)

//______________________________________________________________________________
AliAnalysisTaskPIDResponse::AliAnalysisTaskPIDResponse():
AliAnalysisTaskSE(),
fIsMC(kFALSE),
fCachePID(kTRUE),
fOADBPath(),
fSpecialDetResponse(),
fPIDResponse(0x0),
fRun(0),
fOldRun(0),
fRecoPass(0),
fIsTunedOnData(kFALSE),
fRecoPassTuned(0),
fUseTPCEtaCorrection(kFALSE)//TODO: In future, default kTRUE
{
  //
  // Dummy constructor
  //
}

//______________________________________________________________________________
AliAnalysisTaskPIDResponse::AliAnalysisTaskPIDResponse(const char* name):
AliAnalysisTaskSE(name),
fIsMC(kFALSE),
fCachePID(kTRUE),
fOADBPath(),
fSpecialDetResponse(),
fPIDResponse(0x0),
fRun(0),
fOldRun(0),
fRecoPass(0),
fIsTunedOnData(kFALSE),
fRecoPassTuned(0),
fUseTPCEtaCorrection(kFALSE)//TODO: In future, default kTRUE
{
  //
  // Default constructor
  //
  DefineInput(0,TChain::Class());
}

//______________________________________________________________________________
AliAnalysisTaskPIDResponse::~AliAnalysisTaskPIDResponse()
{
  //
  // Destructor
  //
}

//______________________________________________________________________________
void AliAnalysisTaskPIDResponse::UserCreateOutputObjects()
{
  //
  // Create the output QA objects
  //

  AliLog::SetClassDebugLevel("AliAnalysisTaskPIDResponse",10);

  //input hander
  AliAnalysisManager *man=AliAnalysisManager::GetAnalysisManager();
  AliInputEventHandler *inputHandler=dynamic_cast<AliInputEventHandler*>(man->GetInputEventHandler());
  if (!inputHandler) AliFatal("Input handler needed");

  //pid response object
  inputHandler->CreatePIDResponse(fIsMC);
  fPIDResponse=inputHandler->GetPIDResponse();
  if (!fPIDResponse) AliFatal("PIDResponse object was not created");

  fPIDResponse->SetOADBPath(AliAnalysisManager::GetOADBPath());
  fPIDResponse->SetCachePID(fCachePID);
  if (!fOADBPath.IsNull()) fPIDResponse->SetOADBPath(fOADBPath.Data());

  if(fIsTunedOnData) fPIDResponse->SetTunedOnData(kTRUE,fRecoPassTuned);

  if (!fSpecialDetResponse.IsNull()){
    TObjArray *arr=fSpecialDetResponse.Tokenize("; ");
    for (Int_t i=0; i<arr->GetEntriesFast();++i){
      TString resp(arr->At(i)->GetName());
      if (resp.BeginsWith("TPC:")){
        resp.ReplaceAll("TPC:","");
        fPIDResponse->SetCustomTPCpidResponse(resp.Data());
        AliInfo(Form("Setting custom TPC response file: '%s'",resp.Data()));
      }
    }
    delete arr;
  }
}

//______________________________________________________________________________
void AliAnalysisTaskPIDResponse::UserExec(Option_t */*option*/)
{
  // Setup the PID response functions and fill the QA histograms
  //
  AliVEvent *event=InputEvent();
  if (!event) return;
  fRun=event->GetRunNumber();

  if (fRun!=fOldRun){
    SetRecoInfo();
    fOldRun=fRun;
  }

  fPIDResponse->SetUseTPCEtaCorrection(fUseTPCEtaCorrection);
  fPIDResponse->InitialiseEvent(event,fRecoPass);
  AliESDpid *pidresp = dynamic_cast<AliESDpid*>(fPIDResponse);
  if(pidresp && AliAnalysisManager::GetAnalysisManager()->GetMCtruthEventHandler()){
      pidresp->SetEventHandler(AliAnalysisManager::GetAnalysisManager()->GetMCtruthEventHandler());
  }
  //create and attach transient PID object
//   if (fCachePID) {
//     fPIDResponse->FillTrackDetectorPID();
//   }
}

//______________________________________________________________________________
void AliAnalysisTaskPIDResponse::SetRecoInfo()
{
  //
  // Set reconstruction information
  //

  //reset information
  fRecoPass=0;

  //Get UserInfo from the current tree
  AliAnalysisManager *mgr=AliAnalysisManager::GetAnalysisManager();
  AliVEventHandler *inputHandler=mgr->GetInputEventHandler();
  if (!inputHandler) return;

  TList *uiList = inputHandler->GetUserInfo();
  AliProdInfo prodInfo(uiList);
  prodInfo.List();

  TTree *tree= (TTree*)inputHandler->GetTree();
  TFile *file= (TFile*)tree->GetCurrentFile();
  if (!file) {
    AliError("Current file not found, cannot set reconstruction information");
    return;
  } else {
    TString fileName(file->GetName());
    fPIDResponse->SetCurrentFile(fileName.Data());
  }

  if (!(prodInfo.IsMC())) {      // reco pass is needed only for data
    fRecoPass = prodInfo.GetRecoPass();
    if (fRecoPass < 0) {   // as last resort we find pass from file name (UGLY, but not stored in ESDs/AODs before LHC12d )
      TString fileName(file->GetName());
      if (fileName.Contains("pass1") ) {
	fRecoPass=1;
      } else if (fileName.Contains("pass2") ) {
	fRecoPass=2;
      } else if (fileName.Contains("pass3") ) {
	fRecoPass=3;
      } else if (fileName.Contains("pass4") ) {
	fRecoPass=4;
      } else if (fileName.Contains("pass5") ) {
	fRecoPass=5;
      }
    }
    if (fRecoPass <= 0) {
      AliError(" ******** Failed to find reconstruction pass number *********");
      AliError(" ******** Insert pass number inside the path of your local file ******");
      AliError(" ******** PID information loaded for 'pass 0': parameters unreliable ******");
    }
  }

}
<commit_msg>Avoid check on reco pass for MC if prodInfo is not present<commit_after>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id: AliAnalysisTaskPIDResponse.cxx 43811 2010-09-23 14:13:31Z wiechula $ */
#include <TFile.h>
#include <TChain.h>

#include <AliAnalysisManager.h>
#include <AliInputEventHandler.h>
#include <AliVEventHandler.h>
#include <AliVEvent.h>
#include <AliVParticle.h>
#include <AliVTrack.h>
#include <AliLog.h>
#include <AliPIDResponse.h>
#include <AliESDpid.h>
#include <AliProdInfo.h>

#include "AliAnalysisTaskPIDResponse.h"

ClassImp(AliAnalysisTaskPIDResponse)

//______________________________________________________________________________
AliAnalysisTaskPIDResponse::AliAnalysisTaskPIDResponse():
AliAnalysisTaskSE(),
fIsMC(kFALSE),
fCachePID(kTRUE),
fOADBPath(),
fSpecialDetResponse(),
fPIDResponse(0x0),
fRun(0),
fOldRun(0),
fRecoPass(0),
fIsTunedOnData(kFALSE),
fRecoPassTuned(0),
fUseTPCEtaCorrection(kFALSE)//TODO: In future, default kTRUE
{
  //
  // Dummy constructor
  //
}

//______________________________________________________________________________
AliAnalysisTaskPIDResponse::AliAnalysisTaskPIDResponse(const char* name):
AliAnalysisTaskSE(name),
fIsMC(kFALSE),
fCachePID(kTRUE),
fOADBPath(),
fSpecialDetResponse(),
fPIDResponse(0x0),
fRun(0),
fOldRun(0),
fRecoPass(0),
fIsTunedOnData(kFALSE),
fRecoPassTuned(0),
fUseTPCEtaCorrection(kFALSE)//TODO: In future, default kTRUE
{
  //
  // Default constructor
  //
  DefineInput(0,TChain::Class());
}

//______________________________________________________________________________
AliAnalysisTaskPIDResponse::~AliAnalysisTaskPIDResponse()
{
  //
  // Destructor
  //
}

//______________________________________________________________________________
void AliAnalysisTaskPIDResponse::UserCreateOutputObjects()
{
  //
  // Create the output QA objects
  //

  AliLog::SetClassDebugLevel("AliAnalysisTaskPIDResponse",10);

  //input hander
  AliAnalysisManager *man=AliAnalysisManager::GetAnalysisManager();
  AliInputEventHandler *inputHandler=dynamic_cast<AliInputEventHandler*>(man->GetInputEventHandler());
  if (!inputHandler) AliFatal("Input handler needed");

  //pid response object
  inputHandler->CreatePIDResponse(fIsMC);
  fPIDResponse=inputHandler->GetPIDResponse();
  if (!fPIDResponse) AliFatal("PIDResponse object was not created");

  fPIDResponse->SetOADBPath(AliAnalysisManager::GetOADBPath());
  fPIDResponse->SetCachePID(fCachePID);
  if (!fOADBPath.IsNull()) fPIDResponse->SetOADBPath(fOADBPath.Data());

  if(fIsTunedOnData) fPIDResponse->SetTunedOnData(kTRUE,fRecoPassTuned);

  if (!fSpecialDetResponse.IsNull()){
    TObjArray *arr=fSpecialDetResponse.Tokenize("; ");
    for (Int_t i=0; i<arr->GetEntriesFast();++i){
      TString resp(arr->At(i)->GetName());
      if (resp.BeginsWith("TPC:")){
        resp.ReplaceAll("TPC:","");
        fPIDResponse->SetCustomTPCpidResponse(resp.Data());
        AliInfo(Form("Setting custom TPC response file: '%s'",resp.Data()));
      }
    }
    delete arr;
  }
}

//______________________________________________________________________________
void AliAnalysisTaskPIDResponse::UserExec(Option_t */*option*/)
{
  // Setup the PID response functions and fill the QA histograms
  //
  AliVEvent *event=InputEvent();
  if (!event) return;
  fRun=event->GetRunNumber();

  if (fRun!=fOldRun){
    SetRecoInfo();
    fOldRun=fRun;
  }

  fPIDResponse->SetUseTPCEtaCorrection(fUseTPCEtaCorrection);
  fPIDResponse->InitialiseEvent(event,fRecoPass);
  AliESDpid *pidresp = dynamic_cast<AliESDpid*>(fPIDResponse);
  if(pidresp && AliAnalysisManager::GetAnalysisManager()->GetMCtruthEventHandler()){
      pidresp->SetEventHandler(AliAnalysisManager::GetAnalysisManager()->GetMCtruthEventHandler());
  }
  //create and attach transient PID object
//   if (fCachePID) {
//     fPIDResponse->FillTrackDetectorPID();
//   }
}

//______________________________________________________________________________
void AliAnalysisTaskPIDResponse::SetRecoInfo()
{
  //
  // Set reconstruction information
  //

  //reset information
  fRecoPass=0;

  //Get UserInfo from the current tree
  AliAnalysisManager *mgr=AliAnalysisManager::GetAnalysisManager();
  AliVEventHandler *inputHandler=mgr->GetInputEventHandler();
  if (!inputHandler) return;

  TList *uiList = inputHandler->GetUserInfo();
  AliProdInfo prodInfo(uiList);
  prodInfo.List();

  TTree *tree= (TTree*)inputHandler->GetTree();
  TFile *file= (TFile*)tree->GetCurrentFile();
  if (!file) {
    AliError("Current file not found, cannot set reconstruction information");
    return;
  } else {
    TString fileName(file->GetName());
    fPIDResponse->SetCurrentFile(fileName.Data());
  }

  if ( (prodInfo.IsMC() == kFALSE) || (fIsMC == kFALSE) ) {      // reco pass is needed only for data
    fRecoPass = prodInfo.GetRecoPass();
    if (fRecoPass < 0) {   // as last resort we find pass from file name (UGLY, but not stored in ESDs/AODs before LHC12d )
      TString fileName(file->GetName());
      if (fileName.Contains("pass1") ) {
	fRecoPass=1;
      } else if (fileName.Contains("pass2") ) {
	fRecoPass=2;
      } else if (fileName.Contains("pass3") ) {
	fRecoPass=3;
      } else if (fileName.Contains("pass4") ) {
	fRecoPass=4;
      } else if (fileName.Contains("pass5") ) {
	fRecoPass=5;
      }
    }
    if (fRecoPass <= 0) {
      AliError(" ******** Failed to find reconstruction pass number *********");
      AliError(" ******** Insert pass number inside the path of your local file ******");
      AliError(" ******** PID information loaded for 'pass 0': parameters unreliable ******");
    }
  }

}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "ClientSession.h"
#include "..\..\PacketType.h"
#include "ClientManager.h"
#include "DatabaseJobContext.h"
#include "DatabaseJobManager.h"

bool ClientSession::OnConnect(SOCKADDR_IN* addr)
{
	memcpy(&mClientAddr, addr, sizeof(SOCKADDR_IN)) ;

	///  ͺŷ ٲٰ
	u_long arg = 1 ;
	ioctlsocket(mSocket, FIONBIO, &arg) ;

	/// nagle ˰
	int opt = 1 ;
	setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(int)) ;

	printf("[DEBUG] Client Connected: IP=%s, PORT=%d\n", inet_ntoa(mClientAddr.sin_addr), ntohs(mClientAddr.sin_port)) ;

	mConnected = true ;

	return PostRecv() ;
}

bool ClientSession::PostRecv()
{
	if ( !IsConnected() )
		return false ;

	DWORD recvbytes = 0 ;
	DWORD flags = 0 ;
	WSABUF buf ;
	buf.len = (ULONG)mRecvBuffer.GetFreeSpaceSize() ;
	buf.buf = (char*)mRecvBuffer.GetBuffer() ;

	memset(&mOverlappedRecv, 0, sizeof(OverlappedIO)) ;
	mOverlappedRecv.mObject = this ;

	/// 񵿱
	if ( SOCKET_ERROR == WSARecv(mSocket, &buf, 1, &recvbytes, &flags, &mOverlappedRecv, RecvCompletion) )
	{
		if ( WSAGetLastError() != WSA_IO_PENDING )
			return false ;
	}

	IncOverlappedRequest() ;

	return true ;
}

void ClientSession::Disconnect()
{
	if ( !IsConnected() )
		return ;

	printf("[DEBUG] Client Disconnected: IP=%s, PORT=%d\n", inet_ntoa(mClientAddr.sin_addr), ntohs(mClientAddr.sin_port)) ;

	/// ﰢ

	LINGER lingerOption;
	lingerOption.l_onoff = 1;
	lingerOption.l_linger = 0;

	/// no TCP TIME_WAIT
	if (SOCKET_ERROR == setsockopt(mSocket, SOL_SOCKET, SO_LINGER, (char*)&lingerOption, sizeof(LINGER)))
	{
		printf_s("[DEBUG] setsockopt linger option error: %d\n", GetLastError());
		return;
	}

	closesocket(mSocket) ;

	mConnected = false ;
}

void ClientSession::OnRead(size_t len)
{
	mRecvBuffer.Commit(len) ;

	/// Ŷ Ľϰ ó
	while ( true )
	{
		/// Ŷ  ũ ŭ оͺ
		PacketHeader header ;
		if ( false == mRecvBuffer.Peek((char*)&header, sizeof(PacketHeader)) )
			return ;

		/// Ŷ ϼ Ǵ°?
		if ( mRecvBuffer.GetStoredSize() < (header.mSize - sizeof(PacketHeader)) )
			return ;

		/// Ŷ ڵ鸵
		switch ( header.mType )
		{
		case PKT_CS_LOGIN:
			{
				LoginRequest inPacket ;
				mRecvBuffer.Read((char*)&inPacket, header.mSize) ;

				/// α DB ۾ ľ ϱ  DB ۾ ûѴ.
				LoadPlayerDataContext* newDbJob = new LoadPlayerDataContext(mSocket, inPacket.mPlayerId) ;
				GDatabaseJobManager->PushDatabaseJobRequest(newDbJob) ;

			}
			break ;

		case PKT_CS_CHAT:
			{
				ChatBroadcastRequest inPacket ;
				mRecvBuffer.Read((char*)&inPacket, header.mSize) ;

				ChatBroadcastResult outPacket ;
				outPacket.mPlayerId = inPacket.mPlayerId ;
				strcpy_s(outPacket.mName, mPlayerName) ;
				strcpy_s(outPacket.mChat, inPacket.mChat) ;

				/// ä ٷ  ϸ
				if ( !Broadcast(&outPacket) )
					return ;

			}
			break ;

		default:
			{
				///   ̻ Ŷ Ŵ.
				Disconnect() ;
				return ;
			}
			break ;
		}
	}
}

bool ClientSession::SendRequest(PacketHeader* pkt)
{
	if ( !IsConnected() )
		return false ;

	/// Send û ۿ ׾Ƴٰ ѹ .
	if ( false == mSendBuffer.Write((char*)pkt, pkt->mSize) )
	{
		///  뷮
		Disconnect() ;
		return false ;
	}

	return true;

}

bool ClientSession::SendFlush()
{
	if (!IsConnected())
		return false;

	///  Ͱ  ׳
	if (mSendBuffer.GetContiguiousBytes() == 0)
		return true;

	DWORD sendbytes = 0;
	DWORD flags = 0;

	WSABUF buf;
	buf.len = (ULONG)mSendBuffer.GetContiguiousBytes();
	buf.buf = (char*)mSendBuffer.GetBufferStart();

	memset(&mOverlappedSend, 0, sizeof(OverlappedIO));
	mOverlappedSend.mObject = this;

	// 񵿱
	if (SOCKET_ERROR == WSASend(mSocket, &buf, 1, &sendbytes, flags, &mOverlappedSend, SendCompletion))
	{
		if (WSAGetLastError() != WSA_IO_PENDING)
			return false;
	}

	IncOverlappedRequest();

	return true;
}

void ClientSession::OnWriteComplete(size_t len)
{
	///  Ϸ ʹ ۿ
	mSendBuffer.Remove(len) ;

	/// ?   쵵 ֳ? (Ŀ send queue áų, Send Completion  send  ?)
	if ( mSendBuffer.GetContiguiousBytes() > 0 )
	{
		assert(false) ;
	}

}

bool ClientSession::Broadcast(PacketHeader* pkt)
{
	if ( !SendRequest(pkt) )
		return false ;

	if ( !IsConnected() )
		return false ;

	GClientManager->BroadcastPacket(this, pkt) ;

	return true ;
}

void ClientSession::OnTick()
{
	/// Ŭ󺰷 ֱ ؾߵ  ⿡

	/// Ư ֱ DB ġ
	if ( ++mDbUpdateCount == PLAYER_DB_UPDATE_CYCLE )
	{
		mDbUpdateCount = 0 ;
		UpdatePlayerDataContext* updatePlayer = new UpdatePlayerDataContext(mSocket, mPlayerId) ;

		updatePlayer->mPosX = mPosX ;
		updatePlayer->mPosY = mPosY ;
		updatePlayer->mPosZ = mPosZ ;
		strcpy_s(updatePlayer->mComment, "updated_test") ; ///< ϴ ׽Ʈ
		GDatabaseJobManager->PushDatabaseJobRequest(updatePlayer) ;
	}

}

void ClientSession::DatabaseJobDone(DatabaseJobContext* result)
{
	CRASH_ASSERT( mSocket == result->mSockKey ) ;

	const type_info& typeInfo = typeid(*result) ;

	if ( typeInfo == typeid(LoadPlayerDataContext) )
	{
		LoadPlayerDataContext* login = dynamic_cast<LoadPlayerDataContext*>(result) ;

		LoginDone(login->mPlayerId, login->mPosX, login->mPosY, login->mPosZ, login->mPlayerName) ;

	}
	else if ( typeInfo == typeid(UpdatePlayerDataContext) )
	{
		UpdateDone() ;
	}
	else
	{
		CRASH_ASSERT(false) ;
	}

}

void ClientSession::UpdateDone()
{
	///  ֱ     .  ׽Ʈ ؼ..
	printf("DEBUG: Player[%d] Update Done\n", mPlayerId) ;
}

void ClientSession::LoginDone(int pid, double x, double y, double z, const char* name)
{
	LoginResult outPacket ;

	outPacket.mPlayerId = mPlayerId = pid ;
	outPacket.mPosX = mPosX = x ;
	outPacket.mPosY = mPosY = y ;
	outPacket.mPosZ = mPosZ = z ;
	strcpy_s(mPlayerName, name) ;
	strcpy_s(outPacket.mName, name) ;

	SendRequest(&outPacket) ;

	mLogon = true ;
}

///////////////////////////////////////////////////////////

void CALLBACK RecvCompletion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
	ClientSession* fromClient = static_cast<OverlappedIO*>(lpOverlapped)->mObject ;

	fromClient->DecOverlappedRequest() ;

	if ( !fromClient->IsConnected() )
		return ;

	///  ߻ ش
	if ( dwError || cbTransferred == 0 )
	{
		fromClient->Disconnect() ;
		return ;
	}

	///   ó
	fromClient->OnRead(cbTransferred) ;

	/// ٽ ޱ
	if ( false == fromClient->PostRecv() )
	{
		fromClient->Disconnect() ;
		return ;
	}
}

void CALLBACK SendCompletion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
	ClientSession* fromClient = static_cast<OverlappedIO*>(lpOverlapped)->mObject ;

	fromClient->DecOverlappedRequest() ;

	if ( !fromClient->IsConnected() )
		return ;

	///  ߻ ش
	if ( dwError || cbTransferred == 0 )
	{
		fromClient->Disconnect() ;
		return ;
	}

	fromClient->OnWriteComplete(cbTransferred) ;

}

<commit_msg>fix header.mSize (PacketHeader + Payload)<commit_after>#include "stdafx.h"
#include "ClientSession.h"
#include "..\..\PacketType.h"
#include "ClientManager.h"
#include "DatabaseJobContext.h"
#include "DatabaseJobManager.h"

bool ClientSession::OnConnect(SOCKADDR_IN* addr)
{
	memcpy(&mClientAddr, addr, sizeof(SOCKADDR_IN)) ;

	/// 소켓을 넌블러킹으로 바꾸고
	u_long arg = 1 ;
	ioctlsocket(mSocket, FIONBIO, &arg) ;

	/// nagle 알고리즘 끄기
	int opt = 1 ;
	setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(int)) ;

	printf("[DEBUG] Client Connected: IP=%s, PORT=%d\n", inet_ntoa(mClientAddr.sin_addr), ntohs(mClientAddr.sin_port)) ;

	mConnected = true ;

	return PostRecv() ;
}

bool ClientSession::PostRecv()
{
	if ( !IsConnected() )
		return false ;

	DWORD recvbytes = 0 ;
	DWORD flags = 0 ;
	WSABUF buf ;
	buf.len = (ULONG)mRecvBuffer.GetFreeSpaceSize() ;
	buf.buf = (char*)mRecvBuffer.GetBuffer() ;

	memset(&mOverlappedRecv, 0, sizeof(OverlappedIO)) ;
	mOverlappedRecv.mObject = this ;

	/// 비동기 입출력 시작
	if ( SOCKET_ERROR == WSARecv(mSocket, &buf, 1, &recvbytes, &flags, &mOverlappedRecv, RecvCompletion) )
	{
		if ( WSAGetLastError() != WSA_IO_PENDING )
			return false ;
	}

	IncOverlappedRequest() ;

	return true ;
}

void ClientSession::Disconnect()
{
	if ( !IsConnected() )
		return ;

	printf("[DEBUG] Client Disconnected: IP=%s, PORT=%d\n", inet_ntoa(mClientAddr.sin_addr), ntohs(mClientAddr.sin_port)) ;

	/// 즉각 해제

	LINGER lingerOption;
	lingerOption.l_onoff = 1;
	lingerOption.l_linger = 0;

	/// no TCP TIME_WAIT
	if (SOCKET_ERROR == setsockopt(mSocket, SOL_SOCKET, SO_LINGER, (char*)&lingerOption, sizeof(LINGER)))
	{
		printf_s("[DEBUG] setsockopt linger option error: %d\n", GetLastError());
		return;
	}

	closesocket(mSocket) ;

	mConnected = false ;
}

void ClientSession::OnRead(size_t len)
{
	mRecvBuffer.Commit(len) ;

	/// 패킷 파싱하고 처리
	while ( true )
	{
		/// 패킷 헤더 크기 만큼 읽어와보기
		PacketHeader header ;
		if ( false == mRecvBuffer.Peek((char*)&header, sizeof(PacketHeader)) )
			return ;

		/// 패킷 완성이 되는가?
		if ( mRecvBuffer.GetStoredSize() < header.mSize )
			return ;

		/// 패킷 핸들링
		switch ( header.mType )
		{
		case PKT_CS_LOGIN:
			{
				LoginRequest inPacket ;
				mRecvBuffer.Read((char*)&inPacket, header.mSize) ;

				/// 로그인은 DB 작업을 거쳐야 하기 때문에 DB 작업 요청한다.
				LoadPlayerDataContext* newDbJob = new LoadPlayerDataContext(mSocket, inPacket.mPlayerId) ;
				GDatabaseJobManager->PushDatabaseJobRequest(newDbJob) ;

			}
			break ;

		case PKT_CS_CHAT:
			{
				ChatBroadcastRequest inPacket ;
				mRecvBuffer.Read((char*)&inPacket, header.mSize) ;

				ChatBroadcastResult outPacket ;
				outPacket.mPlayerId = inPacket.mPlayerId ;
				strcpy_s(outPacket.mName, mPlayerName) ;
				strcpy_s(outPacket.mChat, inPacket.mChat) ;

				/// 채팅은 바로 방송 하면 끝
				if ( !Broadcast(&outPacket) )
					return ;

			}
			break ;

		default:
			{
				/// 여기 들어오면 이상한 패킷 보낸거다.
				Disconnect() ;
				return ;
			}
			break ;
		}
	}
}

bool ClientSession::SendRequest(PacketHeader* pkt)
{
	if ( !IsConnected() )
		return false ;

	/// Send 요청은 버퍼에 쌓아놨다가 한번에 보낸다.
	if ( false == mSendBuffer.Write((char*)pkt, pkt->mSize) )
	{
		/// 버퍼 용량 부족인 경우는 끊어버림
		Disconnect() ;
		return false ;
	}

	return true;

}

bool ClientSession::SendFlush()
{
	if (!IsConnected())
		return false;

	/// 보낼 데이터가 없으면 그냥 리턴
	if (mSendBuffer.GetContiguiousBytes() == 0)
		return true;

	DWORD sendbytes = 0;
	DWORD flags = 0;

	WSABUF buf;
	buf.len = (ULONG)mSendBuffer.GetContiguiousBytes();
	buf.buf = (char*)mSendBuffer.GetBufferStart();

	memset(&mOverlappedSend, 0, sizeof(OverlappedIO));
	mOverlappedSend.mObject = this;

	// 비동기 입출력 시작
	if (SOCKET_ERROR == WSASend(mSocket, &buf, 1, &sendbytes, flags, &mOverlappedSend, SendCompletion))
	{
		if (WSAGetLastError() != WSA_IO_PENDING)
			return false;
	}

	IncOverlappedRequest();

	return true;
}

void ClientSession::OnWriteComplete(size_t len)
{
	/// 보내기 완료한 데이터는 버퍼에서 제거
	mSendBuffer.Remove(len) ;

	/// 얼래? 덜 보낸 경우도 있나? (커널의 send queue가 꽉찼거나, Send Completion이전에 또 send 한 경우?)
	if ( mSendBuffer.GetContiguiousBytes() > 0 )
	{
		assert(false) ;
	}

}

bool ClientSession::Broadcast(PacketHeader* pkt)
{
	if ( !SendRequest(pkt) )
		return false ;

	if ( !IsConnected() )
		return false ;

	GClientManager->BroadcastPacket(this, pkt) ;

	return true ;
}

void ClientSession::OnTick()
{
	/// 클라별로 주기적으로 해야될 일은 여기에

	/// 특정 주기로 DB에 위치 저장
	if ( ++mDbUpdateCount == PLAYER_DB_UPDATE_CYCLE )
	{
		mDbUpdateCount = 0 ;
		UpdatePlayerDataContext* updatePlayer = new UpdatePlayerDataContext(mSocket, mPlayerId) ;

		updatePlayer->mPosX = mPosX ;
		updatePlayer->mPosY = mPosY ;
		updatePlayer->mPosZ = mPosZ ;
		strcpy_s(updatePlayer->mComment, "updated_test") ; ///< 일단은 테스트를 위해
		GDatabaseJobManager->PushDatabaseJobRequest(updatePlayer) ;
	}

}

void ClientSession::DatabaseJobDone(DatabaseJobContext* result)
{
	CRASH_ASSERT( mSocket == result->mSockKey ) ;

	const type_info& typeInfo = typeid(*result) ;

	if ( typeInfo == typeid(LoadPlayerDataContext) )
	{
		LoadPlayerDataContext* login = dynamic_cast<LoadPlayerDataContext*>(result) ;

		LoginDone(login->mPlayerId, login->mPosX, login->mPosY, login->mPosZ, login->mPlayerName) ;

	}
	else if ( typeInfo == typeid(UpdatePlayerDataContext) )
	{
		UpdateDone() ;
	}
	else
	{
		CRASH_ASSERT(false) ;
	}

}

void ClientSession::UpdateDone()
{
	/// 콘텐츠를 넣기 전까지는 딱히 해줄 것이 없다. 단지 테스트를 위해서..
	printf("DEBUG: Player[%d] Update Done\n", mPlayerId) ;
}

void ClientSession::LoginDone(int pid, double x, double y, double z, const char* name)
{
	LoginResult outPacket ;

	outPacket.mPlayerId = mPlayerId = pid ;
	outPacket.mPosX = mPosX = x ;
	outPacket.mPosY = mPosY = y ;
	outPacket.mPosZ = mPosZ = z ;
	strcpy_s(mPlayerName, name) ;
	strcpy_s(outPacket.mName, name) ;

	SendRequest(&outPacket) ;

	mLogon = true ;
}

///////////////////////////////////////////////////////////

void CALLBACK RecvCompletion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
	ClientSession* fromClient = static_cast<OverlappedIO*>(lpOverlapped)->mObject ;

	fromClient->DecOverlappedRequest() ;

	if ( !fromClient->IsConnected() )
		return ;

	/// 에러 발생시 해당 세션 종료
	if ( dwError || cbTransferred == 0 )
	{
		fromClient->Disconnect() ;
		return ;
	}

	/// 받은 데이터 처리
	fromClient->OnRead(cbTransferred) ;

	/// 다시 받기
	if ( false == fromClient->PostRecv() )
	{
		fromClient->Disconnect() ;
		return ;
	}
}

void CALLBACK SendCompletion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
	ClientSession* fromClient = static_cast<OverlappedIO*>(lpOverlapped)->mObject ;

	fromClient->DecOverlappedRequest() ;

	if ( !fromClient->IsConnected() )
		return ;

	/// 에러 발생시 해당 세션 종료
	if ( dwError || cbTransferred == 0 )
	{
		fromClient->Disconnect() ;
		return ;
	}

	fromClient->OnWriteComplete(cbTransferred) ;

}

<|endoftext|>