text:
// Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: yanshiguang02@baidu.com

#include "nameserver_impl.h"

#include <set>
#include <map>
#include <sstream>

#include <boost/bind.hpp>
#include <gflags/gflags.h>
#include <sofa/pbrpc/pbrpc.h>

#include <common/counter.h>
#include <common/logging.h>
#include <common/string_util.h>

#include "nameserver/block_mapping_manager.h"

#include "nameserver/sync.h"
#include "nameserver/chunkserver_manager.h"
#include "nameserver/namespace.h"

#include "proto/status_code.pb.h"

DECLARE_bool(bfs_web_kick_enable);
DECLARE_int32(nameserver_safemode_time);
DECLARE_int32(chunkserver_max_pending_buffers);
DECLARE_int32(nameserver_report_thread_num);
DECLARE_int32(nameserver_work_thread_num);
DECLARE_int32(blockmapping_bucket_num);

namespace baidu {
namespace bfs {

common::Counter g_get_location;
common::Counter g_add_block;
common::Counter g_heart_beat;
common::Counter g_block_report;
common::Counter g_unlink;
common::Counter g_create_file;
common::Counter g_list_dir;
common::Counter g_report_blocks;

NameServerImpl::NameServerImpl(Sync* sync) : safe_mode_(FLAGS_nameserver_safemode_time), sync_(sync) {
    block_mapping_manager_ = new BlockMappingManager(FLAGS_blockmapping_bucket_num);
    report_thread_pool_ = new common::ThreadPool(FLAGS_nameserver_report_thread_num);
    work_thread_pool_ = new common::ThreadPool(FLAGS_nameserver_work_thread_num);
    chunkserver_manager_ = new ChunkServerManager(work_thread_pool_, block_mapping_manager_);
    namespace_ = new NameSpace(false);
    if (sync_) {
        sync_->Init(boost::bind(&NameSpace::TailLog, namespace_, _1));
    }
    CheckLeader();
    start_time_ = common::timer::get_micros();
    work_thread_pool_->AddTask(boost::bind(&NameServerImpl::LogStatus, this));
}

NameServerImpl::~NameServerImpl() {
}

void NameServerImpl::CheckLeader() {
    if (!sync_ || sync_->IsLeader()) {
        LOG(INFO, "Leader nameserver, rebuild block map.");
        NameServerLog log;
        boost::function<void (const FileInfo&)> task =
            boost::bind(&NameServerImpl::RebuildBlockMapCallback, this, _1);
        namespace_->Activate(task, &log);
        if (!LogRemote(log, boost::function<void (bool)>())) {
            LOG(FATAL, "LogRemote namespace update fail");
        }
        safe_mode_ = FLAGS_nameserver_safemode_time;
        work_thread_pool_->DelayTask(1000, boost::bind(&NameServerImpl::CheckSafemode, this));
        is_leader_ = true;
    } else {
        is_leader_ = false;
        work_thread_pool_->DelayTask(100, boost::bind(&NameServerImpl::CheckLeader, this));
        //LOG(INFO, "Delay CheckLeader");
    }
}

void NameServerImpl::CheckSafemode() {
    int now_time = (common::timer::get_micros() - start_time_) / 1000000;
    int safe_mode = safe_mode_;
    if (safe_mode == 0) {
        return;
    }
    int new_safe_mode = FLAGS_nameserver_safemode_time - now_time;
    if (new_safe_mode <= 0) {
        LOG(INFO, "Now time %d", now_time);
        LeaveSafemode();
        return;
    }
    common::atomic_comp_swap(&safe_mode_, new_safe_mode, safe_mode);
    work_thread_pool_->DelayTask(1000, boost::bind(&NameServerImpl::CheckSafemode, this));
}
void NameServerImpl::LeaveSafemode() {
    LOG(INFO, "Nameserver leave safemode");
    block_mapping_manager_->SetSafeMode(false);
    safe_mode_ = 0;
}

void NameServerImpl::LogStatus() {
    LOG(INFO, "[Status] create %ld list %ld get_loc %ld add_block %ld "
              "unlink %ld report %ld %ld heartbeat %ld",
        g_create_file.Clear(), g_list_dir.Clear(), g_get_location.Clear(),
        g_add_block.Clear(), g_unlink.Clear(), g_block_report.Clear(),
        g_report_blocks.Clear(), g_heart_beat.Clear());
    work_thread_pool_->DelayTask(1000, boost::bind(&NameServerImpl::LogStatus, this));
}

void NameServerImpl::HeartBeat(::google::protobuf::RpcController* controller,
                         const HeartBeatRequest* request,
                         HeartBeatResponse* response,
                         ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    g_heart_beat.Inc();
    // printf("Receive HeartBeat() from %s\n", request->data_server_addr().c_str());
    int64_t version = request->namespace_version();
    if (version == namespace_->Version()) {
        chunkserver_manager_->HandleHeartBeat(request, response);
    } else {
        response->set_status(kVersionError);
    }
    response->set_namespace_version(namespace_->Version());
    done->Run();
}

void NameServerImpl::Register(::google::protobuf::RpcController* controller,
                   const ::baidu::bfs::RegisterRequest* request,
                   ::baidu::bfs::RegisterResponse* response,
                   ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    sofa::pbrpc::RpcController* sofa_cntl =
        reinterpret_cast<sofa::pbrpc::RpcController*>(controller);
    const std::string& address = request->chunkserver_addr();
    const std::string& ip_address = sofa_cntl->RemoteAddress();
    const std::string cs_ip = ip_address.substr(ip_address.find(':'));
    LOG(INFO, "Register ip: %s", ip_address.c_str());
    int64_t version = request->namespace_version();
    if (version != namespace_->Version()) {
        LOG(INFO, "Register from %s version %ld mismatch %ld, remove internal",
            address.c_str(), version, namespace_->Version());
        chunkserver_manager_->RemoveChunkServer(address);
    } else {
        LOG(INFO, "Register from %s, version= %ld", address.c_str(), version);
        chunkserver_manager_->HandleRegister(cs_ip, request, response);
    }
    response->set_namespace_version(namespace_->Version());
    done->Run();
}

void NameServerImpl::BlockReceived(::google::protobuf::RpcController* controller,
                       const BlockReceivedRequest* request,
                       BlockReceivedResponse* response,
                       ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    if (!response->has_sequence_id()) {
        response->set_sequence_id(request->sequence_id());
        boost::function<void ()> task =
            boost::bind(&NameServerImpl::BlockReceived, this, controller, request, response, done);
        work_thread_pool_->AddTask(task);
        return;
    }
    g_block_report.Inc();
    int32_t cs_id = request->chunkserver_id();
    LOG(INFO, "BlockReceived from C%d, %s, %d blocks",
        cs_id, request->chunkserver_addr().c_str(), request->blocks_size());
    const ::google::protobuf::RepeatedPtrField<ReportBlockInfo>& blocks = request->blocks();

    int old_id = chunkserver_manager_->GetChunkServerId(request->chunkserver_addr());
    if (cs_id != old_id) {
        LOG(INFO, "ChunkServer %s id mismatch, old: C%d new: C%d",
            request->chunkserver_addr().c_str(), old_id, cs_id);
        response->set_status(kUnknownCs);
        done->Run();
        return;
    }
    for (int i = 0; i < blocks.size(); i++) {
        g_report_blocks.Inc();
        const ReportBlockInfo& block =  blocks.Get(i);
        int64_t block_id = block.block_id();
        int64_t block_size = block.block_size();
        int64_t block_version = block.version();
        LOG(INFO, "BlockReceived C%d #%ld V%ld %ld",
            cs_id, block_id, block_version, block_size);
        // update block -> cs;
        if (block_mapping_manager_->UpdateBlockInfo(block_id, cs_id,
                                            block_size,
                                            block_version)) {
            // update cs -> block
            chunkserver_manager_->AddBlock(cs_id, block_id);
        } else {
            LOG(INFO, "BlockReceived drop C%d #%ld V%ld %ld",
                cs_id, block_id, block_version, block_size);
        }
    }
    response->set_status(kOK);
    done->Run();
}

void NameServerImpl::BlockReport(::google::protobuf::RpcController* controller,
                   const BlockReportRequest* request,
                   BlockReportResponse* response,
                   ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    g_block_report.Inc();
    if (!response->has_sequence_id()) {
        response->set_sequence_id(request->sequence_id());
        boost::function<void ()> task =
            boost::bind(&NameServerImpl::BlockReport, this, controller, request, response, done);
        report_thread_pool_->AddTask(task);
        return;
    }
    int32_t cs_id = request->chunkserver_id();
    LOG(INFO, "Report from C%d %s %d blocks\n",
        cs_id, request->chunkserver_addr().c_str(), request->blocks_size());
    const ::google::protobuf::RepeatedPtrField<ReportBlockInfo>& blocks = request->blocks();

    int old_id = chunkserver_manager_->GetChunkServerId(request->chunkserver_addr());
    if (cs_id != old_id) {
        LOG(INFO, "ChunkServer %s id mismatch, old: C%d new: C%d , need to re-register",
            request->chunkserver_addr().c_str(), old_id, cs_id);
        response->set_status(kUnknownCs);
        done->Run();
        return;
    }
    for (int i = 0; i < blocks.size(); i++) {
        g_report_blocks.Inc();
        const ReportBlockInfo& block =  blocks.Get(i);
        int64_t cur_block_id = block.block_id();
        int64_t cur_block_size = block.block_size();

        // update block -> cs
        int64_t block_version = block.version();
        if (!block_mapping_manager_->UpdateBlockInfo(cur_block_id, cs_id,
                                             cur_block_size,
                                             block_version)) {
            response->add_obsolete_blocks(cur_block_id);
            chunkserver_manager_->RemoveBlock(cs_id, cur_block_id);
            LOG(INFO, "BlockReport remove obsolete block: #%ld C%d ", cur_block_id, cs_id);
            continue;
        }

        // update cs -> block
        chunkserver_manager_->AddBlock(cs_id, cur_block_id);
    }

    // recover replica
    if (!safe_mode_) {
        std::vector<std::pair<int64_t, std::vector<std::string> > > recover_blocks;
        int hi_num = 0;
        chunkserver_manager_->PickRecoverBlocks(cs_id, &recover_blocks, &hi_num);
        int32_t priority = 0;
        for (std::vector<std::pair<int64_t, std::vector<std::string> > >::iterator it =
                recover_blocks.begin(); it != recover_blocks.end(); ++it) {
            ReplicaInfo* rep = response->add_new_replicas();
            rep->set_block_id((*it).first);
            rep->set_priority(priority++ < hi_num);
            for (std::vector<std::string>::iterator dest_it = (*it).second.begin();
                 dest_it != (*it).second.end(); ++dest_it) {
                rep->add_chunkserver_address(*dest_it);
            }
        }
        LOG(INFO, "Response to C%d %s new_replicas_size= %d",
            cs_id, request->chunkserver_addr().c_str(), response->new_replicas_size());
    }
    block_mapping_manager_->GetCloseBlocks(cs_id, response->mutable_close_blocks());
    response->set_status(kOK);
    done->Run();
}

void NameServerImpl::PushBlockReport(::google::protobuf::RpcController* controller,
                   const PushBlockReportRequest* request,
                   PushBlockReportResponse* response,
                   ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    response->set_status(kOK);
    int32_t cs_id = request->chunkserver_id();
    for (int i = 0; i < request->blocks_size(); i++) {
        block_mapping_manager_->ProcessRecoveredBlock(cs_id, request->blocks(i));
    }
    done->Run();
}

void NameServerImpl::CreateFile(::google::protobuf::RpcController* controller,
                        const CreateFileRequest* request,
                        CreateFileResponse* response,
                        ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    g_create_file.Inc();
    response->set_sequence_id(request->sequence_id());
    std::string path = NameSpace::NormalizePath(request->file_name());
    int flags = request->flags();
    int mode = request->mode();
    if (mode == 0) {
        mode = 0644;    // default mode
    }
    int replica_num = request->replica_num();
    NameServerLog log;
    std::vector<int64_t> blocks_to_remove;
    StatusCode status = namespace_->CreateFile(path, flags, mode, replica_num, &blocks_to_remove, &log);
    for (size_t i = 0; i < blocks_to_remove.size(); i++) {
        block_mapping_manager_->RemoveBlock(blocks_to_remove[i]);
    }
    response->set_status(status);
    if (status != kOK) {
        done->Run();
        return;
    }
    LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                               controller, request, response, done,
                               (std::vector<FileInfo>*)NULL, _1));
}

bool NameServerImpl::LogRemote(const NameServerLog& log, boost::function<void (bool)> callback) {
    if (sync_ == NULL) {
        if (!callback.empty()) {
            work_thread_pool_->AddTask(boost::bind(callback, true));
        }
        return true;
    }
    std::string logstr;
    if (!log.SerializeToString(&logstr)) {
        LOG(FATAL, "Serialize log fail");
    }
    if (callback.empty()) {
        return sync_->Log(logstr);
    } else {
        sync_->Log(logstr, callback);
        return true;
    }
}

void NameServerImpl::SyncLogCallback(::google::protobuf::RpcController* controller,
                                     const ::google::protobuf::Message* request,
                                     ::google::protobuf::Message* response,
                                     ::google::protobuf::Closure* done,
                                     std::vector<FileInfo>* removed,
                                     bool ret) {
    if (!ret) {
        controller->SetFailed("SyncLogFail");
    } else if (removed) {
        for (uint32_t i = 0; i < removed->size(); i++) {
            block_mapping_manager_->RemoveBlocksForFile((*removed)[i]);
        }
        delete removed;
    }
    done->Run();
    if (!ret) {
        LOG(FATAL, "SyncLog fail");
    }
}

void NameServerImpl::AddBlock(::google::protobuf::RpcController* controller,
                         const AddBlockRequest* request,
                         AddBlockResponse* response,
                         ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    if (safe_mode_) {
        LOG(INFO, "AddBlock for %s failed, safe mode.", request->file_name().c_str());
        response->set_status(kSafeMode);
        done->Run();
        return;
    }
    g_add_block.Inc();
    std::string path = NameSpace::NormalizePath(request->file_name());
    FileInfo file_info;
    if (!namespace_->GetFileInfo(path, &file_info)) {
        LOG(INFO, "AddBlock file not found: %s", path.c_str());
        response->set_status(kNsNotFound);
        done->Run();
        return;
    }

    if (file_info.blocks_size() > 0) {
        block_mapping_manager_->RemoveBlocksForFile(file_info);
        file_info.clear_blocks();
    }
    /// replica num
    int replica_num = file_info.replicas();
    /// check lease for write
    std::vector<std::pair<int32_t, std::string> > chains;
    if (chunkserver_manager_->GetChunkServerChains(replica_num, &chains, request->client_address())) {
        NameServerLog log;
        int64_t new_block_id = namespace_->GetNewBlockId(&log);
        LOG(INFO, "[AddBlock] new block for %s #%ld R%d %s",
            path.c_str(), new_block_id, replica_num, request->client_address().c_str());
        file_info.add_blocks(new_block_id);
        file_info.set_version(-1);
        ///TODO: Lost update? Get&Update not atomic.
        for (int i = 0; i < replica_num; i++) {
            file_info.add_cs_addrs(chunkserver_manager_->GetChunkServerAddr(chains[i].first));
        }
        if (!namespace_->UpdateFileInfo(file_info, &log)) {
            LOG(WARNING, "Update file info fail: %s", path.c_str());
            response->set_status(kUpdateError);
        }
        LocatedBlock* block = response->mutable_block();
        std::vector<int32_t> replicas;
        for (int i = 0; i < replica_num; i++) {
            ChunkServerInfo* info = block->add_chains();
            int32_t cs_id = chains[i].first;
            info->set_address(chains[i].second);
            LOG(INFO, "Add C%d %s to #%ld response",
                cs_id, chains[i].second.c_str(), new_block_id);
            replicas.push_back(cs_id);
            // update cs -> block
            chunkserver_manager_->AddBlock(cs_id, new_block_id);
        }
        block_mapping_manager_->AddNewBlock(new_block_id, replica_num, -1, 0, &replicas);
        block->set_block_id(new_block_id);
        response->set_status(kOK);
        LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                                   controller, request, response, done,
                                   (std::vector<FileInfo>*)NULL, _1));
    } else {
        LOG(WARNING, "AddBlock for %s failed.", path.c_str());
        response->set_status(kGetChunkServerError);
        done->Run();
    }
}

void NameServerImpl::FinishBlock(::google::protobuf::RpcController* controller,
                         const FinishBlockRequest* request,
                         FinishBlockResponse* response,
                         ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    int64_t block_id = request->block_id();
    int64_t block_version = request->block_version();
    response->set_sequence_id(request->sequence_id());
    std::string file_name = NameSpace::NormalizePath(request->file_name());
    if (request->close_with_error()) {
        LOG(INFO, "Sdk close %s with error", file_name.c_str());
        block_mapping_manager_->MarkIncomplete(block_id);
        response->set_status(kOK);
        done->Run();
        return;
    }
    FileInfo file_info;
    if (!namespace_->GetFileInfo(file_name, &file_info)) {
        LOG(INFO, "FinishBlock file not found: #%ld %s", block_id, file_name.c_str());
        response->set_status(kNsNotFound);
        done->Run();
        return;
    }
    file_info.set_version(block_version);
    file_info.set_size(request->block_size());
    NameServerLog log;
    if (!namespace_->UpdateFileInfo(file_info, &log)) {
        LOG(WARNING, "FinishBlock fail: #%ld %s", block_id, file_name.c_str());
        response->set_status(kUpdateError);
        done->Run();
        return;
    }
    StatusCode ret = block_mapping_manager_->CheckBlockVersion(block_id, block_version);
    response->set_status(ret);
    if (ret != kOK) {
        LOG(INFO, "FinishBlock fail: #%ld %s", block_id, file_name.c_str());
        done->Run();
    } else {
        LOG(DEBUG, "FinishBlock #%ld %s", block_id, file_name.c_str());
        LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                                   controller, request, response, done,
                                   (std::vector<FileInfo>*)NULL, _1));
    }
}

void NameServerImpl::GetFileLocation(::google::protobuf::RpcController* controller,
                      const FileLocationRequest* request,
                      FileLocationResponse* response,
                      ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    std::string path = NameSpace::NormalizePath(request->file_name());
    LOG(INFO, "NameServerImpl::GetFileLocation: %s\n", request->file_name().c_str());
    // Get file_key
    g_get_location.Inc();

    FileInfo info;
    if (!namespace_->GetFileInfo(path, &info)) {
        // No this file
        LOG(INFO, "NameServerImpl::GetFileLocation: NotFound: %s",
            request->file_name().c_str());
        response->set_status(kNsNotFound);
    } else {
        for (int i = 0; i < info.blocks_size(); i++) {
            int64_t block_id = info.blocks(i);
            std::vector<int32_t> replica;
            int64_t block_size = 0;
            if (!block_mapping_manager_->GetLocatedBlock(block_id, &replica, &block_size)) {
                LOG(WARNING, "GetFileLocation GetBlockReplica fail #%ld ", block_id);
                break;
            }
            LocatedBlock* lcblock = response->add_blocks();
            lcblock->set_block_id(block_id);
            lcblock->set_block_size(block_size);
            for (uint32_t i = 0; i < replica.size(); i++) {
                int32_t server_id = replica[i];
                std::string addr = chunkserver_manager_->GetChunkServerAddr(server_id);
                if (addr == "") {
                    LOG(WARNING, "GetChunkServerAddr from id: C%d fail.", server_id);
                    continue;
                }
                LOG(INFO, "return server C%d %s for #%ld ", server_id, addr.c_str(), block_id);
                ChunkServerInfo* cs_info = lcblock->add_chains();
                cs_info->set_address(addr);
            }
            LOG(INFO, "NameServerImpl::GetFileLocation: %s return #%ld R%lu",
                request->file_name().c_str(), block_id, replica.size());
        }
        // success if file exist
        response->set_status(kOK);
    }
    done->Run();
}

void NameServerImpl::ListDirectory(::google::protobuf::RpcController* controller,
                        const ListDirectoryRequest* request,
                        ListDirectoryResponse* response,
                        ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    if (!response->has_sequence_id()) {
        response->set_sequence_id(request->sequence_id());
        boost::function<void ()> task =
            boost::bind(&NameServerImpl::ListDirectory, this, controller, request, response, done);
        work_thread_pool_->AddTask(task);
        return;
    }
    g_list_dir.Inc();
    response->set_sequence_id(request->sequence_id());
    std::string path = NameSpace::NormalizePath(request->path());
    common::timer::AutoTimer at(100, "ListDirectory", path.c_str());

    StatusCode status = namespace_->ListDirectory(path, response->mutable_files());
    response->set_status(status);
    done->Run();
}

void NameServerImpl::Stat(::google::protobuf::RpcController* controller,
                          const StatRequest* request,
                          StatResponse* response,
                          ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    std::string path = NameSpace::NormalizePath(request->path());
    LOG(INFO, "Stat: %s\n", path.c_str());

    FileInfo info;
    if (namespace_->GetFileInfo(path, &info)) {
        FileInfo* out_info = response->mutable_file_info();
        out_info->CopyFrom(info);
        //maybe haven't been written info meta
        if (!out_info->size()) {
            int64_t file_size = 0;
            for (int i = 0; i < out_info->blocks_size(); i++) {
                int64_t block_id = out_info->blocks(i);
                NSBlock nsblock;
                if (!block_mapping_manager_->GetBlock(block_id, &nsblock)) {
                    continue;
                }
                file_size += nsblock.block_size;
            }
            out_info->set_size(file_size);
        }
        response->set_status(kOK);
        LOG(INFO, "Stat: %s return: %ld", path.c_str(), out_info->size());
    } else {
        LOG(INFO, "Stat: %s return: not found", path.c_str());
        response->set_status(kNsNotFound);
    }
    done->Run();
}

void NameServerImpl::Rename(::google::protobuf::RpcController* controller,
                            const RenameRequest* request,
                            RenameResponse* response,
                            ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    std::string oldpath = NameSpace::NormalizePath(request->oldpath());
    std::string newpath = NameSpace::NormalizePath(request->newpath());

    bool need_unlink;
    FileInfo remove_file;
    NameServerLog log;
    StatusCode status = namespace_->Rename(oldpath, newpath, &need_unlink, &remove_file, &log);
    response->set_status(status);
    if (status != kOK) {
        done->Run();
        return;
    }
    std::vector<FileInfo>* removed = NULL;
    if (need_unlink) {
        removed = new std::vector<FileInfo>;
        removed->push_back(remove_file);
    }
    LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                               controller, request, response, done, removed, _1));
}

void NameServerImpl::Unlink(::google::protobuf::RpcController* controller,
                            const UnlinkRequest* request,
                            UnlinkResponse* response,
                            ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    g_unlink.Inc();
    response->set_sequence_id(request->sequence_id());
    std::string path = NameSpace::NormalizePath(request->path());

    FileInfo file_info;
    NameServerLog log;
    StatusCode status = namespace_->RemoveFile(path, &file_info, &log);
    LOG(INFO, "Unlink: %s return %s", path.c_str(), StatusCode_Name(status).c_str());
    response->set_status(status);
    if (status != kOK) {
        done->Run();
        return;
    }
    std::vector<FileInfo>* removed = new std::vector<FileInfo>;
    removed->push_back(file_info);
    LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                               controller, request, response, done,
                               removed, _1));
}

void NameServerImpl::DeleteDirectory(::google::protobuf::RpcController* controller,
                                     const DeleteDirectoryRequest* request,
                                     DeleteDirectoryResponse* response,
                                     ::google::protobuf::Closure* done)  {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    std::string path = NameSpace::NormalizePath(request->path());
    bool recursive = request->recursive();
    if (path.empty() || path[0] != '/') {
        response->set_status(kBadParameter);
        done->Run();
    }
    std::vector<FileInfo>* removed = new std::vector<FileInfo>;
    NameServerLog log;
    StatusCode ret_status = namespace_->DeleteDirectory(path, recursive, removed, &log);
    response->set_status(ret_status);
    if (ret_status != kOK) {
        done->Run();
        return;
    }
    LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                               controller, request, response, done, removed, _1));
}

void NameServerImpl::ChangeReplicaNum(::google::protobuf::RpcController* controller,
                                      const ChangeReplicaNumRequest* request,
                                      ChangeReplicaNumResponse* response,
                                      ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    response->set_sequence_id(request->sequence_id());
    std::string file_name = NameSpace::NormalizePath(request->file_name());
    int32_t replica_num = request->replica_num();
    StatusCode ret_status = kOK;
    FileInfo file_info;
    if (namespace_->GetFileInfo(file_name, &file_info)) {
        file_info.set_replicas(replica_num);
        NameServerLog log;
        bool ret = namespace_->UpdateFileInfo(file_info, &log);
        assert(ret);
        for (int i = 0; i < file_info.blocks_size(); i++) {
            if (block_mapping_manager_->ChangeReplicaNum(file_info.blocks(i), replica_num)) {
                LOG(INFO, "Change %s replica num to %d", file_name.c_str(), replica_num);
            } else {
                ///TODO: need to undo when file have multiple blocks?
                LOG(WARNING, "Change %s replica num to %d fail", file_name.c_str(), replica_num);
                ret_status = kNotOK;
                break;
            }
        }
        response->set_status(kOK);
        LogRemote(log, boost::bind(&NameServerImpl::SyncLogCallback, this,
                                   controller, request, response, done,
                                   (std::vector<FileInfo>*)NULL, _1));
        return;
    } else {
        LOG(INFO, "Change replica num not found: %s", file_name.c_str());
        ret_status = kNsNotFound;
    }
    response->set_status(ret_status);
    done->Run();
}

void NameServerImpl::RebuildBlockMapCallback(const FileInfo& file_info) {
    for (int i = 0; i < file_info.blocks_size(); i++) {
        int64_t block_id = file_info.blocks(i);
        int64_t version = file_info.version();
        block_mapping_manager_->AddNewBlock(block_id, file_info.replicas(),
                                    version, file_info.size(), NULL);
    }
}

void NameServerImpl::SysStat(::google::protobuf::RpcController* controller,
                             const SysStatRequest* request,
                             SysStatResponse* response,
                             ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    sofa::pbrpc::RpcController* ctl = reinterpret_cast<sofa::pbrpc::RpcController*>(controller);
    LOG(INFO, "SysStat from %s", ctl->RemoteAddress().c_str());
    chunkserver_manager_->ListChunkServers(response->mutable_chunkservers());
    response->set_status(kOK);
    done->Run();
}

void NameServerImpl::ShutdownChunkServer(::google::protobuf::RpcController* controller,
        const ShutdownChunkServerRequest* request,
        ShutdownChunkServerResponse* response,
        ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    StatusCode status = chunkserver_manager_->ShutdownChunkServer(request->chunkserver_address());
    response->set_status(status);
    done->Run();
}

void NameServerImpl::ShutdownChunkServerStat(::google::protobuf::RpcController* controller,
        const ShutdownChunkServerStatRequest* request,
        ShutdownChunkServerStatResponse* response,
        ::google::protobuf::Closure* done) {
    if (!is_leader_) {
        response->set_status(kIsFollower);
        done->Run();
        return;
    }
    bool in_progress = chunkserver_manager_->GetShutdownChunkServerStat();
    response->set_status(kOK);
    response->set_in_offline_progress(in_progress);
    done->Run();
}

void NameServerImpl::TransToString(const std::map<int32_t, std::set<int64_t> >& chk_set,
                                    std::string* output) {
    for (std::map<int32_t, std::set<int64_t> >::const_iterator it =
            chk_set.begin(); it != chk_set.end(); ++it) {
        output->append(common::NumToString(it->first) + ": ");
        const std::set<int64_t>& block_set = it->second;
        std::string cur_cs_str;
        TransToString(block_set, &cur_cs_str);
        output->append(cur_cs_str);
        output->append("<br>");
    }
}

void NameServerImpl::TransToString(const std::set<int64_t>& block_set, std::string* output) {
    for (std::set<int64_t>::const_iterator it = block_set.begin();
            it != block_set.end(); ++it) {
        output->append(common::NumToString(*it) + " ");
        if (output->size() > 1024) {
            output->append("...");
            break;
        }
    }
}

void NameServerImpl::ListRecover(sofa::pbrpc::HTTPResponse* response) {
    RecoverBlockSet recover_blocks;
    block_mapping_manager_->ListRecover(&recover_blocks);
    std::string hi_recover, lo_recover, lost, hi_check, lo_check, incomplete;
    TransToString(recover_blocks.hi_recover, &hi_recover);
    TransToString(recover_blocks.lo_recover, &lo_recover);
    TransToString(recover_blocks.lost, &lost);
    TransToString(recover_blocks.hi_check, &hi_check);
    TransToString(recover_blocks.lo_check, &lo_check);
    TransToString(recover_blocks.incomplete, &incomplete);
    std::string str =
            "<html><head><title>Recover Details</title>\n"
            "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
            "<script src=\"http://libs.baidu.com/jquery/1.8.3/jquery.min.js\"></script>\n"
            "<link href=\"http://apps.bdimg.com/libs/bootstrap/3.2.0/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
            "</head>\n";
    str += "<body><div class=\"col-sm-12  col-md-12\">";
    str += "<h1>分布式文件系统控制台 - RecoverDetails</h1>";

    str += "<table class=\"table\">";
    str += "<tr><td>lost</td></tr>";
    str += "<tr><td>" + lost + "</td></tr>";
    str += "<tr><td>incomplete</td></tr>";
    str += "<tr><td>" + incomplete + "</td></tr>";
    str += "<tr><td>hi_check</td></tr>";
    str += "<tr><td>" + hi_check + "</td></tr>";
    str += "<tr><td>lo_check</td></tr>";
    str += "<tr><td>" + lo_check + "</td></tr>";
    str += "<tr><td>hi_recover</td></tr>";
    str += "<tr><td>" + hi_recover + "</td></tr>";
    str += "<tr><td>lo_recover</td></tr>";
    str += "<tr><td>" + lo_recover + "</td></tr></table>";

    str += "</div></body><html>";
    response->content->Append(str);
    return;
}

bool NameServerImpl::WebService(const sofa::pbrpc::HTTPRequest& request,
                                sofa::pbrpc::HTTPResponse& response) {
    const std::string& path = request.path;
    if (path == "/dfs/switchtoleader") {
        if (sync_) {
            sync_->SwitchToLeader();
        }
        return true;
    } else if (path == "/dfs/details") {
        ListRecover(&response);
        return true;
    } else if (path == "/dfs/leave_safemode") {
        LeaveSafemode();
        response.content->Append("<body onload=\"history.back()\"></body>");
        return true;
    } else if (path == "/dfs/kick" && FLAGS_bfs_web_kick_enable) {
        std::map<std::string, std::string>::const_iterator it =
            request.query_params->find("cs");
        if (it == request.query_params->end()) {
            return false;
        }
        std::stringstream ss(it->second);
        int cs_id;
        if (ss >> cs_id && chunkserver_manager_->KickChunkServer(cs_id)) {
            response.content->Append("<body onload=\"history.back()\"></body>");
            return true;
        }
        return false;
    }

    ::google::protobuf::RepeatedPtrField<ChunkServerInfo>* chunkservers
        = new ::google::protobuf::RepeatedPtrField<ChunkServerInfo>;
    chunkserver_manager_->ListChunkServers(chunkservers);

    std::string table_str;
    std::string str =
            "<html><head><title>BFS console</title>\n"
            "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
            //"<link rel=\"stylesheet\" type=\"text/css\" "
            //    "href=\"http://www.w3school.com.cn/c5.css\"/>\n"
            //"<style> body { background: #f9f9f9;}"
            //"a:link,a:visited{color:#4078c0;} a:link{text-decoration:none;}"
            //"</style>\n"
            "<script src=\"http://libs.baidu.com/jquery/1.8.3/jquery.min.js\"></script>\n"
            "<link href=\"http://apps.bdimg.com/libs/bootstrap/3.2.0/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
            "</head>\n";
    str += "<body><div class=\"col-sm-12  col-md-12\">";

    table_str +=
        "<table class=\"table\">"
        "<tr><td>id</td><td>address</td><td>blocks</td><td>Data size</td>"
        "<td>Disk quota</td><td>Disk used</td><td>Writing buffers</td>"
        "<td>status</td><td>last_check</td><tr>";
    int dead_num = 0;
    int64_t total_quota = 0;
    int64_t total_data = 0;
    int overladen_num = 0;
    for (int i = 0; i < chunkservers->size(); i++) {
        const ChunkServerInfo& chunkserver = chunkservers->Get(i);
        if (chunkservers->Get(i).is_dead()) {
            dead_num++;
        } else {
            total_quota += chunkserver.disk_quota();
            total_data += chunkserver.data_size();
            if (chunkserver.buffers() > FLAGS_chunkserver_max_pending_buffers * 0.8) {
                overladen_num++;
            }
        }

        table_str += "</td><td>";
        table_str += common::NumToString(chunkserver.id());
        table_str += "</td><td>";
        table_str += "<a href=\"http://" + chunkserver.address() + "/dfs\">"
               + chunkserver.address() + "</a>";
        table_str += "</td><td>";
        table_str += common::NumToString(chunkserver.block_num());
        table_str += "</td><td>";
        table_str += common::HumanReadableString(chunkserver.data_size()) + "B";
        table_str += "</td><td>";
        table_str += common::HumanReadableString(chunkserver.disk_quota()) + "B";
        std::string ratio = common::NumToString(
            chunkserver.data_size() * 100 / chunkserver.disk_quota());
        std::string bg_color = chunkserver.is_dead() ? "background-color:#CCC;" : "";
        table_str += "</td><td><div class=\"progress\" style=\"margin-bottom:0\">"
               "<div class=\"progress-bar\" "
                    "role=\"progressbar\" aria-valuenow=\"60\" aria-valuemin=\"0\" "
                    "aria-valuemax=\"100\" "
                    "style=\"width: "+ ratio + "%; color:#000;" + bg_color + "\">" + ratio + "%"
               "</div></div>";
        table_str += "</td><td>";
        table_str += common::NumToString(chunkserver.pending_writes()) + "/" +
                     common::NumToString(chunkserver.buffers());
        table_str += "</td><td>";
        if (chunkserver.is_dead()) {
            table_str += "dead";
        } else if (chunkserver.kick()) {
            table_str += "kicked";
        } else if (chunkserver.status() == kCsReadonly) {
            table_str += "Readonly";
        } else if (FLAGS_bfs_web_kick_enable) {
            table_str += "alive (<a href=\"/dfs/kick?cs=" + common::NumToString(chunkserver.id())
                      + "\">kick</a>)";
        } else {
            table_str += "alive";
        }
        table_str += "</td><td>";
        table_str += common::NumToString(
                        common::timer::now_time() - chunkserver.last_heartbeat());
        table_str += "</td></tr>";
    }
    table_str += "</table>";

    RecoverBlockNum recover_num;
    block_mapping_manager_->GetStat(-1, &recover_num);
    int32_t w_qps, r_qps;
    int64_t w_speed, r_speed, recover_speed;
    chunkserver_manager_->GetStat(&w_qps, &w_speed, &r_qps, &r_speed, &recover_speed);
    str += "<h1 style=\"margin-top: 10px; margin-bottom: 0px;\">分布式文件系统控制台 - NameServer</h1>";

    str += "<div class=\"row\">";
    str += "<div class=\"col-sm-6 col-md-6\">";
    str += "<h4 align=left>Nameserver status</h4>";

    str += "<div class=\"col-sm-6 col-md-6\">";
    str += "Total: " + common::HumanReadableString(total_quota) + "B</br>";
    str += "Used: " + common::HumanReadableString(total_data) + "B</br>";
    str += "Safemode: " + common::NumToString(safe_mode_);
    if (safe_mode_) {
        str += " <a href=\"/dfs/leave_safemode\">leave</a>";
    }
    str += "</br>";
    str += "Pending tasks: "
        + common::NumToString(work_thread_pool_->PendingNum()) + " "
        + common::NumToString(report_thread_pool_->PendingNum()) + "</br>";
    str += "<a href=\"/service?name=baidu.bfs.NameServer\">Rpc status</a>";
    str += "</div>"; // <div class="col-sm-6 col-md-6">

    str += "<div class=\"col-sm-6 col-md-6\">";
    str += "Recover(hi/lo): " + common::NumToString(recover_num.hi_recover_num) + "/" + common::NumToString(recover_num.lo_recover_num) + "</br>";
    str += "Pending: " + common::NumToString(recover_num.hi_pending) + "/" + common::NumToString(recover_num.lo_pending) + "</br>";
    str += "Lost: " + common::NumToString(recover_num.lost_num) + "</br>";
    str += "Incomplete: " + common::NumToString(recover_num.incomplete_num) + "</br>";
    str += "<a href=\"/dfs/details\">Details</a>";
    str += "</div>"; // <div class="col-sm-6 col-md-6">
    str += "</div>"; // <div class="col-sm-6 col-md-6">

    str += "<div class=\"col-sm-6 col-md-6\">";
    str += "<h4 align=left>ChunkServer status</h4>";
    str += "<div class=\"col-sm-6 col-md-6\">";
    str += "Total: " + common::NumToString(chunkservers->size())+"</br>";
    str += "Alive: " + common::NumToString(chunkservers->size() - dead_num)+"</br>";
    str += "Dead: " + common::NumToString(dead_num)+"</br>";
    str += "Overload: " + common::NumToString(overladen_num)+"</p>";
    str += "</div>"; // <div class="col-sm-6 col-md-6">

    str += "<div class=\"col-sm-6 col-md-6\">";
    str += "w_qps: " + common::NumToString(w_qps)+"</br>";
    str += "w_speed: " + common::HumanReadableString(w_speed)+"</br>";
    str += "r_qps: " + common::NumToString(r_qps)+"</br>";
    str += "r_speed: " + common::HumanReadableString(r_speed)+"</br>";
    str += "recover_speed: " + common::HumanReadableString(recover_speed)+"</p>";
    str += "</div>"; // <div class="col-sm-6 col-md-6">
    str += "</div>"; // <div class="col-sm-6 col-md-6">
    str += "</div>"; // <div class="row">

    str += "<script> var int = setInterval('window.location.reload()', 1000);"
           "function check(box) {"
           "if(box.checked) {"
           "    int = setInterval('window.location.reload()', 1000);"
           "} else {"
           "    clearInterval(int);"
           "}"
           "}</script>"
           "<input onclick=\"javascript:check(this)\" "
           "checked=\"checked\" type=\"checkbox\">自动刷新</input>";
    str += table_str;
    str += "</body></html>";
    delete chunkservers;
    response.content->Append(str);
    return true;
}

} // namespace bfs
} // namespace baidu

/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */

ext: cc


text:
/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 * @file  Moses3.cpp
 * @brief 3D Pose
 */

//#include <gtsam/3rdparty/Sophus/sophus/sim3.cpp>
//#include <gtsam/3rdparty/Sophus/sophus/so3.cpp>
//#include <gtsam/3rdparty/Sophus/sophus/scso3.cpp>

#include <gtsam/geometry/Moses3.h>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/geometry/concepts.h>
#include <gtsam/base/Lie-inl.h>
#include <boost/foreach.hpp>
#include <iostream>
#include <cmath>

using namespace std;
using namespace Sophus;

namespace gtsam {

/** Explicit instantiation of base class to export members */
INSTANTIATE_LIE(Moses3);

/** instantiate concept checks */
GTSAM_CONCEPT_POSE_INST(Moses3);

static const Matrix3 I3 = eye(3), Z3 = zeros(3, 3), _I3 = -I3;
static const Matrix6 I6 = eye(6);
static const Matrix7 I7 = eye(7);

/* ************************************************************************* */
Moses3::Moses3(const Pose2& pose2) :
    Sim3(ScSO3(Rot3::rodriguez(0, 0, pose2.theta()).matrix()), (
        Point3(pose2.x(), pose2.y(), 0)).vector()) {
}

/* ************************************************************************* */
// Calculate Adjoint map
// Ad_pose is 6*6 matrix that when applied to twist xi, returns Ad_pose(xi)
// Experimental - unit tests of derivatives based on it do not check out yet
Matrix7 Moses3::AdjointMap() const {
/*
  const Matrix3 R = R_.matrix();
  const Vector3 t = t_.vector();
  Matrix3 A = skewSymmetric(t) * R;
  Matrix6 adj;
  adj << R, Z3, A, R;
  return adj;
*/
  return this->Adj();
}

/* ************************************************************************* */
// Calculate Adjoint map
// From Sim3
Matrix7 Moses3::AdjointMap7() const {
  return this->Adj();
}

/* ************************************************************************* */
//Remove
Matrix7 Moses3::adjointMap(const Vector& xi) {
  /*
  Matrix3 w_hat = skewSymmetric(xi(0), xi(1), xi(2));
  Matrix3 v_hat = skewSymmetric(xi(3), xi(4), xi(5));
  Matrix6 adj;
  adj << w_hat, Z3, v_hat, w_hat;*/

  return adjointMap7(xi);
}

/* ************************************************************************* */
Matrix7 Moses3::adjointMap7(const Vector& xi) {
  /*
  Matrix3 w_hat = skewSymmetric(xi(0), xi(1), xi(2));
  Matrix3 v_hat = skewSymmetric(xi(3), xi(4), xi(5));
  Matrix6 adj;
  adj << w_hat, Z3, v_hat, w_hat;*/

  return d_lieBracketab_by_d_a(xi);
}

/* ************************************************************************* */
Vector Moses3::adjoint(const Vector& xi, const Vector& y,
    boost::optional<Matrix&> H) {
  if (H) {
    *H = zeros(7, 7);
    for (int i = 0; i < 7; ++i) {
      Vector dxi = zero(7);
      dxi(i) = 1.0;
      Matrix Gi = adjointMap7(dxi);
      (*H).col(i) = Gi * y;
    }
  }
  return adjointMap7(xi) * y; // Should it be = Sim3::lieBracket(xi, y) or Sim3::lieBracket(y, xi). [a,b] = d/dt(adj(a)*b).
}

/* ************************************************************************* */
Vector Moses3::adjointTranspose(const Vector& xi, const Vector& y,
    boost::optional<Matrix&> H) {
  if (H) {
    *H = zeros(7, 7);
    for (int i = 0; i < 7; ++i) {
      Vector dxi = zero(7);
      dxi(i) = 1.0;
      Matrix GTi = adjointMap7(dxi).transpose();
      (*H).col(i) = GTi * y;
    }
  }
  return adjointMap7(xi).transpose() * y;
}

/* *************************************************************************
Matrix6 Moses3::dExpInv_exp(const Vector& xi) {
  // Bernoulli numbers, from Wikipedia
  static const Vector B = (Vector(9) << 1.0, -1.0 / 2.0, 1. / 6., 0.0, -1.0 / 30.0,
      0.0, 1.0 / 42.0, 0.0, -1.0 / 30);
  static const int N = 5; // order of approximation
  Matrix res = I6;
  Matrix6 ad_i = I6;
  Matrix6 ad_xi = adjointMap(xi);
  double fac = 1.0;
  for (int i = 1; i < N; ++i) {
    ad_i = ad_xi * ad_i;
    fac = fac * i;
    res = res + B(i) / fac * ad_i;
  }
  return res;
}*/

/* ************************************************************************* */
void Moses3::print(const string& s) const {
  cout << s;
  cout << *this; //DOES *this WORK here ??? Defined in Sim3
  //R_.print("R:\n");
  //t_.print("t: ");
}

/* ************************************************************************* */
bool Moses3::equals(const Moses3& pose, double tol) const {
  return (Rot3(this->rotation_matrix())).equals(Rot3(pose.rotation_matrix()), tol) && (Point3(this->Sim3::translation())).equals(Point3(pose.Sim3::translation()), tol) && (fabs(this->scale() - pose.scale()) < tol) ;
}

/* ************************************************************************* */
/** Test test */
Moses3 Moses3::Expmap(const Vector& xi) {
	return Moses3(Sim3::exp(xi));
}

/* **************REMOVE*********************************************************** */
Vector7 Moses3::Logmap(const Moses3& p) {
	Vector7 vec;
	return  Sim3::log(Sim3(p.scso3(), p.Sim3::translation()));
}

/* btak remove************************************************************************* */
Vector7  Moses3::Logmap7(const Moses3& p) {
	Vector7 vec;
	return  Sim3::log(Sim3(p.scso3(), p.Sim3::translation()));
}

/* *************************************************************************
Moses3 Moses3::retractFirstOrder(const Vector& xi) const {
  Vector3 omega(sub(xi, 0, 3));
  Point3 v(sub(xi, 3, 6));
  Rot3 R = R_.retract(omega); // R is done exactly
  Point3 t = t_ + R_ * v; // First order t approximation
  return Moses3(R, t);
}*/

/* ************************************************************************* */
// Different versions of retract
Moses3 Moses3::retract(const Vector& xi, Moses3::CoordinatesMode mode) const {
  if (mode == Moses3::EXPMAP) {
    // Lie group exponential map, traces out geodesic
    return compose(Expmap(xi));

  } /*else if (mode == Moses3::FIRST_ORDER) {
    // First order
    return retractFirstOrder(xi);

  }*/
  else {
    // Point3 t = t_.retract(v.vector()); // Incorrect version retracts t independently
    // Point3 t = t_ + R_ * (v+Point3(omega).cross(v)/2); // Second order t approximation
    assert(false);
    exit(1);
  }
}

/* ************************************************************************* */
// different versions of localCoordinates
Vector7 Moses3::localCoordinates(const Moses3& T,
    Moses3::CoordinatesMode mode) const {
  if (mode == Moses3::EXPMAP) {
    // Lie group logarithm map, exact inverse of exponential map
    return Logmap7(between(T));
  } else {
    assert(false);
    exit(1);
  }
}

/* ************************************************************************* */
Matrix4 Moses3::matrix() const {
  return this->Sim3::matrix();
}

/* ************************************************************************* */
// Check scale man
Moses3 Moses3::transform_to(const Moses3& pose) const {
  //Rot3 cRv = R_ * Rot3(pose.R_.inverse());// Scale should not be lost here!! Using scso3.

	const Point3 Tt(this->Sim3::translation());

  ScSO3 cRv = (scso3_) * pose.scso3().inverse();// Scale should not be lost here!! Using scso3. So this has scale included.
  Point3 t = pose.transform_to(Tt); // But this has scale included too. WTF.
  return Moses3(cRv, t);
}

/* ************************************************************************* */
Point3 Moses3::transform_from(const Point3& p, boost::optional<Matrix&> Dpose,
    boost::optional<Matrix&> Dpoint) const {

	const Rot3 Rr(this->Sim3::rotation_matrix());
	const double Ss(this->Sim3::scale());
	const Point3 Tt(this->Sim3::translation());

  if (Dpose) {
    const Matrix R = Rr.matrix();
    Matrix DR = R * skewSymmetric(-p.x(), -p.y(), -p.z()); //Should it have scso3 or just so3 ? It should have scso3 coz y = sRx + st. dy/dx = sR
    Dpose->resize(3, 7);
    Vector3 scaleCol=(Ss*(R*p.vector()));//TODO This needs checking. Formula obtained by matching with numerical derivative values. Make sure it theritically makes sense.
    //(scaleCol) << 0.2, 1.25977596, -1.70380883;//Bullshit hack REMOVE
    (*Dpose) <<  Ss*R, (Ss)*DR, scaleCol; //TODO This needs checking. Mostly incorrect. //should the scale component of jacobi be zero ? or 0 0 1 ?
  }
  if (Dpoint)
    *Dpoint = Ss*Rr.matrix(); //TODO This needs checking. Mostly incorrect.
  return Point3(static_cast<const Sim3&>(*this)*p.vector()); //Using Sim3 group action. //TODO This needs checking. Mostly correct.
}

/* ************************************************************************* */
// check scale man
// unrotate(p - t_)*s_ : P_world of point. t_ translation of new frame wrt world cordi in world coordi. Multiply (or divide?) with scale (axis strech).
Point3 Moses3::transform_to(const Point3& p, boost::optional<Matrix&> Dpose,
    boost::optional<Matrix&> Dpoint) const {

	const Rot3 Rr(this->Sim3::rotation_matrix());
	const double Ss(this->Sim3::scale());
	const Point3 Tt(this->Sim3::translation());

  const Point3 result = Rr.unrotate((p - Tt)/Ss); // should it be (p - 1/s_*t_) or 1/s_*(p - t_). Multiply or divide by scale. ?
  												// We are multiplying in transform_from (t_ + s*Rot(P)), so here we divide. //TODO This needs checking. Mostly correct.
  if (Dpose) {
    const Point3& q = result;
    Matrix DR = skewSymmetric(q.x(), q.y(), q.z());
    Dpose->resize(3, 7);
    Vector3 scaleCol=(-1)*q.vector();//TODO This needs checking. Mostly incorrect.
    (*Dpose) << _I3, DR, scaleCol; //TODO This needs checking. Mostly incorrect.
  }
  if (Dpoint)
    *Dpoint = (1/Ss)*Rr.transpose();  //TODO This needs checking. Mostly incorrect.
  return result;
}

/* ************************************************************************* */
Moses3 Moses3::compose(const Moses3& p2, boost::optional<Matrix&> H1,
    boost::optional<Matrix&> H2) const {
  if (H1)
    *H1 = p2.inverse().AdjointMap();
  if (H2)
    *H2 = I7;
  return (*this) * p2;
}

/* *************************************************************************
 // TODO: Test
  //	   Implement Derivatives
  */
Moses3 Moses3::inverse(boost::optional<Matrix&> H1) const {
  if (H1)
    *H1 = -AdjointMap(); // TODO Replace with correct function here
  Rot3 Rt = R_.inverse();
  Moses3 inv(this->Sim3::inverse());
  return inv;
}

/* ************************************************************************* */
// between = compose(p2,inverse(p1));
Moses3 Moses3::between(const Moses3& p2, boost::optional<Matrix&> H1,
    boost::optional<Matrix&> H2) const {
  Moses3 result = inverse() * p2;
  if (H1)
    *H1 = -result.inverse().AdjointMap();
  if (H2)
    *H2 = I7;
  return result;
}

/* ************************************************************************* */
double Moses3::range(const Point3& point, boost::optional<Matrix&> H1,
    boost::optional<Matrix&> H2) const {
  if (!H1 && !H2)
    return transform_to(point).norm();
  Point3 d = transform_to(point, H1, H2);
  double x = d.x(), y = d.y(), z = d.z(), d2 = x * x + y * y + z * z, n = sqrt(
      d2);
  Matrix D_result_d = (Matrix(1, 3) << x / n, y / n, z / n);
  if (H1)
    *H1 = D_result_d * (*H1);
  if (H2)
    *H2 = D_result_d * (*H2);
  return n;
}

/* ************************************************************************* */
// This needs proper testing
double Moses3::range(const Moses3& point, boost::optional<Matrix&> H1,
    boost::optional<Matrix&> H2) const {
  double r = range(Point3(point.Sim3::translation()), H1, H2); //IMPORTANT!!! Should it be just translation or scaled translation ?? Scaled translation spoils derivatives.
  if (H2) {
    Matrix H2_ = *H2 * point.scso3().matrix(); //Checked via numerical derivatives
    *H2 = zeros(1, 7);
    insertSub(*H2, H2_, 0, 0);
  }
  return r;
}

/* ************************************************************************* */

/*
boost::optional<Moses3> align(const vector<Point3Pair>& pairs) {
  const size_t n = pairs.size();
  if (n < 3)
    return boost::none; // we need at least three pairs

  // calculate centroids
  Vector cp = zero(3), cq = zero(3);
  BOOST_FOREACH(const Point3Pair& pair, pairs){
  cp += pair.first.vector();
  cq += pair.second.vector();
}
  double f = 1.0 / n;
  cp *= f;
  cq *= f;

  // Add to form H matrix
  Matrix H = zeros(3, 3);
  BOOST_FOREACH(const Point3Pair& pair, pairs){
  Vector dp = pair.first.vector() - cp;
  Vector dq = pair.second.vector() - cq;
  H += dp * dq.transpose();
}

// Compute SVD
  Matrix U, V;
  Vector S;
  svd(H, U, S, V);

  // Recover transform with correction from Eggert97machinevisionandapplications
  Matrix UVtranspose = U * V.transpose();
  Matrix detWeighting = eye(3, 3);
  detWeighting(2, 2) = UVtranspose.determinant();
  Rot3 R(Matrix(V * detWeighting * U.transpose()));
  Point3 t = Point3(cq) - R * Point3(cp);
  return Moses3(R, t);
}

*/
/* ************************************************************************* */
std::ostream &operator<<(std::ostream &os, const Moses3& pose) {
  os << pose.Sim3::scso3() << "\n" << Point3(pose.Sim3::translation()) << endl;
  return os;
}

} // namespace gtsam

ext: cpp


text:
// AirMap Platform SDK
// Copyright © 2018 AirMap, Inc. All rights reserved.
//
// 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.
#define BOOST_TEST_MODULE daemon

#include <airmap/boost/context.h>
#include <airmap/date_time.h>
#include <airmap/mavlink/boost/tcp_channel.h>
#include <airmap/mavlink/boost/tcp_route.h>
#include <airmap/mavlink/router.h>
#include <airmap/monitor/daemon.h>

#include <mock/client.h>

#include <boost/test/included/unit_test.hpp>

#include <thread>

namespace ip = boost::asio::ip;
using tcp    = boost::asio::ip::tcp;

BOOST_AUTO_TEST_CASE(daemon_creates_flights_for_state_change_to_active) {
  using mock::_;

  static constexpr const std::uint8_t system_id    = 42;
  static constexpr const std::uint8_t component_id = 84;
  static constexpr const std::uint32_t custom_mode = 0;
  static const std::string api_key                 = "api_key";
  static const std::string user_id                 = "id";
  static const std::string aircraft_id             = "aircraft_id";
  static const std::string flight_id               = "flight_id";
  static const std::string key                     = "key";

  auto position_message = []() -> mavlink_message_t {
    mavlink_message_t msg;
    mavlink_msg_global_position_int_pack(system_id, component_id, &msg,
                                         airmap::microseconds_since_epoch(airmap::Clock::universal_time()), 35 * 1E7,
                                         -78 * 1E7, 106 * 1E3, 220 * 1E3, 3 * 1E2, 4 * 1E2, 5 * 1E2, 180 * 1E2);
    return msg;
  };

  std::vector<std::function<mavlink_message_t()>> recording = {
      position_message,
      []() -> mavlink_message_t {
        mavlink_message_t msg;
        mavlink_msg_heartbeat_pack(system_id, component_id, &msg, MAV_TYPE_HELICOPTER, MAV_AUTOPILOT_GENERIC,
                                   MAV_MODE_GUIDED_ARMED, custom_mode, MAV_STATE_ACTIVE);
        return msg;
      },
      position_message};

  constexpr const std::uint16_t port = 9090;
  auto logger                        = airmap::create_default_logger(std::cout);
  auto context                       = airmap::boost::Context::create(logger);
  auto tcp_route =
      airmap::mavlink::boost::TcpRoute::create(context->io_service(), tcp::endpoint{tcp::v4(), port}, logger, {});

  airmap::mavlink::Router router{{tcp_route}};
  router.start();

  auto search_flight_response = airmap::Flights::Search::Response{};
  auto search_flights_result  = airmap::Flights::Search::Result{search_flight_response};

  auto token                 = airmap::Token::Anonymous{user_id};
  auto authentication_result = airmap::Authenticator::AuthenticateAnonymously::Result{token};

  auto flight               = airmap::Flight{};
  flight.id                 = flight_id;
  auto create_flight_result = airmap::Flights::CreateFlight::Result{flight};

  auto response                  = airmap::Flights::StartFlightCommunications::Response{};
  response.key                   = key;
  auto start_flight_comms_result = airmap::Flights::StartFlightCommunications::Result{response};

  mock::Authenticator authenticator;
  REQUIRE_CALL(authenticator, authenticate_anonymously(_, _)).SIDE_EFFECT(_2(authentication_result));

  mock::Flights flights;
  REQUIRE_CALL(flights, search(_, _)).SIDE_EFFECT(_2(search_flights_result));
  REQUIRE_CALL(flights, create_flight_by_point(_, _)).SIDE_EFFECT(_2(create_flight_result));
  REQUIRE_CALL(flights, start_flight_communications(_, _)).SIDE_EFFECT(_2(start_flight_comms_result));

  mock::Telemetry telemetry;
  REQUIRE_CALL(telemetry, submit_updates(_, _, _)).LR_SIDE_EFFECT(context->stop(airmap::Context::ReturnCode::success));

  mock::Traffic traffic;
  auto traffic_monitor = std::make_shared<mock::Traffic::Monitor>();
  REQUIRE_CALL(*traffic_monitor, subscribe(_));
  auto monitor_result = airmap::Traffic::Monitor::Result{traffic_monitor};
  REQUIRE_CALL(traffic, monitor(_, _)).SIDE_EFFECT(_2(monitor_result));

  auto client = std::make_shared<mock::Client>();

  REQUIRE_CALL(*client, authenticator()).LR_RETURN(std::ref(authenticator));
  REQUIRE_CALL(*client, flights()).LR_RETURN(std::ref(flights)).TIMES(AT_LEAST(1));
  REQUIRE_CALL(*client, telemetry()).LR_RETURN(std::ref(telemetry));
  REQUIRE_CALL(*client, traffic()).LR_RETURN(std::ref(traffic));

  auto channel = std::make_shared<airmap::mavlink::boost::TcpChannel>(logger, context->io_service(),
                                                                      ip::address::from_string("127.0.0.1"), port);

  airmap::Credentials::Anonymous anon;
  anon.id = user_id;

  airmap::Credentials credentials;
  credentials.api_key   = api_key;
  credentials.anonymous = anon;

  airmap::monitor::Daemon::Configuration config{credentials, aircraft_id, logger,        channel,
                                                context,     client,      "0.0.0.0:9292"};

  auto daemon = airmap::monitor::Daemon::create(config);
  daemon->start();

  std::thread worker{[context]() { context->run(); }};

  std::this_thread::sleep_for(std::chrono::milliseconds{500});

  for (const auto& step : recording) {
    router.route(step());
  }

  if (worker.joinable())
    worker.join();
}

max_stars_repo_name: YUNEEC/platform-sdk

max_issues_repo_name: YUNEEC/platform-sdk

max_forks_repo_name: YUNEEC/platform-sdk


text:
//==================================================================================================
/*
  Copyright 2012 - 2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================

#include <boost/simd/pack.hpp>
#include <boost/simd/io.hpp>
#include <boost/simd/functions/splat.hpp>
#include <boost/simd/functions/plus.hpp>
#include <boost/simd/functions/multiplies.hpp>
#include <iostream>

//! [scalar-dot]
template<typename Value> Value dot(Value* first1, Value* last1, Value* first2)
{
  Value v{};

  while(first1 != last1)
  {
    v += *first1 *  *first2;
    first1++;
    first2++;
  }

  return v;
}
//! [scalar-dot]

int main()
{
}


text:
#include "instruction.hpp"

#include <unordered_map>
#include <typeinfo>
#include <typeindex>
#include <boost/function.hpp>
#include <boost/format.hpp>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Argument.h>
#include <llvm/IR/Constants.h>

#include <libraries/core/std_library.hpp>
#include <util/llvm_type.hpp>
#include <util/misc.hpp>
#include <frontends/llvm/translate.hpp>

using namespace std;

namespace llpm {

void LLVMInstruction::createName() {
    this->name(NameInstruction(_ins));
}

std::string LLVMInstruction::NameInstruction(llvm::Value* val) {
    if (val->hasName()) {
        return val->getName();
    }

    llvm::Instruction* ins = llvm::dyn_cast_or_null<llvm::Instruction>(val);
    if (ins == NULL)
        return "";
    unsigned idx = 0;
    auto bb = ins->getParent();
    for (auto iter = bb->begin();
         iter != bb->end() && (llvm::Instruction*)iter != ins;
         iter++) {
        idx ++;
    }
    return str(boost::format("%1%i%2%")
                    % bb->getName().str()
                    % idx);
}

unsigned LLVMInstruction::GetNumHWOperands(llvm::Instruction* ins) {
    switch (ins->getOpcode()) {
    case llvm::Instruction::PHI:
        return 1;
    case llvm::Instruction::Br:
        if (llvm::dyn_cast<llvm::BranchInst>(ins)->isUnconditional())
            return 0;
        else
            return 1;
    case llvm::Instruction::Call:
        return llvm::dyn_cast<llvm::CallInst>(ins)->getNumArgOperands();
    default:
        return ins->getNumOperands();
    }
}

bool LLVMInstruction::HWIgnoresOperand(llvm::Instruction* ins, unsigned idx) {
    switch (ins->getOpcode()) {
    case llvm::Instruction::PHI:
        return idx > 0;
    case llvm::Instruction::Br:
        if (llvm::dyn_cast<llvm::BranchInst>(ins)->isUnconditional())
            return true;
        else
            return idx > 0;
    case llvm::Instruction::Call:
        return idx >=
            llvm::dyn_cast<llvm::CallInst>(ins)->getNumArgOperands();
    default:
        return false;
    }
}

llvm::Type* LLVMInstruction::GetInput(llvm::Instruction* ins) {
    /**
     * Special case inputs
     */
    switch(ins->getOpcode()) {
    case llvm::Instruction::Br:
        if (llvm::dyn_cast<llvm::BranchInst>(ins)->isUnconditional())
            return llvm::Type::getVoidTy(ins->getContext());
        else
            return ins->getOperand(0)->getType();
    case llvm::Instruction::PHI:
        return ins->getOperand(0)->getType();
    }

    /**
     * Default inputs
     */
    if (GetNumHWOperands(ins) == 0)
        return llvm::Type::getVoidTy(ins->getContext());
    if (GetNumHWOperands(ins) == 1) {
        for (unsigned i=0; i<ins->getNumOperands(); i++) {
            if (!HWIgnoresOperand(ins, i))
                return LLVMFunction::sanitizeType(
                            ins->getOperand(i));
        }
    }

    vector<llvm::Type*> ty;
    for (unsigned i=0; i<ins->getNumOperands(); i++) {
        if (!HWIgnoresOperand(ins, i))
            ty.push_back(ins->getOperand(i)->getType());
    }
    return llvm::StructType::get(ins->getContext(), ty);
}

llvm::Type* LLVMInstruction::GetOutput(llvm::Instruction* ins) {
    switch (ins->getOpcode()) {
    case llvm::Instruction::Br:
        if (llvm::dyn_cast<llvm::BranchInst>(ins)->isUnconditional())
            return llvm::Type::getVoidTy(ins->getContext());
        else
            return ins->getOperand(0)->getType();
    case llvm::Instruction::Ret:
        if (ins->getNumOperands() > 0)
            return ins->getOperand(0)->getType();
        return llvm::Type::getVoidTy(ins->getContext());
    default:
        return ins->getType();
    }
}

DependenceRule LLVMImpureInstruction::deps(
    const OutputPort* op) const {
    assert(std::find(outputs().begin(), outputs().end(), op)
           != outputs().end());
    return DependenceRule(DependenceRule::Custom, inputs());
}

template<typename Inner>
class WrapperInstruction: public LLVMPureInstruction {
public:
    WrapperInstruction(const LLVMBasicBlock* bb, llvm::Instruction* ins) :
        LLVMPureInstruction(bb, ins, GetInput(ins), GetOutput(ins))
    { }

    static WrapperInstruction* Create(const LLVMBasicBlock* bb, llvm::Instruction* ins) {
        return new WrapperInstruction(bb, ins);
    }

    virtual bool refinable() const {
        return true;
    }

    virtual bool refine(ConnectionDB& conns) const;
};

template<>
bool WrapperInstruction<Identity>::refine(ConnectionDB& conns) const
{
    auto b = new Identity(LLVMInstruction::GetOutput(_ins));

    conns.remap(input(), b->din());
    conns.remap(output(), b->dout());

    if (_ins->getNumOperands() == 0) {
        // No input, need a constant
        Constant* c;
        if (_ins->getType()->isVoidTy())
            c = new Constant(_ins->getType());
        else
            c = new Constant(llvm::Constant::getNullValue(_ins->getType()));
        conns.connect(c->dout(), b->din());
    }
    return true;
}

template<>
bool WrapperInstruction<Multiplexer>::refine(
    ConnectionDB& conns) const
{
    vector<llvm::Type*> inpTypes = {
        _ins->getOperand(0)->getType(),
        _ins->getOperand(1)->getType(),
        _ins->getOperand(2)->getType()
    };
    auto split = new Split(inpTypes);
    auto m = new Multiplexer(2, _ins->getOperand(1)->getType());
    auto b = new Join( m->din()->type() );
    conns.connect(b->dout(), m->din());
    conns.remap(input(), split->din());
    conns.connect(split->dout(0), b->din(0));
    // Reverse the second and third inputs since select is 'true'
    // result then 'false' result.
    conns.connect(split->dout(1), b->din(2));
    conns.connect(split->dout(2), b->din(1));
    conns.remap(output(), m->dout());
    return true;
}

template<>
bool WrapperInstruction<IntExtend>::refine(
    ConnectionDB& conns) const
{
    assert(_ins->getNumOperands() == 1);
    llvm::CastInst* ci = llvm::dyn_cast<llvm::CastInst>(_ins);
    assert(ci != NULL);
    unsigned diff = ci->getDestTy()->getScalarSizeInBits()
                        - ci->getSrcTy()->getScalarSizeInBits();
    bool sign = ci->getOpcode() == llvm::Instruction::SExt;
    IntExtend* b = new IntExtend(diff, sign, ci->getSrcTy());

    conns.remap(input(), b->din());
    conns.remap(output(), b->dout());
    return true;
}

template<>
bool WrapperInstruction<Extract>::refine(
    ConnectionDB& conns) const
{
    llvm::ExtractElementInst* ee =
        llvm::dyn_cast_or_null<llvm::ExtractElementInst>(_ins);
    if (ee != NULL) {
        llvm::Value* indexOp = ee->getIndexOperand();
        llvm::ConstantInt* ci =
            llvm::dyn_cast_or_null<llvm::ConstantInt>(indexOp);

        auto s = new Split(input()->type());
        conns.remap(input(), s->din());
        if (ci != NULL) {
            auto e = new Extract(ee->getVectorOperand()->getType(),
                                 {(unsigned)ci->getLimitedValue()});
            conns.connect(s->dout(0), e->din());
            conns.remap(output(), e->dout());
            return true;
        } else {
            auto N = numContainedTypes(s->dout(0)->type());
            assert(N > 0);
            auto m = new Multiplexer(N,
                                     nthType(s->dout(0)->type(), 0));

            auto idxTrunc = new IntTruncate(s->dout(1)->type(),
                                            llvm::Type::getIntNTy(ee->getContext(),
                                                                  idxwidth(N)));
            conns.connect(s->dout(1), idxTrunc->din());
            auto j = m->din()->join(conns);
            conns.connect(j->din(0), idxTrunc->dout());
            assert(j->din_size() == N+1);

            auto dataSplit = s->dout(0)->split(conns);
            for (unsigned i=0; i<N; i++) {
                conns.connect(j->din(i+1), dataSplit->dout(i));
            }
            conns.remap(output(), m->dout());
            return true;
        }
    }
    return false;
}

template<>
bool WrapperInstruction<ReplaceElement>::refine(
    ConnectionDB& conns) const
{
    llvm::InsertElementInst* ie =
        llvm::dyn_cast_or_null<llvm::InsertElementInst>(_ins);
    if (ie != NULL) {
        llvm::Value* indexOp = ie->getOperand(2);
        llvm::ConstantInt* ci =
            llvm::dyn_cast_or_null<llvm::ConstantInt>(indexOp);

        if (ci != NULL) {
            auto s = new Split(input()->type());
            conns.remap(input(), s->din());
            auto j = new Join({s->dout(0)->type(), s->dout(1)->type()});
            conns.connect(s->dout(0), j->din(0));
            conns.connect(s->dout(1), j->din(1));
            auto re = new ReplaceElement(ie->getOperand(0)->getType(),
                                         ci->getLimitedValue());
            conns.connect(j->dout(), re->din());
            conns.remap(output(), re->dout());
            return true;
        } else {
            auto re = new ReplaceElement(ie->getOperand(0)->getType());

            auto s = new Split(input()->type());
            conns.remap(input(), s->din());
            auto j = new Join(re->din()->type());
            conns.connect(j->dout(), re->din());
            conns.connect(s->dout(0), j->din(0));
            conns.connect(s->dout(1), j->din(1));

            auto trunc = new IntTruncate(s->dout(2)->type(),
                                         j->din(2)->type());
            conns.connect(s->dout(2), trunc->din());
            conns.connect(trunc->dout(), j->din(2));

            conns.remap(input(), s->din());
            conns.remap(output(), re->dout());
            return true;
        }
    }
    return false;
}

template<>
bool WrapperInstruction<Shift>::refine(
    ConnectionDB& conns) const
{
    auto s = new Split(input()->type());
    unsigned shftWidth = idxwidth(bitwidth(_ins->getOperand(0)->getType()));
    auto trunc = new IntTruncate(
        _ins->getOperand(1)->getType(),
        llvm::Type::getIntNTy(_ins->getContext(), shftWidth));
    conns.connect(s->dout(1), trunc->din());

    Shift* ret = NULL;
    switch(_ins->getOpcode()) {
    case llvm::Instruction::Shl:
        ret = new Shift(_ins->getOperand(0)->getType(),
                        Shift::Left,
                        Shift::Logical);
        break;
    case llvm::Instruction::LShr:
        ret = new Shift(_ins->getOperand(0)->getType(),
                        Shift::Right,
                        Shift::Logical);
        break;
    case llvm::Instruction::AShr:
        ret = new Shift(_ins->getOperand(0)->getType(),
                        Shift::Right,
                        Shift::Arithmetic);
        break;
    default:
        throw InvalidArgument("Don't know how to convert to shift!");
    }
    conns.connect(s->dout(0), ret->din(conns, 0));
    conns.connect(trunc->dout(), ret->din(conns, 1));
    conns.remap(output(), ret->dout());
    conns.remap(input(), s->din());
    return true;
}

template<>
bool WrapperInstruction<IntCompare>::refine(
    ConnectionDB& conns) const
{
    llvm::ICmpInst* icmp = llvm::dyn_cast<llvm::ICmpInst>(_ins);
    IntCompare::Cmp cmp;
    bool swapOps;
    bool isSigned;
    switch(icmp->getPredicate()) {
    case llvm::CmpInst::ICMP_EQ:
        cmp = IntCompare::EQ;
        swapOps = false;
        isSigned = false;
        break;
    case llvm::CmpInst::ICMP_NE:
        cmp = IntCompare::NEQ;
        swapOps = false;
        isSigned = false;
        break;
    case llvm::CmpInst::ICMP_UGT:
        cmp = IntCompare::GT;
        swapOps = false;
        isSigned = false;
        break;
    case llvm::CmpInst::ICMP_UGE:
        cmp = IntCompare::GTE;
        swapOps = false;
        isSigned = false;
        break;
    case llvm::CmpInst::ICMP_ULT:
        cmp = IntCompare::GT;
        swapOps = true;
        isSigned = false;
        break;
    case llvm::CmpInst::ICMP_ULE:
        cmp = IntCompare::GTE;
        swapOps = true;
        isSigned = false;
        break;
    case llvm::CmpInst::ICMP_SGT:
        cmp = IntCompare::GT;
        swapOps = false;
        isSigned = true;
        break;
    case llvm::CmpInst::ICMP_SGE:
        cmp = IntCompare::GTE;
        swapOps = false;
        isSigned = true;
        break;
    case llvm::CmpInst::ICMP_SLT:
        cmp = IntCompare::GT;
        swapOps = true;
        isSigned = true;
        break;
    case llvm::CmpInst::ICMP_SLE:
        cmp = IntCompare::GTE;
        swapOps = true;
        isSigned = true;
        break;
    default:
        throw InvalidArgument("Don't know how to convert to IntCompare!");
    }

    if (!swapOps) {
        auto cmpBlk = new IntCompare(_ins->getOperand(0)->getType(),
                                     _ins->getOperand(1)->getType(),
                                     cmp, isSigned);
        conns.remap(input(), cmpBlk->din());
        conns.remap(output(), cmpBlk->dout());
    } else {
        auto cmpBlk = new IntCompare(_ins->getOperand(1)->getType(),
                                     _ins->getOperand(0)->getType(),
                                     cmp, isSigned);
        auto s = new Split(input()->type());
        conns.connect(cmpBlk->din(conns, 0), s->dout(1));
        conns.connect(cmpBlk->din(conns, 1), s->dout(0));
        conns.remap(input(), s->din());
        conns.remap(output(), cmpBlk->dout());
    }
    return true;
}

template<typename C>
bool WrapperInstruction<C>::refine(
    ConnectionDB& conns) const
{
    assert(_ins->getNumOperands() == 1);
    C* b = new C(GetInput(_ins), GetOutput(_ins));

    conns.remap(input(), b->din());
    conns.remap(output(), b->dout());
    return true;
}

template<typename Inner>
class TruncatingIntWrapperInstruction: public LLVMPureInstruction {
    Inner* New() const;

public:
    TruncatingIntWrapperInstruction(const LLVMBasicBlock* bb, llvm::Instruction* ins) :
        LLVMPureInstruction(bb, ins)
    { }

    static TruncatingIntWrapperInstruction* Create(const LLVMBasicBlock* bb, llvm::Instruction* ins) {
        return new TruncatingIntWrapperInstruction(bb, ins);
    }

    virtual bool refinable() const {
        return true;
    }

    virtual bool refine(ConnectionDB& conns) const {
        Inner* b = New();
        conns.remap(input(), b->din());
        IntTruncate* trunc = new IntTruncate(b->dout()->type(), output()->type());
        conns.connect(b->dout(), trunc->din());
        conns.remap(output(), trunc->dout());
        return true;
    }
};

template<>
IntAddition* TruncatingIntWrapperInstruction<IntAddition>::New() const {
    std::vector<llvm::Type*> ops;
    for (unsigned i=0; i<_ins->getNumOperands(); i++) {
        if (!HWIgnoresOperand(_ins, i))
            ops.push_back(_ins->getOperand(i)->getType());
    }
    return new IntAddition(ops);
}

template<>
IntMultiply* TruncatingIntWrapperInstruction<IntMultiply>::New() const {
    std::vector<llvm::Type*> ops;
    for (unsigned i=0; i<_ins->getNumOperands(); i++) {
        if (!HWIgnoresOperand(_ins, i))
            ops.push_back(_ins->getOperand(i)->getType());
    }
    return new IntMultiply(ops);
}

template<>
IntSubtraction* TruncatingIntWrapperInstruction<IntSubtraction>::New() const {
    return new IntSubtraction(_ins->getOperand(0)->getType(),
                              _ins->getOperand(1)->getType());
}

template<typename G>
G* TruncatingIntWrapperInstruction<G>::New() const {
    switch (_ins->getOpcode()) {
    case llvm::Instruction::UDiv:
        return new G(_ins->getOperand(0)->getType(),
                     _ins->getOperand(1)->getType(),
                     false);
    case llvm::Instruction::SDiv:
        return new G(_ins->getOperand(0)->getType(),
                     _ins->getOperand(1)->getType(),
                     true);
    case llvm::Instruction::URem:
        return new G(_ins->getOperand(0)->getType(),
                     _ins->getOperand(1)->getType(),
                     false);
    case llvm::Instruction::SRem:
        return new G(_ins->getOperand(0)->getType(),
                     _ins->getOperand(1)->getType(),
                     true);
    default:
        throw InvalidArgument("Don't know how to create int instruction!");
    }
}

template<typename Inner>
class IntWrapperInstruction: public LLVMPureInstruction {
    Inner* New() const;

public:
    IntWrapperInstruction(const LLVMBasicBlock* bb, llvm::Instruction* ins) :
        LLVMPureInstruction(bb, ins)
    { }

    static IntWrapperInstruction* Create(const LLVMBasicBlock* bb, llvm::Instruction* ins) {
        return new IntWrapperInstruction(bb, ins);
    }

    virtual bool refinable() const {
        return true;
    }

    virtual bool refine(ConnectionDB& conns) const {
        Inner* b = New();
        conns.remap(input(), b->din());
        conns.remap(output(), b->dout());
        return true;
    }
};

template<>
Bitwise* IntWrapperInstruction<Bitwise>::New() const {
    Bitwise::Op op;
    switch(_ins->getOpcode()) {
    case llvm::Instruction::And:
        op = Bitwise::AND;
        break;
    case llvm::Instruction::Or:
        op = Bitwise::OR;
        break;
    case llvm::Instruction::Xor:
        op = Bitwise::XOR;
        break;
    default:
        throw InvalidArgument("Don't know how to convert to bitwise!");
    }

    return new Bitwise(2, _ins->getOperand(0)->getType(), op);
}

class FlowInstruction: public LLVMPureInstruction {
public:
    FlowInstruction(const LLVMBasicBlock* bb, llvm::Instruction* ins) :
        LLVMPureInstruction(bb, ins, GetInput(ins), GetOutput(ins))
    { }

    static LLVMInstruction* Create(const LLVMBasicBlock* bb, llvm::Instruction* ins) {
        if (ins->getNumOperands() == 1)
            return new LLVMConstant(bb, ins);
        return new FlowInstruction(bb, ins);
    }

    virtual bool refinable() const {
        return true;
    }

    virtual bool refine(ConnectionDB& conns) const
    {
        auto i = new Identity(this->din()->type());

        conns.remap(input(), i->din());
        conns.remap(output(), i->dout());

        return true;
    }
};

LLVMCallInstruction::LLVMCallInstruction(const LLVMBasicBlock* bb,
                                         llvm::Instruction* ins) :
        LLVMImpureInstruction(bb, ins, &_din),
        _din(this, GetInput(ins), "x"),
        _dout(this, GetOutput(ins), "a"),
        _call(this, GetInput(ins), "call"),
        _ret(this, GetOutput(ins), "ret") { }

bool LLVMCallInstruction::refine(ConnectionDB& conns) const {
    auto inputID = new Identity(input()->type());
    conns.remap(input(), inputID->din());
    conns.remap(call(), inputID->dout());
    auto outputID = new Identity(output()->type());
    conns.remap(output(), outputID->dout());
    conns.remap(ret(), outputID->din());
    return true;
}

LLVMInstruction* LLVMCallInstruction::Create(
        const LLVMBasicBlock* bb, llvm::Instruction* ins) {
    return new LLVMCallInstruction(bb, ins);
}

std::string LLVMMiscInstruction::print() const {
    return valuestr(_ins);
}

LLVMLoadInstruction::LLVMLoadInstruction(const LLVMBasicBlock* bb,
                                         llvm::Instruction* ins) :
        LLVMImpureInstruction(bb, ins, &_din),
        _din(this, GetInput(ins), "x"),
        _dout(this, GetOutput(ins), "a"),
        _readReq(this, GetInput(ins), "readReq"),
        _readResp(this, GetOutput(ins), "readResp") { }

bool LLVMLoadInstruction::refine(ConnectionDB& conns) const {
    auto inputID = new Identity(input()->type());
    conns.remap(input(), inputID->din());
    conns.remap(readReq(), inputID->dout());
    auto outputID = new Identity(output()->type());
    conns.remap(output(), outputID->dout());
    conns.remap(readResp(), outputID->din());
    return true;
}

LLVMInstruction* LLVMLoadInstruction::Create(
        const LLVMBasicBlock* bb, llvm::Instruction* ins) {
    if (isByvalLoad(ins))
        return WrapperInstruction<Identity>::Create(bb, ins);
    return new LLVMLoadInstruction(bb, ins);
}

bool LLVMLoadInstruction::isByval(llvm::Value* val) {
    if (llvm::Argument* arg = llvm::dyn_cast_or_null<llvm::Argument>(val)) {
        return arg->hasByValAttr();
    }
    return false;
}

bool LLVMLoadInstruction::isByvalLoad(llvm::Instruction* ins) {
    if (llvm::LoadInst* li = llvm::dyn_cast_or_null<llvm::LoadInst>(ins)) {
        return isByval(li->getOperand(0));
    }
    return false;
}

LLVMStoreInstruction::LLVMStoreInstruction(const LLVMBasicBlock* bb,
                                           llvm::Instruction* ins) :
    LLVMImpureInstruction(bb, ins, &_din),
    _din(this, GetInput(ins), "x"),
    _dout(this, GetOutput(ins), "a"),
    _writeReq(this, GetInput(ins), "writeReq"),
    _writeResp(this, GetOutput(ins), "writeResp") { }

bool LLVMStoreInstruction::refine(ConnectionDB& conns) const {
    auto inputID = new Identity(input()->type());
    conns.remap(input(), inputID->din());
    conns.remap(writeReq(), inputID->dout());
    auto outputID = new Identity(output()->type());
    conns.remap(output(), outputID->dout());
    conns.remap(writeResp(), outputID->din());
    return true;
}

typedef boost::function<LLVMInstruction* (const LLVMBasicBlock* bb, llvm::Instruction*)> InsConstructor;
std::unordered_map<unsigned, InsConstructor > Constructors = {
    {llvm::Instruction::PHI, WrapperInstruction<Identity>::Create},
    {llvm::Instruction::Select, WrapperInstruction<Multiplexer>::Create},

    // Integer binary operators
    {llvm::Instruction::Add,  TruncatingIntWrapperInstruction<IntAddition>::Create},
    {llvm::Instruction::Sub,  TruncatingIntWrapperInstruction<IntSubtraction>::Create},
    {llvm::Instruction::Mul,  TruncatingIntWrapperInstruction<IntMultiply>::Create},
    {llvm::Instruction::UDiv, TruncatingIntWrapperInstruction<IntDivide>::Create},
    {llvm::Instruction::SDiv, TruncatingIntWrapperInstruction<IntDivide>::Create},
    {llvm::Instruction::URem, TruncatingIntWrapperInstruction<IntRemainder>::Create},
    {llvm::Instruction::SRem, TruncatingIntWrapperInstruction<IntRemainder>::Create},
    {llvm::Instruction::ICmp, WrapperInstruction<IntCompare>::Create},

    // Conversion operators
    {llvm::Instruction::BitCast, WrapperInstruction<Cast>::Create},
    {llvm::Instruction::Trunc, WrapperInstruction<IntTruncate>::Create},
    {llvm::Instruction::ZExt, WrapperInstruction<IntExtend>::Create},
    {llvm::Instruction::SExt, WrapperInstruction<IntExtend>::Create},
    {llvm::Instruction::InsertElement,
        WrapperInstruction<ReplaceElement>::Create},

    // Packing Operators
    {llvm::Instruction::ExtractElement, WrapperInstruction<Extract>::Create},

    // Logical operators (integer operands)
    {llvm::Instruction::Shl, WrapperInstruction<Shift>::Create}, // Shift left  (logical)
    {llvm::Instruction::LShr, WrapperInstruction<Shift>::Create}, // Shift right (logical)
    {llvm::Instruction::AShr, WrapperInstruction<Shift>::Create}, // Shift right (arithmetic)
    {llvm::Instruction::And, IntWrapperInstruction<Bitwise>::Create},
    {llvm::Instruction::Or, IntWrapperInstruction<Bitwise>::Create},
    {llvm::Instruction::Xor, IntWrapperInstruction<Bitwise>::Create},

    // Control flow operators
    {llvm::Instruction::Ret, WrapperInstruction<Identity>::Create},
    {llvm::Instruction::Br, FlowInstruction::Create},
    {llvm::Instruction::Switch, FlowInstruction::Create},
    {llvm::Instruction::Call, LLVMCallInstruction::Create},

    // Memory ops
    {llvm::Instruction::Load, LLVMLoadInstruction::Create},
    {llvm::Instruction::Store, LLVMStoreInstruction::Create},
};

LLVMInstruction* LLVMInstruction::Create(const LLVMBasicBlock* bb,
                                         llvm::Instruction* ins) {
    assert(ins != NULL);
    auto f = Constructors.find(ins->getOpcode());
    if (f == Constructors.end())
        return new LLVMMiscInstruction(bb, ins);
    return f->second(bb, ins);
}

} // namespace llpm

max_stars_repo_name: llpm/llpm

max_issues_repo_name: llpm/llpm

max_forks_repo_name: llpm/llpm


text: #include <boost/property_map/parallel/simple_trigger.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/*
 -----------------------------------------------------------------------------------------------------------------------
 The MIT License (MIT)

 Copyright (c) 2018 Ralph-Gordon Paul. All rights reserved.

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 persons to whom the Software is furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 -----------------------------------------------------------------------------------------------------------------------
*/

#include <rgpaul/RGPZip.hpp>

#include <sys/stat.h>
#include <ctime>
#include <fstream>

#include <unzip.h>
#include <zconf.h>
#include <zip.h>
#include <zlib.h>
#include <boost/filesystem.hpp>

// ---------------------------------------------------------------------------------------------------------------------
// Constructor / Destructor
// ---------------------------------------------------------------------------------------------------------------------

rgpaul::Zip::Zip() {}

rgpaul::Zip::~Zip()
{
    // close any open files
    closeZipFile();
    closeUnzipFile();
}

// ---------------------------------------------------------------------------------------------------------------------
// Accessors
// ---------------------------------------------------------------------------------------------------------------------

std::shared_ptr<std::vector<std::string>> rgpaul::Zip::unzippedFiles() const { return _unzippedFiles; }

// ---------------------------------------------------------------------------------------------------------------------
// ZIP Functions
// ---------------------------------------------------------------------------------------------------------------------

bool rgpaul::Zip::createZipFile(const std::string &filepath, bool append, const std::string &password)
{
    _password = password;
    int appendData = append ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE;

    _zipFile = zipOpen(filepath.data(), appendData);

    return _zipFile != nullptr;
}

bool rgpaul::Zip::addFileToZip(const std::string &filePath, const std::string &newName)
{
    struct stat fileStat = {0};
    int status = ::stat(filePath.c_str(), &fileStat);

    std::time_t timestamp = fileStat.st_mtime;

    std::chrono::system_clock::time_point tp;  // epoche (1.1.1970)
    std::chrono::system_clock::time_point modifiedDate = tp + std::chrono::seconds(timestamp);

    std::ifstream fs(filePath, std::ios::binary | std::ios::ate);

    if (!fs.is_open())
    {
        return false;
    }

    long long fileSize = fs.tellg();
    fs.seekg(0);

    // TODO: Improve this chunk of code. We read the whole file into memory.
    std::shared_ptr<std::vector<uint8_t>> data = std::make_shared<std::vector<uint8_t>>(fileSize);
    fs.read(reinterpret_cast<char *>(&(*data)[0]), fileSize);
    fs.close();

    return addDataToZip(data, newName, modifiedDate);
}

bool rgpaul::Zip::addDataToZip(std::shared_ptr<std::vector<uint8_t>> data, const std::string &newName,
                               std::chrono::system_clock::time_point created)
{
    if (data == nullptr || newName.empty() || _zipFile == nullptr)
    {
        return false;
    }

    std::time_t timestamp = std::chrono::system_clock::to_time_t(created);
    std::tm local_tm = *localtime(&timestamp);
    zip_fileinfo zipInfo = {{0, 0, 0, 0, 0, 0}, 0, 0, 0};

    zipInfo.tmz_date.tm_sec = static_cast<uInt>(local_tm.tm_sec);
    zipInfo.tmz_date.tm_min = static_cast<uInt>(local_tm.tm_min);
    zipInfo.tmz_date.tm_hour = static_cast<uInt>(local_tm.tm_hour);
    zipInfo.tmz_date.tm_mday = static_cast<uInt>(local_tm.tm_mday);
    zipInfo.tmz_date.tm_mon = static_cast<uInt>(local_tm.tm_mon);
    zipInfo.tmz_date.tm_year = static_cast<uInt>(local_tm.tm_year + 1900);

    if (_password.empty())
    {
        if (zipOpenNewFileInZip(_zipFile,              // file
                                newName.c_str(),       // filename
                                &zipInfo,              // zipfi
                                nullptr,               // extrafield_local
                                0,                     // size_extrafield_local
                                nullptr,               // extrafield_global
                                0,                     // size_extrafield_global
                                nullptr,               // comment
                                Z_DEFLATED,            // method
                                Z_DEFAULT_COMPRESSION  // level
                                ) != Z_OK)
        {
            return false;
        }
    }
    else
    {
        uLong crc = crc32(0L, nullptr, 0L);
        crc = crc32(crc, reinterpret_cast<const Bytef *>(&(*data)[0]), data->size());
        if (zipOpenNewFileInZip3(_zipFile,               // file
                                 newName.c_str(),        // filename
                                 &zipInfo,               // zipfi
                                 nullptr, 0,             // extrafield_local
                                 nullptr, 0,             // extrafield_global
                                 nullptr,                // comment
                                 Z_DEFLATED,             // method
                                 Z_DEFAULT_COMPRESSION,  // level
                                 0,                      // raw
                                 15,                     // windowBits
                                 8,                      // memLevel
                                 Z_DEFAULT_STRATEGY,     // strategy
                                 _password.c_str(),      // password
                                 crc                     // crcForCrypting
                                 ) != Z_OK)
        {
            return false;
        }
    }

    if (zipWriteInFileInZip(_zipFile, &(*data)[0], data->size()) != Z_OK)
    {
        return false;
    }

    return zipCloseFileInZip(_zipFile) == Z_OK;
}

bool rgpaul::Zip::closeZipFile()
{
    _password.clear();

    if (_zipFile == nullptr)
    {
        return false;
    }

    if (zipClose(_zipFile, nullptr) != Z_OK)
    {
        _zipFile = nullptr;
        return false;
    }

    _zipFile = nullptr;
    return true;
}

// ---------------------------------------------------------------------------------------------------------------------
// UNZIP Functions
// ---------------------------------------------------------------------------------------------------------------------

bool rgpaul::Zip::openUnzipFile(const std::string &filePath, const std::string &password)
{
    _password = password;

    _unzippedFiles = std::make_shared<std::vector<std::string>>();

    _unzipFile = unzOpen(filePath.c_str());

    return _unzipFile != nullptr;
}

bool rgpaul::Zip::unzipFiles(std::string targetPath, bool overwrite)
{
    if (targetPath.empty() || _unzipFile == nullptr)
    {
        return false;
    }

    // targetPath needs a trailing /
    if (targetPath.back() != '/')
    {
        targetPath.append("/");
    }

    bool success = true;

    char buffer[4096] = {0};

    const char *password = {nullptr};

    if (!_password.empty())
    {
        password = _password.c_str();
    }

    if (unzGoToFirstFile(_unzipFile) != UNZ_OK)
    {
        return false;
    }

    do  // we iterate over all files
    {
        // open current file
        if (password != nullptr)
        {
            // we have a password - so we need to open with it
            if (unzOpenCurrentFilePassword(_unzipFile, password) != UNZ_OK)
            {
                success = false;
                break;
            }
        }
        else
        {
            // we don't have a password - just open
            if (unzOpenCurrentFile(_unzipFile) != UNZ_OK)
            {
                success = false;
                break;
            }
        }

        // reading file info
        unz_file_info fileInfo;
        if (unzGetCurrentFileInfo(_unzipFile, &fileInfo, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK)
        {
            success = false;
            unzCloseCurrentFile(_unzipFile);
            break;
        }

        // get filename from the fileInfo
        char *cFileName = new char[fileInfo.size_filename + 1];
        unzGetCurrentFileInfo(_unzipFile, &fileInfo, cFileName, fileInfo.size_filename + 1, nullptr, 0, nullptr, 0);
        cFileName[fileInfo.size_filename] = '\0';

        // check filename for paths
        std::string fileName = cFileName;

        delete[] cFileName;

        // replace all '\' with '/'
        unsigned long index;
        while ((index = fileName.find_first_of('\\')) != std::string::npos)
        {
            fileName.replace(index, 1, "/");
        }
        std::string fullPath = targetPath + fileName;

        // create directory / parent directories
        index = fullPath.find_last_of('/');
        if (index != std::string::npos)
        {
            boost::filesystem::path dir(fullPath.substr(0, index + 1));
            boost::filesystem::create_directories(dir);
        }

        std::shared_ptr<std::ofstream> fs;
        int read = 0;
        while ((read = unzReadCurrentFile(_unzipFile, buffer, 4096)) > 0)
        {
            if (!fs || !fs->is_open())
            {
                if (boost::filesystem::exists(fullPath) && !overwrite)
                {
                    break;
                }

                fs = std::make_shared<std::ofstream>(fullPath, std::ios::binary | std::ios::trunc);

                if (!fs->is_open())
                {
                    fs.reset();
                    break;
                }
            }

            fs->write(buffer, read);
        }

        // check if we got an error
        if (read < 0)
        {
            success = false;
        }

        if (fs)
        {
            fs->close();  // flush the data to the file
            // we list all unzipped files
            _unzippedFiles->push_back(fullPath);
            fs.reset();
        }

        // we are done with the file -> close the file
        if (unzCloseCurrentFile(_unzipFile) != UNZ_OK)
        {
            success = false;
            break;
        }

    }  // goto next file
    while (unzGoToNextFile(_unzipFile) == UNZ_OK && success);

    return success;
}

bool rgpaul::Zip::closeUnzipFile()
{
    _password.clear();

    if (_unzipFile == nullptr)
    {
        return false;
    }

    if (unzClose(_unzipFile) != UNZ_OK)
    {
        _unzipFile = nullptr;
        return false;
    }

    _unzipFile = nullptr;
    return true;
}

max_stars_repo_name: Manromen/RGPZip

max_issues_repo_name: Manromen/RGPZip

max_forks_repo_name: Manromen/RGPZip