<commit_before>//! Copyright (c) 2013 ASMlover. All rights reserved.
//!
//! Redistribution and use in source and binary forms, with or without
//! modification, are permitted provided that the following conditions
//! are met:
//!
//!  * Redistributions of source code must retain the above copyright
//!    notice, this list ofconditions and the following disclaimer.
//!
//!  * Redistributions in binary form must reproduce the above copyright
//!    notice, this list of conditions and the following disclaimer in
//!    the documentation and/or other materialsprovided with the
//!    distribution.
//!
//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//! COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//! POSSIBILITY OF SUCH DAMAGE.
#include "select_ev.h"
#include "thread.h"
#include "address.h"
#include "socket.h"
#include "connector_mgr.h"
#include "listener.h"

Listener::Listener(void)
  : running_(false)
  , listener_(NULL)
  , thread_(NULL)
  , conn_mgr_(NULL)
{
}

Listener::~Listener(void)
{
}

bool
Listener::Start(const char* ip, unsigned short port)
{
  listener_ = new Socket();
  if (NULL == listener_)
    return false;
  listener_->Open();
  listener_->SetReuseAddr();
  listener_->Bind(ip, port);
  listener_->Listen();

  do {
    conn_mgr_ = new ConnectorMgr();
    if (NULL == conn_mgr_)
      break;

    thread_ = new Thread();
    if (NULL == thread_)
      break;

    running_ = true;
    thread_->Start(&Listener::Routine, this);

    return true;
  } while (0);

  Stop();
  return false;
}

void
Listener::Stop(void)
{
  running_ = false;

  if (NULL != thread_) {
    thread_->Stop();

    delete thread_;
    thread_ = NULL;
  }
  if (NULL != conn_mgr_) {
    conn_mgr_->CloseAll();

    delete conn_mgr_;
    conn_mgr_ = NULL;
  }
  if (NULL != listener_) {
    listener_->Close();

    delete listener_;
    listener_ = NULL;
  }
}

void
Listener::Routine(void* argument)
{
  Listener* self = static_cast<Listener*>(argument);
  if (NULL == self)
    return;

  Socket s;
  Address addr;
  while (self->running_) {
    if (!self->listener_->Accept(&s, &addr)) {
      Sleep(1);
      continue;
    }
    else {
    }
  }
}
<commit_msg>improved listener module<commit_after>//! Copyright (c) 2013 ASMlover. All rights reserved.
//!
//! Redistribution and use in source and binary forms, with or without
//! modification, are permitted provided that the following conditions
//! are met:
//!
//!  * Redistributions of source code must retain the above copyright
//!    notice, this list ofconditions and the following disclaimer.
//!
//!  * Redistributions in binary form must reproduce the above copyright
//!    notice, this list of conditions and the following disclaimer in
//!    the documentation and/or other materialsprovided with the
//!    distribution.
//!
//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//! COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//! POSSIBILITY OF SUCH DAMAGE.
#include "select_ev.h"
#include "thread.h"
#include "address.h"
#include "socket.h"
#include "connector_mgr.h"
#include "listener.h"

Listener::Listener(void)
  : running_(false)
  , listener_(NULL)
  , thread_(NULL)
  , conn_mgr_(NULL)
{
}

Listener::~Listener(void)
{
}

bool
Listener::Start(const char* ip, unsigned short port)
{
  listener_ = new Socket();
  if (NULL == listener_)
    return false;
  listener_->Open();
  listener_->SetReuseAddr();
  listener_->Bind(ip, port);
  listener_->Listen();

  do {
    conn_mgr_ = new ConnectorMgr();
    if (NULL == conn_mgr_)
      break;

    thread_ = new Thread();
    if (NULL == thread_)
      break;

    running_ = true;
    thread_->Start(&Listener::Routine, this);

    return true;
  } while (0);

  Stop();
  return false;
}

void
Listener::Stop(void)
{
  running_ = false;

  if (NULL != thread_) {
    thread_->Stop();

    delete thread_;
    thread_ = NULL;
  }
  if (NULL != conn_mgr_) {
    conn_mgr_->CloseAll();

    delete conn_mgr_;
    conn_mgr_ = NULL;
  }
  if (NULL != listener_) {
    listener_->Close();

    delete listener_;
    listener_ = NULL;
  }
}

void
Listener::Routine(void* argument)
{
  Listener* self = static_cast<Listener*>(argument);
  if (NULL == self || NULL == self->conn_mgr_)
    return;

  Socket s;
  Address addr;
  while (self->running_) {
    if (!self->listener_->Accept(&s, &addr)) {
      Sleep(1);
      continue;
    }
    else {
      int fd = s.fd();
      int worker_id = self->conn_mgr_->SuitableWorker();
      Connector* conn = self->conn_mgr_->Insert(fd, worker_id);
      if (NULL != conn) {
        //! TODO:
        //! add event
      }
      else {
        s.Close();
      }

      s.Detach();
      addr.Detach();
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2015, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "AlsaCtlPortConfig.hpp"
#include "MappingContext.h"
#include "AlsaMappingKeys.hpp"
#include <string.h>
#include <string>
#include <assert.h>
#include <sstream>
#include <limits>

using std::string;

#define base AlsaSubsystemObject

AlsaCtlPortConfig::AlsaCtlPortConfig(const string &mappingValue,
                                     CInstanceConfigurableElement *instanceConfigurableElement,
                                     const CMappingContext &context,
                                     core::log::Logger& logger,
                                     const PortConfig &defaultPortConfig)
    : base(mappingValue,
           instanceConfigurableElement,
           context,
           logger),
      _device(context.getItemAsInteger(AlsaCtlDevice)),
      _portConfig(defaultPortConfig)
{

}

bool AlsaCtlPortConfig::receiveFromHW(string &error)
{
    blackboardWrite(&_portConfig, sizeof(_portConfig));

    return true;
}

bool AlsaCtlPortConfig::sendToHW(string &error)
{
    PortConfig portConfig;
    blackboardRead(&portConfig, sizeof(portConfig));

    // If device update is needed, close all the stream
    if (isDeviceUpdateNeeded(portConfig)) {

        // Close playback and capture
        closeStream(Playback);
        closeStream(Capture);

        // Save new configuration
        _portConfig.channelNumber = portConfig.channelNumber;
        _portConfig.format = portConfig.format;
        _portConfig.sampleRate = portConfig.sampleRate;

    } else {

        // Close playback/capture streams if asked for
        if (!portConfig.isStreamEnabled[Playback]) {

            closeStream(Playback);
        }
        if (!portConfig.isStreamEnabled[Capture]) {

            closeStream(Capture);
        }
    }

    // Open and configure required streams
    if (portConfig.isStreamEnabled[Playback]) {

        if (!openStream(Playback, error)) {

            return false;
        }
    }
    if (portConfig.isStreamEnabled[Capture]) {

        if (!openStream(Capture, error)) {

            return false;
        }
    }

    // Check port configuration has been considered
    assert(!memcmp(&_portConfig, &portConfig, sizeof(_portConfig)));

    return true;
}

bool AlsaCtlPortConfig::openStream(StreamDirection streamDirection, string &error)
{
    // Stream needs to be opened only if it was closed previously
    if (!_portConfig.isStreamEnabled[streamDirection]) {

        if (!doOpenStream(streamDirection, error)) {

            return false;
        }

        // Stream has to be opened
        _portConfig.isStreamEnabled[streamDirection] = true;
    }

    return true;
}

void AlsaCtlPortConfig::closeStream(StreamDirection streamDirection)
{
    // Stream needs to be closed only if it was opened previously
    if (_portConfig.isStreamEnabled[streamDirection]) {

        // Stream has to be closed
        doCloseStream(streamDirection);

        _portConfig.isStreamEnabled[streamDirection] = false;
    }
}

string AlsaCtlPortConfig::formatAlsaError(StreamDirection streamDirection,
                                          const string &functionName,
                                          const string &error)
{
    std::ostringstream stringStream;

    stringStream << (streamDirection ? "Capture" : "Playback") << " " <<
        functionName << " error: " << error;

    return stringStream.str();
}

bool AlsaCtlPortConfig::isStreamEnabled(StreamDirection streamDirection) const
{
    return _portConfig.isStreamEnabled[streamDirection] != 0;

}

bool AlsaCtlPortConfig::isDeviceUpdateNeeded(const PortConfig &portConfig) const
{
    return (_portConfig.channelNumber != portConfig.channelNumber) ||
           (_portConfig.format != portConfig.format) ||
           (_portConfig.sampleRate != portConfig.sampleRate);
}
<commit_msg>Fix compilation error when using CLANG<commit_after>/*
 * Copyright (c) 2011-2015, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "AlsaCtlPortConfig.hpp"
#include "MappingContext.h"
#include "AlsaMappingKeys.hpp"
#include <string.h>
#include <string>
#include <assert.h>
#include <sstream>
#include <limits>

using std::string;

#define base AlsaSubsystemObject

AlsaCtlPortConfig::AlsaCtlPortConfig(const string &mappingValue,
                                     CInstanceConfigurableElement *instanceConfigurableElement,
                                     const CMappingContext &context,
                                     core::log::Logger& logger,
                                     const PortConfig &defaultPortConfig)
    : base(mappingValue,
           instanceConfigurableElement,
           context,
           logger),
      _device(context.getItemAsInteger(AlsaCtlDevice)),
      _portConfig(defaultPortConfig)
{

}

bool AlsaCtlPortConfig::receiveFromHW(string &/*error*/)
{
    blackboardWrite(&_portConfig, sizeof(_portConfig));

    return true;
}

bool AlsaCtlPortConfig::sendToHW(string &error)
{
    PortConfig portConfig;
    blackboardRead(&portConfig, sizeof(portConfig));

    // If device update is needed, close all the stream
    if (isDeviceUpdateNeeded(portConfig)) {

        // Close playback and capture
        closeStream(Playback);
        closeStream(Capture);

        // Save new configuration
        _portConfig.channelNumber = portConfig.channelNumber;
        _portConfig.format = portConfig.format;
        _portConfig.sampleRate = portConfig.sampleRate;

    } else {

        // Close playback/capture streams if asked for
        if (!portConfig.isStreamEnabled[Playback]) {

            closeStream(Playback);
        }
        if (!portConfig.isStreamEnabled[Capture]) {

            closeStream(Capture);
        }
    }

    // Open and configure required streams
    if (portConfig.isStreamEnabled[Playback]) {

        if (!openStream(Playback, error)) {

            return false;
        }
    }
    if (portConfig.isStreamEnabled[Capture]) {

        if (!openStream(Capture, error)) {

            return false;
        }
    }

    // Check port configuration has been considered
    assert(!memcmp(&_portConfig, &portConfig, sizeof(_portConfig)));

    return true;
}

bool AlsaCtlPortConfig::openStream(StreamDirection streamDirection, string &error)
{
    // Stream needs to be opened only if it was closed previously
    if (!_portConfig.isStreamEnabled[streamDirection]) {

        if (!doOpenStream(streamDirection, error)) {

            return false;
        }

        // Stream has to be opened
        _portConfig.isStreamEnabled[streamDirection] = true;
    }

    return true;
}

void AlsaCtlPortConfig::closeStream(StreamDirection streamDirection)
{
    // Stream needs to be closed only if it was opened previously
    if (_portConfig.isStreamEnabled[streamDirection]) {

        // Stream has to be closed
        doCloseStream(streamDirection);

        _portConfig.isStreamEnabled[streamDirection] = false;
    }
}

string AlsaCtlPortConfig::formatAlsaError(StreamDirection streamDirection,
                                          const string &functionName,
                                          const string &error)
{
    std::ostringstream stringStream;

    stringStream << (streamDirection ? "Capture" : "Playback") << " " <<
        functionName << " error: " << error;

    return stringStream.str();
}

bool AlsaCtlPortConfig::isStreamEnabled(StreamDirection streamDirection) const
{
    return _portConfig.isStreamEnabled[streamDirection] != 0;

}

bool AlsaCtlPortConfig::isDeviceUpdateNeeded(const PortConfig &portConfig) const
{
    return (_portConfig.channelNumber != portConfig.channelNumber) ||
           (_portConfig.format != portConfig.format) ||
           (_portConfig.sampleRate != portConfig.sampleRate);
}
<|endoftext|>


<commit_before>#pragma once
#include "singleton.hpp"
#include "restag.hpp"
#include "optional.hpp"
#include <unordered_map>

namespace spi {
	//! 名前付きリソースマネージャ
	/*!
		中身の保持はすべてスマートポインタで行う
		シングルスレッド動作
	*/
	template <
		class T,
		class K = std::string,
		class Allocator = std::allocator<T>
	>
	class ResMgrName {
		public:
			using value_t = T;
			using shared_t = std::shared_ptr<value_t>;
			using key_t = K;
		private:
			using this_t = ResMgrName<T,K>;
			using tag_t = ResTag<value_t>;
			using Map = std::unordered_map<key_t, tag_t>;
			using Val2Key = std::unordered_map<const value_t*, key_t>;
			struct Resource {
				Map			map;
				Val2Key		v2k;
			};
			using Resource_SP = std::shared_ptr<Resource>;

			template <bool B>
			class _iterator : public Map::iterator {
				public:
					using base_t = typename Map::iterator;
					using base_t::base_t;
					using v_t = std::conditional_t<B, const value_t, value_t>;
					using s_t = std::shared_ptr<v_t>;
					s_t operator *() const {
						return base_t::operator*().second.weak.lock();
					}
					s_t operator ->() const {
						return base_t::operator*().second.weak.lock();
					}
			};
			using iterator = _iterator<false>;
			using const_iterator = _iterator<true>;

			Resource_SP		_resource;

			template <class T2>
			static void _Release(const Resource_SP& r, T2* p) noexcept {
				auto& m = r->map;
				auto& v2k = r->v2k;
				typename Allocator::template rebind<T2>::other alc;
				const auto itr = v2k.find(p);
				try {
					Assert0(itr != v2k.end());
					const auto itr2 = m.find(itr->second);
					Assert0(itr2 != m.end());
					v2k.erase(itr);
					m.erase(itr2);
					alc.destroy(p);
					alc.deallocate(p, 1);
				} catch(...) {}
			}
			const static key_t s_anonymousPrefix;
			// 無名リソースを作成する際の通し番号
			uint64_t _acounter;
			key_t _makeAKey() {
				key_t key(s_anonymousPrefix);
				key += std::to_string(_acounter++);
				return key;
			}

			template <class Ar, class T2, class K2>
			friend void save(Ar&, const ResMgrName<T2,K2>&);
			template <class Ar, class T2, class K2>
			friend void load(Ar&, ResMgrName<T2,K2>&);

			static bool _HasAnonymousPrefix(const key_t& s) {
				if(s.length() >= s_anonymousPrefix.length()) {
					auto itr0 = s.cbegin(),
						 itr1 = s_anonymousPrefix.cbegin();
					do {
						if(*itr0 != *itr1)
							return false;
						++itr0;
						++itr1;
					} while(itr1 != s_anonymousPrefix.cend());
					return true;
				}
				return false;
			}
			template <class T2>
			auto _getDeleter() {
				return [r=_resource](T2 *const p){ _Release(r, p); };
			}
		protected:
			//! 継承先のクラスでキーの改変をする必要があればこれをオーバーライドする
			virtual void _modifyResourceName(key_t& /*key*/) const {}

		public:
			ResMgrName():
				_resource(std::make_shared<Resource>()),
				_acounter(0)
			{}
			iterator begin() noexcept { return _resource->map.begin(); }
			iterator end() noexcept { return _resource->map.end(); }
			const_iterator begin() const noexcept { return _resource->map.begin(); }
			const_iterator cbegin() const noexcept { return _resource->map.cbegin(); }
			const_iterator end() const noexcept { return _resource->map.end(); }
			const_iterator cend() const noexcept { return _resource->map.cend(); }

			// (主にデバッグ用)
			bool operator == (const ResMgrName& m) const noexcept {
				auto	&m0 = _resource->map,
						&m1 = m._resource->map;
				if(m0.size() == m1.size()) {
					auto itr0 = m0.begin(),
						 itr1 = m1.begin();
					while(itr0 != m0.end()) {
						if(*itr0 != *itr1)
							return false;
						++itr0;
						++itr1;
					}
					return _acounter == m._acounter;
				}
				return false;
			}
			bool operator != (const ResMgrName& m) const noexcept {
				return !(this->operator == (m));
			}
			template <class T2>
			struct Constructor {
				using Alc = typename Allocator::template rebind<T2>::other;
				Alc			alc;
				T2*			pointer;
				Constructor():
					pointer(alc.allocate(1))
				{}
				template <class... Ts>
				void operator()(Ts&&... ts) {
					alc.construct(pointer, std::forward<Ts>(ts)...);
				}
			};

			// ---- 名前付きリソース作成 ----
			template <class T2, class Make>
			auto acquireWithMake(const key_t& k, Make&& make) {
				key_t tk(k);
				if(!_HasAnonymousPrefix(tk))
					_modifyResourceName(tk);
				// 既に同じ名前でリソースを確保済みならばそれを返す
				if(auto ret = get(tk))
					return std::make_pair(std::static_pointer_cast<T2>(ret), false);
				// 新しくリソースを作成
				Constructor<T2> ctor;
				make(tk, ctor);

				auto& res = *_resource;
				std::shared_ptr<T2> sp(
					ctor.pointer,
					_getDeleter<T2>()
				);
				res.map.emplace(tk, sp);
				res.v2k.emplace(sp.get(), tk);
				return std::make_pair(sp, true);
			}
			template <class T2, class... Ts>
			auto emplaceWithType(const key_t& k, Ts&&... ts) {
				return acquireWithMake<T2>(k, [&ts...](auto& /*key*/, auto&& mk){
					mk(std::forward<Ts>(ts)...);
				});
			}
			template <class... Ts>
			auto emplace(const key_t& k, Ts&&... ts) {
				return emplaceWithType<value_t>(k, std::forward<Ts>(ts)...);
			}

			// ---- 無名リソース作成 ----
			template <class P, class... Ts>
			auto acquireA(Ts&&... ts) {
				for(;;) {
					// 適当にリソース名を生成して、ダブりがなければOK
					const auto key = _makeAKey();
					auto ret = acquireWithMake<P>(key,
								[&ts...](auto& /*key*/, auto&& mk){
									mk(std::forward<Ts>(ts)...);
								});
					if(ret.second)
						return ret.first;
				}
			}
			template <class T2, class... Ts>
			auto emplaceA_WithType(Ts&&... ts) {
				return acquireA<T2>(std::forward<Ts>(ts)...);
			}
			template <class... Ts>
			auto emplaceA(Ts&&... ts) {
				return emplaceA_WithType<value_t>(std::forward<Ts>(ts)...);
			}

			Optional<const key_t&> getKey(const shared_t& p) const {
				return getKey(p.get());
			}
			Optional<const key_t&> getKey(const value_t* p) const {
				auto& v2k = _resource->v2k;
				const auto itr = v2k.find(p);
				if(itr != v2k.end())
					return itr->second;
				return none;
			}
			shared_t get(const key_t& k) {
				key_t tk(k);
				auto& map = _resource->map;
				const auto itr = map.find(tk);
				if(itr != map.end()) {
					shared_t ret(itr->second.weak.lock());
					D_Assert0(ret);
					return ret;
				}
				return nullptr;
			}
			std::size_t size() const noexcept {
				return _resource->map.size();
			}
	};
	template <class T, class K, class A>
	const typename ResMgrName<T,K,A>::key_t ResMgrName<T,K,A>::s_anonymousPrefix("__anonymous__");
}
<commit_msg>ResMgrName: 名前付きリソースの置き換え<commit_after>#pragma once
#include "singleton.hpp"
#include "restag.hpp"
#include "optional.hpp"
#include <unordered_map>

namespace spi {
	//! 名前付きリソースマネージャ
	/*!
		中身の保持はすべてスマートポインタで行う
		シングルスレッド動作
	*/
	template <
		class T,
		class K = std::string,
		class Allocator = std::allocator<T>
	>
	class ResMgrName {
		public:
			using value_t = T;
			using shared_t = std::shared_ptr<value_t>;
			using key_t = K;
		private:
			using this_t = ResMgrName<T,K>;
			using tag_t = ResTag<value_t>;
			using Map = std::unordered_map<key_t, tag_t>;
			using Val2Key = std::unordered_map<const value_t*, key_t>;
			struct Resource {
				Map			map;
				Val2Key		v2k;
			};
			using Resource_SP = std::shared_ptr<Resource>;

			template <bool B>
			class _iterator : public Map::iterator {
				public:
					using base_t = typename Map::iterator;
					using base_t::base_t;
					using v_t = std::conditional_t<B, const value_t, value_t>;
					using s_t = std::shared_ptr<v_t>;
					s_t operator *() const {
						return base_t::operator*().second.weak.lock();
					}
					s_t operator ->() const {
						return base_t::operator*().second.weak.lock();
					}
			};
			using iterator = _iterator<false>;
			using const_iterator = _iterator<true>;

			Resource_SP		_resource;

			template <class T2>
			static void _Release(const Resource_SP& r, T2* p) noexcept {
				auto& m = r->map;
				auto& v2k = r->v2k;
				typename Allocator::template rebind<T2>::other alc;
				const auto itr = v2k.find(p);
				try {
					Assert0(itr != v2k.end());
					const auto itr2 = m.find(itr->second);
					Assert0(itr2 != m.end());
					v2k.erase(itr);
					m.erase(itr2);
					alc.destroy(p);
					alc.deallocate(p, 1);
				} catch(...) {}
			}
			const static key_t s_anonymousPrefix;
			// 無名リソースを作成する際の通し番号
			uint64_t _acounter;
			key_t _makeAKey() {
				// 適当にリソース名を生成して、ダブりがなければOK
				for(;;) {
					key_t key(s_anonymousPrefix);
					key += std::to_string(_acounter++);
					if(_resource->map.count(key) == 0)
						return key;
				}
			}

			template <class Ar, class T2, class K2>
			friend void save(Ar&, const ResMgrName<T2,K2>&);
			template <class Ar, class T2, class K2>
			friend void load(Ar&, ResMgrName<T2,K2>&);

			key_t _convertKey(const key_t& s) {
				key_t tk(s);
				if(!_HasAnonymousPrefix(tk))
					_modifyResourceName(tk);
				return tk;
			}
			static bool _HasAnonymousPrefix(const key_t& s) {
				if(s.size() >= s_anonymousPrefix.size()) {
					auto itr0 = s.cbegin(),
						 itr1 = s_anonymousPrefix.cbegin();
					do {
						if(*itr0 != *itr1)
							return false;
						++itr0;
						++itr1;
					} while(itr1 != s_anonymousPrefix.cend());
					return true;
				}
				return false;
			}
			template <class T2>
			auto _getDeleter() {
				return [r=_resource](T2 *const p){ _Release(r, p); };
			}
			//! key->resource の関連付けを解除
			tag_t _eraseKey2Tag(const key_t& k_from) {
				auto& m = _resource->map;
				const auto itr = m.find(k_from);
				D_Assert0(itr != m.end());

				tag_t tag = itr->second;
				m.erase(itr);
				return tag;
			}
			//! resource -> key の関連付けを解除
			void _eraseV2K(const value_t* ptr) {
				auto& v2k = _resource->v2k;
				const auto itr = v2k.find(ptr);
				D_Assert0(itr != v2k.end());
				v2k.erase(itr);
			}
			//! あるキーに関連付けられているリソースを別の名前と関連付けしなおす
			void _renameEntry(const key_t& k_from, const key_t& k_to) {
				tag_t tag = _eraseKey2Tag(k_from);
				_eraseV2K(tag.ptr);

				_resource->map.emplace(k_to, tag);
				_resource->v2k.emplace(tag.ptr, k_to);
			}
			template <class T2, class Make>
			auto _addEntry(const key_t& k, const Make& make) {
				// 新しくリソースを作成
				Constructor<T2> ctor;
				make(k, ctor);

				std::shared_ptr<T2> sp(
					ctor.pointer,
					_getDeleter<T2>()
				);
				auto& res = *_resource;
				res.map.emplace(k, sp);
				res.v2k.emplace(sp.get(), k);
				return sp;
			}
		protected:
			//! 継承先のクラスでキーの改変をする必要があればこれをオーバーライドする
			virtual void _modifyResourceName(key_t& /*key*/) const {}

		public:
			ResMgrName():
				_resource(std::make_shared<Resource>()),
				_acounter(0)
			{}
			iterator begin() noexcept { return _resource->map.begin(); }
			iterator end() noexcept { return _resource->map.end(); }
			const_iterator begin() const noexcept { return _resource->map.begin(); }
			const_iterator cbegin() const noexcept { return _resource->map.cbegin(); }
			const_iterator end() const noexcept { return _resource->map.end(); }
			const_iterator cend() const noexcept { return _resource->map.cend(); }

			// (主にデバッグ用)
			bool operator == (const ResMgrName& m) const noexcept {
				auto	&m0 = _resource->map,
						&m1 = m._resource->map;
				if(m0.size() == m1.size()) {
					auto itr0 = m0.begin(),
						 itr1 = m1.begin();
					while(itr0 != m0.end()) {
						if(*itr0 != *itr1)
							return false;
						++itr0;
						++itr1;
					}
					return _acounter == m._acounter;
				}
				return false;
			}
			bool operator != (const ResMgrName& m) const noexcept {
				return !(this->operator == (m));
			}
			template <class T2>
			struct Constructor {
				using Alc = typename Allocator::template rebind<T2>::other;
				Alc			alc;
				T2*			pointer;
				Constructor():
					pointer(alc.allocate(1))
				{}
				template <class... Ts>
				void operator()(Ts&&... ts) {
					alc.construct(pointer, std::forward<Ts>(ts)...);
				}
			};

			// ---- 名前付きリソース作成 ----
			template <class T2, class Make>
			auto acquireWithMake(const key_t& k, Make&& make) {
				const key_t tk = _convertKey(k);
				// 既に同じ名前でリソースを確保済みならばそれを返す
				if(auto ret = get(tk))
					return std::make_pair(std::static_pointer_cast<T2>(ret), false);
				return std::make_pair(_addEntry<T2>(tk, make), true);
			}
			//! 型を指定してのリソース確保
			template <class T2, class... Ts>
			auto emplaceWithType(const key_t& k, Ts&&... ts) {
				return acquireWithMake<T2>(k, [&ts...](auto& /*key*/, auto&& mk){
					mk(std::forward<Ts>(ts)...);
				});
			}
			template <class... Ts>
			auto emplace(const key_t& k, Ts&&... ts) {
				return emplaceWithType<value_t>(k, std::forward<Ts>(ts)...);
			}
			bool setAnonymous(const key_t& k, key_t* oldKey=nullptr) {
				auto& m = _resource->map;
				const auto itr = m.find(k);
				if(itr != m.end()) {
					const auto nk = _makeAKey();
					if(oldKey)
						*oldKey = nk;
					_renameEntry(k, nk);
					return true;
				}
				return false;
			}
			/*!
				\param[in] oldKey	有効なポインタを指定すれば上書きされたリソースの新しいキー名がセットされる
				\return std::pair(first=置き換えられた後のリソースハンドル, second=古いリソースが置き換えられたかのフラグ)
			*/
			template <class T2, class Make>
			auto replaceWithMake(const key_t& k, const Make& make, key_t* oldKey=nullptr) {
				const key_t tk = _convertKey(k);
				// 既に同じ名前でリソースを確保済みならばキーを無名リソースキーに書き換え
				const bool hasOld = setAnonymous(tk, oldKey);
				return std::make_pair(_addEntry<T2>(tk, make), hasOld);
			}
			template <class T2, class... Ts>
			auto replaceEmplaceWithType(const key_t& k, key_t* oldKey, Ts&&... ts) {
				return replaceWithMake<T2>(k, [&ts...](auto& /*key*/, auto&& mk){
					mk(std::forward<Ts>(ts)...);
				}, oldKey);
			}
			template <class... Ts>
			auto replaceEmplace(const key_t& k, key_t* oldKey, Ts&&... ts) {
				return replaceEmplaceWithType<value_t>(k, oldKey, std::forward<Ts>(ts)...);
			}

			// ---- 無名リソース作成 ----
			template <class P, class... Ts>
			auto acquireA(Ts&&... ts) {
				const auto key = _makeAKey();
				auto ret = acquireWithMake<P>(key,
							[&ts...](auto& /*key*/, auto&& mk){
								mk(std::forward<Ts>(ts)...);
							});
				D_Assert0(ret.second);
				return ret.first;
			}
			//! データ型を指定しての無名リソース確保
			template <class T2, class... Ts>
			auto emplaceA_WithType(Ts&&... ts) {
				return acquireA<T2>(std::forward<Ts>(ts)...);
			}
			//! 無名リソース確保(データ型 = value_t>
			template <class... Ts>
			auto emplaceA(Ts&&... ts) {
				return emplaceA_WithType<value_t>(std::forward<Ts>(ts)...);
			}

			//! リソースに対応するキーを取得(from shared_ptr)
			Optional<const key_t&> getKey(const shared_t& p) const {
				return getKey(p.get());
			}
			//! リソースに対応するキーを取得(from pointer)
			/*! 管轄外のリソースや不正なポインタを入力した場合、noneが返る */
			Optional<const key_t&> getKey(const value_t* p) const {
				auto& v2k = _resource->v2k;
				const auto itr = v2k.find(p);
				if(itr != v2k.end())
					return itr->second;
				return none;
			}
			//! キーに対応するリソースを取り出す(ない場合はnullptrを返す)
			shared_t get(const key_t& k) {
				const key_t tk = _convertKey(k);
				auto& map = _resource->map;
				const auto itr = map.find(tk);
				if(itr != map.end()) {
					shared_t ret(itr->second.weak.lock());
					D_Assert0(ret);
					return ret;
				}
				return nullptr;
			}
			//! 確保されたリソース数
			std::size_t size() const noexcept {
				return _resource->map.size();
			}
	};
	template <class T>
	const char* GetAnonymousPrefix(T*) { return u8"__anonymous__"; }
	inline const char16_t* GetAnonymousPrefix(std::u16string*) { return  u"__anonymous__"; }
	inline const char32_t* GetAnonymousPrefix(std::u32string*) { return  U"__anonymous__"; }

	template <class T, class K, class A>
	const typename ResMgrName<T,K,A>::key_t ResMgrName<T,K,A>::s_anonymousPrefix(GetAnonymousPrefix((K*)nullptr));
}
<|endoftext|>


<commit_before>/*!
 *  Copyright (c) 2018 by Contributors
 * \file alter_op_layout.cc
 * \brief Alter the operator layouts. Keep inferred layouts (if any) from previous stages.
 *        e.g., convolution may calculates faster with NCHW16c layout.
 */
#include <nnvm/pass.h>
#include <nnvm/op_attr_types.h>
#include <nnvm/layout.h>
#include <nnvm/compiler/op_attr_types.h>
#include <nnvm/pass_functions.h>
#include <tvm/tvm.h>
#include <algorithm>
#include <functional>
#include "./compile_engine.h"
#include "./graph_transform.h"

namespace nnvm {
namespace compiler {
namespace {

tvm::Array<tvm::Tensor> GetTensorInfo(const IndexedGraph& idx_graph,
                                      const uint32_t nid,
                                      const ShapeVector& shape_vec,
                                      const DTypeVector& dtype_vec) {
  tvm::Array<tvm::Tensor> vec;
  for (uint32_t i = 0; i < idx_graph[nid].source->num_outputs(); ++i) {
    tvm::Array<tvm::Expr> shape;
    for (int64_t x : shape_vec[idx_graph.entry_id(nid, i)]) {
      CHECK_LE(x, static_cast<int64_t>(std::numeric_limits<int>::max()));
      shape.push_back(tvm::make_const(tvm::Int(32), x));
    }
    vec.push_back(tvm::placeholder(
      shape, GetTVMType(dtype_vec[idx_graph.entry_id(nid, i)])));
  }
  return vec;
}

Graph AlterOpLayout(const Graph& src) {
  static auto& falter_op_layout =
    Op::GetAttr<nnvm::compiler::FTVMAlterOpLayout >("FTVMAlterOpLayout");

  const ShapeVector& shape_vec = src.GetAttr<ShapeVector>("shape");
  const DTypeVector& dtype_vec = src.GetAttr<DTypeVector>("dtype");
  const IndexedGraph& idx_graph = src.indexed_graph();

  std::vector<std::vector<Layout> > in_layouts_of_node(idx_graph.num_nodes());
  std::vector<std::vector<Layout> > out_layouts_of_node(idx_graph.num_nodes());
  std::unordered_map<const Node*, uint32_t> new_nodes;

  if (src.HasAttr("layout")) {
    // record layouts so that LayoutTransform pass can fix layouts correctly,
    // e.g., conv2d can be replaced by some contrib implement
    // whose layout is different from the original one
    // (which was imported from a model file).
    const auto& layouts = src.GetAttr<std::vector<Layout> >("layout");
    for (uint32_t nid = 0; nid < idx_graph.num_nodes(); ++nid) {
      const auto &inode = idx_graph[nid];
      if (falter_op_layout.count(inode.source->op())) {
        // do not record input layouts of nodes that will be replaced.
        continue;
      }
      std::vector<Layout> in_layout;
      for (const auto& e : inode.inputs) {
        in_layout.emplace_back(layouts[idx_graph.entry_id(e)]);
      }
      in_layouts_of_node[nid] = in_layout;

      std::vector<Layout> out_layout;
      for (uint i = 0; i < inode.source->num_outputs(); ++i) {
        out_layout.emplace_back(layouts[idx_graph.entry_id(nid, i)]);
      }
      out_layouts_of_node[nid] = out_layout;
    }
  }

  auto transform = [&](uint32_t nid,
                       const NodePtr& n,
                       std::vector<NodeEntry>* ret) {
    nnvm::compiler::FTVMAlterOpLayout fn_alter_op_layout =
      falter_op_layout.get(n->op(), nullptr);
    if (fn_alter_op_layout == nullptr) {
      new_nodes[n.get()] = nid;
      return false;
    }

    // construct parameters for registered function
    std::vector<Symbol> op_inputs;
    tvm::Array<tvm::Tensor> tensor_infos;
    CHECK_EQ(n->num_inputs(), idx_graph[nid].inputs.size());
    for (uint32_t i = 0; i < n->num_inputs(); ++i) {
      const nnvm::NodeEntry& input = n->inputs[i];
      // input operator
      Symbol op_input;
      op_input.outputs.push_back(input);
      op_inputs.push_back(op_input);

      // input tinfo, extract from the original graph
      // because it was where infer_shape & infer_type applied.
      tvm::Array<tvm::Tensor> op_output_tinfos =
        GetTensorInfo(idx_graph, idx_graph[nid].inputs[i].node_id,
                      shape_vec, dtype_vec);
      tensor_infos.push_back(op_output_tinfos[input.index]);
    }
    // callback registered function to get a new operator.
    auto op = fn_alter_op_layout(n->attrs, Symbol::CreateGroup(op_inputs), tensor_infos);
    *ret = op.outputs;
    return true;
  };

  Graph ret = nnvm::compiler::GraphTransform(src, transform);

  if (src.HasAttr("layout")) {
    // restore the layouts to return graph
    const auto& ret_idx = ret.indexed_graph();
    std::vector<Layout> ret_layouts(ret_idx.num_node_entries(), Layout::Undef());
    for (uint32_t nid = 0; nid < ret_idx.num_nodes(); ++nid) {
      const auto& inode = ret_idx[nid];
      if (new_nodes.count(inode.source)) {
        const std::vector<Layout>& in_layouts =
          in_layouts_of_node[new_nodes[inode.source]];
        for (uint32_t i = 0; i < inode.inputs.size(); ++i) {
          const auto& e = inode.inputs[i];
          ret_layouts[ret_idx.entry_id(e)] = in_layouts[i];
        }
        const std::vector<Layout>& out_layouts =
          out_layouts_of_node[new_nodes[inode.source]];
        for (uint32_t i = 0; i < inode.source->num_outputs(); ++i) {
          ret_layouts[ret_idx.entry_id(nid, i)] = out_layouts[i];
        }
      }
    }

    // cannot call indexed_graph() before return the origin Graph,
    // thus create a new one.
    nnvm::Graph new_ret;
    new_ret.outputs = ret.outputs;
    new_ret.attrs["layout"] = std::make_shared<any>(std::move(ret_layouts));
    return new_ret;
  }

  return ret;
}

// register pass
NNVM_REGISTER_PASS(AlterOpLayout)
.set_body(AlterOpLayout)
.set_change_graph(true);

}  // namespace
}  // namespace compiler
}  // namespace nnvm
<commit_msg>use uint32_t (#478)<commit_after>/*!
 *  Copyright (c) 2018 by Contributors
 * \file alter_op_layout.cc
 * \brief Alter the operator layouts. Keep inferred layouts (if any) from previous stages.
 *        e.g., convolution may calculates faster with NCHW16c layout.
 */
#include <nnvm/pass.h>
#include <nnvm/op_attr_types.h>
#include <nnvm/layout.h>
#include <nnvm/compiler/op_attr_types.h>
#include <nnvm/pass_functions.h>
#include <tvm/tvm.h>
#include <algorithm>
#include <functional>
#include "./compile_engine.h"
#include "./graph_transform.h"

namespace nnvm {
namespace compiler {
namespace {

tvm::Array<tvm::Tensor> GetTensorInfo(const IndexedGraph& idx_graph,
                                      const uint32_t nid,
                                      const ShapeVector& shape_vec,
                                      const DTypeVector& dtype_vec) {
  tvm::Array<tvm::Tensor> vec;
  for (uint32_t i = 0; i < idx_graph[nid].source->num_outputs(); ++i) {
    tvm::Array<tvm::Expr> shape;
    for (int64_t x : shape_vec[idx_graph.entry_id(nid, i)]) {
      CHECK_LE(x, static_cast<int64_t>(std::numeric_limits<int>::max()));
      shape.push_back(tvm::make_const(tvm::Int(32), x));
    }
    vec.push_back(tvm::placeholder(
      shape, GetTVMType(dtype_vec[idx_graph.entry_id(nid, i)])));
  }
  return vec;
}

Graph AlterOpLayout(const Graph& src) {
  static auto& falter_op_layout =
    Op::GetAttr<nnvm::compiler::FTVMAlterOpLayout >("FTVMAlterOpLayout");

  const ShapeVector& shape_vec = src.GetAttr<ShapeVector>("shape");
  const DTypeVector& dtype_vec = src.GetAttr<DTypeVector>("dtype");
  const IndexedGraph& idx_graph = src.indexed_graph();

  std::vector<std::vector<Layout> > in_layouts_of_node(idx_graph.num_nodes());
  std::vector<std::vector<Layout> > out_layouts_of_node(idx_graph.num_nodes());
  std::unordered_map<const Node*, uint32_t> new_nodes;

  if (src.HasAttr("layout")) {
    // record layouts so that LayoutTransform pass can fix layouts correctly,
    // e.g., conv2d can be replaced by some contrib implement
    // whose layout is different from the original one
    // (which was imported from a model file).
    const auto& layouts = src.GetAttr<std::vector<Layout> >("layout");
    for (uint32_t nid = 0; nid < idx_graph.num_nodes(); ++nid) {
      const auto &inode = idx_graph[nid];
      if (falter_op_layout.count(inode.source->op())) {
        // do not record input layouts of nodes that will be replaced.
        continue;
      }
      std::vector<Layout> in_layout;
      for (const auto& e : inode.inputs) {
        in_layout.emplace_back(layouts[idx_graph.entry_id(e)]);
      }
      in_layouts_of_node[nid] = in_layout;

      std::vector<Layout> out_layout;
      for (uint32_t i = 0; i < inode.source->num_outputs(); ++i) {
        out_layout.emplace_back(layouts[idx_graph.entry_id(nid, i)]);
      }
      out_layouts_of_node[nid] = out_layout;
    }
  }

  auto transform = [&](uint32_t nid,
                       const NodePtr& n,
                       std::vector<NodeEntry>* ret) {
    nnvm::compiler::FTVMAlterOpLayout fn_alter_op_layout =
      falter_op_layout.get(n->op(), nullptr);
    if (fn_alter_op_layout == nullptr) {
      new_nodes[n.get()] = nid;
      return false;
    }

    // construct parameters for registered function
    std::vector<Symbol> op_inputs;
    tvm::Array<tvm::Tensor> tensor_infos;
    CHECK_EQ(n->num_inputs(), idx_graph[nid].inputs.size());
    for (uint32_t i = 0; i < n->num_inputs(); ++i) {
      const nnvm::NodeEntry& input = n->inputs[i];
      // input operator
      Symbol op_input;
      op_input.outputs.push_back(input);
      op_inputs.push_back(op_input);

      // input tinfo, extract from the original graph
      // because it was where infer_shape & infer_type applied.
      tvm::Array<tvm::Tensor> op_output_tinfos =
        GetTensorInfo(idx_graph, idx_graph[nid].inputs[i].node_id,
                      shape_vec, dtype_vec);
      tensor_infos.push_back(op_output_tinfos[input.index]);
    }
    // callback registered function to get a new operator.
    auto op = fn_alter_op_layout(n->attrs, Symbol::CreateGroup(op_inputs), tensor_infos);
    *ret = op.outputs;
    return true;
  };

  Graph ret = nnvm::compiler::GraphTransform(src, transform);

  if (src.HasAttr("layout")) {
    // restore the layouts to return graph
    const auto& ret_idx = ret.indexed_graph();
    std::vector<Layout> ret_layouts(ret_idx.num_node_entries(), Layout::Undef());
    for (uint32_t nid = 0; nid < ret_idx.num_nodes(); ++nid) {
      const auto& inode = ret_idx[nid];
      if (new_nodes.count(inode.source)) {
        const std::vector<Layout>& in_layouts =
          in_layouts_of_node[new_nodes[inode.source]];
        for (uint32_t i = 0; i < inode.inputs.size(); ++i) {
          const auto& e = inode.inputs[i];
          ret_layouts[ret_idx.entry_id(e)] = in_layouts[i];
        }
        const std::vector<Layout>& out_layouts =
          out_layouts_of_node[new_nodes[inode.source]];
        for (uint32_t i = 0; i < inode.source->num_outputs(); ++i) {
          ret_layouts[ret_idx.entry_id(nid, i)] = out_layouts[i];
        }
      }
    }

    // cannot call indexed_graph() before return the origin Graph,
    // thus create a new one.
    nnvm::Graph new_ret;
    new_ret.outputs = ret.outputs;
    new_ret.attrs["layout"] = std::make_shared<any>(std::move(ret_layouts));
    return new_ret;
  }

  return ret;
}

// register pass
NNVM_REGISTER_PASS(AlterOpLayout)
.set_body(AlterOpLayout)
.set_change_graph(true);

}  // namespace
}  // namespace compiler
}  // namespace nnvm
<|endoftext|>


<commit_before>/*
    Copyright (c) 2007 Till Adam <adam@kde.org>

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

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

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "maildirresource.h"

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtDBus/QDBusConnection>

#include <kdebug.h>
#include <kurl.h>
#include <kfiledialog.h>
#include <klocale.h>

#include <libakonadi/collectionlistjob.h>
#include <libakonadi/collectionmodifyjob.h>
#include <libakonadi/itemappendjob.h>
#include <libakonadi/itemfetchjob.h>
#include <libakonadi/itemstorejob.h>
#include <libakonadi/session.h>

#include <maildir/maildir.h>

#include <kmime/kmime_message.h>

#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<KMime::Message> MessagePtr;

using namespace Akonadi;
using KPIM::Maildir;

MaildirResource::MaildirResource( const QString &id )
    :ResourceBase( id )
{
}

MaildirResource::~ MaildirResource()
{
}

bool MaildirResource::requestItemDelivery( const Akonadi::DataReference &ref, const QStringList &parts, const QDBusMessage &msg )
{
  const QString rid = ref.remoteId();
  const QString dir = rid.left( rid.lastIndexOf( QDir::separator() ) );
  const QString entry = rid.mid( rid.lastIndexOf( QDir::separator() ) + 1 );

  Maildir md( dir );
  if ( !md.isValid() )
    return false;

  const QByteArray data = md.readEntry( entry );
  KMime::Message *mail = new KMime::Message();
  mail->setContent( data );
  mail->parse();

  Item item( ref );
  item.setMimeType( "message/rfc822" );
  item.setPayload( MessagePtr( mail ) );
  ItemStoreJob *job = new ItemStoreJob( item, session() );
  job->storePayload();

  return deliverItem( job, msg );
}

void MaildirResource::aboutToQuit()
{
  kDebug() << "Implement me: " << k_funcinfo << endl;
}

void MaildirResource::configure()
{
  QString oldDir = settings()->value( "General/Path" ).toString();
  KUrl url;
  if ( !oldDir.isEmpty() )
    url = KUrl::fromPath( oldDir );
  else
    url = KUrl::fromPath( QDir::homePath() );
  QString newDir = KFileDialog::getExistingDirectory( url, 0, i18n("Select Mail Directory") );
  if ( newDir.isEmpty() )
    return;
  if ( oldDir == newDir )
    return;
  settings()->setValue( "General/Path", newDir );
  synchronize();
}

void MaildirResource::itemAdded( const Akonadi::Item & item, const Akonadi::Collection& collection )
{
    Maildir dir( collection.remoteId() );
    QString errMsg;
    if ( !dir.isValid( errMsg ) ) {
      error( errMsg );
      return;
    }
    // we can only deal with mail
    if ( item.mimeType() != "message/rfc822" ) {
      error( i18n("Only email messages can be added to the Maildir resource!") );
      return;
    }
    const MessagePtr mail = item.payload<MessagePtr>();
    dir.addEntry( mail->encodedContent() );
}

void MaildirResource::itemChanged( const Akonadi::Item& item, const QStringList& parts )
{
    if ( !parts.contains( Item::PartBody ) ) return;

    const QString rid = item.reference().remoteId();
    const QString path = rid.left( rid.lastIndexOf( QDir::separator() ) );
    const QString entry = rid.mid( rid.lastIndexOf( QDir::separator() ) + 1 );

    Maildir dir( path );
    QString errMsg;
    if ( !dir.isValid( errMsg ) ) {
        error( errMsg );
        return;
    }
    // we can only deal with mail
    if ( item.mimeType() != "message/rfc822" ) {
        error( i18n("Only email messages can be added to the Maildir resource!") );
        return;
    }
    const MessagePtr mail = item.payload<MessagePtr>();
    dir.writeEntry( entry, mail->encodedContent() );
}

void MaildirResource::itemRemoved(const Akonadi::DataReference & ref)
{
  kDebug() << "Implement me: " << k_funcinfo << endl;
 }

void MaildirResource::retrieveCollections()
{
  Maildir dir( settings()->value( "General/Path" ).toString() );
  QString errMsg;
  if ( !dir.isValid( errMsg ) ) {
    error( errMsg );
    collectionsRetrieved( Collection::List() );
  }

  Collection root;
  root.setParent( Collection::root() );
  root.setRemoteId( settings()->value( "General/Path" ).toString() );
  root.setName( name() );
  QStringList mimeTypes;
  mimeTypes << "message/rfc822" << Collection::collectionMimeType();
  root.setContentTypes( mimeTypes );
  root.setCachePolicyId( 1 ); // ### just for testing

  Collection::List list;
  list << root;
  foreach ( const QString sub, dir.subFolderList() ) {
    Collection c;
    c.setRemoteId( sub );
    c.setParent( root );
    c.setContentTypes( mimeTypes );
    list << c;
  }
  collectionsRetrieved( list );
}

void MaildirResource::synchronizeCollection(const Akonadi::Collection & col)
{
  ItemFetchJob *fetch = new ItemFetchJob( col, session() );
  if ( !fetch->exec() ) {
    changeStatus( Error, i18n("Unable to fetch listing of collection '%1': %2", col.name(), fetch->errorString()) );
    return;
  }
  Item::List items = fetch->items();

  changeProgress( 0 );

  Maildir md( col.remoteId() );
  if ( !md.isValid() ) {
    error( i18n("Invalid maildir" ) );
    return;
  }
  QStringList entryList = md.entryList();

  int counter = 0;
  foreach ( const QString entry, entryList ) {
    const QString rid = col.remoteId() + QDir::separator() + entry;
    bool found = false;
    foreach ( Item item, items ) {
      if ( item.reference().remoteId() == rid ) {
        found = true;
        break;
      }
    }
    if ( found )
      continue;
    Item item( DataReference( -1, rid ) );
    item.setMimeType( "message/rfc822" );
    ItemAppendJob *append = new ItemAppendJob( item, col, session() );
    if ( !append->exec() ) {
      changeProgress( 0 );
      changeStatus( Error, i18n("Appending new message failed: %1", append->errorString()) );
      return;
    }

    counter++;
    int percentage = (counter * 100) / entryList.count();
    changeProgress( percentage );
  }
  collectionSynchronized();
}

#include "maildirresource.moc"
<commit_msg>And finally implement removing entries in teh resource too.<commit_after>/*
    Copyright (c) 2007 Till Adam <adam@kde.org>

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

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

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "maildirresource.h"

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtDBus/QDBusConnection>

#include <kdebug.h>
#include <kurl.h>
#include <kfiledialog.h>
#include <klocale.h>

#include <libakonadi/collectionlistjob.h>
#include <libakonadi/collectionmodifyjob.h>
#include <libakonadi/itemappendjob.h>
#include <libakonadi/itemfetchjob.h>
#include <libakonadi/itemstorejob.h>
#include <libakonadi/session.h>

#include <maildir/maildir.h>

#include <kmime/kmime_message.h>

#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<KMime::Message> MessagePtr;

using namespace Akonadi;
using KPIM::Maildir;

MaildirResource::MaildirResource( const QString &id )
    :ResourceBase( id )
{
}

MaildirResource::~ MaildirResource()
{
}

bool MaildirResource::requestItemDelivery( const Akonadi::DataReference &ref, const QStringList &parts, const QDBusMessage &msg )
{
  const QString rid = ref.remoteId();
  const QString dir = rid.left( rid.lastIndexOf( QDir::separator() ) );
  const QString entry = rid.mid( rid.lastIndexOf( QDir::separator() ) + 1 );

  Maildir md( dir );
  if ( !md.isValid() )
    return false;

  const QByteArray data = md.readEntry( entry );
  KMime::Message *mail = new KMime::Message();
  mail->setContent( data );
  mail->parse();

  Item item( ref );
  item.setMimeType( "message/rfc822" );
  item.setPayload( MessagePtr( mail ) );
  ItemStoreJob *job = new ItemStoreJob( item, session() );
  job->storePayload();

  return deliverItem( job, msg );
}

void MaildirResource::aboutToQuit()
{
  kDebug() << "Implement me: " << k_funcinfo << endl;
}

void MaildirResource::configure()
{
  QString oldDir = settings()->value( "General/Path" ).toString();
  KUrl url;
  if ( !oldDir.isEmpty() )
    url = KUrl::fromPath( oldDir );
  else
    url = KUrl::fromPath( QDir::homePath() );
  QString newDir = KFileDialog::getExistingDirectory( url, 0, i18n("Select Mail Directory") );
  if ( newDir.isEmpty() )
    return;
  if ( oldDir == newDir )
    return;
  settings()->setValue( "General/Path", newDir );
  synchronize();
}

void MaildirResource::itemAdded( const Akonadi::Item & item, const Akonadi::Collection& collection )
{
    Maildir dir( collection.remoteId() );
    QString errMsg;
    if ( !dir.isValid( errMsg ) ) {
      error( errMsg );
      return;
    }
    // we can only deal with mail
    if ( item.mimeType() != "message/rfc822" ) {
      error( i18n("Only email messages can be added to the Maildir resource!") );
      return;
    }
    const MessagePtr mail = item.payload<MessagePtr>();
    dir.addEntry( mail->encodedContent() );
}

void MaildirResource::itemChanged( const Akonadi::Item& item, const QStringList& parts )
{
    if ( !parts.contains( Item::PartBody ) ) return;

    const QString rid = item.reference().remoteId();
    const QString path = rid.left( rid.lastIndexOf( QDir::separator() ) );
    const QString entry = rid.mid( rid.lastIndexOf( QDir::separator() ) + 1 );

    Maildir dir( path );
    QString errMsg;
    if ( !dir.isValid( errMsg ) ) {
        error( errMsg );
        return;
    }
    // we can only deal with mail
    if ( item.mimeType() != "message/rfc822" ) {
        error( i18n("Only email messages can be added to the Maildir resource!") );
        return;
    }
    const MessagePtr mail = item.payload<MessagePtr>();
    dir.writeEntry( entry, mail->encodedContent() );
}

void MaildirResource::itemRemoved(const Akonadi::DataReference & ref)
{
    const QString rid = ref.remoteId();
    const QString path = rid.left( rid.lastIndexOf( QDir::separator() ) );
    const QString entry = rid.mid( rid.lastIndexOf( QDir::separator() ) + 1 );

    Maildir dir( path );
    QString errMsg;
    if ( !dir.isValid( errMsg ) ) {
        error( errMsg );
        return;
    }
    if ( !dir.removeEntry( entry ) ) {
        error( i18n("Failed to delete item: %1", rid) );
    }

}

void MaildirResource::retrieveCollections()
{
  Maildir dir( settings()->value( "General/Path" ).toString() );
  QString errMsg;
  if ( !dir.isValid( errMsg ) ) {
    error( errMsg );
    collectionsRetrieved( Collection::List() );
  }

  Collection root;
  root.setParent( Collection::root() );
  root.setRemoteId( settings()->value( "General/Path" ).toString() );
  root.setName( name() );
  QStringList mimeTypes;
  mimeTypes << "message/rfc822" << Collection::collectionMimeType();
  root.setContentTypes( mimeTypes );
  root.setCachePolicyId( 1 ); // ### just for testing

  Collection::List list;
  list << root;
  foreach ( const QString sub, dir.subFolderList() ) {
    Collection c;
    c.setRemoteId( sub );
    c.setParent( root );
    c.setContentTypes( mimeTypes );
    list << c;
  }
  collectionsRetrieved( list );
}

void MaildirResource::synchronizeCollection(const Akonadi::Collection & col)
{
  ItemFetchJob *fetch = new ItemFetchJob( col, session() );
  if ( !fetch->exec() ) {
    changeStatus( Error, i18n("Unable to fetch listing of collection '%1': %2", col.name(), fetch->errorString()) );
    return;
  }
  Item::List items = fetch->items();

  changeProgress( 0 );

  Maildir md( col.remoteId() );
  if ( !md.isValid() ) {
    error( i18n("Invalid maildir" ) );
    return;
  }
  QStringList entryList = md.entryList();

  int counter = 0;
  foreach ( const QString entry, entryList ) {
    const QString rid = col.remoteId() + QDir::separator() + entry;
    bool found = false;
    foreach ( Item item, items ) {
      if ( item.reference().remoteId() == rid ) {
        found = true;
        break;
      }
    }
    if ( found )
      continue;
    Item item( DataReference( -1, rid ) );
    item.setMimeType( "message/rfc822" );
    ItemAppendJob *append = new ItemAppendJob( item, col, session() );
    if ( !append->exec() ) {
      changeProgress( 0 );
      changeStatus( Error, i18n("Appending new message failed: %1", append->errorString()) );
      return;
    }

    counter++;
    int percentage = (counter * 100) / entryList.count();
    changeProgress( percentage );
  }
  collectionSynchronized();
}

#include "maildirresource.moc"
<|endoftext|>


<commit_before>#include <libport/lexical-cast.hh>

#include <ast/exps-type.hh>
#include <ast/parametric-ast.hh>
#include <parser/ast-factory.hh>
#include <rewrite/pattern-binder.hh>

namespace rewrite
{
  using parser::ast_string;

  ast::rExp
  PatternBinder::to_binding(ast::rConstExp original)
  {
    PARAMETRIC_AST(rewrite, "Pattern.Binding.new(%exp:1)");
    rewrite % ast_string(original->location_get(), next_name());
    ast::rExp res = exp(rewrite);
    res->original_set(original);
    return res;
  }

  void
  PatternBinder::bind(ast::rConstLValue what, bool decl)
  {
    PARAMETRIC_AST(bind, "%lvalue:1 = %exp:2");
    PARAMETRIC_AST(bind_decl, "var %lvalue:1 = %exp:2");
    bindings_->children_get().push_back
      (exp((decl ? bind_decl : bind)
           % const_cast<ast::LValue*>(what.get())
           % value_get()));
  }

  ast::rExp
  PatternBinder::value_get()
  {
    PARAMETRIC_AST(get, "%lvalue:1 . bindings[%exp:2]");
    return exp(get % pattern_ % ast_string(ast::loc(), next_name()));
  }

  PatternBinder::PatternBinder(ast::rLValue pattern,
                               const ast::loc& loc,
                               bool assign)
    : bindings_(new ast::Pipe(loc, ast::exps_type()))
    , pattern_(pattern)
    , i_(0)
    , assign_(assign)
  {}

  libport::Symbol
  PatternBinder::next_name()
  {
    return libport::Symbol(boost::lexical_cast<std::string>(i_));
  }

  void
  PatternBinder::visit(const ast::Binding* binding)
  {
    i_++;
    result_ = to_binding(binding);
    bind(binding->what_get(), true);
  }

  void
  PatternBinder::visit(const ast::Call* call)
  {
    if (call->arguments_get() || !assign_)
    {
      super_type::visit(call);
      return;
    }
    i_++;
    result_ = to_binding(call);
    bind(call, false);
  }

  void
  PatternBinder::visit(const ast::Property* prop)
  {
    ast::loc loc = prop->location_get();

    i_++;
    result_ = to_binding(prop);
    bind(prop, false);
  }

  void
  PatternBinder::visit(const ast::Subscript* sub)
  {
    i_++;
    result_ = to_binding(sub);
    bind(sub, false);
  }

  ast::rPipe
  PatternBinder::bindings_get() const
  {
    return bindings_;
  }
}
<commit_msg>Likewise, assign list elements and properties only in assignments.<commit_after>#include <libport/lexical-cast.hh>

#include <ast/exps-type.hh>
#include <ast/parametric-ast.hh>
#include <parser/ast-factory.hh>
#include <rewrite/pattern-binder.hh>

namespace rewrite
{
  using parser::ast_string;

  ast::rExp
  PatternBinder::to_binding(ast::rConstExp original)
  {
    PARAMETRIC_AST(rewrite, "Pattern.Binding.new(%exp:1)");
    rewrite % ast_string(original->location_get(), next_name());
    ast::rExp res = exp(rewrite);
    res->original_set(original);
    return res;
  }

  void
  PatternBinder::bind(ast::rConstLValue what, bool decl)
  {
    PARAMETRIC_AST(bind, "%lvalue:1 = %exp:2");
    PARAMETRIC_AST(bind_decl, "var %lvalue:1 = %exp:2");
    bindings_->children_get().push_back
      (exp((decl ? bind_decl : bind)
           % const_cast<ast::LValue*>(what.get())
           % value_get()));
  }

  ast::rExp
  PatternBinder::value_get()
  {
    PARAMETRIC_AST(get, "%lvalue:1 . bindings[%exp:2]");
    return exp(get % pattern_ % ast_string(ast::loc(), next_name()));
  }

  PatternBinder::PatternBinder(ast::rLValue pattern,
                               const ast::loc& loc,
                               bool assign)
    : bindings_(new ast::Pipe(loc, ast::exps_type()))
    , pattern_(pattern)
    , i_(0)
    , assign_(assign)
  {}

  libport::Symbol
  PatternBinder::next_name()
  {
    return libport::Symbol(boost::lexical_cast<std::string>(i_));
  }

  void
  PatternBinder::visit(const ast::Binding* binding)
  {
    i_++;
    result_ = to_binding(binding);
    bind(binding->what_get(), true);
  }

  void
  PatternBinder::visit(const ast::Call* call)
  {
    if (call->arguments_get() || !assign_)
    {
      super_type::visit(call);
      return;
    }
    i_++;
    result_ = to_binding(call);
    bind(call, false);
  }

  void
  PatternBinder::visit(const ast::Property* prop)
  {
    if (!assign_)
    {
      super_type::visit(call);
      return;
    }

    i_++;
    result_ = to_binding(prop);
    bind(prop, false);
  }

  void
  PatternBinder::visit(const ast::Subscript* sub)
  {
    if (!assign_)
    {
      super_type::visit(call);
      return;
    }

    i_++;
    result_ = to_binding(sub);
    bind(sub, false);
  }

  ast::rPipe
  PatternBinder::bindings_get() const
  {
    return bindings_;
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test_file_util.h"

#include <windows.h>

#include <vector>

#include "base/file_util.h"

namespace file_util {

bool EvictFileFromSystemCache(const wchar_t* file) {
  // Request exclusive access to the file and overwrite it with no buffering.
  win_util::ScopedHandle hfile(
      CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING,
                 NULL));
  if (!hfile)
    return false;

  // Execute in chunks. It could be optimized. We want to do few of these since
  // these opterations will be slow without the cache.
  char buffer[4096];
  int total_bytes = 0;
  DWORD bytes_read;
  for (;;) {
    bytes_read = 0;
    ReadFile(hfile, buffer, sizeof(buffer), &bytes_read, NULL);
    if (bytes_read == 0)
      break;

    SetFilePointer(hfile, total_bytes, 0, FILE_BEGIN);
    if (!WriteFile(hfile, buffer, bytes_read, &bytes_read, NULL))
      return false;
    total_bytes += bytes_read;
  }
  return true;
}

// Like CopyFileNoCache but recursively copies all files and subdirectories
// in the given input directory to the output directory.
bool CopyRecursiveDirNoCache(const std::wstring& source_dir,
                             const std::wstring& dest_dir) {
  // Try to create the directory if it doesn't already exist.
  if (!CreateDirectory(dest_dir)) {
    if (GetLastError() != ERROR_ALREADY_EXISTS)
      return false;
  }

  std::vector<std::wstring> files_copied;

  std::wstring src(source_dir);
  file_util::AppendToPath(&src, L"*");

  WIN32_FIND_DATA fd;
  HANDLE fh = FindFirstFile(src.c_str(), &fd);
  if (fh == INVALID_HANDLE_VALUE)
    return false;

  do {
    std::wstring cur_file(fd.cFileName);
    if (cur_file == L"." || cur_file == L"..")
      continue;  // Skip these special entries.

    std::wstring cur_source_path(source_dir);
    file_util::AppendToPath(&cur_source_path, cur_file);

    std::wstring cur_dest_path(dest_dir);
    file_util::AppendToPath(&cur_dest_path, cur_file);

    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
      // Recursively copy a subdirectory. We stripped "." and ".." already.
      if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) {
        FindClose(fh);
        return false;
      }
    } else {
      // Copy the file.
      if (!::CopyFile(cur_source_path.c_str(), cur_dest_path.c_str(), false)) {
        FindClose(fh);
        return false;
      }

      // We don't check for errors from this function, often, we are copying
      // files that are in the repository, and they will have read-only set.
      // This will prevent us from evicting from the cache, but these don't
      // matter anyway.
      EvictFileFromSystemCache(cur_dest_path.c_str());
    }
  } while (FindNextFile(fh, &fd));

  FindClose(fh);
  return true;
}

}  // namespace file_util

<commit_msg>ScopedHandle is not in a namespace.<commit_after>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test_file_util.h"

#include <windows.h>

#include <vector>

#include "base/file_util.h"
#include "base/scoped_handle.h"

namespace file_util {

bool EvictFileFromSystemCache(const wchar_t* file) {
  // Request exclusive access to the file and overwrite it with no buffering.
  ScopedHandle hfile(
      CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING,
                 NULL));
  if (!hfile)
    return false;

  // Execute in chunks. It could be optimized. We want to do few of these since
  // these opterations will be slow without the cache.
  char buffer[4096];
  int total_bytes = 0;
  DWORD bytes_read;
  for (;;) {
    bytes_read = 0;
    ReadFile(hfile, buffer, sizeof(buffer), &bytes_read, NULL);
    if (bytes_read == 0)
      break;

    SetFilePointer(hfile, total_bytes, 0, FILE_BEGIN);
    if (!WriteFile(hfile, buffer, bytes_read, &bytes_read, NULL))
      return false;
    total_bytes += bytes_read;
  }
  return true;
}

// Like CopyFileNoCache but recursively copies all files and subdirectories
// in the given input directory to the output directory.
bool CopyRecursiveDirNoCache(const std::wstring& source_dir,
                             const std::wstring& dest_dir) {
  // Try to create the directory if it doesn't already exist.
  if (!CreateDirectory(dest_dir)) {
    if (GetLastError() != ERROR_ALREADY_EXISTS)
      return false;
  }

  std::vector<std::wstring> files_copied;

  std::wstring src(source_dir);
  file_util::AppendToPath(&src, L"*");

  WIN32_FIND_DATA fd;
  HANDLE fh = FindFirstFile(src.c_str(), &fd);
  if (fh == INVALID_HANDLE_VALUE)
    return false;

  do {
    std::wstring cur_file(fd.cFileName);
    if (cur_file == L"." || cur_file == L"..")
      continue;  // Skip these special entries.

    std::wstring cur_source_path(source_dir);
    file_util::AppendToPath(&cur_source_path, cur_file);

    std::wstring cur_dest_path(dest_dir);
    file_util::AppendToPath(&cur_dest_path, cur_file);

    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
      // Recursively copy a subdirectory. We stripped "." and ".." already.
      if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) {
        FindClose(fh);
        return false;
      }
    } else {
      // Copy the file.
      if (!::CopyFile(cur_source_path.c_str(), cur_dest_path.c_str(), false)) {
        FindClose(fh);
        return false;
      }

      // We don't check for errors from this function, often, we are copying
      // files that are in the repository, and they will have read-only set.
      // This will prevent us from evicting from the cache, but these don't
      // matter anyway.
      EvictFileFromSystemCache(cur_dest_path.c_str());
    }
  } while (FindNextFile(fh, &fd));

  FindClose(fh);
  return true;
}

}  // namespace file_util

<|endoftext|>


<commit_before>/***************************************************************************
 * rubyinterpreter.cpp
 * This file is part of the KDE project
 * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/
#include "rubyextension.h"

#include <st.h>

#include <qmap.h>
#include <qstring.h>

#include "api/list.h"

#include "rubyconfig.h"

#include <version.h>
#if(RUBY_VERSION_MAJOR==1 && RUBY_VERSION_MINOR == 8 && RUBY_VERSION_TEENY <= 1)
void rb_hash_foreach _((VALUE, int (*)(ANYARGS), VALUE));
#endif

namespace Kross {

namespace Ruby {

class RubyExtensionPrivate {
    friend class RubyExtension;
    /// The \a Kross::Api::Object this RubyExtension wraps.
    Kross::Api::Object::Ptr m_object;
    ///
    static VALUE s_krossObject;
};

VALUE RubyExtensionPrivate::s_krossObject = 0;

VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
{
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "method_missing(argc, argv, self)" << endl;
#endif
    if(argc < 1)
    {
        return 0;
    }
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "Converting self to Kross::Api::Object" << endl;
#endif

    Kross::Api::Object::Ptr object = toObject( self );
    return RubyExtension::call_method(object, argc, argv);
}

VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
{
    QString funcname = rb_id2name(SYM2ID(argv[0]));
    QValueList<Api::Object::Ptr> argsList;
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "Building arguments list for function : " << funcname << " there are " << (argc-1) << " arguments." << endl;
#endif
    for(int i = 1; i < argc; i++)
    {
        Kross::Api::Object::Ptr obj = toObject(argv[i]);
        if(obj) argsList.append(obj);
    }
    Kross::Api::Object::Ptr result;
    if(object->hasChild(funcname)) {
#ifdef KROSS_RUBY_EXTENSION_DEBUG
        kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) << endl;
#endif
        result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
    }
    else {
#ifdef KROSS_RUBY_EXTENSION_DEBUG
        kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) << endl;
#endif
        result = object->call(funcname, new Api::List(argsList));
    }

    return toVALUE(result);
}

RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
{
    d->m_object = object;
}

RubyExtension::~RubyExtension()
{
}

typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;

int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE  vmap)
{
    QMap<QString, Kross::Api::Object::Ptr>* map;
    Data_Get_Struct(vmap, mStrObj, map);
    if (key != Qundef)
    {
        Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
        if(o) map->replace(STR2CSTR(key), o);
    }
    return ST_CONTINUE;
}

Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
{
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
#endif
    switch( TYPE( value ) )
    {
        case T_DATA:
        {
#ifdef KROSS_RUBY_EXTENSION_DEBUG
            kdDebug() << "Object is a Kross Object" << endl;
#endif
            VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
            if(TYPE(result) == T_TRUE)
            {
                RubyExtension* objectExtension;
                Data_Get_Struct(value, RubyExtension, objectExtension);
                Kross::Api::Object::Ptr object = objectExtension->d->m_object;
                return object;
            } else {
                kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
                return 0;
            }
        }
        case T_FLOAT:
            return new Kross::Api::Variant(NUM2DBL(value));
        case T_STRING:
            return new Kross::Api::Variant(QString(STR2CSTR(value)));
        case T_ARRAY:
        {
            QValueList<Kross::Api::Object::Ptr> l;
            for(int i = 0; i < RARRAY(value)->len; i++)
            {
                Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
                if(o) l.append(o);
            }
            return new Kross::Api::List(l);
        }
        case T_FIXNUM:
            return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
        case T_HASH:
        {
            QMap<QString, Kross::Api::Object::Ptr> map;
            VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
            rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
            return new Kross::Api::Dict(map);
        }
        case T_BIGNUM:
        {
            return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
        }
        case T_TRUE:
        {
            return new Kross::Api::Variant(true);
        }
        case T_FALSE:
        {
            return new Kross::Api::Variant(false);
        }
        case T_SYMBOL:
        {
            return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
        }
        case T_MATCH:
        case T_OBJECT:
        case T_FILE:
        case T_STRUCT:
        case T_REGEXP:
        case T_MODULE:
        case T_ICLASS:
        case T_CLASS:
            kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
        default:
        case T_NIL:
            return 0;
    }
}

VALUE RubyExtension::toVALUE(const QString& s)
{
    return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
}

VALUE RubyExtension::toVALUE(QStringList list)
{
    VALUE l = rb_ary_new();
    for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
        rb_ary_push(l, toVALUE(*it));
    return l;
}

VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
{
    VALUE h = rb_hash_new();
    for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
        rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
    return h;

}

VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
{
    VALUE l = rb_ary_new();
    for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
        rb_ary_push(l, toVALUE(*it));
    return l;
}

VALUE RubyExtension::toVALUE(const QVariant& variant)
{

    switch(variant.type()) {
        case QVariant::Invalid:
            return Qnil;
        case QVariant::Bool:
            return (variant.toBool()) ? Qtrue : Qfalse;
        case QVariant::Int:
            return INT2FIX(variant.toInt());
        case QVariant::UInt:
            return UINT2NUM(variant.toUInt());
        case QVariant::Double:
            return rb_float_new(variant.toDouble());
        case QVariant::Date:
        case QVariant::Time:
        case QVariant::DateTime:
        case QVariant::ByteArray:
        case QVariant::BitArray:
        case QVariant::CString:
        case QVariant::String:
            return toVALUE(variant.toString());
        case QVariant::StringList:
            return toVALUE(variant.toStringList());
        case QVariant::Map:
            return toVALUE(variant.toMap());
        case QVariant::List:
            return toVALUE(variant.toList());

        // To handle following both cases is a bit difficult
        // cause Python doesn't spend an easy possibility
        // for such large numbers (TODO maybe BigInt?). So,
        // we risk overflows here, but well...
        case QVariant::LongLong: {
            return INT2NUM((long)variant.toLongLong());
        }
        case QVariant::ULongLong:
            return UINT2NUM((unsigned long)variant.toULongLong());
        default: {
            kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
            return Qundef;
        }
    }
}

VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
{
    if(! object) {
        return 0;
    }

    if(object->getClassName() == "Kross::Api::Variant") {
        QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
        return toVALUE(v);
    }

    if(object->getClassName() == "Kross::Api::List") {
        Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
        return toVALUE((Kross::Api::List::Ptr)list);
    }

    if(object->getClassName() == "Kross::Api::Dict") {
        Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
        return toVALUE((Kross::Api::Dict::Ptr)dict);
    }

    if(RubyExtensionPrivate::s_krossObject == 0)
    {
        RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
        rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing",  (VALUE (*)(...))RubyExtension::method_missing, -1);
    }
    return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, free, new RubyExtension(object) );
}

VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
{
    VALUE l = rb_ary_new();
    uint count = list ? list->count() : 0;
    for(uint i = 0; i < count; i++)
        rb_ary_push(l, toVALUE(list->item(i)));
    return l;

}

}

}
<commit_msg>- drop support for ruby <= 1.8.1<commit_after>/***************************************************************************
 * rubyinterpreter.cpp
 * This file is part of the KDE project
 * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/
#include "rubyextension.h"

#include <st.h>

#include <qmap.h>
#include <qstring.h>

#include "api/list.h"

#include "rubyconfig.h"

namespace Kross {

namespace Ruby {

class RubyExtensionPrivate {
    friend class RubyExtension;
    /// The \a Kross::Api::Object this RubyExtension wraps.
    Kross::Api::Object::Ptr m_object;
    ///
    static VALUE s_krossObject;
};

VALUE RubyExtensionPrivate::s_krossObject = 0;

VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
{
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "method_missing(argc, argv, self)" << endl;
#endif
    if(argc < 1)
    {
        return 0;
    }
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "Converting self to Kross::Api::Object" << endl;
#endif

    Kross::Api::Object::Ptr object = toObject( self );
    return RubyExtension::call_method(object, argc, argv);
}

VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
{
    QString funcname = rb_id2name(SYM2ID(argv[0]));
    QValueList<Api::Object::Ptr> argsList;
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "Building arguments list for function : " << funcname << " there are " << (argc-1) << " arguments." << endl;
#endif
    for(int i = 1; i < argc; i++)
    {
        Kross::Api::Object::Ptr obj = toObject(argv[i]);
        if(obj) argsList.append(obj);
    }
    Kross::Api::Object::Ptr result;
    if(object->hasChild(funcname)) {
#ifdef KROSS_RUBY_EXTENSION_DEBUG
        kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) << endl;
#endif
        result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
    }
    else {
#ifdef KROSS_RUBY_EXTENSION_DEBUG
        kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) << endl;
#endif
        result = object->call(funcname, new Api::List(argsList));
    }

    return toVALUE(result);
}

RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
{
    d->m_object = object;
}

RubyExtension::~RubyExtension()
{
}

typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;

int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE  vmap)
{
    QMap<QString, Kross::Api::Object::Ptr>* map;
    Data_Get_Struct(vmap, mStrObj, map);
    if (key != Qundef)
    {
        Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
        if(o) map->replace(STR2CSTR(key), o);
    }
    return ST_CONTINUE;
}

Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
{
#ifdef KROSS_RUBY_EXTENSION_DEBUG
    kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
#endif
    switch( TYPE( value ) )
    {
        case T_DATA:
        {
#ifdef KROSS_RUBY_EXTENSION_DEBUG
            kdDebug() << "Object is a Kross Object" << endl;
#endif
            VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
            if(TYPE(result) == T_TRUE)
            {
                RubyExtension* objectExtension;
                Data_Get_Struct(value, RubyExtension, objectExtension);
                Kross::Api::Object::Ptr object = objectExtension->d->m_object;
                return object;
            } else {
                kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
                return 0;
            }
        }
        case T_FLOAT:
            return new Kross::Api::Variant(NUM2DBL(value));
        case T_STRING:
            return new Kross::Api::Variant(QString(STR2CSTR(value)));
        case T_ARRAY:
        {
            QValueList<Kross::Api::Object::Ptr> l;
            for(int i = 0; i < RARRAY(value)->len; i++)
            {
                Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
                if(o) l.append(o);
            }
            return new Kross::Api::List(l);
        }
        case T_FIXNUM:
            return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
        case T_HASH:
        {
            QMap<QString, Kross::Api::Object::Ptr> map;
            VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
            rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
            return new Kross::Api::Dict(map);
        }
        case T_BIGNUM:
        {
            return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
        }
        case T_TRUE:
        {
            return new Kross::Api::Variant(true);
        }
        case T_FALSE:
        {
            return new Kross::Api::Variant(false);
        }
        case T_SYMBOL:
        {
            return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
        }
        case T_MATCH:
        case T_OBJECT:
        case T_FILE:
        case T_STRUCT:
        case T_REGEXP:
        case T_MODULE:
        case T_ICLASS:
        case T_CLASS:
            kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
        default:
        case T_NIL:
            return 0;
    }
}

VALUE RubyExtension::toVALUE(const QString& s)
{
    return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
}

VALUE RubyExtension::toVALUE(QStringList list)
{
    VALUE l = rb_ary_new();
    for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
        rb_ary_push(l, toVALUE(*it));
    return l;
}

VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
{
    VALUE h = rb_hash_new();
    for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
        rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
    return h;

}

VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
{
    VALUE l = rb_ary_new();
    for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
        rb_ary_push(l, toVALUE(*it));
    return l;
}

VALUE RubyExtension::toVALUE(const QVariant& variant)
{

    switch(variant.type()) {
        case QVariant::Invalid:
            return Qnil;
        case QVariant::Bool:
            return (variant.toBool()) ? Qtrue : Qfalse;
        case QVariant::Int:
            return INT2FIX(variant.toInt());
        case QVariant::UInt:
            return UINT2NUM(variant.toUInt());
        case QVariant::Double:
            return rb_float_new(variant.toDouble());
        case QVariant::Date:
        case QVariant::Time:
        case QVariant::DateTime:
        case QVariant::ByteArray:
        case QVariant::BitArray:
        case QVariant::CString:
        case QVariant::String:
            return toVALUE(variant.toString());
        case QVariant::StringList:
            return toVALUE(variant.toStringList());
        case QVariant::Map:
            return toVALUE(variant.toMap());
        case QVariant::List:
            return toVALUE(variant.toList());

        // To handle following both cases is a bit difficult
        // cause Python doesn't spend an easy possibility
        // for such large numbers (TODO maybe BigInt?). So,
        // we risk overflows here, but well...
        case QVariant::LongLong: {
            return INT2NUM((long)variant.toLongLong());
        }
        case QVariant::ULongLong:
            return UINT2NUM((unsigned long)variant.toULongLong());
        default: {
            kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
            return Qundef;
        }
    }
}

VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
{
    if(! object) {
        return 0;
    }

    if(object->getClassName() == "Kross::Api::Variant") {
        QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
        return toVALUE(v);
    }

    if(object->getClassName() == "Kross::Api::List") {
        Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
        return toVALUE((Kross::Api::List::Ptr)list);
    }

    if(object->getClassName() == "Kross::Api::Dict") {
        Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
        return toVALUE((Kross::Api::Dict::Ptr)dict);
    }

    if(RubyExtensionPrivate::s_krossObject == 0)
    {
        RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
        rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing",  (VALUE (*)(...))RubyExtension::method_missing, -1);
    }
    return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, free, new RubyExtension(object) );
}

VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
{
    VALUE l = rb_ary_new();
    uint count = list ? list->count() : 0;
    for(uint i = 0; i < count; i++)
        rb_ary_push(l, toVALUE(list->item(i)));
    return l;

}

}

}
<|endoftext|>


<commit_before>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test_file_util.h"

#include <windows.h>

#include <vector>

#include "base/file_util.h"
#include "base/scoped_handle.h"

namespace file_util {

bool EvictFileFromSystemCache(const wchar_t* file) {
  // Request exclusive access to the file and overwrite it with no buffering.
  ScopedHandle hfile(
      CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING,
                 NULL));
  if (!hfile)
    return false;

  // Execute in chunks. It could be optimized. We want to do few of these since
  // these opterations will be slow without the cache.
  char buffer[4096];
  int total_bytes = 0;
  DWORD bytes_read;
  for (;;) {
    bytes_read = 0;
    ReadFile(hfile, buffer, sizeof(buffer), &bytes_read, NULL);
    if (bytes_read == 0)
      break;

    SetFilePointer(hfile, total_bytes, 0, FILE_BEGIN);
    if (!WriteFile(hfile, buffer, bytes_read, &bytes_read, NULL))
      return false;
    total_bytes += bytes_read;
  }
  return true;
}

// Like CopyFileNoCache but recursively copies all files and subdirectories
// in the given input directory to the output directory.
bool CopyRecursiveDirNoCache(const std::wstring& source_dir,
                             const std::wstring& dest_dir) {
  // Try to create the directory if it doesn't already exist.
  if (!CreateDirectory(dest_dir)) {
    if (GetLastError() != ERROR_ALREADY_EXISTS)
      return false;
  }

  std::vector<std::wstring> files_copied;

  std::wstring src(source_dir);
  file_util::AppendToPath(&src, L"*");

  WIN32_FIND_DATA fd;
  HANDLE fh = FindFirstFile(src.c_str(), &fd);
  if (fh == INVALID_HANDLE_VALUE)
    return false;

  do {
    std::wstring cur_file(fd.cFileName);
    if (cur_file == L"." || cur_file == L"..")
      continue;  // Skip these special entries.

    std::wstring cur_source_path(source_dir);
    file_util::AppendToPath(&cur_source_path, cur_file);

    std::wstring cur_dest_path(dest_dir);
    file_util::AppendToPath(&cur_dest_path, cur_file);

    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
      // Recursively copy a subdirectory. We stripped "." and ".." already.
      if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) {
        FindClose(fh);
        return false;
      }
    } else {
      // Copy the file.
      if (!::CopyFile(cur_source_path.c_str(), cur_dest_path.c_str(), false)) {
        FindClose(fh);
        return false;
      }

      // We don't check for errors from this function, often, we are copying
      // files that are in the repository, and they will have read-only set.
      // This will prevent us from evicting from the cache, but these don't
      // matter anyway.
      EvictFileFromSystemCache(cur_dest_path.c_str());
    }
  } while (FindNextFile(fh, &fd));

  FindClose(fh);
  return true;
}

}  // namespace file_util

<commit_msg>Making sure that the buffer we use with ReadFile/WriteFile is page aligned.  Also restoring file attributes after evicting it from the cache (since the function writes to the file).<commit_after>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test_file_util.h"

#include <windows.h>

#include <vector>

#include "base/file_util.h"
#include "base/scoped_handle.h"

namespace file_util {

// We could use GetSystemInfo to get the page size, but this serves
// our purpose fine since 4K is the page size on x86 as well as x64.
static const ptrdiff_t kPageSize = 4096;

bool EvictFileFromSystemCache(const wchar_t* file) {
  // Request exclusive access to the file and overwrite it with no buffering.
  ScopedHandle file_handle(
      CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL));
  if (!file_handle)
    return false;

  // Get some attributes to restore later.
  BY_HANDLE_FILE_INFORMATION bhi = {0};
  CHECK(::GetFileInformationByHandle(file_handle, &bhi));

  // Execute in chunks. It could be optimized. We want to do few of these since
  // these operations will be slow without the cache.

  // Non-buffered reads and writes need to be sector aligned and since sector
  // sizes typically range from 512-4096 bytes, we just use the page size.
  // The buffer size is twice the size of a page (minus one) since we need to
  // get an aligned pointer into the buffer that we can use.
  char buffer[2 * kPageSize - 1];
  // Get an aligned pointer into buffer.
  char* read_write = reinterpret_cast<char*>(
      reinterpret_cast<ptrdiff_t>(buffer + kPageSize - 1) & ~(kPageSize - 1));
  DCHECK((reinterpret_cast<int>(read_write) % kPageSize) == 0);

  // If the file size isn't a multiple of kPageSize, we'll need special
  // processing.
  bool file_is_page_aligned = true;
  int total_bytes = 0;
  DWORD bytes_read, bytes_written;
  for (;;) {
    bytes_read = 0;
    ReadFile(file_handle, read_write, kPageSize, &bytes_read, NULL);
    if (bytes_read == 0)
      break;

    if (bytes_read < kPageSize) {
      // Zero out the remaining part of the buffer.
      // WriteFile will fail if we provide a buffer size that isn't a
      // sector multiple, so we'll have to write the entire buffer with
      // padded zeros and then use SetEndOfFile to truncate the file.
      ZeroMemory(read_write + bytes_read, kPageSize - bytes_read);
      file_is_page_aligned = false;
    }

    // Move back to the position we just read from.
    // Note that SetFilePointer will also fail if total_bytes isn't sector
    // aligned, but that shouldn't happen here.
    DCHECK((total_bytes % kPageSize) == 0);
    SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN);
    if (!WriteFile(file_handle, read_write, kPageSize, &bytes_written, NULL) ||
        bytes_written != kPageSize) {
      DCHECK(false);
      return false;
    }

    total_bytes += bytes_read;

    // If this is false, then we just processed the last portion of the file.
    if (!file_is_page_aligned)
      break;
  }

  if (!file_is_page_aligned) {
    // The size of the file isn't a multiple of the page size, so we'll have
    // to open the file again, this time without the FILE_FLAG_NO_BUFFERING
    // flag and use SetEndOfFile to mark EOF.
    file_handle.Set(CreateFile(file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
                               0, NULL));
    CHECK(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN) !=
          INVALID_SET_FILE_POINTER);
    CHECK(::SetEndOfFile(file_handle));
  }

  // Restore the file attributes.
  CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime,
                      &bhi.ftLastWriteTime));

  return true;
}

// Like CopyFileNoCache but recursively copies all files and subdirectories
// in the given input directory to the output directory.
bool CopyRecursiveDirNoCache(const std::wstring& source_dir,
                             const std::wstring& dest_dir) {
  // Try to create the directory if it doesn't already exist.
  if (!CreateDirectory(dest_dir)) {
    if (GetLastError() != ERROR_ALREADY_EXISTS)
      return false;
  }

  std::vector<std::wstring> files_copied;

  std::wstring src(source_dir);
  file_util::AppendToPath(&src, L"*");

  WIN32_FIND_DATA fd;
  HANDLE fh = FindFirstFile(src.c_str(), &fd);
  if (fh == INVALID_HANDLE_VALUE)
    return false;

  do {
    std::wstring cur_file(fd.cFileName);
    if (cur_file == L"." || cur_file == L"..")
      continue;  // Skip these special entries.

    std::wstring cur_source_path(source_dir);
    file_util::AppendToPath(&cur_source_path, cur_file);

    std::wstring cur_dest_path(dest_dir);
    file_util::AppendToPath(&cur_dest_path, cur_file);

    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
      // Recursively copy a subdirectory. We stripped "." and ".." already.
      if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) {
        FindClose(fh);
        return false;
      }
    } else {
      // Copy the file.
      if (!::CopyFile(cur_source_path.c_str(), cur_dest_path.c_str(), false)) {
        FindClose(fh);
        return false;
      }

      // We don't check for errors from this function, often, we are copying
      // files that are in the repository, and they will have read-only set.
      // This will prevent us from evicting from the cache, but these don't
      // matter anyway.
      EvictFileFromSystemCache(cur_dest_path.c_str());
    }
  } while (FindNextFile(fh, &fd));

  FindClose(fh);
  return true;
}

}  // namespace file_util

<|endoftext|>