<commit_before>// Copyright 2014 Toggl Desktop developers.

// NB! Setters should not directly calculate
// anything besides setting the field asked.
// This is because same setters are used when
// loading from database, JSON etc. If time entry
// needs to be recalculated after some user
// action, the recalculation should be started
// from context, using specific functions, not
// setters.

#include "../src/time_entry.h"

#include <sstream>
#include <algorithm>

#include <json/json.h>  // NOLINT

#include "./const.h"
#include "./formatter.h"
#include "./https_client.h"

#include "Poco/DateTime.h"
#include "Poco/LocalDateTime.h"
#include "Poco/Logger.h"
#include "Poco/NumberParser.h"
#include "Poco/Timestamp.h"

namespace toggl {

bool TimeEntry::ResolveError(const error err) {
    if (durationTooLarge(err) && Stop() && Start()) {
        Poco::UInt64 seconds =
            (std::min)(Stop() - Start(),
                       Poco::UInt64(kMaxTimeEntryDurationSeconds));
        SetDurationInSeconds(seconds);
        return true;
    }
    if (stopTimeMustBeAfterStartTime(err) && Stop() && Start()) {
        SetStop(Start() + DurationInSeconds());
        return true;
    }
    if (userCannotAccessWorkspace(err)) {
        SetWID(0);
        SetPID(0);
        SetTID(0);
        return true;
    }
    if (userCannotAccessTheSelectedProject(err)) {
        SetPID(0);
        SetTID(0);
        return true;
    }
    if (userCannotAccessSelectedTask(err)) {
        SetTID(0);
        return true;
    }
    if (billableIsAPremiumFeature(err)) {
        SetBillable(false);
        return true;
    }
    if (isMissingCreatedWith(err)) {
        SetCreatedWith(HTTPSClientConfig::UserAgent());
        return true;
    }
    return false;
}

bool TimeEntry::isMissingCreatedWith(const error err) const {
    return std::string::npos != std::string(err).find(
        "created_with needs to be provided an a valid string");
}

bool TimeEntry::userCannotAccessTheSelectedProject(
    const error err) const {
    return (std::string::npos != std::string(err).find(
        "User cannot access the selected project"));
}

bool TimeEntry::userCannotAccessSelectedTask(
    const error err) const {
    return (std::string::npos != std::string(err).find(
        "User cannot access selected task"));
}

bool TimeEntry::durationTooLarge(const error err) const {
    return (std::string::npos != std::string(err).find(
        "Max allowed duration per 1 time entry is 1000 hours"));
}

bool TimeEntry::stopTimeMustBeAfterStartTime(const error err) const {
    return (std::string::npos != std::string(err).find(
        "Stop time must be after start time"));
}

bool TimeEntry::billableIsAPremiumFeature(const error err) const {
    return (std::string::npos != std::string(err).find(
        "Billable is a premium feature"));
}

void TimeEntry::DiscardAt(const Poco::UInt64 at) {
    poco_assert(at);

    Poco::Int64 duration = at + DurationInSeconds();
    if (duration < 0) {
        duration = -1 * duration;
    }

    SetDurationInSeconds(duration);

    poco_assert(DurationInSeconds() >= 0);

    SetStop(at);

    SetUIModified();
}

void TimeEntry::StopTracking() {
    DiscardAt(time(0));
}

std::string TimeEntry::String() const {
    std::stringstream ss;
    ss  << "ID=" << ID()
        << " local_id=" << LocalID()
        << " description=" << description_
        << " wid=" << wid_
        << " guid=" << GUID()
        << " pid=" << pid_
        << " tid=" << tid_
        << " start=" << start_
        << " stop=" << stop_
        << " duration=" << duration_in_seconds_
        << " billable=" << billable_
        << " duronly=" << duronly_
        << " tags=" << Tags()
        << " created_with=" << CreatedWith()
        << " ui_modified_at=" << UIModifiedAt()
        << " deleted_at=" << DeletedAt()
        << " updated_at=" << UpdatedAt();
    return ss.str();
}

void TimeEntry::SetDurOnly(const bool value) {
    if (duronly_ != value) {
        duronly_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStart(const Poco::UInt64 value) {
    if (start_ != value) {
        start_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStop(const Poco::UInt64 value) {
    if (stop_ != value) {
        stop_ = value;
        SetDirty();
    }
}

void TimeEntry::SetDescription(const std::string value) {
    if (description_ != value) {
        description_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStopString(const std::string value) {
    SetStop(Formatter::Parse8601(value));
}

void TimeEntry::SetCreatedWith(const std::string value) {
    if (created_with_ != value) {
        created_with_ = value;
        SetDirty();
    }
}

void TimeEntry::SetBillable(const bool value) {
    if (billable_ != value) {
        billable_ = value;
        SetDirty();
    }
}

void TimeEntry::SetWID(const Poco::UInt64 value) {
    if (wid_ != value) {
        wid_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStopUserInput(const std::string value) {
    SetStopString(value);

    if (Stop() < Start()) {
        // Stop time cannot be before start time,
        // it'll get an error from backend.
        Poco::Timestamp ts =
            Poco::Timestamp::fromEpochTime(Stop()) + 1*Poco::Timespan::DAYS;
        SetStop(ts.epochTime());
    }

    poco_assert(Stop() >= Start());

    if (!IsTracking()) {
        SetDurationInSeconds(Stop() - Start());
    }

    if (Dirty()) {
        SetUIModified();
    }
}

void TimeEntry::SetTID(const Poco::UInt64 value) {
    if (tid_ != value) {
        tid_ = value;
        SetDirty();
    }
}

static const char kTagSeparator = '\t';

void TimeEntry::SetTags(const std::string tags) {
    if (Tags() != tags) {
        TagNames.clear();
        if (!tags.empty()) {
            std::stringstream ss(tags);
            while (ss.good()) {
                std::string tag;
                getline(ss, tag, kTagSeparator);
                TagNames.push_back(tag);
            }
        }
        SetDirty();
    }
}

void TimeEntry::SetPID(const Poco::UInt64 value) {
    if (pid_ != value) {
        pid_ = value;
        SetDirty();
    }
}

void TimeEntry::SetDurationInSeconds(const Poco::Int64 value) {
    if (duration_in_seconds_ != value) {
        duration_in_seconds_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStartUserInput(const std::string value) {
    Poco::Int64 start = Formatter::Parse8601(value);
    if (IsTracking()) {
        SetDurationInSeconds(-start);
    } else {
        SetStop(start + DurationInSeconds());
    }
    SetStart(start);

    if (Dirty()) {
        SetUIModified();
    }
}

void TimeEntry::SetStartString(const std::string value) {
    SetStart(Formatter::Parse8601(value));
}

void TimeEntry::SetDurationUserInput(const std::string value) {
    int seconds = Formatter::ParseDurationString(value);
    if (IsTracking()) {
        time_t now = time(0);
        time_t start = now - seconds;
        SetStart(start);
        SetDurationInSeconds(-start);
    } else {
        SetDurationInSeconds(seconds);
    }
    SetStop(Start() + seconds);

    if (Dirty()) {
        SetUIModified();
    }
}

void TimeEntry::SetProjectGUID(const std::string value) {
    if (project_guid_ != value) {
        project_guid_ = value;
        SetDirty();
    }
}

const std::string TimeEntry::Tags() const {
    std::stringstream ss;
    for (std::vector<std::string>::const_iterator it =
        TagNames.begin();
            it != TagNames.end();
            it++) {
        if (it != TagNames.begin()) {
            ss << kTagSeparator;
        }
        ss << *it;
    }
    return ss.str();
}

std::string TimeEntry::DateHeaderString() const {
    return Formatter::FormatDateHeader(start_);
}

std::string TimeEntry::StopString() const {
    return Formatter::Format8601(stop_);
}

std::string TimeEntry::StartString() const {
    return Formatter::Format8601(start_);
}

bool TimeEntry::IsToday() const {
    Poco::Timestamp ts = Poco::Timestamp::fromEpochTime(Start());
    Poco::LocalDateTime datetime(ts);
    Poco::LocalDateTime today;
    return today.year() == datetime.year() &&
           today.month() == datetime.month() &&
           today.day() == datetime.day();
}

void TimeEntry::LoadFromJSON(Json::Value data) {
    Json::Value modified = data["ui_modified_at"];
    Poco::UInt64 ui_modified_at(0);
    if (modified.isString()) {
        ui_modified_at = Poco::NumberParser::parseUnsigned64(
            modified.asString());
    } else {
        ui_modified_at = modified.asUInt64();
    }
    if (UIModifiedAt() > ui_modified_at) {
        std::stringstream ss;
        ss  << "Will not overwrite time entry "
            << String()
            << " with server data because we have a newer ui_modified_at";
        logger().debug(ss.str());
        return;
    }

    if (data.isMember("guid")) {
        SetGUID(data["guid"].asString());
    }

    if (data.isMember("tags")) {
        loadTagsFromJSON(data["tags"]);
    }

    if (data.isMember("created_with")) {
        SetCreatedWith(data["created_with"].asString());
    }

    if (data.isMember("id")) {
        SetID(data["id"].asUInt64());
    }

    SetDescription(data["description"].asString());

    if (data.isMember("wid")) {
        SetWID(data["wid"].asUInt64());
    } else {
        SetWID(0);
    }
    if (data.isMember("pid")) {
        SetPID(data["pid"].asUInt64());
    } else {
        SetPID(0);
    }
    if (data.isMember("tid")) {
        SetTID(data["tid"].asUInt64());
    } else {
        SetTID(0);
    }
    SetStartString(data["start"].asString());
    SetStopString(data["stop"].asString());
    SetDurationInSeconds(data["duration"].asInt64());
    SetBillable(data["billable"].asBool());
    SetDurOnly(data["duronly"].asBool());
    SetUpdatedAtString(data["at"].asString());

    SetUIModifiedAt(0);
}

Json::Value TimeEntry::SaveToJSON() const {
    Json::Value n;
    if (ID()) {
        n["id"] = Json::UInt64(ID());
    }
    n["description"] = Formatter::EscapeJSONString(Description());
    // Workspace ID can't be 0 on server side. So don't
    // send 0 if we have no default workspace ID, because
    // NULL is not 0
    if (WID()) {
        n["wid"] = Json::UInt64(WID());
    }
    n["guid"] = GUID();
    if (!PID() && !ProjectGUID().empty()) {
        n["pid"] = ProjectGUID();
    } else {
        n["pid"] = Json::UInt64(PID());
    }
    n["tid"] = Json::UInt64(TID());
    n["start"] = StartString();
    if (Stop()) {
        n["stop"] = StopString();
    }
    n["duration"] = Json::Int64(DurationInSeconds());
    n["billable"] = Billable();
    n["duronly"] = DurOnly();
    n["ui_modified_at"] = Json::UInt64(UIModifiedAt());
    n["created_with"] = Formatter::EscapeJSONString(CreatedWith());

    Json::Value tag_nodes;
    for (std::vector<std::string>::const_iterator it = TagNames.begin();
            it != TagNames.end();
            it++) {
        std::string tag_name = Formatter::EscapeJSONString(*it);
        tag_nodes.append(Json::Value(tag_name));
    }
    n["tags"] = tag_nodes;

    return n;
}

Poco::UInt64 TimeEntry::AbsDuration(const Poco::Int64 value) {
    Poco::Int64 duration = value;

    // Duration is negative when time is tracking
    if (duration < 0) {
        duration += time(0);
    }
    // If after calculation time is still negative,
    // either computer clock is wrong or user
    // has set start time to the future. Render positive
    // duration only:
    if (duration < 0) {
        duration *= -1;
    }

    return static_cast<Poco::UInt64>(duration);
}

void TimeEntry::loadTagsFromJSON(Json::Value list) {
    TagNames.clear();

    for (unsigned int i = 0; i < list.size(); i++) {
        std::string tag = list[i].asString();
        if (!tag.empty()) {
            TagNames.push_back(tag);
        }
    }
}

}   // namespace toggl
<commit_msg> Fixed typo in error message (lib)<commit_after>// Copyright 2014 Toggl Desktop developers.

// NB! Setters should not directly calculate
// anything besides setting the field asked.
// This is because same setters are used when
// loading from database, JSON etc. If time entry
// needs to be recalculated after some user
// action, the recalculation should be started
// from context, using specific functions, not
// setters.

#include "../src/time_entry.h"

#include <sstream>
#include <algorithm>

#include <json/json.h>  // NOLINT

#include "./const.h"
#include "./formatter.h"
#include "./https_client.h"

#include "Poco/DateTime.h"
#include "Poco/LocalDateTime.h"
#include "Poco/Logger.h"
#include "Poco/NumberParser.h"
#include "Poco/Timestamp.h"

namespace toggl {

bool TimeEntry::ResolveError(const error err) {
    if (durationTooLarge(err) && Stop() && Start()) {
        Poco::UInt64 seconds =
            (std::min)(Stop() - Start(),
                       Poco::UInt64(kMaxTimeEntryDurationSeconds));
        SetDurationInSeconds(seconds);
        return true;
    }
    if (stopTimeMustBeAfterStartTime(err) && Stop() && Start()) {
        SetStop(Start() + DurationInSeconds());
        return true;
    }
    if (userCannotAccessWorkspace(err)) {
        SetWID(0);
        SetPID(0);
        SetTID(0);
        return true;
    }
    if (userCannotAccessTheSelectedProject(err)) {
        SetPID(0);
        SetTID(0);
        return true;
    }
    if (userCannotAccessSelectedTask(err)) {
        SetTID(0);
        return true;
    }
    if (billableIsAPremiumFeature(err)) {
        SetBillable(false);
        return true;
    }
    if (isMissingCreatedWith(err)) {
        SetCreatedWith(HTTPSClientConfig::UserAgent());
        return true;
    }
    return false;
}

bool TimeEntry::isMissingCreatedWith(const error err) const {
    return std::string::npos != std::string(err).find(
        "created_with needs to be provided a valid string");
}

bool TimeEntry::userCannotAccessTheSelectedProject(
    const error err) const {
    return (std::string::npos != std::string(err).find(
        "User cannot access the selected project"));
}

bool TimeEntry::userCannotAccessSelectedTask(
    const error err) const {
    return (std::string::npos != std::string(err).find(
        "User cannot access selected task"));
}

bool TimeEntry::durationTooLarge(const error err) const {
    return (std::string::npos != std::string(err).find(
        "Max allowed duration per 1 time entry is 1000 hours"));
}

bool TimeEntry::stopTimeMustBeAfterStartTime(const error err) const {
    return (std::string::npos != std::string(err).find(
        "Stop time must be after start time"));
}

bool TimeEntry::billableIsAPremiumFeature(const error err) const {
    return (std::string::npos != std::string(err).find(
        "Billable is a premium feature"));
}

void TimeEntry::DiscardAt(const Poco::UInt64 at) {
    poco_assert(at);

    Poco::Int64 duration = at + DurationInSeconds();
    if (duration < 0) {
        duration = -1 * duration;
    }

    SetDurationInSeconds(duration);

    poco_assert(DurationInSeconds() >= 0);

    SetStop(at);

    SetUIModified();
}

void TimeEntry::StopTracking() {
    DiscardAt(time(0));
}

std::string TimeEntry::String() const {
    std::stringstream ss;
    ss  << "ID=" << ID()
        << " local_id=" << LocalID()
        << " description=" << description_
        << " wid=" << wid_
        << " guid=" << GUID()
        << " pid=" << pid_
        << " tid=" << tid_
        << " start=" << start_
        << " stop=" << stop_
        << " duration=" << duration_in_seconds_
        << " billable=" << billable_
        << " duronly=" << duronly_
        << " tags=" << Tags()
        << " created_with=" << CreatedWith()
        << " ui_modified_at=" << UIModifiedAt()
        << " deleted_at=" << DeletedAt()
        << " updated_at=" << UpdatedAt();
    return ss.str();
}

void TimeEntry::SetDurOnly(const bool value) {
    if (duronly_ != value) {
        duronly_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStart(const Poco::UInt64 value) {
    if (start_ != value) {
        start_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStop(const Poco::UInt64 value) {
    if (stop_ != value) {
        stop_ = value;
        SetDirty();
    }
}

void TimeEntry::SetDescription(const std::string value) {
    if (description_ != value) {
        description_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStopString(const std::string value) {
    SetStop(Formatter::Parse8601(value));
}

void TimeEntry::SetCreatedWith(const std::string value) {
    if (created_with_ != value) {
        created_with_ = value;
        SetDirty();
    }
}

void TimeEntry::SetBillable(const bool value) {
    if (billable_ != value) {
        billable_ = value;
        SetDirty();
    }
}

void TimeEntry::SetWID(const Poco::UInt64 value) {
    if (wid_ != value) {
        wid_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStopUserInput(const std::string value) {
    SetStopString(value);

    if (Stop() < Start()) {
        // Stop time cannot be before start time,
        // it'll get an error from backend.
        Poco::Timestamp ts =
            Poco::Timestamp::fromEpochTime(Stop()) + 1*Poco::Timespan::DAYS;
        SetStop(ts.epochTime());
    }

    poco_assert(Stop() >= Start());

    if (!IsTracking()) {
        SetDurationInSeconds(Stop() - Start());
    }

    if (Dirty()) {
        SetUIModified();
    }
}

void TimeEntry::SetTID(const Poco::UInt64 value) {
    if (tid_ != value) {
        tid_ = value;
        SetDirty();
    }
}

static const char kTagSeparator = '\t';

void TimeEntry::SetTags(const std::string tags) {
    if (Tags() != tags) {
        TagNames.clear();
        if (!tags.empty()) {
            std::stringstream ss(tags);
            while (ss.good()) {
                std::string tag;
                getline(ss, tag, kTagSeparator);
                TagNames.push_back(tag);
            }
        }
        SetDirty();
    }
}

void TimeEntry::SetPID(const Poco::UInt64 value) {
    if (pid_ != value) {
        pid_ = value;
        SetDirty();
    }
}

void TimeEntry::SetDurationInSeconds(const Poco::Int64 value) {
    if (duration_in_seconds_ != value) {
        duration_in_seconds_ = value;
        SetDirty();
    }
}

void TimeEntry::SetStartUserInput(const std::string value) {
    Poco::Int64 start = Formatter::Parse8601(value);
    if (IsTracking()) {
        SetDurationInSeconds(-start);
    } else {
        SetStop(start + DurationInSeconds());
    }
    SetStart(start);

    if (Dirty()) {
        SetUIModified();
    }
}

void TimeEntry::SetStartString(const std::string value) {
    SetStart(Formatter::Parse8601(value));
}

void TimeEntry::SetDurationUserInput(const std::string value) {
    int seconds = Formatter::ParseDurationString(value);
    if (IsTracking()) {
        time_t now = time(0);
        time_t start = now - seconds;
        SetStart(start);
        SetDurationInSeconds(-start);
    } else {
        SetDurationInSeconds(seconds);
    }
    SetStop(Start() + seconds);

    if (Dirty()) {
        SetUIModified();
    }
}

void TimeEntry::SetProjectGUID(const std::string value) {
    if (project_guid_ != value) {
        project_guid_ = value;
        SetDirty();
    }
}

const std::string TimeEntry::Tags() const {
    std::stringstream ss;
    for (std::vector<std::string>::const_iterator it =
        TagNames.begin();
            it != TagNames.end();
            it++) {
        if (it != TagNames.begin()) {
            ss << kTagSeparator;
        }
        ss << *it;
    }
    return ss.str();
}

std::string TimeEntry::DateHeaderString() const {
    return Formatter::FormatDateHeader(start_);
}

std::string TimeEntry::StopString() const {
    return Formatter::Format8601(stop_);
}

std::string TimeEntry::StartString() const {
    return Formatter::Format8601(start_);
}

bool TimeEntry::IsToday() const {
    Poco::Timestamp ts = Poco::Timestamp::fromEpochTime(Start());
    Poco::LocalDateTime datetime(ts);
    Poco::LocalDateTime today;
    return today.year() == datetime.year() &&
           today.month() == datetime.month() &&
           today.day() == datetime.day();
}

void TimeEntry::LoadFromJSON(Json::Value data) {
    Json::Value modified = data["ui_modified_at"];
    Poco::UInt64 ui_modified_at(0);
    if (modified.isString()) {
        ui_modified_at = Poco::NumberParser::parseUnsigned64(
            modified.asString());
    } else {
        ui_modified_at = modified.asUInt64();
    }
    if (UIModifiedAt() > ui_modified_at) {
        std::stringstream ss;
        ss  << "Will not overwrite time entry "
            << String()
            << " with server data because we have a newer ui_modified_at";
        logger().debug(ss.str());
        return;
    }

    if (data.isMember("guid")) {
        SetGUID(data["guid"].asString());
    }

    if (data.isMember("tags")) {
        loadTagsFromJSON(data["tags"]);
    }

    if (data.isMember("created_with")) {
        SetCreatedWith(data["created_with"].asString());
    }

    if (data.isMember("id")) {
        SetID(data["id"].asUInt64());
    }

    SetDescription(data["description"].asString());

    if (data.isMember("wid")) {
        SetWID(data["wid"].asUInt64());
    } else {
        SetWID(0);
    }
    if (data.isMember("pid")) {
        SetPID(data["pid"].asUInt64());
    } else {
        SetPID(0);
    }
    if (data.isMember("tid")) {
        SetTID(data["tid"].asUInt64());
    } else {
        SetTID(0);
    }
    SetStartString(data["start"].asString());
    SetStopString(data["stop"].asString());
    SetDurationInSeconds(data["duration"].asInt64());
    SetBillable(data["billable"].asBool());
    SetDurOnly(data["duronly"].asBool());
    SetUpdatedAtString(data["at"].asString());

    SetUIModifiedAt(0);
}

Json::Value TimeEntry::SaveToJSON() const {
    Json::Value n;
    if (ID()) {
        n["id"] = Json::UInt64(ID());
    }
    n["description"] = Formatter::EscapeJSONString(Description());
    // Workspace ID can't be 0 on server side. So don't
    // send 0 if we have no default workspace ID, because
    // NULL is not 0
    if (WID()) {
        n["wid"] = Json::UInt64(WID());
    }
    n["guid"] = GUID();
    if (!PID() && !ProjectGUID().empty()) {
        n["pid"] = ProjectGUID();
    } else {
        n["pid"] = Json::UInt64(PID());
    }
    n["tid"] = Json::UInt64(TID());
    n["start"] = StartString();
    if (Stop()) {
        n["stop"] = StopString();
    }
    n["duration"] = Json::Int64(DurationInSeconds());
    n["billable"] = Billable();
    n["duronly"] = DurOnly();
    n["ui_modified_at"] = Json::UInt64(UIModifiedAt());
    n["created_with"] = Formatter::EscapeJSONString(CreatedWith());

    Json::Value tag_nodes;
    for (std::vector<std::string>::const_iterator it = TagNames.begin();
            it != TagNames.end();
            it++) {
        std::string tag_name = Formatter::EscapeJSONString(*it);
        tag_nodes.append(Json::Value(tag_name));
    }
    n["tags"] = tag_nodes;

    return n;
}

Poco::UInt64 TimeEntry::AbsDuration(const Poco::Int64 value) {
    Poco::Int64 duration = value;

    // Duration is negative when time is tracking
    if (duration < 0) {
        duration += time(0);
    }
    // If after calculation time is still negative,
    // either computer clock is wrong or user
    // has set start time to the future. Render positive
    // duration only:
    if (duration < 0) {
        duration *= -1;
    }

    return static_cast<Poco::UInt64>(duration);
}

void TimeEntry::loadTagsFromJSON(Json::Value list) {
    TagNames.clear();

    for (unsigned int i = 0; i < list.size(); i++) {
        std::string tag = list[i].asString();
        if (!tag.empty()) {
            TagNames.push_back(tag);
        }
    }
}

}   // namespace toggl
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
/// \file  timestamp.cpp
//----------------------------------------------------------------------------
/// \brief Implementation of timestamp class.
//----------------------------------------------------------------------------
// Copyright (c) 2011 Serge Aleynikov <saleyn@gmail.com>
// Created: 2011-09-10
//----------------------------------------------------------------------------
/*
***** BEGIN LICENSE BLOCK *****

This file is part of the utxx open-source project.

Copyright (C) 2011 Serge Aleynikov <saleyn@gmail.com>

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

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

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

***** END LICENSE BLOCK *****
*/

#include <utxx/convert.hpp>
#include <utxx/timestamp.hpp>
#include <utxx/compiler_hints.hpp>
#include <utxx/string.hpp>
#include <utxx/time.hpp>
#include <utxx/error.hpp>
#include <stdio.h>

namespace utxx {

boost::mutex            timestamp::s_mutex;
thread_local long       timestamp::s_next_local_midnight_nseconds = 0;
thread_local long       timestamp::s_next_utc_midnight_nseconds   = 0;
thread_local time_t     timestamp::s_utc_nsec_offset              = 0;
thread_local char       timestamp::s_local_timestamp[16];
thread_local char       timestamp::s_utc_timestamp[16];
thread_local char       timestamp::s_local_timezone[8];

#ifdef DEBUG_TIMESTAMP
volatile long timestamp::s_hrcalls;
volatile long timestamp::s_syscalls;
#endif

namespace {
    static const char* s_values[] = {
        "none", "date", "date-time",
        "date-time-msec", "date-time-usec", "date-time-nsec",
        "time", "time-msec", "time-usec",   "time-nsec"
    };
}

stamp_type parse_stamp_type(const std::string& a_line) {
    stamp_type t = find_index<stamp_type>(s_values, a_line, (stamp_type)-1, true);

    if (int(t) == -1)
        throw badarg_error("parse_stamp_tpe: invalid timestamp type: ", a_line);
    return t;
}

const char* to_string(stamp_type a_type) {
    assert(size_t(a_type) < length(s_values));
    return s_values[a_type];
}

char* timestamp::write_date(char* a_buf, time_t a_utc_seconds, bool a_utc,
                            size_t eos_pos, char a_sep, bool a_use_cached_date)
{
    long nsec = a_utc_seconds*1000000000L;

    // If not same day - update cached string value
    if (unlikely(nsec >= s_next_utc_midnight_nseconds))
        update_midnight_nseconds(now_utc());

    auto today_utc_midnight = s_next_utc_midnight_nseconds - 86400000000000L;

    if (a_sep || !a_use_cached_date || nsec < today_utc_midnight)
        return internal_write_date(a_buf, a_utc_seconds, a_utc, eos_pos, a_sep);
    else {
        strncpy(a_buf, a_utc ? s_utc_timestamp : s_local_timestamp, 9);
        if (eos_pos) { a_buf[eos_pos] = '\0'; return a_buf + eos_pos; }
        return a_buf + 9;
    }
}

void timestamp::update_midnight_nseconds(time_val a_now)
{
    auto s = a_now.sec();
    struct tm tm;
    localtime_r(&s, &tm);
    s_utc_nsec_offset = tm.tm_gmtoff * 1000000000L;
    auto now_midnight_nsecs   = a_now.nanoseconds() -
                                a_now.nanoseconds() % (86400L * 1000000000L);
    auto local_midnight_nsecs = now_midnight_nsecs - s_utc_nsec_offset;

    s_next_utc_midnight_nseconds   = now_midnight_nsecs + 86400L * 1000000000L;
    s_next_local_midnight_nseconds = a_now.nanoseconds() >= local_midnight_nsecs
                                   ? (s_next_utc_midnight_nseconds - s_utc_nsec_offset)
                                   : local_midnight_nsecs;

    strncpy(s_local_timezone, tm.tm_zone, sizeof(s_local_timezone)-1);
    s_local_timezone[sizeof(s_local_timezone)-1] = '\0';

    // the mutex is not needed here at all - s_timestamp lives in TLS storage
    internal_write_date(s_local_timestamp, s, false, 9, '\0');
    internal_write_date(s_utc_timestamp,   s, true,  9, '\0');
}

size_t timestamp::format_size(stamp_type a_tp)
{
    switch (a_tp) {
        case NO_TIMESTAMP:          return 0;
        case TIME:                  return 8;
        case TIME_WITH_MSEC:        return 12;
        case TIME_WITH_USEC:        return 15;
        case TIME_WITH_NSEC:        return 18;
        case DATE:                  return 8;
        case DATE_TIME:             return 17;
        case DATE_TIME_WITH_MSEC:   return 21;
        case DATE_TIME_WITH_USEC:   return 24;
        case DATE_TIME_WITH_NSEC:   return 27;
        default:                    break;
    }
    assert(false);  // should never get here
    return 0;
}

int timestamp::format(stamp_type a_tp, time_val tv, char* a_buf, size_t a_sz,
                      bool a_utc, bool a_day_chk, bool a_use_cached_date)
{
    BOOST_ASSERT((a_tp < DATE_TIME && a_sz > 17) || a_sz > 27);

    if (unlikely(a_tp == NO_TIMESTAMP)) {
        a_buf[0] = '\0';
        return 0;
    }

    auto pair = tv.split();

    // If small time is given, it's a relative value.
    bool rel = pair.first < 86400L;
    auto now = a_day_chk || rel ? update().split() : pair;

    if (rel)
        pair.first += now.first;

    auto sec = a_utc ?  pair.first
                     : (pair.first + s_utc_nsec_offset / 1000000000L);
    char* p;

    switch (a_tp) {
        case TIME:
        case TIME_WITH_MSEC:
        case TIME_WITH_USEC:
        case TIME_WITH_NSEC: {
            p  = time_val::write_time(sec, pair.second, a_buf, a_tp);
            return p - a_buf;
        }
        case DATE:
            p = write_date(a_buf, sec, a_utc, 8, '\0', a_use_cached_date);
            return 8;
        case DATE_TIME:
        case DATE_TIME_WITH_MSEC:
        case DATE_TIME_WITH_USEC:
        case DATE_TIME_WITH_NSEC: {
            p = write_date(a_buf, sec, a_utc, 0, '\0', a_use_cached_date);
            p = time_val::write_time(sec, pair.second, p, stamp_type(a_tp+4));
            return p - a_buf;
        }
        default:
            strcpy(a_buf, "UNDEFINED");
            return -1;
    }
}

time_val timestamp::from_string(const char* a_datetime, size_t n, bool a_utc) {
    if (unlikely(n < 8 ||
                (n > 8 && (n < 17 || a_datetime[8]  != '-' ||
                           a_datetime[11] != ':' || a_datetime[14] != ':'))))
        throw badarg_error("Invalid time format: ", std::string(a_datetime, n));

    const char* p = a_datetime;

    auto parse = [&p](int digits) {
        int i = 0; const char* end = p + digits;
        for (; p != end; ++p) i = (10*i) + (*p - '0');
        return i;
    };

    unsigned hour = 0, min = 0, sec = 0;
    long     nsec = 0;
    int      year = parse(4);
    unsigned mon  = parse(2);
    unsigned day  = parse(2);
    if (n > 8) {
        p++;
        hour = parse(2); p++;
        min  = parse(2); p++;
        sec  = parse(2);

        const char* dot = p++;

        if (*dot == '.' && n > 17)
        {
            const char* end = p + std::min<size_t>(6, n - (p - a_datetime));
            while (*p >= '0' && *p <= '9' && p != end)
                nsec = 10*nsec + (*p++ - '0');

            int len = p - dot - 1;
            switch (len)
            {
                case 3:  nsec *= 1000000; break;
                case 6:  nsec *= 1000;    break;
                case 9:  break;
                default: throw badarg_error("Invalid microsecond format: ",
                                            std::string(a_datetime, end - a_datetime));
            }
        }
    }

    return (a_utc
          ? time_val::universal_time(year, mon, day, hour, min, sec, 0)
          : time_val::local_time(year, mon, day, hour, min, sec, 0)) + nsecs(nsec);
}

} // namespace utxx
<commit_msg>Fix size check<commit_after>//----------------------------------------------------------------------------
/// \file  timestamp.cpp
//----------------------------------------------------------------------------
/// \brief Implementation of timestamp class.
//----------------------------------------------------------------------------
// Copyright (c) 2011 Serge Aleynikov <saleyn@gmail.com>
// Created: 2011-09-10
//----------------------------------------------------------------------------
/*
***** BEGIN LICENSE BLOCK *****

This file is part of the utxx open-source project.

Copyright (C) 2011 Serge Aleynikov <saleyn@gmail.com>

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

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

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

***** END LICENSE BLOCK *****
*/

#include <utxx/convert.hpp>
#include <utxx/timestamp.hpp>
#include <utxx/compiler_hints.hpp>
#include <utxx/string.hpp>
#include <utxx/time.hpp>
#include <utxx/error.hpp>
#include <stdio.h>

namespace utxx {

boost::mutex            timestamp::s_mutex;
thread_local long       timestamp::s_next_local_midnight_nseconds = 0;
thread_local long       timestamp::s_next_utc_midnight_nseconds   = 0;
thread_local time_t     timestamp::s_utc_nsec_offset              = 0;
thread_local char       timestamp::s_local_timestamp[16];
thread_local char       timestamp::s_utc_timestamp[16];
thread_local char       timestamp::s_local_timezone[8];

#ifdef DEBUG_TIMESTAMP
volatile long timestamp::s_hrcalls;
volatile long timestamp::s_syscalls;
#endif

namespace {
    static const char* s_values[] = {
        "none", "date", "date-time",
        "date-time-msec", "date-time-usec", "date-time-nsec",
        "time", "time-msec", "time-usec",   "time-nsec"
    };
}

stamp_type parse_stamp_type(const std::string& a_line) {
    stamp_type t = find_index<stamp_type>(s_values, a_line, (stamp_type)-1, true);

    if (int(t) == -1)
        throw badarg_error("parse_stamp_tpe: invalid timestamp type: ", a_line);
    return t;
}

const char* to_string(stamp_type a_type) {
    assert(size_t(a_type) < length(s_values));
    return s_values[a_type];
}

char* timestamp::write_date(char* a_buf, time_t a_utc_seconds, bool a_utc,
                            size_t eos_pos, char a_sep, bool a_use_cached_date)
{
    long nsec = a_utc_seconds*1000000000L;

    // If not same day - update cached string value
    if (unlikely(nsec >= s_next_utc_midnight_nseconds))
        update_midnight_nseconds(now_utc());

    auto today_utc_midnight = s_next_utc_midnight_nseconds - 86400000000000L;

    if (a_sep || !a_use_cached_date || nsec < today_utc_midnight)
        return internal_write_date(a_buf, a_utc_seconds, a_utc, eos_pos, a_sep);
    else {
        strncpy(a_buf, a_utc ? s_utc_timestamp : s_local_timestamp, 9);
        if (eos_pos) { a_buf[eos_pos] = '\0'; return a_buf + eos_pos; }
        return a_buf + 9;
    }
}

void timestamp::update_midnight_nseconds(time_val a_now)
{
    auto s = a_now.sec();
    struct tm tm;
    localtime_r(&s, &tm);
    s_utc_nsec_offset = tm.tm_gmtoff * 1000000000L;
    auto now_midnight_nsecs   = a_now.nanoseconds() -
                                a_now.nanoseconds() % (86400L * 1000000000L);
    auto local_midnight_nsecs = now_midnight_nsecs - s_utc_nsec_offset;

    s_next_utc_midnight_nseconds   = now_midnight_nsecs + 86400L * 1000000000L;
    s_next_local_midnight_nseconds = a_now.nanoseconds() >= local_midnight_nsecs
                                   ? (s_next_utc_midnight_nseconds - s_utc_nsec_offset)
                                   : local_midnight_nsecs;

    strncpy(s_local_timezone, tm.tm_zone, sizeof(s_local_timezone)-1);
    s_local_timezone[sizeof(s_local_timezone)-1] = '\0';

    // the mutex is not needed here at all - s_timestamp lives in TLS storage
    internal_write_date(s_local_timestamp, s, false, 9, '\0');
    internal_write_date(s_utc_timestamp,   s, true,  9, '\0');
}

size_t timestamp::format_size(stamp_type a_tp)
{
    switch (a_tp) {
        case NO_TIMESTAMP:          return 0;
        case TIME:                  return 8;
        case TIME_WITH_MSEC:        return 12;
        case TIME_WITH_USEC:        return 15;
        case TIME_WITH_NSEC:        return 18;
        case DATE:                  return 8;
        case DATE_TIME:             return 17;
        case DATE_TIME_WITH_MSEC:   return 21;
        case DATE_TIME_WITH_USEC:   return 24;
        case DATE_TIME_WITH_NSEC:   return 27;
        default:                    break;
    }
    assert(false);  // should never get here
    return 0;
}

int timestamp::format(stamp_type a_tp, time_val tv, char* a_buf, size_t a_sz,
                      bool a_utc, bool a_day_chk, bool a_use_cached_date)
{
    BOOST_ASSERT(a_sz >= format_size(a_tp));

    if (unlikely(a_tp == NO_TIMESTAMP)) {
        a_buf[0] = '\0';
        return 0;
    }

    auto pair = tv.split();

    // If small time is given, it's a relative value.
    bool rel = pair.first < 86400L;
    auto now = a_day_chk || rel ? update().split() : pair;

    if (rel)
        pair.first += now.first;

    auto sec = a_utc ?  pair.first
                     : (pair.first + s_utc_nsec_offset / 1000000000L);
    char* p;

    switch (a_tp) {
        case TIME:
        case TIME_WITH_MSEC:
        case TIME_WITH_USEC:
        case TIME_WITH_NSEC: {
            p  = time_val::write_time(sec, pair.second, a_buf, a_tp);
            return p - a_buf;
        }
        case DATE:
            p = write_date(a_buf, sec, a_utc, 8, '\0', a_use_cached_date);
            return 8;
        case DATE_TIME:
        case DATE_TIME_WITH_MSEC:
        case DATE_TIME_WITH_USEC:
        case DATE_TIME_WITH_NSEC: {
            p = write_date(a_buf, sec, a_utc, 0, '\0', a_use_cached_date);
            p = time_val::write_time(sec, pair.second, p, stamp_type(a_tp+4));
            return p - a_buf;
        }
        default:
            strcpy(a_buf, "UNDEFINED");
            return -1;
    }
}

time_val timestamp::from_string(const char* a_datetime, size_t n, bool a_utc) {
    if (unlikely(n < 8 ||
                (n > 8 && (n < 17 || a_datetime[8]  != '-' ||
                           a_datetime[11] != ':' || a_datetime[14] != ':'))))
        throw badarg_error("Invalid time format: ", std::string(a_datetime, n));

    const char* p = a_datetime;

    auto parse = [&p](int digits) {
        int i = 0; const char* end = p + digits;
        for (; p != end; ++p) i = (10*i) + (*p - '0');
        return i;
    };

    unsigned hour = 0, min = 0, sec = 0;
    long     nsec = 0;
    int      year = parse(4);
    unsigned mon  = parse(2);
    unsigned day  = parse(2);
    if (n > 8) {
        p++;
        hour = parse(2); p++;
        min  = parse(2); p++;
        sec  = parse(2);

        const char* dot = p++;

        if (*dot == '.' && n > 17)
        {
            const char* end = p + std::min<size_t>(6, n - (p - a_datetime));
            while (*p >= '0' && *p <= '9' && p != end)
                nsec = 10*nsec + (*p++ - '0');

            int len = p - dot - 1;
            switch (len)
            {
                case 3:  nsec *= 1000000; break;
                case 6:  nsec *= 1000;    break;
                case 9:  break;
                default: throw badarg_error("Invalid microsecond format: ",
                                            std::string(a_datetime, end - a_datetime));
            }
        }
    }

    return (a_utc
          ? time_val::universal_time(year, mon, day, hour, min, sec, 0)
          : time_val::local_time(year, mon, day, hour, min, sec, 0)) + nsecs(nsec);
}

} // namespace utxx
<|endoftext|>


<commit_before>#line 2 "togo/hash.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file hash.hpp
@brief Hashing utilities.
@ingroup hash
*/

#pragma once

#include <togo/config.hpp>
#include <togo/debug_constraints.hpp>
#include <togo/types.hpp>

#include <am/hash/fnv.hpp>

namespace togo {
namespace hash {

/**
	@addtogroup hash
	@{
*/

// TODO: Build constexpr version of MurmurHash2 (both lengths) and
// use it instead of FNV-1a

namespace {
	static constexpr am::hash::HashLength const
	hash32_length = am::hash::HashLength::HL32,
	hash64_length = am::hash::HashLength::HL64;
}

/** @cond INTERNAL */
TOGO_CONSTRAIN_SAME(hash32, am::detail::hash::fnv_hash_type<hash32_length>);
TOGO_CONSTRAIN_SAME(hash64, am::detail::hash::fnv_hash_type<hash64_length>);
/** @endcond */

/**
	Calculate 32-bit hash.
*/
inline hash32
calc32(
	char const* const data,
	unsigned const size
) {
	return
		size != 0
		? am::hash::fnv1a<hash32_length>(data, size)
		: hash32{0}
	;
}

/**
	Calculate 64-bit hash.
*/
inline hash64
calc64(
	char const* const data,
	unsigned const size
) {
	return
		size != 0
		? am::hash::fnv1a<hash64_length>(data, size)
		: hash64{0}
	;
}

/**
	32-bit hash literal.
*/
inline constexpr hash32
operator"" _hash32(
	char const* const data,
	std::size_t const size
) {
	return
		size != 0
		? am::hash::fnv1a_c<hash32_length>(data, size)
		: hash32{0}
	;
}

/**
	64-bit hash literal.
*/
inline constexpr hash64
operator"" _hash64(
	char const* const data,
	std::size_t const size
) {
	return
		size != 0
		? am::hash::fnv1a_c<hash64_length>(data, size)
		: hash64{0}
	;
}

/// 32-bit hash identity (hash of nothing).
static constexpr hash32 const
IDENTITY32{0}; // = ""_hash32;

/// 64-bit hash identity (hash of nothing).
static constexpr hash64 const
IDENTITY64{0}; // = ""_hash64;

/** @} */ // end of doc-group hash

} // namespace hash
} // namespace togo
<commit_msg>hash: added calc overloads for StringRef.<commit_after>#line 2 "togo/hash.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file hash.hpp
@brief Hashing utilities.
@ingroup hash
*/

#pragma once

#include <togo/config.hpp>
#include <togo/debug_constraints.hpp>
#include <togo/types.hpp>
#include <togo/string.hpp>

#include <am/hash/fnv.hpp>

namespace togo {
namespace hash {

/**
	@addtogroup hash
	@{
*/

// TODO: Build constexpr version of MurmurHash2 (both lengths) and
// use it instead of FNV-1a

namespace {
	static constexpr am::hash::HashLength const
	hash32_length = am::hash::HashLength::HL32,
	hash64_length = am::hash::HashLength::HL64;
}

/** @cond INTERNAL */
TOGO_CONSTRAIN_SAME(hash32, am::detail::hash::fnv_hash_type<hash32_length>);
TOGO_CONSTRAIN_SAME(hash64, am::detail::hash::fnv_hash_type<hash64_length>);
/** @endcond */

/**
	Calculate 32-bit hash.
*/
inline hash32
calc32(
	char const* const data,
	unsigned const size
) {
	return
		size != 0
		? am::hash::fnv1a<hash32_length>(data, size)
		: hash32{0}
	;
}

/**
	Calculate 32-bit hash from string reference.
*/
inline hash32 calc32(StringRef const& ref) {
	return hash::calc32(ref.data, ref.size);
}

/**
	Calculate 64-bit hash.
*/
inline hash64
calc64(
	char const* const data,
	unsigned const size
) {
	return
		size != 0
		? am::hash::fnv1a<hash64_length>(data, size)
		: hash64{0}
	;
}

/**
	Calculate 64-bit hash from string reference.
*/
inline hash64 calc64(StringRef const& ref) {
	return hash::calc64(ref.data, ref.size);
}

/**
	32-bit hash literal.
*/
inline constexpr hash32
operator"" _hash32(
	char const* const data,
	std::size_t const size
) {
	return
		size != 0
		? am::hash::fnv1a_c<hash32_length>(data, size)
		: hash32{0}
	;
}

/**
	64-bit hash literal.
*/
inline constexpr hash64
operator"" _hash64(
	char const* const data,
	std::size_t const size
) {
	return
		size != 0
		? am::hash::fnv1a_c<hash64_length>(data, size)
		: hash64{0}
	;
}

/// 32-bit hash identity (hash of nothing).
static constexpr hash32 const
IDENTITY32{0}; // = ""_hash32;

/// 64-bit hash identity (hash of nothing).
static constexpr hash64 const
IDENTITY64{0}; // = ""_hash64;

/** @} */ // end of doc-group hash

} // namespace hash
} // namespace togo
<|endoftext|>


<commit_before>#line 2 "togo/tags.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file tags.hpp
@brief Variation/placeholder tags.
@ingroup utility
*/

#pragma once

#include <togo/config.hpp>

namespace togo {

/**
	@addtogroup utility
	@{
*/

/** @name Variation/placeholder tags */ /// @{

/**
	Null value tag.
*/
enum class null_tag {};

/**
	NUL-terminated string tag.
*/
enum class cstr_tag {};

/// @}

/** @} */ // end of doc-group utility

} // namespace togo
<commit_msg>tags: added bool_tag.<commit_after>#line 2 "togo/tags.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file tags.hpp
@brief Variation/placeholder tags.
@ingroup utility
*/

#pragma once

#include <togo/config.hpp>

namespace togo {

/**
	@addtogroup utility
	@{
*/

/** @name Variation/placeholder tags */ /// @{

/**
	Null value tag.
*/
enum class null_tag {};

/**
	NUL-terminated string tag.
*/
enum class cstr_tag {};

/**
	Bool string tag.
*/
enum class bool_tag {};

/// @}

/** @} */ // end of doc-group utility

} // namespace togo
<|endoftext|>


<commit_before>#include "ros/ros.h"
#include <gtest/gtest.h>
#include "geometry_msgs/Wrench.h"
#include "vortex_msgs/ThrusterForces.h"

class AllocatorTest : public ::testing::Test
{
public:
  AllocatorTest()
  {
    pub = nh.advertise<geometry_msgs::Wrench>("rov_forces", 10);
    sub = nh.subscribe("thruster_forces", 10, &AllocatorTest::Callback, this);
    message_received = false;

    if (!nh.getParam("/propulsion/thrusters/num", num_thrusters))
      ROS_FATAL("Failed to read parameter number of thrusters.");

    thrust.resize(num_thrusters);
  }

  void SetUp()
  {
    while (!IsNodeReady())
      ros::spinOnce();
  }

  void Publish(double surge, double sway, double heave, double roll, double pitch, double yaw)
  {
    geometry_msgs::Wrench msg;
    msg.force.x  = surge;
    msg.force.y  = sway;
    msg.force.z  = heave;
    msg.torque.x = roll;
    msg.torque.y = pitch;
    msg.torque.z = yaw;
    pub.publish(msg);
  }

  void WaitForMessage()
  {
    while (!message_received)
      ros::spinOnce();
  }

  int                 num_thrusters;
  std::vector<double> thrust;
  static const double MAX_ERROR = 1e-6;

 private:
  ros::NodeHandle nh;
  ros::Publisher  pub;
  ros::Subscriber sub;
  bool message_received;

  void Callback(const vortex_msgs::ThrusterForces& msg)
  {
    thrust = msg.thrust;
    message_received = true;
  }

  bool IsNodeReady()
  {
    return (pub.getNumSubscribers() > 0) && (sub.getNumPublishers() > 0);
  }
};

TEST_F(AllocatorTest, CheckResponsiveness)
{
  Publish(0, 0, 0, 0, 0, 0);
  WaitForMessage();
}

TEST_F(AllocatorTest, ZeroInput)
{
  Publish(0, 0, 0, 0, 0, 0);
  WaitForMessage();

  for(std::vector<double>::iterator it = thrust.begin(); it != thrust.end(); ++it)
  {
    EXPECT_NEAR(*it, 0, MAX_ERROR);
  }
}

TEST_F(AllocatorTest, Forward)
{
  Publish(1, 0, 0, 0, 0, 0);
  WaitForMessage();

  EXPECT_TRUE(thrust[0] > 0);
  EXPECT_TRUE(thrust[1] > 0);
  EXPECT_TRUE(thrust[2] < 0);
  EXPECT_TRUE(thrust[3] < 0);
  EXPECT_TRUE(thrust[4] < 0);
  EXPECT_TRUE(thrust[5] > 0);
}

TEST_F(AllocatorTest, Sideways)
{
  Publish(0, 1, 0, 0, 0, 0);
  WaitForMessage();

  EXPECT_TRUE(thrust[0] > 0);
  EXPECT_TRUE(thrust[1] < 0);
  EXPECT_TRUE(thrust[2] < 0);
  EXPECT_TRUE(thrust[3] > 0);
  EXPECT_NEAR(thrust[4], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[5], 0, MAX_ERROR);
}

TEST_F(AllocatorTest, Downward)
{
  Publish(0, 0, 1, 0, 0, 0);
  WaitForMessage();

  EXPECT_NEAR(thrust[0], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[1], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[2], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[3], 0, MAX_ERROR);
  EXPECT_TRUE(thrust[4] > 0);
  EXPECT_TRUE(thrust[5] > 0);
}

TEST_F(AllocatorTest, TiltUp)
{
  Publish(0, 0, 0, 0, 1, 0);
  WaitForMessage();

  EXPECT_NEAR(thrust[0], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[1], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[2], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[3], 0, MAX_ERROR);
  EXPECT_TRUE(thrust[4] < 0);
  EXPECT_TRUE(thrust[5] > 0);
}

TEST_F(AllocatorTest, TurnRight)
{
  Publish(0, 0, 0, 0, 0, 1);
  WaitForMessage();

  EXPECT_TRUE(thrust[0] > 0);
  EXPECT_TRUE(thrust[1] < 0);
  EXPECT_TRUE(thrust[2] > 0);
  EXPECT_TRUE(thrust[3] < 0);
  EXPECT_NEAR(thrust[4], 0, MAX_ERROR);
  EXPECT_NEAR(thrust[5], 0, MAX_ERROR);
}

int main(int argc, char **argv)
{
  testing::InitGoogleTest(&argc, argv);
  ros::init(argc, argv, "allocator_test");

  int ret = RUN_ALL_TESTS();
  ros::shutdown();
  return ret;
}
<commit_msg>Hard code values, not only signs<commit_after>#include "ros/ros.h"
#include <gtest/gtest.h>
#include "geometry_msgs/Wrench.h"
#include "vortex_msgs/ThrusterForces.h"

class AllocatorTest : public ::testing::Test
{
public:
  AllocatorTest()
  {
    pub = nh.advertise<geometry_msgs::Wrench>("rov_forces", 10);
    sub = nh.subscribe("thruster_forces", 10, &AllocatorTest::Callback, this);
    message_received = false;

    if (!nh.getParam("/propulsion/thrusters/num", num_thrusters))
      ROS_FATAL("Failed to read parameter number of thrusters.");

    thrust.resize(num_thrusters);
  }

  void SetUp()
  {
    while (!IsNodeReady())
      ros::spinOnce();
  }

  void Publish(double surge, double sway, double heave, double roll, double pitch, double yaw)
  {
    geometry_msgs::Wrench msg;
    msg.force.x  = surge;
    msg.force.y  = sway;
    msg.force.z  = heave;
    msg.torque.x = roll;
    msg.torque.y = pitch;
    msg.torque.z = yaw;
    pub.publish(msg);
  }

  // TODO: This should be vectorized somehow in number of arguments
  void ExpectThrustNear(double f1, double f2, double f3, double f4, double f5, double f6)
  {
    double arr[] = {f1, f2, f3, f4, f5, f6};
    std::vector<double> thrust_expected (arr, arr + sizeof(arr) / sizeof(arr[0]) );

    for (int i = 0; i < thrust.size(); ++i)
      EXPECT_NEAR(thrust[i], thrust_expected[i], MAX_ERROR);
  }

  void ExpectThrustNear(double* arr)
  {
    for (int i = 0; i < thrust.size(); ++i)
      EXPECT_NEAR(thrust[i], arr[i], MAX_ERROR);
  }

  void WaitForMessage()
  {
    while (!message_received)
      ros::spinOnce();
  }

  int                 num_thrusters;
  std::vector<double> thrust;
  static const double MAX_ERROR = 1e-4;

 private:
  ros::NodeHandle nh;
  ros::Publisher  pub;
  ros::Subscriber sub;
  bool message_received;

  void Callback(const vortex_msgs::ThrusterForces& msg)
  {
    thrust = msg.thrust;
    message_received = true;
  }

  bool IsNodeReady()
  {
    return (pub.getNumSubscribers() > 0) && (sub.getNumPublishers() > 0);
  }
};

TEST_F(AllocatorTest, CheckResponsiveness)
{
  Publish(0, 0, 0, 0, 0, 0);
  WaitForMessage();
}

TEST_F(AllocatorTest, ZeroInput)
{
  Publish(0, 0, 0, 0, 0, 0);
  WaitForMessage();

  double thrust_expected[] = {0,0,0,0,0,0};
  ExpectThrustNear(thrust_expected);
}

TEST_F(AllocatorTest, Forward)
{
  Publish(1, 0, 0, 0, 0, 0);
  WaitForMessage();

  double thrust_expected[] = {0.35356, 0.35356, -0.35356, -0.35356, -0.20639, 0.20639};
  ExpectThrustNear(thrust_expected);
}

TEST_F(AllocatorTest, Sideways)
{
  Publish(0, 1, 0, 0, 0, 0);
  WaitForMessage();

  double thrust_expected[] = {0.35356, -0.35356, -0.35356, 0.35356, 0.0, 0.0};
  ExpectThrustNear(thrust_expected);
}

TEST_F(AllocatorTest, Downward)
{
  Publish(0, 0, 1, 0, 0, 0);
  WaitForMessage();

  double thrust_expected[] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.5};
  ExpectThrustNear(thrust_expected);
}

TEST_F(AllocatorTest, TiltUp)
{
  Publish(0, 0, 0, 0, 1, 0);
  WaitForMessage();

  double thrust_expected[] = {0.0, 0.0, 0.0, 0.0, -3.1250, 3.1250};
  ExpectThrustNear(thrust_expected);
}

TEST_F(AllocatorTest, TurnRight)
{
  Publish(0, 0, 0, 0, 0, 1);
  WaitForMessage();

  double thrust_expected[] = {1.1666, -1.1666, 1.1666, -1.1666, 0.0, 0.0};
  ExpectThrustNear(thrust_expected);
}

int main(int argc, char **argv)
{
  testing::InitGoogleTest(&argc, argv);
  ros::init(argc, argv, "allocator_test");

  int ret = RUN_ALL_TESTS();
  ros::shutdown();
  return ret;
}
<|endoftext|>


<commit_before>//===- PowerPCRegisterInfo.cpp - PowerPC Register Information ---*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the PowerPC implementation of the MRegisterInfo class.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "reginfo"
#include "PowerPC.h"
#include "PowerPCRegisterInfo.h"
#include "PowerPCInstrBuilder.h"
#include "llvm/Constants.h"
#include "llvm/Type.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/Target/TargetFrameInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "Support/CommandLine.h"
#include "Support/Debug.h"
#include "Support/STLExtras.h"
#include <iostream>
using namespace llvm;

PowerPCRegisterInfo::PowerPCRegisterInfo()
  : PowerPCGenRegisterInfo(PPC32::ADJCALLSTACKDOWN,
                           PPC32::ADJCALLSTACKUP) {}

static unsigned getIdx(const TargetRegisterClass *RC) {
  if (RC == PowerPC::GPRCRegisterClass) {
    switch (RC->getSize()) {
      default: assert(0 && "Invalid data size!");
      case 1:  return 0;
      case 2:  return 1;
      case 4:  return 2;
    }
  } else if (RC == PowerPC::FPRCRegisterClass) {
    switch (RC->getSize()) {
      default: assert(0 && "Invalid data size!");
      case 4:  return 3;
      case 8:  return 4;
    }
  }
  std::cerr << "Invalid register class to getIdx()!\n";
  abort();
}

int
PowerPCRegisterInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
                                         MachineBasicBlock::iterator MI,
                                         unsigned SrcReg, int FrameIdx,
                                         const TargetRegisterClass *RC) const {
  static const unsigned Opcode[] = {
    PPC32::STB, PPC32::STH, PPC32::STW, PPC32::STFS, PPC32::STFD
  };
  unsigned OC = Opcode[getIdx(RC)];
  MBB.insert(MI, addFrameReference(BuildMI(OC, 3).addReg(SrcReg),FrameIdx));
  return 1;
}

int PowerPCRegisterInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator MI,
                                          unsigned DestReg, int FrameIdx,
                                          const TargetRegisterClass *RC) const{
  static const unsigned Opcode[] = {
    PPC32::LBZ, PPC32::LHZ, PPC32::LWZ, PPC32::LFS, PPC32::LFD
  };
  unsigned OC = Opcode[getIdx(RC)];
  MBB.insert(MI, addFrameReference(BuildMI(OC, 2, DestReg), FrameIdx));
  return 1;
}

int PowerPCRegisterInfo::copyRegToReg(MachineBasicBlock &MBB,
                                      MachineBasicBlock::iterator MI,
                                      unsigned DestReg, unsigned SrcReg,
                                      const TargetRegisterClass *RC) const {
  MachineInstr *I;

  if (RC == PowerPC::GPRCRegisterClass) {
    I = BuildMI(PPC32::OR, 2, DestReg).addReg(SrcReg).addReg(SrcReg);
  } else if (RC == PowerPC::FPRCRegisterClass) {
    I = BuildMI(PPC32::FMR, 1, DestReg).addReg(SrcReg);
  } else {
    std::cerr << "Attempt to copy register that is not GPR or FPR";
    abort();
  }
  MBB.insert(MI, I);
  return 1;
}

//===----------------------------------------------------------------------===//
// Stack Frame Processing methods
//===----------------------------------------------------------------------===//

// hasFP - Return true if the specified function should have a dedicated frame
// pointer register.  This is true if the function has variable sized allocas or
// if frame pointer elimination is disabled.
//
static bool hasFP(MachineFunction &MF) {
  return NoFramePointerElim || MF.getFrameInfo()->hasVarSizedObjects();
}

void PowerPCRegisterInfo::
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
                              MachineBasicBlock::iterator I) const {
  if (hasFP(MF)) {
    // If we have a frame pointer, convert as follows:
    // adjcallstackdown instruction => 'sub r1, r1, <amt>' and
    // adjcallstackup instruction   => 'add r1, r1, <amt>'
    MachineInstr *Old = I;
    int Amount = Old->getOperand(0).getImmedValue();
    if (Amount != 0) {
      // We need to keep the stack aligned properly.  To do this, we round the
      // amount of space needed for the outgoing arguments up to the next
      // alignment boundary.
      unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment();
      Amount = (Amount+Align-1)/Align*Align;

      MachineInstr *New;
      if (Old->getOpcode() == PPC32::ADJCALLSTACKDOWN) {
        New = BuildMI(PPC32::ADDI, 2, PPC32::R1).addReg(PPC32::R1)
                .addSImm(-Amount);
      } else {
        assert(Old->getOpcode() == PPC32::ADJCALLSTACKUP);
        New = BuildMI(PPC32::ADDI, 2, PPC32::R1).addReg(PPC32::R1)
                .addSImm(Amount);
      }

      // Replace the pseudo instruction with a new instruction...
      MBB.insert(I, New);
    }
  }

  MBB.erase(I);
}

void
PowerPCRegisterInfo::eliminateFrameIndex(MachineFunction &MF,
                                         MachineBasicBlock::iterator II) const {
  unsigned i = 0;
  MachineInstr &MI = *II;
  while (!MI.getOperand(i).isFrameIndex()) {
    ++i;
    assert(i < MI.getNumOperands() && "Instr doesn't have FrameIndex operand!");
  }

  int FrameIndex = MI.getOperand(i).getFrameIndex();

  // Replace the FrameIndex with base register with GPR1.
  MI.SetMachineOperandReg(i, PPC32::R1);

  // Take into account whether it's an add or mem instruction
  unsigned OffIdx = (i == 2) ? 1 : 2;
  // Now add the frame object offset to the offset from r1.
  int Offset = MF.getFrameInfo()->getObjectOffset(FrameIndex) +
               MI.getOperand(OffIdx).getImmedValue()+4;

  if (!hasFP(MF))
    Offset += MF.getFrameInfo()->getStackSize();

  MI.SetMachineOperandConst(OffIdx, MachineOperand::MO_SignExtendedImmed, Offset);
  DEBUG(std::cerr << "offset = " << Offset << std::endl);
}

void
PowerPCRegisterInfo::processFunctionBeforeFrameFinalized(MachineFunction &MF)
  const {
  // Do Nothing
}

void PowerPCRegisterInfo::emitPrologue(MachineFunction &MF) const {
  MachineBasicBlock &MBB = MF.front();   // Prolog goes in entry BB
  MachineBasicBlock::iterator MBBI = MBB.begin();
  MachineFrameInfo *MFI = MF.getFrameInfo();
  MachineInstr *MI;

  // Get the number of bytes to allocate from the FrameInfo
  unsigned NumBytes = MFI->getStackSize();

  // FIXME: the assembly printer inserts "calls" aka branch-and-link to get the
  // PC address. We may not know about those calls at this time, so be
  // conservative.
  if (MFI->hasCalls() || true) {
    // When we have no frame pointer, we reserve argument space for call sites
    // in the function immediately on entry to the current function.  This
    // eliminates the need for add/sub brackets around call sites.
    //
    NumBytes += MFI->getMaxCallFrameSize() +
                24   /* Predefined PowerPC link area */ +
                32   /* Predefined PowerPC params area */ +
                 0   /* local variables - managed by llvm*/ +
             0 * 4   /* volatile GPRs used - managed by llvm */ +
             0 * 8   /* volatile FPRs used - managed by llvm */;

    // Round the size to a multiple of the alignment (don't forget the 4 byte
    // offset though).
    unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment();
    NumBytes = ((NumBytes+4)+Align-1)/Align*Align - 4;

    // Store the incoming LR so it is preserved across calls
    MI = BuildMI(PPC32::MFLR, 0, PPC32::R0);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::STW, 3).addReg(PPC32::R0).addSImm(8).addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
  }

  // Update frame info to pretend that this is part of the stack...
  MFI->setStackSize(NumBytes);

  // adjust stack pointer: r1 -= numbytes
  if (NumBytes <= 32768) {
    MI = BuildMI(PPC32::STWU, 3).addReg(PPC32::R1).addSImm(-NumBytes)
      .addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
  } else {
    int NegNumbytes = -NumBytes;
    MI = BuildMI(PPC32::LIS, 1, PPC32::R0).addSImm(NegNumbytes >> 16);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::ORI, 2, PPC32::R0).addReg(PPC32::R0)
      .addImm(NegNumbytes & 0xFFFF);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::STWUX, 3).addReg(PPC32::R1).addReg(PPC32::R1)
      .addReg(PPC32::R0);
    MBB.insert(MBBI, MI);
  }
}

void PowerPCRegisterInfo::emitEpilogue(MachineFunction &MF,
                                       MachineBasicBlock &MBB) const {
  const MachineFrameInfo *MFI = MF.getFrameInfo();
  MachineBasicBlock::iterator MBBI = prior(MBB.end());
  MachineInstr *MI;
  assert(MBBI->getOpcode() == PPC32::BLR &&
         "Can only insert epilog into returning blocks");

  // Get the number of bytes allocated from the FrameInfo...
  unsigned NumBytes = MFI->getStackSize();

  // If we have calls, restore the LR value before we branch to it
  // FIXME: the assembly printer inserts "calls" aka branch-and-link to get the
  // PC address. We may not know about those calls at this time, so be
  // conservative.
  if (MFI->hasCalls() || true) {
    // Restore LR
    MI = BuildMI(PPC32::LWZ, 2, PPC32::R0).addSImm(NumBytes+8).addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::MTLR, 1).addReg(PPC32::R0);
    MBB.insert(MBBI, MI);
  }
  // Adjust stack pointer back
  if (NumBytes <= 32767) {
    MI = BuildMI(PPC32::ADDI, 2, PPC32::R1).addReg(PPC32::R1).addSImm(NumBytes);
    MBB.insert(MBBI, MI);
  } else {
    MI = BuildMI(PPC32::LWZ, 2, PPC32::R1).addSImm(0).addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
  }
}

#include "PowerPCGenRegisterInfo.inc"

const TargetRegisterClass*
PowerPCRegisterInfo::getRegClassForType(const Type* Ty) const {
  switch (Ty->getTypeID()) {
    case Type::LongTyID:
    case Type::ULongTyID: assert(0 && "Long values can't fit in registers!");
    default:              assert(0 && "Invalid type to getClass!");
    case Type::BoolTyID:
    case Type::SByteTyID:
    case Type::UByteTyID:
    case Type::ShortTyID:
    case Type::UShortTyID:
    case Type::IntTyID:
    case Type::UIntTyID:
    case Type::PointerTyID: return &GPRCInstance;

    case Type::FloatTyID:
    case Type::DoubleTyID: return &FPRCInstance;
  }
}

<commit_msg>Do not store the stack pointer if the stack size is 0. Also, convert C-style comments to C++ and make sure code wraps at 80 cols.<commit_after>//===- PowerPCRegisterInfo.cpp - PowerPC Register Information ---*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the PowerPC implementation of the MRegisterInfo class.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "reginfo"
#include "PowerPC.h"
#include "PowerPCRegisterInfo.h"
#include "PowerPCInstrBuilder.h"
#include "llvm/Constants.h"
#include "llvm/Type.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/Target/TargetFrameInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "Support/CommandLine.h"
#include "Support/Debug.h"
#include "Support/STLExtras.h"
#include <iostream>
using namespace llvm;

PowerPCRegisterInfo::PowerPCRegisterInfo()
  : PowerPCGenRegisterInfo(PPC32::ADJCALLSTACKDOWN,
                           PPC32::ADJCALLSTACKUP) {}

static unsigned getIdx(const TargetRegisterClass *RC) {
  if (RC == PowerPC::GPRCRegisterClass) {
    switch (RC->getSize()) {
      default: assert(0 && "Invalid data size!");
      case 1:  return 0;
      case 2:  return 1;
      case 4:  return 2;
    }
  } else if (RC == PowerPC::FPRCRegisterClass) {
    switch (RC->getSize()) {
      default: assert(0 && "Invalid data size!");
      case 4:  return 3;
      case 8:  return 4;
    }
  }
  std::cerr << "Invalid register class to getIdx()!\n";
  abort();
}

int
PowerPCRegisterInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
                                         MachineBasicBlock::iterator MI,
                                         unsigned SrcReg, int FrameIdx,
                                         const TargetRegisterClass *RC) const {
  static const unsigned Opcode[] = {
    PPC32::STB, PPC32::STH, PPC32::STW, PPC32::STFS, PPC32::STFD
  };
  unsigned OC = Opcode[getIdx(RC)];
  MBB.insert(MI, addFrameReference(BuildMI(OC, 3).addReg(SrcReg),FrameIdx));
  return 1;
}

int PowerPCRegisterInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator MI,
                                          unsigned DestReg, int FrameIdx,
                                          const TargetRegisterClass *RC) const{
  static const unsigned Opcode[] = {
    PPC32::LBZ, PPC32::LHZ, PPC32::LWZ, PPC32::LFS, PPC32::LFD
  };
  unsigned OC = Opcode[getIdx(RC)];
  MBB.insert(MI, addFrameReference(BuildMI(OC, 2, DestReg), FrameIdx));
  return 1;
}

int PowerPCRegisterInfo::copyRegToReg(MachineBasicBlock &MBB,
                                      MachineBasicBlock::iterator MI,
                                      unsigned DestReg, unsigned SrcReg,
                                      const TargetRegisterClass *RC) const {
  MachineInstr *I;

  if (RC == PowerPC::GPRCRegisterClass) {
    I = BuildMI(PPC32::OR, 2, DestReg).addReg(SrcReg).addReg(SrcReg);
  } else if (RC == PowerPC::FPRCRegisterClass) {
    I = BuildMI(PPC32::FMR, 1, DestReg).addReg(SrcReg);
  } else {
    std::cerr << "Attempt to copy register that is not GPR or FPR";
    abort();
  }
  MBB.insert(MI, I);
  return 1;
}

//===----------------------------------------------------------------------===//
// Stack Frame Processing methods
//===----------------------------------------------------------------------===//

// hasFP - Return true if the specified function should have a dedicated frame
// pointer register.  This is true if the function has variable sized allocas or
// if frame pointer elimination is disabled.
//
static bool hasFP(MachineFunction &MF) {
  return NoFramePointerElim || MF.getFrameInfo()->hasVarSizedObjects();
}

void PowerPCRegisterInfo::
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
                              MachineBasicBlock::iterator I) const {
  if (hasFP(MF)) {
    // If we have a frame pointer, convert as follows:
    // adjcallstackdown instruction => 'sub r1, r1, <amt>' and
    // adjcallstackup instruction   => 'add r1, r1, <amt>'
    MachineInstr *Old = I;
    int Amount = Old->getOperand(0).getImmedValue();
    if (Amount != 0) {
      // We need to keep the stack aligned properly.  To do this, we round the
      // amount of space needed for the outgoing arguments up to the next
      // alignment boundary.
      unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment();
      Amount = (Amount+Align-1)/Align*Align;

      MachineInstr *New;
      if (Old->getOpcode() == PPC32::ADJCALLSTACKDOWN) {
        New = BuildMI(PPC32::ADDI, 2, PPC32::R1).addReg(PPC32::R1)
                .addSImm(-Amount);
      } else {
        assert(Old->getOpcode() == PPC32::ADJCALLSTACKUP);
        New = BuildMI(PPC32::ADDI, 2, PPC32::R1).addReg(PPC32::R1)
                .addSImm(Amount);
      }

      // Replace the pseudo instruction with a new instruction...
      MBB.insert(I, New);
    }
  }

  MBB.erase(I);
}

void
PowerPCRegisterInfo::eliminateFrameIndex(MachineFunction &MF,
                                         MachineBasicBlock::iterator II) const {
  unsigned i = 0;
  MachineInstr &MI = *II;
  while (!MI.getOperand(i).isFrameIndex()) {
    ++i;
    assert(i < MI.getNumOperands() && "Instr doesn't have FrameIndex operand!");
  }

  int FrameIndex = MI.getOperand(i).getFrameIndex();

  // Replace the FrameIndex with base register with GPR1.
  MI.SetMachineOperandReg(i, PPC32::R1);

  // Take into account whether it's an add or mem instruction
  unsigned OffIdx = (i == 2) ? 1 : 2;
  // Now add the frame object offset to the offset from r1.
  int Offset = MF.getFrameInfo()->getObjectOffset(FrameIndex) +
               MI.getOperand(OffIdx).getImmedValue()+4;

  if (!hasFP(MF))
    Offset += MF.getFrameInfo()->getStackSize();

  MI.SetMachineOperandConst(OffIdx,MachineOperand::MO_SignExtendedImmed,Offset);
  DEBUG(std::cerr << "offset = " << Offset << std::endl);
}

void
PowerPCRegisterInfo::processFunctionBeforeFrameFinalized(MachineFunction &MF)
  const {
  // Do Nothing
}

void PowerPCRegisterInfo::emitPrologue(MachineFunction &MF) const {
  MachineBasicBlock &MBB = MF.front();   // Prolog goes in entry BB
  MachineBasicBlock::iterator MBBI = MBB.begin();
  MachineFrameInfo *MFI = MF.getFrameInfo();
  MachineInstr *MI;

  // Get the number of bytes to allocate from the FrameInfo
  unsigned NumBytes = MFI->getStackSize();

  // FIXME: the assembly printer inserts "calls" aka branch-and-link to get the
  // PC address. We may not know about those calls at this time, so be
  // conservative.
  if (MFI->hasCalls() || true) {
    // When we have no frame pointer, we reserve argument space for call sites
    // in the function immediately on entry to the current function.  This
    // eliminates the need for add/sub brackets around call sites.
    //
    NumBytes += MFI->getMaxCallFrameSize() +
                24 + // Predefined PowerPC link area
                32 + // Predefined PowerPC params area
                 0 + // local variables - managed by LLVM
             0 * 4 + // volatile GPRs used - managed by LLVM
             0 * 8;  // volatile FPRs used - managed by LLVM

    // Round the size to a multiple of the alignment (don't forget the 4 byte
    // offset though).
    unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment();
    NumBytes = ((NumBytes+4)+Align-1)/Align*Align - 4;

    // Store the incoming LR so it is preserved across calls
    MI = BuildMI(PPC32::MFLR, 0, PPC32::R0);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::STW, 3).addReg(PPC32::R0).addSImm(8).addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
  }

  // Update frame info to pretend that this is part of the stack...
  MFI->setStackSize(NumBytes);

  // Do we need to allocate space on the stack?
  if (NumBytes == 0) return;

  // adjust stack pointer: r1 -= numbytes
  if (NumBytes <= 32768) {
    MI = BuildMI(PPC32::STWU, 3).addReg(PPC32::R1).addSImm(-NumBytes)
      .addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
  } else {
    int NegNumbytes = -NumBytes;
    MI = BuildMI(PPC32::LIS, 1, PPC32::R0).addSImm(NegNumbytes >> 16);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::ORI, 2, PPC32::R0).addReg(PPC32::R0)
      .addImm(NegNumbytes & 0xFFFF);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::STWUX, 3).addReg(PPC32::R1).addReg(PPC32::R1)
      .addReg(PPC32::R0);
    MBB.insert(MBBI, MI);
  }
}

void PowerPCRegisterInfo::emitEpilogue(MachineFunction &MF,
                                       MachineBasicBlock &MBB) const {
  const MachineFrameInfo *MFI = MF.getFrameInfo();
  MachineBasicBlock::iterator MBBI = prior(MBB.end());
  MachineInstr *MI;
  assert(MBBI->getOpcode() == PPC32::BLR &&
         "Can only insert epilog into returning blocks");

  // Get the number of bytes allocated from the FrameInfo...
  unsigned NumBytes = MFI->getStackSize();

  // If we have calls, restore the LR value before we branch to it
  // FIXME: the assembly printer inserts "calls" aka branch-and-link to get the
  // PC address. We may not know about those calls at this time, so be
  // conservative.
  if (MFI->hasCalls() || true) {
    // Restore LR
    MI = BuildMI(PPC32::LWZ, 2,PPC32::R0).addSImm(NumBytes+8).addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
    MI = BuildMI(PPC32::MTLR, 1).addReg(PPC32::R0);
    MBB.insert(MBBI, MI);
  }

  // Do we need to adjust the stack pointer back?
  if (NumBytes == 0) return;

  // Adjust stack pointer back
  if (NumBytes <= 32767) {
    MI = BuildMI(PPC32::ADDI, 2, PPC32::R1).addReg(PPC32::R1).addSImm(NumBytes);
    MBB.insert(MBBI, MI);
  } else {
    MI = BuildMI(PPC32::LWZ, 2, PPC32::R1).addSImm(0).addReg(PPC32::R1);
    MBB.insert(MBBI, MI);
  }
}

#include "PowerPCGenRegisterInfo.inc"

const TargetRegisterClass*
PowerPCRegisterInfo::getRegClassForType(const Type* Ty) const {
  switch (Ty->getTypeID()) {
    case Type::LongTyID:
    case Type::ULongTyID: assert(0 && "Long values can't fit in registers!");
    default:              assert(0 && "Invalid type to getClass!");
    case Type::BoolTyID:
    case Type::SByteTyID:
    case Type::UByteTyID:
    case Type::ShortTyID:
    case Type::UShortTyID:
    case Type::IntTyID:
    case Type::UIntTyID:
    case Type::PointerTyID: return &GPRCInstance;

    case Type::FloatTyID:
    case Type::DoubleTyID: return &FPRCInstance;
  }
}

<|endoftext|>


<commit_before>#ifndef SCAN_CLASSIFY_SWITCH_BIT_BLOCK_RECOMBINER_HPP_
#define SCAN_CLASSIFY_SWITCH_BIT_BLOCK_RECOMBINER_HPP_

#include "clotho/recombination/bit_block_recombiner_def.hpp"
#include "clotho/recombination/inspect_methods.hpp"

#include "clotho/utility/bit_masks.hpp"

namespace clotho {
namespace recombine {
namespace walker {
namespace tag {

/**
 * Scan and Classify walks the set bits in a block and stores their
 * index (explodes the block into list of bit indices). The list of
 * indices is iterated and the elements at these offsets are classified.
 *
 * This results in:
 *      - a single bit walking loop per 32-bit block
 *      - a simple loop over offsets
 *      - switch over the bit indices
 *
 * Thought process:
 *      - Bit scanning could more streamline (less context switching)
 *      - Requires more memory (64 * 4 = 256 Bytes)
 *      - Simpler classification loop (indices will be ordered as a result of scan)
 *      - Switch logic allows mask bits sets to be defined inline rather than computed
 *
 *  Concern:
 *      - Doubles the number of iterative steps
 */
struct scan_and_classify_switch {};

}   // namespace tag
}   // namespace walker
}   // namespace recombine
}   // namespace clotho

namespace clotho {
namespace recombine {

#define CHECK_0()   if( m_cfier( *first ) ) { res |= (Block) OFFSET( 0 ); }
#define CHECK( x )  if( m_cfier( *(first + x) ) ) { res |= (Block) OFFSET( x ); }

template < class Classifier, class InspectMethodTag >
class bit_block_recombiner< Classifier, InspectMethodTag, clotho::recombine::walker::tag::scan_and_classify_switch > {
public:
    typedef Classifier classifier_type;

    bit_block_recombiner( const classifier_type & cfier ) : m_cfier( cfier ) {}

    template < class Block, class ElementIterator >
    Block operator()( const Block b0, const Block b1, const ElementIterator first ) {
        // Only compute the hash of each set bit
        // In effect, delay the offset lookup to be performed by the switching logic
        // Goal is to eliminate a 'double-lookup' (lookup from hash table, lookup based upon hash)
        // This assumes switch logic results in a 'jump table'
        unsigned int count = clotho::utility::hash_set_bits( InspectMethodTag::select(b0, b1), indices );

        Block res = (Block)0;
        unsigned int * idx = indices;
        while( count-- ) {
            switch( *idx++ ) {
            case 0:
                CHECK_0() break;
            case 1:
                CHECK(1) break;
            case 2:
                CHECK(28) break;
            case 3:
                CHECK(2) break;
            case 4:
                CHECK(29) break;
            case 5:
                CHECK(14) break;
            case 6:
                CHECK(24) break;
            case 7:
                CHECK(3) break;
            case 8:
                CHECK(30) break;
            case 9:
                CHECK(22) break;
            case 10:
                CHECK(20) break;
            case 11:
                CHECK(15) break;
            case 12:
                CHECK(25) break;
            case 13:
                CHECK(17) break;
            case 14:
                CHECK(4) break;
            case 15:
                CHECK(8) break;
            case 16:
                CHECK(31) break;
            case 17:
                CHECK(27) break;
            case 18:
                CHECK(13) break;
            case 19:
                CHECK(23) break;
            case 20:
                CHECK(21) break;
            case 21:
                CHECK(19) break;
            case 22:
                CHECK(16) break;
            case 23:
                CHECK(7) break;
            case 24:
                CHECK(26) break;
            case 25:
                CHECK(12) break;
            case 26:
                CHECK(18) break;
            case 27:
                CHECK(6) break;
            case 28:
                CHECK(11) break;
            case 29:
                CHECK(5) break;
            case 30:
                CHECK(10) break;
            case 31:
                CHECK(9) break;
            case 32:
                CHECK(32) break;    // 0
            case 33:
                CHECK(33) break;    // 1
            case 34:
                CHECK(60) break;    // 28
            case 35:
                CHECK(34) break;    // 2
            case 36:
                CHECK(61) break;    // 29
            case 37:
                CHECK(46) break;    // 14
            case 38:
                CHECK(56) break;    // 24
            case 39:
                CHECK(35) break;    // 3
            case 40:
                CHECK(62) break;    // 30
            case 41:
                CHECK(54) break;    // 22
            case 42:
                CHECK(52) break;    // 20
            case 43:
                CHECK(47) break;    // 15
            case 44:
                CHECK(57) break;    // 25
            case 45:
                CHECK(49) break;    // 17
            case 46:
                CHECK(36) break;    // 4
            case 47:
                CHECK(40) break;    // 8
            case 48:
                CHECK(63) break;    // 31
            case 49:
                CHECK(59) break;    // 27
            case 50:
                CHECK(45) break;    // 13
            case 51:
                CHECK(55) break;    // 23
            case 52:
                CHECK(53) break;    // 21
            case 53:
                CHECK(51) break;    // 19
            case 54:
                CHECK(48) break;    // 16
            case 55:
                CHECK(39) break;    // 7
            case 56:
                CHECK(58) break;    // 26
            case 57:
                CHECK(44) break;    // 12
            case 58:
                CHECK(50) break;    // 18
            case 59:
                CHECK(38) break;    //  6
            case 60:
                CHECK(43) break;    // 11
            case 61:
                CHECK(37) break;    //  5
            case 62:
                CHECK(42) break;    // 10
            case 63:
                CHECK(41) break;    //  9
            default:
                break;
            }
        }

        Block rec = ((b0 & res) | (b1 & ~res));
        return rec;
    }

protected:
    classifier_type m_cfier;
    unsigned int indices[ 64 ];
};

#undef CHECK_0
#undef CHECK

}   // namespace recombine
}   // namespace clotho
#endif  // SCAN_CLASSIFY_SWITCH_BIT_BLOCK_RECOMBINER_HPP_
<commit_msg>Made walking methods; Debating whether should be completely separated<commit_after>#ifndef SCAN_CLASSIFY_SWITCH_BIT_BLOCK_RECOMBINER_HPP_
#define SCAN_CLASSIFY_SWITCH_BIT_BLOCK_RECOMBINER_HPP_

#include "clotho/recombination/bit_block_recombiner_def.hpp"
#include "clotho/recombination/inspect_methods.hpp"

#include "clotho/utility/bit_masks.hpp"

namespace clotho {
namespace recombine {
namespace walker {
namespace tag {

/**
 * Scan and Classify walks the set bits in a block and stores their
 * index (explodes the block into list of bit indices). The list of
 * indices is iterated and the elements at these offsets are classified.
 *
 * This results in:
 *      - a single bit walking loop per 32-bit block
 *      - a simple loop over offsets
 *      - switch over the bit indices
 *
 * Thought process:
 *      - Bit scanning could more streamline (less context switching)
 *      - Requires more memory (64 * 4 = 256 Bytes)
 *      - Simpler classification loop (indices will be ordered as a result of scan)
 *      - Switch logic allows mask bits sets to be defined inline rather than computed
 *
 *  Concern:
 *      - Doubles the number of iterative steps
 */
struct scan_and_classify_switch {};

}   // namespace tag
}   // namespace walker
}   // namespace recombine
}   // namespace clotho

namespace clotho {
namespace recombine {

#define CHECK_0()   if( m_cfier( *first ) ) { res |= (Block) OFFSET( 0 ); }
#define CHECK( x )  if( m_cfier( *(first + x) ) ) { res |= (Block) OFFSET( x ); }

template < class Classifier, class InspectMethodTag >
class bit_block_recombiner< Classifier, InspectMethodTag, clotho::recombine::walker::tag::scan_and_classify_switch > {
public:
    typedef Classifier classifier_type;

    bit_block_recombiner( const classifier_type & cfier ) : m_cfier( cfier ) {}

    template < class Block, class ElementIterator >
    Block operator()( const Block b0, const Block b1, const ElementIterator first ) {

        Block mask = walk( InspectMethodTag::select(b0, b1), first );

        Block rec = ((b0 & mask) | (b1 & ~mask));
        return rec;
    }

protected:
    classifier_type m_cfier;
    unsigned int indices[ 64 ];

    template < class ElementIterator >
    unsigned int walk( unsigned int b, const ElementIterator first ) {
        // Only compute the hash of each set bit
        // In effect, delay the offset lookup to be performed by the switching logic
        // Goal is to eliminate a 'double-lookup' (lookup from hash table, lookup based upon hash)
        // This assumes switch logic results in a 'jump table'
        //
        typedef unsigned int Block;
        unsigned int count = clotho::utility::hash_set_bits( b, indices );

        Block res = (Block)0;
        unsigned int * idx = indices;
        while( count-- ) {
            switch( *idx++ ) {
            case 0:
                CHECK_0() break;
            case 1:
                CHECK(1) break;
            case 2:
                CHECK(28) break;
            case 3:
                CHECK(2) break;
            case 4:
                CHECK(29) break;
            case 5:
                CHECK(14) break;
            case 6:
                CHECK(24) break;
            case 7:
                CHECK(3) break;
            case 8:
                CHECK(30) break;
            case 9:
                CHECK(22) break;
            case 10:
                CHECK(20) break;
            case 11:
                CHECK(15) break;
            case 12:
                CHECK(25) break;
            case 13:
                CHECK(17) break;
            case 14:
                CHECK(4) break;
            case 15:
                CHECK(8) break;
            case 16:
                CHECK(31) break;
            case 17:
                CHECK(27) break;
            case 18:
                CHECK(13) break;
            case 19:
                CHECK(23) break;
            case 20:
                CHECK(21) break;
            case 21:
                CHECK(19) break;
            case 22:
                CHECK(16) break;
            case 23:
                CHECK(7) break;
            case 24:
                CHECK(26) break;
            case 25:
                CHECK(12) break;
            case 26:
                CHECK(18) break;
            case 27:
                CHECK(6) break;
            case 28:
                CHECK(11) break;
            case 29:
                CHECK(5) break;
            case 30:
                CHECK(10) break;
            case 31:
                CHECK(9) break;
            default:
                break;
            }
        }
        return res;
    }

    template < class ElementIterator >
    unsigned long walk( unsigned long b, const ElementIterator first ) {
        // Only compute the hash of each set bit
        // In effect, delay the offset lookup to be performed by the switching logic
        // Goal is to eliminate a 'double-lookup' (lookup from hash table, lookup based upon hash)
        // This assumes switch logic results in a 'jump table'
        //

        unsigned int lo = (unsigned int)b;
        unsigned long mask = (unsigned long)walk( lo, first );

        lo = (unsigned int) ( b >> 32 );
        if( lo ) {
            ElementIterator tmp = (first + 32);
            unsigned long hi_mask = (unsigned long)walk(lo, tmp );
            mask |= (hi_mask << 32);
        }

        return mask;
    }

    template < class ElementIterator >
    unsigned long unrolled_walk( unsigned long b, const ElementIterator first ) {
        // Only compute the hash of each set bit
        // In effect, delay the offset lookup to be performed by the switching logic
        // Goal is to eliminate a 'double-lookup' (lookup from hash table, lookup based upon hash)
        // This assumes switch logic results in a 'jump table'
        //
        typedef unsigned long Block;
        unsigned int count = clotho::utility::hash_set_bits( b, indices );

        Block res = (Block)0;
        unsigned int * idx = indices;
        while( count-- ) {
            switch( *idx++ ) {
            case 0:
                CHECK_0() break;
            case 1:
                CHECK(1) break;
            case 2:
                CHECK(28) break;
            case 3:
                CHECK(2) break;
            case 4:
                CHECK(29) break;
            case 5:
                CHECK(14) break;
            case 6:
                CHECK(24) break;
            case 7:
                CHECK(3) break;
            case 8:
                CHECK(30) break;
            case 9:
                CHECK(22) break;
            case 10:
                CHECK(20) break;
            case 11:
                CHECK(15) break;
            case 12:
                CHECK(25) break;
            case 13:
                CHECK(17) break;
            case 14:
                CHECK(4) break;
            case 15:
                CHECK(8) break;
            case 16:
                CHECK(31) break;
            case 17:
                CHECK(27) break;
            case 18:
                CHECK(13) break;
            case 19:
                CHECK(23) break;
            case 20:
                CHECK(21) break;
            case 21:
                CHECK(19) break;
            case 22:
                CHECK(16) break;
            case 23:
                CHECK(7) break;
            case 24:
                CHECK(26) break;
            case 25:
                CHECK(12) break;
            case 26:
                CHECK(18) break;
            case 27:
                CHECK(6) break;
            case 28:
                CHECK(11) break;
            case 29:
                CHECK(5) break;
            case 30:
                CHECK(10) break;
            case 31:
                CHECK(9) break;
            case 32:
                CHECK(32) break;    // 0
            case 33:
                CHECK(33) break;    // 1
            case 34:
                CHECK(60) break;    // 28
            case 35:
                CHECK(34) break;    // 2
            case 36:
                CHECK(61) break;    // 29
            case 37:
                CHECK(46) break;    // 14
            case 38:
                CHECK(56) break;    // 24
            case 39:
                CHECK(35) break;    // 3
            case 40:
                CHECK(62) break;    // 30
            case 41:
                CHECK(54) break;    // 22
            case 42:
                CHECK(52) break;    // 20
            case 43:
                CHECK(47) break;    // 15
            case 44:
                CHECK(57) break;    // 25
            case 45:
                CHECK(49) break;    // 17
            case 46:
                CHECK(36) break;    // 4
            case 47:
                CHECK(40) break;    // 8
            case 48:
                CHECK(63) break;    // 31
            case 49:
                CHECK(59) break;    // 27
            case 50:
                CHECK(45) break;    // 13
            case 51:
                CHECK(55) break;    // 23
            case 52:
                CHECK(53) break;    // 21
            case 53:
                CHECK(51) break;    // 19
            case 54:
                CHECK(48) break;    // 16
            case 55:
                CHECK(39) break;    // 7
            case 56:
                CHECK(58) break;    // 26
            case 57:
                CHECK(44) break;    // 12
            case 58:
                CHECK(50) break;    // 18
            case 59:
                CHECK(38) break;    //  6
            case 60:
                CHECK(43) break;    // 11
            case 61:
                CHECK(37) break;    //  5
            case 62:
                CHECK(42) break;    // 10
            case 63:
                CHECK(41) break;    //  9
            default:
                break;
            }
        }
        return res;
    }
};

#undef CHECK_0
#undef CHECK

}   // namespace recombine
}   // namespace clotho
#endif  // SCAN_CLASSIFY_SWITCH_BIT_BLOCK_RECOMBINER_HPP_
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015, Lakshman Anumolu, Pradeep Garigipati
 * All rights reserved.
 *
 * This file is part of MeshIO whose distribution is governed by
 * the BSD 2-Clause License contained in the accompanying LICENSE.txt
 * file.
 */

#include <gtest/gtest.h>
#include <meshio/stl.hpp>
#include "testHelpers.hpp"

using namespace std;
using namespace meshio;

TEST(STL, READ_BINARY)
{
    /* Reference STLData object */
    vector< STLData<float> > referenceObjs;
    initializeReferenceSTLObj(referenceObjs);

    /* Read stl file using library function */
    vector< STLData<float> > objs;
    stl::read<float>(objs, TEST_DIR "/cube_binary.stl");

    EXPECT_TRUE((objs[0] == referenceObjs[0]) == true);

    referenceObjs.clear();
    objs.clear();
}

TEST(STL, READ_ASCII)
{
    /* Reference STLData object */
    vector< STLData<float> > referenceObjs;
    initializeReferenceSTLObj(referenceObjs);

    vector< STLData<float> > objs;
    stl::read<float>(objs, TEST_DIR "/cube_ascii.stl");

    EXPECT_TRUE((objs[0] == referenceObjs[0]) == true);

    referenceObjs.clear();
    objs.clear();
}

TEST(STL, WRITE_ASCII)
{
    /* Reference STLData object */
    vector< STLData<float> > referenceObjs;
    initializeReferenceSTLObj(referenceObjs);

    vector< STLData<float> > objs;
    stl::read<float>(objs, TEST_DIR "/cube_ascii.stl");
    stl::write(TEST_DIR "/cube_ascii2ascii.stl", meshio::STL_ASCII, objs);

    referenceObjs.clear();
    objs.clear();
}

TEST(STL, CROSS_CHECK)
{
    vector< STLData<float> > binReadObjs;
    stl::read<float>(binReadObjs, TEST_DIR "/cube_binary.stl");

    vector< STLData<float> > asciiReadObjs;
    stl::read<float>(asciiReadObjs, TEST_DIR "/cube_ascii.stl");

    EXPECT_TRUE((binReadObjs[0] == asciiReadObjs[0]) == true);
}<commit_msg>Corrected unit test condition checks<commit_after>/*
 * Copyright (c) 2015, Lakshman Anumolu, Pradeep Garigipati
 * All rights reserved.
 *
 * This file is part of MeshIO whose distribution is governed by
 * the BSD 2-Clause License contained in the accompanying LICENSE.txt
 * file.
 */

#include <gtest/gtest.h>
#include <meshio/stl.hpp>
#include "testHelpers.hpp"

using namespace std;
using namespace meshio;

TEST(STL, READ_BINARY)
{
    /* Reference STLData object */
    vector< STLData<float> > referenceObjs;
    initializeReferenceSTLObj(referenceObjs);

    /* Read stl file using library function */
    vector< STLData<float> > objs;
    stl::read<float>(objs, TEST_DIR "/cube_binary.stl");

    EXPECT_TRUE(objs[0] == referenceObjs[0]);

    referenceObjs.clear();
    objs.clear();
}

TEST(STL, READ_ASCII)
{
    /* Reference STLData object */
    vector< STLData<float> > referenceObjs;
    initializeReferenceSTLObj(referenceObjs);

    vector< STLData<float> > objs;
    stl::read<float>(objs, TEST_DIR "/cube_ascii.stl");

    EXPECT_TRUE(objs[0] == referenceObjs[0]);

    referenceObjs.clear();
    objs.clear();
}

TEST(STL, WRITE_ASCII)
{
    /* Reference STLData object */
    vector< STLData<float> > referenceObjs;
    initializeReferenceSTLObj(referenceObjs);

    vector< STLData<float> > objs;
    stl::read<float>(objs, TEST_DIR "/cube_ascii.stl");
    stl::write(TEST_DIR "/cube_ascii2ascii.stl", meshio::STL_ASCII, objs);

    referenceObjs.clear();
    objs.clear();
}

TEST(STL, CROSS_CHECK)
{
    vector< STLData<float> > binReadObjs;
    stl::read<float>(binReadObjs, TEST_DIR "/cube_binary.stl");

    vector< STLData<float> > asciiReadObjs;
    stl::read<float>(asciiReadObjs, TEST_DIR "/cube_ascii.stl");

    EXPECT_TRUE(binReadObjs[0] == asciiReadObjs[0]);
}<|endoftext|>


<commit_before>#include "calc.cpp"
#include <iostream>

int main(){
	while (std::cin) {
		std::cout << "Please type your term to calculate: ";
		int result = calc(std::cin);
		std::cout << "= " << result << '\n';
	}
}
<commit_msg>simpler prompt<commit_after>#include "calc.cpp"
#include <iostream>

int main(){
	while (std::cin) {
		std::cout << "Please type your input term: ";
		int result = calc(std::cin);
		std::cout << "= " << result << '\n';
	}
}
<|endoftext|>