<commit_before>// Copyright (c) 2016, 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.
//

#include <gflags/gflags.h>
#include <vector>
#include <string>
#include <iostream>
#include <common/string_util.h>
#include <common/thread_pool.h>
#include <boost/bind.hpp>

#include "mark.h"

DECLARE_string(flagfile);
DECLARE_string(nameserver_nodes);

DEFINE_string(mode, "put", "[put | read]");
DEFINE_int64(count, 0, "put/read/delete file count");
DEFINE_int32(thread, 5, "thread num");
DEFINE_int32(seed, 301, "random seed");
DEFINE_int32(file_size, 1024, "file size in KB");
DEFINE_string(folder, "test", "write data to which folder");
DEFINE_bool(break_on_failure, true, "exit when error occurs");

namespace baidu {
namespace bfs {

// borrowed from LevelDB
class Random {
private:
    uint32_t seed_;
public:
    explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { }
    uint32_t Next() {
        static const uint32_t M = 2147483647L;
        static const uint64_t A = 16807;
        uint64_t product = seed_ * A;
        seed_ = static_cast<uint32_t>((product >> 31) + (product & M));
        if (seed_ > M) {
            seed_ -= M;
        }
        return seed_;
    }
    uint32_t Uniform(int n) { return Next() % n; }
};

Mark::Mark() : fs_(NULL), file_size_(FLAGS_file_size << 10), exit_(false) {
    if (!FS::OpenFileSystem(FLAGS_nameserver_nodes.c_str(), &fs_, FSOptions())) {
        std::cerr << "Open filesytem failed " << FLAGS_nameserver_nodes << std::endl;
        exit(EXIT_FAILURE);
    }

    thread_pool_ = new common::ThreadPool(FLAGS_thread + 1);
    rand_ = new Random*[FLAGS_thread];
    for (int i = 0; i < FLAGS_thread; i++) {
        rand_[i] = new Random(FLAGS_seed + i);
    }
}

bool Mark::FinishPut(File* file, int thread_id) {
    if (file) {
        if (!file->Close()) {
            delete file;
            rand_[thread_id]->Next();
            return false;
        }
        delete file;
    }
    rand_[thread_id]->Next();
    return true;
}

void Mark::Put(const std::string& filename, const std::string& base, int thread_id) {
    File* file;
    if (!fs_->OpenFile(filename.c_str(), O_WRONLY | O_TRUNC, 664, &file, WriteOptions())) {
        if (FLAGS_break_on_failure) {
            std::cerr << "OpenFile failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            FinishPut(file, thread_id);
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    int64_t len = 0;
    int64_t base_size = (1 << 20) / 2;
    while (len < file_size_) {
        uint32_t w = base_size + rand_[thread_id]->Uniform(base_size);

        uint32_t write_len = file->Write(base.c_str(), w);
        if (write_len != w) {
            if (FLAGS_break_on_failure) {
                std::cerr << "Write length does not match write_len = "
                        << write_len << " should be " << w << std::endl;
                exit(EXIT_FAILURE);
            } else {
                FinishPut(file, thread_id);
                std::cerr << "[Failed] " << filename << std::endl;
                return;
            }
        }
        len += write_len;
    }
    if (!FinishPut(file, thread_id)) {
        if (FLAGS_break_on_failure) {
            std::cerr << "Close file failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    put_counter_.Inc();
    all_counter_.Inc();
}

bool Mark::FinishRead(File* file) {
    if (file) {
        if (!file->Close()) {
            delete file;
            return false;
        }
        delete file;
    }
    return true;
}

void Mark::Read(const std::string& filename, const std::string& base, int thread_id) {
    File* file;
    if (!fs_->OpenFile(filename.c_str(), O_RDONLY, &file, ReadOptions())) {
        if (FLAGS_break_on_failure) {
            std::cerr << "Open file failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            FinishRead(file);
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    int64_t buf_size = 1 << 20;
    int64_t base_size = buf_size / 2;
    char buf[buf_size];
    int64_t bytes = 0;
    int32_t len = 0;
    while (1) {
        uint32_t r = base_size + rand_[thread_id]->Uniform(base_size);
        len = file->Read(buf, r);
        if (len < 0) {
            if (FLAGS_break_on_failure) {
                std::cerr << "Read length error" << std::endl;
                exit(EXIT_FAILURE);
            } else {
                FinishRead(file);
                std::cerr << "[Failed] " << filename << std::endl;
                return;
            }
        }
        if (len == 0) {
            break;
        }
        if (base.substr(0, len) != std::string(buf, len)) {
            if (FLAGS_break_on_failure) {
                std::cerr << "Read varify failed " << filename << " : bytes = " << bytes
                        << " len = " << len << " r = " << r << std::endl;
                exit(EXIT_FAILURE);
            } else {
                FinishRead(file);
                std::cerr << "[Failed] " << filename << std::endl;
                return;
            }
        }
        bytes += len;
    }
    BfsFileInfo info;
    fs_->Stat(filename.c_str(), &info);
    if (bytes != info.size) {
        if (FLAGS_break_on_failure) {
            std::cerr << "File size mismatch " << filename << " size = " << bytes
                    << " should be " << info.size << std::endl;
            exit(EXIT_FAILURE);
        } else {
            FinishRead(file);
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    if (!FinishRead(file)) {
        if (FLAGS_break_on_failure) {
            std::cerr << "Close file failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    read_counter_.Inc();
    all_counter_.Inc();
}

void Mark::Delete(const std::string& filename) {
    if(!fs_->DeleteFile(filename.c_str())) {
        assert(0);
    }
    del_counter_.Dec();
}

void Mark::PutWrapper(int thread_id) {
    std::string prefix = common::NumToString(thread_id);
    int name_id = 0;
    int64_t count = 0;
    std::string base;
    RandomString(&base, 1<<20, thread_id);
    while (FLAGS_count == 0 || count != FLAGS_count) {
        std::string filename = "/" + FLAGS_folder + "/" + prefix + "/" + common::NumToString(name_id);
        Put(filename, base, thread_id);
        ++name_id;
        ++count;
    }
    exit_ = true;
}

void Mark::ReadWrapper(int thread_id) {
    std::string prefix = common::NumToString(thread_id);
    int name_id = 0;
    int64_t count = 0;
    std::string base;
    RandomString(&base, 1<<20, thread_id);
    while (FLAGS_count == 0 || count != FLAGS_count) {
        std::string filename = "/" + FLAGS_folder + "/" + prefix + "/" + common::NumToString(name_id);
        Read(filename, base, thread_id);
        ++name_id;
        ++count;
    }
    exit_ = true;
}

void Mark::PrintStat() {
    std::cout << "Put\t" << put_counter_.Get() << "\tDel\t" << del_counter_.Get()
              << "\tRead\t" << read_counter_.Get() << "\tAll\t" << all_counter_.Get() << std::endl;
    put_counter_.Set(0);
    del_counter_.Set(0);
    read_counter_.Set(0);
    thread_pool_->DelayTask(1000, boost::bind(&Mark::PrintStat, this));
}

void Mark::Run() {
    PrintStat();
    if (FLAGS_mode == "put") {
        fs_->CreateDirectory(("/" + FLAGS_folder).c_str());
        for (int i = 0; i < FLAGS_thread; ++i) {
            std::string prefix = common::NumToString(i);
            fs_->CreateDirectory(("/" + FLAGS_folder + "/" + prefix).c_str());
        }
        for (int i = 0; i < FLAGS_thread; ++i) {
            thread_pool_->AddTask(boost::bind(&Mark::PutWrapper, this, i));
        }
    } else if (FLAGS_mode == "read") {
        for (int i = 0; i < FLAGS_thread; ++i) {
            thread_pool_->AddTask(boost::bind(&Mark::ReadWrapper, this, i));
        }
    }
    while (!exit_) {
        sleep(1);
    }
    thread_pool_->Stop(true);
    if (FLAGS_count != 0) {
        std::cout << "Total " << FLAGS_mode << " " << FLAGS_count * FLAGS_thread << std::endl;
    }
}

void Mark::RandomString(std::string* out, int size, int rand_index) {
    out->resize(size);
    for (int i = 0; i < size; i++) {
        (*out)[i] = static_cast<char>(' ' + rand_[rand_index]->Next());
    }
}

} // namespace bfs
} // namespace baidu

int main(int argc, char* argv[]) {
    FLAGS_flagfile = "bfs.flag";
    ::google::ParseCommandLineFlags(&argc, &argv, false);
    baidu::bfs::Mark mark;
    mark.Run();
    return 0;
}
<commit_msg>fix mark error code<commit_after>// Copyright (c) 2016, 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.
//

#include <gflags/gflags.h>
#include <vector>
#include <string>
#include <iostream>
#include <common/string_util.h>
#include <common/thread_pool.h>
#include <boost/bind.hpp>

#include "mark.h"

DECLARE_string(flagfile);
DECLARE_string(nameserver_nodes);

DEFINE_string(mode, "put", "[put | read]");
DEFINE_int64(count, 0, "put/read/delete file count");
DEFINE_int32(thread, 5, "thread num");
DEFINE_int32(seed, 301, "random seed");
DEFINE_int32(file_size, 1024, "file size in KB");
DEFINE_string(folder, "test", "write data to which folder");
DEFINE_bool(break_on_failure, true, "exit when error occurs");

namespace baidu {
namespace bfs {

// borrowed from LevelDB
class Random {
private:
    uint32_t seed_;
public:
    explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { }
    uint32_t Next() {
        static const uint32_t M = 2147483647L;
        static const uint64_t A = 16807;
        uint64_t product = seed_ * A;
        seed_ = static_cast<uint32_t>((product >> 31) + (product & M));
        if (seed_ > M) {
            seed_ -= M;
        }
        return seed_;
    }
    uint32_t Uniform(int n) { return Next() % n; }
};

Mark::Mark() : fs_(NULL), file_size_(FLAGS_file_size << 10), exit_(false) {
    if (!FS::OpenFileSystem(FLAGS_nameserver_nodes.c_str(), &fs_, FSOptions())) {
        std::cerr << "Open filesytem failed " << FLAGS_nameserver_nodes << std::endl;
        exit(EXIT_FAILURE);
    }

    thread_pool_ = new common::ThreadPool(FLAGS_thread + 1);
    rand_ = new Random*[FLAGS_thread];
    for (int i = 0; i < FLAGS_thread; i++) {
        rand_[i] = new Random(FLAGS_seed + i);
    }
}

bool Mark::FinishPut(File* file, int thread_id) {
    if (file) {
        if (OK != file->Close()) {
            delete file;
            rand_[thread_id]->Next();
            return false;
        }
        delete file;
    }
    rand_[thread_id]->Next();
    return true;
}

void Mark::Put(const std::string& filename, const std::string& base, int thread_id) {
    File* file;
    if (OK != fs_->OpenFile(filename.c_str(), O_WRONLY | O_TRUNC, 664, &file, WriteOptions())) {
        if (FLAGS_break_on_failure) {
            std::cerr << "OpenFile failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            FinishPut(file, thread_id);
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    int64_t len = 0;
    int64_t base_size = (1 << 20) / 2;
    while (len < file_size_) {
        uint32_t w = base_size + rand_[thread_id]->Uniform(base_size);

        uint32_t write_len = file->Write(base.c_str(), w);
        if (write_len != w) {
            if (FLAGS_break_on_failure) {
                std::cerr << "Write length does not match write_len = "
                        << write_len << " should be " << w << std::endl;
                exit(EXIT_FAILURE);
            } else {
                FinishPut(file, thread_id);
                std::cerr << "[Failed] " << filename << std::endl;
                return;
            }
        }
        len += write_len;
    }
    if (!FinishPut(file, thread_id)) {
        if (FLAGS_break_on_failure) {
            std::cerr << "Close file failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    put_counter_.Inc();
    all_counter_.Inc();
}

bool Mark::FinishRead(File* file) {
    if (file) {
        if (OK != file->Close()) {
            delete file;
            return false;
        }
        delete file;
    }
    return true;
}

void Mark::Read(const std::string& filename, const std::string& base, int thread_id) {
    File* file;
    if (OK != fs_->OpenFile(filename.c_str(), O_RDONLY, &file, ReadOptions())) {
        if (FLAGS_break_on_failure) {
            std::cerr << "Open file failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            FinishRead(file);
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    int64_t buf_size = 1 << 20;
    int64_t base_size = buf_size / 2;
    char buf[buf_size];
    int64_t bytes = 0;
    int32_t len = 0;
    while (1) {
        uint32_t r = base_size + rand_[thread_id]->Uniform(base_size);
        len = file->Read(buf, r);
        if (len < 0) {
            if (FLAGS_break_on_failure) {
                std::cerr << "Read length error" << std::endl;
                exit(EXIT_FAILURE);
            } else {
                FinishRead(file);
                std::cerr << "[Failed] " << filename << std::endl;
                return;
            }
        }
        if (len == 0) {
            break;
        }
        if (base.substr(0, len) != std::string(buf, len)) {
            if (FLAGS_break_on_failure) {
                std::cerr << "Read varify failed " << filename << " : bytes = " << bytes
                        << " len = " << len << " r = " << r << std::endl;
                exit(EXIT_FAILURE);
            } else {
                FinishRead(file);
                std::cerr << "[Failed] " << filename << std::endl;
                return;
            }
        }
        bytes += len;
    }
    BfsFileInfo info;
    fs_->Stat(filename.c_str(), &info);
    if (bytes != info.size) {
        if (FLAGS_break_on_failure) {
            std::cerr << "File size mismatch " << filename << " size = " << bytes
                    << " should be " << info.size << std::endl;
            exit(EXIT_FAILURE);
        } else {
            FinishRead(file);
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    if (!FinishRead(file)) {
        if (FLAGS_break_on_failure) {
            std::cerr << "Close file failed " << filename << std::endl;
            exit(EXIT_FAILURE);
        } else {
            std::cerr << "[Failed] " << filename << std::endl;
            return;
        }
    }
    read_counter_.Inc();
    all_counter_.Inc();
}

void Mark::Delete(const std::string& filename) {
    if(OK != fs_->DeleteFile(filename.c_str())) {
        assert(0);
    }
    del_counter_.Dec();
}

void Mark::PutWrapper(int thread_id) {
    std::string prefix = common::NumToString(thread_id);
    int name_id = 0;
    int64_t count = 0;
    std::string base;
    RandomString(&base, 1<<20, thread_id);
    while (FLAGS_count == 0 || count != FLAGS_count) {
        std::string filename = "/" + FLAGS_folder + "/" + prefix + "/" + common::NumToString(name_id);
        Put(filename, base, thread_id);
        ++name_id;
        ++count;
    }
    exit_ = true;
}

void Mark::ReadWrapper(int thread_id) {
    std::string prefix = common::NumToString(thread_id);
    int name_id = 0;
    int64_t count = 0;
    std::string base;
    RandomString(&base, 1<<20, thread_id);
    while (FLAGS_count == 0 || count != FLAGS_count) {
        std::string filename = "/" + FLAGS_folder + "/" + prefix + "/" + common::NumToString(name_id);
        Read(filename, base, thread_id);
        ++name_id;
        ++count;
    }
    exit_ = true;
}

void Mark::PrintStat() {
    std::cout << "Put\t" << put_counter_.Get() << "\tDel\t" << del_counter_.Get()
              << "\tRead\t" << read_counter_.Get() << "\tAll\t" << all_counter_.Get() << std::endl;
    put_counter_.Set(0);
    del_counter_.Set(0);
    read_counter_.Set(0);
    thread_pool_->DelayTask(1000, boost::bind(&Mark::PrintStat, this));
}

void Mark::Run() {
    PrintStat();
    if (FLAGS_mode == "put") {
        fs_->CreateDirectory(("/" + FLAGS_folder).c_str());
        for (int i = 0; i < FLAGS_thread; ++i) {
            std::string prefix = common::NumToString(i);
            fs_->CreateDirectory(("/" + FLAGS_folder + "/" + prefix).c_str());
        }
        for (int i = 0; i < FLAGS_thread; ++i) {
            thread_pool_->AddTask(boost::bind(&Mark::PutWrapper, this, i));
        }
    } else if (FLAGS_mode == "read") {
        for (int i = 0; i < FLAGS_thread; ++i) {
            thread_pool_->AddTask(boost::bind(&Mark::ReadWrapper, this, i));
        }
    }
    while (!exit_) {
        sleep(1);
    }
    thread_pool_->Stop(true);
    if (FLAGS_count != 0) {
        std::cout << "Total " << FLAGS_mode << " " << FLAGS_count * FLAGS_thread << std::endl;
    }
}

void Mark::RandomString(std::string* out, int size, int rand_index) {
    out->resize(size);
    for (int i = 0; i < size; i++) {
        (*out)[i] = static_cast<char>(' ' + rand_[rand_index]->Next());
    }
}

} // namespace bfs
} // namespace baidu

int main(int argc, char* argv[]) {
    FLAGS_flagfile = "bfs.flag";
    ::google::ParseCommandLineFlags(&argc, &argv, false);
    baidu::bfs::Mark mark;
    mark.Run();
    return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2018 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_INTERNAL_UNTIL_HPP
#define TAO_PEGTL_INTERNAL_UNTIL_HPP

#include "../config.hpp"

#include "bytes.hpp"
#include "eof.hpp"
#include "not_at.hpp"
#include "rule_conjunction.hpp"
#include "skip_control.hpp"
#include "star.hpp"

#include "../apply_mode.hpp"
#include "../rewind_mode.hpp"

#include "../analysis/generic.hpp"

namespace tao
{
   namespace TAO_PEGTL_NAMESPACE
   {
      namespace internal
      {
         template< typename Cond, typename... Rules >
         struct until;

         template< typename Cond >
         struct until< Cond >
         {
            using analyze_t = analysis::generic< analysis::rule_type::SEQ, star< not_at< Cond >, not_at< eof >, bytes< 1 > >, Cond >;

            template< apply_mode A,
                      rewind_mode M,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename... States >
            static bool match( Input& in, States&&... st )
            {
               auto m = in.template mark< M >();

               while( !Control< Cond >::template match< A, rewind_mode::REQUIRED, Action, Control >( in, st... ) ) {
                  if( in.empty() ) {
                     return false;
                  }
                  in.bump();
               }
               return m( true );
            }
         };

         template< typename Cond, typename... Rules >
         struct until
         {
            using analyze_t = analysis::generic< analysis::rule_type::SEQ, star< not_at< Cond >, not_at< eof >, Rules... >, Cond >;

            template< apply_mode A,
                      rewind_mode M,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename... States >
            static bool match( Input& in, States&&... st )
            {
               auto m = in.template mark< M >();
               using m_t = decltype( m );

               while( !Control< Cond >::template match< A, rewind_mode::REQUIRED, Action, Control >( in, st... ) ) {
                  if( in.empty() || ( !rule_conjunction< Rules... >::template match< A, m_t::next_rewind_mode, Action, Control >( in, st... ) ) ) {
                     return false;
                  }
               }
               return m( true );
            }
         };

         template< typename Cond, typename... Rules >
         struct skip_control< until< Cond, Rules... > > : std::true_type
         {
         };

      }  // namespace internal

   }  // namespace TAO_PEGTL_NAMESPACE

}  // namespace tao

#endif
<commit_msg>Remove superfluous (and potentially wrong) optimization<commit_after>// Copyright (c) 2014-2018 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_INTERNAL_UNTIL_HPP
#define TAO_PEGTL_INTERNAL_UNTIL_HPP

#include "../config.hpp"

#include "bytes.hpp"
#include "eof.hpp"
#include "not_at.hpp"
#include "rule_conjunction.hpp"
#include "skip_control.hpp"
#include "star.hpp"

#include "../apply_mode.hpp"
#include "../rewind_mode.hpp"

#include "../analysis/generic.hpp"

namespace tao
{
   namespace TAO_PEGTL_NAMESPACE
   {
      namespace internal
      {
         template< typename Cond, typename... Rules >
         struct until;

         template< typename Cond >
         struct until< Cond >
         {
            using analyze_t = analysis::generic< analysis::rule_type::SEQ, star< not_at< Cond >, not_at< eof >, bytes< 1 > >, Cond >;

            template< apply_mode A,
                      rewind_mode M,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename... States >
            static bool match( Input& in, States&&... st )
            {
               auto m = in.template mark< M >();

               while( !Control< Cond >::template match< A, rewind_mode::REQUIRED, Action, Control >( in, st... ) ) {
                  if( in.empty() ) {
                     return false;
                  }
                  in.bump();
               }
               return m( true );
            }
         };

         template< typename Cond, typename... Rules >
         struct until
         {
            using analyze_t = analysis::generic< analysis::rule_type::SEQ, star< not_at< Cond >, not_at< eof >, Rules... >, Cond >;

            template< apply_mode A,
                      rewind_mode M,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename... States >
            static bool match( Input& in, States&&... st )
            {
               auto m = in.template mark< M >();
               using m_t = decltype( m );

               while( !Control< Cond >::template match< A, rewind_mode::REQUIRED, Action, Control >( in, st... ) ) {
                  if( !rule_conjunction< Rules... >::template match< A, m_t::next_rewind_mode, Action, Control >( in, st... ) ) {
                     return false;
                  }
               }
               return m( true );
            }
         };

         template< typename Cond, typename... Rules >
         struct skip_control< until< Cond, Rules... > > : std::true_type
         {
         };

      }  // namespace internal

   }  // namespace TAO_PEGTL_NAMESPACE

}  // namespace tao

#endif
<|endoftext|>


<commit_before>#ifndef __AXIS_HPP
#define __AXIS_HPP

#ifndef __ODRIVE_MAIN_HPP
#error "This file should not be included directly. Include odrive_main.hpp instead."
#endif

// Warning: Do not reorder these enum values.
// The state machine uses ">" comparision on them.
enum AxisState_t {
    AXIS_STATE_UNDEFINED = 0,           //<! will fall through to idle
    AXIS_STATE_IDLE = 1,                //<! disable PWM and do nothing
    AXIS_STATE_STARTUP_SEQUENCE = 2, //<! the actual sequence is defined by the config.startup_... flags
    AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3,   //<! run all calibration procedures, then idle
    AXIS_STATE_MOTOR_CALIBRATION = 4,   //<! run motor calibration
    AXIS_STATE_SENSORLESS_CONTROL = 5,  //<! run sensorless calibration
    AXIS_STATE_ENCODER_INDEX_SEARCH = 6, //<! run encoder index search
    AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7, //<! run encoder offset calibration
    AXIS_STATE_CLOSED_LOOP_CONTROL = 8  //<! run closed loop control
};

struct AxisConfig_t {
    bool startup_motor_calibration = false;   //<! run motor calibration at startup, skip otherwise
    bool startup_encoder_index_search = false; //<! run encoder index search after startup, skip otherwise
                                               // this only has an effect if encoder.config.use_index is also true
    bool startup_encoder_offset_calibration = false; //<! run encoder offset calibration after startup, skip otherwise
    bool startup_closed_loop_control = false; //<! enable closed loop control after calibration/startup
    bool startup_sensorless_control = false; //<! enable sensorless control after calibration/startup
    bool enable_step_dir = true; //<! enable step/dir input after calibration
                                 //   For M0 this has no effect if enable_uart is true

    float counts_per_step = 2.0f;

    // Spinup settings
    float ramp_up_time = 0.4f;            // [s]
    float ramp_up_distance = 4 * M_PI;    // [rad]
    float spin_up_current = 10.0f;        // [A]
    float spin_up_acceleration = 400.0f;  // [rad/s^2]
    float spin_up_target_vel = 400.0f;    // [rad/s]
};

class Axis {
public:
    enum Error_t {
        ERROR_NO_ERROR = 0x00,
        ERROR_INVALID_STATE = 0x01, //<! an invalid state was requested
        ERROR_DC_BUS_UNDER_VOLTAGE = 0x02,
        ERROR_DC_BUS_OVER_VOLTAGE = 0x04,
        ERROR_CURRENT_MEASUREMENT_TIMEOUT = 0x08,
        ERROR_BRAKE_RESISTOR_DISARMED = 0x10, //<! the brake resistor was unexpectedly disarmed
        ERROR_MOTOR_DISARMED = 0x20, //<! the motor was unexpectedly disarmed
        ERROR_MOTOR_FAILED = 0x40,
        ERROR_SENSORLESS_ESTIMATOR_FAILED = 0x80,
        ERROR_ENCODER_FAILED = 0x100,
        ERROR_CONTROLLER_FAILED = 0x200,
        ERROR_POS_CTRL_DURING_SENSORLESS = 0x400,
    };

    enum thread_signals {
        M_SIGNAL_PH_CURRENT_MEAS = 1u << 0
    };

    Axis(const AxisHardwareConfig_t& hw_config,
            AxisConfig_t& config,
            Encoder& encoder,
            SensorlessEstimator& sensorless_estimator,
            Controller& controller,
            Motor& motor);

    void setup();
    void start_thread();
    void signal_current_meas();
    bool wait_for_current_meas();

    void step_cb();
    void set_step_dir_enabled(bool enable);

    bool check_DRV_fault();
    bool check_PSU_brownout();
    bool do_checks();

    // @brief Runs the specified update handler at the frequency of the current measurements.
    //
    // The loop runs until one of the following conditions:
    //  - update_handler returns false
    //  - the current measurement times out
    //  - the health checks fail (brownout, driver fault line)
    //  - update_handler doesn't update the modulation timings in time
    //    This criterion is ignored if current_state is AXIS_STATE_IDLE
    //
    // If update_handler is going to update the motor timings, you must call motor.arm()
    // shortly before this function.
    //
    // If the function returns, it is guaranteed that error is non-zero, except if the cause
    // for the exit was a negative return value of update_handler or an external
    // state change request (requested_state != AXIS_STATE_DONT_CARE).
    // Under all exit conditions the motor is disarmed and the brake current set to zero.
    // Furthermore, if the update_handler does not set the phase voltages in time, they will
    // go to zero.
    //
    // @tparam T Must be a callable type that takes no arguments and returns a bool
    template<typename T>
    void run_control_loop(const T& update_handler) {
        while (requested_state_ == AXIS_STATE_UNDEFINED) {
            if (!brake_resistor_armed_) {
                error_ |= ERROR_BRAKE_RESISTOR_DISARMED;
                break;
            }
            if ((current_state_ != AXIS_STATE_IDLE) && (motor_.armed_state_ == Motor::ARMED_STATE_DISARMED)) {
                // motor got disarmed in something other than the idle loop
                error_ |= ERROR_MOTOR_DISARMED;
                break;
            }
            if (motor_.error_ != Motor::ERROR_NO_ERROR) {
                error_ |= ERROR_MOTOR_FAILED;
                break;
            }

            if (!do_checks()) // error set during function call
                break;

            if (!update_handler()) // error set during function call
                break;

            // Check we meet deadlines after queueing
            ++loop_counter_;

            // Wait until the current measurement interrupt fires
            if (!wait_for_current_meas()) {
                // maybe the interrupt handler is dead, let's be
                // safe and float the phases
                safety_critical_disarm_motor_pwm(motor_);
                update_brake_current();
                error_ |= ERROR_CURRENT_MEASUREMENT_TIMEOUT;
                break;
            }
        }
    }

    bool run_sensorless_spin_up();
    bool run_sensorless_control_loop();
    bool run_closed_loop_control_loop();
    bool run_idle_loop();

    void run_state_machine_loop();

    const AxisHardwareConfig_t& hw_config_;
    AxisConfig_t& config_;

    Encoder& encoder_;
    SensorlessEstimator& sensorless_estimator_;
    Controller& controller_;
    Motor& motor_;

    osThreadId thread_id_;
    volatile bool thread_id_valid_ = false;

    // variables exposed on protocol
    Error_t error_ = ERROR_NO_ERROR;
    bool enable_step_dir_ = false; // auto enabled after calibration, based on config.enable_step_dir
    AxisState_t requested_state_ = AXIS_STATE_STARTUP_SEQUENCE;
    AxisState_t task_chain_[10] = { AXIS_STATE_UNDEFINED };
    AxisState_t& current_state_ = task_chain_[0];
    uint32_t loop_counter_ = 0;

    // Communication protocol definitions
    auto make_protocol_definitions() {
        return make_protocol_member_list(
            make_protocol_property("error", &error_),
            make_protocol_property("enable_step_dir", &enable_step_dir_),
            make_protocol_ro_property("current_state", &current_state_),
            make_protocol_property("requested_state", &requested_state_),
            make_protocol_ro_property("loop_counter", &loop_counter_),
            make_protocol_object("config",
                make_protocol_property("startup_motor_calibration", &config_.startup_motor_calibration),
                make_protocol_property("startup_encoder_index_search", &config_.startup_encoder_index_search),
                make_protocol_property("startup_encoder_offset_calibration", &config_.startup_encoder_offset_calibration),
                make_protocol_property("startup_closed_loop_control", &config_.startup_closed_loop_control),
                make_protocol_property("startup_sensorless_control", &config_.startup_sensorless_control),
                make_protocol_property("enable_step_dir", &config_.enable_step_dir),
                make_protocol_property("counts_per_step", &config_.counts_per_step),
                make_protocol_property("ramp_up_time", &config_.ramp_up_time),
                make_protocol_property("ramp_up_distance", &config_.ramp_up_distance),
                make_protocol_property("spin_up_current", &config_.spin_up_current),
                make_protocol_property("spin_up_acceleration", &config_.spin_up_acceleration),
                make_protocol_property("spin_up_target_vel", &config_.spin_up_target_vel)
            ),
            make_protocol_object("motor", motor_.make_protocol_definitions()),
            make_protocol_object("controller", controller_.make_protocol_definitions()),
            make_protocol_object("encoder", encoder_.make_protocol_definitions()),
            make_protocol_object("sensorless_estimator", sensorless_estimator_.make_protocol_definitions())
        );
    }
};

DEFINE_ENUM_FLAG_OPERATORS(Axis::Error_t)

#endif /* __AXIS_HPP */
<commit_msg>fix comment<commit_after>#ifndef __AXIS_HPP
#define __AXIS_HPP

#ifndef __ODRIVE_MAIN_HPP
#error "This file should not be included directly. Include odrive_main.hpp instead."
#endif

// Warning: Do not reorder these enum values.
// The state machine uses ">" comparision on them.
enum AxisState_t {
    AXIS_STATE_UNDEFINED = 0,           //<! will fall through to idle
    AXIS_STATE_IDLE = 1,                //<! disable PWM and do nothing
    AXIS_STATE_STARTUP_SEQUENCE = 2, //<! the actual sequence is defined by the config.startup_... flags
    AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3,   //<! run all calibration procedures, then idle
    AXIS_STATE_MOTOR_CALIBRATION = 4,   //<! run motor calibration
    AXIS_STATE_SENSORLESS_CONTROL = 5,  //<! run sensorless control
    AXIS_STATE_ENCODER_INDEX_SEARCH = 6, //<! run encoder index search
    AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7, //<! run encoder offset calibration
    AXIS_STATE_CLOSED_LOOP_CONTROL = 8  //<! run closed loop control
};

struct AxisConfig_t {
    bool startup_motor_calibration = false;   //<! run motor calibration at startup, skip otherwise
    bool startup_encoder_index_search = false; //<! run encoder index search after startup, skip otherwise
                                               // this only has an effect if encoder.config.use_index is also true
    bool startup_encoder_offset_calibration = false; //<! run encoder offset calibration after startup, skip otherwise
    bool startup_closed_loop_control = false; //<! enable closed loop control after calibration/startup
    bool startup_sensorless_control = false; //<! enable sensorless control after calibration/startup
    bool enable_step_dir = true; //<! enable step/dir input after calibration
                                 //   For M0 this has no effect if enable_uart is true

    float counts_per_step = 2.0f;

    // Spinup settings
    float ramp_up_time = 0.4f;            // [s]
    float ramp_up_distance = 4 * M_PI;    // [rad]
    float spin_up_current = 10.0f;        // [A]
    float spin_up_acceleration = 400.0f;  // [rad/s^2]
    float spin_up_target_vel = 400.0f;    // [rad/s]
};

class Axis {
public:
    enum Error_t {
        ERROR_NO_ERROR = 0x00,
        ERROR_INVALID_STATE = 0x01, //<! an invalid state was requested
        ERROR_DC_BUS_UNDER_VOLTAGE = 0x02,
        ERROR_DC_BUS_OVER_VOLTAGE = 0x04,
        ERROR_CURRENT_MEASUREMENT_TIMEOUT = 0x08,
        ERROR_BRAKE_RESISTOR_DISARMED = 0x10, //<! the brake resistor was unexpectedly disarmed
        ERROR_MOTOR_DISARMED = 0x20, //<! the motor was unexpectedly disarmed
        ERROR_MOTOR_FAILED = 0x40,
        ERROR_SENSORLESS_ESTIMATOR_FAILED = 0x80,
        ERROR_ENCODER_FAILED = 0x100,
        ERROR_CONTROLLER_FAILED = 0x200,
        ERROR_POS_CTRL_DURING_SENSORLESS = 0x400,
    };

    enum thread_signals {
        M_SIGNAL_PH_CURRENT_MEAS = 1u << 0
    };

    Axis(const AxisHardwareConfig_t& hw_config,
            AxisConfig_t& config,
            Encoder& encoder,
            SensorlessEstimator& sensorless_estimator,
            Controller& controller,
            Motor& motor);

    void setup();
    void start_thread();
    void signal_current_meas();
    bool wait_for_current_meas();

    void step_cb();
    void set_step_dir_enabled(bool enable);

    bool check_DRV_fault();
    bool check_PSU_brownout();
    bool do_checks();

    // @brief Runs the specified update handler at the frequency of the current measurements.
    //
    // The loop runs until one of the following conditions:
    //  - update_handler returns false
    //  - the current measurement times out
    //  - the health checks fail (brownout, driver fault line)
    //  - update_handler doesn't update the modulation timings in time
    //    This criterion is ignored if current_state is AXIS_STATE_IDLE
    //
    // If update_handler is going to update the motor timings, you must call motor.arm()
    // shortly before this function.
    //
    // If the function returns, it is guaranteed that error is non-zero, except if the cause
    // for the exit was a negative return value of update_handler or an external
    // state change request (requested_state != AXIS_STATE_DONT_CARE).
    // Under all exit conditions the motor is disarmed and the brake current set to zero.
    // Furthermore, if the update_handler does not set the phase voltages in time, they will
    // go to zero.
    //
    // @tparam T Must be a callable type that takes no arguments and returns a bool
    template<typename T>
    void run_control_loop(const T& update_handler) {
        while (requested_state_ == AXIS_STATE_UNDEFINED) {
            if (!brake_resistor_armed_) {
                error_ |= ERROR_BRAKE_RESISTOR_DISARMED;
                break;
            }
            if ((current_state_ != AXIS_STATE_IDLE) && (motor_.armed_state_ == Motor::ARMED_STATE_DISARMED)) {
                // motor got disarmed in something other than the idle loop
                error_ |= ERROR_MOTOR_DISARMED;
                break;
            }
            if (motor_.error_ != Motor::ERROR_NO_ERROR) {
                error_ |= ERROR_MOTOR_FAILED;
                break;
            }

            if (!do_checks()) // error set during function call
                break;

            if (!update_handler()) // error set during function call
                break;

            // Check we meet deadlines after queueing
            ++loop_counter_;

            // Wait until the current measurement interrupt fires
            if (!wait_for_current_meas()) {
                // maybe the interrupt handler is dead, let's be
                // safe and float the phases
                safety_critical_disarm_motor_pwm(motor_);
                update_brake_current();
                error_ |= ERROR_CURRENT_MEASUREMENT_TIMEOUT;
                break;
            }
        }
    }

    bool run_sensorless_spin_up();
    bool run_sensorless_control_loop();
    bool run_closed_loop_control_loop();
    bool run_idle_loop();

    void run_state_machine_loop();

    const AxisHardwareConfig_t& hw_config_;
    AxisConfig_t& config_;

    Encoder& encoder_;
    SensorlessEstimator& sensorless_estimator_;
    Controller& controller_;
    Motor& motor_;

    osThreadId thread_id_;
    volatile bool thread_id_valid_ = false;

    // variables exposed on protocol
    Error_t error_ = ERROR_NO_ERROR;
    bool enable_step_dir_ = false; // auto enabled after calibration, based on config.enable_step_dir
    AxisState_t requested_state_ = AXIS_STATE_STARTUP_SEQUENCE;
    AxisState_t task_chain_[10] = { AXIS_STATE_UNDEFINED };
    AxisState_t& current_state_ = task_chain_[0];
    uint32_t loop_counter_ = 0;

    // Communication protocol definitions
    auto make_protocol_definitions() {
        return make_protocol_member_list(
            make_protocol_property("error", &error_),
            make_protocol_property("enable_step_dir", &enable_step_dir_),
            make_protocol_ro_property("current_state", &current_state_),
            make_protocol_property("requested_state", &requested_state_),
            make_protocol_ro_property("loop_counter", &loop_counter_),
            make_protocol_object("config",
                make_protocol_property("startup_motor_calibration", &config_.startup_motor_calibration),
                make_protocol_property("startup_encoder_index_search", &config_.startup_encoder_index_search),
                make_protocol_property("startup_encoder_offset_calibration", &config_.startup_encoder_offset_calibration),
                make_protocol_property("startup_closed_loop_control", &config_.startup_closed_loop_control),
                make_protocol_property("startup_sensorless_control", &config_.startup_sensorless_control),
                make_protocol_property("enable_step_dir", &config_.enable_step_dir),
                make_protocol_property("counts_per_step", &config_.counts_per_step),
                make_protocol_property("ramp_up_time", &config_.ramp_up_time),
                make_protocol_property("ramp_up_distance", &config_.ramp_up_distance),
                make_protocol_property("spin_up_current", &config_.spin_up_current),
                make_protocol_property("spin_up_acceleration", &config_.spin_up_acceleration),
                make_protocol_property("spin_up_target_vel", &config_.spin_up_target_vel)
            ),
            make_protocol_object("motor", motor_.make_protocol_definitions()),
            make_protocol_object("controller", controller_.make_protocol_definitions()),
            make_protocol_object("encoder", encoder_.make_protocol_definitions()),
            make_protocol_object("sensorless_estimator", sensorless_estimator_.make_protocol_definitions())
        );
    }
};

DEFINE_ENUM_FLAG_OPERATORS(Axis::Error_t)

#endif /* __AXIS_HPP */
<|endoftext|>


<commit_before>// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "timedata.h"

#include "netbase.h"
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"

#include <boost/foreach.hpp>

using namespace std;

static CCriticalSection cs_nTimeOffset;
static int64_t nTimeOffset = 0;

/**
 * "Never go to sea with two chronometers; take one or three."
 * Our three time sources are:
 *  - System clock
 *  - Median of other nodes clocks
 *  - The user (asking the user to fix the system clock if the first two disagree)
 */
int64_t GetTimeOffset()
{
    LOCK(cs_nTimeOffset);
    return nTimeOffset;
}

int64_t GetAdjustedTime()
{
    return GetTime() + GetTimeOffset();
}

static int64_t abs64(int64_t n)
{
    return (n >= 0 ? n : -n);
}

#define BITCOIN_TIMEDATA_MAX_SAMPLES 200

void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
{
    LOCK(cs_nTimeOffset);
    // Ignore duplicates
    static set<CNetAddr> setKnown;
    if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
        return;
    if (!setKnown.insert(ip).second)
        return;

    // Add data
    static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0);
    vTimeOffsets.input(nOffsetSample);
    LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);

    // There is a known issue here (see issue #4521):
    //
    // - The structure vTimeOffsets contains up to 200 elements, after which
    // any new element added to it will not increase its size, replacing the
    // oldest element.
    //
    // - The condition to update nTimeOffset includes checking whether the
    // number of elements in vTimeOffsets is odd, which will never happen after
    // there are 200 elements.
    //
    // But in this case the 'bug' is protective against some attacks, and may
    // actually explain why we've never seen attacks which manipulate the
    // clock offset.
    //
    // So we should hold off on fixing this and clean it up as part of
    // a timing cleanup that strengthens it in a number of other ways.
    //
    if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
    {
        int64_t nMedian = vTimeOffsets.median();
        std::vector<int64_t> vSorted = vTimeOffsets.sorted();
        // Only let other nodes change our time by so much
        if (abs64(nMedian) < 70 * 60)
        {
            nTimeOffset = nMedian;
        }
        else
        {
            nTimeOffset = 0;

            static bool fDone;
            if (!fDone)
            {
                // If nobody has a time different than ours but within 5 minutes of ours, give a warning
                bool fMatch = false;
                BOOST_FOREACH(int64_t nOffset, vSorted)
                    if (nOffset != 0 && abs64(nOffset) < 5 * 60)
                        fMatch = true;

                if (!fMatch)
                {
                    fDone = true;
                    string strMessage = _("Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.");
                    strMiscWarning = strMessage;
                    uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
                }
            }
        }
        if (fDebug) {
            BOOST_FOREACH(int64_t n, vSorted)
                LogPrintf("%+d  ", n);
            LogPrintf("|  ");
        }
        LogPrintf("nTimeOffset = %+d  (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
    }
}
<commit_msg>Move time data log print to 'net' category to reduce log noise<commit_after>// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "timedata.h"

#include "netbase.h"
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"

#include <boost/foreach.hpp>

using namespace std;

static CCriticalSection cs_nTimeOffset;
static int64_t nTimeOffset = 0;

/**
 * "Never go to sea with two chronometers; take one or three."
 * Our three time sources are:
 *  - System clock
 *  - Median of other nodes clocks
 *  - The user (asking the user to fix the system clock if the first two disagree)
 */
int64_t GetTimeOffset()
{
    LOCK(cs_nTimeOffset);
    return nTimeOffset;
}

int64_t GetAdjustedTime()
{
    return GetTime() + GetTimeOffset();
}

static int64_t abs64(int64_t n)
{
    return (n >= 0 ? n : -n);
}

#define BITCOIN_TIMEDATA_MAX_SAMPLES 200

void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
{
    LOCK(cs_nTimeOffset);
    // Ignore duplicates
    static set<CNetAddr> setKnown;
    if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
        return;
    if (!setKnown.insert(ip).second)
        return;

    // Add data
    static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0);
    vTimeOffsets.input(nOffsetSample);
    LogPrint("net","added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);

    // There is a known issue here (see issue #4521):
    //
    // - The structure vTimeOffsets contains up to 200 elements, after which
    // any new element added to it will not increase its size, replacing the
    // oldest element.
    //
    // - The condition to update nTimeOffset includes checking whether the
    // number of elements in vTimeOffsets is odd, which will never happen after
    // there are 200 elements.
    //
    // But in this case the 'bug' is protective against some attacks, and may
    // actually explain why we've never seen attacks which manipulate the
    // clock offset.
    //
    // So we should hold off on fixing this and clean it up as part of
    // a timing cleanup that strengthens it in a number of other ways.
    //
    if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
    {
        int64_t nMedian = vTimeOffsets.median();
        std::vector<int64_t> vSorted = vTimeOffsets.sorted();
        // Only let other nodes change our time by so much
        if (abs64(nMedian) < 70 * 60)
        {
            nTimeOffset = nMedian;
        }
        else
        {
            nTimeOffset = 0;

            static bool fDone;
            if (!fDone)
            {
                // If nobody has a time different than ours but within 5 minutes of ours, give a warning
                bool fMatch = false;
                BOOST_FOREACH(int64_t nOffset, vSorted)
                    if (nOffset != 0 && abs64(nOffset) < 5 * 60)
                        fMatch = true;

                if (!fMatch)
                {
                    fDone = true;
                    string strMessage = _("Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.");
                    strMiscWarning = strMessage;
                    uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
                }
            }
        }

        BOOST_FOREACH(int64_t n, vSorted)
            LogPrint("net", "%+d  ", n);
        LogPrint("net", "|  ");

        LogPrint("net", "nTimeOffset = %+d  (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
    }
}
<|endoftext|>


<commit_before>#include "PyramidTree.h"
#include "Hashing.h"
#include "Util.h"
#include <algorithm>
#include <sstream>

namespace mdsearch
{

	Point computeInitialMedianPoint(int maxBucketNumber, int numDimensions)
	{
		Real m = static_cast<Real>(1.0 / numDimensions);
		int div = ceil(pow(maxBucketNumber, m));
		return Point(numDimensions, div);
	}

	PyramidTree::PyramidTree(unsigned int nDimensions, const Region& treeBoundary) :
		IndexStructure(nDimensions), boundary(treeBoundary),
		minPoint(nDimensions), maxPoint(nDimensions), medianPoint(nDimensions),
		bucketInterval(1)
	{
		// Compute the interval between buckets
		bucketInterval = static_cast<Real>(
			MAX_BUCKET_NUMBER / (numDimensions * 2)
		);
		bucketInterval = floor(bucketInterval);
		medianPoint = computeInitialMedianPoint(MAX_BUCKET_NUMBER, numDimensions);

		// Ensure boundaries are NEVER ZERO SIZED and the maximum
		// values are always larger than the minimum
		for (unsigned int d = 0; (d < numDimensions); d++)
		{
			if (boundary[d].max <= boundary[d].min)
			{
				boundary[d].max = boundary[d].min + 1;
			}
		}
		// Construct min-max points for SSE hashing function
		for (unsigned int i = 0; (i < boundary.numDimensions()); i++)
		{
			minPoint[i] = boundary[i].min;
			maxPoint[i] = boundary[i].max;
		}
	}

	void PyramidTree::clear()
	{
		// NOTE: Using assigment not clear() to ensure memory is de-allocated
		// (through destructors of containers)
		hashMap = OneDMap();
	}

	bool PyramidTree::insert(const Point& point)
	{
		// TODO: add boundary check w/ point here???

		PTBucket* bucket = getContainingBucket(point);
		if (bucket) // if bucket for point exists
		{
			// If point is stored in bucket - it ALREADY EXISTS
			if (getPointIndexInBucket(point, bucket) != -1)
			{
				return false;
			}
			else
			{
				insertToStructure(point, bucket);
				return true;
			}
		}
		else // if bucker does not exist, pass NULL to indicate new bucket
		{
			insertToStructure(point, NULL);
			return true;
		}
	}

	bool PyramidTree::remove(const Point& point)
	{
		PTBucket* bucket = getContainingBucket(point);
		// PTBucket has been found, point MIGHT be stored in structure
		if (bucket)
		{
			int index = getPointIndexInBucket(point, bucket);
			// If the point was found in bucket
			if (index != -1)
			{
				bucket->points.erase(bucket->points.begin() + index);
				bucket->pointSums.erase(bucket->pointSums.begin() + index);
				return true;
			}
			// Point is not contained in bucket -- cannot remove
			else
			{
				return false;
			}
		}
		// No bucket found, so point is not being stored in the structure
		else
		{
			return false;
		}
	}

	bool PyramidTree::update(const Point& oldPoint, const Point& newPoint)
	{
		if (remove(oldPoint)) // if point was removed successfully (i.e. it existed)
		{
			insert(newPoint); // insert a new point in its place!
			return true;
		}
		else
		{
			return false;
		}
	}

	bool PyramidTree::pointExists(const Point& point)
	{
		PTBucket* bucket = getContainingBucket(point);
		return (bucket && (getPointIndexInBucket(point, bucket) != -1));
	}

	PointList PyramidTree::pointsInRegion(const Region& region)
	{
		// TODO
		return PointList();
	}

	const Region& PyramidTree::getBoundary() const
	{
		return boundary;
	}

	unsigned int PyramidTree::numPointsStored() const
	{
		int total = 0;
		for (OneDMap::const_iterator it = hashMap.begin(); (it != hashMap.end()); it++)
			total += it->second.points.size();
		return total;
	}

	Real PyramidTree::averagePointsPerBucket() const
	{
		return static_cast<Real>(numPointsStored()) / hashMap.size();
	}

	std::string PyramidTree::toString() const
	{
		std::stringstream ss;
		ss << "Pyramid Tree\n";
		ss << "\tLoad Factor of Hash Table: " << hashMap.load_factor() << "\n";
		ss << "\tTotal Points Stored: " << numPointsStored() << "\n";
		ss << "\tAverage Points Per Bucket: " << averagePointsPerBucket() << "\n";
		ss << "\tBuckets:\n";
		for (OneDMap::const_iterator it = hashMap.begin(); (it != hashMap.end()); it++)
		{
			ss << "\t\tKey: " << it->first << ", Num Elements: " << it->second.points.size() << "\n";
		}
		return ss.str();
	}

	void PyramidTree::insertToStructure(const Point& point, PTBucket* bucket)
	{
		if (bucket) // if bucket for point already exisrs
		{
			bucket->points.push_back(point);
			bucket->pointSums.push_back(point.sum());
		}
		else // if bucket does not exist for point, create it!
		{
			#ifdef MDSEARCH_USE_SSE_HASHING
				int searchKey = hashPointSSE(numDimensions, point, minPoint, maxPoint, medianPoint);
			#else
				int searchKey = hashPoint(numDimensions, point, minPoint, maxPoint, medianPoint);
			#endif
			PTBucket newBucket;
			newBucket.points.push_back(point);
			newBucket.pointSums.push_back(point.sum());
			hashMap[searchKey] = newBucket;
		}
	}

	PTBucket* PyramidTree::getContainingBucket(const Point& point)
	{
		// Hash point into one-dimensional key
		#ifdef MDSEARCH_USE_SSE_HASHING
			int searchKey = hashPointSSE(numDimensions, point, minPoint, maxPoint, medianPoint);
		#else
			int searchKey = hashPoint(numDimensions, point, minPoint, maxPoint, medianPoint);
		#endif
		// Search underlying splay tree to find point's bucket
		OneDMap::iterator it = hashMap.find(searchKey);
		if (it != hashMap.end())
			return &(it->second); // pointer to bucket
		else
			return NULL;
	}

	int PyramidTree::getPointIndexInBucket(const Point& point,
		const PTBucket* bucket) const
	{
		// Search through points in bucket to see if it contains the given point
		Real pSum = point.sum();
		for (int index = 0; (index < bucket->points.size()); index++)
			if (pSum == bucket->pointSums[index] && point == bucket->points[index])
				return index;
		return -1;
	}

}
<commit_msg>Added erase-remove idiom to bucket pyramid tree to increase performence of delete() operation<commit_after>#include "PyramidTree.h"
#include "Hashing.h"
#include "Util.h"
#include <algorithm>
#include <sstream>

namespace mdsearch
{

	Point computeInitialMedianPoint(int maxBucketNumber, int numDimensions)
	{
		Real m = static_cast<Real>(1.0 / numDimensions);
		int div = ceil(pow(maxBucketNumber, m));
		return Point(numDimensions, div);
	}

	PyramidTree::PyramidTree(unsigned int nDimensions, const Region& treeBoundary) :
		IndexStructure(nDimensions), boundary(treeBoundary),
		minPoint(nDimensions), maxPoint(nDimensions), medianPoint(nDimensions),
		bucketInterval(1)
	{
		// Compute the interval between buckets
		bucketInterval = static_cast<Real>(
			MAX_BUCKET_NUMBER / (numDimensions * 2)
		);
		bucketInterval = floor(bucketInterval);
		medianPoint = computeInitialMedianPoint(MAX_BUCKET_NUMBER, numDimensions);

		// Ensure boundaries are NEVER ZERO SIZED and the maximum
		// values are always larger than the minimum
		for (unsigned int d = 0; (d < numDimensions); d++)
		{
			if (boundary[d].max <= boundary[d].min)
			{
				boundary[d].max = boundary[d].min + 1;
			}
		}
		// Construct min-max points for SSE hashing function
		for (unsigned int i = 0; (i < boundary.numDimensions()); i++)
		{
			minPoint[i] = boundary[i].min;
			maxPoint[i] = boundary[i].max;
		}
	}

	void PyramidTree::clear()
	{
		// NOTE: Using assigment not clear() to ensure memory is de-allocated
		// (through destructors of containers)
		hashMap = OneDMap();
	}

	bool PyramidTree::insert(const Point& point)
	{
		// TODO: add boundary check w/ point here???

		PTBucket* bucket = getContainingBucket(point);
		if (bucket) // if bucket for point exists
		{
			// If point is stored in bucket - it ALREADY EXISTS
			if (getPointIndexInBucket(point, bucket) != -1)
			{
				return false;
			}
			else
			{
				insertToStructure(point, bucket);
				return true;
			}
		}
		else // if bucker does not exist, pass NULL to indicate new bucket
		{
			insertToStructure(point, NULL);
			return true;
		}
	}

	bool PyramidTree::remove(const Point& point)
	{
		PTBucket* bucket = getContainingBucket(point);
		// PTBucket has been found, point MIGHT be stored in structure
		if (bucket)
		{
			int index = getPointIndexInBucket(point, bucket);
			// If the point was found in bucket
			if (index != -1)
			{
				// Swapping last element with the element to remove, so when the
				// desired point is removed it won't cause a potentially O(n) move
				// operation.
				std::iter_swap(bucket->points.begin() + index, bucket->points.end() - 1);
				std::iter_swap(bucket->pointSums.begin() + index, bucket->pointSums.end() - 1);
				bucket->points.erase(bucket->points.end() - 1);
				bucket->pointSums.erase(bucket->pointSums.end() - 1);
				return true;
			}
			// Point is not contained in bucket -- cannot remove
			else
			{
				return false;
			}
		}
		// No bucket found, so point is not being stored in the structure
		else
		{
			return false;
		}
	}

	bool PyramidTree::update(const Point& oldPoint, const Point& newPoint)
	{
		if (remove(oldPoint)) // if point was removed successfully (i.e. it existed)
		{
			insert(newPoint); // insert a new point in its place!
			return true;
		}
		else
		{
			return false;
		}
	}

	bool PyramidTree::pointExists(const Point& point)
	{
		PTBucket* bucket = getContainingBucket(point);
		return (bucket && (getPointIndexInBucket(point, bucket) != -1));
	}

	PointList PyramidTree::pointsInRegion(const Region& region)
	{
		// TODO
		return PointList();
	}

	const Region& PyramidTree::getBoundary() const
	{
		return boundary;
	}

	unsigned int PyramidTree::numPointsStored() const
	{
		int total = 0;
		for (OneDMap::const_iterator it = hashMap.begin(); (it != hashMap.end()); it++)
			total += it->second.points.size();
		return total;
	}

	Real PyramidTree::averagePointsPerBucket() const
	{
		return static_cast<Real>(numPointsStored()) / hashMap.size();
	}

	std::string PyramidTree::toString() const
	{
		std::stringstream ss;
		ss << "Pyramid Tree\n";
		ss << "\tLoad Factor of Hash Table: " << hashMap.load_factor() << "\n";
		ss << "\tTotal Points Stored: " << numPointsStored() << "\n";
		ss << "\tAverage Points Per Bucket: " << averagePointsPerBucket() << "\n";
		ss << "\tBuckets:\n";
		for (OneDMap::const_iterator it = hashMap.begin(); (it != hashMap.end()); it++)
		{
			ss << "\t\tKey: " << it->first << ", Num Elements: " << it->second.points.size() << "\n";
		}
		return ss.str();
	}

	void PyramidTree::insertToStructure(const Point& point, PTBucket* bucket)
	{
		if (bucket) // if bucket for point already exisrs
		{
			bucket->points.push_back(point);
			bucket->pointSums.push_back(point.sum());
		}
		else // if bucket does not exist for point, create it!
		{
			#ifdef MDSEARCH_USE_SSE_HASHING
				int searchKey = hashPointSSE(numDimensions, point, minPoint, maxPoint, medianPoint);
			#else
				int searchKey = hashPoint(numDimensions, point, minPoint, maxPoint, medianPoint);
			#endif
			PTBucket newBucket;
			newBucket.points.push_back(point);
			newBucket.pointSums.push_back(point.sum());
			hashMap[searchKey] = newBucket;
		}
	}

	PTBucket* PyramidTree::getContainingBucket(const Point& point)
	{
		// Hash point into one-dimensional key
		#ifdef MDSEARCH_USE_SSE_HASHING
			int searchKey = hashPointSSE(numDimensions, point, minPoint, maxPoint, medianPoint);
		#else
			int searchKey = hashPoint(numDimensions, point, minPoint, maxPoint, medianPoint);
		#endif
		// Search underlying splay tree to find point's bucket
		OneDMap::iterator it = hashMap.find(searchKey);
		if (it != hashMap.end())
			return &(it->second); // pointer to bucket
		else
			return NULL;
	}

	int PyramidTree::getPointIndexInBucket(const Point& point,
		const PTBucket* bucket) const
	{
		// Search through points in bucket to see if it contains the given point
		Real pSum = point.sum();
		for (int index = 0; (index < bucket->points.size()); index++)
			if (pSum == bucket->pointSums[index] && point == bucket->points[index])
				return index;
		return -1;
	}

}
<|endoftext|>


<commit_before>/**
 * @file llinstantmessage.cpp
 * @author Phoenix
 * @date 2005-08-29
 * @brief Constants and functions used in IM.
 *
 * $LicenseInfo:firstyear=2005&license=viewergpl$
 *
 * Copyright (c) 2005-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "lldbstrings.h"
#include "llinstantmessage.h"
#include "llhost.h"
#include "lluuid.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llsdutil_math.h"
#include "llpointer.h"
#include "message.h"

#include "message.h"

const U8 IM_ONLINE = 0;
const U8 IM_OFFLINE = 1;

const S32 VOTE_YES = 1;
const S32 VOTE_NO = 0;
const S32 VOTE_ABSTAIN = -1;

const S32 VOTE_MAJORITY = 0;
const S32 VOTE_SUPER_MAJORITY = 1;
const S32 VOTE_UNANIMOUS = 2;

const char EMPTY_BINARY_BUCKET[] = "";
const S32 EMPTY_BINARY_BUCKET_SIZE = 1;
const U32 NO_TIMESTAMP = 0;
const std::string SYSTEM_FROM("Second Life");
const S32 IM_TTL = 1;

/**
 * LLIMInfo
 */
LLIMInfo::LLIMInfo() :
	mParentEstateID(0),
	mOffline(0),
	mViewerThinksToIsOnline(false),
	mTimeStamp(0),
	mSource(IM_FROM_SIM),
	mTTL(IM_TTL)
{
}

LLIMInfo::LLIMInfo(
	const LLUUID& from_id,
	BOOL from_group,
	const LLUUID& to_id,
	EInstantMessage im_type,
	const std::string& name,
	const std::string& message,
	const LLUUID& id,
	U32 parent_estate_id,
	const LLUUID& region_id,
	const LLVector3& position,
	LLSD data,
	U8 offline,
	U32 timestamp,
	EIMSource source,
	S32 ttl) :
	mFromID(from_id),
	mFromGroup(from_group),
	mToID(to_id),
	mParentEstateID(0),
	mRegionID(region_id),
	mPosition(position),
	mOffline(offline),
	mViewerThinksToIsOnline(false),
	mIMType(im_type),
	mID(id),
	mTimeStamp(timestamp),
	mName(name),
	mMessage(message),
	mData(data),
	mSource(source),
	mTTL(ttl)
{
}

LLIMInfo::LLIMInfo(LLMessageSystem* msg, EIMSource source, S32 ttl) :
	mViewerThinksToIsOnline(false),
	mSource(source),
	mTTL(ttl)
{
	unpackMessageBlock(msg);
}

LLIMInfo::~LLIMInfo()
{
}

void LLIMInfo::packInstantMessage(LLMessageSystem* msg) const
{
	lldebugs << "LLIMInfo::packInstantMessage()" << llendl;
	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
	packMessageBlock(msg);
}

void LLIMInfo::packMessageBlock(LLMessageSystem* msg) const
{
	// Construct binary bucket
	std::vector<U8> bucket;
	if (mData.has("binary_bucket"))
	{
		bucket = mData["binary_bucket"].asBinary();
	}
	pack_instant_message_block(
		msg,
		mFromID,
		mFromGroup,
		LLUUID::null,
		mToID,
		mName,
		mMessage,
		mOffline,
		mIMType,
		mID,
		mParentEstateID,
		mRegionID,
		mPosition,
		mTimeStamp,
		&bucket[0],
		bucket.size());
}

void pack_instant_message(
	LLMessageSystem* msg,
	const LLUUID& from_id,
	BOOL from_group,
	const LLUUID& session_id,
	const LLUUID& to_id,
	const std::string& name,
	const std::string& message,
	U8 offline,
	EInstantMessage dialog,
	const LLUUID& id,
	U32 parent_estate_id,
	const LLUUID& region_id,
	const LLVector3& position,
	U32 timestamp,
	const U8* binary_bucket,
	S32 binary_bucket_size)
{
	lldebugs << "pack_instant_message()" << llendl;
	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
	pack_instant_message_block(
		msg,
		from_id,
		from_group,
		session_id,
		to_id,
		name,
		message,
		offline,
		dialog,
		id,
		parent_estate_id,
		region_id,
		position,
		timestamp,
		binary_bucket,
		binary_bucket_size);
}

void pack_instant_message_block(
	LLMessageSystem* msg,
	const LLUUID& from_id,
	BOOL from_group,
	const LLUUID& session_id,
	const LLUUID& to_id,
	const std::string& name,
	const std::string& message,
	U8 offline,
	EInstantMessage dialog,
	const LLUUID& id,
	U32 parent_estate_id,
	const LLUUID& region_id,
	const LLVector3& position,
	U32 timestamp,
	const U8* binary_bucket,
	S32 binary_bucket_size)
{
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, from_id);
	msg->addUUIDFast(_PREHASH_SessionID, session_id);
	msg->nextBlockFast(_PREHASH_MessageBlock);
	msg->addBOOLFast(_PREHASH_FromGroup, from_group);
	msg->addUUIDFast(_PREHASH_ToAgentID, to_id);
	msg->addU32Fast(_PREHASH_ParentEstateID, parent_estate_id);
	msg->addUUIDFast(_PREHASH_RegionID, region_id);
	msg->addVector3Fast(_PREHASH_Position, position);
	msg->addU8Fast(_PREHASH_Offline, offline);
	msg->addU8Fast(_PREHASH_Dialog, (U8) dialog);
	msg->addUUIDFast(_PREHASH_ID, id);
	msg->addU32Fast(_PREHASH_Timestamp, timestamp);
	msg->addStringFast(_PREHASH_FromAgentName, name);
	S32 bytes_left = MTUBYTES;
	if(!message.empty())
	{
		char buffer[MTUBYTES];
		int num_written = snprintf(buffer, MTUBYTES, "%s", message.c_str());	/* Flawfinder: ignore */
		// snprintf returns number of bytes that would have been written
		// had the output not being truncated. In that case, it will
		// return either -1 or value >= passed in size value . So a check needs to be added
		// to detect truncation, and if there is any, only account for the
		// actual number of bytes written..and not what could have been
		// written.
		if (num_written < 0 || num_written >= MTUBYTES)
		{
			num_written = MTUBYTES - 1;
			llwarns << "pack_instant_message_block: message truncated: " << message << llendl;
		}

		bytes_left -= num_written;
		bytes_left = llmax(0, bytes_left);
		msg->addStringFast(_PREHASH_Message, buffer);
	}
	else
	{
		msg->addStringFast(_PREHASH_Message, NULL);
	}
	const U8* bb;
	if(binary_bucket)
	{
		bb = binary_bucket;
		binary_bucket_size = llmin(bytes_left, binary_bucket_size);
	}
	else
	{
		bb = (const U8*)EMPTY_BINARY_BUCKET;
		binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE;
	}
	msg->addBinaryDataFast(_PREHASH_BinaryBucket, bb, binary_bucket_size);
}

void LLIMInfo::unpackMessageBlock(LLMessageSystem* msg)
{
	msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, mFromID);
	msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, mFromGroup);
	msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, mToID);
	msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, mParentEstateID);
	msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, mRegionID);
	msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, mPosition);
	msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Offline, mOffline);
	U8 dialog;
	msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Dialog, dialog);
	mIMType = (EInstantMessage) dialog;
	msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, mID);
	msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_Timestamp, mTimeStamp);
	msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, mName);

	msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, mMessage);

	S32 binary_bucket_size = llmin(
		MTUBYTES,
		msg->getSizeFast(
			_PREHASH_MessageBlock,
			_PREHASH_BinaryBucket));
	if(binary_bucket_size > 0)
	{
		std::vector<U8> bucket;
		bucket.resize(binary_bucket_size);

		msg->getBinaryDataFast(
			_PREHASH_MessageBlock,
			_PREHASH_BinaryBucket,
			&bucket[0],
			0,
			0,
			binary_bucket_size);
		mData["binary_bucket"] = bucket;
	}
	else
	{
		mData.clear();
	}
}

LLSD im_info_to_llsd(LLPointer<LLIMInfo> im_info)
{
	LLSD param_version;
	param_version["version"] = 1;
	LLSD param_message;
	param_message["from_id"] = im_info->mFromID;
	param_message["from_group"] = im_info->mFromGroup;
	param_message["to_id"] = im_info->mToID;
	param_message["from_name"] = im_info->mName;
	param_message["message"] = im_info->mMessage;
	param_message["type"] = (S32)im_info->mIMType;
	param_message["id"] = im_info->mID;
	param_message["timestamp"] = (S32)im_info->mTimeStamp;
	param_message["offline"] = (S32)im_info->mOffline;
	param_message["parent_estate_id"] = (S32)im_info->mParentEstateID;
	param_message["region_id"] = im_info->mRegionID;
	param_message["position"] = ll_sd_from_vector3(im_info->mPosition);
	param_message["data"] = im_info->mData;
	param_message["source"]= im_info->mSource;
	param_message["ttl"] = im_info->mTTL;

	LLSD param_agent;
	param_agent["agent_id"] = im_info->mFromID;

	LLSD params;
	params["version_params"] = param_version;
	params["message_params"] = param_message;
	params["agent_params"] = param_agent;

	return params;
}

LLPointer<LLIMInfo> llsd_to_im_info(const LLSD& im_info_sd)
{
	LLSD param_message = im_info_sd["message_params"];
	LLSD param_agent = im_info_sd["agent_params"];

	LLPointer<LLIMInfo> im_info = new LLIMInfo(
		param_message["from_id"].asUUID(),
		param_message["from_group"].asBoolean(),
		param_message["to_id"].asUUID(),
		(EInstantMessage) param_message["type"].asInteger(),
		param_message["from_name"].asString(),
		param_message["message"].asString(),
		param_message["id"].asUUID(),
		(U32) param_message["parent_estate_id"].asInteger(),
		param_message["region_id"].asUUID(),
		ll_vector3_from_sd(param_message["position"]),
		param_message["data"],
		(U8) param_message["offline"].asInteger(),
		(U32) param_message["timestamp"].asInteger(),
		(EIMSource)param_message["source"].asInteger(),
		param_message["ttl"].asInteger());

	return im_info;
}

LLPointer<LLIMInfo> LLIMInfo::clone()
{
	return new LLIMInfo(
			mFromID,
			mFromGroup,
			mToID,
			mIMType,
			mName,
			mMessage,
			mID,
			mParentEstateID,
			mRegionID,
			mPosition,
			mData,
			mOffline,
			mTimeStamp,
			mSource,
			mTTL);
}

<commit_msg>CID-315<commit_after>/**
 * @file llinstantmessage.cpp
 * @author Phoenix
 * @date 2005-08-29
 * @brief Constants and functions used in IM.
 *
 * $LicenseInfo:firstyear=2005&license=viewergpl$
 *
 * Copyright (c) 2005-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "lldbstrings.h"
#include "llinstantmessage.h"
#include "llhost.h"
#include "lluuid.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llsdutil_math.h"
#include "llpointer.h"
#include "message.h"

#include "message.h"

const U8 IM_ONLINE = 0;
const U8 IM_OFFLINE = 1;

const S32 VOTE_YES = 1;
const S32 VOTE_NO = 0;
const S32 VOTE_ABSTAIN = -1;

const S32 VOTE_MAJORITY = 0;
const S32 VOTE_SUPER_MAJORITY = 1;
const S32 VOTE_UNANIMOUS = 2;

const char EMPTY_BINARY_BUCKET[] = "";
const S32 EMPTY_BINARY_BUCKET_SIZE = 1;
const U32 NO_TIMESTAMP = 0;
const std::string SYSTEM_FROM("Second Life");
const S32 IM_TTL = 1;

/**
 * LLIMInfo
 */
LLIMInfo::LLIMInfo() :
	mFromGroup(FALSE),
	mParentEstateID(0),
	mOffline(0),
	mViewerThinksToIsOnline(false),
	mIMType(IM_NOTHING_SPECIAL),
	mTimeStamp(0),
	mSource(IM_FROM_SIM),
	mTTL(IM_TTL)
{
}

LLIMInfo::LLIMInfo(
	const LLUUID& from_id,
	BOOL from_group,
	const LLUUID& to_id,
	EInstantMessage im_type,
	const std::string& name,
	const std::string& message,
	const LLUUID& id,
	U32 parent_estate_id,
	const LLUUID& region_id,
	const LLVector3& position,
	LLSD data,
	U8 offline,
	U32 timestamp,
	EIMSource source,
	S32 ttl) :
	mFromID(from_id),
	mFromGroup(from_group),
	mToID(to_id),
	mParentEstateID(0),
	mRegionID(region_id),
	mPosition(position),
	mOffline(offline),
	mViewerThinksToIsOnline(false),
	mIMType(im_type),
	mID(id),
	mTimeStamp(timestamp),
	mName(name),
	mMessage(message),
	mData(data),
	mSource(source),
	mTTL(ttl)
{
}

LLIMInfo::LLIMInfo(LLMessageSystem* msg, EIMSource source, S32 ttl) :
	mViewerThinksToIsOnline(false),
	mSource(source),
	mTTL(ttl)
{
	unpackMessageBlock(msg);
}

LLIMInfo::~LLIMInfo()
{
}

void LLIMInfo::packInstantMessage(LLMessageSystem* msg) const
{
	lldebugs << "LLIMInfo::packInstantMessage()" << llendl;
	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
	packMessageBlock(msg);
}

void LLIMInfo::packMessageBlock(LLMessageSystem* msg) const
{
	// Construct binary bucket
	std::vector<U8> bucket;
	if (mData.has("binary_bucket"))
	{
		bucket = mData["binary_bucket"].asBinary();
	}
	pack_instant_message_block(
		msg,
		mFromID,
		mFromGroup,
		LLUUID::null,
		mToID,
		mName,
		mMessage,
		mOffline,
		mIMType,
		mID,
		mParentEstateID,
		mRegionID,
		mPosition,
		mTimeStamp,
		&bucket[0],
		bucket.size());
}

void pack_instant_message(
	LLMessageSystem* msg,
	const LLUUID& from_id,
	BOOL from_group,
	const LLUUID& session_id,
	const LLUUID& to_id,
	const std::string& name,
	const std::string& message,
	U8 offline,
	EInstantMessage dialog,
	const LLUUID& id,
	U32 parent_estate_id,
	const LLUUID& region_id,
	const LLVector3& position,
	U32 timestamp,
	const U8* binary_bucket,
	S32 binary_bucket_size)
{
	lldebugs << "pack_instant_message()" << llendl;
	msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
	pack_instant_message_block(
		msg,
		from_id,
		from_group,
		session_id,
		to_id,
		name,
		message,
		offline,
		dialog,
		id,
		parent_estate_id,
		region_id,
		position,
		timestamp,
		binary_bucket,
		binary_bucket_size);
}

void pack_instant_message_block(
	LLMessageSystem* msg,
	const LLUUID& from_id,
	BOOL from_group,
	const LLUUID& session_id,
	const LLUUID& to_id,
	const std::string& name,
	const std::string& message,
	U8 offline,
	EInstantMessage dialog,
	const LLUUID& id,
	U32 parent_estate_id,
	const LLUUID& region_id,
	const LLVector3& position,
	U32 timestamp,
	const U8* binary_bucket,
	S32 binary_bucket_size)
{
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, from_id);
	msg->addUUIDFast(_PREHASH_SessionID, session_id);
	msg->nextBlockFast(_PREHASH_MessageBlock);
	msg->addBOOLFast(_PREHASH_FromGroup, from_group);
	msg->addUUIDFast(_PREHASH_ToAgentID, to_id);
	msg->addU32Fast(_PREHASH_ParentEstateID, parent_estate_id);
	msg->addUUIDFast(_PREHASH_RegionID, region_id);
	msg->addVector3Fast(_PREHASH_Position, position);
	msg->addU8Fast(_PREHASH_Offline, offline);
	msg->addU8Fast(_PREHASH_Dialog, (U8) dialog);
	msg->addUUIDFast(_PREHASH_ID, id);
	msg->addU32Fast(_PREHASH_Timestamp, timestamp);
	msg->addStringFast(_PREHASH_FromAgentName, name);
	S32 bytes_left = MTUBYTES;
	if(!message.empty())
	{
		char buffer[MTUBYTES];
		int num_written = snprintf(buffer, MTUBYTES, "%s", message.c_str());	/* Flawfinder: ignore */
		// snprintf returns number of bytes that would have been written
		// had the output not being truncated. In that case, it will
		// return either -1 or value >= passed in size value . So a check needs to be added
		// to detect truncation, and if there is any, only account for the
		// actual number of bytes written..and not what could have been
		// written.
		if (num_written < 0 || num_written >= MTUBYTES)
		{
			num_written = MTUBYTES - 1;
			llwarns << "pack_instant_message_block: message truncated: " << message << llendl;
		}

		bytes_left -= num_written;
		bytes_left = llmax(0, bytes_left);
		msg->addStringFast(_PREHASH_Message, buffer);
	}
	else
	{
		msg->addStringFast(_PREHASH_Message, NULL);
	}
	const U8* bb;
	if(binary_bucket)
	{
		bb = binary_bucket;
		binary_bucket_size = llmin(bytes_left, binary_bucket_size);
	}
	else
	{
		bb = (const U8*)EMPTY_BINARY_BUCKET;
		binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE;
	}
	msg->addBinaryDataFast(_PREHASH_BinaryBucket, bb, binary_bucket_size);
}

void LLIMInfo::unpackMessageBlock(LLMessageSystem* msg)
{
	msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, mFromID);
	msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, mFromGroup);
	msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, mToID);
	msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, mParentEstateID);
	msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, mRegionID);
	msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, mPosition);
	msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Offline, mOffline);
	U8 dialog;
	msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Dialog, dialog);
	mIMType = (EInstantMessage) dialog;
	msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, mID);
	msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_Timestamp, mTimeStamp);
	msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, mName);

	msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, mMessage);

	S32 binary_bucket_size = llmin(
		MTUBYTES,
		msg->getSizeFast(
			_PREHASH_MessageBlock,
			_PREHASH_BinaryBucket));
	if(binary_bucket_size > 0)
	{
		std::vector<U8> bucket;
		bucket.resize(binary_bucket_size);

		msg->getBinaryDataFast(
			_PREHASH_MessageBlock,
			_PREHASH_BinaryBucket,
			&bucket[0],
			0,
			0,
			binary_bucket_size);
		mData["binary_bucket"] = bucket;
	}
	else
	{
		mData.clear();
	}
}

LLSD im_info_to_llsd(LLPointer<LLIMInfo> im_info)
{
	LLSD param_version;
	param_version["version"] = 1;
	LLSD param_message;
	param_message["from_id"] = im_info->mFromID;
	param_message["from_group"] = im_info->mFromGroup;
	param_message["to_id"] = im_info->mToID;
	param_message["from_name"] = im_info->mName;
	param_message["message"] = im_info->mMessage;
	param_message["type"] = (S32)im_info->mIMType;
	param_message["id"] = im_info->mID;
	param_message["timestamp"] = (S32)im_info->mTimeStamp;
	param_message["offline"] = (S32)im_info->mOffline;
	param_message["parent_estate_id"] = (S32)im_info->mParentEstateID;
	param_message["region_id"] = im_info->mRegionID;
	param_message["position"] = ll_sd_from_vector3(im_info->mPosition);
	param_message["data"] = im_info->mData;
	param_message["source"]= im_info->mSource;
	param_message["ttl"] = im_info->mTTL;

	LLSD param_agent;
	param_agent["agent_id"] = im_info->mFromID;

	LLSD params;
	params["version_params"] = param_version;
	params["message_params"] = param_message;
	params["agent_params"] = param_agent;

	return params;
}

LLPointer<LLIMInfo> llsd_to_im_info(const LLSD& im_info_sd)
{
	LLSD param_message = im_info_sd["message_params"];
	LLSD param_agent = im_info_sd["agent_params"];

	LLPointer<LLIMInfo> im_info = new LLIMInfo(
		param_message["from_id"].asUUID(),
		param_message["from_group"].asBoolean(),
		param_message["to_id"].asUUID(),
		(EInstantMessage) param_message["type"].asInteger(),
		param_message["from_name"].asString(),
		param_message["message"].asString(),
		param_message["id"].asUUID(),
		(U32) param_message["parent_estate_id"].asInteger(),
		param_message["region_id"].asUUID(),
		ll_vector3_from_sd(param_message["position"]),
		param_message["data"],
		(U8) param_message["offline"].asInteger(),
		(U32) param_message["timestamp"].asInteger(),
		(EIMSource)param_message["source"].asInteger(),
		param_message["ttl"].asInteger());

	return im_info;
}

LLPointer<LLIMInfo> LLIMInfo::clone()
{
	return new LLIMInfo(
			mFromID,
			mFromGroup,
			mToID,
			mIMType,
			mName,
			mMessage,
			mID,
			mParentEstateID,
			mRegionID,
			mPosition,
			mData,
			mOffline,
			mTimeStamp,
			mSource,
			mTTL);
}

<|endoftext|>