<commit_before>// Copyright: 2014, 2015, Ableton AG, Berlin. All rights reserved.

#pragma once

#include <atria/xform/reduce.hpp>
#include <atria/meta/common_type.hpp>
#include <atria/estd/type_traits.hpp>
#include <atria/estd/utility.hpp>

#include <boost/optional.hpp>
#include <cassert>

namespace atria {
namespace xform {

namespace detail {

//!
// Utility to write simple transducers easily.  Most transducers have
// the following structure:
//
// @code{.hs}
//  xducer params... =     -- 1. this returns a transducer
//    \ reducer ->         -- 2. this is a transducer
//       \ state, input -> -- 3. this is transformed version of 'reducer'
//           f(params..., reducer, state, input)
// @endocde
//
// Writing functions that return functions without erasing their type
// is very cumbersome in C++11 (this is not true with decltype(auto)
// in the next standard).  The `transducer_impl` type implements the first
// two calls. The `ReducerGenT` parameter should have a nested
// template `apply` that implements the last call.  It will be
// constructed `reducer, params...` and it should be callable with
// `state, input`.
//
// @see map
// @see filter
//
template<typename ReducerGenT,
         typename ...ParamTs>
struct transducer_impl : std::tuple<ParamTs...>
{
  using base_t = std::tuple<ParamTs...>;

  template <typename ...Ts>
  constexpr transducer_impl(Ts ...ts)
    : base_t(std::move(ts)...)
  {}

  template<typename ReducerT>
  constexpr auto operator() (ReducerT&& reducer) const
    -> typename ReducerGenT::template apply<
      estd::decay_t<ReducerT>,
      estd::decay_t<ParamTs>...
    >
  {
    using indexes_t = estd::make_index_sequence<sizeof...(ParamTs)>;
    return this->make(std::forward<ReducerT>(reducer), indexes_t());
  }

  template<typename ReducerT, std::size_t...indexes_t>
  constexpr auto make(ReducerT&& reducer, estd::index_sequence<indexes_t...>) const
    -> typename ReducerGenT::template apply<
      estd::decay_t<ReducerT>,
      estd::decay_t<ParamTs>...
    >
  {
    return { std::forward<ReducerT>(reducer),
             std::get<indexes_t>(*this)... };
  }
};

struct map_reducer
{
  template <typename ReducerT,
            typename MappingT>
  struct apply
  {
    ReducerT reducer;
    MappingT mapping;

    template <typename State, typename ...Inputs>
    auto operator() (State&& s, Inputs&& ...is)
      -> decltype(reducer(std::forward<State>(s),
                          mapping(std::forward<Inputs>(is)...)))
    {
      return reducer(std::forward<State>(s),
                     mapping(std::forward<Inputs>(is)...));
    }
  };
};

struct cat_reducer
{
  template <typename ReducerT>
  struct apply
  {
    ReducerT reducer;

    template <typename State, typename ...Inputs>
    auto operator() (State&& s, Inputs&& ...is)
      -> decltype(reduce_nested(reducer,
                                std::forward<State>(s),
                                std::forward<Inputs>(is)...))
    {
      return reduce_nested(reducer,
                           std::forward<State>(s),
                           std::forward<Inputs>(is)...);
    }
  };
};

struct filter_reducer
{
  template <typename ReducerT,
            typename PredicateT>
  struct apply
  {
    ReducerT reducer;
    PredicateT predicate;

    template <typename State, typename ...Inputs>
    auto operator() (State&& s, Inputs&& ...is)
      -> decltype(true
                  ? reducer(std::forward<State>(s),
                            std::forward<Inputs>(is)...)
                  : std::forward<State>(s))
    {
      return predicate(std::forward<Inputs>(is)...)
        ? reducer(std::forward<State>(s),
                  std::forward<Inputs>(is)...)
        : std::forward<State>(s);
    }
  };
};

struct take_reducer
{
  struct tag {};

  template <typename ReducerT,
            typename IntegralT>
  struct apply
  {
    ReducerT reducer;
    IntegralT total;

    template <typename StateT, typename ...InputTs>
    auto operator() (StateT&& s, InputTs&& ...is)
      -> decltype(wrap_state<take_reducer::tag>(
                    reducer(state_unwrap(s), std::forward<InputTs>(is)...),
                    state_data(s, total) - 1))
    {
      return wrap_state<take_reducer::tag>(
        reducer(state_unwrap(s), std::forward<InputTs>(is)...),
        state_data(s, total) - 1);
    }
  };

  template <typename T>
  friend bool state_data_is_reduced(take_reducer::tag, T&& n)
  {
    return std::forward<T>(n) == 0;
  }
};

} // namespace detail

template <typename T>
using map_t = detail::transducer_impl<detail::map_reducer, T>;

//!
// Similar to clojure.core/map$1
//
template <typename MappingT>
auto map(MappingT&& mapping)
  -> map_t<estd::decay_t<MappingT> >
{
  return map_t<estd::decay_t<MappingT> > {
    std::forward<MappingT>(mapping) };
}

//!
// Similar to clojure.core/cat
//
constexpr auto cat = detail::transducer_impl<detail::cat_reducer> {};

//!
// Similar to clojure.core/mapcat$1
//
template <typename MappingT>
auto mapcat(MappingT&& mapping)
  -> decltype(comp(
                cat,
                map(std::forward<MappingT>(mapping))))
{
  return comp(
    cat,
    map(std::forward<MappingT>(mapping)));
}

template <typename T>
using take_t = detail::transducer_impl<detail::take_reducer, T>;

//!
// Similar to clojure.core/take$1
//
template <typename IntegralT>
auto take(IntegralT&& n)
  -> take_t<estd::decay_t<IntegralT> >
{
  return take_t<estd::decay_t<IntegralT> > { n };
}

template <typename T>
using filter_t = detail::transducer_impl<detail::filter_reducer, T>;

//!
// Similar to clojure.core/filter$1
// If no `ResultT` is given, the resulting reducers will try to deduce
// it from (true ? reducer(inputs...) : state), which might no be
// possible if state and inputs are incompatible.
//
template <typename ResultT=void, typename PredicateT>
auto filter(PredicateT&& predicate)
  -> filter_t<estd::decay_t<PredicateT> >
{
  return filter_t<estd::decay_t<PredicateT> > {
    std::forward<PredicateT>(predicate) };
}

} // namespace xform
} // namespace atria
<commit_msg>xform: make all compilers happy about `transducer_impl` constructors<commit_after>// Copyright: 2014, 2015, Ableton AG, Berlin. All rights reserved.

#pragma once

#include <atria/xform/reduce.hpp>
#include <atria/meta/common_type.hpp>
#include <atria/estd/type_traits.hpp>
#include <atria/estd/utility.hpp>

#include <boost/optional.hpp>
#include <cassert>

namespace atria {
namespace xform {

namespace detail {

//!
// Utility to write simple transducers easily.  Most transducers have
// the following structure:
//
// @code{.hs}
//  xducer params... =     -- 1. this returns a transducer
//    \ reducer ->         -- 2. this is a transducer
//       \ state, input -> -- 3. this is transformed version of 'reducer'
//           f(params..., reducer, state, input)
// @endocde
//
// Writing functions that return functions without erasing their type
// is very cumbersome in C++11 (this is not true with decltype(auto)
// in the next standard).  The `transducer_impl` type implements the first
// two calls. The `ReducerGenT` parameter should have a nested
// template `apply` that implements the last call.  It will be
// constructed `reducer, params...` and it should be callable with
// `state, input`.
//
// @see map
// @see filter
//
template<typename ReducerGenT,
         typename ...ParamTs>
struct transducer_impl : std::tuple<ParamTs...>
{
  using base_t = std::tuple<ParamTs...>;

  // using base_t::base_t;
  transducer_impl() = default;
  transducer_impl(const transducer_impl&) = default;
  transducer_impl(transducer_impl&&) = default;
  transducer_impl& operator=(const transducer_impl&) = default;
  transducer_impl& operator=(transducer_impl&&) = default;

  template <typename ...Ts>
  transducer_impl(Ts ...ts)
    : base_t(std::move(ts)...)
  {}

  template<typename ReducerT>
  constexpr auto operator() (ReducerT&& reducer) const
    -> typename ReducerGenT::template apply<
      estd::decay_t<ReducerT>,
      estd::decay_t<ParamTs>...
    >
  {
    using indexes_t = estd::make_index_sequence<sizeof...(ParamTs)>;
    return this->make(std::forward<ReducerT>(reducer), indexes_t());
  }

  template<typename ReducerT, std::size_t...indexes_t>
  constexpr auto make(ReducerT&& reducer, estd::index_sequence<indexes_t...>) const
    -> typename ReducerGenT::template apply<
      estd::decay_t<ReducerT>,
      estd::decay_t<ParamTs>...
    >
  {
    return { std::forward<ReducerT>(reducer),
             std::get<indexes_t>(*this)... };
  }
};

struct map_reducer
{
  template <typename ReducerT,
            typename MappingT>
  struct apply
  {
    ReducerT reducer;
    MappingT mapping;

    template <typename State, typename ...Inputs>
    auto operator() (State&& s, Inputs&& ...is)
      -> decltype(reducer(std::forward<State>(s),
                          mapping(std::forward<Inputs>(is)...)))
    {
      return reducer(std::forward<State>(s),
                     mapping(std::forward<Inputs>(is)...));
    }
  };
};

struct cat_reducer
{
  template <typename ReducerT>
  struct apply
  {
    ReducerT reducer;

    template <typename State, typename ...Inputs>
    auto operator() (State&& s, Inputs&& ...is)
      -> decltype(reduce_nested(reducer,
                                std::forward<State>(s),
                                std::forward<Inputs>(is)...))
    {
      return reduce_nested(reducer,
                           std::forward<State>(s),
                           std::forward<Inputs>(is)...);
    }
  };
};

struct filter_reducer
{
  template <typename ReducerT,
            typename PredicateT>
  struct apply
  {
    ReducerT reducer;
    PredicateT predicate;

    template <typename State, typename ...Inputs>
    auto operator() (State&& s, Inputs&& ...is)
      -> decltype(true
                  ? reducer(std::forward<State>(s),
                            std::forward<Inputs>(is)...)
                  : std::forward<State>(s))
    {
      return predicate(std::forward<Inputs>(is)...)
        ? reducer(std::forward<State>(s),
                  std::forward<Inputs>(is)...)
        : std::forward<State>(s);
    }
  };
};

struct take_reducer
{
  struct tag {};

  template <typename ReducerT,
            typename IntegralT>
  struct apply
  {
    ReducerT reducer;
    IntegralT total;

    template <typename StateT, typename ...InputTs>
    auto operator() (StateT&& s, InputTs&& ...is)
      -> decltype(wrap_state<take_reducer::tag>(
                    reducer(state_unwrap(s), std::forward<InputTs>(is)...),
                    state_data(s, total) - 1))
    {
      return wrap_state<take_reducer::tag>(
        reducer(state_unwrap(s), std::forward<InputTs>(is)...),
        state_data(s, total) - 1);
    }
  };

  template <typename T>
  friend bool state_data_is_reduced(take_reducer::tag, T&& n)
  {
    return std::forward<T>(n) == 0;
  }
};

} // namespace detail

template <typename T>
using map_t = detail::transducer_impl<detail::map_reducer, T>;

//!
// Similar to clojure.core/map$1
//
template <typename MappingT>
auto map(MappingT&& mapping)
  -> map_t<estd::decay_t<MappingT> >
{
  return map_t<estd::decay_t<MappingT> > {
    std::forward<MappingT>(mapping) };
}

//!
// Similar to clojure.core/cat
//
constexpr auto cat = detail::transducer_impl<detail::cat_reducer> {};

//!
// Similar to clojure.core/mapcat$1
//
template <typename MappingT>
auto mapcat(MappingT&& mapping)
  -> decltype(comp(
                cat,
                map(std::forward<MappingT>(mapping))))
{
  return comp(
    cat,
    map(std::forward<MappingT>(mapping)));
}

template <typename T>
using take_t = detail::transducer_impl<detail::take_reducer, T>;

//!
// Similar to clojure.core/take$1
//
template <typename IntegralT>
auto take(IntegralT&& n)
  -> take_t<estd::decay_t<IntegralT> >
{
  return take_t<estd::decay_t<IntegralT> > { n };
}

template <typename T>
using filter_t = detail::transducer_impl<detail::filter_reducer, T>;

//!
// Similar to clojure.core/filter$1
// If no `ResultT` is given, the resulting reducers will try to deduce
// it from (true ? reducer(inputs...) : state), which might no be
// possible if state and inputs are incompatible.
//
template <typename ResultT=void, typename PredicateT>
auto filter(PredicateT&& predicate)
  -> filter_t<estd::decay_t<PredicateT> >
{
  return filter_t<estd::decay_t<PredicateT> > {
    std::forward<PredicateT>(predicate) };
}

} // namespace xform
} // namespace atria
<|endoftext|>


<commit_before>// audio_dev_creator.cpp
//
// These functions coordinate the presence of multiple supported audio device
// types on a single machine.  It builds a database of creators and recognizers
// based upon the compile options, and uses that to determine which object
// to build based on the passed-in descriptor.

#include <ugens.h>

#ifdef LINUX
#include "SinglePortOSSAudioDevice.h"
#include "MultiPortOSSAudioDevice.h"
#include "TestAudioDevice.h"
#endif
#ifdef ALSA
#include "ALSAAudioDevice.h"
#endif
#ifdef NETAUDIO
#include "NetAudioDevice.h"
#endif
#ifdef MACOSX
#include "OSXAudioDevice.h"
#endif
#ifdef SGI
#include "SGIAudioDevice.h"
#endif
#ifdef JACK
#include "JackAudioDevice.h"
#endif

#include "AudioIODevice.h"

typedef AudioDevice * (*CreatorFun)(const char *, const char *, int mode);

struct AudioDevEntry {
	bool		(*recognizer)(const char *desc);
	CreatorFun	creator;
};

static const AudioDevEntry s_AudioDevEntries[] = {
#ifdef NETAUDIO
	{ &NetAudioDevice::recognize, &NetAudioDevice::create },
#endif
#ifdef MACOSX
	{ &OSXAudioDevice::recognize, &OSXAudioDevice::create },
#endif
#ifdef ALSA
	{ &ALSAAudioDevice::recognize, &ALSAAudioDevice::create },
#endif
#ifdef LINUX
#ifdef TEST_AUDIO_DEVICE
	{ &TestAudioDevice::recognize, &TestAudioDevice::create },
#endif
	{ &MultiPortOSSAudioDevice::recognize, &MultiPortOSSAudioDevice::create },
	{ &SinglePortOSSAudioDevice::recognize, &SinglePortOSSAudioDevice::create },
#endif
#ifdef SGI
	{ &SGIAudioDevice::recognize, &SGIAudioDevice::create },
#endif
#ifdef JACK
	{ &JackAudioDevice::recognize, &JackAudioDevice::create },
#endif
	{ NULL, NULL }
};

AudioDevice *
createAudioDevice(const char *inputDesc,
				  const char *outputDesc,
				  bool recording, bool playing)
{
	CreatorFun iCreator = NULL, oCreator = NULL;
	AudioDevice *theDevice = NULL;
	const AudioDevEntry *currentEntry;

	if (recording) {
		// Search for creator for inputDesc.
		for (currentEntry = s_AudioDevEntries;
			 currentEntry->recognizer != NULL;
			 ++currentEntry)
		{
			if (currentEntry->recognizer(inputDesc) == true) {
				iCreator = currentEntry->creator;
				break;
			}
		}
		if (iCreator == NULL) {
			fprintf(stderr, "Unrecognized input device name '%s'.\n",
				inputDesc);
			return NULL;
		}
	}
	if (playing) {
		// Search for creator for outputDesc.
		for (currentEntry = s_AudioDevEntries;
			 currentEntry->recognizer != NULL;
			 ++currentEntry)
		{
			if (currentEntry->recognizer(outputDesc) == true) {
				oCreator = currentEntry->creator;
				break;
			}
		}
		if (oCreator == NULL) {
			fprintf(stderr, "Unrecognized output device name '%s'.\n",
				outputDesc);
			return NULL;
		}
	}
	// Handle full duplex requests.
	if (recording && playing) {
		AudioDevice *fullDuplexDevice = NULL;
		// If same HW for input and output, try to create a single
		// full-duplex device.
		if (iCreator == oCreator) {
			fullDuplexDevice = iCreator(inputDesc,
										outputDesc,
										AudioDevice::RecordPlayback);

		}
		// If that failed, OR the input and output HW are different, create
		// a compound device from the two, and drive it via input.
		if (fullDuplexDevice == NULL) {
			AudioDevice *inDev = iCreator(inputDesc, NULL, AudioDevice::Record);
			AudioDevice *outDev = oCreator(NULL, outputDesc, AudioDevice::Playback);
			if (!inDev) {
				fprintf(stderr, "Failed to create input audio device!\n");
				delete outDev;
				return NULL;
			}
			else if (!outDev) {
				fprintf(stderr, "Failed to create output audio device!\n");
				delete inDev;
				return NULL;
			}
			// Unfortunately, we still need a hack here:  Full duplex network audio
			// must select on input (i.e., input device must be active, output
			// device passive) but all others must do it the other way.

			bool forceInputActive = false;
#ifdef NETAUDIO
			if (iCreator == NetAudioDevice::create)
				forceInputActive = true;
#endif

			fullDuplexDevice = new AudioIODevice(inDev, outDev, forceInputActive);
		}
		theDevice = fullDuplexDevice;
	}
	// Handle half-duplex requests.
	else theDevice = (recording) ?
						iCreator(inputDesc, NULL, AudioDevice::Record)
						: oCreator(NULL, outputDesc, AudioDevice::Playback);

	return theDevice;
}
<commit_msg>Move JACK function ptr list so that JACK can be selected on OS X.<commit_after>// audio_dev_creator.cpp
//
// These functions coordinate the presence of multiple supported audio device
// types on a single machine.  It builds a database of creators and recognizers
// based upon the compile options, and uses that to determine which object
// to build based on the passed-in descriptor.

#include <ugens.h>

#ifdef LINUX
#include "SinglePortOSSAudioDevice.h"
#include "MultiPortOSSAudioDevice.h"
#include "TestAudioDevice.h"
#endif
#ifdef ALSA
#include "ALSAAudioDevice.h"
#endif
#ifdef NETAUDIO
#include "NetAudioDevice.h"
#endif
#ifdef MACOSX
#include "OSXAudioDevice.h"
#endif
#ifdef SGI
#include "SGIAudioDevice.h"
#endif
#ifdef JACK
#include "JackAudioDevice.h"
#endif

#include "AudioIODevice.h"

typedef AudioDevice * (*CreatorFun)(const char *, const char *, int mode);

struct AudioDevEntry {
	bool		(*recognizer)(const char *desc);
	CreatorFun	creator;
};

static const AudioDevEntry s_AudioDevEntries[] = {
#ifdef NETAUDIO
	{ &NetAudioDevice::recognize, &NetAudioDevice::create },
#endif
#ifdef JACK // before MACOSX, since OSXAudioDevice::recognize matches anything
	{ &JackAudioDevice::recognize, &JackAudioDevice::create },
#endif
#ifdef MACOSX
	{ &OSXAudioDevice::recognize, &OSXAudioDevice::create },
#endif
#ifdef ALSA
	{ &ALSAAudioDevice::recognize, &ALSAAudioDevice::create },
#endif
#ifdef LINUX
#ifdef TEST_AUDIO_DEVICE
	{ &TestAudioDevice::recognize, &TestAudioDevice::create },
#endif
	{ &MultiPortOSSAudioDevice::recognize, &MultiPortOSSAudioDevice::create },
	{ &SinglePortOSSAudioDevice::recognize, &SinglePortOSSAudioDevice::create },
#endif
#ifdef SGI
	{ &SGIAudioDevice::recognize, &SGIAudioDevice::create },
#endif
	{ NULL, NULL }
};

AudioDevice *
createAudioDevice(const char *inputDesc,
				  const char *outputDesc,
				  bool recording, bool playing)
{
	CreatorFun iCreator = NULL, oCreator = NULL;
	AudioDevice *theDevice = NULL;
	const AudioDevEntry *currentEntry;

	if (recording) {
		// Search for creator for inputDesc.
		for (currentEntry = s_AudioDevEntries;
			 currentEntry->recognizer != NULL;
			 ++currentEntry)
		{
			if (currentEntry->recognizer(inputDesc) == true) {
				iCreator = currentEntry->creator;
				break;
			}
		}
		if (iCreator == NULL) {
			fprintf(stderr, "Unrecognized input device name '%s'.\n",
				inputDesc);
			return NULL;
		}
	}
	if (playing) {
		// Search for creator for outputDesc.
		for (currentEntry = s_AudioDevEntries;
			 currentEntry->recognizer != NULL;
			 ++currentEntry)
		{
			if (currentEntry->recognizer(outputDesc) == true) {
				oCreator = currentEntry->creator;
				break;
			}
		}
		if (oCreator == NULL) {
			fprintf(stderr, "Unrecognized output device name '%s'.\n",
				outputDesc);
			return NULL;
		}
	}
	// Handle full duplex requests.
	if (recording && playing) {
		AudioDevice *fullDuplexDevice = NULL;
		// If same HW for input and output, try to create a single
		// full-duplex device.
		if (iCreator == oCreator) {
			fullDuplexDevice = iCreator(inputDesc,
										outputDesc,
										AudioDevice::RecordPlayback);

		}
		// If that failed, OR the input and output HW are different, create
		// a compound device from the two, and drive it via input.
		if (fullDuplexDevice == NULL) {
			AudioDevice *inDev = iCreator(inputDesc, NULL, AudioDevice::Record);
			AudioDevice *outDev = oCreator(NULL, outputDesc, AudioDevice::Playback);
			if (!inDev) {
				fprintf(stderr, "Failed to create input audio device!\n");
				delete outDev;
				return NULL;
			}
			else if (!outDev) {
				fprintf(stderr, "Failed to create output audio device!\n");
				delete inDev;
				return NULL;
			}
			// Unfortunately, we still need a hack here:  Full duplex network audio
			// must select on input (i.e., input device must be active, output
			// device passive) but all others must do it the other way.

			bool forceInputActive = false;
#ifdef NETAUDIO
			if (iCreator == NetAudioDevice::create)
				forceInputActive = true;
#endif

			fullDuplexDevice = new AudioIODevice(inDev, outDev, forceInputActive);
		}
		theDevice = fullDuplexDevice;
	}
	// Handle half-duplex requests.
	else theDevice = (recording) ?
						iCreator(inputDesc, NULL, AudioDevice::Record)
						: oCreator(NULL, outputDesc, AudioDevice::Playback);

	return theDevice;
}
<|endoftext|>


<commit_before><commit_msg>- yes/no dialog: set default button to "no" if requested (value == 0)<commit_after><|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id$
 */

#include <windows.h>

#include <xercesc/util/FileManagers/WindowsFileMgr.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>

XERCES_CPP_NAMESPACE_BEGIN

static bool isBackSlash(XMLCh c) {
    return c == chBackSlash ||
           c == chYenSign   ||
           c == chWonSign;
}

WindowsFileMgr::WindowsFileMgr()
{
    // Figure out if we are on NT and save that flag for later use
    OSVERSIONINFO   OSVer;
    OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    ::GetVersionEx(&OSVer);
    _onNT = (OSVer.dwPlatformId == VER_PLATFORM_WIN32_NT);
}

WindowsFileMgr::~WindowsFileMgr()
{
}

FileHandle
WindowsFileMgr::fileOpen(const XMLCh* fileName, bool toWrite, MemoryManager* const manager)
{
    // Watch for obvious wierdness
    if (!fileName)
        return 0;

    //
    //  We have to play a little trick here. If its /x:.....
    //  style fully qualified path, we have to toss the leading /
    //  character.
    //
    const XMLCh* nameToOpen = fileName;
    if (*fileName == chForwardSlash)
    {
        if (XMLString::stringLen(fileName) > 3)
        {
            if (*(fileName + 2) == chColon)
            {
                const XMLCh chDrive = *(fileName + 1);
                if (((chDrive >= chLatin_A) && (chDrive <= chLatin_Z))
                ||  ((chDrive >= chLatin_a) && (chDrive <= chLatin_z)))
                {
                    nameToOpen = fileName + 1;
                }
            }

            // Similarly for UNC paths
            if ( *(fileName + 1) == *(fileName + 2) &&
                 (*(fileName + 1) == chForwardSlash ||
                  *(fileName + 1) == chBackSlash) )
            {
                nameToOpen = fileName + 1;
            }
        }
    }

    //  Ok, this might look stupid but its a semi-expedient way to deal
    //  with a thorny problem. Shift-JIS and some other Asian encodings
    //  are fundamentally broken and map both the backslash and the Yen
    //  sign to the same code point. Transcoders have to pick one or the
    //  other to map '\' to Unicode and tend to choose the Yen sign.
    //
    //  Unicode Yen or Won signs as directory separators will fail.
    //
    //  So, we will check this path name for Yen or won signs and, if they are
    //  there, we'll replace them with slashes.
    //
    //  A further twist:  we replace Yen and Won with forward slashes rather
    //   than back slashes.  Either form of slash will work as a directory
    //   separator.  On Win 95 and 98, though, Unicode back-slashes may
    //   fail to transode back to 8-bit 0x5C with some Unicode converters
    //   to  some of the problematic code pages.  Forward slashes always
    //   transcode correctly back to 8 bit char * form.
    //
    XMLCh *tmpUName = 0;

    const XMLCh* srcPtr = nameToOpen;
    while (*srcPtr)
    {
        if (*srcPtr == chYenSign ||
            *srcPtr == chWonSign)
            break;
        srcPtr++;
    }

    //
    //  If we found a yen, then we have to create a temp file name. Else
    //  go with the file name as is and save the overhead.
    //
    if (*srcPtr)
    {
        tmpUName = XMLString::replicate(nameToOpen, manager);

        XMLCh* tmpPtr = tmpUName;
        while (*tmpPtr)
        {
            if (*tmpPtr == chYenSign ||
                *tmpPtr == chWonSign)
                *tmpPtr = chForwardSlash;
            tmpPtr++;
        }
        nameToOpen = tmpUName;
    }
    FileHandle retVal = 0;
    if (_onNT)
    {
        retVal = ::CreateFileW
            (
            (LPCWSTR) nameToOpen
            , toWrite?GENERIC_WRITE:GENERIC_READ
            , FILE_SHARE_READ
            , 0
            , toWrite?CREATE_ALWAYS:OPEN_EXISTING
            , toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
            , 0
            );
    }
    else
    {
        //
        //  We are Win 95 / 98.  Take the Unicode file name back to (char *)
        //    so that we can open it.
        //
        char* tmpName = XMLString::transcode(nameToOpen, manager);
        retVal = ::CreateFileA
            (
            tmpName
            , toWrite?GENERIC_WRITE:GENERIC_READ
            , FILE_SHARE_READ
            , 0
            , toWrite?CREATE_ALWAYS:OPEN_EXISTING
            , toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
            , 0
            );
        manager->deallocate(tmpName);//delete [] tmpName;
    }

    if (tmpUName)
        manager->deallocate(tmpUName);//delete [] tmpUName;

    if (retVal == INVALID_HANDLE_VALUE)
        return 0;

    return retVal;
}

FileHandle
WindowsFileMgr::fileOpen(const char* path, bool toWrite, MemoryManager* const manager)
{
    XMLCh* tmpFileName = XMLString::transcode(path, manager);
    ArrayJanitor<XMLCh> janText(tmpFileName, manager);
    return fileOpen(tmpFileName, toWrite, manager);
}

FileHandle
WindowsFileMgr::openStdIn(MemoryManager* const manager)
{
    //
    //  Get the standard input handle. Duplicate it and return that copy
    //  since the outside world cannot tell the difference and will shut
    //  down this handle when its done with it. If we gave out the orignal,
    //  shutting it would prevent any further output.
    //
    HANDLE stdInOrg = ::GetStdHandle(STD_INPUT_HANDLE);
    if (stdInOrg == INVALID_HANDLE_VALUE) {
        XMLCh stdinStr[] = {chLatin_s, chLatin_t, chLatin_d, chLatin_i, chLatin_n, chNull};
        ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, stdinStr, manager);
    }

    HANDLE retHandle;
    if (!::DuplicateHandle
    (
        ::GetCurrentProcess()
        , stdInOrg
        , ::GetCurrentProcess()
        , &retHandle
        , 0
        , FALSE
        , DUPLICATE_SAME_ACCESS))
    {
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotDupHandle, manager);
    }
    return retHandle;
}

void
WindowsFileMgr::fileClose(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    if (!::CloseHandle(f))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile, manager);
}

void
WindowsFileMgr::fileReset(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    // Seek to the start of the file
    if (::SetFilePointer(f, 0, 0, FILE_BEGIN) == 0xFFFFFFFF)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile, manager);
}

XMLFilePos
WindowsFileMgr::curPos(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    // Get the current position
    LONG high=0;
    DWORD low = ::SetFilePointer(f, 0, &high, FILE_CURRENT);
    if (low == INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);

    return (((XMLFilePos)high) << 32) | low;
}

XMLFilePos
WindowsFileMgr::fileSize(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    DWORD high=0;
    DWORD low=GetFileSize(f, &high);
    if(low==INVALID_FILE_SIZE && GetLastError()!=NO_ERROR)
        // TODO: find a better exception
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);

    return (((XMLFilePos)high) << 32) | low;
}

XMLSize_t
WindowsFileMgr::fileRead(FileHandle f, XMLSize_t byteCount, XMLByte* buffer, MemoryManager* const manager)
{
    if (!f || !buffer)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    DWORD bytesRead = 0;
    if (!::ReadFile(f, buffer, (DWORD)byteCount, &bytesRead, 0))
    {
        //
        //  Check specially for a broken pipe error. If we get this, it just
        //  means no more data from the pipe, so return zero.
        //
        if (::GetLastError() != ERROR_BROKEN_PIPE)
            ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile, manager);
    }
    return (unsigned int)bytesRead;
}

void
WindowsFileMgr::fileWrite(FileHandle f, XMLSize_t byteCount, const XMLByte* buffer, MemoryManager* const manager)
{
    if (!f || !buffer)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    const XMLByte* tmpFlush = buffer;

    while (byteCount > 0)
    {
        DWORD bytesWritten = 0;
        if (!::WriteFile(f, tmpFlush, (DWORD)byteCount, &bytesWritten, 0))
            ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotWriteToFile, manager);

        tmpFlush+=bytesWritten;
        byteCount-=bytesWritten;
    }
}

XMLCh*
WindowsFileMgr::getFullPath(const XMLCh* const srcPath, MemoryManager* const manager)
{
    //
    //  If we are on NT, then use wide character APIs, else use ASCII APIs.
    //  We have to do it manually since we are only built in ASCII mode from
    //  the standpoint of the APIs.
    //
    if (_onNT)
    {
        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 1024;
        XMLCh tmpPath[bufSize + 1];

        XMLCh* namePart = 0;
        if (!::GetFullPathNameW((LPCWSTR)srcPath, bufSize, (LPWSTR)tmpPath, (LPWSTR*)&namePart))
            return 0;

        // Return a copy of the path
        return XMLString::replicate(tmpPath, manager);
    }
     else
    {
        // Transcode the incoming string
        char* tmpSrcPath = XMLString::transcode(srcPath, manager);
        ArrayJanitor<char> janSrcPath(tmpSrcPath, manager);

        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 511;
        char tmpPath[511 + 1];

        char* namePart = 0;
        if (!::GetFullPathNameA(tmpSrcPath, bufSize, tmpPath, &namePart))
            return 0;

        // Return a transcoded copy of the path
        return XMLString::transcode(tmpPath, manager);
    }
}

XMLCh*
WindowsFileMgr::getCurrentDirectory(MemoryManager* const manager)
{
    //
    //  If we are on NT, then use wide character APIs, else use ASCII APIs.
    //  We have to do it manually since we are only built in ASCII mode from
    //  the standpoint of the APIs.
    //
    if (_onNT)
    {
        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 1024;
        XMLCh tmpPath[bufSize + 1];

        if (!::GetCurrentDirectoryW(bufSize, (LPWSTR)tmpPath))
            return 0;

        // Return a copy of the path
        return XMLString::replicate(tmpPath, manager);
    }
     else
    {
        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 511;
        char tmpPath[511 + 1];

        if (!::GetCurrentDirectoryA(bufSize, tmpPath))
            return 0;

        // Return a transcoded copy of the path
        return XMLString::transcode(tmpPath, manager);
    }
}

bool
WindowsFileMgr::isRelative(const XMLCh* const toCheck, MemoryManager* const /*manager*/)
{
    // Check for pathological case of empty path
    if (!toCheck || !toCheck[0])
        return false;

    //
    //  If its starts with a drive, then it cannot be relative. Note that
    //  we checked the drive not being empty above, so worst case its one
    //  char long and the check of the 1st char will fail because its really
    //  a null character.
    //
    if (toCheck[1] == chColon)
    {
        if (((toCheck[0] >= chLatin_A) && (toCheck[0] <= chLatin_Z))
        ||  ((toCheck[0] >= chLatin_a) && (toCheck[0] <= chLatin_z)))
        {
            return false;
        }
    }

    //
    //  If it starts with a double slash, then it cannot be relative since
    //  it's a remote file.
    //
    if (isBackSlash(toCheck[0]))
        return false;

    // Else assume its a relative path
    return true;
}

XERCES_CPP_NAMESPACE_END

<commit_msg>Be compatible with Platform SDK older than 2003<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id$
 */

#include <windows.h>

#include <xercesc/util/FileManagers/WindowsFileMgr.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>

#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

XERCES_CPP_NAMESPACE_BEGIN

static bool isBackSlash(XMLCh c) {
    return c == chBackSlash ||
           c == chYenSign   ||
           c == chWonSign;
}

WindowsFileMgr::WindowsFileMgr()
{
    // Figure out if we are on NT and save that flag for later use
    OSVERSIONINFO   OSVer;
    OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    ::GetVersionEx(&OSVer);
    _onNT = (OSVer.dwPlatformId == VER_PLATFORM_WIN32_NT);
}

WindowsFileMgr::~WindowsFileMgr()
{
}

FileHandle
WindowsFileMgr::fileOpen(const XMLCh* fileName, bool toWrite, MemoryManager* const manager)
{
    // Watch for obvious wierdness
    if (!fileName)
        return 0;

    //
    //  We have to play a little trick here. If its /x:.....
    //  style fully qualified path, we have to toss the leading /
    //  character.
    //
    const XMLCh* nameToOpen = fileName;
    if (*fileName == chForwardSlash)
    {
        if (XMLString::stringLen(fileName) > 3)
        {
            if (*(fileName + 2) == chColon)
            {
                const XMLCh chDrive = *(fileName + 1);
                if (((chDrive >= chLatin_A) && (chDrive <= chLatin_Z))
                ||  ((chDrive >= chLatin_a) && (chDrive <= chLatin_z)))
                {
                    nameToOpen = fileName + 1;
                }
            }

            // Similarly for UNC paths
            if ( *(fileName + 1) == *(fileName + 2) &&
                 (*(fileName + 1) == chForwardSlash ||
                  *(fileName + 1) == chBackSlash) )
            {
                nameToOpen = fileName + 1;
            }
        }
    }

    //  Ok, this might look stupid but its a semi-expedient way to deal
    //  with a thorny problem. Shift-JIS and some other Asian encodings
    //  are fundamentally broken and map both the backslash and the Yen
    //  sign to the same code point. Transcoders have to pick one or the
    //  other to map '\' to Unicode and tend to choose the Yen sign.
    //
    //  Unicode Yen or Won signs as directory separators will fail.
    //
    //  So, we will check this path name for Yen or won signs and, if they are
    //  there, we'll replace them with slashes.
    //
    //  A further twist:  we replace Yen and Won with forward slashes rather
    //   than back slashes.  Either form of slash will work as a directory
    //   separator.  On Win 95 and 98, though, Unicode back-slashes may
    //   fail to transode back to 8-bit 0x5C with some Unicode converters
    //   to  some of the problematic code pages.  Forward slashes always
    //   transcode correctly back to 8 bit char * form.
    //
    XMLCh *tmpUName = 0;

    const XMLCh* srcPtr = nameToOpen;
    while (*srcPtr)
    {
        if (*srcPtr == chYenSign ||
            *srcPtr == chWonSign)
            break;
        srcPtr++;
    }

    //
    //  If we found a yen, then we have to create a temp file name. Else
    //  go with the file name as is and save the overhead.
    //
    if (*srcPtr)
    {
        tmpUName = XMLString::replicate(nameToOpen, manager);

        XMLCh* tmpPtr = tmpUName;
        while (*tmpPtr)
        {
            if (*tmpPtr == chYenSign ||
                *tmpPtr == chWonSign)
                *tmpPtr = chForwardSlash;
            tmpPtr++;
        }
        nameToOpen = tmpUName;
    }
    FileHandle retVal = 0;
    if (_onNT)
    {
        retVal = ::CreateFileW
            (
            (LPCWSTR) nameToOpen
            , toWrite?GENERIC_WRITE:GENERIC_READ
            , FILE_SHARE_READ
            , 0
            , toWrite?CREATE_ALWAYS:OPEN_EXISTING
            , toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
            , 0
            );
    }
    else
    {
        //
        //  We are Win 95 / 98.  Take the Unicode file name back to (char *)
        //    so that we can open it.
        //
        char* tmpName = XMLString::transcode(nameToOpen, manager);
        retVal = ::CreateFileA
            (
            tmpName
            , toWrite?GENERIC_WRITE:GENERIC_READ
            , FILE_SHARE_READ
            , 0
            , toWrite?CREATE_ALWAYS:OPEN_EXISTING
            , toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
            , 0
            );
        manager->deallocate(tmpName);//delete [] tmpName;
    }

    if (tmpUName)
        manager->deallocate(tmpUName);//delete [] tmpUName;

    if (retVal == INVALID_HANDLE_VALUE)
        return 0;

    return retVal;
}

FileHandle
WindowsFileMgr::fileOpen(const char* path, bool toWrite, MemoryManager* const manager)
{
    XMLCh* tmpFileName = XMLString::transcode(path, manager);
    ArrayJanitor<XMLCh> janText(tmpFileName, manager);
    return fileOpen(tmpFileName, toWrite, manager);
}

FileHandle
WindowsFileMgr::openStdIn(MemoryManager* const manager)
{
    //
    //  Get the standard input handle. Duplicate it and return that copy
    //  since the outside world cannot tell the difference and will shut
    //  down this handle when its done with it. If we gave out the orignal,
    //  shutting it would prevent any further output.
    //
    HANDLE stdInOrg = ::GetStdHandle(STD_INPUT_HANDLE);
    if (stdInOrg == INVALID_HANDLE_VALUE) {
        XMLCh stdinStr[] = {chLatin_s, chLatin_t, chLatin_d, chLatin_i, chLatin_n, chNull};
        ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, stdinStr, manager);
    }

    HANDLE retHandle;
    if (!::DuplicateHandle
    (
        ::GetCurrentProcess()
        , stdInOrg
        , ::GetCurrentProcess()
        , &retHandle
        , 0
        , FALSE
        , DUPLICATE_SAME_ACCESS))
    {
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotDupHandle, manager);
    }
    return retHandle;
}

void
WindowsFileMgr::fileClose(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    if (!::CloseHandle(f))
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile, manager);
}

void
WindowsFileMgr::fileReset(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    // Seek to the start of the file
    if (::SetFilePointer(f, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile, manager);
}

XMLFilePos
WindowsFileMgr::curPos(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    // Get the current position
    LONG high=0;
    DWORD low = ::SetFilePointer(f, 0, &high, FILE_CURRENT);
    if (low == INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR)
        ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);

    return (((XMLFilePos)high) << 32) | low;
}

XMLFilePos
WindowsFileMgr::fileSize(FileHandle f, MemoryManager* const manager)
{
    if (!f)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    DWORD high=0;
    DWORD low=::GetFileSize(f, &high);
    if(low==INVALID_FILE_SIZE && GetLastError()!=NO_ERROR)
        // TODO: find a better exception
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);

    return (((XMLFilePos)high) << 32) | low;
}

XMLSize_t
WindowsFileMgr::fileRead(FileHandle f, XMLSize_t byteCount, XMLByte* buffer, MemoryManager* const manager)
{
    if (!f || !buffer)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    DWORD bytesRead = 0;
    if (!::ReadFile(f, buffer, (DWORD)byteCount, &bytesRead, 0))
    {
        //
        //  Check specially for a broken pipe error. If we get this, it just
        //  means no more data from the pipe, so return zero.
        //
        if (::GetLastError() != ERROR_BROKEN_PIPE)
            ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile, manager);
    }
    return (unsigned int)bytesRead;
}

void
WindowsFileMgr::fileWrite(FileHandle f, XMLSize_t byteCount, const XMLByte* buffer, MemoryManager* const manager)
{
    if (!f || !buffer)
		ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);

    const XMLByte* tmpFlush = buffer;

    while (byteCount > 0)
    {
        DWORD bytesWritten = 0;
        if (!::WriteFile(f, tmpFlush, (DWORD)byteCount, &bytesWritten, 0))
            ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotWriteToFile, manager);

        tmpFlush+=bytesWritten;
        byteCount-=bytesWritten;
    }
}

XMLCh*
WindowsFileMgr::getFullPath(const XMLCh* const srcPath, MemoryManager* const manager)
{
    //
    //  If we are on NT, then use wide character APIs, else use ASCII APIs.
    //  We have to do it manually since we are only built in ASCII mode from
    //  the standpoint of the APIs.
    //
    if (_onNT)
    {
        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 1024;
        XMLCh tmpPath[bufSize + 1];

        XMLCh* namePart = 0;
        if (!::GetFullPathNameW((LPCWSTR)srcPath, bufSize, (LPWSTR)tmpPath, (LPWSTR*)&namePart))
            return 0;

        // Return a copy of the path
        return XMLString::replicate(tmpPath, manager);
    }
     else
    {
        // Transcode the incoming string
        char* tmpSrcPath = XMLString::transcode(srcPath, manager);
        ArrayJanitor<char> janSrcPath(tmpSrcPath, manager);

        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 511;
        char tmpPath[511 + 1];

        char* namePart = 0;
        if (!::GetFullPathNameA(tmpSrcPath, bufSize, tmpPath, &namePart))
            return 0;

        // Return a transcoded copy of the path
        return XMLString::transcode(tmpPath, manager);
    }
}

XMLCh*
WindowsFileMgr::getCurrentDirectory(MemoryManager* const manager)
{
    //
    //  If we are on NT, then use wide character APIs, else use ASCII APIs.
    //  We have to do it manually since we are only built in ASCII mode from
    //  the standpoint of the APIs.
    //
    if (_onNT)
    {
        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 1024;
        XMLCh tmpPath[bufSize + 1];

        if (!::GetCurrentDirectoryW(bufSize, (LPWSTR)tmpPath))
            return 0;

        // Return a copy of the path
        return XMLString::replicate(tmpPath, manager);
    }
     else
    {
        // Use a local buffer that is big enough for the largest legal path
        const unsigned int bufSize = 511;
        char tmpPath[511 + 1];

        if (!::GetCurrentDirectoryA(bufSize, tmpPath))
            return 0;

        // Return a transcoded copy of the path
        return XMLString::transcode(tmpPath, manager);
    }
}

bool
WindowsFileMgr::isRelative(const XMLCh* const toCheck, MemoryManager* const /*manager*/)
{
    // Check for pathological case of empty path
    if (!toCheck || !toCheck[0])
        return false;

    //
    //  If its starts with a drive, then it cannot be relative. Note that
    //  we checked the drive not being empty above, so worst case its one
    //  char long and the check of the 1st char will fail because its really
    //  a null character.
    //
    if (toCheck[1] == chColon)
    {
        if (((toCheck[0] >= chLatin_A) && (toCheck[0] <= chLatin_Z))
        ||  ((toCheck[0] >= chLatin_a) && (toCheck[0] <= chLatin_z)))
        {
            return false;
        }
    }

    //
    //  If it starts with a double slash, then it cannot be relative since
    //  it's a remote file.
    //
    if (isBackSlash(toCheck[0]))
        return false;

    // Else assume its a relative path
    return true;
}

XERCES_CPP_NAMESPACE_END

<|endoftext|>


<commit_before>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <af/version.h>
#include <af/opencl.h>
#include <cl.hpp>
#include <platform.hpp>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <cstring>
#include <errorcodes.hpp>
#include <err_opencl.hpp>

using std::string;
using std::vector;
using std::ostringstream;
using std::runtime_error;

using cl::Platform;
using cl::Context;
using cl::CommandQueue;
using cl::Device;

namespace opencl
{
static const char *get_system(void)
{
    return
#if defined(ARCH_32)
    "32-bit "
#elif defined(ARCH_64)
    "64-bit "
#endif

#if defined(OS_LNX)
    "Linux";
#elif defined(OS_WIN)
    "Windows";
#elif defined(OS_MAC)
    "Mac OSX";
#endif
}

DeviceManager& DeviceManager::getInstance()
{
    static DeviceManager my_instance;
    return my_instance;
}

DeviceManager::~DeviceManager()
{
    //TODO: FIXME:
    // OpenCL libs on Windows platforms
    // are crashing the application at program exit
    // most probably a reference counting issue based
    // on the investigation done so far. This problem
    // doesn't seem to happen on Linux or MacOSX.
    // So, clean up OpenCL resources on non-Windows platforms
#ifndef OS_WIN
    for (auto q: mQueues) delete q;
    for (auto d : mDevices) delete d;
    for (auto c : mContexts) delete c;
    for (auto p : mPlatforms) delete p;
#endif
}

void DeviceManager::setContext(int device)
{
    mActiveQId = device;
    for (int i = 0; i< (int)mCtxOffsets.size(); ++i) {
        if (device< (int)mCtxOffsets[i]) {
            mActiveCtxId = i;
            break;
        }
    }
}

DeviceManager::DeviceManager()
    : mActiveCtxId(0), mActiveQId(0)
{
    std::vector<cl::Platform>   platforms;
    Platform::get(&platforms);

    cl_device_type DEVC_TYPES[3] = {CL_DEVICE_TYPE_GPU,
                                    CL_DEVICE_TYPE_ACCELERATOR,
                                    CL_DEVICE_TYPE_CPU};

    for (auto &platform : platforms)
        mPlatforms.push_back(new Platform(platform));

    for (auto devType : DEVC_TYPES) {
        for (auto &platform : platforms) {
            cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platform()), 0};
            Context *temp = nullptr;
            try {
                temp = new Context(devType, cps);
                mContexts.push_back(temp);
            }
            catch (const cl::Error &error) {
                if (error.err() != CL_DEVICE_NOT_FOUND)
                    CL_TO_AF_ERROR(error);
            }
        }
    }

    unsigned nDevices = 0;
    for (auto context : mContexts) {
        vector<Device> devices = context->getInfo<CL_CONTEXT_DEVICES>();

        for(auto &dev : devices) {
            nDevices++;
            mDevices.push_back(new Device(dev));
            mQueues.push_back(new CommandQueue(*context, dev));
        }

        mCtxOffsets.push_back(nDevices);
    }

    const char* deviceENV = getenv("AF_OPENCL_DEFAULT_DEVICE");
    if(deviceENV) {
        std::stringstream s(deviceENV);
        int def_device = -1;
        s >> def_device;
        if(def_device < 0 || def_device >= (int)nDevices) {
            printf("WARNING: AF_OPENCL_DEFAULT_DEVICE is out of range\n");
            printf("Setting default device as 0\n");
        } else {
            setContext(def_device);
        }
    }
}

std::string getInfo()
{
    ostringstream info;
    info << "ArrayFire v" << AF_VERSION << AF_VERSION_MINOR
         << " (OpenCL, " << get_system() << ", build " << AF_REVISION << ")" << std::endl;

    unsigned nDevices = 0;
    for (auto context : DeviceManager::getInstance().mContexts) {
        vector<Device> devices = context->getInfo<CL_CONTEXT_DEVICES>();

        for(auto &device:devices) {
            const Platform &platform = device.getInfo<CL_DEVICE_PLATFORM>();
            string platStr = platform.getInfo<CL_PLATFORM_NAME>();
            bool show_braces = ((unsigned)getActiveDeviceId() == nDevices);
            string dstr;
            device.getInfo(CL_DEVICE_NAME, &dstr);

            string id = (show_braces ? string("[") : "-") + std::to_string(nDevices) +
                        (show_braces ? string("]") : "-");
            info << id << " " << platStr << " " << dstr << " ";
            info << device.getInfo<CL_DEVICE_VERSION>();
            info << " Device driver " << device.getInfo<CL_DRIVER_VERSION>();
            info << " FP64 Support("
                 << (device.getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE>()>0 ? "True" : "False")
                 << ")" << std::endl;

            nDevices++;
        }
    }
    return info.str();
}

int getDeviceCount()
{
    return DeviceManager::getInstance().mQueues.size();
}

int getActiveDeviceId()
{
    return DeviceManager::getInstance().mActiveQId;
}

const Context& getContext()
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return *(devMngr.mContexts[devMngr.mActiveCtxId]);
}

CommandQueue& getQueue()
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return *(devMngr.mQueues[devMngr.mActiveQId]);
}

const cl::Device& getDevice()
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return *(devMngr.mDevices[devMngr.mActiveQId]);
}

bool isDoubleSupported(int device)
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return (devMngr.mDevices[device]->getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE>()>0);
}

void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute)
{
    unsigned nDevices = 0;
    unsigned currActiveDevId = (unsigned)getActiveDeviceId();
    bool devset = false;

    for (auto context : DeviceManager::getInstance().mContexts) {
        vector<Device> devices = context->getInfo<CL_CONTEXT_DEVICES>();

        for (auto &device : devices) {
            const Platform &platform = device.getInfo<CL_DEVICE_PLATFORM>();
            string platStr = platform.getInfo<CL_PLATFORM_NAME>();

            if (currActiveDevId == nDevices) {
                string dev_str;
                device.getInfo(CL_DEVICE_NAME, &dev_str);
                string com_str = device.getInfo<CL_DEVICE_VERSION>();
                com_str = com_str.substr(7, 3);

                // strip out whitespace from the device string:
                const std::string& whitespace = " \t";
                const auto strBegin = dev_str.find_first_not_of(whitespace);
                const auto strEnd = dev_str.find_last_not_of(whitespace);
                const auto strRange = strEnd - strBegin + 1;
                dev_str = dev_str.substr(strBegin, strRange);

                // copy to output
                snprintf(d_name, 64, "%s", dev_str.c_str());
                snprintf(d_platform, 10, "OpenCL");
                snprintf(d_toolkit, 64, "%s", platStr.c_str());
                snprintf(d_compute, 10, "%s", com_str.c_str());
                devset = true;
            }
            if(devset) break;
            nDevices++;
        }
        if(devset) break;
    }

    // Sanitize input
    for (int i = 0; i < 31; i++) {
        if (d_name[i] == ' ') {
            if (d_name[i + 1] == 0 || d_name[i + 1] == ' ') d_name[i] = 0;
            else d_name[i] = '_';
        }
    }
}

int setDevice(int device)
{
    DeviceManager& devMngr = DeviceManager::getInstance();

    if (device >= (int)devMngr.mQueues.size() ||
            device>= (int)DeviceManager::MAX_DEVICES) {
        //throw runtime_error("@setDevice: invalid device index");
        return -1;
    }
    else {
        int old = devMngr.mActiveQId;
        devMngr.setContext(device);
        return old;
    }
}

void sync(int device)
{
    try {
        int currDevice = getActiveDeviceId();
        setDevice(device);
        getQueue().finish();
        setDevice(currDevice);
    } catch (cl::Error ex) {
        CL_TO_AF_ERROR(ex);
    }
}

}

namespace afcl
{
    cl_context getContext()
    {
        return opencl::getContext()();
    }

    cl_command_queue getQueue()
    {
        return opencl::getQueue()();
    }

    cl_device_id getDeviceId()
    {
        return opencl::getDevice()();
    }
}
<commit_msg>Cleaning up af::info for OpenCL backend<commit_after>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <af/version.h>
#include <af/opencl.h>
#include <cl.hpp>
#include <platform.hpp>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <cstring>
#include <algorithm>
#include <map>
#include <errorcodes.hpp>
#include <err_opencl.hpp>

using std::string;
using std::vector;
using std::ostringstream;
using std::runtime_error;

using cl::Platform;
using cl::Context;
using cl::CommandQueue;
using cl::Device;

namespace opencl
{
static const char *get_system(void)
{
    return
#if defined(ARCH_32)
    "32-bit "
#elif defined(ARCH_64)
    "64-bit "
#endif

#if defined(OS_LNX)
    "Linux";
#elif defined(OS_WIN)
    "Windows";
#elif defined(OS_MAC)
    "Mac OSX";
#endif
}

DeviceManager& DeviceManager::getInstance()
{
    static DeviceManager my_instance;
    return my_instance;
}

DeviceManager::~DeviceManager()
{
    //TODO: FIXME:
    // OpenCL libs on Windows platforms
    // are crashing the application at program exit
    // most probably a reference counting issue based
    // on the investigation done so far. This problem
    // doesn't seem to happen on Linux or MacOSX.
    // So, clean up OpenCL resources on non-Windows platforms
#ifndef OS_WIN
    for (auto q: mQueues) delete q;
    for (auto d : mDevices) delete d;
    for (auto c : mContexts) delete c;
    for (auto p : mPlatforms) delete p;
#endif
}

void DeviceManager::setContext(int device)
{
    mActiveQId = device;
    for (int i = 0; i< (int)mCtxOffsets.size(); ++i) {
        if (device< (int)mCtxOffsets[i]) {
            mActiveCtxId = i;
            break;
        }
    }
}

DeviceManager::DeviceManager()
    : mActiveCtxId(0), mActiveQId(0)
{
    std::vector<cl::Platform>   platforms;
    Platform::get(&platforms);

    cl_device_type DEVC_TYPES[3] = {CL_DEVICE_TYPE_GPU,
                                    CL_DEVICE_TYPE_ACCELERATOR,
                                    CL_DEVICE_TYPE_CPU};

    for (auto &platform : platforms)
        mPlatforms.push_back(new Platform(platform));

    for (auto devType : DEVC_TYPES) {
        for (auto &platform : platforms) {
            cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platform()), 0};
            Context *temp = nullptr;
            try {
                temp = new Context(devType, cps);
                mContexts.push_back(temp);
            }
            catch (const cl::Error &error) {
                if (error.err() != CL_DEVICE_NOT_FOUND)
                    CL_TO_AF_ERROR(error);
            }
        }
    }

    unsigned nDevices = 0;
    for (auto context : mContexts) {
        vector<Device> devices = context->getInfo<CL_CONTEXT_DEVICES>();

        for(auto &dev : devices) {
            nDevices++;
            mDevices.push_back(new Device(dev));
            mQueues.push_back(new CommandQueue(*context, dev));
        }

        mCtxOffsets.push_back(nDevices);
    }

    const char* deviceENV = getenv("AF_OPENCL_DEFAULT_DEVICE");
    if(deviceENV) {
        std::stringstream s(deviceENV);
        int def_device = -1;
        s >> def_device;
        if(def_device < 0 || def_device >= (int)nDevices) {
            printf("WARNING: AF_OPENCL_DEFAULT_DEVICE is out of range\n");
            printf("Setting default device as 0\n");
        } else {
            setContext(def_device);
        }
    }
}

// http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring/217605#217605
// trim from start
static inline std::string &ltrim(std::string &s)
{
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
                                    std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

static std::string platformMap(std::string &platStr)
{
    static bool isFirst = true;

    typedef std::map<std::string, std::string> strmap_t;
    static strmap_t platMap;
    if (isFirst) {
        platMap["NVIDIA CUDA"] = "NVIDIA  ";
        platMap["Intel(R) OpenCL"] = "INTEL   ";
        platMap["AMD Accelerated Parallel Processing"] = "AMD     ";
        platMap["Intel Gen OCL Driver"] = "BEIGNET ";
        platMap["Apple"] = "APPLE   ";
        isFirst = false;
    }

    strmap_t::iterator idx = platMap.find(platStr);

    if (idx == platMap.end()) {
        return platStr;
    } else {
        return idx->second;
    }
}

std::string getInfo()
{
    ostringstream info;
    info << "ArrayFire v" << AF_VERSION << AF_VERSION_MINOR
         << " (OpenCL, " << get_system() << ", build " << AF_REVISION << ")" << std::endl;

    unsigned nDevices = 0;
    for (auto context : DeviceManager::getInstance().mContexts) {
        vector<Device> devices = context->getInfo<CL_CONTEXT_DEVICES>();

        for(auto &device:devices) {
            const Platform &platform = device.getInfo<CL_DEVICE_PLATFORM>();
            string platStr = platform.getInfo<CL_PLATFORM_NAME>();
            bool show_braces = ((unsigned)getActiveDeviceId() == nDevices);
            string dstr = device.getInfo<CL_DEVICE_NAME>();

            string id = (show_braces ? string("[") : "-") + std::to_string(nDevices) +
                        (show_braces ? string("]") : "-");
            info << id << " " << platformMap(platStr) << ": " << ltrim(dstr) << " ";
#ifndef NDEBUG
            info << device.getInfo<CL_DEVICE_VERSION>();
            info << " Device driver " << device.getInfo<CL_DRIVER_VERSION>();
            info << " FP64 Support("
                 << (device.getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE>()>0 ? "True" : "False")
                 << ")";
#endif
            info << std::endl;

            nDevices++;
        }
    }
    return info.str();
}

int getDeviceCount()
{
    return DeviceManager::getInstance().mQueues.size();
}

int getActiveDeviceId()
{
    return DeviceManager::getInstance().mActiveQId;
}

const Context& getContext()
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return *(devMngr.mContexts[devMngr.mActiveCtxId]);
}

CommandQueue& getQueue()
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return *(devMngr.mQueues[devMngr.mActiveQId]);
}

const cl::Device& getDevice()
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return *(devMngr.mDevices[devMngr.mActiveQId]);
}

bool isDoubleSupported(int device)
{
    DeviceManager& devMngr = DeviceManager::getInstance();
    return (devMngr.mDevices[device]->getInfo<CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE>()>0);
}

void devprop(char* d_name, char* d_platform, char *d_toolkit, char* d_compute)
{
    unsigned nDevices = 0;
    unsigned currActiveDevId = (unsigned)getActiveDeviceId();
    bool devset = false;

    for (auto context : DeviceManager::getInstance().mContexts) {
        vector<Device> devices = context->getInfo<CL_CONTEXT_DEVICES>();

        for (auto &device : devices) {
            const Platform &platform = device.getInfo<CL_DEVICE_PLATFORM>();
            string platStr = platform.getInfo<CL_PLATFORM_NAME>();

            if (currActiveDevId == nDevices) {
                string dev_str;
                device.getInfo(CL_DEVICE_NAME, &dev_str);
                string com_str = device.getInfo<CL_DEVICE_VERSION>();
                com_str = com_str.substr(7, 3);

                // strip out whitespace from the device string:
                const std::string& whitespace = " \t";
                const auto strBegin = dev_str.find_first_not_of(whitespace);
                const auto strEnd = dev_str.find_last_not_of(whitespace);
                const auto strRange = strEnd - strBegin + 1;
                dev_str = dev_str.substr(strBegin, strRange);

                // copy to output
                snprintf(d_name, 64, "%s", dev_str.c_str());
                snprintf(d_platform, 10, "OpenCL");
                snprintf(d_toolkit, 64, "%s", platStr.c_str());
                snprintf(d_compute, 10, "%s", com_str.c_str());
                devset = true;
            }
            if(devset) break;
            nDevices++;
        }
        if(devset) break;
    }

    // Sanitize input
    for (int i = 0; i < 31; i++) {
        if (d_name[i] == ' ') {
            if (d_name[i + 1] == 0 || d_name[i + 1] == ' ') d_name[i] = 0;
            else d_name[i] = '_';
        }
    }
}

int setDevice(int device)
{
    DeviceManager& devMngr = DeviceManager::getInstance();

    if (device >= (int)devMngr.mQueues.size() ||
            device>= (int)DeviceManager::MAX_DEVICES) {
        //throw runtime_error("@setDevice: invalid device index");
        return -1;
    }
    else {
        int old = devMngr.mActiveQId;
        devMngr.setContext(device);
        return old;
    }
}

void sync(int device)
{
    try {
        int currDevice = getActiveDeviceId();
        setDevice(device);
        getQueue().finish();
        setDevice(currDevice);
    } catch (cl::Error ex) {
        CL_TO_AF_ERROR(ex);
    }
}

}

namespace afcl
{
    cl_context getContext()
    {
        return opencl::getContext()();
    }

    cl_command_queue getQueue()
    {
        return opencl::getQueue()();
    }

    cl_device_id getDeviceId()
    {
        return opencl::getDevice()();
    }
}
<|endoftext|>


<commit_before><commit_msg>Sorted List to BST<commit_after><|endoftext|>


<commit_before>#include "btree/parallel_traversal.hpp"

#include "btree/slice.hpp"
#include "buffer_cache/transactor.hpp"
#include "buffer_cache/buf_lock.hpp"
#include "btree/node.hpp"
#include "btree/internal_node.hpp"

// Traversal

// We want a traversal operation to follow a few simple rules.
//
// 1. Get as far away from the root as possible.
//
// 2. Avoid using more than K + O(1) blocks, for some user-selected
// constant K.
//
// 3. Prefetch efficiently.
//
// This code hopefully will be nice to genericize; you could
// reimplement rget if you genericized this.

// The Lifecyle of a block_id_t
//
// Every time we deal with a block_id_t, it goes through these states...
//
// 1. Knowledge of the block_id_t.  This is where we know about the
// block_id_t, and haven't done anything about it yet.
//
// 2. Acquiring its subtree_recency value from the serializer.  The
// block_id_t is grouped with a bunch of others in an array, and we've
// sent a request to the serializer to respond with all these
// subtree_recency values (and the original array).
//
// 3. Acquired the subtree_recency value.  The block_id_t's
// subtree_recency is known, but we still have not attempted to
// acquire the block.  (If the recency is insufficiently recent, we
// stop here.)
//
// 4. Block acquisition pending.  We have sent a request to acquire
// the block.  It has not yet successfully completed.
//
// 5I. Block acquisition complete, it's an internal node, partly
// processed children.  We hold the lock on the block, and the
// children blocks are currently being processed and have not reached
// stage 4.
//
// 6I. Live children all reached stage 4.  We can now release ownership
// of the block.  We stop here.
//
// 5L. Block acquisition complete, it's a leaf node, we may have to
// handle large values.
//
// 6L. Large values all pending or better, so we can release ownership
// of the block.  We stop here.
//
// (6L might not be implemented, or 5L and 6L might depend on whether
// the btree_traversal_helper_t implementation is interested in
// values.)

struct parent_releaser_t;

struct acquisition_waiter_callback_t {
    virtual void you_may_acquire() = 0;
protected:
    ~acquisition_waiter_callback_t() { }
};

class traversal_state_t {
public:
    traversal_state_t(const thread_saver_t& saver, btree_slice_t *_slice, repli_timestamp _transaction_tstamp, btree_traversal_helper_t *_helper)
        : slice(_slice),
          transactor_ptr(boost::make_shared<transactor_t>(saver, _slice->cache(), _helper->transaction_mode(), _transaction_tstamp)),
          helper(_helper) { }

    // The slice whose btree we're traversing
    btree_slice_t *const slice;

    boost::shared_ptr<transactor_t> transactor_ptr;

    // The helper.
    btree_traversal_helper_t *helper;

    cond_t finished_cond;

    // The number of pending + acquired blocks, by level.
    std::vector<int64_t> level_counts;

    int64_t& level_count(int level) {
        rassert(level >= 0);
        if (level >= int(level_counts.size())) {
            rassert(level == int(level_counts.size()), "Somehow we skipped a level! (level = %d, slice = %p)", level, slice);
            level_counts.resize(level + 1, 0);
        }
        return level_counts[level];
    }

    static int64_t level_max(UNUSED int level) {
        // level = 1 is the root level.  These numbers are
        // ridiculously small because we have to spawn a coroutine
        // because the buffer cache is broken.
        if (level <= 3) {
            return 1000;
        } else {
            return 50;
        }
    }

    void consider_pulsing() {
        rassert(coro_t::self());

        // We try to do as many pulses as we can (thus getting
        // behavior equivalent to calling consider_pulsing) but we
        // only actually do one pulse because this function gets
        // called immediately after every single block deallocation,
        // which only decrements counters by 1.

        // Right now we don't actually do more than one pulse at a
        // time, but we should try.

        rassert(level_counts.size() <= acquisition_waiter_stacks.size());
        level_counts.resize(acquisition_waiter_stacks.size(), 0);

        for (int i = level_counts.size() - 1; i >= 0; --i) {
            if (level_counts[i] < level_max(i)) {
                int diff = level_max(i) - level_counts[i];

                while (diff > 0 && !acquisition_waiter_stacks[i].empty()) {
                    acquisition_waiter_callback_t *waiter_cb = acquisition_waiter_stacks[i].back();
                    acquisition_waiter_stacks[i].pop_back();

                    // For some reason the buffer cache is broken.  It
                    // expects transaction_t::acquire to run in a
                    // coroutine!  So we use coro_t::spawn instead of
                    // do_later.
                    coro_t::spawn(boost::bind(&acquisition_waiter_callback_t::you_may_acquire, waiter_cb));
                    diff -= 1;
                }
            }
        }

        if (total_level_count() == 0) {
            finished_cond.pulse();
        }
    }

    int64_t total_level_count() {
        int64_t sum = 0;
        for (int i = 0, n = level_counts.size(); i < n; ++i) {
            sum += level_counts[i];
        }
        return sum;
    }

    std::vector< std::vector<acquisition_waiter_callback_t *> > acquisition_waiter_stacks;

    std::vector<acquisition_waiter_callback_t *>& acquisition_waiter_stack(int level) {
        rassert(level >= 0);
        if (level >= int(acquisition_waiter_stacks.size())) {
            rassert(level == int(acquisition_waiter_stacks.size()), "Somehow we skipped a level! (level = %d, stacks.size() = %d, slice = %p)", level, int(acquisition_waiter_stacks.size()), slice);
            acquisition_waiter_stacks.resize(level + 1);
        }
        return acquisition_waiter_stacks[level];
    }

    void wait() {
        finished_cond.wait();
    }

private:
    DISABLE_COPYING(traversal_state_t);
};

void subtrees_traverse(traversal_state_t *state, parent_releaser_t *releaser, int level, boost::scoped_array<block_id_t>& param_block_ids, int num_block_ids);
void do_a_subtree_traversal(traversal_state_t *state, int level, block_id_t block_id, acquisition_start_callback_t *acq_start_cb);
void process_a_leaf_node(traversal_state_t *state, buf_t *buf, int level);
void process_a_internal_node(traversal_state_t *state, buf_t *buf, int level);

struct node_ready_callback_t {
    virtual void on_node_ready(buf_t *buf) = 0;
protected:
    ~node_ready_callback_t() { }
};

struct acquire_a_node_fsm_t : public acquisition_waiter_callback_t, public block_available_callback_t {
    // Not much of an fsm.
    traversal_state_t *state;
    int level;
    block_id_t block_id;
    acquisition_start_callback_t *acq_start_cb;
    node_ready_callback_t *node_ready_cb;

    void you_may_acquire() {
        state->level_count(level) += 1;

        buf_t *buf = state->transactor_ptr->get()->acquire(block_id, state->helper->btree_node_mode(), this);
        // TODO: This is worthless crap.  We haven't started the
        // acquisition, we've FINISHED acquiring the buf because the
        // interface of acquire is a complete lie.  Thus we have worse
        // performance.
        acq_start_cb->on_started_acquisition();

        rassert(buf, "apparently the transaction_t::acquire interface now takes a callback parameter but never uses it");
        if (buf) {
            on_block_available(buf);
        }
    }

    void on_block_available(buf_t *block) {
        rassert(coro_t::self());
        node_ready_callback_t *local_cb = node_ready_cb;
        delete this;
        local_cb->on_node_ready(block);
    }
};

void acquire_a_node(traversal_state_t *state, int level, block_id_t block_id, acquisition_start_callback_t *acq_start_cb, node_ready_callback_t *node_ready_cb) {
    rassert(coro_t::self());
    acquire_a_node_fsm_t *fsm = new acquire_a_node_fsm_t;
    fsm->state = state;
    fsm->level = level;
    fsm->block_id = block_id;
    fsm->acq_start_cb = acq_start_cb;
    fsm->node_ready_cb = node_ready_cb;

    state->acquisition_waiter_stack(level).push_back(fsm);
    state->consider_pulsing();
}

struct parent_releaser_t {
    virtual void release() = 0;
protected:
    ~parent_releaser_t() { }
};

struct internal_node_releaser_t : public parent_releaser_t {
    buf_t *buf_;
    traversal_state_t *state_;
    virtual void release() {
        state_->helper->postprocess_internal_node(buf_);
        buf_->release();
        delete this;
    }
    internal_node_releaser_t(buf_t *buf, traversal_state_t *state) : buf_(buf), state_(state) { }
};

void btree_parallel_traversal(btree_slice_t *slice, repli_timestamp transaction_tstamp, btree_traversal_helper_t *helper) {
    thread_saver_t saver;
    traversal_state_t state(saver, slice, transaction_tstamp, helper);
    buf_lock_t superblock_buf(saver, *state.transactor_ptr, SUPERBLOCK_ID, helper->btree_superblock_mode());

    const btree_superblock_t *superblock = reinterpret_cast<const btree_superblock_t *>(superblock_buf->get_data_read());

    helper->preprocess_btree_superblock(state.transactor_ptr, superblock);

    block_id_t root_id = superblock->root_block;
    rassert(root_id != SUPERBLOCK_ID);

    struct : public parent_releaser_t {
        buf_t *buf;
        traversal_state_t *state;
        void release() {
            state->helper->postprocess_btree_superblock(buf);
            buf->release();
        }
    } superblock_releaser;
    superblock_releaser.buf = superblock_buf.give_up_ownership();
    superblock_releaser.state = &state;

    if (root_id == NULL_BLOCK_ID) {
        superblock_releaser.release();
        // No root, so no keys in this entire shard.
    } else {
        boost::scoped_array<block_id_t> roots(new block_id_t[1]);
        roots[0] = root_id;
        state.level_count(0) += 1;
        state.acquisition_waiter_stacks.resize(1);
        subtrees_traverse(&state, &superblock_releaser, 1, roots, 1);
        state.wait();
    }
}

void subtrees_traverse(traversal_state_t *state, parent_releaser_t *releaser, int level, boost::scoped_array<block_id_t>& param_block_ids, int num_block_ids) {
    rassert(coro_t::self());
    interesting_children_callback_t *fsm = new interesting_children_callback_t(state, releaser, level);
    state->helper->filter_interesting_children(state->transactor_ptr, param_block_ids.get(), num_block_ids, fsm);
}
struct do_a_subtree_traversal_fsm_t : public node_ready_callback_t {
    traversal_state_t *state;
    int level;

    void on_node_ready(buf_t *buf) {
        rassert(coro_t::self());
        const node_t *node = reinterpret_cast<const node_t *>(buf->get_data_read());

        traversal_state_t *local_state = state;
        int local_level = level;

        if (node::is_leaf(node)) {
            delete this;
            process_a_leaf_node(local_state, buf, local_level);
        } else {
            rassert(node::is_internal(node));

            delete this;
            process_a_internal_node(local_state, buf, local_level);
        }
    }
};

void do_a_subtree_traversal(traversal_state_t *state, int level, block_id_t block_id, acquisition_start_callback_t *acq_start_cb) {
    do_a_subtree_traversal_fsm_t *fsm = new do_a_subtree_traversal_fsm_t;
    fsm->state = state;
    fsm->level = level;

    acquire_a_node(state, level, block_id, acq_start_cb, fsm);
}

// This releases its buf_t parameter.
void process_a_internal_node(traversal_state_t *state, buf_t *buf, int level) {
    const internal_node_t *node = reinterpret_cast<const internal_node_t *>(buf->get_data_read());

    boost::scoped_array<block_id_t> children;
    size_t num_children;
    internal_node::get_children_ids(node, children, &num_children);

    subtrees_traverse(state, new internal_node_releaser_t(buf, state), level + 1, children, num_children);
}

// This releases its buf_t parameter.
void process_a_leaf_node(traversal_state_t *state, buf_t *buf, int level) {
    // This can be run in the scheduler thread.
    state->helper->process_a_leaf(state->transactor_ptr, buf);
    buf->release();
    state->level_count(level) -= 1;
    state->consider_pulsing();
}

void interesting_children_callback_t::receive_interesting_children(boost::scoped_array<block_id_t>& block_ids, int num_block_ids) {
    acquisition_countdown = num_block_ids + 1;

    for (int i = 0; i < num_block_ids; ++i) {
        do_a_subtree_traversal(state, level, block_ids[i], this);
    }

    decr_acquisition_countdown();
}

void interesting_children_callback_t::on_started_acquisition() {
    rassert(coro_t::self());
    decr_acquisition_countdown();
}

void interesting_children_callback_t::decr_acquisition_countdown() {
    rassert(coro_t::self());
    rassert(acquisition_countdown > 0);
    -- acquisition_countdown;
    if (acquisition_countdown == 0) {
        releaser->release();
        state->level_count(level - 1) -= 1;
        state->consider_pulsing();
        delete this;
    }
}

<commit_msg>Fixed traversal_state_t transactor creation without expected change count.. yeah. that bug.<commit_after>#include "btree/parallel_traversal.hpp"

#include "btree/slice.hpp"
#include "buffer_cache/transactor.hpp"
#include "buffer_cache/buf_lock.hpp"
#include "btree/node.hpp"
#include "btree/internal_node.hpp"

// Traversal

// We want a traversal operation to follow a few simple rules.
//
// 1. Get as far away from the root as possible.
//
// 2. Avoid using more than K + O(1) blocks, for some user-selected
// constant K.
//
// 3. Prefetch efficiently.
//
// This code hopefully will be nice to genericize; you could
// reimplement rget if you genericized this.

// The Lifecyle of a block_id_t
//
// Every time we deal with a block_id_t, it goes through these states...
//
// 1. Knowledge of the block_id_t.  This is where we know about the
// block_id_t, and haven't done anything about it yet.
//
// 2. Acquiring its subtree_recency value from the serializer.  The
// block_id_t is grouped with a bunch of others in an array, and we've
// sent a request to the serializer to respond with all these
// subtree_recency values (and the original array).
//
// 3. Acquired the subtree_recency value.  The block_id_t's
// subtree_recency is known, but we still have not attempted to
// acquire the block.  (If the recency is insufficiently recent, we
// stop here.)
//
// 4. Block acquisition pending.  We have sent a request to acquire
// the block.  It has not yet successfully completed.
//
// 5I. Block acquisition complete, it's an internal node, partly
// processed children.  We hold the lock on the block, and the
// children blocks are currently being processed and have not reached
// stage 4.
//
// 6I. Live children all reached stage 4.  We can now release ownership
// of the block.  We stop here.
//
// 5L. Block acquisition complete, it's a leaf node, we may have to
// handle large values.
//
// 6L. Large values all pending or better, so we can release ownership
// of the block.  We stop here.
//
// (6L might not be implemented, or 5L and 6L might depend on whether
// the btree_traversal_helper_t implementation is interested in
// values.)

struct parent_releaser_t;

struct acquisition_waiter_callback_t {
    virtual void you_may_acquire() = 0;
protected:
    ~acquisition_waiter_callback_t() { }
};

class traversal_state_t {
public:
    traversal_state_t(const thread_saver_t& saver, btree_slice_t *_slice, repli_timestamp _transaction_tstamp, btree_traversal_helper_t *_helper)
        : slice(_slice),
          /* We can't compute the expected change count (we're either
             doing nothing or deleting the entire tree or something),
             so we just pass 0.  You could let this be a
             helper-supplied value, if you cared. */
          transactor_ptr(boost::make_shared<transactor_t>(saver, _slice->cache(), _helper->transaction_mode(), 0, _transaction_tstamp)),
          helper(_helper) { }

    // The slice whose btree we're traversing
    btree_slice_t *const slice;

    boost::shared_ptr<transactor_t> transactor_ptr;

    // The helper.
    btree_traversal_helper_t *helper;

    cond_t finished_cond;

    // The number of pending + acquired blocks, by level.
    std::vector<int64_t> level_counts;

    int64_t& level_count(int level) {
        rassert(level >= 0);
        if (level >= int(level_counts.size())) {
            rassert(level == int(level_counts.size()), "Somehow we skipped a level! (level = %d, slice = %p)", level, slice);
            level_counts.resize(level + 1, 0);
        }
        return level_counts[level];
    }

    static int64_t level_max(UNUSED int level) {
        // level = 1 is the root level.  These numbers are
        // ridiculously small because we have to spawn a coroutine
        // because the buffer cache is broken.
        if (level <= 3) {
            return 1000;
        } else {
            return 50;
        }
    }

    void consider_pulsing() {
        rassert(coro_t::self());

        // We try to do as many pulses as we can (thus getting
        // behavior equivalent to calling consider_pulsing) but we
        // only actually do one pulse because this function gets
        // called immediately after every single block deallocation,
        // which only decrements counters by 1.

        // Right now we don't actually do more than one pulse at a
        // time, but we should try.

        rassert(level_counts.size() <= acquisition_waiter_stacks.size());
        level_counts.resize(acquisition_waiter_stacks.size(), 0);

        for (int i = level_counts.size() - 1; i >= 0; --i) {
            if (level_counts[i] < level_max(i)) {
                int diff = level_max(i) - level_counts[i];

                while (diff > 0 && !acquisition_waiter_stacks[i].empty()) {
                    acquisition_waiter_callback_t *waiter_cb = acquisition_waiter_stacks[i].back();
                    acquisition_waiter_stacks[i].pop_back();

                    // For some reason the buffer cache is broken.  It
                    // expects transaction_t::acquire to run in a
                    // coroutine!  So we use coro_t::spawn instead of
                    // do_later.
                    coro_t::spawn(boost::bind(&acquisition_waiter_callback_t::you_may_acquire, waiter_cb));
                    diff -= 1;
                }
            }
        }

        if (total_level_count() == 0) {
            finished_cond.pulse();
        }
    }

    int64_t total_level_count() {
        int64_t sum = 0;
        for (int i = 0, n = level_counts.size(); i < n; ++i) {
            sum += level_counts[i];
        }
        return sum;
    }

    std::vector< std::vector<acquisition_waiter_callback_t *> > acquisition_waiter_stacks;

    std::vector<acquisition_waiter_callback_t *>& acquisition_waiter_stack(int level) {
        rassert(level >= 0);
        if (level >= int(acquisition_waiter_stacks.size())) {
            rassert(level == int(acquisition_waiter_stacks.size()), "Somehow we skipped a level! (level = %d, stacks.size() = %d, slice = %p)", level, int(acquisition_waiter_stacks.size()), slice);
            acquisition_waiter_stacks.resize(level + 1);
        }
        return acquisition_waiter_stacks[level];
    }

    void wait() {
        finished_cond.wait();
    }

private:
    DISABLE_COPYING(traversal_state_t);
};

void subtrees_traverse(traversal_state_t *state, parent_releaser_t *releaser, int level, boost::scoped_array<block_id_t>& param_block_ids, int num_block_ids);
void do_a_subtree_traversal(traversal_state_t *state, int level, block_id_t block_id, acquisition_start_callback_t *acq_start_cb);
void process_a_leaf_node(traversal_state_t *state, buf_t *buf, int level);
void process_a_internal_node(traversal_state_t *state, buf_t *buf, int level);

struct node_ready_callback_t {
    virtual void on_node_ready(buf_t *buf) = 0;
protected:
    ~node_ready_callback_t() { }
};

struct acquire_a_node_fsm_t : public acquisition_waiter_callback_t, public block_available_callback_t {
    // Not much of an fsm.
    traversal_state_t *state;
    int level;
    block_id_t block_id;
    acquisition_start_callback_t *acq_start_cb;
    node_ready_callback_t *node_ready_cb;

    void you_may_acquire() {
        state->level_count(level) += 1;

        buf_t *buf = state->transactor_ptr->get()->acquire(block_id, state->helper->btree_node_mode(), this);
        // TODO: This is worthless crap.  We haven't started the
        // acquisition, we've FINISHED acquiring the buf because the
        // interface of acquire is a complete lie.  Thus we have worse
        // performance.
        acq_start_cb->on_started_acquisition();

        rassert(buf, "apparently the transaction_t::acquire interface now takes a callback parameter but never uses it");
        if (buf) {
            on_block_available(buf);
        }
    }

    void on_block_available(buf_t *block) {
        rassert(coro_t::self());
        node_ready_callback_t *local_cb = node_ready_cb;
        delete this;
        local_cb->on_node_ready(block);
    }
};

void acquire_a_node(traversal_state_t *state, int level, block_id_t block_id, acquisition_start_callback_t *acq_start_cb, node_ready_callback_t *node_ready_cb) {
    rassert(coro_t::self());
    acquire_a_node_fsm_t *fsm = new acquire_a_node_fsm_t;
    fsm->state = state;
    fsm->level = level;
    fsm->block_id = block_id;
    fsm->acq_start_cb = acq_start_cb;
    fsm->node_ready_cb = node_ready_cb;

    state->acquisition_waiter_stack(level).push_back(fsm);
    state->consider_pulsing();
}

struct parent_releaser_t {
    virtual void release() = 0;
protected:
    ~parent_releaser_t() { }
};

struct internal_node_releaser_t : public parent_releaser_t {
    buf_t *buf_;
    traversal_state_t *state_;
    virtual void release() {
        state_->helper->postprocess_internal_node(buf_);
        buf_->release();
        delete this;
    }
    internal_node_releaser_t(buf_t *buf, traversal_state_t *state) : buf_(buf), state_(state) { }
};

void btree_parallel_traversal(btree_slice_t *slice, repli_timestamp transaction_tstamp, btree_traversal_helper_t *helper) {
    thread_saver_t saver;
    traversal_state_t state(saver, slice, transaction_tstamp, helper);
    buf_lock_t superblock_buf(saver, *state.transactor_ptr, SUPERBLOCK_ID, helper->btree_superblock_mode());

    const btree_superblock_t *superblock = reinterpret_cast<const btree_superblock_t *>(superblock_buf->get_data_read());

    helper->preprocess_btree_superblock(state.transactor_ptr, superblock);

    block_id_t root_id = superblock->root_block;
    rassert(root_id != SUPERBLOCK_ID);

    struct : public parent_releaser_t {
        buf_t *buf;
        traversal_state_t *state;
        void release() {
            state->helper->postprocess_btree_superblock(buf);
            buf->release();
        }
    } superblock_releaser;
    superblock_releaser.buf = superblock_buf.give_up_ownership();
    superblock_releaser.state = &state;

    if (root_id == NULL_BLOCK_ID) {
        superblock_releaser.release();
        // No root, so no keys in this entire shard.
    } else {
        boost::scoped_array<block_id_t> roots(new block_id_t[1]);
        roots[0] = root_id;
        state.level_count(0) += 1;
        state.acquisition_waiter_stacks.resize(1);
        subtrees_traverse(&state, &superblock_releaser, 1, roots, 1);
        state.wait();
    }
}

void subtrees_traverse(traversal_state_t *state, parent_releaser_t *releaser, int level, boost::scoped_array<block_id_t>& param_block_ids, int num_block_ids) {
    rassert(coro_t::self());
    interesting_children_callback_t *fsm = new interesting_children_callback_t(state, releaser, level);
    state->helper->filter_interesting_children(state->transactor_ptr, param_block_ids.get(), num_block_ids, fsm);
}
struct do_a_subtree_traversal_fsm_t : public node_ready_callback_t {
    traversal_state_t *state;
    int level;

    void on_node_ready(buf_t *buf) {
        rassert(coro_t::self());
        const node_t *node = reinterpret_cast<const node_t *>(buf->get_data_read());

        traversal_state_t *local_state = state;
        int local_level = level;

        if (node::is_leaf(node)) {
            delete this;
            process_a_leaf_node(local_state, buf, local_level);
        } else {
            rassert(node::is_internal(node));

            delete this;
            process_a_internal_node(local_state, buf, local_level);
        }
    }
};

void do_a_subtree_traversal(traversal_state_t *state, int level, block_id_t block_id, acquisition_start_callback_t *acq_start_cb) {
    do_a_subtree_traversal_fsm_t *fsm = new do_a_subtree_traversal_fsm_t;
    fsm->state = state;
    fsm->level = level;

    acquire_a_node(state, level, block_id, acq_start_cb, fsm);
}

// This releases its buf_t parameter.
void process_a_internal_node(traversal_state_t *state, buf_t *buf, int level) {
    const internal_node_t *node = reinterpret_cast<const internal_node_t *>(buf->get_data_read());

    boost::scoped_array<block_id_t> children;
    size_t num_children;
    internal_node::get_children_ids(node, children, &num_children);

    subtrees_traverse(state, new internal_node_releaser_t(buf, state), level + 1, children, num_children);
}

// This releases its buf_t parameter.
void process_a_leaf_node(traversal_state_t *state, buf_t *buf, int level) {
    // This can be run in the scheduler thread.
    state->helper->process_a_leaf(state->transactor_ptr, buf);
    buf->release();
    state->level_count(level) -= 1;
    state->consider_pulsing();
}

void interesting_children_callback_t::receive_interesting_children(boost::scoped_array<block_id_t>& block_ids, int num_block_ids) {
    acquisition_countdown = num_block_ids + 1;

    for (int i = 0; i < num_block_ids; ++i) {
        do_a_subtree_traversal(state, level, block_ids[i], this);
    }

    decr_acquisition_countdown();
}

void interesting_children_callback_t::on_started_acquisition() {
    rassert(coro_t::self());
    decr_acquisition_countdown();
}

void interesting_children_callback_t::decr_acquisition_countdown() {
    rassert(coro_t::self());
    rassert(acquisition_countdown > 0);
    -- acquisition_countdown;
    if (acquisition_countdown == 0) {
        releaser->release();
        state->level_count(level - 1) -= 1;
        state->consider_pulsing();
        delete this;
    }
}

<|endoftext|>