<commit_before>#include "search.h"

#include "logging.h"

#include <boost/algorithm/string.hpp>

#include <iostream>

void SearchParams::setSearchTerms(const std::string & terms) {
    search_field_ = terms;
    //split search terms, and remove empty ones
    boost::split(search_terms_, terms, boost::is_any_of(" "));
    auto end_it = std::remove_if(search_terms_.begin(), search_terms_.end(), [](const std::string & s)-> bool { return s.empty(); });
    search_terms_.erase(end_it, search_terms_.end());

    regex_fail_ = false;
    if(useRegex()) {
        buildRegex();
    } else {
        search_regexes_.clear();
    }
}

void SearchParams::buildRegex() {
    regex_fail_ = false;
    search_regexes_.clear();
    search_regexes_.reserve(search_terms_.size());
    for(const std::string & st : search_terms_) {
        try {
            search_regexes_.emplace_back(st, std::regex::ECMAScript | std::regex::optimize | (case_sensitive_ ? std::regex::flag_type(0) : std::regex::icase));
        } catch (std::exception &) {
            search_regexes_.clear();
            regex_fail_ = true;
        }
    }
}

bool SearchParams::filterByName(const RefRecord * record) {
    // Feels a bit messy using all these lambdas, and also I'm a bit skeptical if it all getting optimized well
    const auto string_search = [this](const std::string & haystack) -> bool {
        if(case_sensitive_) {
            const auto case_sensitive_single_search = [&haystack](const std::string & needle) -> bool {
                return haystack.cend() != std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
            };
            return std::all_of(search_terms_.cbegin(), search_terms_.cend(), case_sensitive_single_search);
        } else {
            const auto case_insensitive_comparator = [](const char ch1, const char ch2) -> bool { return ::toupper(ch1) == ::toupper(ch2); };
            const auto case_insensitive_single_search = [&case_insensitive_comparator, &haystack](const std::string & needle) -> bool {
                return haystack.cend() != std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), case_insensitive_comparator);
            };
            return std::all_of(search_terms_.cbegin(), search_terms_.cend(), case_insensitive_single_search);
        }
    };

    const auto regex_search = [this](const std::string & haystack) -> bool {
        auto regex_single_search = [&haystack](const std::regex & r) -> bool {
            return std::regex_search(haystack.cbegin(), haystack.cend(), r);
        };
        return std::all_of(search_regexes_.cbegin(), search_regexes_.cend(), regex_single_search);
    };

    const std::string & haystack = record->getName();

    if(false == search_terms_.empty()) {
        if(use_regex_) {
            if(false == regex_search(haystack)) {
                return false;
            }
        } else {
            if(false == string_search(haystack)) {
                return false;
            }
        }
    }

    return true;
}

bool SearchParams::filterByTime(const RefRecord * record, const std::chrono::system_clock::time_point now) {
    if(!change_detection_) {
        return true;
    }

    float timediff;
    if(only_large_changes_) {
        timediff = float(std::chrono::duration_cast<std::chrono::seconds>(now - record->getLastBigUpdateTime()).count());
    } else {
        timediff = float(std::chrono::duration_cast<std::chrono::seconds>(now - record->getLastUpdateTime()).count());
    }
    if(timediff > 10.f) {
        return false;
    }

    return true;
}

void SearchParams::freshSearch(std::vector<RefRecord *> & results_out, const std::vector<RefRecord *> & commandrefs, const std::vector<RefRecord *> & datarefs) {
    std::back_insert_iterator<std::vector<RefRecord *>> result_inserter = std::back_inserter(results_out);
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    results_out.clear();

    //actually perform the search
    if(include_drs_) {
        std::copy_if(datarefs.cbegin(), datarefs.cend(), result_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
    }

    if(include_crs_) {
        std::copy_if(commandrefs.cbegin(), commandrefs.cend(), result_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
    }

    sort(results_out);
}

void SearchParams::updateSearch(std::vector<RefRecord *> & results_in_out, const std::vector<RefRecord *> & new_refs, const std::vector<RefRecord *> & changed_cr, const std::vector<RefRecord *> & changed_dr) {
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    if(change_detection_) {
        //filter existing results by change date and not search term
        /*
        auto new_end = std::remove_if(results_in_out.begin(), results_in_out.end(), [this, now](const RefRecord * r) -> bool { return false == filterByTime(r, now); });
        results_in_out.erase(new_end, results_in_out.end());
        */

        //changed dr/cr filter by search term, and possibly filter out small changes.
        {
            auto filter = [this, now](const RefRecord * r) -> bool {
                if(useOnlyLargeChanges()) { // need additional filtering for only bigger changes then
                    return filterByTimeAndName(r, now);
                } else {
                    return filterByName(r);
                }
            };
            working_buffer.clear();
            std::back_insert_iterator<std::vector<RefRecord *>> working_inserter = std::back_inserter(working_buffer);
            if(include_drs_) {
                std::copy_if(changed_dr.cbegin(), changed_dr.cend(), working_inserter, filter);
            }
            if(include_crs_) {
                std::copy_if(changed_cr.cbegin(), changed_cr.cend(), working_inserter, filter);
            }
            sort(working_buffer);
            inplace_union(results_in_out, working_buffer);
        }

        //new refs, filter by search term and by update date
        if(false == new_refs.empty()) {
            working_buffer.clear();
            std::back_insert_iterator<std::vector<RefRecord *>> working_inserter = std::back_inserter(working_buffer);
            std::copy_if(new_refs.cbegin(), new_refs.cend(), working_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
            sort(working_buffer);
            inplace_union(results_in_out, working_buffer);
        }
    } else {
        //keep existing results
        //filter new refs by search term only
        if(false == new_refs.empty()) {
            working_buffer.clear();
            std::back_insert_iterator<std::vector<RefRecord *>> working_inserter = std::back_inserter(working_buffer);
            std::copy_if(new_refs.cbegin(), new_refs.cend(), working_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
            sort(working_buffer);
            inplace_union(results_in_out, working_buffer);
        }
    }
}<commit_msg>Add negative search terms<commit_after>#include "search.h"

#include "logging.h"

#include <boost/algorithm/string.hpp>

#include <iostream>

void SearchParams::setSearchTerms(const std::string & terms) {
    search_field_ = terms;
    //split search terms, and remove empty ones
    boost::split(search_terms_, terms, boost::is_any_of(" "));
    auto end_it = std::remove_if(search_terms_.begin(), search_terms_.end(), [](const std::string & s)-> bool { return s.empty(); });
    search_terms_.erase(end_it, search_terms_.end());

    regex_fail_ = false;
    if(useRegex()) {
        buildRegex();
    } else {
        search_regexes_.clear();
    }
}

void SearchParams::buildRegex() {
    regex_fail_ = false;
    search_regexes_.clear();
    search_regexes_.reserve(search_terms_.size());
    for(const std::string & st : search_terms_) {
        try {
            search_regexes_.emplace_back(st, std::regex::ECMAScript | std::regex::optimize | (case_sensitive_ ? std::regex::flag_type(0) : std::regex::icase));
        } catch (std::exception &) {
            search_regexes_.clear();
            regex_fail_ = true;
        }
    }
}

bool SearchParams::filterByName(const RefRecord * record) {
    // Feels a bit messy using all these lambdas, and also I'm a bit skeptical if it all getting optimized well
    const auto string_search = [this](const std::string & haystack) -> bool {
        if(case_sensitive_) {
            const auto case_sensitive_single_search = [&haystack](const std::string & needle) -> bool {
                if('-' == needle.front()) {
                    return haystack.cend() == std::search(haystack.begin(), haystack.end(), needle.begin() + 1, needle.end());
                } else {
                    return haystack.cend() != std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
                }
            };
            return std::all_of(search_terms_.cbegin(), search_terms_.cend(), case_sensitive_single_search);
        } else {
            const auto case_insensitive_comparator = [](const char ch1, const char ch2) -> bool { return ::toupper(ch1) == ::toupper(ch2); };
            const auto case_insensitive_single_search = [&case_insensitive_comparator, &haystack](const std::string & needle) -> bool {
                if('-' == needle.front()) {
                    return haystack.cend() == std::search(haystack.begin(), haystack.end(), needle.begin() + 1, needle.end(), case_insensitive_comparator);
                } else {
                    return haystack.cend() != std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), case_insensitive_comparator);
                }
            };
            return std::all_of(search_terms_.cbegin(), search_terms_.cend(), case_insensitive_single_search);
        }
    };

    const auto regex_search = [this](const std::string & haystack) -> bool {
        auto regex_single_search = [&haystack](const std::regex & r) -> bool {
            return std::regex_search(haystack.cbegin(), haystack.cend(), r);
        };
        return std::all_of(search_regexes_.cbegin(), search_regexes_.cend(), regex_single_search);
    };

    const std::string & haystack = record->getName();

    if(false == search_terms_.empty()) {
        if(use_regex_) {
            if(false == regex_search(haystack)) {
                return false;
            }
        } else {
            if(false == string_search(haystack)) {
                return false;
            }
        }
    }

    return true;
}

bool SearchParams::filterByTime(const RefRecord * record, const std::chrono::system_clock::time_point now) {
    if(!change_detection_) {
        return true;
    }

    float timediff;
    if(only_large_changes_) {
        timediff = float(std::chrono::duration_cast<std::chrono::seconds>(now - record->getLastBigUpdateTime()).count());
    } else {
        timediff = float(std::chrono::duration_cast<std::chrono::seconds>(now - record->getLastUpdateTime()).count());
    }
    if(timediff > 10.f) {
        return false;
    }

    return true;
}

void SearchParams::freshSearch(std::vector<RefRecord *> & results_out, const std::vector<RefRecord *> & commandrefs, const std::vector<RefRecord *> & datarefs) {
    std::back_insert_iterator<std::vector<RefRecord *>> result_inserter = std::back_inserter(results_out);
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    results_out.clear();

    //actually perform the search
    if(include_drs_) {
        std::copy_if(datarefs.cbegin(), datarefs.cend(), result_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
    }

    if(include_crs_) {
        std::copy_if(commandrefs.cbegin(), commandrefs.cend(), result_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
    }

    sort(results_out);
}

void SearchParams::updateSearch(std::vector<RefRecord *> & results_in_out, const std::vector<RefRecord *> & new_refs, const std::vector<RefRecord *> & changed_cr, const std::vector<RefRecord *> & changed_dr) {
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    if(change_detection_) {
        //filter existing results by change date and not search term
        /*
        auto new_end = std::remove_if(results_in_out.begin(), results_in_out.end(), [this, now](const RefRecord * r) -> bool { return false == filterByTime(r, now); });
        results_in_out.erase(new_end, results_in_out.end());
        */

        //changed dr/cr filter by search term, and possibly filter out small changes.
        {
            auto filter = [this, now](const RefRecord * r) -> bool {
                if(useOnlyLargeChanges()) { // need additional filtering for only bigger changes then
                    return filterByTimeAndName(r, now);
                } else {
                    return filterByName(r);
                }
            };
            working_buffer.clear();
            std::back_insert_iterator<std::vector<RefRecord *>> working_inserter = std::back_inserter(working_buffer);
            if(include_drs_) {
                std::copy_if(changed_dr.cbegin(), changed_dr.cend(), working_inserter, filter);
            }
            if(include_crs_) {
                std::copy_if(changed_cr.cbegin(), changed_cr.cend(), working_inserter, filter);
            }
            sort(working_buffer);
            inplace_union(results_in_out, working_buffer);
        }

        //new refs, filter by search term and by update date
        if(false == new_refs.empty()) {
            working_buffer.clear();
            std::back_insert_iterator<std::vector<RefRecord *>> working_inserter = std::back_inserter(working_buffer);
            std::copy_if(new_refs.cbegin(), new_refs.cend(), working_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
            sort(working_buffer);
            inplace_union(results_in_out, working_buffer);
        }
    } else {
        //keep existing results
        //filter new refs by search term only
        if(false == new_refs.empty()) {
            working_buffer.clear();
            std::back_insert_iterator<std::vector<RefRecord *>> working_inserter = std::back_inserter(working_buffer);
            std::copy_if(new_refs.cbegin(), new_refs.cend(), working_inserter, [this, now](const RefRecord * r) -> bool { return filterByTimeAndName(r, now); });
            sort(working_buffer);
            inplace_union(results_in_out, working_buffer);
        }
    }
}<|endoftext|>


<commit_before>/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stddef.h>
#include "core/cross/message_commands.h"
#include "tests/common/win/testing_common.h"

namespace o3d {

class MessageCommandsTest : public testing::Test {
};

TEST_F(MessageCommandsTest, GetMessageDescription) {
  EXPECT_STREQ(IMCMessage::GetMessageDescription(
      IMCMessage::ALLOCATE_SHARED_MEMORY), "ALLOCATE_SHARED_MEMORY");
}

TEST_F(MessageCommandsTest, IMCMessage) {
  IMCMessage msg(IMCMessage::INVALID_ID);
  EXPECT_EQ(IMCMessage::INVALID_ID, msg.message_id);
  EXPECT_EQ(4u, sizeof msg);
  EXPECT_EQ(0u, offsetof(IMCMessage, message_id));
}

TEST_F(MessageCommandsTest, MessageInvalidIdTest) {
  MessageInvalidId msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::INVALID_ID), 0);
  EXPECT_EQ(IMCMessage::INVALID_ID, MessageInvalidId::kMessageId);
  EXPECT_EQ(IMCMessage::INVALID_ID, msg.message_id);
  EXPECT_EQ(4u, sizeof(msg));
  EXPECT_EQ(0u, offsetof(MessageInvalidId, message_id));
}

TEST_F(MessageCommandsTest, MessageHelloTest) {
  MessageHello msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::HELLO), 1);
  EXPECT_EQ(IMCMessage::HELLO, MessageHello::kMessageId);
  EXPECT_EQ(msg.message_id, IMCMessage::HELLO);
  EXPECT_EQ(4u, sizeof msg);
  EXPECT_EQ(0u, offsetof(MessageHello, message_id));
}

TEST_F(MessageCommandsTest, MessageAllocateSharedMemoryTest) {
  MessageAllocateSharedMemory msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::ALLOCATE_SHARED_MEMORY), 2);
  EXPECT_EQ(IMCMessage::ALLOCATE_SHARED_MEMORY,
            MessageAllocateSharedMemory::kMessageId);
  EXPECT_EQ(IMCMessage::ALLOCATE_SHARED_MEMORY, msg.message_id);
  EXPECT_EQ(8u, sizeof msg);
  EXPECT_EQ(0u, offsetof(MessageAllocateSharedMemory, message_id));
  EXPECT_EQ(4u, offsetof(MessageAllocateSharedMemory, mem_size));
}

TEST_F(MessageCommandsTest, MessageUpdateTexture2D) {
  MessageUpdateTexture2D msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::UPDATE_TEXTURE2D), 3);
  EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D, MessageUpdateTexture2D::kMessageId);
  EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D, msg.message_id);
  EXPECT_EQ(24u, sizeof msg);
  EXPECT_EQ(0u, offsetof(MessageUpdateTexture2D, message_id));
  EXPECT_EQ(4u, offsetof(MessageUpdateTexture2D, texture_id));
  EXPECT_EQ(8u, offsetof(MessageUpdateTexture2D, level));
  EXPECT_EQ(12u, offsetof(MessageUpdateTexture2D, shared_memory_id));
  EXPECT_EQ(16u, offsetof(MessageUpdateTexture2D, offset));
  EXPECT_EQ(20u, offsetof(MessageUpdateTexture2D, number_of_bytes));
}

TEST_F(MessageCommandsTest, MessageRegisterSharedMemory) {
  MessageRegisterSharedMemory msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::REGISTER_SHARED_MEMORY), 4);
  EXPECT_EQ(IMCMessage::REGISTER_SHARED_MEMORY,
            MessageRegisterSharedMemory::kMessageId);
  EXPECT_EQ(IMCMessage::REGISTER_SHARED_MEMORY, msg.message_id);
  EXPECT_EQ(8u, sizeof msg);
  EXPECT_EQ(0u, offsetof(MessageRegisterSharedMemory, message_id));
  EXPECT_EQ(4u, offsetof(MessageRegisterSharedMemory, mem_size));
}

TEST_F(MessageCommandsTest, MessageUnregisterSharedMemory) {
  MessageUnregisterSharedMemory msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::UNREGISTER_SHARED_MEMORY), 5);
  EXPECT_EQ(IMCMessage::UNREGISTER_SHARED_MEMORY,
            MessageUnregisterSharedMemory::kMessageId);
  EXPECT_EQ(IMCMessage::UNREGISTER_SHARED_MEMORY, msg.message_id);
  EXPECT_EQ(8u, sizeof msg);
  EXPECT_EQ(0u, offsetof(MessageUnregisterSharedMemory, message_id));
  EXPECT_EQ(4u, offsetof(MessageUnregisterSharedMemory, buffer_id));
}

TEST_F(MessageCommandsTest, MessageUpdateTexture2DRect) {
  MessageUpdateTexture2DRect msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::UPDATE_TEXTURE2D_RECT), 6);
  EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D_RECT,
            MessageUpdateTexture2DRect::kMessageId);
  EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D_RECT, msg.message_id);
  EXPECT_EQ(40u, sizeof msg);
  EXPECT_EQ(0u, offsetof(MessageUpdateTexture2DRect, message_id));
  EXPECT_EQ(4u, offsetof(MessageUpdateTexture2DRect, texture_id));
  EXPECT_EQ(8u, offsetof(MessageUpdateTexture2DRect, level));
  EXPECT_EQ(12u, offsetof(MessageUpdateTexture2DRect, x));
  EXPECT_EQ(16u, offsetof(MessageUpdateTexture2DRect, y));
  EXPECT_EQ(20u, offsetof(MessageUpdateTexture2DRect, width));
  EXPECT_EQ(24u, offsetof(MessageUpdateTexture2DRect, height));
  EXPECT_EQ(28u, offsetof(MessageUpdateTexture2DRect, shared_memory_id));
  EXPECT_EQ(32u, offsetof(MessageUpdateTexture2DRect, offset));
  EXPECT_EQ(36u, offsetof(MessageUpdateTexture2DRect, number_of_bytes));
}

}  // namespace o3d

<commit_msg>gcc fixes<commit_after>/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stddef.h>
#include "core/cross/message_commands.h"
#include "tests/common/win/testing_common.h"

namespace o3d {

#define O3D_OFFSETOF(type, field) \
  static_cast<size_t>( \
    reinterpret_cast<char*>(&(reinterpret_cast<type*>(1))->field) - \
  reinterpret_cast<char *>(1))

class MessageCommandsTest : public testing::Test {
};

TEST_F(MessageCommandsTest, GetMessageDescription) {
  EXPECT_STREQ(IMCMessage::GetMessageDescription(
      IMCMessage::ALLOCATE_SHARED_MEMORY), "ALLOCATE_SHARED_MEMORY");
}

TEST_F(MessageCommandsTest, IMCMessage) {
  IMCMessage msg(IMCMessage::INVALID_ID);
  EXPECT_EQ(IMCMessage::INVALID_ID, msg.message_id);
  EXPECT_EQ(4u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(IMCMessage, message_id));
}

TEST_F(MessageCommandsTest, MessageInvalidIdTest) {
  MessageInvalidId msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::INVALID_ID), 0);
  //EXPECT_EQ(IMCMessage::INVALID_ID, MessageInvalidId::kMessageId);
  EXPECT_EQ(IMCMessage::INVALID_ID, msg.message_id);
  EXPECT_EQ(4u, sizeof(msg));
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageInvalidId, message_id));
}

TEST_F(MessageCommandsTest, MessageHelloTest) {
  MessageHello msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::HELLO), 1);
  //EXPECT_EQ(IMCMessage::HELLO, MessageHello::kMessageId);
  EXPECT_EQ(msg.message_id, IMCMessage::HELLO);
  EXPECT_EQ(4u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageHello, message_id));
}

TEST_F(MessageCommandsTest, MessageAllocateSharedMemoryTest) {
  MessageAllocateSharedMemory msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::ALLOCATE_SHARED_MEMORY), 2);
  //EXPECT_EQ(IMCMessage::ALLOCATE_SHARED_MEMORY,
  //          MessageAllocateSharedMemory::kMessageId);
  EXPECT_EQ(IMCMessage::ALLOCATE_SHARED_MEMORY, msg.message_id);
  EXPECT_EQ(8u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageAllocateSharedMemory, message_id));
  EXPECT_EQ(4u, O3D_OFFSETOF(MessageAllocateSharedMemory, mem_size));
}

TEST_F(MessageCommandsTest, MessageUpdateTexture2D) {
  MessageUpdateTexture2D msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::UPDATE_TEXTURE2D), 3);
  //EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D, MessageUpdateTexture2D::kMessageId);
  EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D, msg.message_id);
  EXPECT_EQ(24u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageUpdateTexture2D, message_id));
  EXPECT_EQ(4u, O3D_OFFSETOF(MessageUpdateTexture2D, texture_id));
  EXPECT_EQ(8u, O3D_OFFSETOF(MessageUpdateTexture2D, level));
  EXPECT_EQ(12u, O3D_OFFSETOF(MessageUpdateTexture2D, shared_memory_id));
  EXPECT_EQ(16u, O3D_OFFSETOF(MessageUpdateTexture2D, offset));
  EXPECT_EQ(20u, O3D_OFFSETOF(MessageUpdateTexture2D, number_of_bytes));
}

TEST_F(MessageCommandsTest, MessageRegisterSharedMemory) {
  MessageRegisterSharedMemory msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::REGISTER_SHARED_MEMORY), 4);
  //EXPECT_EQ(IMCMessage::REGISTER_SHARED_MEMORY,
  //          MessageRegisterSharedMemory::kMessageId);
  EXPECT_EQ(IMCMessage::REGISTER_SHARED_MEMORY, msg.message_id);
  EXPECT_EQ(8u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageRegisterSharedMemory, message_id));
  EXPECT_EQ(4u, O3D_OFFSETOF(MessageRegisterSharedMemory, mem_size));
}

TEST_F(MessageCommandsTest, MessageUnregisterSharedMemory) {
  MessageUnregisterSharedMemory msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::UNREGISTER_SHARED_MEMORY), 5);
  //EXPECT_EQ(IMCMessage::UNREGISTER_SHARED_MEMORY,
  //          MessageUnregisterSharedMemory::kMessageId);
  EXPECT_EQ(IMCMessage::UNREGISTER_SHARED_MEMORY, msg.message_id);
  EXPECT_EQ(8u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageUnregisterSharedMemory, message_id));
  EXPECT_EQ(4u, O3D_OFFSETOF(MessageUnregisterSharedMemory, buffer_id));
}

TEST_F(MessageCommandsTest, MessageUpdateTexture2DRect) {
  MessageUpdateTexture2DRect msg;
  EXPECT_EQ(static_cast<int>(IMCMessage::UPDATE_TEXTURE2D_RECT), 6);
  //EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D_RECT,
  //          MessageUpdateTexture2DRect::kMessageId);
  EXPECT_EQ(IMCMessage::UPDATE_TEXTURE2D_RECT, msg.message_id);
  EXPECT_EQ(40u, sizeof msg);
  EXPECT_EQ(0u, O3D_OFFSETOF(MessageUpdateTexture2DRect, message_id));
  EXPECT_EQ(4u, O3D_OFFSETOF(MessageUpdateTexture2DRect, texture_id));
  EXPECT_EQ(8u, O3D_OFFSETOF(MessageUpdateTexture2DRect, level));
  EXPECT_EQ(12u, O3D_OFFSETOF(MessageUpdateTexture2DRect, x));
  EXPECT_EQ(16u, O3D_OFFSETOF(MessageUpdateTexture2DRect, y));
  EXPECT_EQ(20u, O3D_OFFSETOF(MessageUpdateTexture2DRect, width));
  EXPECT_EQ(24u, O3D_OFFSETOF(MessageUpdateTexture2DRect, height));
  EXPECT_EQ(28u, O3D_OFFSETOF(MessageUpdateTexture2DRect, shared_memory_id));
  EXPECT_EQ(32u, O3D_OFFSETOF(MessageUpdateTexture2DRect, offset));
  EXPECT_EQ(36u, O3D_OFFSETOF(MessageUpdateTexture2DRect, number_of_bytes));
}

}  // namespace o3d

<|endoftext|>


<commit_before>#include <cstring>
#include "sector.h"
#include "compress.h"
#include "cso.h"
#include "buffer_pool.h"
#include "zopfli/zopfli.h"
#ifndef NO_DEFLATE7Z
#include "deflate7z.h"
#endif
#include "lz4.h"
#include "lz4hc.h"
#define ZLIB_CONST
#include "zlib.h"

namespace maxcso {

static int InitZlib(z_stream *&z, int strategy) {
	z = reinterpret_cast<z_stream *>(calloc(1, sizeof(z_stream)));
	return deflateInit2(z, 9, Z_DEFLATED, -15, 9, strategy);
}

static void EndZlib(z_stream *&z) {
	deflateEnd(z);
	free(z);
	z = nullptr;
}

Sector::Sector(uint32_t flags)
	: flags_(flags), origMaxCost_(0), lz4MaxCost_(0), busy_(false), enqueued_(false),
	compress_(true), readySize_(0), buffer_(nullptr), best_(nullptr) {
	// Set up the zlib streams, which we will reuse each time we hit this sector.
	if (!(flags_ & TASKFLAG_NO_ZLIB_DEFAULT)) {
		InitZlib(zDefault_, Z_DEFAULT_STRATEGY);
	}
	if (!(flags_ & TASKFLAG_NO_ZLIB_BRUTE)) {
		InitZlib(zFiltered_, Z_FILTERED);
		InitZlib(zHuffman_, Z_HUFFMAN_ONLY);
		InitZlib(zRLE_, Z_RLE);
	}

#ifndef NO_DEFLATE7Z
	if (!(flags_ & TASKFLAG_NO_7ZIP)) {
		Deflate7z::Options opts;
		Deflate7z::SetDefaults(&opts);
		opts.level = 9;
		Deflate7z::Alloc(&deflate7z_, &opts);
	}
#endif
}

Sector::~Sector() {
	// Maybe should throw an error if it wasn't released?
	Release();

	if (!(flags_ & TASKFLAG_NO_ZLIB_DEFAULT)) {
		EndZlib(zDefault_);
	}
	if (!(flags_ & TASKFLAG_NO_ZLIB_BRUTE)) {
		EndZlib(zFiltered_);
		EndZlib(zHuffman_);
		EndZlib(zRLE_);
	}

#ifndef NO_DEFLATE7Z
	if (!(flags_ & TASKFLAG_NO_7ZIP)) {
		Deflate7z::Release(&deflate7z_);
	}
#endif
}

void Sector::Process(int64_t pos, uint8_t *buffer, SectorCallback ready) {
	if (!busy_) {
		busy_ = true;

		pos_ = pos & ~static_cast<uint64_t>(blockSize_ - 1);
		bestSize_ = blockSize_;
		bestFmt_ = SECTOR_FMT_ORIG;

		if (blockSize_ == SECTOR_SIZE) {
			buffer_ = buffer;
		} else {
			buffer_ = pool.Alloc();
			memcpy(buffer_ + pos - pos_, buffer, SECTOR_SIZE);
			pool.Release(buffer);
		}
	} else if (static_cast<uint64_t>(pos - pos_) < pool.bufferSize) {
		memcpy(buffer_ + pos - pos_, buffer, SECTOR_SIZE);
		pool.Release(buffer);
	} else {
		ready_(false, "Invalid buffer pos for this sector block");
		return;
	}

	readySize_ += SECTOR_SIZE;
	if (readySize_ < blockSize_) {
		// We can't process yet, wait for the other buffers.
		return;
	}

	if (enqueued_) {
		ready_(false, "Sector already waiting for queued operation");
		return;
	}

	if (compress_) {
		enqueued_ = true;
		ready_ = ready;
		uv_.queue_work(loop_, &work_, [this](uv_work_t *req) {
			Compress();
			FinalizeBest(align_);
		}, [this](uv_work_t *req, int status) {
			if (status < 0) {
				ready_(false, "Failed to compress sector");
			} else {
				ready_(true, nullptr);
			}
		});
	} else {
		ready(true, nullptr);
	}
}

void Sector::FinalizeBest(uint32_t align) {
	// If bestSize_ wouldn't be smaller after alignment, we should not compress.
	// It won't save space, and it'll waste CPU on the decompression side.
	if (AlignedBestSize(align) >= blockSize_ && best_ != nullptr) {
		pool.Release(best_);
		best_ = nullptr;
		bestSize_ = blockSize_;
		bestFmt_ = SECTOR_FMT_ORIG;
	}
}

void Sector::Compress() {
	if (!(flags_ & TASKFLAG_NO_ZLIB_DEFAULT)) {
		ZlibTrial(zDefault_);
	}
	// Each of these sometimes wins on certain blocks.
	if (!(flags_ & TASKFLAG_NO_ZLIB_BRUTE)) {
		ZlibTrial(zFiltered_);
		ZlibTrial(zHuffman_);
		ZlibTrial(zRLE_);
	}
	if (!(flags_ & TASKFLAG_NO_ZOPFLI)) {
		ZopfliTrial();
	}
	if (!(flags_ & TASKFLAG_NO_7ZIP)) {
		SevenZipTrial();
	}
	if (!(flags_ & (TASKFLAG_NO_LZ4_HC | TASKFLAG_NO_LZ4_HC_BRUTE))) {
		LZ4HCTrial(!(flags_ & TASKFLAG_NO_LZ4_HC_BRUTE));
	}
	if (!(flags_ & TASKFLAG_NO_LZ4_DEFAULT)) {
		LZ4Trial();
	}
}

// TODO: Split these out to separate files?
void Sector::ZlibTrial(z_stream *z) {
	// TODO: Validate the benefit of these with raw on msvc and gcc.
	// Try TOO_FAR?  May not matter much for 2048 bytes.
	// http://jsnell.iki.fi/blog/
	// https://github.com/jtkukunas/zlib
	// https://github.com/cloudflare/zlib

	if (deflateReset(z)) {
		return;
	}

	z->next_in = buffer_;
	z->avail_in = blockSize_;

	uint8_t *result = pool.Alloc();

	z->next_out = result;
	z->avail_out = pool.bufferSize;

	int res;
	while ((res = deflate(z, Z_FINISH)) == Z_OK) {
		continue;
	}
	if (res == Z_STREAM_END) {
		// Success.  Let's check the size.
		SubmitTrial(result, z->total_out, SECTOR_FMT_DEFLATE);
	} else {
		// Failed, just ignore this result.
		// TODO: Log or something?
		pool.Release(result);
	}
}

void Sector::ZopfliTrial() {
	// TODO: Trial blocksplittinglast and blocksplittingmax?
	// Increase numiterations depending on how long it takes?
	// TODO: Should this be static otherwise?
	ZopfliOptions opt;
	ZopfliInitOptions(&opt);
	opt.blocksplittinglast = 1;
	opt.numiterations = 5;

	// Grr, zopfli doesn't allow us to use a fixed-size buffer.
	// Also doesn't return failure?
	unsigned char *out = nullptr;
	size_t outsize = 0;
	ZopfliCompress(&opt, ZOPFLI_FORMAT_DEFLATE, buffer_, blockSize_, &out, &outsize);
	if (out != nullptr) {
		if (outsize > 0 && outsize < static_cast<size_t>(bestSize_)) {
			// So that we have proper release semantics, we copy to our buffer.
			uint8_t *result = pool.Alloc();
			memcpy(result, out, outsize);
			SubmitTrial(result, static_cast<uint32_t>(outsize), SECTOR_FMT_DEFLATE);
		}
		free(out);
	}
}

void Sector::SevenZipTrial() {
#ifndef NO_DEFLATE7Z
	uint8_t *result = pool.Alloc();
	uint32_t resultSize = 0;
	if (Deflate7z::Deflate(deflate7z_, result, pool.bufferSize, buffer_, blockSize_, &resultSize)) {
		SubmitTrial(result, resultSize, SECTOR_FMT_DEFLATE);
	} else {
		pool.Release(result);
	}
#endif
}

void Sector::LZ4HCTrial(bool allowBrute) {
	// Sometimes lower levels can actually win.  But, usually not, so only try a few.
	int level = allowBrute ? 4 : 16;
	for (; level <= 16; level += 3) {
		uint8_t *result = pool.Alloc();
		uint32_t resultSize = LZ4_compress_HC(reinterpret_cast<const char *>(buffer_), reinterpret_cast<char *>(result), blockSize_, pool.bufferSize, level);
		if (resultSize != 0) {
			SubmitTrial(result, resultSize, SECTOR_FMT_LZ4);
		} else {
			pool.Release(result);
		}
	}
}

void Sector::LZ4Trial() {
	uint8_t *result = pool.Alloc();
	uint32_t resultSize = LZ4_compress_default(reinterpret_cast<const char *>(buffer_), reinterpret_cast<char *>(result), blockSize_, pool.bufferSize);
	if (resultSize != 0) {
		SubmitTrial(result, resultSize, SECTOR_FMT_LZ4);
	} else {
		pool.Release(result);
	}
}

// Frees result if it's not better (takes ownership.)
bool Sector::SubmitTrial(uint8_t *result, uint32_t size, SectorFormat fmt) {
	bool better = size + origMaxCost_ < bestSize_;

	// Based on the old and new format, we may want to apply some fuzzing for lz4.
	if (fmt == SECTOR_FMT_LZ4 && bestFmt_ == SECTOR_FMT_DEFLATE) {
		// Allow lz4 to make it larger by a max cost.
		// Also, use lz4 if it's the same size, since it decompresses faster.
		better = size <= bestSize_ + lz4MaxCost_;
	} else if (fmt == SECTOR_FMT_DEFLATE && bestFmt_ == SECTOR_FMT_LZ4) {
		// Reverse of the above.
		better = size + lz4MaxCost_ < bestSize_;
	}

	if (better) {
		bestSize_ = size;
		if (best_) {
			pool.Release(best_);
		}
		best_ = result;
		bestFmt_ = fmt;
		return true;
	} else {
		pool.Release(result);
		return false;
	}
}

void Sector::Release() {
	if (best_ != nullptr) {
		pool.Release(best_);
		best_ = nullptr;
	}
	if (buffer_ != nullptr) {
		pool.Release(buffer_);
		buffer_ = nullptr;
	}

	busy_ = false;
	enqueued_ = false;
	compress_ = true;
	readySize_ = 0;
}

};
<commit_msg>Tune 7zdeflate params for better compression.<commit_after>#include <cstring>
#include "sector.h"
#include "compress.h"
#include "cso.h"
#include "buffer_pool.h"
#include "zopfli/zopfli.h"
#ifndef NO_DEFLATE7Z
#include "deflate7z.h"
#endif
#include "lz4.h"
#include "lz4hc.h"
#define ZLIB_CONST
#include "zlib.h"

namespace maxcso {

static int InitZlib(z_stream *&z, int strategy) {
	z = reinterpret_cast<z_stream *>(calloc(1, sizeof(z_stream)));
	return deflateInit2(z, 9, Z_DEFLATED, -15, 9, strategy);
}

static void EndZlib(z_stream *&z) {
	deflateEnd(z);
	free(z);
	z = nullptr;
}

Sector::Sector(uint32_t flags)
	: flags_(flags), origMaxCost_(0), lz4MaxCost_(0), busy_(false), enqueued_(false),
	compress_(true), readySize_(0), buffer_(nullptr), best_(nullptr) {
	// Set up the zlib streams, which we will reuse each time we hit this sector.
	if (!(flags_ & TASKFLAG_NO_ZLIB_DEFAULT)) {
		InitZlib(zDefault_, Z_DEFAULT_STRATEGY);
	}
	if (!(flags_ & TASKFLAG_NO_ZLIB_BRUTE)) {
		InitZlib(zFiltered_, Z_FILTERED);
		InitZlib(zHuffman_, Z_HUFFMAN_ONLY);
		InitZlib(zRLE_, Z_RLE);
	}

#ifndef NO_DEFLATE7Z
	if (!(flags_ & TASKFLAG_NO_7ZIP)) {
		Deflate7z::Options opts;
		Deflate7z::SetDefaults(&opts);
		opts.level = 9;
		opts.passes = 12;
		opts.fastbytes = 64;
		opts.matchcycles = 32;
		opts.algo = 1;
		Deflate7z::Alloc(&deflate7z_, &opts);
	}
#endif
}

Sector::~Sector() {
	// Maybe should throw an error if it wasn't released?
	Release();

	if (!(flags_ & TASKFLAG_NO_ZLIB_DEFAULT)) {
		EndZlib(zDefault_);
	}
	if (!(flags_ & TASKFLAG_NO_ZLIB_BRUTE)) {
		EndZlib(zFiltered_);
		EndZlib(zHuffman_);
		EndZlib(zRLE_);
	}

#ifndef NO_DEFLATE7Z
	if (!(flags_ & TASKFLAG_NO_7ZIP)) {
		Deflate7z::Release(&deflate7z_);
	}
#endif
}

void Sector::Process(int64_t pos, uint8_t *buffer, SectorCallback ready) {
	if (!busy_) {
		busy_ = true;

		pos_ = pos & ~static_cast<uint64_t>(blockSize_ - 1);
		bestSize_ = blockSize_;
		bestFmt_ = SECTOR_FMT_ORIG;

		if (blockSize_ == SECTOR_SIZE) {
			buffer_ = buffer;
		} else {
			buffer_ = pool.Alloc();
			memcpy(buffer_ + pos - pos_, buffer, SECTOR_SIZE);
			pool.Release(buffer);
		}
	} else if (static_cast<uint64_t>(pos - pos_) < pool.bufferSize) {
		memcpy(buffer_ + pos - pos_, buffer, SECTOR_SIZE);
		pool.Release(buffer);
	} else {
		ready_(false, "Invalid buffer pos for this sector block");
		return;
	}

	readySize_ += SECTOR_SIZE;
	if (readySize_ < blockSize_) {
		// We can't process yet, wait for the other buffers.
		return;
	}

	if (enqueued_) {
		ready_(false, "Sector already waiting for queued operation");
		return;
	}

	if (compress_) {
		enqueued_ = true;
		ready_ = ready;
		uv_.queue_work(loop_, &work_, [this](uv_work_t *req) {
			Compress();
			FinalizeBest(align_);
		}, [this](uv_work_t *req, int status) {
			if (status < 0) {
				ready_(false, "Failed to compress sector");
			} else {
				ready_(true, nullptr);
			}
		});
	} else {
		ready(true, nullptr);
	}
}

void Sector::FinalizeBest(uint32_t align) {
	// If bestSize_ wouldn't be smaller after alignment, we should not compress.
	// It won't save space, and it'll waste CPU on the decompression side.
	if (AlignedBestSize(align) >= blockSize_ && best_ != nullptr) {
		pool.Release(best_);
		best_ = nullptr;
		bestSize_ = blockSize_;
		bestFmt_ = SECTOR_FMT_ORIG;
	}
}

void Sector::Compress() {
	if (!(flags_ & TASKFLAG_NO_ZLIB_DEFAULT)) {
		ZlibTrial(zDefault_);
	}
	// Each of these sometimes wins on certain blocks.
	if (!(flags_ & TASKFLAG_NO_ZLIB_BRUTE)) {
		ZlibTrial(zFiltered_);
		ZlibTrial(zHuffman_);
		ZlibTrial(zRLE_);
	}
	if (!(flags_ & TASKFLAG_NO_ZOPFLI)) {
		ZopfliTrial();
	}
	if (!(flags_ & TASKFLAG_NO_7ZIP)) {
		SevenZipTrial();
	}
	if (!(flags_ & (TASKFLAG_NO_LZ4_HC | TASKFLAG_NO_LZ4_HC_BRUTE))) {
		LZ4HCTrial(!(flags_ & TASKFLAG_NO_LZ4_HC_BRUTE));
	}
	if (!(flags_ & TASKFLAG_NO_LZ4_DEFAULT)) {
		LZ4Trial();
	}
}

// TODO: Split these out to separate files?
void Sector::ZlibTrial(z_stream *z) {
	// TODO: Validate the benefit of these with raw on msvc and gcc.
	// Try TOO_FAR?  May not matter much for 2048 bytes.
	// http://jsnell.iki.fi/blog/
	// https://github.com/jtkukunas/zlib
	// https://github.com/cloudflare/zlib

	if (deflateReset(z)) {
		return;
	}

	z->next_in = buffer_;
	z->avail_in = blockSize_;

	uint8_t *result = pool.Alloc();

	z->next_out = result;
	z->avail_out = pool.bufferSize;

	int res;
	while ((res = deflate(z, Z_FINISH)) == Z_OK) {
		continue;
	}
	if (res == Z_STREAM_END) {
		// Success.  Let's check the size.
		SubmitTrial(result, z->total_out, SECTOR_FMT_DEFLATE);
	} else {
		// Failed, just ignore this result.
		// TODO: Log or something?
		pool.Release(result);
	}
}

void Sector::ZopfliTrial() {
	// TODO: Trial blocksplittinglast and blocksplittingmax?
	// Increase numiterations depending on how long it takes?
	// TODO: Should this be static otherwise?
	ZopfliOptions opt;
	ZopfliInitOptions(&opt);
	opt.blocksplittinglast = 1;
	opt.numiterations = 5;

	// Grr, zopfli doesn't allow us to use a fixed-size buffer.
	// Also doesn't return failure?
	unsigned char *out = nullptr;
	size_t outsize = 0;
	ZopfliCompress(&opt, ZOPFLI_FORMAT_DEFLATE, buffer_, blockSize_, &out, &outsize);
	if (out != nullptr) {
		if (outsize > 0 && outsize < static_cast<size_t>(bestSize_)) {
			// So that we have proper release semantics, we copy to our buffer.
			uint8_t *result = pool.Alloc();
			memcpy(result, out, outsize);
			SubmitTrial(result, static_cast<uint32_t>(outsize), SECTOR_FMT_DEFLATE);
		}
		free(out);
	}
}

void Sector::SevenZipTrial() {
#ifndef NO_DEFLATE7Z
	uint8_t *result = pool.Alloc();
	uint32_t resultSize = 0;
	if (Deflate7z::Deflate(deflate7z_, result, pool.bufferSize, buffer_, blockSize_, &resultSize)) {
		SubmitTrial(result, resultSize, SECTOR_FMT_DEFLATE);
	} else {
		pool.Release(result);
	}
#endif
}

void Sector::LZ4HCTrial(bool allowBrute) {
	// Sometimes lower levels can actually win.  But, usually not, so only try a few.
	int level = allowBrute ? 4 : 16;
	for (; level <= 16; level += 3) {
		uint8_t *result = pool.Alloc();
		uint32_t resultSize = LZ4_compress_HC(reinterpret_cast<const char *>(buffer_), reinterpret_cast<char *>(result), blockSize_, pool.bufferSize, level);
		if (resultSize != 0) {
			SubmitTrial(result, resultSize, SECTOR_FMT_LZ4);
		} else {
			pool.Release(result);
		}
	}
}

void Sector::LZ4Trial() {
	uint8_t *result = pool.Alloc();
	uint32_t resultSize = LZ4_compress_default(reinterpret_cast<const char *>(buffer_), reinterpret_cast<char *>(result), blockSize_, pool.bufferSize);
	if (resultSize != 0) {
		SubmitTrial(result, resultSize, SECTOR_FMT_LZ4);
	} else {
		pool.Release(result);
	}
}

// Frees result if it's not better (takes ownership.)
bool Sector::SubmitTrial(uint8_t *result, uint32_t size, SectorFormat fmt) {
	bool better = size + origMaxCost_ < bestSize_;

	// Based on the old and new format, we may want to apply some fuzzing for lz4.
	if (fmt == SECTOR_FMT_LZ4 && bestFmt_ == SECTOR_FMT_DEFLATE) {
		// Allow lz4 to make it larger by a max cost.
		// Also, use lz4 if it's the same size, since it decompresses faster.
		better = size <= bestSize_ + lz4MaxCost_;
	} else if (fmt == SECTOR_FMT_DEFLATE && bestFmt_ == SECTOR_FMT_LZ4) {
		// Reverse of the above.
		better = size + lz4MaxCost_ < bestSize_;
	}

	if (better) {
		bestSize_ = size;
		if (best_) {
			pool.Release(best_);
		}
		best_ = result;
		bestFmt_ = fmt;
		return true;
	} else {
		pool.Release(result);
		return false;
	}
}

void Sector::Release() {
	if (best_ != nullptr) {
		pool.Release(best_);
		best_ = nullptr;
	}
	if (buffer_ != nullptr) {
		pool.Release(buffer_);
		buffer_ = nullptr;
	}

	busy_ = false;
	enqueued_ = false;
	compress_ = true;
	readySize_ = 0;
}

};
<|endoftext|>


<commit_before>// Copyright 2018 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// If this doesn't come first, there will be compile errors.
#include <winsock2.h>

#include <iostream>

#include <iphlpapi.h>

#include <fwpmtypes.h>
#include <fwpmu.h>
#include <rpcdce.h>

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "fwpuclnt.lib")
#pragma comment(lib, "rpcrt4.lib")

using namespace ::std;

PCWSTR TAP_DEVICE_NAME = L"outline-tap0";
ULONG GET_ADAPTERS_ADDRESSES_BUFFER_SIZE = 16384;

PCWSTR FILTER_PROVIDER_NAME = L"Outline";
PCWSTR SUBLAYER_NAME = L"Smart DNS Block";

UINT64 LOWER_FILTER_WEIGHT = 10;
UINT64 HIGHER_FILTER_WEIGHT = 20;

int main(int argc, char **argv) {
  // Lookup the interface index of outline-tap0.
  PIP_ADAPTER_ADDRESSES adaptersAddresses =
      (IP_ADAPTER_ADDRESSES *)malloc(GET_ADAPTERS_ADDRESSES_BUFFER_SIZE);
  DWORD result = GetAdaptersAddresses(AF_INET, 0, NULL, adaptersAddresses,
                                      &GET_ADAPTERS_ADDRESSES_BUFFER_SIZE);
  if (result != NO_ERROR) {
    wcerr << "could not fetch network device list: " << result << endl;
    return 1;
  }

  UINT32 interfaceIndex;
  PIP_ADAPTER_ADDRESSES adapterAddress = adaptersAddresses;
  while (adapterAddress && wcscmp(TAP_DEVICE_NAME, adapterAddress->FriendlyName) != 0) {
    adapterAddress = adapterAddress->Next;
  }

  if (!adapterAddress) {
    wcerr << "could not find " << TAP_DEVICE_NAME << endl;
    return 1;
  }

  interfaceIndex = adapterAddress->IfIndex;
  wcout << "found " << TAP_DEVICE_NAME << " at index " << interfaceIndex << endl;

  // Connect to the filtering engine. By using a dynamic session, all of our changes are
  // *non-destructive* and will vanish on exit/crash/whatever.
  FWPM_SESSION0 session;
  memset(&session, 0, sizeof(session));
  session.flags = FWPM_SESSION_FLAG_DYNAMIC;

  HANDLE engine = 0;
  result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, &engine);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not connect to to filtering engine: " << result << endl;
    return 1;
  }
  wcout << "connected to filtering engine" << endl;

  // Create our own sublayer.
  //
  // This is recommended by the API documentation to avoid weird interactions with other
  // applications' filters:
  //   https://docs.microsoft.com/en-us/windows/desktop/fwp/best-practices
  //
  // Notes:
  //  - Without a unique ID our filters will be added to FWPM_SUBLAYER_UNIVERSAL *even if they
  //    reference this new sublayer*.
  //  - Since the documentation doesn't say much about sublayer weights, we specify the highest
  //    possible sublayer weight. This seems to work well.
  FWPM_SUBLAYER0 sublayer;
  memset(&sublayer, 0, sizeof(sublayer));
  UuidCreate(&sublayer.subLayerKey);
  sublayer.displayData.name = (PWSTR)SUBLAYER_NAME;
  sublayer.weight = MAXUINT16;

  result = FwpmSubLayerAdd0(engine, &sublayer, NULL);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not create filtering sublayer: " << result << endl;
    return 1;
  }
  wcout << "created filtering sublayer" << endl;

  // Create our filters:
  //  - The first blocks all UDP traffic bound for port 53.
  //  - The second allows all traffic on the TAP device.
  //
  // Crucially, the second has a higher weight.
  //
  // Note:
  //  - Since OutlineService adds a blanket block on all IPv6 traffic, we only need to create IPv4
  //    filters.
  //  - Thanks to the simplicity of the filters and how they will be automatically destroyed on
  //    exit, there's no need to use a transaction here.

  // Blanket UDP port 53 block.
  FWPM_FILTER_CONDITION0 udpBlockConditions[2];
  udpBlockConditions[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
  udpBlockConditions[0].matchType = FWP_MATCH_EQUAL;
  udpBlockConditions[0].conditionValue.type = FWP_UINT8;
  udpBlockConditions[0].conditionValue.uint16 = IPPROTO_UDP;
  udpBlockConditions[1].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
  udpBlockConditions[1].matchType = FWP_MATCH_EQUAL;
  udpBlockConditions[1].conditionValue.type = FWP_UINT16;
  udpBlockConditions[1].conditionValue.uint16 = 53;

  FWPM_FILTER0 udpBlockFilter;
  memset(&udpBlockFilter, 0, sizeof(udpBlockFilter));
  udpBlockFilter.filterCondition = udpBlockConditions;
  udpBlockFilter.numFilterConditions = 2;
  udpBlockFilter.displayData.name = (PWSTR)FILTER_PROVIDER_NAME;
  udpBlockFilter.subLayerKey = sublayer.subLayerKey;
  udpBlockFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
  udpBlockFilter.action.type = FWP_ACTION_BLOCK;
  udpBlockFilter.weight.type = FWP_UINT64;
  udpBlockFilter.weight.uint64 = &LOWER_FILTER_WEIGHT;
  UINT64 filterId;
  result = FwpmFilterAdd0(engine, &udpBlockFilter, NULL, &filterId);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not block port 53: " << result << endl;
    return 1;
  }
  wcout << "port 53 blocked with filter " << filterId << endl;

  // Allow all traffic on the TAP device.
  FWPM_FILTER_CONDITION0 tapDeviceAllowCondition[1];
  tapDeviceAllowCondition[0].fieldKey = FWPM_CONDITION_LOCAL_INTERFACE_INDEX;
  tapDeviceAllowCondition[0].matchType = FWP_MATCH_EQUAL;
  tapDeviceAllowCondition[0].conditionValue.type = FWP_UINT32;
  tapDeviceAllowCondition[0].conditionValue.uint32 = interfaceIndex;

  FWPM_FILTER0 tapDeviceAllowFilter;
  memset(&tapDeviceAllowFilter, 0, sizeof(tapDeviceAllowFilter));
  tapDeviceAllowFilter.filterCondition = tapDeviceAllowCondition;
  tapDeviceAllowFilter.numFilterConditions = 1;
  tapDeviceAllowFilter.displayData.name = (PWSTR)FILTER_PROVIDER_NAME;
  tapDeviceAllowFilter.subLayerKey = sublayer.subLayerKey;
  tapDeviceAllowFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
  tapDeviceAllowFilter.action.type = FWP_ACTION_PERMIT;
  tapDeviceAllowFilter.weight.type = FWP_UINT64;
  tapDeviceAllowFilter.weight.uint64 = &HIGHER_FILTER_WEIGHT;

  result = FwpmFilterAdd0(engine, &tapDeviceAllowFilter, NULL, &filterId);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not allow traffic on " << TAP_DEVICE_NAME << ": " << result << endl;
    return 1;
  }
  wcout << "allowed traffic on " << TAP_DEVICE_NAME << " with filter " << filterId << endl;

  // Wait forever.
  system("pause");
}
<commit_msg>Windows: an unexpected error occurred reports (#994)<commit_after>// Copyright 2018 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// If this doesn't come first, there will be compile errors.
#include <winsock2.h>

#include <iostream>

#include <iphlpapi.h>

#include <fwpmtypes.h>
#include <fwpmu.h>
#include <rpcdce.h>

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "fwpuclnt.lib")
#pragma comment(lib, "rpcrt4.lib")

using namespace ::std;

PCWSTR TAP_DEVICE_NAME = L"outline-tap0";
ULONG GET_ADAPTERS_ADDRESSES_BUFFER_SIZE = 16384;

PCWSTR FILTER_PROVIDER_NAME = L"Outline";
PCWSTR SUBLAYER_NAME = L"Smart DNS Block";

UINT64 MAX_TRIES = 2;
UINT64 LOWER_FILTER_WEIGHT = 10;
UINT64 HIGHER_FILTER_WEIGHT = 20;

int main(int argc, char **argv) {
  // Lookup the interface index of outline-tap0.
  PIP_ADAPTER_ADDRESSES adaptersAddresses = NULL;

  UINT64 count = 0;
  DWORD result = 0;
  do {
    adaptersAddresses = (IP_ADAPTER_ADDRESSES*)malloc(GET_ADAPTERS_ADDRESSES_BUFFER_SIZE);

    // Required value will be told to GET_ADAPTERS_ADDRESSES_BUFFER_SIZE
    // if GetAdaptersAddresses() returns ERROR_BUFFER_OVERFLOW:
    //   https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#return-value
    result = GetAdaptersAddresses(AF_INET, 0, NULL, adaptersAddresses,
                                  &GET_ADAPTERS_ADDRESSES_BUFFER_SIZE);

    if (result != NO_ERROR) {
      wcerr << "could not fetch network device list: " << result << endl;
      free(adaptersAddresses);
      adaptersAddresses = NULL;
    }
    else {
      wcout << "fetch network device list success!";
    }
  } while ((result == ERROR_BUFFER_OVERFLOW) && (count++ < MAX_TRIES));

  if (result != NO_ERROR) {
    wcerr << "could not fetch network device list: " << result << endl;
    return 1;
  }

  UINT32 interfaceIndex;
  PIP_ADAPTER_ADDRESSES adapterAddress = adaptersAddresses;
  while (adapterAddress && wcscmp(TAP_DEVICE_NAME, adapterAddress->FriendlyName) != 0) {
    adapterAddress = adapterAddress->Next;
  }

  if (!adapterAddress) {
    wcerr << "could not find " << TAP_DEVICE_NAME << endl;
    return 1;
  }

  interfaceIndex = adapterAddress->IfIndex;
  wcout << "found " << TAP_DEVICE_NAME << " at index " << interfaceIndex << endl;

  // Connect to the filtering engine. By using a dynamic session, all of our changes are
  // *non-destructive* and will vanish on exit/crash/whatever.
  FWPM_SESSION0 session;
  memset(&session, 0, sizeof(session));
  session.flags = FWPM_SESSION_FLAG_DYNAMIC;

  HANDLE engine = 0;
  result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, &engine);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not connect to to filtering engine: " << result << endl;
    return 1;
  }
  wcout << "connected to filtering engine" << endl;

  // Create our own sublayer.
  //
  // This is recommended by the API documentation to avoid weird interactions with other
  // applications' filters:
  //   https://docs.microsoft.com/en-us/windows/desktop/fwp/best-practices
  //
  // Notes:
  //  - Without a unique ID our filters will be added to FWPM_SUBLAYER_UNIVERSAL *even if they
  //    reference this new sublayer*.
  //  - Since the documentation doesn't say much about sublayer weights, we specify the highest
  //    possible sublayer weight. This seems to work well.
  FWPM_SUBLAYER0 sublayer;
  memset(&sublayer, 0, sizeof(sublayer));
  UuidCreate(&sublayer.subLayerKey);
  sublayer.displayData.name = (PWSTR)SUBLAYER_NAME;
  sublayer.weight = MAXUINT16;

  result = FwpmSubLayerAdd0(engine, &sublayer, NULL);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not create filtering sublayer: " << result << endl;
    return 1;
  }
  wcout << "created filtering sublayer" << endl;

  // Create our filters:
  //  - The first blocks all UDP traffic bound for port 53.
  //  - The second allows all traffic on the TAP device.
  //
  // Crucially, the second has a higher weight.
  //
  // Note:
  //  - Since OutlineService adds a blanket block on all IPv6 traffic, we only need to create IPv4
  //    filters.
  //  - Thanks to the simplicity of the filters and how they will be automatically destroyed on
  //    exit, there's no need to use a transaction here.

  // Blanket UDP port 53 block.
  FWPM_FILTER_CONDITION0 udpBlockConditions[2];
  udpBlockConditions[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
  udpBlockConditions[0].matchType = FWP_MATCH_EQUAL;
  udpBlockConditions[0].conditionValue.type = FWP_UINT8;
  udpBlockConditions[0].conditionValue.uint16 = IPPROTO_UDP;
  udpBlockConditions[1].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
  udpBlockConditions[1].matchType = FWP_MATCH_EQUAL;
  udpBlockConditions[1].conditionValue.type = FWP_UINT16;
  udpBlockConditions[1].conditionValue.uint16 = 53;

  FWPM_FILTER0 udpBlockFilter;
  memset(&udpBlockFilter, 0, sizeof(udpBlockFilter));
  udpBlockFilter.filterCondition = udpBlockConditions;
  udpBlockFilter.numFilterConditions = 2;
  udpBlockFilter.displayData.name = (PWSTR)FILTER_PROVIDER_NAME;
  udpBlockFilter.subLayerKey = sublayer.subLayerKey;
  udpBlockFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
  udpBlockFilter.action.type = FWP_ACTION_BLOCK;
  udpBlockFilter.weight.type = FWP_UINT64;
  udpBlockFilter.weight.uint64 = &LOWER_FILTER_WEIGHT;
  UINT64 filterId;
  result = FwpmFilterAdd0(engine, &udpBlockFilter, NULL, &filterId);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not block port 53: " << result << endl;
    return 1;
  }
  wcout << "port 53 blocked with filter " << filterId << endl;

  // Allow all traffic on the TAP device.
  FWPM_FILTER_CONDITION0 tapDeviceAllowCondition[1];
  tapDeviceAllowCondition[0].fieldKey = FWPM_CONDITION_LOCAL_INTERFACE_INDEX;
  tapDeviceAllowCondition[0].matchType = FWP_MATCH_EQUAL;
  tapDeviceAllowCondition[0].conditionValue.type = FWP_UINT32;
  tapDeviceAllowCondition[0].conditionValue.uint32 = interfaceIndex;

  FWPM_FILTER0 tapDeviceAllowFilter;
  memset(&tapDeviceAllowFilter, 0, sizeof(tapDeviceAllowFilter));
  tapDeviceAllowFilter.filterCondition = tapDeviceAllowCondition;
  tapDeviceAllowFilter.numFilterConditions = 1;
  tapDeviceAllowFilter.displayData.name = (PWSTR)FILTER_PROVIDER_NAME;
  tapDeviceAllowFilter.subLayerKey = sublayer.subLayerKey;
  tapDeviceAllowFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
  tapDeviceAllowFilter.action.type = FWP_ACTION_PERMIT;
  tapDeviceAllowFilter.weight.type = FWP_UINT64;
  tapDeviceAllowFilter.weight.uint64 = &HIGHER_FILTER_WEIGHT;

  result = FwpmFilterAdd0(engine, &tapDeviceAllowFilter, NULL, &filterId);
  if (result != ERROR_SUCCESS) {
    wcerr << "could not allow traffic on " << TAP_DEVICE_NAME << ": " << result << endl;
    return 1;
  }
  wcout << "allowed traffic on " << TAP_DEVICE_NAME << " with filter " << filterId << endl;

  // Wait forever.
  system("pause");
}
<|endoftext|>


<commit_before>//===--- swift-remoteast-test.cpp - RemoteAST testing application ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// This file supports performing target-specific remote reflection tests
// on live swift executables.
//===----------------------------------------------------------------------===//

#include "swift/RemoteAST/RemoteAST.h"
#include "swift/Remote/InProcessMemoryReader.h"
#include "swift/Remote/MetadataReader.h"
#include "swift/Runtime/Metadata.h"
#include "swift/Frontend/Frontend.h"
#include "swift/FrontendTool/FrontendTool.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/LLVMInitialize.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>

#if defined(__ELF__)
#define SWIFT_REMOTEAST_TEST_ABI __attribute__((__visibility__("default")))
#elif defined(__MACH__)
#define SWIFT_REMOTEAST_TEST_ABI __attribute__((__visibility__("default")))
#else
#define SWIFT_REMOTEAST_TEST_ABI __declspec(dllexport)
#endif

using namespace swift;
using namespace swift::remote;
using namespace swift::remoteAST;

#if defined(__APPLE__) && defined(__MACH__)
#include <dlfcn.h>
static unsigned long long computeClassIsSwiftMask(void) {
  uintptr_t *objc_debug_swift_stable_abi_bit_ptr =
    (uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit");
  return objc_debug_swift_stable_abi_bit_ptr ?
           *objc_debug_swift_stable_abi_bit_ptr : 1;
}
#else
static unsigned long long computeClassIsSwiftMask(void) {
  return 1;
}
#endif

extern "C" unsigned long long _swift_classIsSwiftMask =
  computeClassIsSwiftMask();

/// The context for the code we're running.  Set by the observer.
static ASTContext *context = nullptr;

/// The RemoteAST for the code we're running.
std::unique_ptr<RemoteASTContext> remoteContext;

static RemoteASTContext &getRemoteASTContext() {
  if (remoteContext)
    return *remoteContext;

  std::shared_ptr<MemoryReader> reader(new InProcessMemoryReader());
  remoteContext.reset(new RemoteASTContext(*context, std::move(reader)));
  return *remoteContext;
}

// FIXME: swiftcall
/// func printType(forMetadata: Any.Type)
LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI
printMetadataType(const Metadata *typeMetadata) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  auto result =
    remoteAST.getTypeForRemoteTypeMetadata(RemoteAddress(typeMetadata));
  if (result) {
    out << "found type: ";
    result.getValue().print(out);
    out << '\n';
  } else {
    out << result.getFailure().render() << '\n';
  }
}

// FIXME: swiftcall
/// func printDynamicType(_: AnyObject)
LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI
printHeapMetadataType(void *object) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  auto metadataResult =
    remoteAST.getHeapMetadataForObject(RemoteAddress(object));
  if (!metadataResult) {
    out << metadataResult.getFailure().render() << '\n';
    return;
  }
  auto metadata = metadataResult.getValue();

  auto result =
    remoteAST.getTypeForRemoteTypeMetadata(metadata, /*skipArtificial*/ true);
  if (result) {
    out << "found type: ";
    result.getValue().print(out);
    out << '\n';
  } else {
    out << result.getFailure().render() << '\n';
  }
}

static void printMemberOffset(const Metadata *typeMetadata,
                              StringRef memberName, bool passMetadata) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  // The first thing we have to do is get the type.
  auto typeResult =
    remoteAST.getTypeForRemoteTypeMetadata(RemoteAddress(typeMetadata));
  if (!typeResult) {
    out << "failed to find type: " << typeResult.getFailure().render() << '\n';
    return;
  }

  Type type = typeResult.getValue();

  RemoteAddress address =
    (passMetadata ? RemoteAddress(typeMetadata) : RemoteAddress(nullptr));

  auto offsetResult =
    remoteAST.getOffsetOfMember(type, address, memberName);
  if (!offsetResult) {
    out << "failed to find offset: "
        << offsetResult.getFailure().render() << '\n';
    return;
  }

  out << "found offset: " << offsetResult.getValue() << '\n';
}

// FIXME: swiftcall
/// func printTypeMemberOffset(forType: Any.Type, memberName: StaticString)
LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI
printTypeMemberOffset(const Metadata *typeMetadata,
                                      const char *memberName) {
  printMemberOffset(typeMetadata, memberName, /*pass metadata*/ false);
}

// FIXME: swiftcall
/// func printTypeMetadataMemberOffset(forType: Any.Type,
///                                    memberName: StaticString)
LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI
printTypeMetadataMemberOffset(const Metadata *typeMetadata,
                              const char *memberName) {
  printMemberOffset(typeMetadata, memberName, /*pass metadata*/ true);
}

// FIXME: swiftcall
/// func printDynamicTypeAndAddressForExistential<T>(_: T)
LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI
printDynamicTypeAndAddressForExistential(void *object,
                                         const Metadata *typeMetadata) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  // First, retrieve the static type of the existential, so we can understand
  // which kind of existential this is.
  auto staticTypeResult =
      remoteAST.getTypeForRemoteTypeMetadata(RemoteAddress(typeMetadata));
  if (!staticTypeResult) {
    out << "failed to resolve static type: "
        << staticTypeResult.getFailure().render() << '\n';
    return;
  }

  // OK, we can reconstruct the dynamic type and the address now.
  auto result = remoteAST.getDynamicTypeAndAddressForExistential(
      RemoteAddress(object), staticTypeResult.getValue());
  if (result) {
    out << "found type: ";
    result.getValue().InstanceType.print(out);
    out << "\n";
  } else {
    out << result.getFailure().render() << '\n';
  }
}

// FIXME: swiftcall
/// func stopRemoteAST(_: AnyObject)
LLVM_ATTRIBUTE_USED extern "C" void SWIFT_REMOTEAST_TEST_ABI
stopRemoteAST() {
  if (remoteContext)
    remoteContext.reset();
}

namespace {

struct Observer : public FrontendObserver {
  void configuredCompiler(CompilerInstance &instance) override {
    context = &instance.getASTContext();
  }
};

} // end anonymous namespace

int main(int argc, const char *argv[]) {
  PROGRAM_START(argc, argv);

  unsigned numForwardedArgs = argc
      - 1  // we drop argv[0]
      + 1; // -interpret

  SmallVector<const char *, 8> forwardedArgs;
  forwardedArgs.reserve(numForwardedArgs);
  forwardedArgs.append(&argv[1], &argv[argc]);
  forwardedArgs.push_back("-interpret");
  assert(forwardedArgs.size() == numForwardedArgs);

  Observer observer;
  return performFrontend(forwardedArgs, argv[0], (void*) &printMetadataType,
                         &observer);
}
<commit_msg>swift-remoteast-test: correct annotation of used functions (NFC)<commit_after>//===--- swift-remoteast-test.cpp - RemoteAST testing application ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// This file supports performing target-specific remote reflection tests
// on live swift executables.
//===----------------------------------------------------------------------===//

#include "swift/RemoteAST/RemoteAST.h"
#include "swift/Remote/InProcessMemoryReader.h"
#include "swift/Remote/MetadataReader.h"
#include "swift/Runtime/Metadata.h"
#include "swift/Frontend/Frontend.h"
#include "swift/FrontendTool/FrontendTool.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/LLVMInitialize.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>

#if defined(__ELF__)
#define SWIFT_REMOTEAST_TEST_ABI __attribute__((__visibility__("default")))
#elif defined(__MACH__)
#define SWIFT_REMOTEAST_TEST_ABI __attribute__((__visibility__("default")))
#else
#define SWIFT_REMOTEAST_TEST_ABI __declspec(dllexport)
#endif

using namespace swift;
using namespace swift::remote;
using namespace swift::remoteAST;

#if defined(__APPLE__) && defined(__MACH__)
#include <dlfcn.h>
static unsigned long long computeClassIsSwiftMask(void) {
  uintptr_t *objc_debug_swift_stable_abi_bit_ptr =
    (uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit");
  return objc_debug_swift_stable_abi_bit_ptr ?
           *objc_debug_swift_stable_abi_bit_ptr : 1;
}
#else
static unsigned long long computeClassIsSwiftMask(void) {
  return 1;
}
#endif

extern "C" unsigned long long _swift_classIsSwiftMask =
  computeClassIsSwiftMask();

/// The context for the code we're running.  Set by the observer.
static ASTContext *context = nullptr;

/// The RemoteAST for the code we're running.
std::unique_ptr<RemoteASTContext> remoteContext;

static RemoteASTContext &getRemoteASTContext() {
  if (remoteContext)
    return *remoteContext;

  std::shared_ptr<MemoryReader> reader(new InProcessMemoryReader());
  remoteContext.reset(new RemoteASTContext(*context, std::move(reader)));
  return *remoteContext;
}

// FIXME: swiftcall
/// func printType(forMetadata: Any.Type)
extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED
printMetadataType(const Metadata *typeMetadata) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  auto result =
    remoteAST.getTypeForRemoteTypeMetadata(RemoteAddress(typeMetadata));
  if (result) {
    out << "found type: ";
    result.getValue().print(out);
    out << '\n';
  } else {
    out << result.getFailure().render() << '\n';
  }
}

// FIXME: swiftcall
/// func printDynamicType(_: AnyObject)
extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED
printHeapMetadataType(void *object) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  auto metadataResult =
    remoteAST.getHeapMetadataForObject(RemoteAddress(object));
  if (!metadataResult) {
    out << metadataResult.getFailure().render() << '\n';
    return;
  }
  auto metadata = metadataResult.getValue();

  auto result =
    remoteAST.getTypeForRemoteTypeMetadata(metadata, /*skipArtificial*/ true);
  if (result) {
    out << "found type: ";
    result.getValue().print(out);
    out << '\n';
  } else {
    out << result.getFailure().render() << '\n';
  }
}

static void printMemberOffset(const Metadata *typeMetadata,
                              StringRef memberName, bool passMetadata) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  // The first thing we have to do is get the type.
  auto typeResult =
    remoteAST.getTypeForRemoteTypeMetadata(RemoteAddress(typeMetadata));
  if (!typeResult) {
    out << "failed to find type: " << typeResult.getFailure().render() << '\n';
    return;
  }

  Type type = typeResult.getValue();

  RemoteAddress address =
    (passMetadata ? RemoteAddress(typeMetadata) : RemoteAddress(nullptr));

  auto offsetResult =
    remoteAST.getOffsetOfMember(type, address, memberName);
  if (!offsetResult) {
    out << "failed to find offset: "
        << offsetResult.getFailure().render() << '\n';
    return;
  }

  out << "found offset: " << offsetResult.getValue() << '\n';
}

// FIXME: swiftcall
/// func printTypeMemberOffset(forType: Any.Type, memberName: StaticString)
extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED
printTypeMemberOffset(const Metadata *typeMetadata,
                                      const char *memberName) {
  printMemberOffset(typeMetadata, memberName, /*pass metadata*/ false);
}

// FIXME: swiftcall
/// func printTypeMetadataMemberOffset(forType: Any.Type,
///                                    memberName: StaticString)
extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED
printTypeMetadataMemberOffset(const Metadata *typeMetadata,
                              const char *memberName) {
  printMemberOffset(typeMetadata, memberName, /*pass metadata*/ true);
}

// FIXME: swiftcall
/// func printDynamicTypeAndAddressForExistential<T>(_: T)
extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED
printDynamicTypeAndAddressForExistential(void *object,
                                         const Metadata *typeMetadata) {
  auto &remoteAST = getRemoteASTContext();
  auto &out = llvm::outs();

  // First, retrieve the static type of the existential, so we can understand
  // which kind of existential this is.
  auto staticTypeResult =
      remoteAST.getTypeForRemoteTypeMetadata(RemoteAddress(typeMetadata));
  if (!staticTypeResult) {
    out << "failed to resolve static type: "
        << staticTypeResult.getFailure().render() << '\n';
    return;
  }

  // OK, we can reconstruct the dynamic type and the address now.
  auto result = remoteAST.getDynamicTypeAndAddressForExistential(
      RemoteAddress(object), staticTypeResult.getValue());
  if (result) {
    out << "found type: ";
    result.getValue().InstanceType.print(out);
    out << "\n";
  } else {
    out << result.getFailure().render() << '\n';
  }
}

// FIXME: swiftcall
/// func stopRemoteAST(_: AnyObject)
extern "C" void SWIFT_REMOTEAST_TEST_ABI LLVM_ATTRIBUTE_USED
stopRemoteAST() {
  if (remoteContext)
    remoteContext.reset();
}

namespace {

struct Observer : public FrontendObserver {
  void configuredCompiler(CompilerInstance &instance) override {
    context = &instance.getASTContext();
  }
};

} // end anonymous namespace

int main(int argc, const char *argv[]) {
  PROGRAM_START(argc, argv);

  unsigned numForwardedArgs = argc
      - 1  // we drop argv[0]
      + 1; // -interpret

  SmallVector<const char *, 8> forwardedArgs;
  forwardedArgs.reserve(numForwardedArgs);
  forwardedArgs.append(&argv[1], &argv[argc]);
  forwardedArgs.push_back("-interpret");
  assert(forwardedArgs.size() == numForwardedArgs);

  Observer observer;
  return performFrontend(forwardedArgs, argv[0], (void*) &printMetadataType,
                         &observer);
}
<|endoftext|>


<commit_before>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2007  Benjamin S. Kirk, John W. Peterson

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh_common.h"

#ifdef HAVE_PETSC

// C++ includes

// Local Includes
#include "petsc_nonlinear_solver.h"
#include "petsc_vector.h"
#include "petsc_matrix.h"

//--------------------------------------------------------------------
// Functions with C linkage to pass to PETSc.  PETSc will call these
// methods as needed.
//
// Since they must have C linkage they have no knowledge of a namespace.
// Give them an obscure name to avoid namespace pollution.
extern "C"
{
  // Older versions of PETSc do not have the different int typedefs.
  // On 64-bit machines, PetscInt may actually be a long long int.
  // This change occurred in Petsc-2.2.1.
#if PETSC_VERSION_LESS_THAN(2,2,1)
  typedef int PetscErrorCode;
  typedef int PetscInt;
#endif

  //-------------------------------------------------------------------
  // this function is called by PETSc at the end of each nonlinear step
  PetscErrorCode
  __libmesh_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *)
  {
    //int ierr=0;

    //if (its > 0)
      std::cout << "  NL step " << its
		<< std::scientific
		<< ", |residual|_2 = " << fnorm
		<< std::endl;

    //return ierr;
    return 0;
  }

  //---------------------------------------------------------------
  // this function is called by PETSc to evaluate the residual at X
  PetscErrorCode
  __libmesh_petsc_snes_residual (SNES, Vec x, Vec r, void *ctx)
  {
    int ierr=0;

    assert (x   != NULL);
    assert (r   != NULL);
    assert (ctx != NULL);

    PetscNonlinearSolver<Number>* solver =
      static_cast<PetscNonlinearSolver<Number>*> (ctx);

    PetscVector<Number> X_global(x), R(r);
    PetscVector<Number> X_local(X_global.size());

    X_global.localize (X_local);

    if (solver->residual != NULL) solver->residual (X_local, R);
    if (solver->matvec   != NULL) solver->matvec   (X_local, &R, NULL);

    R.close();

    return ierr;
  }

  //---------------------------------------------------------------
  // this function is called by PETSc to evaluate the Jacobian at X
  PetscErrorCode
  __libmesh_petsc_snes_jacobian (SNES, Vec x, Mat *jac, Mat *pc, MatStructure *msflag, void *ctx)
  {
    int ierr=0;

    assert (ctx != NULL);

    PetscNonlinearSolver<Number>* solver =
      static_cast<PetscNonlinearSolver<Number>*> (ctx);

    PetscMatrix<Number> PC(*pc);
    PetscMatrix<Number> Jac(*jac);
    PetscVector<Number> X_global(x);
    PetscVector<Number> X_local (X_global.size());

    X_global.localize (X_local);

    if (solver->jacobian != NULL) solver->jacobian (X_local, PC);
    if (solver->matvec   != NULL) solver->matvec   (X_local, NULL, &PC);

    PC.close();
    Jac.close();

    *msflag = SAME_NONZERO_PATTERN;

    return ierr;
  }

} // end extern "C"
//---------------------------------------------------------------------

//---------------------------------------------------------------------
// PetscNonlinearSolver<> methods
template <typename T>
void PetscNonlinearSolver<T>::clear ()
{
  if (this->initialized())
    {
      this->_is_initialized = false;

      int ierr=0;

      ierr = SNESDestroy(_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }
}

template <typename T>
void PetscNonlinearSolver<T>::init ()
{
  // Initialize the data structures if not done so already.
  if (!this->initialized())
    {
      this->_is_initialized = true;

      int ierr=0;

#if PETSC_VERSION_LESS_THAN(2,1,2)
      // At least until Petsc 2.1.1, the SNESCreate had a different calling syntax.
      // The second argument was of type SNESProblemType, and could have a value of
      // either SNES_NONLINEAR_EQUATIONS or SNES_UNCONSTRAINED_MINIMIZATION.
      ierr = SNESCreate(libMesh::COMM_WORLD, SNES_NONLINEAR_EQUATIONS, &_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

#else

      ierr = SNESCreate(libMesh::COMM_WORLD,&_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

#endif

#if PETSC_VERSION_LESS_THAN(2,3,3)
      ierr = SNESSetMonitor (_snes, __libmesh_petsc_snes_monitor,
			     this, PETSC_NULL);
#else
      // API name change in PETSc 2.3.3
      ierr = SNESMonitorSet (_snes, __libmesh_petsc_snes_monitor,
			     this, PETSC_NULL);
#endif
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = SNESSetFromOptions(_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }
}

template <typename T>
std::pair<unsigned int, Real>
PetscNonlinearSolver<T>::solve (SparseMatrix<T>&  jac_in,  // System Jacobian Matrix
				NumericVector<T>& x_in,    // Solution vector
				NumericVector<T>& r_in,    // Residual vector
				const double,              // Stopping tolerance
				const unsigned int)
{
  this->init ();

  PetscMatrix<T>* jac = dynamic_cast<PetscMatrix<T>*>(&jac_in);
  PetscVector<T>* x   = dynamic_cast<PetscVector<T>*>(&x_in);
  PetscVector<T>* r   = dynamic_cast<PetscVector<T>*>(&r_in);

  // We cast to pointers so we can be sure that they succeeded
  // by comparing the result against NULL.
  assert(jac != NULL); assert(jac->mat() != NULL);
  assert(x   != NULL); assert(x->vec()   != NULL);
  assert(r   != NULL); assert(r->vec()   != NULL);

  int ierr=0;
  int n_iterations =0;

  ierr = SNESSetFunction (_snes, r->vec(), __libmesh_petsc_snes_residual, this);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  ierr = SNESSetJacobian (_snes, jac->mat(), jac->mat(), __libmesh_petsc_snes_jacobian, this);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

// Older versions (at least up to 2.1.5) of SNESSolve took 3 arguments,
// the last one being a pointer to an int to hold the number of iterations required.
# if PETSC_VERSION_LESS_THAN(2,2,0)

 ierr = SNESSolve (_snes, x->vec(), &n_iterations);
        CHKERRABORT(libMesh::COMM_WORLD,ierr);

// 2.2.x style
#elif PETSC_VERSION_LESS_THAN(2,3,0)

 ierr = SNESSolve (_snes, x->vec());
        CHKERRABORT(libMesh::COMM_WORLD,ierr);

// 2.3.x & newer style
#else

 ierr = SNESSolve (_snes, PETSC_NULL, x->vec());
        CHKERRABORT(libMesh::COMM_WORLD,ierr);

#endif

  this->clear();

  // return the # of its. and the final residual norm.  Note that
  // n_iterations may be zero for PETSc versions 2.2.x and greater.
  return std::make_pair(n_iterations, 0.);
}

//------------------------------------------------------------------
// Explicit instantiations
template class PetscNonlinearSolver<Number>;

#endif // #ifdef HAVE_PETSC
<commit_msg>explicitly clear and close the objects<commit_after>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2007  Benjamin S. Kirk, John W. Peterson

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh_common.h"

#ifdef HAVE_PETSC

// C++ includes

// Local Includes
#include "petsc_nonlinear_solver.h"
#include "petsc_vector.h"
#include "petsc_matrix.h"

//--------------------------------------------------------------------
// Functions with C linkage to pass to PETSc.  PETSc will call these
// methods as needed.
//
// Since they must have C linkage they have no knowledge of a namespace.
// Give them an obscure name to avoid namespace pollution.
extern "C"
{
  // Older versions of PETSc do not have the different int typedefs.
  // On 64-bit machines, PetscInt may actually be a long long int.
  // This change occurred in Petsc-2.2.1.
#if PETSC_VERSION_LESS_THAN(2,2,1)
  typedef int PetscErrorCode;
  typedef int PetscInt;
#endif

  //-------------------------------------------------------------------
  // this function is called by PETSc at the end of each nonlinear step
  PetscErrorCode
  __libmesh_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *)
  {
    //int ierr=0;

    //if (its > 0)
      std::cout << "  NL step " << its
		<< std::scientific
		<< ", |residual|_2 = " << fnorm
		<< std::endl;

    //return ierr;
    return 0;
  }

  //---------------------------------------------------------------
  // this function is called by PETSc to evaluate the residual at X
  PetscErrorCode
  __libmesh_petsc_snes_residual (SNES, Vec x, Vec r, void *ctx)
  {
    int ierr=0;

    assert (x   != NULL);
    assert (r   != NULL);
    assert (ctx != NULL);

    PetscNonlinearSolver<Number>* solver =
      static_cast<PetscNonlinearSolver<Number>*> (ctx);

    PetscVector<Number> X_global(x), R(r);
    PetscVector<Number> X_local(X_global.size());

    X_global.localize (X_local);

    R.zero();

    if      (solver->residual != NULL) solver->residual (X_local, R);
    else if (solver->matvec   != NULL) solver->matvec   (X_local, &R, NULL);

    R.close();

    return ierr;
  }

  //---------------------------------------------------------------
  // this function is called by PETSc to evaluate the Jacobian at X
  PetscErrorCode
  __libmesh_petsc_snes_jacobian (SNES, Vec x, Mat *jac, Mat *pc, MatStructure *msflag, void *ctx)
  {
    int ierr=0;

    assert (ctx != NULL);

    PetscNonlinearSolver<Number>* solver =
      static_cast<PetscNonlinearSolver<Number>*> (ctx);

    PetscMatrix<Number> PC(*pc);
    PetscMatrix<Number> Jac(*jac);
    PetscVector<Number> X_global(x);
    PetscVector<Number> X_local (X_global.size());

    X_global.localize (X_local);
    PC.zero();

    if      (solver->jacobian != NULL) solver->jacobian (X_local, PC);
    else if (solver->matvec   != NULL) solver->matvec   (X_local, NULL, &PC);

    PC.close();
    Jac.close();

    *msflag = SAME_NONZERO_PATTERN;

    return ierr;
  }

} // end extern "C"
//---------------------------------------------------------------------

//---------------------------------------------------------------------
// PetscNonlinearSolver<> methods
template <typename T>
void PetscNonlinearSolver<T>::clear ()
{
  if (this->initialized())
    {
      this->_is_initialized = false;

      int ierr=0;

      ierr = SNESDestroy(_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }
}

template <typename T>
void PetscNonlinearSolver<T>::init ()
{
  // Initialize the data structures if not done so already.
  if (!this->initialized())
    {
      this->_is_initialized = true;

      int ierr=0;

#if PETSC_VERSION_LESS_THAN(2,1,2)
      // At least until Petsc 2.1.1, the SNESCreate had a different calling syntax.
      // The second argument was of type SNESProblemType, and could have a value of
      // either SNES_NONLINEAR_EQUATIONS or SNES_UNCONSTRAINED_MINIMIZATION.
      ierr = SNESCreate(libMesh::COMM_WORLD, SNES_NONLINEAR_EQUATIONS, &_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

#else

      ierr = SNESCreate(libMesh::COMM_WORLD,&_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

#endif

#if PETSC_VERSION_LESS_THAN(2,3,3)
      ierr = SNESSetMonitor (_snes, __libmesh_petsc_snes_monitor,
			     this, PETSC_NULL);
#else
      // API name change in PETSc 2.3.3
      ierr = SNESMonitorSet (_snes, __libmesh_petsc_snes_monitor,
			     this, PETSC_NULL);
#endif
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = SNESSetFromOptions(_snes);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }
}

template <typename T>
std::pair<unsigned int, Real>
PetscNonlinearSolver<T>::solve (SparseMatrix<T>&  jac_in,  // System Jacobian Matrix
				NumericVector<T>& x_in,    // Solution vector
				NumericVector<T>& r_in,    // Residual vector
				const double,              // Stopping tolerance
				const unsigned int)
{
  this->init ();

  PetscMatrix<T>* jac = dynamic_cast<PetscMatrix<T>*>(&jac_in);
  PetscVector<T>* x   = dynamic_cast<PetscVector<T>*>(&x_in);
  PetscVector<T>* r   = dynamic_cast<PetscVector<T>*>(&r_in);

  // We cast to pointers so we can be sure that they succeeded
  // by comparing the result against NULL.
  assert(jac != NULL); assert(jac->mat() != NULL);
  assert(x   != NULL); assert(x->vec()   != NULL);
  assert(r   != NULL); assert(r->vec()   != NULL);

  int ierr=0;
  int n_iterations =0;

  ierr = SNESSetFunction (_snes, r->vec(), __libmesh_petsc_snes_residual, this);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  ierr = SNESSetJacobian (_snes, jac->mat(), jac->mat(), __libmesh_petsc_snes_jacobian, this);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

   // Have the Krylov subspace method use our good initial guess rather than 0
   KSP ksp;
   ierr = SNESGetKSP (_snes, &ksp);
          CHKERRABORT(libMesh::COMM_WORLD,ierr);

//    ierr = KSPSetInitialGuessNonzero (ksp, PETSC_TRUE);
//           CHKERRABORT(libMesh::COMM_WORLD,ierr);

// Older versions (at least up to 2.1.5) of SNESSolve took 3 arguments,
// the last one being a pointer to an int to hold the number of iterations required.
# if PETSC_VERSION_LESS_THAN(2,2,0)

 ierr = SNESSolve (_snes, x->vec(), &n_iterations);
        CHKERRABORT(libMesh::COMM_WORLD,ierr);

// 2.2.x style
#elif PETSC_VERSION_LESS_THAN(2,3,0)

 ierr = SNESSolve (_snes, x->vec());
        CHKERRABORT(libMesh::COMM_WORLD,ierr);

// 2.3.x & newer style
#else

 ierr = SNESSolve (_snes, PETSC_NULL, x->vec());
        CHKERRABORT(libMesh::COMM_WORLD,ierr);

#endif

  this->clear();

  // return the # of its. and the final residual norm.  Note that
  // n_iterations may be zero for PETSc versions 2.2.x and greater.
  return std::make_pair(n_iterations, 0.);
}

//------------------------------------------------------------------
// Explicit instantiations
template class PetscNonlinearSolver<Number>;

#endif // #ifdef HAVE_PETSC
<|endoftext|>