<commit_before>/*
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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.
*/

#include <ts/ts.h>
#include <ts/remap.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <time.h>
#include <openssl/sha.h>

#include <string>
#include <list>

// TODO: We should eliminate this when we have unordered_map on all supported platforms
#if HAVE_UNORDERED_MAP
#include <unordered_map>
#else
#include <map>
#endif

static const char *PLUGIN_NAME = "cache_promote";

//////////////////////////////////////////////////////////////////////////////////////////////
// Note that all options for all policies has to go here. Not particularly pretty...
//
static const struct option longopt[] = {{const_cast<char *>("policy"), required_argument, NULL, 'p'},
                                        // This is for both Chance and LRU (optional) policy
                                        {const_cast<char *>("sample"), required_argument, NULL, 's'},
                                        // For the LRU policy
                                        {const_cast<char *>("buckets"), required_argument, NULL, 'b'},
                                        {const_cast<char *>("hits"), required_argument, NULL, 'h'},
                                        // EOF
                                        {NULL, no_argument, NULL, '\0'}};

//////////////////////////////////////////////////////////////////////////////////////////////
// Abstract base class for all policies.
//
class PromotionPolicy
{
public:
  PromotionPolicy() : _sample(0.0)
  {
    // This doesn't have to be perfect, since this is just chance sampling.
    // coverity[dont_call]
    srand48((long)time(NULL));
  }

  void
  setSample(char *s)
  {
    _sample = strtof(s, NULL) / 100.0;
  }

  float
  getSample() const
  {
    return _sample;
  }

  bool
  doSample() const
  {
    if (_sample > 0) {
      // coverity[dont_call]
      double r = drand48();

      if (_sample > r) {
        TSDebug(PLUGIN_NAME, "checking sampling, is %f > %f? Yes!", _sample, r);
      } else {
        TSDebug(PLUGIN_NAME, "checking sampling, is %f > %f? No!", _sample, r);
        return false;
      }
    }
    return true;
  }

  virtual ~PromotionPolicy(){};

  virtual bool
  parseOption(int opt, char *optarg)
  {
    return false;
  }

  // These are pure virtual
  virtual bool doPromote(TSHttpTxn txnp) = 0;
  virtual const char *policyName() const = 0;
  virtual void usage() const = 0;

private:
  float _sample;
};

//////////////////////////////////////////////////////////////////////////////////////////////
// This is the simplest of all policies, just give each request a (small)
// percentage chance to be promoted to cache.
//
class ChancePolicy : public PromotionPolicy
{
public:
  bool doPromote(TSHttpTxn /* txnp ATS_UNUSED */)
  {
    TSDebug(PLUGIN_NAME, "ChancePolicy::doPromote(%f)", getSample());
    return true;
  }

  void
  usage() const
  {
    TSError(PLUGIN_NAME, "Usage: @plugin=%s.so @pparam=--policy=chance @pparam=--sample=<x>%", PLUGIN_NAME);
  }

  const char *
  policyName() const
  {
    return "chance";
  }
};

//////////////////////////////////////////////////////////////////////////////////////////////
// The LRU based policy keeps track of <bucket> number of URLs, with a counter for each slot.
// Objects are not promoted unless the counter reaches <hits> before it gets evicted. An
// optional <chance> parameter can be used to sample hits, this can reduce contention and
// churning in the LRU as well.
//
class LRUHash
{
  friend struct LRUHashHasher;

public:
  LRUHash() { TSDebug(PLUGIN_NAME, "In LRUHash()"); }

  ~LRUHash() { TSDebug(PLUGIN_NAME, "In ~LRUHash()"); }

  LRUHash &operator=(const LRUHash &h)
  {
    TSDebug(PLUGIN_NAME, "copying an LRUHash object");
    memcpy(_hash, h._hash, sizeof(_hash));
    return *this;
  }

  void
  init(char *data, int len)
  {
    SHA_CTX sha;

    SHA1_Init(&sha);
    SHA1_Update(&sha, data, len);
    SHA1_Final(_hash, &sha);
  }

private:
  u_char _hash[SHA_DIGEST_LENGTH];
};

struct LRUHashHasher {
  bool operator()(const LRUHash *s1, const LRUHash *s2) const { return 0 == memcmp(s1->_hash, s2->_hash, sizeof(s2->_hash)); }

  size_t operator()(const LRUHash *s) const { return *((size_t *)s->_hash) ^ *((size_t *)(s->_hash + 9)); }
};

typedef std::pair<LRUHash, unsigned> LRUEntry;
typedef std::list<LRUEntry> LRUList;

static LRUEntry NULL_LRU_ENTRY; // Used to create an "empty" new LRUEntry

// TODO: We should eliminate this when we have unordered_map on all supported platforms.
#if HAVE_UNORDERED_MAP
#include <unordered_map>
typedef std::unordered_map<const LRUHash *, LRUList::iterator, LRUHashHasher, LRUHashHasher> LRUMap;
#else
#include <map>
typedef std::map<LRUHash *, LRUList::iterator> LRUMap;
#endif

class LRUPolicy : public PromotionPolicy
{
public:
  LRUPolicy() : PromotionPolicy(), _buckets(1000), _hits(10)
  {
    // This doesn't have to be perfect, since this is just chance sampling.
    // coverity[dont_call]
    srand48((long)time(NULL) ^ (long)getpid() ^ (long)getppid());
#if HAVE_UNORDERED_MAP
    _map.reserve(_buckets);
#endif
    _lock = TSMutexCreate();
  }

  ~LRUPolicy()
  {
    TSDebug(PLUGIN_NAME, "deleting LRUPolicy object");
    TSMutexLock(_lock);

    _map.clear();
    _list.clear();
    _freelist.clear();

    TSMutexUnlock(_lock);

    // ToDo: Destroy mutex ? TS-1432
  }

  bool
  parseOption(int opt, char *optarg)
  {
    switch (opt) {
    case 'b':
      _buckets = static_cast<unsigned>(strtol(optarg, NULL, 10));
      break;
    case 'h':
      _hits = static_cast<unsigned>(strtol(optarg, NULL, 10));
      break;
    default:
      // All other options are unsupported for this policy
      return false;
    }

    return true;
  }

  bool
  doPromote(TSHttpTxn txnp)
  {
    LRUHash hash;
    LRUMap::iterator map_it;
    int url_len = 0;
    char *url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_len);
    bool ret = false;

    TSDebug(PLUGIN_NAME, "LRUPolicy::doPromote(%.*s ...)", url_len > 30 ? 30 : url_len, url);
    hash.init(url, url_len);

    // We have to hold the lock across all list and hash access / updates
    TSMutexLock(_lock);

    map_it = _map.find(&hash);
    if (_map.end() != map_it) {
      // We have an entry in the URL
      if (++(map_it->second->second) >= _hits) {
        // Promoted! Cleanup the LRU, and signal success. Save the promoted entry on the freelist.
        TSDebug(PLUGIN_NAME, "saving the LRUEntry to the freelist");
        _freelist.splice(_freelist.begin(), _list, map_it->second);
        _map.erase(map_it->first);
        ret = true;
      } else {
        // It's still not promoted, make sure it's moved to the front of the list
        _list.splice(_list.begin(), _list, map_it->second);
      }
    } else {
      // New LRU entry for the URL, try to repurpose the list entry as much as possible
      if (_list.size() >= _buckets) {
        TSDebug(PLUGIN_NAME, "repurposing last LRUHash entry");
        _list.splice(_list.begin(), _list, --_list.end());
        _map.erase(&(_list.begin()->first));
      } else if (_freelist.size() > 0) {
        TSDebug(PLUGIN_NAME, "reusing LRUEntry from freelist");
        _list.splice(_list.begin(), _freelist, _freelist.begin());
      } else {
        TSDebug(PLUGIN_NAME, "creating new LRUEntry");
        _list.push_front(NULL_LRU_ENTRY);
      }
      // Update the "new" LRUEntry and add it to the hash
      _list.begin()->first = hash;
      _list.begin()->second = 1;
      _map[&(_list.begin()->first)] = _list.begin();
    }

    TSMutexUnlock(_lock);

    return ret;
  }

  void
  usage() const
  {
    TSError(PLUGIN_NAME, "Usage: @plugin=%s.so @pparam=--policy=lru @pparam=--buckets=<n> --hits=<m> --sample=<x>", PLUGIN_NAME);
  }

  const char *
  policyName() const
  {
    return "LRU";
  }

private:
  unsigned _buckets;
  unsigned _hits;
  // For the LRU
  TSMutex _lock;
  LRUMap _map;
  LRUList _list, _freelist;
};

//////////////////////////////////////////////////////////////////////////////////////////////
// This holds the configuration for a remap rule, as well as parses the configurations.
//
class PromotionConfig
{
public:
  PromotionConfig() : _policy(NULL) {}

  ~PromotionConfig() { delete _policy; }

  PromotionPolicy *
  getPolicy() const
  {
    return _policy;
  }

  // Parse the command line arguments to the plugin, and instantiate the appropriate policy
  bool
  factory(int argc, char *argv[])
  {
    optind = 0;
    while (true) {
      int opt = getopt_long(argc, (char *const *)argv, "psbh", longopt, NULL);

      if (opt == -1) {
        break;
      } else if (opt == 'p') {
        if (0 == strncasecmp(optarg, "chance", 6)) {
          _policy = new ChancePolicy();
        } else if (0 == strncasecmp(optarg, "lru", 3)) {
          _policy = new LRUPolicy();
        } else {
          TSError("Unknown policy --policy=%s", optarg);
          return false;
        }
        if (_policy) {
          TSDebug(PLUGIN_NAME, "created remap with cache promotion policy = %s", _policy->policyName());
        }
      } else {
        if (_policy) {
          // The --sample (-s) option is allowed for all configs, but only after --policy is specified.
          if (opt == 's') {
            _policy->setSample(optarg);
          } else {
            if (!_policy->parseOption(opt, optarg)) {
              TSError("The specified policy (%s) does not support the -%c option", _policy->policyName(), opt);
              delete _policy;
              _policy = NULL;
              return false;
            }
          }
        } else {
          TSError("The --policy=<n> parameter must come first on the remap configuration");
          return false;
        }
      }
    }

    return true;
  }

private:
  PromotionPolicy *_policy;
};

//////////////////////////////////////////////////////////////////////////////////////////////
// Main "plugin", a TXN hook in the TS_HTTP_READ_CACHE_HDR_HOOK. Unless the policy allows
// caching, we will turn off the cache from here on for the TXN.
//
// NOTE: This is not optimal, the goal was to handle this before we lock the URL in the
// cache. However, that does not work. Hence, for now, we also schedule the continuation
// for READ_RESPONSE_HDR such that we can turn off  the actual cache write.
//
static int
cont_handle_policy(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
  PromotionConfig *config = static_cast<PromotionConfig *>(TSContDataGet(contp));
  int obj_status;

  switch (event) {
  // Main HOOK
  case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
    if (TS_ERROR != TSHttpTxnCacheLookupStatusGet(txnp, &obj_status)) {
      switch (obj_status) {
      case TS_CACHE_LOOKUP_MISS:
      case TS_CACHE_LOOKUP_SKIPPED:
        if (config->getPolicy()->doSample() && config->getPolicy()->doPromote(txnp)) {
          TSDebug(PLUGIN_NAME, "cache-status is %d, and leaving cache on (promoted)", obj_status);
        } else {
          TSDebug(PLUGIN_NAME, "cache-status is %d, and turning off the cache (not promoted)", obj_status);
          TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
        }
        break;
      default:
        // Do nothing, just let it handle the lookup.
        TSDebug(PLUGIN_NAME, "cache-status is %d (hit), nothing to do", obj_status);
        break;
      }
    }
    break;

  // Temporaray hack, to deal with the fact that we can turn off the cache earlier
  case TS_EVENT_HTTP_READ_RESPONSE_HDR:
    TSHttpTxnServerRespNoStoreSet(txnp, 1);
    break;

  // Should not happen
  default:
    TSDebug(PLUGIN_NAME, "Unhandled event %d", (int)event);
    break;
  }

  // Reenable and continue with the state machine.
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Initialize the plugin as a remap plugin.
//
TSReturnCode
TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
{
  if (api_info->size < sizeof(TSRemapInterface)) {
    strncpy(errbuf, "[tsremap_init] - Incorrect size of TSRemapInterface structure", errbuf_size - 1);
    return TS_ERROR;
  }

  if (api_info->tsremap_version < TSREMAP_VERSION) {
    snprintf(errbuf, errbuf_size - 1, "[tsremap_init] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
             (api_info->tsremap_version & 0xffff));
    return TS_ERROR;
  }

  TSDebug(PLUGIN_NAME, "remap plugin is successfully initialized");
  return TS_SUCCESS; /* success */
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf */, int /* errbuf_size */)
{
  PromotionConfig *config = new PromotionConfig;

  --argc;
  ++argv;
  if (config->factory(argc, argv)) {
    TSCont contp = TSContCreate(cont_handle_policy, TSMutexCreate());

    TSContDataSet(contp, static_cast<void *>(config));
    *ih = static_cast<void *>(contp);

    return TS_SUCCESS;
  } else {
    delete config;
    return TS_ERROR;
  }
}

void
TSRemapDeleteInstance(void *ih)
{
  TSCont contp = static_cast<TSCont>(ih);
  PromotionConfig *config = static_cast<PromotionConfig *>(TSContDataGet(contp));

  delete config;
  TSContDestroy(contp);
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Schedule the cache-read continuation for this remap rule.
//
TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo * /* ATS_UNUSED rri */)
{
  if (NULL == ih) {
    TSDebug(PLUGIN_NAME, "No ACLs configured, this is probably a plugin bug");
  } else {
    TSCont contp = static_cast<TSCont>(ih);

    TSHttpTxnHookAdd(rh, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
  }

  return TSREMAP_NO_REMAP;
}
<commit_msg>TS-3505 A few improvements, thanks Gancho for review<commit_after>/*
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you 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.
*/

#include <ts/ts.h>
#include <ts/remap.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <time.h>
#include <openssl/sha.h>

#include <string>
#include <list>

// TODO: We should eliminate this when we have unordered_map on all supported platforms
#if HAVE_UNORDERED_MAP
#include <unordered_map>
#else
#include <map>
#endif

static const char *PLUGIN_NAME = "cache_promote";

//////////////////////////////////////////////////////////////////////////////////////////////
// Note that all options for all policies has to go here. Not particularly pretty...
//
static const struct option longopt[] = {{const_cast<char *>("policy"), required_argument, NULL, 'p'},
                                        // This is for both Chance and LRU (optional) policy
                                        {const_cast<char *>("sample"), required_argument, NULL, 's'},
                                        // For the LRU policy
                                        {const_cast<char *>("buckets"), required_argument, NULL, 'b'},
                                        {const_cast<char *>("hits"), required_argument, NULL, 'h'},
                                        // EOF
                                        {NULL, no_argument, NULL, '\0'}};

//////////////////////////////////////////////////////////////////////////////////////////////
// Abstract base class for all policies.
//
class PromotionPolicy
{
public:
  PromotionPolicy() : _sample(0.0)
  {
    // This doesn't have to be perfect, since this is just chance sampling.
    // coverity[dont_call]
    srand48((long)time(NULL));
  }

  void
  setSample(char *s)
  {
    _sample = strtof(s, NULL) / 100.0;
  }

  float
  getSample() const
  {
    return _sample;
  }

  bool
  doSample() const
  {
    if (_sample > 0) {
      // coverity[dont_call]
      double r = drand48();

      if (_sample > r) {
        TSDebug(PLUGIN_NAME, "checking sampling, is %f > %f? Yes!", _sample, r);
      } else {
        TSDebug(PLUGIN_NAME, "checking sampling, is %f > %f? No!", _sample, r);
        return false;
      }
    }
    return true;
  }

  virtual ~PromotionPolicy(){};

  virtual bool
  parseOption(int opt, char *optarg)
  {
    return false;
  }

  // These are pure virtual
  virtual bool doPromote(TSHttpTxn txnp) = 0;
  virtual const char *policyName() const = 0;
  virtual void usage() const = 0;

private:
  float _sample;
};

//////////////////////////////////////////////////////////////////////////////////////////////
// This is the simplest of all policies, just give each request a (small)
// percentage chance to be promoted to cache.
//
class ChancePolicy : public PromotionPolicy
{
public:
  bool doPromote(TSHttpTxn /* txnp ATS_UNUSED */)
  {
    TSDebug(PLUGIN_NAME, "ChancePolicy::doPromote(%f)", getSample());
    return true;
  }

  void
  usage() const
  {
    TSError(PLUGIN_NAME, "Usage: @plugin=%s.so @pparam=--policy=chance @pparam=--sample=<x>%", PLUGIN_NAME);
  }

  const char *
  policyName() const
  {
    return "chance";
  }
};

//////////////////////////////////////////////////////////////////////////////////////////////
// The LRU based policy keeps track of <bucket> number of URLs, with a counter for each slot.
// Objects are not promoted unless the counter reaches <hits> before it gets evicted. An
// optional <chance> parameter can be used to sample hits, this can reduce contention and
// churning in the LRU as well.
//
class LRUHash
{
  friend struct LRUHashHasher;

public:
  LRUHash() { TSDebug(PLUGIN_NAME, "In LRUHash()"); }

  ~LRUHash() { TSDebug(PLUGIN_NAME, "In ~LRUHash()"); }

  LRUHash &operator=(const LRUHash &h)
  {
    TSDebug(PLUGIN_NAME, "copying an LRUHash object");
    memcpy(_hash, h._hash, sizeof(_hash));
    return *this;
  }

  void
  init(char *data, int len)
  {
    SHA_CTX sha;

    SHA1_Init(&sha);
    SHA1_Update(&sha, data, len);
    SHA1_Final(_hash, &sha);
  }

private:
  u_char _hash[SHA_DIGEST_LENGTH];
};

struct LRUHashHasher {
  bool operator()(const LRUHash *s1, const LRUHash *s2) const { return 0 == memcmp(s1->_hash, s2->_hash, sizeof(s2->_hash)); }

  size_t operator()(const LRUHash *s) const { return *((size_t *)s->_hash) ^ *((size_t *)(s->_hash + 9)); }
};

typedef std::pair<LRUHash, unsigned> LRUEntry;
typedef std::list<LRUEntry> LRUList;

static LRUEntry NULL_LRU_ENTRY; // Used to create an "empty" new LRUEntry

// TODO: We should eliminate this when we have unordered_map on all supported platforms.
#if HAVE_UNORDERED_MAP
#include <unordered_map>
typedef std::unordered_map<const LRUHash *, LRUList::iterator, LRUHashHasher, LRUHashHasher> LRUMap;
#else
#include <map>
typedef std::map<LRUHash *, LRUList::iterator> LRUMap;
#endif

class LRUPolicy : public PromotionPolicy
{
public:
  LRUPolicy() : PromotionPolicy(), _buckets(1000), _hits(10), _lock(TSMutexCreate()) {}

  ~LRUPolicy()
  {
    TSDebug(PLUGIN_NAME, "deleting LRUPolicy object");
    TSMutexLock(_lock);

    _map.clear();
    _list.clear();
    _freelist.clear();

    TSMutexUnlock(_lock);

    // ToDo: Destroy mutex ? TS-1432
  }

  bool
  parseOption(int opt, char *optarg)
  {
    switch (opt) {
    case 'b':
      _buckets = static_cast<unsigned>(strtol(optarg, NULL, 10));
      break;
    case 'h':
      _hits = static_cast<unsigned>(strtol(optarg, NULL, 10));
      break;
    default:
      // All other options are unsupported for this policy
      return false;
    }

    // This doesn't have to be perfect, since this is just chance sampling.
    // coverity[dont_call]
    srand48((long)time(NULL) ^ (long)getpid() ^ (long)getppid());
#if HAVE_UNORDERED_MAP
    _map.reserve(_buckets);
#endif

    return true;
  }

  bool
  doPromote(TSHttpTxn txnp)
  {
    LRUHash hash;
    LRUMap::iterator map_it;
    int url_len = 0;
    char *url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_len);
    bool ret = false;

    TSDebug(PLUGIN_NAME, "LRUPolicy::doPromote(%.*s ...)", url_len > 30 ? 30 : url_len, url);
    hash.init(url, url_len);

    // We have to hold the lock across all list and hash access / updates
    TSMutexLock(_lock);

    map_it = _map.find(&hash);
    if (_map.end() != map_it) {
      // We have an entry in the LRU
      if (++(map_it->second->second) >= _hits) {
        // Promoted! Cleanup the LRU, and signal success. Save the promoted entry on the freelist.
        TSDebug(PLUGIN_NAME, "saving the LRUEntry to the freelist");
        _freelist.splice(_freelist.begin(), _list, map_it->second);
        _map.erase(map_it->first);
        ret = true;
      } else {
        // It's still not promoted, make sure it's moved to the front of the list
        _list.splice(_list.begin(), _list, map_it->second);
      }
    } else {
      // New LRU entry for the URL, try to repurpose the list entry as much as possible
      if (_list.size() >= _buckets) {
        TSDebug(PLUGIN_NAME, "repurposing last LRUHash entry");
        _list.splice(_list.begin(), _list, --_list.end());
        _map.erase(&(_list.begin()->first));
      } else if (_freelist.size() > 0) {
        TSDebug(PLUGIN_NAME, "reusing LRUEntry from freelist");
        _list.splice(_list.begin(), _freelist, _freelist.begin());
      } else {
        TSDebug(PLUGIN_NAME, "creating new LRUEntry");
        _list.push_front(NULL_LRU_ENTRY);
      }
      // Update the "new" LRUEntry and add it to the hash
      _list.begin()->first = hash;
      _list.begin()->second = 1;
      _map[&(_list.begin()->first)] = _list.begin();
    }

    TSMutexUnlock(_lock);

    return ret;
  }

  void
  usage() const
  {
    TSError(PLUGIN_NAME, "Usage: @plugin=%s.so @pparam=--policy=lru @pparam=--buckets=<n> --hits=<m> --sample=<x>", PLUGIN_NAME);
  }

  const char *
  policyName() const
  {
    return "LRU";
  }

private:
  unsigned _buckets;
  unsigned _hits;
  // For the LRU
  TSMutex _lock;
  LRUMap _map;
  LRUList _list, _freelist;
};

//////////////////////////////////////////////////////////////////////////////////////////////
// This holds the configuration for a remap rule, as well as parses the configurations.
//
class PromotionConfig
{
public:
  PromotionConfig() : _policy(NULL) {}

  ~PromotionConfig() { delete _policy; }

  PromotionPolicy *
  getPolicy() const
  {
    return _policy;
  }

  // Parse the command line arguments to the plugin, and instantiate the appropriate policy
  bool
  factory(int argc, char *argv[])
  {
    optind = 0;
    while (true) {
      int opt = getopt_long(argc, (char *const *)argv, "psbh", longopt, NULL);

      if (opt == -1) {
        break;
      } else if (opt == 'p') {
        if (0 == strncasecmp(optarg, "chance", 6)) {
          _policy = new ChancePolicy();
        } else if (0 == strncasecmp(optarg, "lru", 3)) {
          _policy = new LRUPolicy();
        } else {
          TSError("Unknown policy --policy=%s", optarg);
          return false;
        }
        if (_policy) {
          TSDebug(PLUGIN_NAME, "created remap with cache promotion policy = %s", _policy->policyName());
        }
      } else {
        if (_policy) {
          // The --sample (-s) option is allowed for all configs, but only after --policy is specified.
          if (opt == 's') {
            _policy->setSample(optarg);
          } else {
            if (!_policy->parseOption(opt, optarg)) {
              TSError("The specified policy (%s) does not support the -%c option", _policy->policyName(), opt);
              delete _policy;
              _policy = NULL;
              return false;
            }
          }
        } else {
          TSError("The --policy=<n> parameter must come first on the remap configuration");
          return false;
        }
      }
    }

    return true;
  }

private:
  PromotionPolicy *_policy;
};

//////////////////////////////////////////////////////////////////////////////////////////////
// Main "plugin", a TXN hook in the TS_HTTP_READ_CACHE_HDR_HOOK. Unless the policy allows
// caching, we will turn off the cache from here on for the TXN.
//
// NOTE: This is not optimal, the goal was to handle this before we lock the URL in the
// cache. However, that does not work. Hence, for now, we also schedule the continuation
// for READ_RESPONSE_HDR such that we can turn off  the actual cache write.
//
static int
cont_handle_policy(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
  PromotionConfig *config = static_cast<PromotionConfig *>(TSContDataGet(contp));
  int obj_status;

  switch (event) {
  // Main HOOK
  case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
    if (TS_ERROR != TSHttpTxnCacheLookupStatusGet(txnp, &obj_status)) {
      switch (obj_status) {
      case TS_CACHE_LOOKUP_MISS:
      case TS_CACHE_LOOKUP_SKIPPED:
        if (config->getPolicy()->doSample() && config->getPolicy()->doPromote(txnp)) {
          TSDebug(PLUGIN_NAME, "cache-status is %d, and leaving cache on (promoted)", obj_status);
        } else {
          TSDebug(PLUGIN_NAME, "cache-status is %d, and turning off the cache (not promoted)", obj_status);
          TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
        }
        break;
      default:
        // Do nothing, just let it handle the lookup.
        TSDebug(PLUGIN_NAME, "cache-status is %d (hit), nothing to do", obj_status);
        break;
      }
    }
    break;

  // Temporaray hack, to deal with the fact that we can turn off the cache earlier
  case TS_EVENT_HTTP_READ_RESPONSE_HDR:
    TSHttpTxnServerRespNoStoreSet(txnp, 1);
    break;

  // Should not happen
  default:
    TSDebug(PLUGIN_NAME, "Unhandled event %d", (int)event);
    break;
  }

  // Reenable and continue with the state machine.
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Initialize the plugin as a remap plugin.
//
TSReturnCode
TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
{
  if (api_info->size < sizeof(TSRemapInterface)) {
    strncpy(errbuf, "[tsremap_init] - Incorrect size of TSRemapInterface structure", errbuf_size - 1);
    return TS_ERROR;
  }

  if (api_info->tsremap_version < TSREMAP_VERSION) {
    snprintf(errbuf, errbuf_size - 1, "[tsremap_init] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
             (api_info->tsremap_version & 0xffff));
    return TS_ERROR;
  }

  TSDebug(PLUGIN_NAME, "remap plugin is successfully initialized");
  return TS_SUCCESS; /* success */
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf */, int /* errbuf_size */)
{
  PromotionConfig *config = new PromotionConfig;

  --argc;
  ++argv;
  if (config->factory(argc, argv)) {
    TSCont contp = TSContCreate(cont_handle_policy, TSMutexCreate());

    TSContDataSet(contp, static_cast<void *>(config));
    *ih = static_cast<void *>(contp);

    return TS_SUCCESS;
  } else {
    delete config;
    return TS_ERROR;
  }
}

void
TSRemapDeleteInstance(void *ih)
{
  TSCont contp = static_cast<TSCont>(ih);
  PromotionConfig *config = static_cast<PromotionConfig *>(TSContDataGet(contp));

  delete config;
  TSContDestroy(contp);
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Schedule the cache-read continuation for this remap rule.
//
TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo * /* ATS_UNUSED rri */)
{
  if (NULL == ih) {
    TSDebug(PLUGIN_NAME, "No ACLs configured, this is probably a plugin bug");
  } else {
    TSCont contp = static_cast<TSCont>(ih);

    TSHttpTxnHookAdd(rh, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
  }

  return TSREMAP_NO_REMAP;
}
<|endoftext|>


<commit_before>// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __RAPICORN_XMLNODE_HH__
#define __RAPICORN_XMLNODE_HH__

#include <rcore/markup.hh>
#include <rcore/objects.hh>

namespace Rapicorn {

class XmlNode : public virtual ReferenceCountable, public virtual DataListContainer {
  String                name_; // element name
  XmlNode              *parent_;
  StringVector          attribute_names_;
  StringVector          attribute_values_;
  String                file_;
  uint                  line_, char_;
protected:
  explicit              XmlNode         (const String&, uint, uint, const String&);
  uint64                flags           () const;
  void                  flags           (uint64 flags);
  virtual              ~XmlNode         ();
  static void           set_parent      (XmlNode *c, XmlNode *p);
public:
  typedef const vector<XmlNode*>     ConstNodes;
  typedef ConstNodes::const_iterator ConstChildIter;
  String                name            () const                { return name_; }
  XmlNode*              parent          () const                { return parent_; }
  const StringVector&   list_attributes () const                { return attribute_names_; }
  const StringVector&   list_values     () const                { return attribute_values_; }
  bool                  set_attribute   (const String   &name,
                                         const String   &value,
                                         bool            replace = true);
  String                get_attribute   (const String   &name,
                                         bool            case_insensitive = false) const;
  bool                  has_attribute   (const String   &name,
                                         bool            case_insensitive = false) const;
  bool                  del_attribute   (const String   &name);
  String                parsed_file     () const                { return file_; }
  uint                  parsed_line     () const                { return line_; }
  uint                  parsed_char     () const                { return char_; }
  /* text node */
  virtual String        text            () const = 0;
  bool                  istext          () const                { return name_.size() == 0; }
  /* parent node */
  virtual ConstNodes&   children        () const = 0;
  ConstChildIter        children_begin  () const { return children().begin(); }
  ConstChildIter        children_end    () const { return children().end(); }
  const XmlNode*        first_child     (const String   &element_name) const;
  virtual bool          add_child       (XmlNode        &child) = 0;
  virtual bool          del_child       (XmlNode        &child) = 0;
  void                  steal_children  (XmlNode        &parent);
  /* hints */
  void                  break_after     (bool            newline_after_tag);
  bool                  break_after     () const;
  void                  break_within    (bool            newlines_around_chidlren);
  bool                  break_within    () const;
  String                xml_string      (uint64          indent = 0, bool include_outer = true, uint64 recursion_depth = -1) const;
  /* nodes */
  static XmlNode*       create_text     (const String   &utf8text,
                                         uint            line,
                                         uint            _char,
                                         const String   &file);
  static XmlNode*       create_parent   (const String   &element_name,
                                         uint            line,
                                         uint            _char,
                                         const String   &file);
  /* IO */
  static XmlNode*       parse_xml       (const String   &input_name,
                                         const char     *utf8data,
                                         ssize_t         utf8data_len,
                                         MarkupParser::Error *error,
                                         const String   &roottag = "");
  static String         xml_escape      (const String   &input);
};

} // Rapicorn

#endif  /* __RAPICORN_XMLNODE_HH__ */
<commit_msg>RCORE: discourage public XmlNode use<commit_after>// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __RAPICORN_XMLNODE_HH__
#define __RAPICORN_XMLNODE_HH__

#include <rcore/markup.hh>
#include <rcore/objects.hh>

namespace Rapicorn {

/** Simple XML tree representation.
 * @DISCOURAGED: Nonpublic API, data structure used internally.
 */
class XmlNode : public virtual ReferenceCountable, public virtual DataListContainer {
  String                name_; // element name
  XmlNode              *parent_;
  StringVector          attribute_names_;
  StringVector          attribute_values_;
  String                file_;
  uint                  line_, char_;
protected:
  explicit              XmlNode         (const String&, uint, uint, const String&);
  uint64                flags           () const;
  void                  flags           (uint64 flags);
  virtual              ~XmlNode         ();
  static void           set_parent      (XmlNode *c, XmlNode *p);
public:
  typedef const vector<XmlNode*>     ConstNodes;
  typedef ConstNodes::const_iterator ConstChildIter;
  String                name            () const                { return name_; }
  XmlNode*              parent          () const                { return parent_; }
  const StringVector&   list_attributes () const                { return attribute_names_; }
  const StringVector&   list_values     () const                { return attribute_values_; }
  bool                  set_attribute   (const String   &name,
                                         const String   &value,
                                         bool            replace = true);
  String                get_attribute   (const String   &name,
                                         bool            case_insensitive = false) const;
  bool                  has_attribute   (const String   &name,
                                         bool            case_insensitive = false) const;
  bool                  del_attribute   (const String   &name);
  String                parsed_file     () const                { return file_; }
  uint                  parsed_line     () const                { return line_; }
  uint                  parsed_char     () const                { return char_; }
  /* text node */
  virtual String        text            () const = 0;
  bool                  istext          () const                { return name_.size() == 0; }
  /* parent node */
  virtual ConstNodes&   children        () const = 0;
  ConstChildIter        children_begin  () const { return children().begin(); }
  ConstChildIter        children_end    () const { return children().end(); }
  const XmlNode*        first_child     (const String   &element_name) const;
  virtual bool          add_child       (XmlNode        &child) = 0;
  virtual bool          del_child       (XmlNode        &child) = 0;
  void                  steal_children  (XmlNode        &parent);
  /* hints */
  void                  break_after     (bool            newline_after_tag);
  bool                  break_after     () const;
  void                  break_within    (bool            newlines_around_chidlren);
  bool                  break_within    () const;
  String                xml_string      (uint64          indent = 0, bool include_outer = true, uint64 recursion_depth = -1) const;
  /* nodes */
  static XmlNode*       create_text     (const String   &utf8text,
                                         uint            line,
                                         uint            _char,
                                         const String   &file);
  static XmlNode*       create_parent   (const String   &element_name,
                                         uint            line,
                                         uint            _char,
                                         const String   &file);
  /* IO */
  static XmlNode*       parse_xml       (const String   &input_name,
                                         const char     *utf8data,
                                         ssize_t         utf8data_len,
                                         MarkupParser::Error *error,
                                         const String   &roottag = "");
  static String         xml_escape      (const String   &input);
};

} // Rapicorn

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


<commit_before>/**
* @file Directory.cpp
* Implementation of a class for accessing directories.
* @author Colin Graf
*/

#include <cstring>
#ifdef _WIN32
#include <windows.h>
#ifdef _MSC_VER
#include <tchar.h>
#else
#include <strings.h> // strcasecmp
#endif
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fnmatch.h>
#include <cerrno>
#endif

#if defined(_WIN32) && !defined(_MSC_VER)
#define _tcsrchr strrchr
#define _tcspbrk strpbrk
#define _tcsicmp strcasecmp
#define _tcslen strlen
#endif

#include <nstd/Directory.h>
#include <nstd/File.h>
#include <nstd/Debug.h>

Directory::Directory()
{
#ifdef _WIN32
  ASSERT(sizeof(ffd) >= sizeof(WIN32_FIND_DATA));
  findFile = INVALID_HANDLE_VALUE;
#else
  dp = 0;
#endif
}

Directory::~Directory()
{
#ifdef _WIN32
  if(findFile != INVALID_HANDLE_VALUE)
    FindClose((HANDLE)findFile);
#else
  if(dp)
    closedir((DIR*)dp);
#endif
}

bool_t Directory::remove(const String& dir)
{
  String path = dir;
  while(path != _T("."))
  {
#ifdef _WIN32
    if(!RemoveDirectory(path))
      return false;
#else
    if(rmdir(path) != 0)
      return false;
#endif
    path = File::dirname(path);
  }
  return true;
}

bool_t Directory::open(const String& dirpath, const String& pattern, bool_t dirsOnly)
{
#ifdef _WIN32
  if(findFile != INVALID_HANDLE_VALUE)
  {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }

  this->dirsOnly = dirsOnly;
  this->dirpath = dirpath;
  const tchar_t* patExt = _tcsrchr(pattern, _T('.'));
  this->patternExtension = (patExt && !_tcspbrk(patExt + 1, _T("*?"))) ? String(patExt + 1, -1) : String();

  String searchPath = dirpath;
  searchPath.reserve(dirpath.length() + 1 + pattern.length());
  if(!dirpath.isEmpty())
    searchPath.append(_T('/'));
  searchPath.append(pattern);

  findFile = FindFirstFileEx(searchPath,
#if _WIN32_WINNT > 0x0600
	  FindExInfoBasic,
#else
	  FindExInfoStandard,
#endif
	  (LPWIN32_FIND_DATA)ffd, dirsOnly ? FindExSearchLimitToDirectories : FindExSearchNameMatch, NULL, 0);
  if(findFile == INVALID_HANDLE_VALUE)
    return false;
  bufferedEntry = true;
  return true;
#else
  if(dp)
  {
    errno = EINVAL;
    return false;
  }

  this->dirsOnly = dirsOnly;
  this->dirpath = dirpath;
  this->pattern = pattern;

  dp = opendir(dirpath);
  return dp != 0;
#endif
}

bool_t Directory::read(String& name, bool_t& isDir)
{
#ifdef _WIN32
  if(!findFile)
  {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
  for(;;)
  {
    if(bufferedEntry)
      bufferedEntry = false;
    else if(!FindNextFile((HANDLE)findFile, (LPWIN32_FIND_DATA)ffd))
    {
      DWORD lastError = GetLastError();
      FindClose((HANDLE)findFile);
      findFile = INVALID_HANDLE_VALUE;
      SetLastError(lastError);
      return false;
    }
    isDir = (((LPWIN32_FIND_DATA)ffd)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
    if(dirsOnly && !isDir)
      continue;
    const tchar_t* str = ((LPWIN32_FIND_DATA)ffd)->cFileName;
    if(isDir && *str == _T('.') && (str[1] == _T('\0') || (str[1] == _T('.') && str[2] == _T('\0'))))
      continue;

    if(!patternExtension.isEmpty())
    {
      const tchar_t* patExt = _tcsrchr(str, _T('.'));
      if(!patExt || _tcsicmp(patternExtension, patExt + 1) != 0)
        continue;
    }

    name = String(str, (uint_t)_tcslen(str));
    return true;
  }
#else
  if(!dp)
  {
    errno = EINVAL;
    return false;
  }

  for(;;)
  {
    struct dirent* dent = readdir((DIR*)dp);
    if(!dent)
    {
      int lastErrno = errno;
      closedir((DIR*)dp);
      dp = 0;
      errno = lastErrno;
      return false;
    }
    const char_t* const str = dent->d_name;
    if(fnmatch(pattern, str, 0) == 0)
    {
      name = String(str, strlen(str));
      isDir = false;
      if(dent->d_type == DT_DIR)
        isDir = true;
      else if(dent->d_type == DT_LNK || dent->d_type == DT_UNKNOWN)
      {
        String path = dirpath;
        path.append(_T('/'));
        path.append(name);
        struct stat buff;
        if(stat(path, &buff) == 0)
          if(S_ISDIR(buff.st_mode))
            isDir = true;
      }
      if(dirsOnly && !isDir)
        continue;
      if(isDir && *str == _T('.') && (str[1] == _T('\0') || (str[1] == _T('.') && str[2] == _T('\0'))))
        continue;
      return true;
    }
  }
  return false; // unreachable
#endif
}

bool_t Directory::exists(const String& dir)
{
#ifdef _WIN32
  WIN32_FIND_DATA wfd;
  HANDLE hFind = FindFirstFile(dir, &wfd);
  if(hFind == INVALID_HANDLE_VALUE)
    return false;
  bool_t isDir = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
  FindClose(hFind);
  return isDir;
#else
  struct stat buf;
  if(stat(dir, &buf) != 0)
    return false;
  return S_ISDIR(buf.st_mode);
#endif
}

bool_t Directory::create(const String& dir)
{
  // TODO: set errno correctly

  const tchar_t* start = dir;
  const tchar_t* pos = &start[dir.length() - 1];
  for(; pos >= start; --pos)
    if(*pos == _T('\\') || *pos == _T('/'))
    {
      if(!create(dir.substr(0, (int_t)(pos - start))))
      {
        return false;
      }
      break;
    }
  ++pos;
  bool_t result = false;
  if(*pos)
#ifdef _WIN32
    result = CreateDirectory(dir, NULL) == TRUE;
#else
    result = mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
#endif
  return result;
}

bool_t Directory::change(const String& dir)
{
#ifdef _WIN32
  return SetCurrentDirectory(dir) != FALSE;
#else
  return chdir(dir) == 0;
#endif
}
<commit_msg>Directory: Fix pattern matching issue<commit_after>/**
* @file Directory.cpp
* Implementation of a class for accessing directories.
* @author Colin Graf
*/

#include <cstring>
#ifdef _WIN32
#include <windows.h>
#ifdef _MSC_VER
#include <tchar.h>
#else
#include <strings.h> // strcasecmp
#endif
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fnmatch.h>
#include <cerrno>
#endif

#if defined(_WIN32) && !defined(_MSC_VER)
#define _tcsrchr strrchr
#define _tcspbrk strpbrk
#define _tcsicmp strcasecmp
#define _tcslen strlen
#endif

#include <nstd/Directory.h>
#include <nstd/File.h>
#include <nstd/Debug.h>

Directory::Directory()
{
#ifdef _WIN32
  ASSERT(sizeof(ffd) >= sizeof(WIN32_FIND_DATA));
  findFile = INVALID_HANDLE_VALUE;
#else
  dp = 0;
#endif
}

Directory::~Directory()
{
#ifdef _WIN32
  if(findFile != INVALID_HANDLE_VALUE)
    FindClose((HANDLE)findFile);
#else
  if(dp)
    closedir((DIR*)dp);
#endif
}

bool_t Directory::remove(const String& dir)
{
  String path = dir;
  while(path != _T("."))
  {
#ifdef _WIN32
    if(!RemoveDirectory(path))
      return false;
#else
    if(rmdir(path) != 0)
      return false;
#endif
    path = File::dirname(path);
  }
  return true;
}

bool_t Directory::open(const String& dirpath, const String& pattern, bool_t dirsOnly)
{
#ifdef _WIN32
  if(findFile != INVALID_HANDLE_VALUE)
  {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }

  this->dirsOnly = dirsOnly;
  this->dirpath = dirpath;
  const tchar_t* patExt = _tcsrchr(pattern, _T('.'));
  this->patternExtension = (patExt && !_tcspbrk(++patExt, _T("*?"))) ? String(patExt, String::length(patExt)) : String();

  String searchPath = dirpath;
  searchPath.reserve(dirpath.length() + 1 + pattern.length());
  if(!dirpath.isEmpty())
    searchPath.append(_T('/'));
  searchPath.append(pattern);

  findFile = FindFirstFileEx(searchPath,
#if _WIN32_WINNT > 0x0600
    FindExInfoBasic,
#else
    FindExInfoStandard,
#endif
    (LPWIN32_FIND_DATA)ffd, dirsOnly ? FindExSearchLimitToDirectories : FindExSearchNameMatch, NULL, 0);
  if(findFile == INVALID_HANDLE_VALUE)
    return false;
  bufferedEntry = true;
  return true;
#else
  if(dp)
  {
    errno = EINVAL;
    return false;
  }

  this->dirsOnly = dirsOnly;
  this->dirpath = dirpath;
  this->pattern = pattern;

  dp = opendir(dirpath);
  return dp != 0;
#endif
}

bool_t Directory::read(String& name, bool_t& isDir)
{
#ifdef _WIN32
  if(!findFile)
  {
    SetLastError(ERROR_INVALID_HANDLE);
    return false;
  }
  for(;;)
  {
    if(bufferedEntry)
      bufferedEntry = false;
    else if(!FindNextFile((HANDLE)findFile, (LPWIN32_FIND_DATA)ffd))
    {
      DWORD lastError = GetLastError();
      FindClose((HANDLE)findFile);
      findFile = INVALID_HANDLE_VALUE;
      SetLastError(lastError);
      return false;
    }
    isDir = (((LPWIN32_FIND_DATA)ffd)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
    if(dirsOnly && !isDir)
      continue;
    const tchar_t* str = ((LPWIN32_FIND_DATA)ffd)->cFileName;
    if(isDir && *str == _T('.') && (str[1] == _T('\0') || (str[1] == _T('.') && str[2] == _T('\0'))))
      continue;

    if(!patternExtension.isEmpty())
    {
      const tchar_t* patExt = _tcsrchr(str, _T('.'));
      if(!patExt || _tcsicmp(patternExtension, patExt + 1) != 0)
        continue;
    }

    name = String(str, (uint_t)_tcslen(str));
    return true;
  }
#else
  if(!dp)
  {
    errno = EINVAL;
    return false;
  }

  for(;;)
  {
    struct dirent* dent = readdir((DIR*)dp);
    if(!dent)
    {
      int lastErrno = errno;
      closedir((DIR*)dp);
      dp = 0;
      errno = lastErrno;
      return false;
    }
    const char_t* const str = dent->d_name;
    if(fnmatch(pattern, str, 0) == 0)
    {
      name = String(str, strlen(str));
      isDir = false;
      if(dent->d_type == DT_DIR)
        isDir = true;
      else if(dent->d_type == DT_LNK || dent->d_type == DT_UNKNOWN)
      {
        String path = dirpath;
        path.append(_T('/'));
        path.append(name);
        struct stat buff;
        if(stat(path, &buff) == 0)
          if(S_ISDIR(buff.st_mode))
            isDir = true;
      }
      if(dirsOnly && !isDir)
        continue;
      if(isDir && *str == _T('.') && (str[1] == _T('\0') || (str[1] == _T('.') && str[2] == _T('\0'))))
        continue;
      return true;
    }
  }
  return false; // unreachable
#endif
}

bool_t Directory::exists(const String& dir)
{
#ifdef _WIN32
  WIN32_FIND_DATA wfd;
  HANDLE hFind = FindFirstFile(dir, &wfd);
  if(hFind == INVALID_HANDLE_VALUE)
    return false;
  bool_t isDir = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
  FindClose(hFind);
  return isDir;
#else
  struct stat buf;
  if(stat(dir, &buf) != 0)
    return false;
  return S_ISDIR(buf.st_mode);
#endif
}

bool_t Directory::create(const String& dir)
{
  // TODO: set errno correctly

  const tchar_t* start = dir;
  const tchar_t* pos = &start[dir.length() - 1];
  for(; pos >= start; --pos)
    if(*pos == _T('\\') || *pos == _T('/'))
    {
      if(!create(dir.substr(0, (int_t)(pos - start))))
      {
        return false;
      }
      break;
    }
  ++pos;
  bool_t result = false;
  if(*pos)
#ifdef _WIN32
    result = CreateDirectory(dir, NULL) == TRUE;
#else
    result = mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
#endif
  return result;
}

bool_t Directory::change(const String& dir)
{
#ifdef _WIN32
  return SetCurrentDirectory(dir) != FALSE;
#else
  return chdir(dir) == 0;
#endif
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <sh/sh.hpp>
#include <sh/shutil.hpp>
#define GL_GLEXT_LOEGACY
#include <GL/gl.h>
#include <GL/glext.h>
#undef GL_GLEXT_LEGACY
#include "Shader.hpp"
#include "Globals.hpp"
#include "config.h"
#include "ShrikeCanvas.hpp"
#include <sh/ShObjMesh.hpp>

using namespace SH;
using namespace ShUtil;

class EdgeGooch : public Shader {
public:
  EdgeGooch();
  ~EdgeGooch();

  bool init();
  void initLists();

  void bind();

  void render();
  void facemodeVert(const ShObjEdge *e0, const ShObjEdge* e1,
    const ShObjEdge *e2, float move);

  ShProgram vertex() { return vsh & edgevsh;}
  ShProgram fragment() { return fsh & edgefsh; }

  ShProgram vsh, edgevsh;
  ShProgram fsh, edgefsh;

private:
  ShObjMesh m_obj;
  const ShObjMesh *m_canvasobj;
  ShPoint3f eyePosm;
  ShAttrib1f aspect;
  GLuint displayList;
  GLuint edgeDisplayList;

  static EdgeGooch* instance;
};

EdgeGooch::EdgeGooch()
  : Shader("Sillhouetted Gooch") {
    m_canvasobj = 0;
}

EdgeGooch::~EdgeGooch() { }

void EdgeGooch::bind() { }

void EdgeGooch::render() {
  const ShrikeCanvas *canvas = ShrikeCanvas::instance();
  if( m_canvasobj != canvas->getModel()) {
    m_canvasobj = canvas->getModel();
    m_obj = *m_canvasobj;
    m_obj.consolidateVertices();
    m_obj.mergeEdges();
    m_obj.normalizeNormals();
    initLists();
  }
  aspect = canvas->m_width / (float)canvas->m_height;

  // render fill
  shBindShader(vsh);
  shBindShader(fsh);
  glCallList(displayList);

  eyePosm = Globals::mv_inverse | ShPoint3f(0, 0, 0);
  shBindShader(edgevsh);
  shBindShader(edgefsh);
  glCallList(edgeDisplayList);
}

bool EdgeGooch::init()
{
  vsh = ShKernelLib::shVsh(Globals::mv, Globals::mvp);
  vsh = vsh << shExtract("lightPos") << Globals::lightPos;
  vsh = shSwizzle("normal", "lightVec", "posh") << vsh;

  ShColor3f SH_NAMEDECL(diffuseColor, "Diffuse Color") = ShConstant3f(0.9f, 0.9f, 0.6f);
  ShColor3f SH_NAMEDECL(cool, "Cool Color") = ShConstant3f(0.2f, 0.2f, 1.0f);
  ShColor3f SH_NAMEDECL(warm, "Warm Color") = ShConstant3f(1.0f, 0.3f, 0.3f);
  ShAttrib1f SH_NAMEDECL(width, "Edge Width") = ShConstant1f(0.02f);
  width.range(0.001f, 0.5f);
  ShAttrib1f SH_NAMEDECL(poffset, "Polygon Offset") = ShConstant1f(0.0f);
  poffset.range(-1.0f, 1.0f);

  fsh = SH_BEGIN_PROGRAM("gpu:fragment") { // gooch shader
    ShInputNormal3f SH_DECL(normal);
    ShInputVector3f SH_DECL(lightVec);

    normal = normalize(normal);
    lightVec= normalize(lightVec);
    ShConstant1f half(0.5f);
    ShAttrib1f blend = mad(dot(normal, lightVec), half, half);
    ShOutputColor3f SH_DECL(result) = diffuseColor * lerp(blend, warm, cool);
  } SH_END;

  edgevsh = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInOutNormal3f SH_NAMEDECL(n0, "normal"); // MCS normal (n0)
    ShInOutTexCoord2f SH_NAMEDECL(tc0, "texcoord" );
    ShInputTexCoord2f SH_DECL(tc1);
    ShInputTexCoord2f SH_DECL(tc2);
    ShInOutAttrib1f SH_DECL(move); //

    ShInputPoint3f  SH_DECL(p1);
    ShInputNormal3f  SH_DECL(n1);

    ShInputPoint3f  SH_DECL(p2);
    ShInputNormal3f  SH_DECL(n2);

    ShInOutPosition4f SH_NAMEDECL(p0, "posm");

    ShAttrib3f nvv; // nvv(i) holds dot product of ni and view vec to pi
    nvv(0) = normalize(eyePosm - p0(0,1,2)) | n0;
    nvv(1) = normalize(eyePosm - p1) | n1;
    nvv(2) = normalize(eyePosm - p2) | n2;

    // s(i) = 1 if there is a sign change between ni, ni+1
    ShAttrib3f s = (nvv * nvv(1,2,0)) < 0.0f;

    // find interpolated points between p0,p1 and p0,p2
    ShAttrib2f k = -nvv(1,2) / (nvv(0,0) - nvv(1,2));
    ShAttrib2f nk = ShConstant2f(1.0f, 1.0f) - k;
    p1 = lerp(k(0), p0(0,1,2), p1);
    n1 = lerp(k(0), n0, n1);
    tc1 = lerp(k(0), tc0, tc1);
    p2 = lerp(k(1), p0(0,1,2), p2);
    n2 = lerp(k(1), n0, n2);
    tc2 = lerp(k(1), tc0, tc2);

    // pick which interpolated point to use based on conditions
    ShAttrib1f snone = dot(s, s) < 0.5f; // not on sillhouete
    ShAttrib1f nots2_and_s0 = (1.0f - s(2)) * s(0); //
    p0(0,1,2) = snone * p0(0,1,2) + s(2) * p2 + nots2_and_s0 * p1;
    n0 = snone * n0 + s(2) * n2 + nots2_and_s0 * n1;
    tc0 = snone * tc0 + s(2) * tc2 + nots2_and_s0 * tc1;

    move *= ( 1.0f - snone ); // only expand quad on sillhouette
  } SH_END;

  ShProgram translator = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInputPosition4f SH_DECL(posm); // in MCS

    ShInOutTexCoord2f SH_DECL(texcoord);
    ShInOutNormal3f SH_DECL(normal); // in MCS, out VCS
    ShInputAttrib1f SH_DECL(move); // whether to move
    ShOutputPosition4f SH_DECL(posh); // out DCS

    posh = Globals::mvp | posm;

    ShPoint3f offposh = posm(0,1,2) + normal;
    offposh = Globals::mvp | offposh;
    ShPoint3f nph = posh(0,1,2) / posh(3);
    ShVector2f offset = normalize(offposh - nph )(0,1) * width * move * posh(3);
    offset(1) *= aspect;

    posh(0,1) += offset;
    posh(2) += poffset;

    normal = normalize(Globals::mv | normal);
    texcoord(0) += texcoord(1);
    texcoord(1) = move;
  } SH_END;

  edgevsh = namedConnect( edgevsh, translator );

  edgefsh = SH_BEGIN_PROGRAM("gpu:fragment") {
    ShOutputColor4f SH_DECL(kd) = ShConstant4f(0.0f, 0.0f, 0.0f, 1.0f);
  } SH_END;

  return true;
}

void EdgeGooch::facemodeVert(const ShObjEdge *e0, const ShObjEdge* e1,
    const ShObjEdge *e2, float move) {
  // Note, because of current backend binding order, texcoords must go first
  float vs[4];
  vs[3] = 0;
  e0->normal.getValues(vs); glNormal3fv(vs);
  e0->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0, vs);
  e1->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0 + 1, vs);
  e2->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0 + 2, vs);
  glMultiTexCoord1fARB(GL_TEXTURE0 + 3, move);
  e1->start->pos.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 4, vs);
  e1->normal.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 5, vs);
  e2->start->pos.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 6, vs);
  e2->normal.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 7, vs);
  e0->start->pos.getValues(vs); glVertex3fv(vs);
}

void EdgeGooch::initLists() {
  float vs[4];

  displayList = glGenLists(1);
  glNewList(displayList, GL_COMPILE);
  glBegin(GL_TRIANGLES);
    for(ShObjMesh::FaceSet::iterator I = m_obj.faces.begin();
          I != m_obj.faces.end(); ++I) {
      ShObjMesh::Edge* e = (*I)->edge;
      do {
        e->normal.getValues(vs); glNormal3fv(vs);
        e->texcoord.getValues(vs); glTexCoord2fv(vs);
        e->start->pos.getValues(vs); glVertex3fv(vs);
        e = e->next;
      } while( e != (*I)->edge);
    }
  glEnd();
  glEndList();

  ShObjMesh fixedObj = m_obj;
  fixedObj.generateVertexNormals(true); // force generate a single normal per vertex
  fixedObj.normalizeNormals();

  edgeDisplayList = glGenLists(1);
  glNewList(edgeDisplayList, GL_COMPILE);

  glBegin(GL_QUADS);
  for(ShObjMesh::FaceSet::iterator I = fixedObj.faces.begin();
      I != fixedObj.faces.end(); ++I) {
    ShObjMesh::Edge *e0, *e1, *e2;
    e0 = (*I)->edge;
    e1 = e0->next;
    e2 = e1->next;

    // TODO cannot handle non-triangular faces yet
    // (so make sure to triangulate the mesh)
    SH_DEBUG_ASSERT( e2->next == e0 );
    facemodeVert(e0, e1, e2, 0.0f);
    facemodeVert(e1, e0, e2, 0.0f);
    facemodeVert(e1, e0, e2, 1.0f);
    facemodeVert(e0, e1, e2, 1.0f);
  }
  glEnd();
  glEndList();
}

EdgeGooch* EdgeGooch::instance = new EdgeGooch();
<commit_msg>fixed compile bugs<commit_after>#include <iostream>
#include <fstream>
#include <sh/sh.hpp>
#include <sh/shutil.hpp>
#define GL_GLEXT_LEGACY
#include <GL/gl.h>
#include <GL/glext.h>
#undef GL_GLEXT_LEGACY
#include "Shader.hpp"
#include "Globals.hpp"
#include "config.h"
#include "ShrikeCanvas.hpp"
#include <sh/ShObjMesh.hpp>

using namespace SH;
using namespace ShUtil;

class EdgeGooch : public Shader {
public:
  EdgeGooch();
  ~EdgeGooch();

  bool init();
  void initLists();

  void bind();

  void render();
  void facemodeVert(const ShObjEdge *e0, const ShObjEdge* e1,
    const ShObjEdge *e2, float move);

  ShProgram vertex() { return vsh & edgevsh;}
  ShProgram fragment() { return fsh & edgefsh; }

  ShProgram vsh, edgevsh;
  ShProgram fsh, edgefsh;

private:
  ShObjMesh m_obj;
  const ShObjMesh *m_canvasobj;
  ShPoint3f eyePosm;
  ShAttrib1f aspect;
  GLuint displayList;
  GLuint edgeDisplayList;

  static EdgeGooch* instance;
};

EdgeGooch::EdgeGooch()
  : Shader("Sillhouetted Gooch") {
    m_canvasobj = 0;
}

EdgeGooch::~EdgeGooch() { }

void EdgeGooch::bind() { }

void EdgeGooch::render() {
  const ShrikeCanvas *canvas = ShrikeCanvas::instance();
  if( m_canvasobj != canvas->getModel()) {
    m_canvasobj = canvas->getModel();
    m_obj = *m_canvasobj;
    m_obj.consolidateVertices();
    m_obj.mergeEdges();
    m_obj.normalizeNormals();
    initLists();
  }
  aspect = canvas->m_width / (float)canvas->m_height;

  // render fill
  shBindShader(vsh);
  shBindShader(fsh);
  glCallList(displayList);

  eyePosm = Globals::mv_inverse | ShPoint3f(0, 0, 0);
  shBindShader(edgevsh);
  shBindShader(edgefsh);
  glCallList(edgeDisplayList);
}

bool EdgeGooch::init()
{
  vsh = ShKernelLib::shVsh(Globals::mv, Globals::mvp);
  vsh = vsh << shExtract("lightPos") << Globals::lightPos;
  vsh = shSwizzle("normal", "lightVec", "posh") << vsh;

  ShColor3f SH_NAMEDECL(diffuseColor, "Diffuse Color") = ShConstant3f(0.9f, 0.9f, 0.6f);
  ShColor3f SH_NAMEDECL(cool, "Cool Color") = ShConstant3f(0.2f, 0.2f, 1.0f);
  ShColor3f SH_NAMEDECL(warm, "Warm Color") = ShConstant3f(1.0f, 0.3f, 0.3f);
  ShAttrib1f SH_NAMEDECL(width, "Edge Width") = ShConstant1f(0.02f);
  width.range(0.001f, 0.5f);
  ShAttrib1f SH_NAMEDECL(poffset, "Polygon Offset") = ShConstant1f(0.0f);
  poffset.range(-1.0f, 1.0f);

  fsh = SH_BEGIN_PROGRAM("gpu:fragment") { // gooch shader
    ShInputNormal3f SH_DECL(normal);
    ShInputVector3f SH_DECL(lightVec);

    normal = normalize(normal);
    lightVec= normalize(lightVec);
    ShConstant1f half(0.5f);
    ShAttrib1f blend = mad(dot(normal, lightVec), half, half);
    ShOutputColor3f SH_DECL(result) = diffuseColor * lerp(blend, warm, cool);
  } SH_END;

  edgevsh = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInOutNormal3f SH_NAMEDECL(n0, "normal"); // MCS normal (n0)
    ShInOutTexCoord2f SH_NAMEDECL(tc0, "texcoord" );
    ShInputTexCoord2f SH_DECL(tc1);
    ShInputTexCoord2f SH_DECL(tc2);
    ShInOutAttrib1f SH_DECL(move); //

    ShInputPoint3f  SH_DECL(p1);
    ShInputNormal3f  SH_DECL(n1);

    ShInputPoint3f  SH_DECL(p2);
    ShInputNormal3f  SH_DECL(n2);

    ShInOutPosition4f SH_NAMEDECL(p0, "posm");

    ShAttrib3f nvv; // nvv(i) holds dot product of ni and view vec to pi
    nvv(0) = normalize(eyePosm - p0(0,1,2)) | n0;
    nvv(1) = normalize(eyePosm - p1) | n1;
    nvv(2) = normalize(eyePosm - p2) | n2;

    // s(i) = 1 if there is a sign change between ni, ni+1
    ShAttrib3f s = (nvv * nvv(1,2,0)) < 0.0f;

    // find interpolated points between p0,p1 and p0,p2
    ShAttrib2f k = -nvv(1,2) / (nvv(0,0) - nvv(1,2));
    ShAttrib2f nk = ShConstant2f(1.0f, 1.0f) - k;
    p1 = lerp(k(0), p0(0,1,2), p1);
    n1 = lerp(k(0), n0, n1);
    tc1 = lerp(k(0), tc0, tc1);
    p2 = lerp(k(1), p0(0,1,2), p2);
    n2 = lerp(k(1), n0, n2);
    tc2 = lerp(k(1), tc0, tc2);

    // pick which interpolated point to use based on conditions
    ShAttrib1f snone = dot(s, s) < 0.5f; // not on sillhouete
    ShAttrib1f nots2_and_s0 = (1.0f - s(2)) * s(0); //
    p0(0,1,2) = snone * p0(0,1,2) + s(2) * p2 + nots2_and_s0 * p1;
    n0 = snone * n0 + s(2) * n2 + nots2_and_s0 * n1;
    tc0 = snone * tc0 + s(2) * tc2 + nots2_and_s0 * tc1;

    move *= ( 1.0f - snone ); // only expand quad on sillhouette
  } SH_END;

  ShProgram translator = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInputPosition4f SH_DECL(posm); // in MCS

    ShInOutTexCoord2f SH_DECL(texcoord);
    ShInOutNormal3f SH_DECL(normal); // in MCS, out VCS
    ShInputAttrib1f SH_DECL(move); // whether to move
    ShOutputPosition4f SH_DECL(posh); // out DCS

    posh = Globals::mvp | posm;

    ShPoint3f offposh = posm(0,1,2) + normal;
    offposh = Globals::mvp | offposh;
    ShPoint3f nph = posh(0,1,2) / posh(3);
    ShVector2f offset = normalize(offposh - nph )(0,1) * width * move * posh(3);
    offset(1) *= aspect;

    posh(0,1) += offset;
    posh(2) += poffset;

    normal = normalize(Globals::mv | normal);
    texcoord(0) += texcoord(1);
    texcoord(1) = move;
  } SH_END;

  edgevsh = namedConnect( edgevsh, translator );

  edgefsh = SH_BEGIN_PROGRAM("gpu:fragment") {
    ShOutputColor4f SH_DECL(kd) = ShConstant4f(0.0f, 0.0f, 0.0f, 1.0f);
  } SH_END;

  return true;
}

void EdgeGooch::facemodeVert(const ShObjEdge *e0, const ShObjEdge* e1,
    const ShObjEdge *e2, float move) {
  // Note, because of current backend binding order, texcoords must go first
  float vs[4];
  vs[3] = 0;
  e0->normal.getValues(vs); glNormal3fv(vs);
  e0->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0, vs);
  e1->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0 + 1, vs);
  e2->texcoord.getValues(vs); glMultiTexCoord2fvARB(GL_TEXTURE0 + 2, vs);
  glMultiTexCoord1fARB(GL_TEXTURE0 + 3, move);
  e1->start->pos.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 4, vs);
  e1->normal.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 5, vs);
  e2->start->pos.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 6, vs);
  e2->normal.getValues(vs); glMultiTexCoord3fvARB(GL_TEXTURE0 + 7, vs);
  e0->start->pos.getValues(vs); glVertex3fv(vs);
}

void EdgeGooch::initLists() {
  float vs[4];

  displayList = glGenLists(1);
  glNewList(displayList, GL_COMPILE);
  glBegin(GL_TRIANGLES);
    for(ShObjMesh::FaceSet::iterator I = m_obj.faces.begin();
          I != m_obj.faces.end(); ++I) {
      ShObjMesh::Edge* e = (*I)->edge;
      do {
        e->normal.getValues(vs); glNormal3fv(vs);
        e->texcoord.getValues(vs); glTexCoord2fv(vs);
        e->start->pos.getValues(vs); glVertex3fv(vs);
        e = e->next;
      } while( e != (*I)->edge);
    }
  glEnd();
  glEndList();

  ShObjMesh fixedObj = m_obj;
  fixedObj.generateVertexNormals(true); // force generate a single normal per vertex
  fixedObj.normalizeNormals();

  edgeDisplayList = glGenLists(1);
  glNewList(edgeDisplayList, GL_COMPILE);

  glBegin(GL_QUADS);
  for(ShObjMesh::FaceSet::iterator I = fixedObj.faces.begin();
      I != fixedObj.faces.end(); ++I) {
    ShObjMesh::Edge *e0, *e1, *e2;
    e0 = (*I)->edge;
    e1 = e0->next;
    e2 = e1->next;

    // TODO cannot handle non-triangular faces yet
    // (so make sure to triangulate the mesh)
    SH_DEBUG_ASSERT( e2->next == e0 );
    facemodeVert(e0, e1, e2, 0.0f);
    facemodeVert(e1, e0, e2, 0.0f);
    facemodeVert(e1, e0, e2, 1.0f);
    facemodeVert(e0, e1, e2, 1.0f);
  }
  glEnd();
  glEndList();
}

EdgeGooch* EdgeGooch::instance = new EdgeGooch();
<|endoftext|>


<commit_before>#include "Exception.h"

#if defined( _MSC_VER ) && ( _MSC_VER <= 1700 )
#   define snprintf sprintf_s
#endif

namespace storm {

const char* formatExceptionMessage(
    const char *fileName, long line, const char *functionName, const char *description )
{
    const size_t size = 1024;
    static char message[ size ];

    ::snprintf( message, size, "%s(%ld): %s\n%s", fileName, line, functionName, description );

    return message;
}

}
<commit_msg>Fixed Visual C++ 12.0 support<commit_after>#include "Exception.h"

#if defined( _MSC_VER ) && ( _MSC_VER <= 1800 )
#   define snprintf sprintf_s
#endif

namespace storm {

const char* formatExceptionMessage(
    const char *fileName, long line, const char *functionName, const char *description )
{
    const size_t size = 1024;
    static char message[ size ];

    ::snprintf( message, size, "%s(%ld): %s\n%s", fileName, line, functionName, description );

    return message;
}

}
<|endoftext|>


<commit_before>// Copyright 2019 Google Inc.
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

#include "tink/cc/python_file_object_adapter.h"

#include "absl/status/status.h"
#include "pybind11/pybind11.h"
#include "tink/cc/pybind/status_casters.h"

namespace crypto {
namespace tink {

// Defining PYBIND11_OVERLOAD macros here for now.
// TODO(b/146426040): Use future macros for OSS absl::Status, StatusOr
//                    when they become available.

// Macro to populate pybind11 trampoline class virtual methods with a
// util::Status return type. Converts all exceptions derived from
// std::exception to a StatusNotOk return. std::abort is called for
// all other exceptions, to ensure exceptions don't unwind through code
// compiled with -fno-exceptions (or similar, depending on the platform).
// Modeled after PYBIND11_OVERLOAD_INT defined in pybind11/pybind11.h.
#define PYBIND11_OVERLOAD_PURE_STATUS_RETURN(cname, name, ...)            \
  try {                                                                   \
    pybind11::gil_scoped_acquire gil;                                     \
    pybind11::function overload =                                         \
        pybind11::get_overload(static_cast<const cname *>(this), name);   \
    if (!overload) {                                                      \
      return util::Status(absl::StatusCode::kUnimplemented,               \
                          "No Python overload is defined for " name "."); \
    }                                                                     \
    overload(__VA_ARGS__); /* Ignoring return value. */                   \
    return util::Status();                                                \
  } catch (const std::exception &e) {                                     \
    return util::Status(util::error::UNKNOWN, e.what());                  \
  } catch (...) {                                                         \
    std::abort();                                                         \
  }

// Macro to populate pybind11 trampoline class virtual methods with a
// util::StatusOr return type. Converts all exceptions as described for
// PYBIND11_OVERLOAD_PURE_STATUS_RETURN.
#define PYBIND11_OVERLOAD_PURE_STATUSOR_RETURN(statusor_payload_type, cname, \
                                               name, ...)                    \
  try {                                                                      \
    pybind11::gil_scoped_acquire gil;                                        \
    pybind11::function overload =                                            \
        pybind11::get_overload(static_cast<const cname *>(this), name);      \
    if (!overload) {                                                         \
      return util::Status(absl::StatusCode::kUnimplemented,                  \
                          "No Python overload is defined for " name ".");    \
    }                                                                        \
    auto o = overload(__VA_ARGS__);                                          \
    return o.cast<statusor_payload_type>();                                  \
  } catch (const std::exception &e) {                                        \
    return util::Status(util::error::UNKNOWN, e.what());                     \
  } catch (...) {                                                            \
    std::abort();                                                            \
  }

class Pybind11PythonFileObjectAdapter : public PythonFileObjectAdapter {
 public:
  // Inherit the constructors.
  using PythonFileObjectAdapter::PythonFileObjectAdapter;

  // Trampoline for each virtual member function:

  util::StatusOr<int> Write(absl::string_view data) override{
      PYBIND11_OVERLOAD_PURE_STATUSOR_RETURN(
          int, PythonFileObjectAdapter, "write",
          pybind11::bytes(std::string(data)))}

  util::Status Close() override{
      PYBIND11_OVERLOAD_PURE_STATUS_RETURN(PythonFileObjectAdapter, "close")}

  util::StatusOr<std::string> Read(int size) override {
    PYBIND11_OVERLOAD_PURE_STATUSOR_RETURN(std::string, PythonFileObjectAdapter,
                                           "read", size)
  }
};

void PybindRegisterPythonFileObjectAdapter(pybind11::module* module) {
  namespace py = pybind11;
  py::module& m = *module;

  // TODO(b/146492561): Reduce the number of complicated lambdas.
  py::class_<PythonFileObjectAdapter, Pybind11PythonFileObjectAdapter,
             std::shared_ptr<PythonFileObjectAdapter>>(
      m, "PythonFileObjectAdapter")
      .def(py::init<>())
      .def(
          "write",
          [](PythonFileObjectAdapter *self,
             const py::bytes &data) -> util::StatusOr<int> {
            return self->Write(std::string(data));  // TODO(b/145925674)
          },
          py::arg("data"))
      .def("close", &PythonFileObjectAdapter::Close)
      .def(
          "read",
          [](PythonFileObjectAdapter *self, int size)
              -> util::StatusOr<py::bytes> { return self->Read(size); },
          py::arg("size"));
}

}  // namespace tink
}  // namespace crypto
<commit_msg>Remove one more crypto::tink::util::error::UNKNOWN.<commit_after>// Copyright 2019 Google Inc.
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

#include "tink/cc/python_file_object_adapter.h"

#include "absl/status/status.h"
#include "pybind11/pybind11.h"
#include "tink/cc/pybind/status_casters.h"

namespace crypto {
namespace tink {

// Defining PYBIND11_OVERLOAD macros here for now.
// TODO(b/146426040): Use future macros for OSS absl::Status, StatusOr
//                    when they become available.

// Macro to populate pybind11 trampoline class virtual methods with a
// util::Status return type. Converts all exceptions derived from
// std::exception to a StatusNotOk return. std::abort is called for
// all other exceptions, to ensure exceptions don't unwind through code
// compiled with -fno-exceptions (or similar, depending on the platform).
// Modeled after PYBIND11_OVERLOAD_INT defined in pybind11/pybind11.h.
#define PYBIND11_OVERLOAD_PURE_STATUS_RETURN(cname, name, ...)            \
  try {                                                                   \
    pybind11::gil_scoped_acquire gil;                                     \
    pybind11::function overload =                                         \
        pybind11::get_overload(static_cast<const cname *>(this), name);   \
    if (!overload) {                                                      \
      return util::Status(absl::StatusCode::kUnimplemented,               \
                          "No Python overload is defined for " name "."); \
    }                                                                     \
    overload(__VA_ARGS__); /* Ignoring return value. */                   \
    return util::Status();                                                \
  } catch (const std::exception &e) {                                     \
    return util::Status(absl::StatusCode::kUnknown, e.what());            \
  } catch (...) {                                                         \
    std::abort();                                                         \
  }

// Macro to populate pybind11 trampoline class virtual methods with a
// util::StatusOr return type. Converts all exceptions as described for
// PYBIND11_OVERLOAD_PURE_STATUS_RETURN.
#define PYBIND11_OVERLOAD_PURE_STATUSOR_RETURN(statusor_payload_type, cname, \
                                               name, ...)                    \
  try {                                                                      \
    pybind11::gil_scoped_acquire gil;                                        \
    pybind11::function overload =                                            \
        pybind11::get_overload(static_cast<const cname *>(this), name);      \
    if (!overload) {                                                         \
      return util::Status(absl::StatusCode::kUnimplemented,                  \
                          "No Python overload is defined for " name ".");    \
    }                                                                        \
    auto o = overload(__VA_ARGS__);                                          \
    return o.cast<statusor_payload_type>();                                  \
  } catch (const std::exception &e) {                                        \
    return util::Status(absl::StatusCode::kUnknown, e.what());               \
  } catch (...) {                                                            \
    std::abort();                                                            \
  }

class Pybind11PythonFileObjectAdapter : public PythonFileObjectAdapter {
 public:
  // Inherit the constructors.
  using PythonFileObjectAdapter::PythonFileObjectAdapter;

  // Trampoline for each virtual member function:

  util::StatusOr<int> Write(absl::string_view data) override{
      PYBIND11_OVERLOAD_PURE_STATUSOR_RETURN(
          int, PythonFileObjectAdapter, "write",
          pybind11::bytes(std::string(data)))}

  util::Status Close() override{
      PYBIND11_OVERLOAD_PURE_STATUS_RETURN(PythonFileObjectAdapter, "close")}

  util::StatusOr<std::string> Read(int size) override {
    PYBIND11_OVERLOAD_PURE_STATUSOR_RETURN(std::string, PythonFileObjectAdapter,
                                           "read", size)
  }
};

void PybindRegisterPythonFileObjectAdapter(pybind11::module* module) {
  namespace py = pybind11;
  py::module& m = *module;

  // TODO(b/146492561): Reduce the number of complicated lambdas.
  py::class_<PythonFileObjectAdapter, Pybind11PythonFileObjectAdapter,
             std::shared_ptr<PythonFileObjectAdapter>>(
      m, "PythonFileObjectAdapter")
      .def(py::init<>())
      .def(
          "write",
          [](PythonFileObjectAdapter *self,
             const py::bytes &data) -> util::StatusOr<int> {
            return self->Write(std::string(data));  // TODO(b/145925674)
          },
          py::arg("data"))
      .def("close", &PythonFileObjectAdapter::Close)
      .def(
          "read",
          [](PythonFileObjectAdapter *self, int size)
              -> util::StatusOr<py::bytes> { return self->Read(size); },
          py::arg("size"));
}

}  // namespace tink
}  // namespace crypto
<|endoftext|>


<commit_before>#include <ios>
#include <boost/filesystem/fstream.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/algorithm/string.hpp>
#include <cstdio>
#include <zlib.h>
#include "FileSystem.hpp"
#include "Util.hpp"

namespace Sit {
namespace FileSystem {

const boost::filesystem::path SIT_ROOT(".sit/");
const boost::filesystem::path OBJECTS_DIR(".sit/objects");

boost::filesystem::path REPO_ROOT("./");

char buf[256*1024];

#ifdef ENABLE_COMPRESSION
void CompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	boost::filesystem::create_directories(dst.parent_path());
	FILE *in = fopen(src.string().c_str(), "rb");
	gzFile out = gzopen(dst.string().c_str(), "wb");
	if (!in) {
		throw Sit::Util::SitException(std::string("compress fail, input: ") + src.string());
	}
	if (!out) {
		throw Sit::Util::SitException(std::string("compress fail, output: ") + dst.string());
	}

	int num_read = 0;
	while ((num_read = fread(buf, 1, sizeof(buf), in)) > 0) {
		gzwrite(out, buf, num_read);
	}

	fclose(in);
	gzclose(out);
}

void CompressWrite(const boost::filesystem::path &dst, const std::string &content)
{
	boost::filesystem::create_directories(dst.parent_path());
	gzFile out = gzopen(dst.string().c_str(), "wb");
	if (!out) {
		throw Sit::Util::SitException(std::string("compress fail, output: ") + dst.string());
	}

	gzwrite(out, content.c_str(), content.size());

	gzclose(out);
}

void DecompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	boost::filesystem::create_directories(dst.parent_path());
	gzFile in = gzopen(src.string().c_str(), "rb");
	FILE *out = fopen(dst.string().c_str(), "wb");
	if (!in) {
		throw Sit::Util::SitException(std::string("decompress fail, input: ") + src.string());
	}
	if (!out) {
		throw Sit::Util::SitException(std::string("decompress fail, output: ") + dst.string());
	}

	int num_read = 0;
	while ((num_read = gzread(in, buf, sizeof(buf))) > 0) {
		fwrite(buf, 1, num_read, out);
	}

	gzclose(in);
	fclose(out);
}

std::string DecompressRead(const boost::filesystem::path &src)
{
	std::string res;
	gzFile in = gzopen(src.string().c_str(), "rb");
	if (!in) {
		throw Sit::Util::SitException(std::string("decompress fail, input: ") + src.string());
	}

	int num_read = 0;
	while ((num_read = gzread(in, buf, sizeof(buf))) > 0) {
		res.append(buf, num_read);
	}

	gzclose(in);
	return res;
}

#else

void CompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	using namespace boost::filesystem;
	copy_file(src, dst, copy_option::overwrite_if_exists);
}

void CompressWrite(const boost::filesystem::path &dst, const std::string &content)
{
	Write(dst, content);
}

void DecompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	using namespace boost::filesystem;
	copy_file(src, dst, copy_option::overwrite_if_exists);
}

std::string DecompressRead(const boost::filesystem::path &src)
{
	return Read(src);
}
#endif

bool InRepo()
{
	return boost::filesystem::is_directory(REPO_ROOT / SIT_ROOT);
}

void AssertInRepo()
{
	if (!InRepo()) {
		throw Sit::Util::SitException("Fatal: Not in a sit repo.");
	}
}

std::vector<boost::filesystem::path> ListRecursive(const boost::filesystem::path &path, const bool ignoreSit, const bool relativeToRepoRoot)
{
	using namespace boost::filesystem;
	std::vector<boost::filesystem::path> ls;

	auto insert_list = [&] (const boost::filesystem::path & path) {
		const boost::filesystem::path p(GetRelativePath(path));
		const std::string s(p.generic_string());
		if (relativeToRepoRoot) {
			ls.push_back(p);
		} else {
			ls.push_back(path);
		}
	};

	try {
		if (IsDirectory(path)) {
			for(directory_iterator iter(path), end; iter != end ; ++iter) {
				const auto fn = iter->path().filename();
				if (fn == ".sit" && ignoreSit) continue;

				if (is_regular_file(iter->status())) {
					insert_list(iter->path());
					continue;
				}

				for (recursive_directory_iterator iter(path / fn), end; iter != end; ++iter) {
					insert_list(iter->path());
				}
			}
		} else {
			ls.push_back(GetRelativePath(path));
		}
	} catch (const filesystem_error &fe) {
		throw Sit::Util::SitException(std::string("Fatal: Cannot list path: ") + path.string(), fe.what());
	}
	return ls;
}

bool IsExist(const boost::filesystem::path &path)
{
	return boost::filesystem::exists(path);
}

bool IsFile(const boost::filesystem::path &path)
{
	return boost::filesystem::is_regular_file(path);
}

bool IsDirectory(const boost::filesystem::path &path)
{
	return boost::filesystem::is_directory(path);
}

void Write(const boost::filesystem::path &path, const std::string &content)
{
	using namespace boost::filesystem;
	create_directories(path.parent_path());
	ofstream file(path, std::ios::out | std::ios::binary);
	file.write(content.c_str(), content.length());
	if (!file) {
		throw Util::SitException(std::string("Cannot write to path: ") + path.string());
	}
}

std::string Read(const boost::filesystem::path &path)
{
	using namespace boost::iostreams;
	auto fileSize = boost::filesystem::file_size(path);
	if (fileSize > 512 << 20) {
		throw Sit::Util::SitException("Fatal: try to read a file larger than 512MB.");
	}
	char *s = new char[fileSize];
	boost::filesystem::ifstream file(path, std::ios::in | std::ios::binary);
	file.read(s, fileSize);
	std::string str(s, s + fileSize);
	delete[] s;
	return str;
}

void Remove(const boost::filesystem::path& path)
{
	boost::filesystem::remove(path);
}

boost::filesystem::path GetRelativePath(
	const boost::filesystem::path &file,
	const boost::filesystem::path &base)
{
	using namespace boost::filesystem;

	std::string strFullPath = absolute(file).generic_string();
	std::vector<std::string> tmpDirsFile, finalDirsFile;
	boost::split(tmpDirsFile, strFullPath, boost::is_any_of("/"));
	for (const auto &element : tmpDirsFile) {
		if (element == "" || element == ".") {
			continue;
		}
		if (element == "..") {
			if (finalDirsFile.empty()) {
				throw Sit::Util::SitException("Fatal: This is a illegal relative path.");
			} else {
				finalDirsFile.pop_back();
			}
			continue;
		}
		finalDirsFile.push_back(element);
	}

	std::string strFullPathBase = absolute(base).generic_string();
	std::vector<std::string> tmpDirsBase, finalDirsBase;
	boost::split(tmpDirsBase, strFullPathBase, boost::is_any_of("/"));
	for (const auto &element : tmpDirsBase) {
		if (element == "" || element == ".") {
			continue;
		}
		if (element == "..") {
			if (finalDirsBase.empty()) {
				throw Sit::Util::SitException("Fatal: This is a illegal relative path.");
			} else {
				finalDirsBase.pop_back();
			}
			continue;
		}
		finalDirsBase.push_back(element);
	}

	unsigned sameItemCount = 0;

	for (unsigned i = 0; i < std::min(finalDirsBase.size(), finalDirsFile.size()); ++i) {
		if (finalDirsBase[i] == finalDirsFile[i]) {
			++sameItemCount;
		} else {
			break;
		}
	}

	path resultPath("");
	for (unsigned i = sameItemCount; i < finalDirsBase.size(); ++i) {
		if (resultPath.empty()) {
			resultPath = "..";
		} else {
			resultPath = resultPath / "..";
		}
	}
	for (unsigned i = sameItemCount; i < finalDirsFile.size(); ++i) {
		if (resultPath.empty()) {
			resultPath = finalDirsFile[i];
		} else {
			resultPath = resultPath / finalDirsFile[i];
		}
	}

	return resultPath;
}

std::string FileSHA1(const boost::filesystem::path &path)
{
	boost::uuids::detail::sha1 shaEngine;
	boost::filesystem::ifstream file(path, std::ios::in | std::ios::binary);
	unsigned shaValue[5] = {0};
	while (file.good()) {
		file.read(buf, sizeof(buf));
		shaEngine.process_bytes(buf, file.gcount());
	}
	if (!file.eof()) {
		throw Util::SitException(std::string("SHA1sum fail: ") + path.string());
	}
	shaEngine.get_digest(shaValue);
	std::ostringstream result;
	for (auto &x : shaValue) {
		result << std::setw(8) << std::setfill('0') << std::hex << x;
	}
	return result.str();
}

}
}
<commit_msg>bugfix<commit_after>#include <ios>
#include <boost/filesystem/fstream.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/algorithm/string.hpp>
#include <cstdio>
#include <zlib.h>
#include "FileSystem.hpp"
#include "Util.hpp"

namespace Sit {
namespace FileSystem {

const boost::filesystem::path SIT_ROOT(".sit/");
const boost::filesystem::path OBJECTS_DIR(".sit/objects");

boost::filesystem::path REPO_ROOT("./");

char buf[256*1024];

#ifdef ENABLE_COMPRESSION
void CompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	boost::filesystem::create_directories(dst.parent_path());
	FILE *in = fopen(src.string().c_str(), "rb");
	gzFile out = gzopen(dst.string().c_str(), "wb");
	if (!in) {
		throw Sit::Util::SitException(std::string("compress fail, input: ") + src.string());
	}
	if (!out) {
		throw Sit::Util::SitException(std::string("compress fail, output: ") + dst.string());
	}

	int num_read = 0;
	while ((num_read = fread(buf, 1, sizeof(buf), in)) > 0) {
		gzwrite(out, buf, num_read);
	}

	fclose(in);
	gzclose(out);
}

void CompressWrite(const boost::filesystem::path &dst, const std::string &content)
{
	boost::filesystem::create_directories(dst.parent_path());
	gzFile out = gzopen(dst.string().c_str(), "wb");
	if (!out) {
		throw Sit::Util::SitException(std::string("compress fail, output: ") + dst.string());
	}

	gzwrite(out, content.c_str(), content.size());

	gzclose(out);
}

void DecompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	boost::filesystem::create_directories(dst.parent_path());
	gzFile in = gzopen(src.string().c_str(), "rb");
	FILE *out = fopen(dst.string().c_str(), "wb");
	if (!in) {
		throw Sit::Util::SitException(std::string("decompress fail, input: ") + src.string());
	}
	if (!out) {
		throw Sit::Util::SitException(std::string("decompress fail, output: ") + dst.string());
	}

	int num_read = 0;
	while ((num_read = gzread(in, buf, sizeof(buf))) > 0) {
		fwrite(buf, 1, num_read, out);
	}

	gzclose(in);
	fclose(out);
}

std::string DecompressRead(const boost::filesystem::path &src)
{
	std::string res;
	gzFile in = gzopen(src.string().c_str(), "rb");
	if (!in) {
		throw Sit::Util::SitException(std::string("decompress fail, input: ") + src.string());
	}

	int num_read = 0;
	while ((num_read = gzread(in, buf, sizeof(buf))) > 0) {
		res.append(buf, num_read);
	}

	gzclose(in);
	return res;
}

#else

void CompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	using namespace boost::filesystem;
	create_directories(dst.parent_path());
	copy_file(src, dst, copy_option::overwrite_if_exists);
}

void CompressWrite(const boost::filesystem::path &dst, const std::string &content)
{
	using namespace boost::filesystem;
	create_directories(dst.parent_path());
	Write(dst, content);
}

void DecompressCopy(const boost::filesystem::path &src, const boost::filesystem::path &dst)
{
	using namespace boost::filesystem;
	create_directories(dst.parent_path());
	copy_file(src, dst, copy_option::overwrite_if_exists);
}

std::string DecompressRead(const boost::filesystem::path &src)
{
	return Read(src);
}
#endif

bool InRepo()
{
	return boost::filesystem::is_directory(REPO_ROOT / SIT_ROOT);
}

void AssertInRepo()
{
	if (!InRepo()) {
		throw Sit::Util::SitException("Fatal: Not in a sit repo.");
	}
}

std::vector<boost::filesystem::path> ListRecursive(const boost::filesystem::path &path, const bool ignoreSit, const bool relativeToRepoRoot)
{
	using namespace boost::filesystem;
	std::vector<boost::filesystem::path> ls;

	auto insert_list = [&] (const boost::filesystem::path & path) {
		const boost::filesystem::path p(GetRelativePath(path));
		const std::string s(p.generic_string());
		if (relativeToRepoRoot) {
			ls.push_back(p);
		} else {
			ls.push_back(path);
		}
	};

	try {
		if (IsDirectory(path)) {
			for(directory_iterator iter(path), end; iter != end ; ++iter) {
				const auto fn = iter->path().filename();
				if (fn == ".sit" && ignoreSit) continue;

				if (is_regular_file(iter->status())) {
					insert_list(iter->path());
					continue;
				}

				for (recursive_directory_iterator iter(path / fn), end; iter != end; ++iter) {
					insert_list(iter->path());
				}
			}
		} else {
			ls.push_back(GetRelativePath(path));
		}
	} catch (const filesystem_error &fe) {
		throw Sit::Util::SitException(std::string("Fatal: Cannot list path: ") + path.string(), fe.what());
	}
	return ls;
}

bool IsExist(const boost::filesystem::path &path)
{
	return boost::filesystem::exists(path);
}

bool IsFile(const boost::filesystem::path &path)
{
	return boost::filesystem::is_regular_file(path);
}

bool IsDirectory(const boost::filesystem::path &path)
{
	return boost::filesystem::is_directory(path);
}

void Write(const boost::filesystem::path &path, const std::string &content)
{
	using namespace boost::filesystem;
	create_directories(path.parent_path());
	ofstream file(path, std::ios::out | std::ios::binary);
	file.write(content.c_str(), content.length());
	if (!file) {
		throw Util::SitException(std::string("Cannot write to path: ") + path.string());
	}
}

std::string Read(const boost::filesystem::path &path)
{
	using namespace boost::iostreams;
	auto fileSize = boost::filesystem::file_size(path);
	if (fileSize > 512 << 20) {
		throw Sit::Util::SitException("Fatal: try to read a file larger than 512MB.");
	}
	char *s = new char[fileSize];
	boost::filesystem::ifstream file(path, std::ios::in | std::ios::binary);
	file.read(s, fileSize);
	std::string str(s, s + fileSize);
	delete[] s;
	return str;
}

void Remove(const boost::filesystem::path& path)
{
	boost::filesystem::remove(path);
}

boost::filesystem::path GetRelativePath(
	const boost::filesystem::path &file,
	const boost::filesystem::path &base)
{
	using namespace boost::filesystem;

	std::string strFullPath = absolute(file).generic_string();
	std::vector<std::string> tmpDirsFile, finalDirsFile;
	boost::split(tmpDirsFile, strFullPath, boost::is_any_of("/"));
	for (const auto &element : tmpDirsFile) {
		if (element == "" || element == ".") {
			continue;
		}
		if (element == "..") {
			if (finalDirsFile.empty()) {
				throw Sit::Util::SitException("Fatal: This is a illegal relative path.");
			} else {
				finalDirsFile.pop_back();
			}
			continue;
		}
		finalDirsFile.push_back(element);
	}

	std::string strFullPathBase = absolute(base).generic_string();
	std::vector<std::string> tmpDirsBase, finalDirsBase;
	boost::split(tmpDirsBase, strFullPathBase, boost::is_any_of("/"));
	for (const auto &element : tmpDirsBase) {
		if (element == "" || element == ".") {
			continue;
		}
		if (element == "..") {
			if (finalDirsBase.empty()) {
				throw Sit::Util::SitException("Fatal: This is a illegal relative path.");
			} else {
				finalDirsBase.pop_back();
			}
			continue;
		}
		finalDirsBase.push_back(element);
	}

	unsigned sameItemCount = 0;

	for (unsigned i = 0; i < std::min(finalDirsBase.size(), finalDirsFile.size()); ++i) {
		if (finalDirsBase[i] == finalDirsFile[i]) {
			++sameItemCount;
		} else {
			break;
		}
	}

	path resultPath("");
	for (unsigned i = sameItemCount; i < finalDirsBase.size(); ++i) {
		if (resultPath.empty()) {
			resultPath = "..";
		} else {
			resultPath = resultPath / "..";
		}
	}
	for (unsigned i = sameItemCount; i < finalDirsFile.size(); ++i) {
		if (resultPath.empty()) {
			resultPath = finalDirsFile[i];
		} else {
			resultPath = resultPath / finalDirsFile[i];
		}
	}

	return resultPath;
}

std::string FileSHA1(const boost::filesystem::path &path)
{
	boost::uuids::detail::sha1 shaEngine;
	boost::filesystem::ifstream file(path, std::ios::in | std::ios::binary);
	unsigned shaValue[5] = {0};
	while (file.good()) {
		file.read(buf, sizeof(buf));
		shaEngine.process_bytes(buf, file.gcount());
	}
	if (!file.eof()) {
		throw Util::SitException(std::string("SHA1sum fail: ") + path.string());
	}
	shaEngine.get_digest(shaValue);
	std::ostringstream result;
	for (auto &x : shaValue) {
		result << std::setw(8) << std::setfill('0') << std::hex << x;
	}
	return result.str();
}

}
}
<|endoftext|>