<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.
 */

/**
 * @author Matthew Hoyt (mhoyt@ca.ibm.com)
 */

#if !defined(XALANDEQUE_HEADER_GUARD_1357924680)
#define XALANDEQUE_HEADER_GUARD_1357924680

// Base include file.  Must be first.
#include <xalanc/Include/PlatformDefinitions.hpp>

#include <xalanc/Include/XalanVector.hpp>
#include <xalanc/Include/XalanMemoryManagement.hpp>

XALAN_CPP_NAMESPACE_BEGIN

template <class Value>
struct XalanDequeIteratorTraits
{
    typedef Value           value_type;
    typedef Value&          reference;
    typedef Value*          pointer;
    typedef const Value&    const_reference;
};

template <class Value>
struct XalanDequeConstIteratorTraits
{
    typedef Value           value_type;
    typedef const Value&    reference;
    typedef const Value*    pointer;
    typedef const Value&    const_reference;
};

template <class XalanDequeTraits, class XalanDeque>
struct  XalanDequeIterator
{
    typedef size_t  size_type;
    typedef typename XalanDequeTraits::value_type       value_type;
    typedef typename XalanDequeTraits::reference        reference;
    typedef typename XalanDequeTraits::pointer          pointer;
    typedef typename XalanDequeTraits::const_reference  const_reference;
    typedef ptrdiff_t                                   difference_type;

    typedef XalanDequeIterator<XalanDequeIteratorTraits<value_type>, XalanDeque> Iterator;

    typedef XALAN_STD_QUALIFIER random_access_iterator_tag iterator_category;

    XalanDequeIterator(XalanDeque*  deque,
                       size_type    pos) :
        m_deque(deque),
        m_pos(pos)
    {
    }

    XalanDequeIterator(const Iterator & iterator) :
        m_deque(iterator.m_deque),
        m_pos(iterator.m_pos)
    {
    }

    XalanDequeIterator& operator=(const Iterator & iterator)
    {
        m_deque = iterator.m_deque;
        m_pos = iterator.m_pos;
        return *this;
    }

    XalanDequeIterator& operator++()
    {
        ++m_pos;
        return *this;
    }

    XalanDequeIterator operator++(int)
    {
        XalanDequeIterator temp = *this;
        ++m_pos;
        return temp;
    }

    XalanDequeIterator& operator--()
    {
        --m_pos;
        return *this;
    }

    pointer operator->()
    {
        return &(*m_deque[m_pos]);
    }

    reference operator*()
    {
        return (*m_deque)[m_pos];
    }

    const_reference operator*() const
    {
        return (*m_deque)[m_pos];
    }

    XalanDequeIterator operator+(difference_type difference) const
    {
        return XalanDequeIterator(m_deque, m_pos + difference);
    }

    XalanDequeIterator operator-(difference_type difference) const
    {
        return XalanDequeIterator(m_deque, m_pos - difference);
    }

    difference_type operator-(const XalanDequeIterator &theRhs) const
    {
        return m_pos - theRhs.m_pos;
    }

    bool operator==(const XalanDequeIterator & theRhs) const
    {
        return (theRhs.m_deque == m_deque)
            &&  theRhs.m_pos == m_pos;
    }

    bool operator!=(const XalanDequeIterator & theRhs) const
    {
        return !(theRhs == *this);
    }

    bool operator<(const XalanDequeIterator & theRhs) const
    {
        return m_pos < theRhs.m_pos;
    }

    XalanDeque* m_deque;
    size_type   m_pos;
};

/**
 * Xalan implementation of deque
 */
template <class Type, class ConstructionTraits = MemoryManagedConstructionTraits<Type> >
class XalanDeque
{
public:

    typedef size_t  size_type;

    typedef Type            value_type;
    typedef Type&           reference;
    typedef const Type&     const_reference;

    typedef XalanVector<Type, ConstructionTraits>       BlockType;

    typedef XalanVector<BlockType*> BlockIndexType;

    typedef XalanDeque<Type, ConstructionTraits>                            ThisType;
    typedef XalanDequeIterator<XalanDequeIteratorTraits<value_type>, ThisType>      iterator;
    typedef XalanDequeIterator<XalanDequeConstIteratorTraits<value_type>, ThisType>     const_iterator;

#if defined(XALAN_HAS_STD_ITERATORS)
    typedef XALAN_STD_QUALIFIER reverse_iterator<iterator>          reverse_iterator_;
    typedef XALAN_STD_QUALIFIER reverse_iterator<const_iterator>    const_reverse_iterator_;
#elif defined(XALAN_RW_NO_CLASS_PARTIAL_SPEC)
    typedef XALAN_STD_QUALIFIER reverse_iterator<
        iterator,
        XALAN_STD_QUALIFIER random_access_iterator_tag,
        value_type> reverse_iterator_;
    typedef XALAN_STD_QUALIFIER reverse_iterator<
        const_iterator,
        XALAN_STD_QUALIFIER random_access_iterator_tag,
        const value_type> const_reverse_iterator_;
#else
    typedef XALAN_STD_QUALIFIER reverse_iterator<iterator, value_type>                          reverse_iterator_;
    typedef XALAN_STD_QUALIFIER reverse_iterator<const_iterator, value_type, const_reference>   const_reverse_iterator_;
#endif

    typedef reverse_iterator_           reverse_iterator;
    typedef const_reverse_iterator_     const_reverse_iterator;

    typedef typename ConstructionTraits::Constructor    Constructor;
    typedef typename Constructor::ConstructableType     ConstructibleType;

    XalanDeque(
            MemoryManager& memoryManager,
            size_type initialSize = 0,
            size_type blockSize = 10) :
        m_memoryManager(&memoryManager),
        m_blockSize(blockSize),
        m_blockIndex(memoryManager,
                    initialSize / blockSize + (initialSize % blockSize == 0 ? 0 : 1)),
        m_freeBlockVector(memoryManager)
    {
        typename Constructor::ConstructableType  defaultValue(*m_memoryManager);

        XALAN_STD_QUALIFIER fill_n(XALAN_STD_QUALIFIER back_inserter(*this), initialSize, defaultValue.value);
    }

    XalanDeque(const XalanDeque& theRhs, MemoryManager& memoryManager) :
        m_memoryManager(&memoryManager),
        m_blockSize(theRhs.m_blockSize),
        m_blockIndex(*theRhs.m_memoryManager,
                    theRhs.size() / theRhs.m_blockSize + (theRhs.size() % theRhs.m_blockSize == 0 ? 0 : 1)),
        m_freeBlockVector(memoryManager)
    {
        XALAN_STD_QUALIFIER copy(theRhs.begin(), theRhs.end(), XALAN_STD_QUALIFIER back_inserter(*this));
    }

   static XalanDeque*
   create(
            MemoryManager&  theManager,
            size_type       initialSize = 0,
            size_type       blockSize = 10)
    {
        typedef XalanDeque ThisType;

        XalanAllocationGuard    theGuard(theManager, theManager.allocate(sizeof(ThisType)));

        ThisType* const     theResult =
            new (theGuard.get()) ThisType(theManager, initialSize, blockSize);

        theGuard.release();

        return theResult;
    }

    ~XalanDeque()
    {
        destroyBlockList(m_freeBlockVector);

        destroyBlockList(m_blockIndex);
    }

    iterator begin()
    {
        return iterator(this, 0);
    }

    const_iterator begin() const
    {
        return const_iterator(const_cast<XalanDeque*>(this), 0);
    }

    iterator end()
    {
        return iterator(this, size());
    }

    const_iterator end() const
    {
         return const_iterator(const_cast<XalanDeque*>(this), size());
    }

    const_reverse_iterator rbegin() const
    {
        return const_reverse_iterator(end());
    }

    const_reverse_iterator rend() const
    {
        return const_reverse_iterator(begin());
    }

    bool empty() const
    {
        return m_blockIndex.empty();
    }

    size_type size() const
    {
       if (m_blockIndex.empty())
       {
           return 0;
       }
       else
       {
           return (m_blockIndex.size() - 1) * m_blockSize
               +  m_blockIndex.back()->size();
       }
    }

    value_type& back()
    {
        return m_blockIndex.back()->back();
    }

    value_type& operator[](size_type index)
    {
        BlockType & block = *(m_blockIndex[index / m_blockSize]);
        return block[index % m_blockSize];
    }

    const value_type& operator[](size_type index) const
    {
        BlockType & block = *(m_blockIndex[index / m_blockSize]);
        return block[index % m_blockSize];
    }

    void clear()
    {
        typename BlockIndexType::iterator iter = m_blockIndex.begin();

        m_freeBlockVector.reserve(m_freeBlockVector.size() + m_blockIndex.size());

        while (iter != m_blockIndex.end())
        {
            (*iter)->clear();
            m_freeBlockVector.push_back(*iter);
            ++iter;
        }

        m_blockIndex.clear();
    }

    void push_back(const value_type & value)
    {
        if (m_blockIndex.empty() ||
            m_blockIndex.back()->size() >= m_blockSize)
        {
            m_blockIndex.push_back(getNewBlock());
        }

        m_blockIndex.back()->push_back(value);
    }

    void pop_back()
    {
        BlockType & lastBlock = *(m_blockIndex.back());
        lastBlock.pop_back();
        if (lastBlock.empty())
        {
            m_freeBlockVector.push_back(&lastBlock);
            m_blockIndex.pop_back();
        }
    }

    void resize(size_type newSize)
    {
        typename Constructor::ConstructableType  defaultValue(*m_memoryManager);

        if (newSize > size())
        {
            for (size_type i = 0; i < newSize - size(); ++i)
            {
                push_back(defaultValue.value);
            }
        }
        else
        {
            for (size_type i = 0; i < size() - newSize; ++i)
            {
                pop_back();
            }
        }
    }

    void swap(XalanDeque& theRhs)
    {
        MemoryManager* tempMemoryManager = m_memoryManager;
        m_memoryManager = theRhs.m_memoryManager;
        theRhs.m_memoryManager = tempMemoryManager;

        theRhs.m_blockIndex.swap(m_blockIndex);
        theRhs.m_freeBlockVector.swap(m_freeBlockVector);
    }

    XalanDeque & operator=(const XalanDeque & theRhs)
    {
        clear();
        XALAN_STD_QUALIFIER copy(theRhs.begin(), theRhs.end(), XALAN_STD_QUALIFIER back_inserter(*this));
        return *this;
    }

    MemoryManager&
    getMemoryManager()
    {
        assert (m_memoryManager != 0);

        return *m_memoryManager;
    }

protected:

    BlockType* getNewBlock()
    {
        BlockType * newBlock;

        if (m_freeBlockVector.empty())
        {
            newBlock = allocate(1);
            new (&*newBlock) BlockType(*m_memoryManager, m_blockSize);
        }
        else
        {
            newBlock = m_freeBlockVector.back();
            m_freeBlockVector.pop_back();
        }

        assert (newBlock != 0);

        return newBlock;
    }

    BlockType*
    allocate(size_type  size)
    {
        const size_type     theBytesNeeded = size * sizeof(BlockType);

        BlockType* pointer = (BlockType*)m_memoryManager->allocate(theBytesNeeded);

        assert(pointer != 0);

        return pointer;
    }

    void
    destroyBlockList(BlockIndexType&    theBlockIndex)
    {
        typename BlockIndexType::iterator iter =
            theBlockIndex.begin();

        while (iter != theBlockIndex.end())
        {
            XalanDestroy(*m_memoryManager, *(*iter));

            ++iter;
        }
    }

    void
    deallocate(BlockType*  pointer)
    {
        m_memoryManager->deallocate(pointer);
    }

    MemoryManager*  m_memoryManager;
    const size_type     m_blockSize;

    BlockIndexType     m_blockIndex;
    BlockIndexType     m_freeBlockVector;

private:

    // These are not implemented
    XalanDeque();

    XalanDeque(const XalanDeque&);
};

XALAN_CPP_NAMESPACE_END

#endif  // XALANDEQUE_HEADER_GUARD_1357924680

<commit_msg>Removed obsolete author tag. Fixed assignment operator to protect against self-assignment. Updated XalanDequeIterator and XalanDeque to avoid exposing data members.  Fixed some issues where exceptions might cause memory leaks. Used a typedef for better clarity, and added and updated some comments.<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.
 */

#if !defined(XALANDEQUE_HEADER_GUARD_1357924680)
#define XALANDEQUE_HEADER_GUARD_1357924680

// Base include file.  Must be first.
#include <xalanc/Include/PlatformDefinitions.hpp>

#include <xalanc/Include/XalanVector.hpp>
#include <xalanc/Include/XalanMemoryManagement.hpp>

XALAN_CPP_NAMESPACE_BEGIN

template <class Value>
struct XalanDequeIteratorTraits
{
    typedef Value           value_type;
    typedef Value&          reference;
    typedef Value*          pointer;
    typedef const Value&    const_reference;
};

template <class Value>
struct XalanDequeConstIteratorTraits
{
    typedef Value           value_type;
    typedef const Value&    reference;
    typedef const Value*    pointer;
    typedef const Value&    const_reference;
};

template <class Traits, class XalanDeque>
class XalanDequeIterator
{
public:

    typedef size_t                              size_type;
    typedef typename Traits::value_type         value_type;
    typedef typename Traits::reference          reference;
    typedef typename Traits::pointer            pointer;
    typedef typename Traits::const_reference    const_reference;
    typedef ptrdiff_t                           difference_type;

    typedef XALAN_STD_QUALIFIER random_access_iterator_tag  iterator_category;

    // The non-const iterator type.  In the case of the non-const instatiation, this
    // is the same type.
    typedef XalanDequeIterator<XalanDequeIteratorTraits<value_type>, XalanDeque>    Iterator;

    // The const version needs access to our private data members for copy construction and
    // assignment.  For the const instantiation, this is a superfluous friend declaration,
    // since it's the same type as the class itself.
    friend class XalanDequeIterator<XalanDequeConstIteratorTraits<value_type>, XalanDeque>;

    XalanDequeIterator(
                XalanDeque*     deque,
                size_type       pos) :
        m_deque(deque),
        m_pos(pos)
    {
    }

    // The is standard copy-construction for the non-const iterator type.  For the
    // const iterator type, this is copy construction from the non-const type, and the
    // compiler will generate the standard copy constructor.
    XalanDequeIterator(const Iterator&  iterator) :
        m_deque(iterator.m_deque),
        m_pos(iterator.m_pos)
    {
    }

    // The is the standard assignment operator for the non-const iterator type.
    // For the const iterator type, this is the assignment operator from the
    // non-const type, and the compiler will generate the standard assignment
    // operator.
    XalanDequeIterator&
    operator=(const Iterator&   iterator)
    {
        m_deque = iterator.m_deque;
        m_pos = iterator.m_pos;

        return *this;
    }

    XalanDequeIterator&
    operator++()
    {
        ++m_pos;

        return *this;
    }

    XalanDequeIterator
    operator++(int)
    {
        XalanDequeIterator temp = *this;
        ++m_pos;

        return temp;
    }

    XalanDequeIterator&
    operator--()
    {
        --m_pos;

        return *this;
    }

    pointer
    operator->()
    {
        return &(*m_deque[m_pos]);
    }

    reference
    operator*()
    {
        return (*m_deque)[m_pos];
    }

    const_reference
    operator*() const
    {
        return (*m_deque)[m_pos];
    }

    XalanDequeIterator
    operator+(difference_type   difference) const
    {
        return XalanDequeIterator(m_deque, m_pos + difference);
    }

    XalanDequeIterator
    operator-(difference_type   difference) const
    {
        return XalanDequeIterator(m_deque, m_pos - difference);
    }

    difference_type
    operator-(const XalanDequeIterator&     theRHS) const
    {
        return m_pos - theRHS.m_pos;
    }

    bool
    operator==(const XalanDequeIterator&    theRHS) const
    {
        return theRHS.m_deque == m_deque &&
               theRHS.m_pos == m_pos;
    }

    bool
    operator!=(const XalanDequeIterator&   theRHS) const
    {
        return !(theRHS == *this);
    }

    bool
    operator<(const XalanDequeIterator&     theRHS) const
    {
        return m_pos < theRHS.m_pos;
    }

private:

    XalanDeque*     m_deque;

    size_type       m_pos;
};

/**
 * Xalan implementation of deque
 */
template <class Type, class ConstructionTraits = MemoryManagedConstructionTraits<Type> >
class XalanDeque
{
public:

    typedef size_t  size_type;

    typedef Type            value_type;
    typedef Type&           reference;
    typedef const Type&     const_reference;

    typedef XalanVector<Type, ConstructionTraits>   BlockType;
    typedef XalanVector<BlockType*>                 BlockIndexType;

    typedef XalanDeque<Type, ConstructionTraits>    ThisType;

    typedef XalanDequeIterator<XalanDequeIteratorTraits<value_type>, ThisType>          iterator;
    typedef XalanDequeIterator<XalanDequeConstIteratorTraits<value_type>, ThisType>     const_iterator;

#if defined(XALAN_HAS_STD_ITERATORS)
    typedef XALAN_STD_QUALIFIER reverse_iterator<iterator>          reverse_iterator_;
    typedef XALAN_STD_QUALIFIER reverse_iterator<const_iterator>    const_reverse_iterator_;
#elif defined(XALAN_RW_NO_CLASS_PARTIAL_SPEC)
    typedef typename iterator::iterator_category    iterator_category;

    // This is a specific case for the Rogue Wave STL on Solaris.
    typedef XALAN_STD_QUALIFIER reverse_iterator<
        iterator,
        iterator_category,
        value_type> reverse_iterator_;

    typedef XALAN_STD_QUALIFIER reverse_iterator<
        const_iterator,
        iterator_category,
        const value_type> const_reverse_iterator_;
#else
    typedef XALAN_STD_QUALIFIER reverse_iterator<
        iterator,
        value_type>        reverse_iterator_;

    typedef XALAN_STD_QUALIFIER reverse_iterator<
        const_iterator,
        value_type,
        const_reference>   const_reverse_iterator_;
#endif

    typedef reverse_iterator_           reverse_iterator;
    typedef const_reverse_iterator_     const_reverse_iterator;

    typedef typename ConstructionTraits::Constructor    Constructor;
    typedef typename Constructor::ConstructableType     ConstructableType;

    XalanDeque(
            MemoryManager&  memoryManager,
            size_type       initialSize = 0,
            size_type       blockSize = 10) :
        m_memoryManager(&memoryManager),
        m_blockSize(blockSize),
        m_blockIndex(memoryManager,
                    initialSize / blockSize + (initialSize % blockSize == 0 ? 0 : 1)),
        m_freeBlockVector(memoryManager)
    {
        const ConstructableType     defaultValue(*m_memoryManager);

        XALAN_USING_STD(fill_n)
        XALAN_USING_STD(back_inserter)

        fill_n(
            back_inserter(*this),
            initialSize,
            defaultValue.value);
    }

    XalanDeque(
                const XalanDeque&   theRHS,
                MemoryManager&      theMemoryManager) :
        m_memoryManager(&theMemoryManager),
        m_blockSize(theRHS.m_blockSize),
        m_blockIndex(*theRHS.m_memoryManager,
                    theRHS.size() / theRHS.m_blockSize + (theRHS.size() % theRHS.m_blockSize == 0 ? 0 : 1)),
        m_freeBlockVector(theMemoryManager)
    {
        XALAN_USING_STD(copy)
        XALAN_USING_STD(back_inserter)

        copy(
            theRHS.begin(),
            theRHS.end(),
            back_inserter(*this));
    }

   static XalanDeque*
   create(
            MemoryManager&  theManager,
            size_type       initialSize = 0,
            size_type       blockSize = 10)
    {
        XalanAllocationGuard    theGuard(theManager, theManager.allocate(sizeof(ThisType)));

        ThisType* const     theResult =
            new (theGuard.get()) ThisType(theManager, initialSize, blockSize);

        theGuard.release();

        return theResult;
    }

    ~XalanDeque()
    {
        destroyBlockList(m_freeBlockVector);

        destroyBlockList(m_blockIndex);
    }

    iterator
    begin()
    {
        return iterator(this, 0);
    }

    const_iterator
    begin() const
    {
        return const_iterator(const_cast<XalanDeque*>(this), 0);
    }

    iterator
    end()
    {
        return iterator(this, size());
    }

    const_iterator
    end() const
    {
         return const_iterator(const_cast<XalanDeque*>(this), size());
    }

    const_reverse_iterator
    rbegin() const
    {
        return const_reverse_iterator(end());
    }

    const_reverse_iterator
    rend() const
    {
        return const_reverse_iterator(begin());
    }

    bool
    empty() const
    {
        return m_blockIndex.empty();
    }

    size_type
    size() const
    {
       if (m_blockIndex.empty())
       {
           return 0;
       }
       else
       {
           return (m_blockIndex.size() - 1) * m_blockSize
               +  m_blockIndex.back()->size();
       }
    }

    value_type&
    back()
    {
        return m_blockIndex.back()->back();
    }

    value_type&
    operator[](size_type    index)
    {
        BlockType&  block = *m_blockIndex[index / m_blockSize];

        return block[index % m_blockSize];
    }

    const value_type&
    operator[](size_type    index) const
    {
        BlockType&  block = *m_blockIndex[index / m_blockSize];

        return block[index % m_blockSize];
    }

    void
    clear()
    {
        typename BlockIndexType::iterator   iter = m_blockIndex.begin();

        m_freeBlockVector.reserve(m_freeBlockVector.size() + m_blockIndex.size());

        while (iter != m_blockIndex.end())
        {
            (*iter)->clear();
            m_freeBlockVector.push_back(*iter);
            ++iter;
        }

        m_blockIndex.clear();
    }

    void
    push_back(const value_type&     value)
    {
        if (m_blockIndex.empty() ||
            m_blockIndex.back()->size() >= m_blockSize)
        {
            pushNewIndexBlock();
        }

        m_blockIndex.back()->push_back(value);
    }

    void
    pop_back()
    {
        assert(!empty());

        BlockType&  lastBlock = *m_blockIndex.back();
        lastBlock.pop_back();

        if (lastBlock.empty())
        {
            m_freeBlockVector.push_back(&lastBlock);
            m_blockIndex.pop_back();
        }
    }

    void
    resize(size_type    newSize)
    {
        const ConstructableType     defaultValue(*m_memoryManager);

        if (newSize > size())
        {
            for (size_type i = 0; i < newSize - size(); ++i)
            {
                push_back(defaultValue.value);
            }
        }
        else
        {
            for (size_type i = 0; i < size() - newSize; ++i)
            {
                pop_back();
            }
        }
    }

    void
    swap(XalanDeque&    theRHS)
    {
        XALAN_USING_STD(swap)

        swap(m_memoryManager, theRHS.m_memoryManager);

        theRHS.m_blockIndex.swap(m_blockIndex);
        theRHS.m_freeBlockVector.swap(m_freeBlockVector);
    }

    XalanDeque&
    operator=(const XalanDeque&     theRHS)
    {
        if (this != &theRHS)
        {
            XALAN_USING_STD(copy)
            XALAN_USING_STD(back_inserter)

            clear();

            copy(
                theRHS.begin(),
                theRHS.end(),
                back_inserter(*this));
        }

        return *this;
    }

    MemoryManager&
    getMemoryManager()
    {
        assert (m_memoryManager != 0);

        return *m_memoryManager;
    }

private:

    void
    pushNewIndexBlock()
    {
        // Allocate space first, so we don't have to worry
        // about an out-of-memory error once we've constructed
        // the new block.
        m_blockIndex.push_back(0);

        if (m_freeBlockVector.empty())
        {
            XalanConstruct(
                *m_memoryManager,
                m_blockIndex.back(),
                *m_memoryManager,
                m_blockSize);
        }
        else
        {
            m_blockIndex.back() = m_freeBlockVector.back();

            // Now that ownership has been transfered, pop
            // it off the free list.
            m_freeBlockVector.pop_back();
        }

        assert(m_blockIndex.back() != 0);
    }

    void
    destroyBlockList(BlockIndexType&    theBlockIndex)
    {
        typename BlockIndexType::iterator iter =
            theBlockIndex.begin();

        while (iter != theBlockIndex.end())
        {
            XalanDestroy(*m_memoryManager, *iter);

            ++iter;
        }
    }

    void
    deallocate(BlockType*  pointer)
    {
        m_memoryManager->deallocate(pointer);
    }

    MemoryManager*      m_memoryManager;

    const size_type     m_blockSize;

    BlockIndexType     m_blockIndex;
    BlockIndexType     m_freeBlockVector;

    // These are not implemented
    XalanDeque();

    XalanDeque(const XalanDeque&);
};

XALAN_CPP_NAMESPACE_END

#endif  // XALANDEQUE_HEADER_GUARD_1357924680

<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/cpu/stack_walker.h"

#include <gflags/gflags.h>

#include <mutex>

#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h"
#include "xenia/cpu/backend/backend.h"
#include "xenia/cpu/backend/code_cache.h"
#include "xenia/cpu/processor.h"

DEFINE_bool(debug_symbol_loader, false,
            "Enable dbghelp debug logging and validation.");

// Must be included after platform_win.h:
#pragma warning(push)
#pragma warning(disable : 4091)
#include <dbghelp.h>  // NOLINT(build/include_order)
#pragma warning(pop)

typedef DWORD(__stdcall* LPSYMGETOPTIONS)(VOID);
typedef DWORD(__stdcall* LPSYMSETOPTIONS)(IN DWORD SymOptions);
typedef BOOL(__stdcall* LPSYMINITIALIZE)(IN HANDLE hProcess,
                                         IN PSTR UserSearchPath,
                                         IN BOOL fInvadeProcess);
typedef BOOL(__stdcall* LPSTACKWALK64)(
    DWORD MachineType, HANDLE hProcess, HANDLE hThread,
    LPSTACKFRAME64 StackFrame, PVOID ContextRecord,
    PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
    PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
    PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
    PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
typedef PVOID(__stdcall* LPSYMFUNCTIONTABLEACCESS64)(
    HANDLE hProcess,
    DWORD64 AddrBase);  // DbgHelp.h typedef PFUNCTION_TABLE_ACCESS_ROUTINE64
typedef DWORD64(__stdcall* LPSYMGETMODULEBASE64)(
    HANDLE hProcess,
    DWORD64 AddrBase);  // DbgHelp.h typedef PGET_MODULE_BASE_ROUTINE64
typedef BOOL(__stdcall* LPSYMGETSYMFROMADDR64)(IN HANDLE hProcess,
                                               IN DWORD64 qwAddr,
                                               OUT PDWORD64 pdwDisplacement,
                                               OUT PIMAGEHLP_SYMBOL64 Symbol);

LPSYMGETOPTIONS sym_get_options_ = nullptr;
LPSYMSETOPTIONS sym_set_options_ = nullptr;
LPSYMINITIALIZE sym_initialize_ = nullptr;
LPSTACKWALK64 stack_walk_64_ = nullptr;
LPSYMFUNCTIONTABLEACCESS64 sym_function_table_access_64_ = nullptr;
LPSYMGETMODULEBASE64 sym_get_module_base_64_ = nullptr;
LPSYMGETSYMFROMADDR64 sym_get_sym_from_addr_64_ = nullptr;

namespace xe {
namespace cpu {

bool InitializeStackWalker() {
  if (sym_get_options_) {
    // Already initialized.
    return true;
  }

  // Attempt to load dbghelp.
  // NOTE: we never free it. That's fine.
  HMODULE module = LoadLibrary(TEXT("dbghelp.dll"));
  if (!module) {
    XELOGE("Unable to load dbghelp.dll - not found on path or invalid");
    return false;
  }
  sym_get_options_ = reinterpret_cast<LPSYMGETOPTIONS>(
      GetProcAddress(module, "SymGetOptions"));
  sym_set_options_ = reinterpret_cast<LPSYMSETOPTIONS>(
      GetProcAddress(module, "SymSetOptions"));
  sym_initialize_ = reinterpret_cast<LPSYMINITIALIZE>(
      GetProcAddress(module, "SymInitialize"));
  stack_walk_64_ =
      reinterpret_cast<LPSTACKWALK64>(GetProcAddress(module, "StackWalk64"));
  sym_function_table_access_64_ = reinterpret_cast<LPSYMFUNCTIONTABLEACCESS64>(
      GetProcAddress(module, "SymFunctionTableAccess64"));
  sym_get_module_base_64_ = reinterpret_cast<LPSYMGETMODULEBASE64>(
      GetProcAddress(module, "SymGetModuleBase64"));
  sym_get_sym_from_addr_64_ = reinterpret_cast<LPSYMGETSYMFROMADDR64>(
      GetProcAddress(module, "SymGetSymFromAddr64"));
  if (!sym_get_options_ || !sym_set_options_ || !sym_initialize_ ||
      !stack_walk_64_ || !sym_function_table_access_64_ ||
      !sym_get_module_base_64_ || !sym_get_sym_from_addr_64_) {
    XELOGE("Unable to get one or more symbols from dbghelp.dll");
    return false;
  }

  // Initialize the symbol lookup services.
  DWORD options = sym_get_options_();
  if (FLAGS_debug_symbol_loader) {
    options |= SYMOPT_DEBUG;
  }
  options |= SYMOPT_DEFERRED_LOADS;
  options |= SYMOPT_LOAD_LINES;
  options |= SYMOPT_FAIL_CRITICAL_ERRORS;
  sym_set_options_(options);
  if (!sym_initialize_(GetCurrentProcess(), nullptr, TRUE)) {
    XELOGE("Unable to initialize symbol services");
    return false;
  }

  return true;
}

class Win32StackWalker : public StackWalker {
 public:
  explicit Win32StackWalker(backend::CodeCache* code_cache) {
    // Get the boundaries of the code cache so we can quickly tell if a symbol
    // is ours or not.
    // We store these globally so that the Sym* callbacks can access them.
    // They never change, so it's fine even if they are touched from multiple
    // threads.
    code_cache_ = code_cache;
    code_cache_min_ = code_cache_->base_address();
    code_cache_max_ = code_cache_->base_address() + code_cache_->total_size();
  }

  bool Initialize() {
    std::lock_guard<std::mutex> lock(dbghelp_mutex_);
    return InitializeStackWalker();
  }

  size_t CaptureStackTrace(uint64_t* frame_host_pcs, size_t frame_offset,
                           size_t frame_count,
                           uint64_t* out_stack_hash) override {
    *out_stack_hash = 0;
    // Simple method: captures just stack frame PC addresses, optionally
    // computing a whole-stack hash.
    ULONG back_trace_hash = 0;
    DWORD frames_to_skip = DWORD(frame_offset) + 1;
    DWORD frames_to_capture =
        std::min(DWORD(frame_count), UINT16_MAX - frames_to_skip);
    USHORT captured_count = CaptureStackBackTrace(
        frames_to_skip, frames_to_capture,
        reinterpret_cast<PVOID*>(frame_host_pcs), &back_trace_hash);
    if (out_stack_hash) {
      *out_stack_hash = back_trace_hash;
    }
    return captured_count;
  }

  size_t CaptureStackTrace(void* thread_handle, uint64_t* frame_host_pcs,
                           size_t frame_offset, size_t frame_count,
                           const X64Context* in_host_context,
                           X64Context* out_host_context,
                           uint64_t* out_stack_hash) override {
    // TODO(benvanik): use xstate?
    // https://msdn.microsoft.com/en-us/library/windows/desktop/hh134240(v=vs.85).aspx
    // Query context. Thread must be suspended.
    // Need at least CONTEXT_CONTROL (for rip and rsp) and CONTEXT_INTEGER (for
    // rbp).
    CONTEXT thread_context;
    if (!in_host_context) {
      // If not given a context we need to ask for it.
      // This gets the state of the thread exactly where it was when suspended.
      thread_context.ContextFlags = CONTEXT_FULL;
      if (!GetThreadContext(thread_handle, &thread_context)) {
        XELOGE("Unable to read thread context for stack walk");
        return 0;
      }
    } else {
      // Copy thread context local. We will be modifying it during stack
      // walking, so we don't want to mess with the incoming copy.
      thread_context.Rip = in_host_context->rip;
      thread_context.EFlags = in_host_context->eflags;
      std::memcpy(&thread_context.Rax, in_host_context->int_registers,
                  sizeof(in_host_context->int_registers));
      std::memcpy(&thread_context.Xmm0, in_host_context->xmm_registers,
                  sizeof(in_host_context->xmm_registers));
    }

    if (out_host_context) {
      // Write out the captured thread context if the caller asked for it.
      out_host_context->rip = thread_context.Rip;
      out_host_context->eflags = thread_context.EFlags;
      std::memcpy(out_host_context->int_registers, &thread_context.Rax,
                  sizeof(out_host_context->int_registers));
      std::memcpy(out_host_context->xmm_registers, &thread_context.Xmm0,
                  sizeof(out_host_context->xmm_registers));
    }

    // Setup the frame for walking.
    STACKFRAME64 stack_frame = {0};
    stack_frame.AddrPC.Mode = AddrModeFlat;
    stack_frame.AddrPC.Offset = thread_context.Rip;
    stack_frame.AddrFrame.Mode = AddrModeFlat;
    stack_frame.AddrFrame.Offset = thread_context.Rbp;
    stack_frame.AddrStack.Mode = AddrModeFlat;
    stack_frame.AddrStack.Offset = thread_context.Rsp;

    // Walk the stack.
    // Note that StackWalk64 is thread safe, though other dbghelp functions are
    // not.
    size_t frame_index = 0;
    while (frame_index < frame_count &&
           stack_walk_64_(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(),
                          thread_handle, &stack_frame, &thread_context, nullptr,
                          XSymFunctionTableAccess64, XSymGetModuleBase64,
                          nullptr) == TRUE) {
      if (frame_index >= frame_offset) {
        frame_host_pcs[frame_index - frame_offset] = stack_frame.AddrPC.Offset;
      }
      ++frame_index;
    }

    return frame_index - frame_offset;
  }

  bool ResolveStack(uint64_t* frame_host_pcs, StackFrame* frames,
                    size_t frame_count) override {
    // TODO(benvanik): collect symbols to resolve with dbghelp and resolve
    // afterward in a smaller lock.
    std::lock_guard<std::mutex> lock(dbghelp_mutex_);

    for (size_t i = 0; i < frame_count; ++i) {
      auto& frame = frames[i];
      std::memset(&frame, 0, sizeof(frame));
      frame.host_pc = frame_host_pcs[i];

      // If in the generated range, we know it's ours.
      if (frame.host_pc >= code_cache_min_ && frame.host_pc < code_cache_max_) {
        // Guest symbol, so we can look it up quickly in the code cache.
        frame.type = StackFrame::Type::kGuest;
        auto function = code_cache_->LookupFunction(frame.host_pc);
        if (function) {
          frame.guest_symbol.function = function;
          // Figure out where in guest code we are by looking up the
          // displacement in x64 from the JIT'ed code start to the PC.
          if (function->is_guest()) {
            auto guest_function = static_cast<GuestFunction*>(function);
            frame.guest_pc =
                guest_function->MapMachineCodeToGuestAddress(frame.host_pc);
          }
        } else {
          frame.guest_symbol.function = nullptr;
        }
      } else {
        // Host symbol, which means either emulator or system.
        frame.type = StackFrame::Type::kHost;
        // TODO(benvanik): cache so that we can avoid calling into dbghelp (and
        // taking the lock).
        union {
          IMAGEHLP_SYMBOL64 info;
          uint8_t buffer[sizeof(IMAGEHLP_SYMBOL64) +
                         MAX_SYM_NAME * sizeof(CHAR) + sizeof(ULONG64) - 1];
        } symbol;
        symbol.info.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
        symbol.info.MaxNameLength = MAX_SYM_NAME;
        uint64_t displacement = 0;
        if (sym_get_sym_from_addr_64_(GetCurrentProcess(), frame.host_pc,
                                      &displacement, &symbol.info)) {
          // Resolved successfully.
          // TODO(benvanik): stash: module, base, displacement, name?
          frame.host_symbol.address = symbol.info.Address;
          std::strncpy(frame.host_symbol.name, symbol.info.Name, 256);
        }
      }
    }
    return true;
  }

 private:
  static PVOID WINAPI XSymFunctionTableAccess64(__in HANDLE hProcess,
                                                __in DWORD64 AddrBase) {
    if (AddrBase >= code_cache_min_ && AddrBase < code_cache_max_) {
      // Within our generated range so ask code cache.
      return code_cache_->LookupUnwindInfo(AddrBase);
    }
    // Normal symbol lookup.
    return sym_function_table_access_64_(hProcess, AddrBase);
  }

  static DWORD64 WINAPI XSymGetModuleBase64(_In_ HANDLE hProcess,
                                            _In_ DWORD64 dwAddr) {
    if (dwAddr >= code_cache_min_ && dwAddr < code_cache_max_) {
      // In our generated range all addresses are relative to the code cache
      // base.
      return code_cache_min_;
    }
    // Normal module base lookup.
    return sym_get_module_base_64_(hProcess, dwAddr);
  }

  std::mutex dbghelp_mutex_;

  static xe::cpu::backend::CodeCache* code_cache_;
  static uint32_t code_cache_min_;
  static uint32_t code_cache_max_;
};

xe::cpu::backend::CodeCache* Win32StackWalker::code_cache_ = nullptr;
uint32_t Win32StackWalker::code_cache_min_ = 0;
uint32_t Win32StackWalker::code_cache_max_ = 0;

std::unique_ptr<StackWalker> StackWalker::Create(
    backend::CodeCache* code_cache) {
  auto stack_walker = std::make_unique<Win32StackWalker>(code_cache);
  if (!stack_walker->Initialize()) {
    XELOGE("Unable to initialize stack walker");
    return nullptr;
  }
  return std::unique_ptr<StackWalker>(stack_walker.release());
}

}  // namespace cpu
}  // namespace xe
<commit_msg>Fix CaptureStackTrace with null out_stack_hash<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/cpu/stack_walker.h"

#include <gflags/gflags.h>

#include <mutex>

#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h"
#include "xenia/cpu/backend/backend.h"
#include "xenia/cpu/backend/code_cache.h"
#include "xenia/cpu/processor.h"

DEFINE_bool(debug_symbol_loader, false,
            "Enable dbghelp debug logging and validation.");

// Must be included after platform_win.h:
#pragma warning(push)
#pragma warning(disable : 4091)
#include <dbghelp.h>  // NOLINT(build/include_order)
#pragma warning(pop)

typedef DWORD(__stdcall* LPSYMGETOPTIONS)(VOID);
typedef DWORD(__stdcall* LPSYMSETOPTIONS)(IN DWORD SymOptions);
typedef BOOL(__stdcall* LPSYMINITIALIZE)(IN HANDLE hProcess,
                                         IN PSTR UserSearchPath,
                                         IN BOOL fInvadeProcess);
typedef BOOL(__stdcall* LPSTACKWALK64)(
    DWORD MachineType, HANDLE hProcess, HANDLE hThread,
    LPSTACKFRAME64 StackFrame, PVOID ContextRecord,
    PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
    PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
    PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
    PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
typedef PVOID(__stdcall* LPSYMFUNCTIONTABLEACCESS64)(
    HANDLE hProcess,
    DWORD64 AddrBase);  // DbgHelp.h typedef PFUNCTION_TABLE_ACCESS_ROUTINE64
typedef DWORD64(__stdcall* LPSYMGETMODULEBASE64)(
    HANDLE hProcess,
    DWORD64 AddrBase);  // DbgHelp.h typedef PGET_MODULE_BASE_ROUTINE64
typedef BOOL(__stdcall* LPSYMGETSYMFROMADDR64)(IN HANDLE hProcess,
                                               IN DWORD64 qwAddr,
                                               OUT PDWORD64 pdwDisplacement,
                                               OUT PIMAGEHLP_SYMBOL64 Symbol);

LPSYMGETOPTIONS sym_get_options_ = nullptr;
LPSYMSETOPTIONS sym_set_options_ = nullptr;
LPSYMINITIALIZE sym_initialize_ = nullptr;
LPSTACKWALK64 stack_walk_64_ = nullptr;
LPSYMFUNCTIONTABLEACCESS64 sym_function_table_access_64_ = nullptr;
LPSYMGETMODULEBASE64 sym_get_module_base_64_ = nullptr;
LPSYMGETSYMFROMADDR64 sym_get_sym_from_addr_64_ = nullptr;

namespace xe {
namespace cpu {

bool InitializeStackWalker() {
  if (sym_get_options_) {
    // Already initialized.
    return true;
  }

  // Attempt to load dbghelp.
  // NOTE: we never free it. That's fine.
  HMODULE module = LoadLibrary(TEXT("dbghelp.dll"));
  if (!module) {
    XELOGE("Unable to load dbghelp.dll - not found on path or invalid");
    return false;
  }
  sym_get_options_ = reinterpret_cast<LPSYMGETOPTIONS>(
      GetProcAddress(module, "SymGetOptions"));
  sym_set_options_ = reinterpret_cast<LPSYMSETOPTIONS>(
      GetProcAddress(module, "SymSetOptions"));
  sym_initialize_ = reinterpret_cast<LPSYMINITIALIZE>(
      GetProcAddress(module, "SymInitialize"));
  stack_walk_64_ =
      reinterpret_cast<LPSTACKWALK64>(GetProcAddress(module, "StackWalk64"));
  sym_function_table_access_64_ = reinterpret_cast<LPSYMFUNCTIONTABLEACCESS64>(
      GetProcAddress(module, "SymFunctionTableAccess64"));
  sym_get_module_base_64_ = reinterpret_cast<LPSYMGETMODULEBASE64>(
      GetProcAddress(module, "SymGetModuleBase64"));
  sym_get_sym_from_addr_64_ = reinterpret_cast<LPSYMGETSYMFROMADDR64>(
      GetProcAddress(module, "SymGetSymFromAddr64"));
  if (!sym_get_options_ || !sym_set_options_ || !sym_initialize_ ||
      !stack_walk_64_ || !sym_function_table_access_64_ ||
      !sym_get_module_base_64_ || !sym_get_sym_from_addr_64_) {
    XELOGE("Unable to get one or more symbols from dbghelp.dll");
    return false;
  }

  // Initialize the symbol lookup services.
  DWORD options = sym_get_options_();
  if (FLAGS_debug_symbol_loader) {
    options |= SYMOPT_DEBUG;
  }
  options |= SYMOPT_DEFERRED_LOADS;
  options |= SYMOPT_LOAD_LINES;
  options |= SYMOPT_FAIL_CRITICAL_ERRORS;
  sym_set_options_(options);
  if (!sym_initialize_(GetCurrentProcess(), nullptr, TRUE)) {
    XELOGE("Unable to initialize symbol services");
    return false;
  }

  return true;
}

class Win32StackWalker : public StackWalker {
 public:
  explicit Win32StackWalker(backend::CodeCache* code_cache) {
    // Get the boundaries of the code cache so we can quickly tell if a symbol
    // is ours or not.
    // We store these globally so that the Sym* callbacks can access them.
    // They never change, so it's fine even if they are touched from multiple
    // threads.
    code_cache_ = code_cache;
    code_cache_min_ = code_cache_->base_address();
    code_cache_max_ = code_cache_->base_address() + code_cache_->total_size();
  }

  bool Initialize() {
    std::lock_guard<std::mutex> lock(dbghelp_mutex_);
    return InitializeStackWalker();
  }

  size_t CaptureStackTrace(uint64_t* frame_host_pcs, size_t frame_offset,
                           size_t frame_count,
                           uint64_t* out_stack_hash) override {
    if (out_stack_hash) {
      *out_stack_hash = 0;
    }
    // Simple method: captures just stack frame PC addresses, optionally
    // computing a whole-stack hash.
    ULONG back_trace_hash = 0;
    DWORD frames_to_skip = DWORD(frame_offset) + 1;
    DWORD frames_to_capture =
        std::min(DWORD(frame_count), UINT16_MAX - frames_to_skip);
    USHORT captured_count = CaptureStackBackTrace(
        frames_to_skip, frames_to_capture,
        reinterpret_cast<PVOID*>(frame_host_pcs), &back_trace_hash);
    if (out_stack_hash) {
      *out_stack_hash = back_trace_hash;
    }
    return captured_count;
  }

  size_t CaptureStackTrace(void* thread_handle, uint64_t* frame_host_pcs,
                           size_t frame_offset, size_t frame_count,
                           const X64Context* in_host_context,
                           X64Context* out_host_context,
                           uint64_t* out_stack_hash) override {
    // TODO(benvanik): use xstate?
    // https://msdn.microsoft.com/en-us/library/windows/desktop/hh134240(v=vs.85).aspx
    // Query context. Thread must be suspended.
    // Need at least CONTEXT_CONTROL (for rip and rsp) and CONTEXT_INTEGER (for
    // rbp).
    CONTEXT thread_context;
    if (!in_host_context) {
      // If not given a context we need to ask for it.
      // This gets the state of the thread exactly where it was when suspended.
      thread_context.ContextFlags = CONTEXT_FULL;
      if (!GetThreadContext(thread_handle, &thread_context)) {
        XELOGE("Unable to read thread context for stack walk");
        return 0;
      }
    } else {
      // Copy thread context local. We will be modifying it during stack
      // walking, so we don't want to mess with the incoming copy.
      thread_context.Rip = in_host_context->rip;
      thread_context.EFlags = in_host_context->eflags;
      std::memcpy(&thread_context.Rax, in_host_context->int_registers,
                  sizeof(in_host_context->int_registers));
      std::memcpy(&thread_context.Xmm0, in_host_context->xmm_registers,
                  sizeof(in_host_context->xmm_registers));
    }

    if (out_host_context) {
      // Write out the captured thread context if the caller asked for it.
      out_host_context->rip = thread_context.Rip;
      out_host_context->eflags = thread_context.EFlags;
      std::memcpy(out_host_context->int_registers, &thread_context.Rax,
                  sizeof(out_host_context->int_registers));
      std::memcpy(out_host_context->xmm_registers, &thread_context.Xmm0,
                  sizeof(out_host_context->xmm_registers));
    }

    // Setup the frame for walking.
    STACKFRAME64 stack_frame = {0};
    stack_frame.AddrPC.Mode = AddrModeFlat;
    stack_frame.AddrPC.Offset = thread_context.Rip;
    stack_frame.AddrFrame.Mode = AddrModeFlat;
    stack_frame.AddrFrame.Offset = thread_context.Rbp;
    stack_frame.AddrStack.Mode = AddrModeFlat;
    stack_frame.AddrStack.Offset = thread_context.Rsp;

    // Walk the stack.
    // Note that StackWalk64 is thread safe, though other dbghelp functions are
    // not.
    size_t frame_index = 0;
    while (frame_index < frame_count &&
           stack_walk_64_(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(),
                          thread_handle, &stack_frame, &thread_context, nullptr,
                          XSymFunctionTableAccess64, XSymGetModuleBase64,
                          nullptr) == TRUE) {
      if (frame_index >= frame_offset) {
        frame_host_pcs[frame_index - frame_offset] = stack_frame.AddrPC.Offset;
      }
      ++frame_index;
    }

    return frame_index - frame_offset;
  }

  bool ResolveStack(uint64_t* frame_host_pcs, StackFrame* frames,
                    size_t frame_count) override {
    // TODO(benvanik): collect symbols to resolve with dbghelp and resolve
    // afterward in a smaller lock.
    std::lock_guard<std::mutex> lock(dbghelp_mutex_);

    for (size_t i = 0; i < frame_count; ++i) {
      auto& frame = frames[i];
      std::memset(&frame, 0, sizeof(frame));
      frame.host_pc = frame_host_pcs[i];

      // If in the generated range, we know it's ours.
      if (frame.host_pc >= code_cache_min_ && frame.host_pc < code_cache_max_) {
        // Guest symbol, so we can look it up quickly in the code cache.
        frame.type = StackFrame::Type::kGuest;
        auto function = code_cache_->LookupFunction(frame.host_pc);
        if (function) {
          frame.guest_symbol.function = function;
          // Figure out where in guest code we are by looking up the
          // displacement in x64 from the JIT'ed code start to the PC.
          if (function->is_guest()) {
            auto guest_function = static_cast<GuestFunction*>(function);
            frame.guest_pc =
                guest_function->MapMachineCodeToGuestAddress(frame.host_pc);
          }
        } else {
          frame.guest_symbol.function = nullptr;
        }
      } else {
        // Host symbol, which means either emulator or system.
        frame.type = StackFrame::Type::kHost;
        // TODO(benvanik): cache so that we can avoid calling into dbghelp (and
        // taking the lock).
        union {
          IMAGEHLP_SYMBOL64 info;
          uint8_t buffer[sizeof(IMAGEHLP_SYMBOL64) +
                         MAX_SYM_NAME * sizeof(CHAR) + sizeof(ULONG64) - 1];
        } symbol;
        symbol.info.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
        symbol.info.MaxNameLength = MAX_SYM_NAME;
        uint64_t displacement = 0;
        if (sym_get_sym_from_addr_64_(GetCurrentProcess(), frame.host_pc,
                                      &displacement, &symbol.info)) {
          // Resolved successfully.
          // TODO(benvanik): stash: module, base, displacement, name?
          frame.host_symbol.address = symbol.info.Address;
          std::strncpy(frame.host_symbol.name, symbol.info.Name, 256);
        }
      }
    }
    return true;
  }

 private:
  static PVOID WINAPI XSymFunctionTableAccess64(__in HANDLE hProcess,
                                                __in DWORD64 AddrBase) {
    if (AddrBase >= code_cache_min_ && AddrBase < code_cache_max_) {
      // Within our generated range so ask code cache.
      return code_cache_->LookupUnwindInfo(AddrBase);
    }
    // Normal symbol lookup.
    return sym_function_table_access_64_(hProcess, AddrBase);
  }

  static DWORD64 WINAPI XSymGetModuleBase64(_In_ HANDLE hProcess,
                                            _In_ DWORD64 dwAddr) {
    if (dwAddr >= code_cache_min_ && dwAddr < code_cache_max_) {
      // In our generated range all addresses are relative to the code cache
      // base.
      return code_cache_min_;
    }
    // Normal module base lookup.
    return sym_get_module_base_64_(hProcess, dwAddr);
  }

  std::mutex dbghelp_mutex_;

  static xe::cpu::backend::CodeCache* code_cache_;
  static uint32_t code_cache_min_;
  static uint32_t code_cache_max_;
};

xe::cpu::backend::CodeCache* Win32StackWalker::code_cache_ = nullptr;
uint32_t Win32StackWalker::code_cache_min_ = 0;
uint32_t Win32StackWalker::code_cache_max_ = 0;

std::unique_ptr<StackWalker> StackWalker::Create(
    backend::CodeCache* code_cache) {
  auto stack_walker = std::make_unique<Win32StackWalker>(code_cache);
  if (!stack_walker->Initialize()) {
    XELOGE("Unable to initialize stack walker");
    return nullptr;
  }
  return std::unique_ptr<StackWalker>(stack_walker.release());
}

}  // namespace cpu
}  // namespace xe
<|endoftext|>


<commit_before><commit_msg>reader_permit: release_base_resources(): also update _resources<commit_after><|endoftext|>


<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/http/HttpFileHandler.h>
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HttpResponse.h>
#include <xzero/http/HttpRangeDef.h>
#include <xzero/http/HeaderFieldList.h>
#include <xzero/Random.h>
#include <xzero/io/File.h>
#include <xzero/io/FileView.h>
#include <xzero/UnixTime.h>
#include <xzero/Tokenizer.h>
#include <xzero/Buffer.h>
#include <xzero/RuntimeError.h>
#include <xzero/logging.h>
#include <xzero/sysconfig.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace xzero {
namespace http {

// {{{ helper methods
/**
 * Converts a range-spec into real offsets.
 *
 */
inline constexpr std::pair<size_t, size_t> makeOffsets(
    const std::pair<size_t, size_t>& p, size_t actualSize) {
  return p.first == HttpRangeDef::npos
             ? std::make_pair(actualSize - p.second,
                              actualSize - 1)  // last N bytes
             : p.second == HttpRangeDef::npos && p.second > actualSize
                   ? std::make_pair(p.first, actualSize - 1)  // from fixed N to
                                                              // the end of file
                   : std::make_pair(p.first, p.second);       // fixed range
}

/**
 * generates a boundary tag.
 *
 * \return a value usable as boundary tag.
 */
static std::string generateDefaultBoundaryID() {
  static Random rng;
  static const char* map = "0123456789abcdef";
  char buf[16 + 1];

  for (size_t i = 0; i < sizeof(buf) - 1; ++i)
    buf[i] = map[rng.random64() % (sizeof(buf) - 1)];

  buf[sizeof(buf) - 1] = '\0';

  return std::string(buf);
}
// }}}

HttpFileHandler::HttpFileHandler()
    : HttpFileHandler(std::bind(&generateDefaultBoundaryID)) {
}

HttpFileHandler::HttpFileHandler(std::function<std::string()> generateBoundaryID)
    : generateBoundaryID_(generateBoundaryID) {
}

HttpFileHandler::~HttpFileHandler() {
}

HttpStatus HttpFileHandler::handle(HttpRequest* request,
                                   HttpResponse* response,
                                   std::shared_ptr<File> transferFile) {
  if (!transferFile->isRegular())
    return HttpStatus::NotFound;

  HttpStatus status = handleClientCache(*transferFile, request, response);
  switch (status) {
    case HttpStatus::Undefined:
      break;
    case HttpStatus::NotModified:
      // 304
      response->setStatus(status);
      response->completed();
      return status;
    case HttpStatus::PreconditionFailed:
      // 412
      return status;
    default:
      logFatal("internal bug; unhandled status code");
  }

  switch (transferFile->errorCode()) {
    case 0:
      break;
    case ENOENT:
      return HttpStatus::NotFound;
    case EACCES:
    case EPERM:
      return HttpStatus::Forbidden;
    default:
      RAISE_ERRNO(transferFile->errorCode());
  }

  int fd = -1;
  if (request->method() == HttpMethod::GET) {
    fd = transferFile->createPosixChannel(File::Read | File::NonBlocking);
    if (fd < 0) {
      switch (errno) {
        case EPERM:
        case EACCES:
          return HttpStatus::Forbidden;
        case ENOSYS:
          // TODO: in that case we're prjobably on MemoryFile and we need to read differently
        default:
          RAISE_ERRNO(errno);
      }
    }
  } else if (request->method() != HttpMethod::HEAD) {
    return HttpStatus::MethodNotAllowed;
  }

  response->addHeader("Allow", "GET, HEAD");
  response->addHeader("Last-Modified", transferFile->lastModified());
  response->addHeader("ETag", transferFile->etag());

  if (handleRangeRequest(*transferFile, fd, request, response))
    return HttpStatus::PartialContent;

  // XXX Only set status code to 200 (Ok) when the status code hasn't been set
  // previously yet. This may occur due to an internal redirect.
  if (!isError(response->status()))
    response->setStatus(HttpStatus::Ok);

  response->addHeader("Accept-Ranges", "bytes");
  response->addHeader("Content-Type", transferFile->mimetype());

  response->setContentLength(transferFile->size());

  if (fd >= 0) {  // GET request
#if defined(HAVE_POSIX_FADVISE)
    posix_fadvise(fd, 0, transferFile->size(), POSIX_FADV_SEQUENTIAL);
#endif
    response->write(FileView(fd, 0, transferFile->size(), true));
    response->completed();
  } else {
    response->completed();
  }

  // This is the expected response status, even though it may have
  // been overridden (due to internal redirect by setting failure code
  // before invoking this handler).
  return HttpStatus::Ok;
}

HttpStatus HttpFileHandler::handleClientCache(const File& transferFile,
                                              HttpRequest* request,
                                              HttpResponse* response) {
  static const char* timeFormat = "%a, %d %b %Y %H:%M:%S GMT";

  // If-Modified-Since
  do {
    const std::string& value = request->headers().get("If-Modified-Since");
    if (value.empty()) continue;

    UnixTime dt(UnixTime::parseString(value, timeFormat).value());

    if (transferFile.mtime() > dt.unixtime()) continue;

    return HttpStatus::NotModified;
  } while (0);

  // If-Unmodified-Since
  do {
    const std::string& value = request->headers().get("If-Unmodified-Since");
    if (value.empty()) continue;

    UnixTime dt(UnixTime::parseString(value, timeFormat).value());

    if (transferFile.mtime() <= dt.unixtime()) continue;

    return HttpStatus::PreconditionFailed;
  } while (0);

  // If-Match
  do {
    const std::string& value = request->headers().get("If-Match");
    if (value.empty()) continue;

    if (value == "*") continue;

    // XXX: on static files we probably don't need the token-list support
    if (value == transferFile.etag()) continue;

    return HttpStatus::PreconditionFailed;
  } while (0);

  // If-None-Match
  do {
    const std::string& value = request->headers().get("If-None-Match");
    if (value.empty()) continue;

    // XXX: on static files we probably don't need the token-list support
    if (value != transferFile.etag()) continue;

    return HttpStatus::PreconditionFailed;
  } while (0);

  return HttpStatus::Undefined;
}

/**
 * Retrieves the number of digits of a positive number.
 */
static size_t numdigits(size_t number) {
  size_t result = 0;

  do {
    result++;
    number /= 10;
  } while (number != 0);

  return result;
}

bool HttpFileHandler::handleRangeRequest(const File& transferFile, int fd,
                                         HttpRequest* request,
                                         HttpResponse* response) {
  const bool isHeadReq = fd < 0;
  BufferRef range_value(request->headers().get("Range"));
  HttpRangeDef range;

  // if no range request or range request was invalid (by syntax) we fall back
  // to a full response
  if (range_value.empty() || !range.parse(range_value))
    return false;

  std::string ifRangeCond = request->headers().get("If-Range");
  if (!ifRangeCond.empty()
        && !equals(ifRangeCond, transferFile.etag())
        && !equals(ifRangeCond, transferFile.lastModified()))
    return false;

  response->setStatus(HttpStatus::PartialContent);

  size_t numRanges = range.size();
  if (numRanges > 1) {
    // generate a multipart/byteranged response, as we've more than one range to
    // serve

    std::string boundary(generateBoundaryID_());
    size_t contentLength = 0;

    // precompute final content-length
    for (size_t i = 0; i < numRanges; ++i) {
      // add ranged chunk length
      const auto offsets = makeOffsets(range[i], transferFile.size());
      const size_t partLength = 1 + offsets.second - offsets.first;

      const size_t headerLen = sizeof("\r\n--") - 1
                             + boundary.size()
                             + sizeof("\r\nContent-Type: ") - 1
                             + transferFile.mimetype().size()
                             + sizeof("\r\nContent-Range: bytes ") - 1
                             + numdigits(offsets.first)
                             + sizeof("-") - 1
                             + numdigits(offsets.second)
                             + sizeof("/") - 1
                             + numdigits(transferFile.size())
                             + sizeof("\r\n\r\n") - 1;

      contentLength += headerLen + partLength;
    }

    // add trailer length
    const size_t trailerLen = sizeof("\r\n--") - 1
                            + boundary.size()
                            + sizeof("--\r\n") - 1;
    contentLength += trailerLen;

    // populate response info
    response->setContentLength(contentLength);
    response->addHeader("Content-Type",
                        "multipart/byteranges; boundary=" + boundary);

    // populate body
    for (size_t i = 0; i != numRanges; ++i) {
      const std::pair<size_t, size_t> offsets(
          makeOffsets(range[i], transferFile.size()));

      if (offsets.second < offsets.first) { // FIXME why did I do this here?
        response->setStatus(HttpStatus::PartialContent);
        response->completed();
        return true;
      }

      const size_t partLength = 1 + offsets.second - offsets.first;

      Buffer buf;
      buf.push_back("\r\n--");
      buf.push_back(boundary);
      buf.push_back("\r\nContent-Type: ");
      buf.push_back(transferFile.mimetype());

      buf.push_back("\r\nContent-Range: bytes ");
      buf.push_back(offsets.first);
      buf.push_back("-");
      buf.push_back(offsets.second);
      buf.push_back("/");
      buf.push_back(transferFile.size());
      buf.push_back("\r\n\r\n");

      if (!isHeadReq) {
        bool last = i + 1 == numRanges;
        response->write(std::move(buf));
        response->write(FileView(fd, offsets.first, partLength, last));
      }
    }

    Buffer buf;
    buf.push_back("\r\n--");
    buf.push_back(boundary);
    buf.push_back("--\r\n");
    response->write(std::move(buf));
  } else {  // generate a simple (single) partial response
    std::pair<size_t, size_t> offsets(
        makeOffsets(range[0], transferFile.size()));

    if (offsets.second < offsets.first) {
      response->sendError(HttpStatus::RequestedRangeNotSatisfiable);
      return true;
    }

    response->addHeader("Content-Type", transferFile.mimetype());

    size_t length = 1 + offsets.second - offsets.first;
    response->setContentLength(length);

    char cr[128];
    snprintf(cr, sizeof(cr), "bytes %zu-%zu/%zu", offsets.first, offsets.second,
             transferFile.size());
    response->addHeader("Content-Range", cr);

    if (fd >= 0) {
#if defined(HAVE_POSIX_FADVISE)
      posix_fadvise(fd, offsets.first, length, POSIX_FADV_SEQUENTIAL);
#endif
      response->write(FileView(fd, offsets.first, length, true));
    }
  }

  response->completed();
  return true;
}

} // namespace http
} // namespace xzero
<commit_msg>wipsnip<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/http/HttpFileHandler.h>
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HttpResponse.h>
#include <xzero/http/HttpRangeDef.h>
#include <xzero/http/HeaderFieldList.h>
#include <xzero/Random.h>
#include <xzero/io/File.h>
#include <xzero/io/FileView.h>
#include <xzero/UnixTime.h>
#include <xzero/Tokenizer.h>
#include <xzero/Buffer.h>
#include <xzero/RuntimeError.h>
#include <xzero/logging.h>
#include <xzero/sysconfig.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace xzero {
namespace http {

// {{{ helper methods
/**
 * Converts a range-spec into real offsets.
 *
 */
inline constexpr std::pair<size_t, size_t> makeOffsets(
    const std::pair<size_t, size_t>& p, size_t actualSize) {
  return p.first == HttpRangeDef::npos
             ? std::make_pair(actualSize - p.second,
                              actualSize - 1)  // last N bytes
             : p.second == HttpRangeDef::npos && p.second > actualSize
                   ? std::make_pair(p.first, actualSize - 1)  // from fixed N to
                                                              // the end of file
                   : std::make_pair(p.first, p.second);       // fixed range
}

/**
 * generates a boundary tag.
 *
 * \return a value usable as boundary tag.
 */
static std::string generateDefaultBoundaryID() {
  static Random rng;
  static const char* map = "0123456789abcdef";
  char buf[16 + 1];

  for (size_t i = 0; i < sizeof(buf) - 1; ++i)
    buf[i] = map[rng.random64() % (sizeof(buf) - 1)];

  buf[sizeof(buf) - 1] = '\0';

  return std::string(buf);
}
// }}}

HttpFileHandler::HttpFileHandler()
    : HttpFileHandler(std::bind(&generateDefaultBoundaryID)) {
}

HttpFileHandler::HttpFileHandler(std::function<std::string()> generateBoundaryID)
    : generateBoundaryID_(generateBoundaryID) {
}

HttpFileHandler::~HttpFileHandler() {
}

HttpStatus HttpFileHandler::handle(HttpRequest* request,
                                   HttpResponse* response,
                                   std::shared_ptr<File> transferFile) {
  if (!transferFile->isRegular())
    return HttpStatus::NotFound;

  HttpStatus status = handleClientCache(*transferFile, request, response);
  switch (status) {
    case HttpStatus::Undefined:
      break;
    case HttpStatus::NotModified:
      // 304
      response->setStatus(status);
      response->completed();
      return status;
    case HttpStatus::PreconditionFailed:
      // 412
      return status;
    default:
      logFatal("internal bug; unhandled status code");
  }

  switch (transferFile->errorCode()) {
    case 0:
      break;
    case ENOENT:
      return HttpStatus::NotFound;
    case EACCES:
    case EPERM:
      return HttpStatus::Forbidden;
    default:
      RAISE_ERRNO(transferFile->errorCode());
  }

  int fd = -1;
  if (request->method() == HttpMethod::GET) {
    fd = transferFile->createPosixChannel(File::Read | File::NonBlocking);
    if (fd < 0) {
      switch (errno) {
        case EPERM:
        case EACCES:
          return HttpStatus::Forbidden;
        case ENOSYS:
          // TODO: in that case we're prjobably on MemoryFile and we need to read differently
        default:
          RAISE_ERRNO(errno);
      }
    }
  } else if (request->method() != HttpMethod::HEAD) {
    return HttpStatus::MethodNotAllowed;
  }

  response->addHeader("Allow", "GET, HEAD");
  response->addHeader("Last-Modified", transferFile->lastModified());
  response->addHeader("ETag", transferFile->etag());

  if (handleRangeRequest(*transferFile, fd, request, response))
    return HttpStatus::PartialContent;

  // XXX Only set status code to 200 (Ok) when the status code hasn't been set
  // previously yet. This may occur due to an internal redirect.
  if (!isError(response->status()))
    response->setStatus(HttpStatus::Ok);

  response->addHeader("Accept-Ranges", "bytes");
  response->addHeader("Content-Type", transferFile->mimetype());

  response->setContentLength(transferFile->size());

  if (fd >= 0) {  // GET request
#if defined(HAVE_POSIX_FADVISE)
    posix_fadvise(fd, 0, transferFile->size(), POSIX_FADV_SEQUENTIAL);
#endif
    response->write(FileView(fd, 0, transferFile->size(), true));
    response->completed();
  } else {
    response->completed();
  }

  // This is the expected response status, even though it may have
  // been overridden (due to internal redirect by setting failure code
  // before invoking this handler).
  return HttpStatus::Ok;
}

static inline uint64_t getUnixMicros(const struct tm& tm) {
  uint64_t days = tm.tm_mday - 1;

  for (auto i = 1970; i < tm.tm_year; ++i)
    days += 365 + ISO8601::isLeapYear(i);

  for (auto i = 1; i < civil.month(); ++i)
    days += ISO8601::daysInMonth(tm.tm_year, i);

  return days * kMicrosPerDay +
         tm.tm_hour * kMicrosPerHour +
         tm.tm_min * kMicrosPerMinute +
         tm.tm_sec * kMicrosPerSecond;
}

static std::optional<UnixTime> parseTime(const std::string& timeStr) {
#if defined(HAVE_STRPTIME)
  static const char* timeFormat = "%a, %d %b %Y %H:%M:%S GMT";

  struct tm tm;

  if (strptime(timeStr.c_str(), timeFormat, &tm) == nullptr)
    return std::nullopt;

  time_t t = mktime(&tm);
  char buf[256];
  printf("parseTime: \"%s\" -> %lu\n", timeStr.c_str(), t);
  printf("       ->: \"%s\"\n", asctime_r(&tm, buf));
  return UnixTime(t * kMicrosPerSecond);
  // CivilTime ct;
  // ct.setSecond(tm.tm_sec);
  // ct.setMinute(tm.tm_min);
  // ct.setHour(tm.tm_hour);
  // ct.setDay(tm.tm_mday);
  // ct.setMonth(tm.tm_mon + 1);
  // ct.setYear(tm.tm_year + 1900);
  // return UnixTime(ct);

  // time_t t = mktime(&tm);
  // return UnixTime(t);
#endif
}

HttpStatus HttpFileHandler::handleClientCache(const File& transferFile,
                                              HttpRequest* request,
                                              HttpResponse* response) {
  // If-Modified-Since
  do {
    const std::string& value = request->headers().get("If-Modified-Since");
    if (value.empty()) continue;

    std::optional<UnixTime> dt = parseTime(value);
    if (!dt)
      continue;

    if (transferFile.mtime() > dt.value().unixtime())
      continue;

    return HttpStatus::NotModified;
  } while (0);

  // If-Unmodified-Since
  do {
    const std::string& value = request->headers().get("If-Unmodified-Since");
    if (value.empty())
      continue;

    std::optional<UnixTime> dt = parseTime(value);
    if (!dt)
      continue;

    if (transferFile.mtime() <= dt.value().unixtime())
      continue;

    return HttpStatus::PreconditionFailed;
  } while (0);

  // If-Match
  do {
    const std::string& value = request->headers().get("If-Match");
    if (value.empty())
      continue;

    if (value == "*")
      continue;

    // XXX: on static files we probably don't need the token-list support
    if (value == transferFile.etag())
      continue;

    return HttpStatus::PreconditionFailed;
  } while (0);

  // If-None-Match
  do {
    const std::string& value = request->headers().get("If-None-Match");
    if (value.empty())
      continue;

    // XXX: on static files we probably don't need the token-list support
    if (value != transferFile.etag())
      continue;

    return HttpStatus::PreconditionFailed;
  } while (0);

  return HttpStatus::Undefined;
}

/**
 * Retrieves the number of digits of a positive number.
 */
static size_t numdigits(size_t number) {
  size_t result = 0;

  do {
    result++;
    number /= 10;
  } while (number != 0);

  return result;
}

bool HttpFileHandler::handleRangeRequest(const File& transferFile, int fd,
                                         HttpRequest* request,
                                         HttpResponse* response) {
  const bool isHeadReq = fd < 0;
  BufferRef range_value(request->headers().get("Range"));
  HttpRangeDef range;

  // if no range request or range request was invalid (by syntax) we fall back
  // to a full response
  if (range_value.empty() || !range.parse(range_value))
    return false;

  std::string ifRangeCond = request->headers().get("If-Range");
  if (!ifRangeCond.empty()
        && !equals(ifRangeCond, transferFile.etag())
        && !equals(ifRangeCond, transferFile.lastModified()))
    return false;

  response->setStatus(HttpStatus::PartialContent);

  size_t numRanges = range.size();
  if (numRanges > 1) {
    // generate a multipart/byteranged response, as we've more than one range to
    // serve

    std::string boundary(generateBoundaryID_());
    size_t contentLength = 0;

    // precompute final content-length
    for (size_t i = 0; i < numRanges; ++i) {
      // add ranged chunk length
      const auto offsets = makeOffsets(range[i], transferFile.size());
      const size_t partLength = 1 + offsets.second - offsets.first;

      const size_t headerLen = sizeof("\r\n--") - 1
                             + boundary.size()
                             + sizeof("\r\nContent-Type: ") - 1
                             + transferFile.mimetype().size()
                             + sizeof("\r\nContent-Range: bytes ") - 1
                             + numdigits(offsets.first)
                             + sizeof("-") - 1
                             + numdigits(offsets.second)
                             + sizeof("/") - 1
                             + numdigits(transferFile.size())
                             + sizeof("\r\n\r\n") - 1;

      contentLength += headerLen + partLength;
    }

    // add trailer length
    const size_t trailerLen = sizeof("\r\n--") - 1
                            + boundary.size()
                            + sizeof("--\r\n") - 1;
    contentLength += trailerLen;

    // populate response info
    response->setContentLength(contentLength);
    response->addHeader("Content-Type",
                        "multipart/byteranges; boundary=" + boundary);

    // populate body
    for (size_t i = 0; i != numRanges; ++i) {
      const std::pair<size_t, size_t> offsets(
          makeOffsets(range[i], transferFile.size()));

      if (offsets.second < offsets.first) { // FIXME why did I do this here?
        response->setStatus(HttpStatus::PartialContent);
        response->completed();
        return true;
      }

      const size_t partLength = 1 + offsets.second - offsets.first;

      Buffer buf;
      buf.push_back("\r\n--");
      buf.push_back(boundary);
      buf.push_back("\r\nContent-Type: ");
      buf.push_back(transferFile.mimetype());

      buf.push_back("\r\nContent-Range: bytes ");
      buf.push_back(offsets.first);
      buf.push_back("-");
      buf.push_back(offsets.second);
      buf.push_back("/");
      buf.push_back(transferFile.size());
      buf.push_back("\r\n\r\n");

      if (!isHeadReq) {
        bool last = i + 1 == numRanges;
        response->write(std::move(buf));
        response->write(FileView(fd, offsets.first, partLength, last));
      }
    }

    Buffer buf;
    buf.push_back("\r\n--");
    buf.push_back(boundary);
    buf.push_back("--\r\n");
    response->write(std::move(buf));
  } else {  // generate a simple (single) partial response
    std::pair<size_t, size_t> offsets(
        makeOffsets(range[0], transferFile.size()));

    if (offsets.second < offsets.first) {
      response->sendError(HttpStatus::RequestedRangeNotSatisfiable);
      return true;
    }

    response->addHeader("Content-Type", transferFile.mimetype());

    size_t length = 1 + offsets.second - offsets.first;
    response->setContentLength(length);

    char cr[128];
    snprintf(cr, sizeof(cr), "bytes %zu-%zu/%zu", offsets.first, offsets.second,
             transferFile.size());
    response->addHeader("Content-Range", cr);

    if (fd >= 0) {
#if defined(HAVE_POSIX_FADVISE)
      posix_fadvise(fd, offsets.first, length, POSIX_FADV_SEQUENTIAL);
#endif
      response->write(FileView(fd, offsets.first, length, true));
    }
  }

  response->completed();
  return true;
}

} // namespace http
} // namespace xzero
<|endoftext|>


<commit_before><commit_msg>Make gaussinv() static and public<commit_after><|endoftext|>


<commit_before>#include <ast/cloner.hxx>
#include <ast/new-clone.hh>
#include <ast/parametric-ast.hh>

#include <object/symbols.hh>

#include <parser/ast-factory.hh>
#include <parser/parse.hh>

#include <rewrite/desugarer.hh>

namespace rewrite
{
  using parser::ast_call;
  using parser::ast_string;
  using parser::ast_lvalue_once;
  using parser::ast_lvalue_wrap;

  void
  Desugarer::visit(const ast::Binding* binding)
  {
    PARAMETRIC_AST(rewrite,
                   "Pattern.Binding.new(%exp:1)");
    rewrite % parser::ast_string(binding->location_get(),
                                 binding->name_get());
    result_ = exp(rewrite);
    result_->original_set(binding);
  }

  void Desugarer::visit(const ast::Class* c)
  {
    ast::loc l = c->location_get();
    ast::rLValue what = recurse(c->what_get());
    libport::Symbol name = what->call()->name_get();
    ast::rNary content = recurse(c->content_get());

    PARAMETRIC_AST(desugar,
      "var %lvalue:1 ="
      "{"
      "  var '$tmp' = Object.clone |"
      "  %exp:2 |"
      "  '$tmp'.setSlot(\"type\", %exp:3) |"
      "  '$tmp'.setSlot(%exp:4, function () { this }) |"
      "  do ('$tmp')"
      "  {"
      "    %exp:5 |"
      "  } |"
      "  '$tmp'"
      "}"
      );

    ast::exps_type* protos = maybe_recurse_collection(c->protos_get());
    ast::rExp protos_set;
    if (protos)
    {
      PARAMETRIC_AST(setProtos, "'$tmp'.setProtos(%exp:1)");
      protos_set = exp(setProtos % new ast::List(l, protos));
    }
    else
      protos_set = new ast::Noop(l, 0);

    desugar % what
      % protos_set
      % ast_string(l, name)
      % ast_string(l, libport::Symbol("as" + name.name_get()))
      % content;

    result_ = exp(desugar);
    result_->original_set(c);
  }

  void Desugarer::visit(const ast::Decrementation* dec)
  {
    PARAMETRIC_AST(decrement, "(%lvalue:1 -= 1) + 1");

    ast::rExp res = recurse(exp(decrement % dec->exp_get()));
    res->original_set(dec);
    result_ = res;
  }

  void Desugarer::visit(const ast::Emit* e)
  {
    ast::rExp event = recurse(e->event_get());
    ast::exps_type* args =
      maybe_recurse_collection(e->arguments_get());

    if (ast::rExp duration = e->duration_get())
    {
      PARAMETRIC_AST(emit,
                     "var '$emit' = %exp:1.trigger(%exps:2) |"
                     "detach({ sleep(%exp:3) | '$emit'.stop})");

      result_ = exp(emit %  event % args % duration);
    }
    else
    {
      PARAMETRIC_AST(emit, "%exp:1 . 'emit'(%exps:2)");
      result_ = exp(emit % event % args);
    }
    result_->original_set(e);
  }

  void Desugarer::visit(const ast::Incrementation* inc)
  {
    PARAMETRIC_AST(increment, "(%lvalue:1 += 1) - 1");

    ast::rExp res = recurse(exp(increment % inc->exp_get()));
    res->original_set(inc);
    result_ = res;
  }

  void Desugarer::visit(const ast::OpAssignment* a)
  {
    PARAMETRIC_AST(desugar, "%lvalue:1 = %exp:2 . %id:3 (%exp:4)");

    ast::rLValue what = recurse(a->what_get());
    ast::rLValue tgt = ast_lvalue_once(what);

    desugar % tgt
      % new_clone(tgt)
      % a->op_get()
      % recurse(a->value_get());

    result_ = ast_lvalue_wrap(what, exp(desugar));
    result_->original_set(a);
  }

  void Desugarer::visit(const ast::PropertyRead* p)
  {
    PARAMETRIC_AST(read, "%exp:1.getProperty(%exp:2, %exp:3)");

    ast::rCall owner = p->owner_get();
    read % owner->target_get()
      % ast_string(owner->location_get(), owner->name_get())
      % ast_string(p->location_get(), p->name_get());
    result_ = exp(read);
    result_->original_set(p);
  }

  void Desugarer::visit(const ast::PropertyWrite* p)
  {
    PARAMETRIC_AST(read, "%exp:1.setProperty(%exp:2, %exp:3, %exp:4)");

    ast::rCall owner = p->owner_get();
    read % owner->target_get()
      % ast_string(owner->location_get(), owner->name_get())
      % ast_string(p->location_get(), p->name_get())
      % p->value_get();
    result_ = exp(read);
    result_->original_set(p);
  }
}
<commit_msg>Optimize "emit ev ~ inf".<commit_after>#include <ast/cloner.hxx>
#include <ast/new-clone.hh>
#include <ast/parametric-ast.hh>

#include <object/symbols.hh>

#include <parser/ast-factory.hh>
#include <parser/parse.hh>

#include <rewrite/desugarer.hh>

namespace rewrite
{
  using parser::ast_call;
  using parser::ast_string;
  using parser::ast_lvalue_once;
  using parser::ast_lvalue_wrap;

  void
  Desugarer::visit(const ast::Binding* binding)
  {
    PARAMETRIC_AST(rewrite,
                   "Pattern.Binding.new(%exp:1)");
    rewrite % parser::ast_string(binding->location_get(),
                                 binding->name_get());
    result_ = exp(rewrite);
    result_->original_set(binding);
  }

  void Desugarer::visit(const ast::Class* c)
  {
    ast::loc l = c->location_get();
    ast::rLValue what = recurse(c->what_get());
    libport::Symbol name = what->call()->name_get();
    ast::rNary content = recurse(c->content_get());

    PARAMETRIC_AST(desugar,
      "var %lvalue:1 ="
      "{"
      "  var '$tmp' = Object.clone |"
      "  %exp:2 |"
      "  '$tmp'.setSlot(\"type\", %exp:3) |"
      "  '$tmp'.setSlot(%exp:4, function () { this }) |"
      "  do ('$tmp')"
      "  {"
      "    %exp:5 |"
      "  } |"
      "  '$tmp'"
      "}"
      );

    ast::exps_type* protos = maybe_recurse_collection(c->protos_get());
    ast::rExp protos_set;
    if (protos)
    {
      PARAMETRIC_AST(setProtos, "'$tmp'.setProtos(%exp:1)");
      protos_set = exp(setProtos % new ast::List(l, protos));
    }
    else
      protos_set = new ast::Noop(l, 0);

    desugar % what
      % protos_set
      % ast_string(l, name)
      % ast_string(l, libport::Symbol("as" + name.name_get()))
      % content;

    result_ = exp(desugar);
    result_->original_set(c);
  }

  void Desugarer::visit(const ast::Decrementation* dec)
  {
    PARAMETRIC_AST(decrement, "(%lvalue:1 -= 1) + 1");

    ast::rExp res = recurse(exp(decrement % dec->exp_get()));
    res->original_set(dec);
    result_ = res;
  }

  void Desugarer::visit(const ast::Emit* e)
  {
    ast::rExp event = recurse(e->event_get());
    ast::exps_type* args =
      maybe_recurse_collection(e->arguments_get());

    if (ast::rExp duration = e->duration_get())
    {
      PARAMETRIC_AST(emit,
                     "{"
		     " var '$emit' = %exp:1.trigger(%exps:2) |"
		     " var '$duration' = %exp:3 |"
                     " if ('$duration' != inf) "
		     "  detach({ sleep('$duration') | '$emit'.stop})"
		     "}");

      result_ = exp(emit %  event % args % duration);
    }
    else
    {
      PARAMETRIC_AST(emit, "%exp:1 . 'emit'(%exps:2)");
      result_ = exp(emit % event % args);
    }
    result_->original_set(e);
  }

  void Desugarer::visit(const ast::Incrementation* inc)
  {
    PARAMETRIC_AST(increment, "(%lvalue:1 += 1) - 1");

    ast::rExp res = recurse(exp(increment % inc->exp_get()));
    res->original_set(inc);
    result_ = res;
  }

  void Desugarer::visit(const ast::OpAssignment* a)
  {
    PARAMETRIC_AST(desugar, "%lvalue:1 = %exp:2 . %id:3 (%exp:4)");

    ast::rLValue what = recurse(a->what_get());
    ast::rLValue tgt = ast_lvalue_once(what);

    desugar % tgt
      % new_clone(tgt)
      % a->op_get()
      % recurse(a->value_get());

    result_ = ast_lvalue_wrap(what, exp(desugar));
    result_->original_set(a);
  }

  void Desugarer::visit(const ast::PropertyRead* p)
  {
    PARAMETRIC_AST(read, "%exp:1.getProperty(%exp:2, %exp:3)");

    ast::rCall owner = p->owner_get();
    read % owner->target_get()
      % ast_string(owner->location_get(), owner->name_get())
      % ast_string(p->location_get(), p->name_get());
    result_ = exp(read);
    result_->original_set(p);
  }

  void Desugarer::visit(const ast::PropertyWrite* p)
  {
    PARAMETRIC_AST(read, "%exp:1.setProperty(%exp:2, %exp:3, %exp:4)");

    ast::rCall owner = p->owner_get();
    read % owner->target_get()
      % ast_string(owner->location_get(), owner->name_get())
      % ast_string(p->location_get(), p->name_get())
      % p->value_get();
    result_ = exp(read);
    result_->original_set(p);
  }
}
<|endoftext|>