<commit_before>#include <cctype>

#include <libport/lexical-cast.hh>

#include <object/format-info.hh>
#include <object/symbols.hh>

namespace object
{
  FormatInfo::FormatInfo()
    : alignment_(Align::RIGHT)
    , alt_(false)
    , case_(Case::UNDEFINED)
    , group_(0)
    , pad_(' ')
    , pattern_("%s")
    , precision_(6)
    , prefix_(0)
    , spec_('s')
    , width_(0)
  {
    proto_add(proto ? proto : Object::proto);
  }

  FormatInfo::FormatInfo(rFormatInfo model)
    : alignment_(model->alignment_)
    , alt_(model->alt_)
    , case_(model->case_)
    , group_(model->group_)
    , pad_(model->pad_)
    , pattern_(model->pattern_)
    , precision_(model->precision_)
    , prefix_(model->prefix_)
    , spec_(model->spec_)
    , width_(model->width_)
  {
    proto_add(model);
  }

  void
  FormatInfo::init(const std::string& pattern)
  {
    size_t cursor = 0;
    bool piped;

    if (pattern.empty())
      RAISE("format: pattern is empty");
    if (pattern[0] != '%')
      RAISE("format: pattern \"" + pattern + "\" doesn't begin with %");
    if (pattern.size() == 1)
      RAISE("format: trailing `%'");
    if ((piped = pattern[1] == '|'))
    {
      size_t pos = pattern.find_first_of('|', 2);
      if (pos == pattern.npos)
        RAISE("format: format begins with '|' but "
              "does not end with '|'");
      pattern_ = pattern.substr(0, pos + 1);
      cursor++;
    }

    // Parsing flags.
    std::string flags("-=+#0 '");
    std::string excludes("");
    char current;
    while ((pattern.size() > ++cursor)
           && flags.npos != flags.find(current = pattern[cursor]))
    {
// FIXME:
//    if (excludes.npos != excludes.find(current))
//      WARNING;
      switch (current)
      {
        case '-': alignment_ = Align::LEFT; excludes += "-="; break;
        case '=': alignment_ = Align::CENTER; excludes += "-="; break;
        case '+': prefix_ = '+'; excludes + " +"; break;
        case ' ': prefix_ = ' '; excludes + " +"; break;
        case '0': pad_ = '0'; break;
        case '#': alt_ = true; break;
        case '\'': group_ = ' '; break;
      };
    }

    // Parsing width.
    std::string substr_ =
      pattern.substr(cursor,
                     pattern.find_first_not_of("0123456789",
                                               cursor) - cursor);
    if (substr_.size())
    {
      width_ = lexical_cast<size_t>(substr_);
      cursor += substr_.size();
    }

    // Parsing precision.
    if (pattern.size() > cursor && pattern[cursor] == '.' && cursor++)
    {
      substr_ =
        pattern.substr(cursor, pattern.find_first_not_of("0123456789",
                                                         cursor) - cursor);
      if (substr_.size())
      {
        precision_ = lexical_cast<unsigned int>(substr_);
        cursor += substr_.size();
      }
      else
        runner::raise_primitive_error(std::string("format: Unexpected \"")
                                      + pattern[cursor]
                                      + "\", expected width ([1-9][0-9]*).");
    }

    // Parsing spec.
    if ((piped && cursor < pattern_.size() - 1)
        || (!piped && cursor < pattern.size()))
    {
      spec_ = tolower(current = pattern[cursor]);
      substr_ = "sdbxoefgEGDX";
      if (substr_.find(spec_) != substr_.npos)
      {
        if (spec_ != 's')
          case_ = (islower(pattern[cursor])) ? Case::LOWER : Case::UPPER;
      }
      else
        runner::raise_primitive_error
          (std::string("format: \"") + spec_
           + "\" is not a valid conversion type character");
    }

    int overflow;
    if (!piped)
      pattern_ = pattern.substr(0, ++cursor);
    else if (0 < (overflow = pattern_.size() - 1 - ++cursor))
      RAISE("format: format is between pipes and \""
            + pattern_.substr(cursor, overflow)
            + "\" are too many characters");
  }

  std::string
  FormatInfo::as_string() const
  {
    return pattern_;
  }

  void
  FormatInfo::initialize(CxxObject::Binder<FormatInfo>& bind)
  {
    bind(SYMBOL(init), &FormatInfo::init);
    bind(SYMBOL(asString), &FormatInfo::as_string);
    bind(SYMBOL(width), &FormatInfo::width_get);
    bind(SYMBOL(precision), &FormatInfo::precision_get);
    bind(SYMBOL(alt), &FormatInfo::alt_get);
    bind(SYMBOL(prefix), &FormatInfo::prefix_get);
    bind(SYMBOL(alignment), &FormatInfo::alignment_get);
    bind(SYMBOL(uppercase), &FormatInfo::case_get);
    bind(SYMBOL(group), &FormatInfo::group_get);
    bind(SYMBOL(pad), &FormatInfo::pad_get);
    bind(SYMBOL(spec), &FormatInfo::spec_get);
    bind(SYMBOL(pattern), &FormatInfo::pattern_get);
  }

  rObject
  FormatInfo::proto_make()
  {
    return new FormatInfo();
  }

  URBI_CXX_OBJECT_REGISTER(FormatInfo);
} // namespace object
<commit_msg>format-info: style changes.<commit_after>#include <cctype>

#include <libport/lexical-cast.hh>

#include <object/format-info.hh>
#include <object/symbols.hh>

namespace object
{
  FormatInfo::FormatInfo()
    : alignment_(Align::RIGHT)
    , alt_(false)
    , case_(Case::UNDEFINED)
    , group_(0)
    , pad_(' ')
    , pattern_("%s")
    , precision_(6)
    , prefix_(0)
    , spec_('s')
    , width_(0)
  {
    proto_add(proto ? proto : Object::proto);
  }

  FormatInfo::FormatInfo(rFormatInfo model)
    : alignment_(model->alignment_)
    , alt_(model->alt_)
    , case_(model->case_)
    , group_(model->group_)
    , pad_(model->pad_)
    , pattern_(model->pattern_)
    , precision_(model->precision_)
    , prefix_(model->prefix_)
    , spec_(model->spec_)
    , width_(model->width_)
  {
    proto_add(model);
  }

  void
  FormatInfo::init(const std::string& pattern)
  {
    size_t cursor = 0;
    bool piped;

    if (pattern.empty())
      RAISE("format: pattern is empty");
    if (pattern[0] != '%')
      RAISE("format: pattern \"" + pattern + "\" doesn't begin with %");
    if (pattern.size() == 1)
      RAISE("format: trailing `%'");
    if ((piped = pattern[1] == '|'))
    {
      size_t pos = pattern.find_first_of('|', 2);
      if (pos == pattern.npos)
        RAISE("format: format begins with '|' but "
              "does not end with '|'");
      pattern_ = pattern.substr(0, pos + 1);
      cursor++;
    }

    // Parsing flags.
    std::string flags("-=+#0 '");
    std::string excludes("");
    char current;
    while ((pattern.size() > ++cursor)
           && flags.npos != flags.find(current = pattern[cursor]))
    {
// FIXME:
//    if (excludes.npos != excludes.find(current))
//      WARNING;
      switch (current)
      {
        case '-': alignment_ = Align::LEFT;   excludes += "-="; break;
        case '=': alignment_ = Align::CENTER; excludes += "-="; break;
        case '+': prefix_ = '+';              excludes += " +"; break;
        case ' ': prefix_ = ' ';              excludes += " +"; break;
        case '0': pad_ = '0'; break;
        case '#': alt_ = true; break;
        case '\'': group_ = ' '; break;
      };
    }

    // Parsing width.
    std::string substr_ =
      pattern.substr(cursor,
                     pattern.find_first_not_of("0123456789",
                                               cursor) - cursor);
    if (substr_.size())
    {
      width_ = lexical_cast<size_t>(substr_);
      cursor += substr_.size();
    }

    // Parsing precision.
    if (pattern.size() > cursor && pattern[cursor] == '.' && cursor++)
    {
      substr_ =
        pattern.substr(cursor, pattern.find_first_not_of("0123456789",
                                                         cursor) - cursor);
      if (substr_.size())
      {
        precision_ = lexical_cast<unsigned int>(substr_);
        cursor += substr_.size();
      }
      else
        runner::raise_primitive_error(std::string("format: Unexpected \"")
                                      + pattern[cursor]
                                      + "\", expected width ([1-9][0-9]*).");
    }

    // Parsing spec.
    if ((piped && cursor < pattern_.size() - 1)
        || (!piped && cursor < pattern.size()))
    {
      spec_ = tolower(current = pattern[cursor]);
      substr_ = "sdbxoefgEGDX";
      if (substr_.find(spec_) != substr_.npos)
      {
        if (spec_ != 's')
          case_ = (islower(pattern[cursor])) ? Case::LOWER : Case::UPPER;
      }
      else
        runner::raise_primitive_error
          (std::string("format: \"") + spec_
           + "\" is not a valid conversion type character");
    }

    int overflow;
    if (!piped)
      pattern_ = pattern.substr(0, ++cursor);
    else if (0 < (overflow = pattern_.size() - 1 - ++cursor))
      RAISE("format: format is between pipes and \""
            + pattern_.substr(cursor, overflow)
            + "\" are too many characters");
  }

  std::string
  FormatInfo::as_string() const
  {
    return pattern_;
  }

  void
  FormatInfo::initialize(CxxObject::Binder<FormatInfo>& bind)
  {
    bind(SYMBOL(init), &FormatInfo::init);
    bind(SYMBOL(asString), &FormatInfo::as_string);
    bind(SYMBOL(width), &FormatInfo::width_get);
    bind(SYMBOL(precision), &FormatInfo::precision_get);
    bind(SYMBOL(alt), &FormatInfo::alt_get);
    bind(SYMBOL(prefix), &FormatInfo::prefix_get);
    bind(SYMBOL(alignment), &FormatInfo::alignment_get);
    bind(SYMBOL(uppercase), &FormatInfo::case_get);
    bind(SYMBOL(group), &FormatInfo::group_get);
    bind(SYMBOL(pad), &FormatInfo::pad_get);
    bind(SYMBOL(spec), &FormatInfo::spec_get);
    bind(SYMBOL(pattern), &FormatInfo::pattern_get);
  }

  rObject
  FormatInfo::proto_make()
  {
    return new FormatInfo();
  }

  URBI_CXX_OBJECT_REGISTER(FormatInfo);
} // namespace object
<|endoftext|>


<commit_before>#include <libport/containers.hh>

#include <object/object.hh>

namespace object
{
  bool HashSlots::set(const key_type& key, value_type v)
  {
    std::pair<iterator, bool> where = content_.insert(std::make_pair(key, v));
    if (!where.second)
      return false;
    return true;
  }

  void HashSlots::update(const key_type& key, value_type v)
  {
    content_[key] = v;
  }

  HashSlots::value_type HashSlots::get(const key_type& key) const
  {
    const_iterator where = content_.find(key);
    if (where == content_.end())
      return 0;
    return where->second;
  }

  void HashSlots::erase(const key_type& key)
  {
    content_.erase(key);
  }

  bool HashSlots::has(const key_type& key) const
  {
    return libport::mhas(content_, key);
  }

  inline HashSlots::iterator
  HashSlots::begin()
  {
    return content_.begin();
  }

  inline HashSlots::iterator
  HashSlots::end()
  {
    return content_.end();
  }

  inline HashSlots::const_iterator
  HashSlots::begin() const
  {
    return content_.begin();
  }

  inline HashSlots::const_iterator
  HashSlots::end() const
  {
    return content_.end();
  }
}
<commit_msg>Formatting change.<commit_after>#include <libport/containers.hh>

#include <object/object.hh>

namespace object
{
  inline bool
  HashSlots::set(const key_type& key, value_type v)
  {
    std::pair<iterator, bool> where = content_.insert(std::make_pair(key, v));
    if (!where.second)
      return false;
    return true;
  }

  inline void
  HashSlots::update(const key_type& key, value_type v)
  {
    content_[key] = v;
  }

  inline HashSlots::value_type
  HashSlots::get(const key_type& key) const
  {
    const_iterator where = content_.find(key);
    if (where == content_.end())
      return 0;
    return where->second;
  }

  inline void
  HashSlots::erase(const key_type& key)
  {
    content_.erase(key);
  }

  inline bool
  HashSlots::has(const key_type& key) const
  {
    return libport::mhas(content_, key);
  }

  inline HashSlots::iterator
  HashSlots::begin()
  {
    return content_.begin();
  }

  inline HashSlots::iterator
  HashSlots::end()
  {
    return content_.end();
  }

  inline HashSlots::const_iterator
  HashSlots::begin() const
  {
    return content_.begin();
  }

  inline HashSlots::const_iterator
  HashSlots::end() const
  {
    return content_.end();
  }
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_get_poundv_bucket_attr.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file p9_pm_get_poundv_bucket_attr.C
/// @brief Grab PM data from certain bucket in #V keyword in LRPX record
///

// ----------------------------------------------------------------------
// Includes
// ----------------------------------------------------------------------
#include <p9_pm_get_poundv_bucket_attr.H>
#include <mvpd_access_defs.H>
#include <attribute_ids.H>

fapi2::ReturnCode p9_pm_get_poundv_bucket_attr(
    const fapi2::Target<fapi2::TARGET_TYPE_EQ>& i_target,
    uint8_t* o_data)
{
    FAPI_IMP("Entering p9_pm_get_poundv_bucket_attr ....");
    uint8_t* l_prDataPtr = NULL;
    uint8_t* l_fullVpdData = NULL;
    uint8_t l_overridePresent = 0;
    uint32_t l_vpdSize = 0;
    uint8_t l_eqChipUnitPos = 0;
    uint8_t l_bucketId;
    fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS;

    do
    {
        //To read MVPD we will need the proc parent of the inputted EQ target
        fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_procParent =
            i_target.getParent<fapi2::TARGET_TYPE_PROC_CHIP>();

        //check if bucket num has been overriden
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_POUNDV_BUCKET_NUM_OVERRIDE,
                               i_target,
                               l_overridePresent));

        if(l_overridePresent != 0)
        {
            //If it has been overriden then get the override
            FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_POUNDV_BUCKET_NUM,
                                   i_target,
                                   l_bucketId));
        }
        else
        {
            //Otherwise get the bucket num from MVPD data
            //First read is to get size of vpd record, note the o_buffer is NULL
            l_rc = getMvpdField(fapi2::MVPD_RECORD_VINI,
                                fapi2::MVPD_KEYWORD_PR,
                                l_procParent,
                                NULL,
                                l_vpdSize);

            if(l_rc)
            {
                FAPI_ERR("p9_pm_get_poundv_bucket_attr:: Error reading PR keyword size from VINI record");
                break;
            }

            l_prDataPtr = reinterpret_cast<uint8_t*>(malloc(l_vpdSize));

            //Second read is to get data of vpd record
            l_rc = getMvpdField(fapi2::MVPD_RECORD_VINI,
                                fapi2::MVPD_KEYWORD_PR,
                                l_procParent,
                                l_prDataPtr,
                                l_vpdSize);

            if(l_rc)
            {
                FAPI_ERR("p9_pm_get_poundv_bucket_attr:: Error reading PR keyword from VINI record");
                break;
            }

            //Bucket ID is byte[4] of the PR keyword
            memcpy(&l_bucketId, (l_prDataPtr + 4), sizeof(uint8_t));
        }

        //Need to determine which LRP record to read from depending on which
        //bucket we are getting the power management data from. FapiPos will
        //tell us which LRP record to use.
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CHIP_UNIT_POS,
                               i_target,
                               l_eqChipUnitPos));
        fapi2::MvpdRecord lprRecord = fapi2::MVPD_RECORD_LRP0;

        switch(l_eqChipUnitPos)
        {
            case 0:
                lprRecord = fapi2::MVPD_RECORD_LRP0;
                break;

            case 1:
                lprRecord = fapi2::MVPD_RECORD_LRP1;
                break;

            case 2:
                lprRecord = fapi2::MVPD_RECORD_LRP2;
                break;

            case 3:
                lprRecord = fapi2::MVPD_RECORD_LRP3;
                break;

            case 4:
                lprRecord = fapi2::MVPD_RECORD_LRP4;
                break;

            case 5:
                lprRecord = fapi2::MVPD_RECORD_LRP5;
                break;

            default:
                FAPI_ERR("No LRP record found for EQ with fapi pos = %d", l_eqChipUnitPos);
                fapi2::Assert(0);
                break;
        }

        //Reset VPD size because we want to find size of another VPD record
        l_vpdSize = 0;

        //First read is to get size of vpd record, note the o_buffer is NULL
        l_rc = getMvpdField(lprRecord,
                            fapi2::MVPD_KEYWORD_PDV,
                            l_procParent,
                            NULL,
                            l_vpdSize);

        if(l_rc)
        {
            FAPI_ERR("p9_pm_get_poundv_bucket_attr:: Error reading PDV keyword size from LRP%d record", l_eqChipUnitPos);
            break;
        }

        //Allocate memory for vpd data
        l_fullVpdData = reinterpret_cast<uint8_t*>(malloc(l_vpdSize));

        FAPI_ASSERT(l_vpdSize - 4 - ((l_bucketId - 1) * 0x33) >= sizeof(fapi2::ATTR_POUNDV_BUCKET_DATA_Type),
                    fapi2::BAD_VPD_READ()
                    .set_EXPECTED_SIZE(sizeof(fapi2::ATTR_POUNDV_BUCKET_DATA_Type))
                    .set_ACTUAL_SIZE(l_vpdSize - 4 - ((l_bucketId - 1) * 0x33)),
                    "#V data read was too small!" );

        //Second read is to get data of vpd record
        l_rc = getMvpdField(lprRecord,
                            fapi2::MVPD_KEYWORD_PDV,
                            l_procParent,
                            l_fullVpdData,
                            l_vpdSize);

        if(l_rc)
        {
            FAPI_ERR("p9_pm_get_poundv_bucket_attr:: Error reading PDV keyword from LRP%d record", l_eqChipUnitPos);
            break;
        }

        //#V record is laid out as follows:
        //Name:     0x2 byte
        //Length:   0x2 byte
        //Version:  0x1 byte **buffer starts here
        //PNP:      0x3 byte
        //bucket a: 0x33 byte
        //bucket b: 0x33 byte
        //bucket c: 0x33 byte
        //bucket d: 0x33 byte
        //bucket e: 0x33 byte
        //bucket f: 0x33 byte
        if( *l_fullVpdData == 0x2)
        {
            memcpy(o_data,
                   l_fullVpdData + 4 + (l_bucketId - 1) * 0x33,
                   sizeof(fapi2::ATTR_POUNDV_BUCKET_DATA_Type));
        }
        else
        {
            FAPI_ERR("p9_pm_get_poundv_bucket_attr::Invalid #V record version: 0x%x", *l_fullVpdData);
            FAPI_ASSERT(0,
                        fapi2::INVALID_POUNDV_VERSION()
                        .set_POUNDV_VERSION(*l_fullVpdData),
                        "#V is of an invalid version!" );
        }

    }
    while(0);

fapi_try_exit:

    if(l_fullVpdData != NULL)
    {
        free(l_fullVpdData);
    }

    if(l_prDataPtr != NULL)
    {
        free(l_prDataPtr);
    }

    FAPI_IMP("Exiting p9_pm_get_poundv_bucket_attr ....");

    //If there is no issue in the current err, check
    //the local rc to see if the mvpd access methods caught anything
    if((fapi2::current_err == fapi2::FAPI2_RC_SUCCESS) &&
       (l_rc != fapi2::FAPI2_RC_SUCCESS))
    {
        fapi2::current_err = l_rc;
    }

    return fapi2::current_err;
}
<commit_msg>Modify #V accessor function for new version 3<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_get_poundv_bucket_attr.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file p9_pm_get_poundv_bucket_attr.C
/// @brief Grab PM data from certain bucket in #V keyword in LRPX record
///

// ----------------------------------------------------------------------
// Includes
// ----------------------------------------------------------------------
#include <p9_pm_get_poundv_bucket_attr.H>
#include <p9_pm_get_poundv_bucket.H>
#include <mvpd_access_defs.H>
#include <attribute_ids.H>

fapi2::ReturnCode p9_pm_get_poundv_bucket_attr(
    const fapi2::Target<fapi2::TARGET_TYPE_EQ>& i_target,
    uint8_t* o_data)
{
    FAPI_DBG("Entering p9_pm_get_poundv_bucket_attr ....");
    uint8_t* l_prDataPtr = NULL;
    uint8_t* l_fullVpdData = NULL;
    uint8_t l_overridePresent = 0;
    uint32_t l_tempVpdSize = 0;
    uint32_t l_vpdSize = 0;
    uint8_t l_eqChipUnitPos = 0;
    uint8_t l_bucketId;
    uint8_t l_bucketSize = 0;
    uint32_t l_sysNestFreq = 0;
    fapi2::voltageBucketData_t* l_currentBucket = NULL;
    uint8_t l_numMatches = 0;

    fapi2::MvpdRecord lrpRecord = fapi2::MVPD_RECORD_LAST;
    //To read MVPD we will need the proc parent of the inputted EQ target
    fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_procParent =
        i_target.getParent<fapi2::TARGET_TYPE_PROC_CHIP>();

    //Need to determine which LRP record to read from depending on which
    //bucket we are getting the power management data from. FapiPos will
    //tell us which LRP record to use.
    FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CHIP_UNIT_POS,
                           i_target,
                           l_eqChipUnitPos));

    FAPI_ASSERT( l_eqChipUnitPos < NUM_BUCKETS ,
                 fapi2::INVALID_EQ_CHIP_POS().
                 set_EQ_POSITION( l_eqChipUnitPos ),
                 "Invalid EQ chip unit position = 0x%X",
                 l_eqChipUnitPos);

    //The enumeration for the LRPx records are just 3 more
    //than the EQ chip unit pos. See mvpd_access_defs.H
    lrpRecord = static_cast<fapi2::MvpdRecord>(l_eqChipUnitPos +
                fapi2::MVPD_RECORD_LRP0 );

    //check if bucket num has been overriden
    FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_POUNDV_BUCKET_NUM_OVERRIDE,
                           i_target,
                           l_overridePresent));

    if(l_overridePresent != 0)
    {
        //If it has been overriden then get the override
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_POUNDV_BUCKET_NUM,
                               i_target,
                               l_bucketId));
    }
    else
    {
        //First read is to get size of vpd record, note the o_buffer is NULL
        FAPI_TRY( getMvpdField(lrpRecord,
                               fapi2::MVPD_KEYWORD_PDV,
                               l_procParent,
                               NULL,
                               l_tempVpdSize) );

        //save off the actual vpd size
        l_vpdSize = l_tempVpdSize;
        //Allocate memory for vpd data
        l_fullVpdData = reinterpret_cast<uint8_t*>(malloc(l_tempVpdSize));

        //Second read is to get data of vpd record
        FAPI_TRY( getMvpdField(lrpRecord,
                               fapi2::MVPD_KEYWORD_PDV,
                               l_procParent,
                               l_fullVpdData,
                               l_tempVpdSize) );

        //Version 2:
        //#V record is laid out as follows:
        //Name:     0x2 byte
        //Length:   0x2 byte
        //Version:  0x1 byte **buffer starts here
        //PNP:      0x3 byte
        //bucket a: 0x33 byte
        //bucket b: 0x33 byte
        //bucket c: 0x33 byte
        //bucket d: 0x33 byte
        //bucket e: 0x33 byte
        //bucket f: 0x33 byte
        if( *l_fullVpdData == POUNDV_VERSION_2)
        {
            //Set the size of the bucket
            l_bucketSize = VERSION_2_BUCKET_SIZE;

            //Reset VPD size because we want to find size of another VPD record
            l_tempVpdSize = 0;

            //First read is to get size of vpd record, note the o_buffer is NULL
            FAPI_TRY( getMvpdField(fapi2::MVPD_RECORD_VINI,
                                   fapi2::MVPD_KEYWORD_PR,
                                   l_procParent,
                                   NULL,
                                   l_tempVpdSize) );

            l_prDataPtr = reinterpret_cast<uint8_t*>(malloc(l_tempVpdSize));

            //Second read is to get data of vpd record
            FAPI_TRY( getMvpdField(fapi2::MVPD_RECORD_VINI,
                                   fapi2::MVPD_KEYWORD_PR,
                                   l_procParent,
                                   l_prDataPtr,
                                   l_tempVpdSize) );

            //Bucket ID is byte[4] of the PR keyword
            memcpy(&l_bucketId, (l_prDataPtr + 4), sizeof(uint8_t));

        }
        //Version 3:
        //#V record is laid out as follows:
        //Name:     0x2 byte
        //Length:   0x2 byte
        //Version:  0x1 byte **buffer starts here
        //PNP:      0x3 byte
        //bucket a: 0x3D byte
        //bucket b: 0x3D byte
        //bucket c: 0x3D byte
        //bucket d: 0x3D byte
        //bucket e: 0x3D byte
        //bucket f: 0x3D byte
        else if( *l_fullVpdData == POUNDV_VERSION_3 )
        {
            // Set the size of the bucket
            l_bucketSize = VERSION_3_BUCKET_SIZE;

            // Version 3 uses the nest frequency to choose the bucket Id
            // get the system target to find the NEST_FREQ_MHZ
            fapi2::Target<fapi2::TARGET_TYPE_SYSTEM> l_sysParent;

            FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_FREQ_PB_MHZ,
                                   l_sysParent,
                                   l_sysNestFreq));
            //cast the voltage data into an array of buckets
            fapi2::voltageBucketData_t* l_buckets = reinterpret_cast
                                                    <fapi2::voltageBucketData_t*>
                                                    (l_fullVpdData + POUNDV_BUCKET_OFFSET);

            for(int i = 0; i < NUM_BUCKETS; i++)
            {
                if(l_buckets[i].pbFreq == l_sysNestFreq)
                {
                    l_numMatches++;

                    /*
                     * TODO RTC: 157341
                     * Modify function to fail if multiple buckets have same nest
                     */
                    if(l_numMatches > 1)
                    {
                        FAPI_ERR("p9_pm_get_poundv_bucket_attr::"
                                 " Multiple buckets (%d) reporting the same nest frequency",
                                 l_numMatches);
                    }

                    /*
                    else
                    {
                        l_currentBucket = &l_buckets[i];
                    }
                    */
                    l_currentBucket = &l_buckets[i];
                }
            }

            /*
             * TODO RTC: 157341
             * Modify function to fail if multiple buckets have same nest
             if(l_numMatches == 1)
             {
                 l_bucketId = l_currentBucket->bucketId;
             }
             else
             {

                 FAPI_ERR("p9_pm_get_poundv_bucket_attr::Invalid number of matching nest freqs found. Matches found = %d",
                          l_numMatches );
                 FAPI_ASSERT(0,
                             fapi2::INVALID_MATCHING_FREQ_NUMBER().
                             set_MATCHES_FOUND(l_numMatches),
                             "Matches found is NOT 1" );
             }
             */
            l_bucketId = l_currentBucket->bucketId;
        }
        else
        {
            FAPI_ASSERT( false,
                         fapi2::INVALID_POUNDV_VERSION()
                         .set_POUNDV_VERSION(*l_fullVpdData),
                         "p9_pm_get_poundv_bucket_attr::Invalid #V record version: 0x%x",
                         *l_fullVpdData);
        }

        // This assert ensures the size of the calculated data is correct
        FAPI_ASSERT(l_vpdSize - POUNDV_BUCKET_OFFSET - ((l_bucketId - 1) *
                    l_bucketSize) >= l_bucketSize,
                    fapi2::BAD_VPD_READ().set_EXPECTED_SIZE(sizeof(l_bucketSize))
                    .set_ACTUAL_SIZE(l_vpdSize - POUNDV_BUCKET_OFFSET -
                                     ((l_bucketId - 1) * l_bucketSize)),
                    "#V data read was too small!" );

    }// else no override

    // Ensure we got a valid bucket id
    // NOTE: Bucket IDs range from 1-6
    FAPI_ASSERT( (l_bucketId <= NUM_BUCKETS) && (l_bucketId != 0),
                 fapi2::INVALID_BUCKET_ID()
                 .set_TARGET(i_target)
                 .set_BUCKET_ID(l_bucketId),
                 "Invalid Bucket Id = %d",
                 l_bucketId );

    // Use the selected bucket id to populate the output data
    memcpy(o_data,
           l_fullVpdData + POUNDV_BUCKET_OFFSET + (l_bucketId - 1) * l_bucketSize,
           l_bucketSize);

fapi_try_exit:

    if(l_fullVpdData != NULL)
    {
        free(l_fullVpdData);
        l_fullVpdData = NULL;
    }

    if(l_prDataPtr != NULL)
    {
        free(l_prDataPtr);
        l_prDataPtr = NULL;
    }

    FAPI_DBG("Exiting p9_pm_get_poundv_bucket_attr ....");

    return fapi2::current_err;
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"
#include "bzfgl.h"
#include "OpenGLTexture.h"
#include "OpenGLGState.h"

#if defined(GL_VERSION_1_1)
#  define	BZF_TEXTURE_OBJECT
#elif defined(GL_EXT_texture_object)
#  define	BZF_TEXTURE_OBJECT
#  define	glBindTexture		glBindTextureEXT
#  define	glDeleteTextures	glDeleteTexturesEXT
#  define	glGenTextures		glGenTexturesEXT
#endif

#if defined(GL_VERSION_1_1)
#  define	BZF_INTENSITY_FORMAT	GL_INTENSITY
#elif defined(GL_EXT_texture)
#  define	BZF_INTENSITY_FORMAT	GL_INTENSITY_EXT
#else
#  define	BZF_INTENSITY_FORMAT	0 /* not used */
#endif

static int		hasTextureObject = -1;

//
// OpenGLTexture::Rep
//

const GLenum		OpenGLTexture::minifyFilter[] = {
				GL_NEAREST,
				GL_NEAREST,
				GL_LINEAR,
				GL_NEAREST_MIPMAP_NEAREST,
				GL_LINEAR_MIPMAP_NEAREST,
				GL_NEAREST_MIPMAP_LINEAR,
				GL_LINEAR_MIPMAP_LINEAR
			};
const GLenum		OpenGLTexture::magnifyFilter[] = {
				GL_NEAREST,
				GL_NEAREST,
				GL_LINEAR,
				GL_NEAREST,
				GL_LINEAR,
				GL_NEAREST,
				GL_LINEAR
			};
const char*		OpenGLTexture::configFilterValues[] = {
				"no",
				"nearest",
				"linear",
				"nearestmipmapnearest",
				"linearmipmapnearest",
				"nearestmipmaplinear",
				"linearmipmaplinear"
};

//
// OpenGLTexture
//

OpenGLTexture::Filter	OpenGLTexture::filter = LinearMipmapLinear;

OpenGLTexture::OpenGLTexture(int _width, int _height,
				const GLvoid* pixels,
				Filter _maxFilter,
				bool _repeat,
				int _internalFormat)
				: alpha(false),
				width(_width),
				height(_height),
				repeat(_repeat),
				internalFormat(_internalFormat),
				maxFilter(_maxFilter),
				list(0)
{
  // check for texture object extension
  if (hasTextureObject < 0) {
#if defined(GL_VERSION_1_1)
    hasTextureObject = 1;
#elif defined(GL_EXT_texture_object)
    hasTextureObject = (strstr((const char*)glGetString(GL_EXTENSIONS),
					"GL_EXT_texture_object") != NULL);
#else
    hasTextureObject = 0;
#endif // BZF_TEXTURE_OBJECT
  }

  // make texture map object/list
#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glGenTextures(1, &list);
  else
#endif // BZF_TEXTURE_OBJECT
  list = glGenLists(1);

  // get internal format if not provided
  if (internalFormat == 0)
    internalFormat = getBestFormat(width, height, pixels);

  // copy the original texture image
  image = new GLubyte[4 * width * height];
  ::memcpy(image, pixels, 4 * width * height);

  { int patlabor_remove_this_once_tm_does_this; }
  setFilter(std::string("linearmipmaplinear"));

  initContext();

  // watch for context recreation
  OpenGLGState::registerContextInitializer(static_initContext, (void*)this);
}

OpenGLTexture::~OpenGLTexture()
{
  OpenGLGState::unregisterContextInitializer(static_initContext, (void*)this);

  // free image data
  delete[] image;

  // free OpenGL display list or texture object
#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0) {
    if (list) glDeleteTextures(1, &list);
  }
  else
#endif // BZF_TEXTURE_OBJECT
  if (list) glDeleteLists(list, 1);
}

void OpenGLTexture::static_initContext(void *that)
{
  ((OpenGLTexture*) that)->initContext();
}

void OpenGLTexture::initContext()
{
  // set size
  int tmpWidth = width;
  int tmpHeight = height;

  // get minimum valid size for texture (boost to 2^m x 2^n)
  GLint scaledWidth = 1, scaledHeight = 1;
  GLint maxTextureSize;
  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
  if (maxTextureSize > 512) maxTextureSize = 512;
  while (scaledWidth < tmpWidth) scaledWidth <<= 1;
  while (scaledHeight < tmpHeight) scaledHeight <<= 1;
  if (scaledWidth > maxTextureSize) scaledWidth = maxTextureSize;
  if (scaledHeight > maxTextureSize) scaledHeight = maxTextureSize;
  const int copyWidth = (scaledWidth > tmpWidth) ? scaledWidth : tmpWidth;
  const int copyHeight = (scaledHeight > tmpHeight) ? scaledHeight : tmpHeight;

  // make buffers for copied and scaled data
  GLubyte* origData = new GLubyte[4 * copyWidth * copyHeight + 4];
  GLubyte* data = (GLubyte*)(((unsigned long)origData & ~3) + 4);
  GLubyte* origScaledData = new GLubyte[4 * scaledWidth * scaledHeight + 4];
  GLubyte* scaledData = (GLubyte*)(((unsigned long)origScaledData & ~3) + 4);
  ::memcpy(data, image, 4 * tmpWidth * tmpHeight);

  // note if internal format uses alpha
  switch (internalFormat) {
    case BZF_INTENSITY_FORMAT:
    case GL_LUMINANCE_ALPHA:
#if defined(GL_LUMINANCE4_ALPHA4)
    case GL_LUMINANCE4_ALPHA4:
#elif defined(GL_LUMINANCE4_ALPHA4_EXT)
    case GL_LUMINANCE4_ALPHA4_EXT:
#endif
    case GL_RGBA:
#if defined(GL_INTENSITY4)
    case GL_INTENSITY4:
#elif defined(GL_INTENSITY4_EXT)
    case GL_INTENSITY4_EXT:
#endif
      alpha = true;
      break;

    default:
      alpha = false;
      break;
  }

  // now make texture map display list (compute all mipmaps, if requested).
  // compute next mipmap from current mipmap to save time.
  const bool mipmap = ((int)maxFilter > (int)Linear);
  GLint mipmapLevel = 0;

#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glBindTexture(GL_TEXTURE_2D, list);
  else
#endif // BZF_TEXTURE_OBJECT
  glNewList(list, GL_COMPILE);

  setFilter((Filter)maxFilter);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
			repeat ? GL_REPEAT : GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
			repeat ? GL_REPEAT : GL_CLAMP);
  do {
    bool doScale = (scaledWidth != tmpWidth || scaledHeight != tmpHeight);

    // scale image to next mipmap level
    if (doScale)
      gluScaleImage(
		GL_RGBA, tmpWidth, tmpHeight, GL_UNSIGNED_BYTE, data,
		scaledWidth ? scaledWidth : 1,
		scaledHeight ? scaledHeight : 1,
		GL_UNSIGNED_BYTE, scaledData);

    // make texture
    glTexImage2D(GL_TEXTURE_2D, mipmapLevel, internalFormat,
		scaledWidth ? scaledWidth : 1,
		scaledHeight ? scaledHeight : 1,
		0, GL_RGBA, GL_UNSIGNED_BYTE,
		doScale ? scaledData : data);

    // prepare for next iteration
    mipmapLevel++;
    tmpWidth = scaledWidth;
    tmpHeight = scaledHeight;
    scaledWidth >>= 1;
    scaledHeight >>= 1;
    if (doScale)
      ::memcpy(data, scaledData, 4 * tmpWidth * tmpHeight);
  } while (mipmap && (scaledWidth > 0 || scaledHeight > 0));

  setFilter(getFilter());

#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glBindTexture(GL_TEXTURE_2D, 0);
  else
#endif // BZF_TEXTURE_OBJECT
  glEndList();

  // free extra buffers
  delete[] origScaledData;
  delete[] origData;
}

OpenGLTexture::Filter	OpenGLTexture::getFilter()
{
  return filter;
}

std::string		OpenGLTexture::getFilterName()
{
  return configFilterValues[static_cast<int>(filter)];
}

void			OpenGLTexture::setFilter(std::string name)
{
  for (unsigned int i = 0; i < (sizeof(configFilterValues) /
				sizeof(configFilterValues[0])); i++)
    if (name == configFilterValues[i])
      setFilter(static_cast<Filter>(i));
}

void			OpenGLTexture::setFilter(Filter _filter)
{
  filter = _filter;

#if defined(BZF_TEXTURE_OBJECT)

  // can only change filters when using texture objects
  if (hasTextureObject <= 0) return;

  int filterIndex = (int) filter;
  // limit filter.  try to keep nearest... filters as nearest and
  // linear... as linear.
  if (filterIndex > maxFilter) {
    if ((filterIndex & 1) == 1)	// nearest...
      if ((maxFilter & 1) == 1) filterIndex = maxFilter;
      else filterIndex = maxFilter > 0 ? maxFilter - 1 : 0;

    else			// linear...
      if ((maxFilter & 1) == 1) filterIndex = maxFilter - 1;
      else filterIndex = maxFilter;
  }

#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glBindTexture(GL_TEXTURE_2D, list);
#endif // BZF_TEXTURE_OBJECT
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minifyFilter[filterIndex]);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnifyFilter[filterIndex]);
#endif // BZF_TEXTURE_OBJECT
}

GLuint			OpenGLTexture::getList() const
{
  return (list);
}

void			OpenGLTexture::execute()
{
  bind();
}

float			OpenGLTexture::getAspectRatio() const
{
    return ((float) height) / ((float) width);
}

void			OpenGLTexture::bind()
{
#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0) {
    if (list) glBindTexture(GL_TEXTURE_2D, list);
    else glBindTexture(GL_TEXTURE_2D, 0);
  }
  else
#endif // BZF_TEXTURE_OBJECT
  if (list) glCallList(list);
  else glTexImage2D(GL_TEXTURE_2D, 0, 3, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
}

int			OpenGLTexture::getBestFormat(
				int width, int height,
				const GLvoid* pixels)
{
  // see if all pixels are achromatic
  const GLubyte* scan = (const GLubyte*)pixels;
  const int size = width * height;
  int i;
  for (i = 0; i < size; scan += 4, i++)
    if (scan[0] != scan[1] || scan[0] != scan[2])
      break;
  const bool useLuminance = (i == size);

  // see if all pixels are opaque
  scan = (const GLubyte*)pixels;
  for (i = 0; i < size; scan += 4, i++)
    if (scan[3] != 0xff)
      break;
  const bool useAlpha = (i != size);

  // intensity format defined in 1.1 and an extension in 1.0
#if defined(GL_VERSION_1_1)
  static const bool hasTextureExt = true;
#elif defined(GL_INTENSITY_EXT)
  static const bool hasTextureExt = (strstr((const char*)
		glGetString(GL_EXTENSIONS), "GL_EXT_texture") != NULL);
#else
  static const bool hasTextureExt = false;
#endif // defined(GL_VERSION_1_1)

  // see if all pixels are r=g=b=a.  if so return intensity format.
  // SGI IMPACT and 3Dfx systems don't support GL_INTENSITY.
  const char* const glRenderer = (const char*)glGetString(GL_RENDERER);
  static bool noIntensity =
	(strncmp(glRenderer, "IMPACT", 6) == 0) ||
	(strncmp(glRenderer, "3Dfx", 4) == 0);
  if (!noIntensity) {
    bool useIntensity = false;
    if (hasTextureExt && useLuminance) {
      scan = (const GLubyte*)pixels;
      for (i = 0; i < size; scan += 4, i++)
	if (scan[3] != scan[0])
	  break;
      useIntensity = (i == size);
    }
    if (useIntensity) return BZF_INTENSITY_FORMAT;
  }

  // pick internal format
  return (useLuminance ?
		(useAlpha ? GL_LUMINANCE_ALPHA : GL_LUMINANCE) :
		(useAlpha ? GL_RGBA : GL_RGB));
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8

<commit_msg>Fixing field initialization order warning<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"
#include "bzfgl.h"
#include "OpenGLTexture.h"
#include "OpenGLGState.h"

#if defined(GL_VERSION_1_1)
#  define	BZF_TEXTURE_OBJECT
#elif defined(GL_EXT_texture_object)
#  define	BZF_TEXTURE_OBJECT
#  define	glBindTexture		glBindTextureEXT
#  define	glDeleteTextures	glDeleteTexturesEXT
#  define	glGenTextures		glGenTexturesEXT
#endif

#if defined(GL_VERSION_1_1)
#  define	BZF_INTENSITY_FORMAT	GL_INTENSITY
#elif defined(GL_EXT_texture)
#  define	BZF_INTENSITY_FORMAT	GL_INTENSITY_EXT
#else
#  define	BZF_INTENSITY_FORMAT	0 /* not used */
#endif

static int		hasTextureObject = -1;

//
// OpenGLTexture::Rep
//

const GLenum		OpenGLTexture::minifyFilter[] = {
				GL_NEAREST,
				GL_NEAREST,
				GL_LINEAR,
				GL_NEAREST_MIPMAP_NEAREST,
				GL_LINEAR_MIPMAP_NEAREST,
				GL_NEAREST_MIPMAP_LINEAR,
				GL_LINEAR_MIPMAP_LINEAR
			};
const GLenum		OpenGLTexture::magnifyFilter[] = {
				GL_NEAREST,
				GL_NEAREST,
				GL_LINEAR,
				GL_NEAREST,
				GL_LINEAR,
				GL_NEAREST,
				GL_LINEAR
			};
const char*		OpenGLTexture::configFilterValues[] = {
				"no",
				"nearest",
				"linear",
				"nearestmipmapnearest",
				"linearmipmapnearest",
				"nearestmipmaplinear",
				"linearmipmaplinear"
};

//
// OpenGLTexture
//

OpenGLTexture::Filter	OpenGLTexture::filter = LinearMipmapLinear;

OpenGLTexture::OpenGLTexture(int _width, int _height,
				const GLvoid* pixels,
				Filter _maxFilter,
				bool _repeat,
				int _internalFormat)
				: alpha(false),
				width(_width),
				height(_height),
				repeat(_repeat),
				internalFormat(_internalFormat),
				list(0),
				maxFilter(_maxFilter)
{
  // check for texture object extension
  if (hasTextureObject < 0) {
#if defined(GL_VERSION_1_1)
    hasTextureObject = 1;
#elif defined(GL_EXT_texture_object)
    hasTextureObject = (strstr((const char*)glGetString(GL_EXTENSIONS),
					"GL_EXT_texture_object") != NULL);
#else
    hasTextureObject = 0;
#endif // BZF_TEXTURE_OBJECT
  }

  // make texture map object/list
#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glGenTextures(1, &list);
  else
#endif // BZF_TEXTURE_OBJECT
  list = glGenLists(1);

  // get internal format if not provided
  if (internalFormat == 0)
    internalFormat = getBestFormat(width, height, pixels);

  // copy the original texture image
  image = new GLubyte[4 * width * height];
  ::memcpy(image, pixels, 4 * width * height);

#ifdef _MSC_VER
  // Suppose Pat want to remind himself
  { int patlabor_remove_this_once_tm_does_this; }
#endif
  setFilter(std::string("linearmipmaplinear"));

  initContext();

  // watch for context recreation
  OpenGLGState::registerContextInitializer(static_initContext, (void*)this);
}

OpenGLTexture::~OpenGLTexture()
{
  OpenGLGState::unregisterContextInitializer(static_initContext, (void*)this);

  // free image data
  delete[] image;

  // free OpenGL display list or texture object
#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0) {
    if (list) glDeleteTextures(1, &list);
  }
  else
#endif // BZF_TEXTURE_OBJECT
  if (list) glDeleteLists(list, 1);
}

void OpenGLTexture::static_initContext(void *that)
{
  ((OpenGLTexture*) that)->initContext();
}

void OpenGLTexture::initContext()
{
  // set size
  int tmpWidth = width;
  int tmpHeight = height;

  // get minimum valid size for texture (boost to 2^m x 2^n)
  GLint scaledWidth = 1, scaledHeight = 1;
  GLint maxTextureSize;
  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
  if (maxTextureSize > 512) maxTextureSize = 512;
  while (scaledWidth < tmpWidth) scaledWidth <<= 1;
  while (scaledHeight < tmpHeight) scaledHeight <<= 1;
  if (scaledWidth > maxTextureSize) scaledWidth = maxTextureSize;
  if (scaledHeight > maxTextureSize) scaledHeight = maxTextureSize;
  const int copyWidth = (scaledWidth > tmpWidth) ? scaledWidth : tmpWidth;
  const int copyHeight = (scaledHeight > tmpHeight) ? scaledHeight : tmpHeight;

  // make buffers for copied and scaled data
  GLubyte* origData = new GLubyte[4 * copyWidth * copyHeight + 4];
  GLubyte* data = (GLubyte*)(((unsigned long)origData & ~3) + 4);
  GLubyte* origScaledData = new GLubyte[4 * scaledWidth * scaledHeight + 4];
  GLubyte* scaledData = (GLubyte*)(((unsigned long)origScaledData & ~3) + 4);
  ::memcpy(data, image, 4 * tmpWidth * tmpHeight);

  // note if internal format uses alpha
  switch (internalFormat) {
    case BZF_INTENSITY_FORMAT:
    case GL_LUMINANCE_ALPHA:
#if defined(GL_LUMINANCE4_ALPHA4)
    case GL_LUMINANCE4_ALPHA4:
#elif defined(GL_LUMINANCE4_ALPHA4_EXT)
    case GL_LUMINANCE4_ALPHA4_EXT:
#endif
    case GL_RGBA:
#if defined(GL_INTENSITY4)
    case GL_INTENSITY4:
#elif defined(GL_INTENSITY4_EXT)
    case GL_INTENSITY4_EXT:
#endif
      alpha = true;
      break;

    default:
      alpha = false;
      break;
  }

  // now make texture map display list (compute all mipmaps, if requested).
  // compute next mipmap from current mipmap to save time.
  const bool mipmap = ((int)maxFilter > (int)Linear);
  GLint mipmapLevel = 0;

#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glBindTexture(GL_TEXTURE_2D, list);
  else
#endif // BZF_TEXTURE_OBJECT
  glNewList(list, GL_COMPILE);

  setFilter((Filter)maxFilter);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
			repeat ? GL_REPEAT : GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
			repeat ? GL_REPEAT : GL_CLAMP);
  do {
    bool doScale = (scaledWidth != tmpWidth || scaledHeight != tmpHeight);

    // scale image to next mipmap level
    if (doScale)
      gluScaleImage(
		GL_RGBA, tmpWidth, tmpHeight, GL_UNSIGNED_BYTE, data,
		scaledWidth ? scaledWidth : 1,
		scaledHeight ? scaledHeight : 1,
		GL_UNSIGNED_BYTE, scaledData);

    // make texture
    glTexImage2D(GL_TEXTURE_2D, mipmapLevel, internalFormat,
		scaledWidth ? scaledWidth : 1,
		scaledHeight ? scaledHeight : 1,
		0, GL_RGBA, GL_UNSIGNED_BYTE,
		doScale ? scaledData : data);

    // prepare for next iteration
    mipmapLevel++;
    tmpWidth = scaledWidth;
    tmpHeight = scaledHeight;
    scaledWidth >>= 1;
    scaledHeight >>= 1;
    if (doScale)
      ::memcpy(data, scaledData, 4 * tmpWidth * tmpHeight);
  } while (mipmap && (scaledWidth > 0 || scaledHeight > 0));

  setFilter(getFilter());

#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glBindTexture(GL_TEXTURE_2D, 0);
  else
#endif // BZF_TEXTURE_OBJECT
  glEndList();

  // free extra buffers
  delete[] origScaledData;
  delete[] origData;
}

OpenGLTexture::Filter	OpenGLTexture::getFilter()
{
  return filter;
}

std::string		OpenGLTexture::getFilterName()
{
  return configFilterValues[static_cast<int>(filter)];
}

void			OpenGLTexture::setFilter(std::string name)
{
  for (unsigned int i = 0; i < (sizeof(configFilterValues) /
				sizeof(configFilterValues[0])); i++)
    if (name == configFilterValues[i])
      setFilter(static_cast<Filter>(i));
}

void			OpenGLTexture::setFilter(Filter _filter)
{
  filter = _filter;

#if defined(BZF_TEXTURE_OBJECT)

  // can only change filters when using texture objects
  if (hasTextureObject <= 0) return;

  int filterIndex = (int) filter;
  // limit filter.  try to keep nearest... filters as nearest and
  // linear... as linear.
  if (filterIndex > maxFilter) {
    if ((filterIndex & 1) == 1)	// nearest...
      if ((maxFilter & 1) == 1) filterIndex = maxFilter;
      else filterIndex = maxFilter > 0 ? maxFilter - 1 : 0;

    else			// linear...
      if ((maxFilter & 1) == 1) filterIndex = maxFilter - 1;
      else filterIndex = maxFilter;
  }

#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0)
    glBindTexture(GL_TEXTURE_2D, list);
#endif // BZF_TEXTURE_OBJECT
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minifyFilter[filterIndex]);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnifyFilter[filterIndex]);
#endif // BZF_TEXTURE_OBJECT
}

GLuint			OpenGLTexture::getList() const
{
  return (list);
}

void			OpenGLTexture::execute()
{
  bind();
}

float			OpenGLTexture::getAspectRatio() const
{
    return ((float) height) / ((float) width);
}

void			OpenGLTexture::bind()
{
#if defined(BZF_TEXTURE_OBJECT)
  if (hasTextureObject > 0) {
    if (list) glBindTexture(GL_TEXTURE_2D, list);
    else glBindTexture(GL_TEXTURE_2D, 0);
  }
  else
#endif // BZF_TEXTURE_OBJECT
  if (list) glCallList(list);
  else glTexImage2D(GL_TEXTURE_2D, 0, 3, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
}

int			OpenGLTexture::getBestFormat(
				int width, int height,
				const GLvoid* pixels)
{
  // see if all pixels are achromatic
  const GLubyte* scan = (const GLubyte*)pixels;
  const int size = width * height;
  int i;
  for (i = 0; i < size; scan += 4, i++)
    if (scan[0] != scan[1] || scan[0] != scan[2])
      break;
  const bool useLuminance = (i == size);

  // see if all pixels are opaque
  scan = (const GLubyte*)pixels;
  for (i = 0; i < size; scan += 4, i++)
    if (scan[3] != 0xff)
      break;
  const bool useAlpha = (i != size);

  // intensity format defined in 1.1 and an extension in 1.0
#if defined(GL_VERSION_1_1)
  static const bool hasTextureExt = true;
#elif defined(GL_INTENSITY_EXT)
  static const bool hasTextureExt = (strstr((const char*)
		glGetString(GL_EXTENSIONS), "GL_EXT_texture") != NULL);
#else
  static const bool hasTextureExt = false;
#endif // defined(GL_VERSION_1_1)

  // see if all pixels are r=g=b=a.  if so return intensity format.
  // SGI IMPACT and 3Dfx systems don't support GL_INTENSITY.
  const char* const glRenderer = (const char*)glGetString(GL_RENDERER);
  static bool noIntensity =
	(strncmp(glRenderer, "IMPACT", 6) == 0) ||
	(strncmp(glRenderer, "3Dfx", 4) == 0);
  if (!noIntensity) {
    bool useIntensity = false;
    if (hasTextureExt && useLuminance) {
      scan = (const GLubyte*)pixels;
      for (i = 0; i < size; scan += 4, i++)
	if (scan[3] != scan[0])
	  break;
      useIntensity = (i == size);
    }
    if (useIntensity) return BZF_INTENSITY_FORMAT;
  }

  // pick internal format
  return (useLuminance ?
		(useAlpha ? GL_LUMINANCE_ALPHA : GL_LUMINANCE) :
		(useAlpha ? GL_RGBA : GL_RGB));
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8

<|endoftext|>


<commit_before>/*
 * SessionSourceDatabase.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <session/SessionSourceDatabase.hpp>

#include <string>
#include <vector>
#include <algorithm>

#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <core/Log.hpp>
#include <core/Exec.hpp>
#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/Hash.hpp>
#include <core/FileSerializer.hpp>
#include <core/DateTime.hpp>

#include <core/system/System.hpp>

#include <core/http/Util.hpp>

#include <r/RUtil.hpp>

#include <session/SessionModuleContext.hpp>

// NOTE: if a file is deleted then its properties database entry is not
// deleted. this has two implications:
//
//   - storage is not reclaimed
//   - the properties can be "resurreced" and re-attached to another
//     file with the same path
//
// One way to overcome this might be to use filesystem metadata to store
// properties rather than a side-database

using namespace core;

namespace session {
namespace source_database {

namespace {

struct PropertiesDatabase
{
   FilePath path;
   FilePath indexFile;
   std::map<std::string,std::string> index;
};

Error getPropertiesDatabase(PropertiesDatabase* pDatabase)
{
   pDatabase->path = path().complete("properties");
   Error error = pDatabase->path.ensureDirectory();
   if (error)
      return error;

   pDatabase->indexFile = pDatabase->path.complete("INDEX");

   if (pDatabase->indexFile.exists())
      return readStringMapFromFile(pDatabase->indexFile, &(pDatabase->index));
   else
      return Success();
}

Error putProperties(const std::string& path, const json::Object& properties)
{
   // url escape path (so we can use key=value persistence)
   std::string escapedPath = http::util::urlEncode(path);

   // get properties database
   PropertiesDatabase propertiesDB;
   Error error = getPropertiesDatabase(&propertiesDB);
   if (error)
      return error;

   // use existing properties file if it exists, otherwise create new
   bool updateIndex = false;
   std::string propertiesFile = propertiesDB.index[escapedPath];
   if (propertiesFile.empty())
   {
      propertiesFile = core::system::generateUuid(false);
      propertiesDB.index[escapedPath] = propertiesFile;
      updateIndex = true;
   }

   // write the file
   std::ostringstream ostr ;
   json::writeFormatted(properties, ostr);
   FilePath propertiesFilePath = propertiesDB.path.complete(propertiesFile);
   error = writeStringToFile(propertiesFilePath, ostr.str());
   if (error)
      return error;

   // update the index if necessary
   if (updateIndex)
      return writeStringMapToFile(propertiesDB.indexFile, propertiesDB.index);
   else
      return Success();
}

Error getProperties(const std::string& path, json::Object* pProperties)
{
   // url escape path (so we can use key=value persistence)
   std::string escapedPath = http::util::urlEncode(path);

   // get properties database
   PropertiesDatabase propertiesDB;
   Error error = getPropertiesDatabase(&propertiesDB);
   if (error)
      return error;

   // check for properties file
   std::string propertiesFile = propertiesDB.index[escapedPath];
   if (propertiesFile.empty())
   {
      // return empty object if there is none
      *pProperties = json::Object();
      return Success();
   }

   // read the properties file
   std::string contents ;
   FilePath propertiesFilePath = propertiesDB.path.complete(propertiesFile);
   error = readStringFromFile(propertiesFilePath, &contents,
                              options().sourceLineEnding());
   if (error)
      return error;

   // parse the json
   json::Value value;
   if ( !json::parse(contents, &value) )
      return systemError(boost::system::errc::bad_message, ERROR_LOCATION);

   // return it
   if (json::isType<json::Object>(value))
      *pProperties = value.get_obj();
   return Success();
}

}  // anonymous namespace

SourceDocument::SourceDocument(const std::string& type)
{
   id_ = core::system::generateUuid();
   type_ = type;
   setContents("");
   dirty_ = false;
   created_ = date_time::millisecondsSinceEpoch();
   sourceOnSave_ = false;
}

std::string SourceDocument::getProperty(const std::string& name)
{
   if (properties_.find(name) != properties_.end())
   {
      json::Value valueJson = properties_[name];
      if (json::isType<std::string>(valueJson))
         return valueJson.get_str();
      else
         return "";
   }
   else
   {
      return "";
   }
}

// set contents from string
void SourceDocument::setContents(const std::string& contents)
{
   contents_ = contents;
   hash_ = hash::crc32Hash(contents_);
}

namespace {

Error readAndDecode(const FilePath& docPath,
                    const std::string& encoding,
                    bool allowSubstChars,
                    std::string* pContents)
{
   // read contents
   std::string encodedContents;
   Error error = readStringFromFile(docPath, &encodedContents,
                                    options().sourceLineEnding());

   if (error)
      return error ;

   error = r::util::iconvstr(encodedContents, encoding, "UTF-8",
                             allowSubstChars, pContents);
   if (error)
      return error;

   stripBOM(pContents);
   // Detect invalid UTF-8 sequences and recover
   error = string_utils::utf8Clean(pContents->begin(),
                                   pContents->end(),
                                   '?');
   return error ;
}

} // anonymous namespace

// set contents from file
Error SourceDocument::setPathAndContents(const std::string& path,
                                         bool allowSubstChars)
{
   // resolve aliased path
   FilePath docPath = module_context::resolveAliasedPath(path);

   std::string contents;
   Error error = readAndDecode(docPath, encoding(), allowSubstChars,
                               &contents);
   if (error)
      return error ;

   // update path and contents
   path_ = path;
   setContents(contents);
   lastKnownWriteTime_ = docPath.lastWriteTime();

   return Success();
}

Error SourceDocument::updateDirty()
{
   if (path().empty())
   {
      dirty_ = !contents_.empty();
   }
   else if (dirty_)
   {
      // This doesn't actually guarantee that dirty state is correct. All
      // it does, at the most, is take a dirty document and mark it clean
      // if the contents are the same as on disk. This is important because
      // the client now has logic to detect when undo/redo causes a document
      // to be reverted to its previous state (i.e. a dirty document can
      // become clean through undo/redo), but that state doesn't get sent
      // back to the server.

      // We don't make a clean document dirty here, even if the contents
      // on disk are different, because we will do that on the client side
      // and the UI logic is a little complicated.

      FilePath docPath = module_context::resolveAliasedPath(path());
      if (docPath.exists() && docPath.size() <= (1024*1024))
      {
         std::string contents;
         Error error = readAndDecode(docPath, encoding(), true, &contents);
         if (error)
            return error;

         if (contents_.length() == contents.length() && hash_ == hash::crc32Hash(contents))
            dirty_ = false;
      }
   }
   return Success();
}

void SourceDocument::editProperties(json::Object& properties)
{
   std::for_each(properties.begin(),
                 properties.end(),
                 boost::bind(&SourceDocument::editProperty, this, _1));
}

void SourceDocument::checkForExternalEdit(std::time_t* pTime)
{
   *pTime = 0;

   if (path_.empty())
      return;

   if (lastKnownWriteTime_ == 0)
      return;

   core::FilePath filePath = module_context::resolveAliasedPath(path_);
   if (!filePath.exists())
      return;

   std::time_t newTime = filePath.lastWriteTime();
   if (newTime != lastKnownWriteTime_)
      *pTime = newTime;
}

void SourceDocument::updateLastKnownWriteTime()
{
   lastKnownWriteTime_ = 0;
   if (path_.empty())
      return;

   core::FilePath filePath = module_context::resolveAliasedPath(path_);
   if (!filePath.exists())
      return;

   lastKnownWriteTime_ = filePath.lastWriteTime();
}

Error SourceDocument::readFromJson(json::Object* pDocJson)
{
   // NOTE: since this class is the one who presumably persisted the
   // json values in the first place we don't do "checked" access to
   // the json data elements. if the persistence format differs from
   // what we expect things will blow up. therefore if we change the
   // persistence format we need to make sure this code is robust
   // in the presence of the old format

   json::Object& docJson = *pDocJson;

   id_ = docJson["id"].get_str();
   json::Value path = docJson["path"];
   path_ = !path.is_null() ? path.get_str() : std::string();

   json::Value type = docJson["type"];
   type_ = !type.is_null() ? type.get_str() : std::string();

   setContents(docJson["contents"].get_str());
   dirty_ = docJson["dirty"].get_bool();
   created_ = docJson["created"].get_real();
   sourceOnSave_ = docJson["source_on_save"].get_bool();

   // read safely (migration)
   json::Value properties = docJson["properties"];
   properties_ = !properties.is_null() ? properties.get_obj() : json::Object();

   json::Value lastKnownWriteTime = docJson["lastKnownWriteTime"];
   lastKnownWriteTime_ = !lastKnownWriteTime.is_null()
                            ? lastKnownWriteTime.get_int64()
                            : 0;

   json::Value encoding = docJson["encoding"];
   encoding_ = !encoding.is_null() ? encoding.get_str() : std::string();

   return Success();
}

void SourceDocument::writeToJson(json::Object* pDocJson) const
{
   json::Object& jsonDoc = *pDocJson;
   jsonDoc["id"] = id();
   jsonDoc["path"] = !path().empty() ? path_ : json::Value();
   jsonDoc["type"] = !type().empty() ? type_ : json::Value();
   jsonDoc["hash"] = hash();
   jsonDoc["contents"] = contents();
   jsonDoc["dirty"] = dirty();
   jsonDoc["created"] = created();
   jsonDoc["source_on_save"] = sourceOnSave();
   jsonDoc["properties"] = properties();
   jsonDoc["lastKnownWriteTime"] = json::Value(
         static_cast<boost::int64_t>(lastKnownWriteTime_));
   jsonDoc["encoding"] = encoding_;
}

void SourceDocument::editProperty(const json::Object::value_type& property)
{
   if (property.second.is_null())
   {
      properties_.erase(property.first);
   }
   else
   {
      properties_[property.first] = property.second;
   }
}

bool sortByCreated(const SourceDocument& doc1, const SourceDocument& doc2)
{
   return doc1.created() < doc2.created();
}

FilePath path()
{
   return module_context::userScratchPath().complete("source_database");
}

Error get(const std::string& id, SourceDocument* pDoc)
{
   FilePath filePath = source_database::path().complete(id);
   if (filePath.exists())
   {
      // read the contents of the file
      std::string contents ;
      Error error = readStringFromFile(filePath, &contents,
                                       options().sourceLineEnding());
      if (error)
         return error;

      // parse the json
      json::Value value;
      if ( !json::parse(contents, &value) )
      {
         return systemError(boost::system::errc::invalid_argument,
                            ERROR_LOCATION);
      }

      // initialize doc from json
      json::Object jsonDoc = value.get_obj();
      return pDoc->readFromJson(&jsonDoc);
   }
   else
   {
      return systemError(boost::system::errc::no_such_file_or_directory,
                         ERROR_LOCATION);
   }
}

Error getDurableProperties(const std::string& path, json::Object* pProperties)
{
   return getProperties(path, pProperties);
}

Error list(std::vector<SourceDocument>* pDocs)
{
   std::vector<FilePath> files ;
   Error error = source_database::path().children(&files);
   if (error)
      return error ;

   BOOST_FOREACH( FilePath& filePath, files )
   {
      if (!filePath.isDirectory())
      {
         // get the source doc
         SourceDocument doc ;
         Error error = source_database::get(filePath.filename(), &doc);
         if (error)
            return error ;

         // add it to the list
         pDocs->push_back(doc);
      }
   }

   return Success();
}

Error put(const SourceDocument& doc)
{
   // get json representation
   json::Object jsonDoc ;
   doc.writeToJson(&jsonDoc);
   std::ostringstream ostr ;
   json::writeFormatted(jsonDoc, ostr);

   // write to file
   FilePath filePath = source_database::path().complete(doc.id());
   Error error = writeStringToFile(filePath, ostr.str());
   if (error)
      return error ;

   // write properties to durable storage
   error = putProperties(doc.path(), doc.properties());
   if (error)
      LOG_ERROR(error);

   return Success();
}

Error remove(const std::string& id)
{
   return source_database::path().complete(id).removeIfExists();
}

Error removeAll()
{
   std::vector<FilePath> files ;
   Error error = source_database::path().children(&files);
   if (error)
      return error ;

   BOOST_FOREACH( FilePath& filePath, files )
   {
      Error error = filePath.remove();
      if (error)
         return error ;
   }

   return Success();
}

Error getSourceDocumentsJson(core::json::Array* pJsonDocs)
{
   // get the docs and sort them by created
   std::vector<SourceDocument> docs ;
   Error error = source_database::list(&docs);
   if (error)
      return error ;
   std::sort(docs.begin(), docs.end(), sortByCreated);

   // populate the array
   pJsonDocs->clear();
   BOOST_FOREACH( SourceDocument& doc, docs )
   {
      // Force dirty state to be checked.
      // Client and server dirty state can get out of sync because
      // undo/redo on the client side can make dirty documents
      // become clean again. I tried pushing the client dirty state
      // back to the server but couldn't convince myself that I
      // got all the edge cases. This approach is simpler--just
      // compare the contents in the doc database to the contents
      // on disk, and only do it when listing documents. However
      // it does mean that reloading the client may cause a dirty
      // document to become clean (if the contents are identical
      // to what's on disk).
      error = doc.updateDirty();
      if (error)
         LOG_ERROR(error);

      json::Object jsonDoc ;
      doc.writeToJson(&jsonDoc);
      pJsonDocs->push_back(jsonDoc);
   }

   return Success();
}

Error initialize()
{
   // make sure the source database exists
   return source_database::path().ensureDirectory();
}

} // namespace source_database
} // namesapce session

<commit_msg>make sure an error reading one source document doesn't abort reading of other source docs<commit_after>/*
 * SessionSourceDatabase.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <session/SessionSourceDatabase.hpp>

#include <string>
#include <vector>
#include <algorithm>

#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <core/Log.hpp>
#include <core/Exec.hpp>
#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/Hash.hpp>
#include <core/FileSerializer.hpp>
#include <core/DateTime.hpp>

#include <core/system/System.hpp>

#include <core/http/Util.hpp>

#include <r/RUtil.hpp>

#include <session/SessionModuleContext.hpp>

// NOTE: if a file is deleted then its properties database entry is not
// deleted. this has two implications:
//
//   - storage is not reclaimed
//   - the properties can be "resurreced" and re-attached to another
//     file with the same path
//
// One way to overcome this might be to use filesystem metadata to store
// properties rather than a side-database

using namespace core;

namespace session {
namespace source_database {

namespace {

struct PropertiesDatabase
{
   FilePath path;
   FilePath indexFile;
   std::map<std::string,std::string> index;
};

Error getPropertiesDatabase(PropertiesDatabase* pDatabase)
{
   pDatabase->path = path().complete("properties");
   Error error = pDatabase->path.ensureDirectory();
   if (error)
      return error;

   pDatabase->indexFile = pDatabase->path.complete("INDEX");

   if (pDatabase->indexFile.exists())
      return readStringMapFromFile(pDatabase->indexFile, &(pDatabase->index));
   else
      return Success();
}

Error putProperties(const std::string& path, const json::Object& properties)
{
   // url escape path (so we can use key=value persistence)
   std::string escapedPath = http::util::urlEncode(path);

   // get properties database
   PropertiesDatabase propertiesDB;
   Error error = getPropertiesDatabase(&propertiesDB);
   if (error)
      return error;

   // use existing properties file if it exists, otherwise create new
   bool updateIndex = false;
   std::string propertiesFile = propertiesDB.index[escapedPath];
   if (propertiesFile.empty())
   {
      propertiesFile = core::system::generateUuid(false);
      propertiesDB.index[escapedPath] = propertiesFile;
      updateIndex = true;
   }

   // write the file
   std::ostringstream ostr ;
   json::writeFormatted(properties, ostr);
   FilePath propertiesFilePath = propertiesDB.path.complete(propertiesFile);
   error = writeStringToFile(propertiesFilePath, ostr.str());
   if (error)
      return error;

   // update the index if necessary
   if (updateIndex)
      return writeStringMapToFile(propertiesDB.indexFile, propertiesDB.index);
   else
      return Success();
}

Error getProperties(const std::string& path, json::Object* pProperties)
{
   // url escape path (so we can use key=value persistence)
   std::string escapedPath = http::util::urlEncode(path);

   // get properties database
   PropertiesDatabase propertiesDB;
   Error error = getPropertiesDatabase(&propertiesDB);
   if (error)
      return error;

   // check for properties file
   std::string propertiesFile = propertiesDB.index[escapedPath];
   if (propertiesFile.empty())
   {
      // return empty object if there is none
      *pProperties = json::Object();
      return Success();
   }

   // read the properties file
   std::string contents ;
   FilePath propertiesFilePath = propertiesDB.path.complete(propertiesFile);
   error = readStringFromFile(propertiesFilePath, &contents,
                              options().sourceLineEnding());
   if (error)
      return error;

   // parse the json
   json::Value value;
   if ( !json::parse(contents, &value) )
      return systemError(boost::system::errc::bad_message, ERROR_LOCATION);

   // return it
   if (json::isType<json::Object>(value))
      *pProperties = value.get_obj();
   return Success();
}

}  // anonymous namespace

SourceDocument::SourceDocument(const std::string& type)
{
   id_ = core::system::generateUuid();
   type_ = type;
   setContents("");
   dirty_ = false;
   created_ = date_time::millisecondsSinceEpoch();
   sourceOnSave_ = false;
}

std::string SourceDocument::getProperty(const std::string& name)
{
   if (properties_.find(name) != properties_.end())
   {
      json::Value valueJson = properties_[name];
      if (json::isType<std::string>(valueJson))
         return valueJson.get_str();
      else
         return "";
   }
   else
   {
      return "";
   }
}

// set contents from string
void SourceDocument::setContents(const std::string& contents)
{
   contents_ = contents;
   hash_ = hash::crc32Hash(contents_);
}

namespace {

Error readAndDecode(const FilePath& docPath,
                    const std::string& encoding,
                    bool allowSubstChars,
                    std::string* pContents)
{
   // read contents
   std::string encodedContents;
   Error error = readStringFromFile(docPath, &encodedContents,
                                    options().sourceLineEnding());

   if (error)
      return error ;

   error = r::util::iconvstr(encodedContents, encoding, "UTF-8",
                             allowSubstChars, pContents);
   if (error)
      return error;

   stripBOM(pContents);
   // Detect invalid UTF-8 sequences and recover
   error = string_utils::utf8Clean(pContents->begin(),
                                   pContents->end(),
                                   '?');
   return error ;
}

} // anonymous namespace

// set contents from file
Error SourceDocument::setPathAndContents(const std::string& path,
                                         bool allowSubstChars)
{
   // resolve aliased path
   FilePath docPath = module_context::resolveAliasedPath(path);

   std::string contents;
   Error error = readAndDecode(docPath, encoding(), allowSubstChars,
                               &contents);
   if (error)
      return error ;

   // update path and contents
   path_ = path;
   setContents(contents);
   lastKnownWriteTime_ = docPath.lastWriteTime();

   return Success();
}

Error SourceDocument::updateDirty()
{
   if (path().empty())
   {
      dirty_ = !contents_.empty();
   }
   else if (dirty_)
   {
      // This doesn't actually guarantee that dirty state is correct. All
      // it does, at the most, is take a dirty document and mark it clean
      // if the contents are the same as on disk. This is important because
      // the client now has logic to detect when undo/redo causes a document
      // to be reverted to its previous state (i.e. a dirty document can
      // become clean through undo/redo), but that state doesn't get sent
      // back to the server.

      // We don't make a clean document dirty here, even if the contents
      // on disk are different, because we will do that on the client side
      // and the UI logic is a little complicated.

      FilePath docPath = module_context::resolveAliasedPath(path());
      if (docPath.exists() && docPath.size() <= (1024*1024))
      {
         std::string contents;
         Error error = readAndDecode(docPath, encoding(), true, &contents);
         if (error)
            return error;

         if (contents_.length() == contents.length() && hash_ == hash::crc32Hash(contents))
            dirty_ = false;
      }
   }
   return Success();
}

void SourceDocument::editProperties(json::Object& properties)
{
   std::for_each(properties.begin(),
                 properties.end(),
                 boost::bind(&SourceDocument::editProperty, this, _1));
}

void SourceDocument::checkForExternalEdit(std::time_t* pTime)
{
   *pTime = 0;

   if (path_.empty())
      return;

   if (lastKnownWriteTime_ == 0)
      return;

   core::FilePath filePath = module_context::resolveAliasedPath(path_);
   if (!filePath.exists())
      return;

   std::time_t newTime = filePath.lastWriteTime();
   if (newTime != lastKnownWriteTime_)
      *pTime = newTime;
}

void SourceDocument::updateLastKnownWriteTime()
{
   lastKnownWriteTime_ = 0;
   if (path_.empty())
      return;

   core::FilePath filePath = module_context::resolveAliasedPath(path_);
   if (!filePath.exists())
      return;

   lastKnownWriteTime_ = filePath.lastWriteTime();
}

Error SourceDocument::readFromJson(json::Object* pDocJson)
{
   // NOTE: since this class is the one who presumably persisted the
   // json values in the first place we don't do "checked" access to
   // the json data elements. if the persistence format differs from
   // what we expect things will blow up. therefore if we change the
   // persistence format we need to make sure this code is robust
   // in the presence of the old format

   json::Object& docJson = *pDocJson;

   id_ = docJson["id"].get_str();
   json::Value path = docJson["path"];
   path_ = !path.is_null() ? path.get_str() : std::string();

   json::Value type = docJson["type"];
   type_ = !type.is_null() ? type.get_str() : std::string();

   setContents(docJson["contents"].get_str());
   dirty_ = docJson["dirty"].get_bool();
   created_ = docJson["created"].get_real();
   sourceOnSave_ = docJson["source_on_save"].get_bool();

   // read safely (migration)
   json::Value properties = docJson["properties"];
   properties_ = !properties.is_null() ? properties.get_obj() : json::Object();

   json::Value lastKnownWriteTime = docJson["lastKnownWriteTime"];
   lastKnownWriteTime_ = !lastKnownWriteTime.is_null()
                            ? lastKnownWriteTime.get_int64()
                            : 0;

   json::Value encoding = docJson["encoding"];
   encoding_ = !encoding.is_null() ? encoding.get_str() : std::string();

   return Success();
}

void SourceDocument::writeToJson(json::Object* pDocJson) const
{
   json::Object& jsonDoc = *pDocJson;
   jsonDoc["id"] = id();
   jsonDoc["path"] = !path().empty() ? path_ : json::Value();
   jsonDoc["type"] = !type().empty() ? type_ : json::Value();
   jsonDoc["hash"] = hash();
   jsonDoc["contents"] = contents();
   jsonDoc["dirty"] = dirty();
   jsonDoc["created"] = created();
   jsonDoc["source_on_save"] = sourceOnSave();
   jsonDoc["properties"] = properties();
   jsonDoc["lastKnownWriteTime"] = json::Value(
         static_cast<boost::int64_t>(lastKnownWriteTime_));
   jsonDoc["encoding"] = encoding_;
}

void SourceDocument::editProperty(const json::Object::value_type& property)
{
   if (property.second.is_null())
   {
      properties_.erase(property.first);
   }
   else
   {
      properties_[property.first] = property.second;
   }
}

bool sortByCreated(const SourceDocument& doc1, const SourceDocument& doc2)
{
   return doc1.created() < doc2.created();
}

FilePath path()
{
   return module_context::userScratchPath().complete("source_database");
}

Error get(const std::string& id, SourceDocument* pDoc)
{
   FilePath filePath = source_database::path().complete(id);
   if (filePath.exists())
   {
      // read the contents of the file
      std::string contents ;
      Error error = readStringFromFile(filePath, &contents,
                                       options().sourceLineEnding());
      if (error)
         return error;

      // parse the json
      json::Value value;
      if ( !json::parse(contents, &value) )
      {
         return systemError(boost::system::errc::invalid_argument,
                            ERROR_LOCATION);
      }

      // initialize doc from json
      json::Object jsonDoc = value.get_obj();
      return pDoc->readFromJson(&jsonDoc);
   }
   else
   {
      return systemError(boost::system::errc::no_such_file_or_directory,
                         ERROR_LOCATION);
   }
}

Error getDurableProperties(const std::string& path, json::Object* pProperties)
{
   return getProperties(path, pProperties);
}

Error list(std::vector<SourceDocument>* pDocs)
{
   std::vector<FilePath> files ;
   Error error = source_database::path().children(&files);
   if (error)
      return error ;

   BOOST_FOREACH( FilePath& filePath, files )
   {
      if (!filePath.isDirectory())
      {
         // get the source doc
         SourceDocument doc ;
         Error error = source_database::get(filePath.filename(), &doc);
         if (!error)
            pDocs->push_back(doc);
         else
            LOG_ERROR(error);
      }
   }

   return Success();
}

Error put(const SourceDocument& doc)
{
   // get json representation
   json::Object jsonDoc ;
   doc.writeToJson(&jsonDoc);
   std::ostringstream ostr ;
   json::writeFormatted(jsonDoc, ostr);

   // write to file
   FilePath filePath = source_database::path().complete(doc.id());
   Error error = writeStringToFile(filePath, ostr.str());
   if (error)
      return error ;

   // write properties to durable storage
   error = putProperties(doc.path(), doc.properties());
   if (error)
      LOG_ERROR(error);

   return Success();
}

Error remove(const std::string& id)
{
   return source_database::path().complete(id).removeIfExists();
}

Error removeAll()
{
   std::vector<FilePath> files ;
   Error error = source_database::path().children(&files);
   if (error)
      return error ;

   BOOST_FOREACH( FilePath& filePath, files )
   {
      Error error = filePath.remove();
      if (error)
         return error ;
   }

   return Success();
}

Error getSourceDocumentsJson(core::json::Array* pJsonDocs)
{
   // get the docs and sort them by created
   std::vector<SourceDocument> docs ;
   Error error = source_database::list(&docs);
   if (error)
      return error ;
   std::sort(docs.begin(), docs.end(), sortByCreated);

   // populate the array
   pJsonDocs->clear();
   BOOST_FOREACH( SourceDocument& doc, docs )
   {
      // Force dirty state to be checked.
      // Client and server dirty state can get out of sync because
      // undo/redo on the client side can make dirty documents
      // become clean again. I tried pushing the client dirty state
      // back to the server but couldn't convince myself that I
      // got all the edge cases. This approach is simpler--just
      // compare the contents in the doc database to the contents
      // on disk, and only do it when listing documents. However
      // it does mean that reloading the client may cause a dirty
      // document to become clean (if the contents are identical
      // to what's on disk).
      error = doc.updateDirty();
      if (error)
         LOG_ERROR(error);

      json::Object jsonDoc ;
      doc.writeToJson(&jsonDoc);
      pJsonDocs->push_back(jsonDoc);
   }

   return Success();
}

Error initialize()
{
   // make sure the source database exists
   return source_database::path().ensureDirectory();
}

} // namespace source_database
} // namesapce session

<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------

#include "cling/Interpreter/InterpreterCallbacks.h"

#include "cling/Interpreter/Interpreter.h"

#include "clang/Sema/Sema.h"

using namespace clang;

namespace cling {

  // pin the vtable here
  InterpreterExternalSemaSource::~InterpreterExternalSemaSource() {}

  bool InterpreterExternalSemaSource::LookupUnqualified(LookupResult& R,
                                                        Scope* S) {
    if (m_Callbacks)
      return m_Callbacks->LookupObject(R, S);

    return false;
  }

  InterpreterCallbacks::InterpreterCallbacks(Interpreter* interp,
                                             InterpreterExternalSemaSource* IESS)
    : m_Interpreter(interp),  m_SemaExternalSource(IESS) {
    if (!IESS)
      m_SemaExternalSource.reset(new InterpreterExternalSemaSource(this));
  }

  // pin the vtable here
  InterpreterCallbacks::~InterpreterCallbacks() {}

  bool InterpreterCallbacks::LookupObject(LookupResult&, Scope*) {
    return false;
  }

} // end namespace cling

// TODO: Make the build system in the testsuite aware how to build that class
// and extract it out there again.
#include "DynamicLookup.h"
#include "cling/Utils/AST.h"

#include "clang/Sema/Lookup.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Scope.h"
namespace cling {
namespace test {
  TestProxy* Tester = 0;

  extern "C" int printf(const char* fmt, ...);
  TestProxy::TestProxy(){}
  int TestProxy::Draw(){ return 12; }
  const char* TestProxy::getVersion(){ return "Interpreter.cpp"; }

  int TestProxy::Add10(int num) { return num + 10;}

  int TestProxy::Add(int a, int b) {
    return a + b;
  }

  void TestProxy::PrintString(std::string s) { printf("%s\n", s.c_str()); }

  bool TestProxy::PrintArray(int a[], size_t size) {
    for (unsigned i = 0; i < size; ++i)
      printf("%i", a[i]);

    printf("%s", "\n");

    return true;
  }

  void TestProxy::PrintArray(float a[][5], size_t size) {
    for (unsigned i = 0; i < size; ++i)
      for (unsigned j = 0; j < 5; ++j)
        printf("%i", (int)a[i][j]);

    printf("%s", "\n");
  }

  void TestProxy::PrintArray(int a[][4][5], size_t size) {
    for (unsigned i = 0; i < size; ++i)
      for (unsigned j = 0; j < 4; ++j)
        for (unsigned k = 0; k < 5; ++k)
          printf("%i", a[i][j][k]);

    printf("%s", "\n");
  }

  SymbolResolverCallback::SymbolResolverCallback(Interpreter* interp)
    : InterpreterCallbacks(interp, new DynamicIDHandler(this)), m_TesterDecl(0) {
    m_Interpreter->process("cling::test::Tester = new cling::test::TestProxy();");
  }

  SymbolResolverCallback::~SymbolResolverCallback() { }

  bool SymbolResolverCallback::LookupObject(LookupResult& R, Scope* S) {
    if (!IsDynamicLookup(R, S))
      return false;

    // Only for demo resolve all unknown objects to cling::test::Tester
    if (!m_TesterDecl) {
      clang::Sema& S = m_Interpreter->getSema();
      clang::NamespaceDecl* NSD = utils::Lookup::Namespace(&S, "cling");
      NSD = utils::Lookup::Namespace(&S, "test", NSD);
      m_TesterDecl = utils::Lookup::Named(&S, "Tester", NSD);
    }
    assert (m_TesterDecl && "Tester not found!");
    R.addDecl(m_TesterDecl);
    return true;
  }

  bool SymbolResolverCallback::IsDynamicLookup(LookupResult& R, Scope* S) {
    if (R.getLookupKind() != Sema::LookupOrdinaryName) return false;
    if (R.isForRedeclaration()) return false;
    // FIXME: Figure out better way to handle:
    // C++ [basic.lookup.classref]p1:
    //   In a class member access expression (5.2.5), if the . or -> token is
    //   immediately followed by an identifier followed by a <, the
    //   identifier must be looked up to determine whether the < is the
    //   beginning of a template argument list (14.2) or a less-than operator.
    //   The identifier is first looked up in the class of the object
    //   expression. If the identifier is not found, it is then looked up in
    //   the context of the entire postfix-expression and shall name a class
    //   or function template.
    //
    // We want to ignore object(.|->)member<template>
    if (R.getSema().PP.LookAhead(0).getKind() == tok::less)
      // TODO: check for . or -> in the cached token stream
      return false;

    for (Scope* DepScope = S; DepScope; DepScope = DepScope->getParent()) {
      if (DeclContext* Ctx = static_cast<DeclContext*>(DepScope->getEntity())) {
        return !Ctx->isDependentContext();
      }
    }

    return true;
  }

} // end test
} // end cling
<commit_msg>Silence coverity #47876.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------

#include "cling/Interpreter/InterpreterCallbacks.h"

#include "cling/Interpreter/Interpreter.h"

#include "clang/Sema/Sema.h"

using namespace clang;

namespace cling {

  // pin the vtable here
  InterpreterExternalSemaSource::~InterpreterExternalSemaSource() {}

  bool InterpreterExternalSemaSource::LookupUnqualified(LookupResult& R,
                                                        Scope* S) {
    if (m_Callbacks)
      return m_Callbacks->LookupObject(R, S);

    return false;
  }

  InterpreterCallbacks::InterpreterCallbacks(Interpreter* interp,
                                             InterpreterExternalSemaSource* IESS)
    : m_Interpreter(interp),  m_SemaExternalSource(IESS) {
    if (!IESS)
      m_SemaExternalSource.reset(new InterpreterExternalSemaSource(this));
  }

  // pin the vtable here
  InterpreterCallbacks::~InterpreterCallbacks() {}

  bool InterpreterCallbacks::LookupObject(LookupResult&, Scope*) {
    return false;
  }

} // end namespace cling

// TODO: Make the build system in the testsuite aware how to build that class
// and extract it out there again.
#include "DynamicLookup.h"
#include "cling/Utils/AST.h"

#include "clang/Sema/Lookup.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Scope.h"
namespace cling {
namespace test {
  TestProxy* Tester = 0;

  extern "C" int printf(const char* fmt, ...);
  TestProxy::TestProxy(){}
  int TestProxy::Draw(){ return 12; }
  const char* TestProxy::getVersion(){ return "Interpreter.cpp"; }

  int TestProxy::Add10(int num) { return num + 10;}

  int TestProxy::Add(int a, int b) {
    return a + b;
  }

  void TestProxy::PrintString(std::string s) { printf("%s\n", s.c_str()); }

  bool TestProxy::PrintArray(int a[], size_t size) {
    for (unsigned i = 0; i < size; ++i)
      printf("%i", a[i]);

    printf("%s", "\n");

    return true;
  }

  void TestProxy::PrintArray(float a[][5], size_t size) {
    for (unsigned i = 0; i < size; ++i)
      for (unsigned j = 0; j < 5; ++j)
        printf("%i", (int)a[i][j]);

    printf("%s", "\n");
  }

  void TestProxy::PrintArray(int a[][4][5], size_t size) {
    for (unsigned i = 0; i < size; ++i)
      for (unsigned j = 0; j < 4; ++j)
        for (unsigned k = 0; k < 5; ++k)
          printf("%i", a[i][j][k]);

    printf("%s", "\n");
  }

  SymbolResolverCallback::SymbolResolverCallback(Interpreter* interp)
    : InterpreterCallbacks(interp, new DynamicIDHandler(this)), m_TesterDecl(0) {
    m_Interpreter->process("cling::test::Tester = new cling::test::TestProxy();");
  }

  SymbolResolverCallback::~SymbolResolverCallback() { }

  bool SymbolResolverCallback::LookupObject(LookupResult& R, Scope* S) {
    if (!IsDynamicLookup(R, S))
      return false;

    // Only for demo resolve all unknown objects to cling::test::Tester
    if (!m_TesterDecl) {
      clang::Sema& SemaRef = m_Interpreter->getSema();
      clang::NamespaceDecl* NSD = utils::Lookup::Namespace(&SemaRef, "cling");
      NSD = utils::Lookup::Namespace(&SemaRef, "test", NSD);
      m_TesterDecl = utils::Lookup::Named(&SemaRef, "Tester", NSD);
    }
    assert (m_TesterDecl && "Tester not found!");
    R.addDecl(m_TesterDecl);
    return true;
  }

  bool SymbolResolverCallback::IsDynamicLookup(LookupResult& R, Scope* S) {
    if (R.getLookupKind() != Sema::LookupOrdinaryName) return false;
    if (R.isForRedeclaration()) return false;
    // FIXME: Figure out better way to handle:
    // C++ [basic.lookup.classref]p1:
    //   In a class member access expression (5.2.5), if the . or -> token is
    //   immediately followed by an identifier followed by a <, the
    //   identifier must be looked up to determine whether the < is the
    //   beginning of a template argument list (14.2) or a less-than operator.
    //   The identifier is first looked up in the class of the object
    //   expression. If the identifier is not found, it is then looked up in
    //   the context of the entire postfix-expression and shall name a class
    //   or function template.
    //
    // We want to ignore object(.|->)member<template>
    if (R.getSema().PP.LookAhead(0).getKind() == tok::less)
      // TODO: check for . or -> in the cached token stream
      return false;

    for (Scope* DepScope = S; DepScope; DepScope = DepScope->getParent()) {
      if (DeclContext* Ctx = static_cast<DeclContext*>(DepScope->getEntity())) {
        return !Ctx->isDependentContext();
      }
    }

    return true;
  }

} // end test
} // end cling
<|endoftext|>