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

//  rank_pairing_heap.hpp
//
// Contains an implementation of the rank-pairing heap described in Haeupler, et al. (2011).
//

#ifndef structures_rank_pairing_heap_hpp
#define structures_rank_pairing_heap_hpp

#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <cstdlib>

namespace structures {

using namespace std;

/**
 * A priority queue data structure that allows amortized O(1) priority increases.
 * Each value is only allows to be popped one time. Values must also be hashable.
 */
template <typename T, typename PriorityType, typename Compare = less<PriorityType>>
class RankPairingHeap {
public:

    /// Construct an empty heap.
    RankPairingHeap();

    /// Construct an empty heap using a non-default comparator.
    RankPairingHeap(const Compare& compare);

    /// Destructor.
    ~RankPairingHeap();

    /// Return the highest priority item on the heap and its priority.
    inline const pair<T, PriorityType>& top() const;

    /// Add the value to the heap if it has not been added yet. If it has been added
    /// but has not been popped, set its priority to the maximum of the current priority
    /// and the given priority. If it has been popped already, do nothing.
    inline void push_or_reprioritize(const T& value, const PriorityType& priority);

    /// Remove the highest priority item from the heap.
    inline void pop();

    /// Return true if there are no items in the heap, else false.
    inline bool empty() const;

    /// Return the number of items on the heap.
    inline size_t size() const;

private:

    class Node;

    /// Add a half-tree to the primary tree through the tournament procedure.
    inline void place_half_tree(Node* node);

    /// Link two half-tree roots and update rank.
    inline void link(Node* winner, Node* loser);

    /// Give an item the maximum of the given priority and its current priority.
    inline void reprioritize(Node* node, const PriorityType& priority);

    /// Find the nodes in the heap by value
    unordered_map<T, Node*> current_nodes;

    /// Roots of the half trees
    Node* first_root = nullptr;
    unordered_set<Node*> other_roots;

    /// Tracker to enable size query
    size_t num_items = 0;

    /// Comparator we are using to select maximum
    Compare compare;

};

/*
 * Represents a value in the heap and a node in the binary tree structure.
 */
template <typename T, typename PriorityType, typename Compare>
class RankPairingHeap<T, PriorityType, Compare>::Node {
public:
    Node(const T& value, const PriorityType& priority) : value(value, priority) {}
    ~Node() {
        delete left;
        delete right;
    }
    pair<T, PriorityType> value;

    uint64_t rank = 0;
    Node* parent = nullptr;
    Node* left = nullptr;
    Node* right = nullptr;
};

template <typename T, typename PriorityType, typename Compare>
RankPairingHeap<T, PriorityType, Compare>::RankPairingHeap() {
    // nothing to do
}

template <typename T, typename PriorityType, typename Compare>
RankPairingHeap<T, PriorityType, Compare>::RankPairingHeap(const Compare& compare) : compare(compare) {
    // nothing to do
}

template <typename T, typename PriorityType, typename Compare>
RankPairingHeap<T, PriorityType, Compare>::~RankPairingHeap() {
    // clean up the heap trees
    delete first_root;
    for (Node* half_tree_root : other_roots) {
        delete half_tree_root;
    }
}

template <typename T, typename PriorityType, typename Compare>
void RankPairingHeap<T, PriorityType, Compare>::link(Node* winner, Node* loser) {
    // tied contests increase the winner's rank
    if (winner->rank == loser->rank) {
        winner->rank++;
    }
    // place winner's left subtree on loser's right subtree
    loser->right = winner->left;
    if (loser->right) {
        loser->right->parent = loser;
    }
    // winner's left subtree is now the loser
    winner->left = loser;
    loser->parent = winner;
}

template <typename T, typename PriorityType, typename Compare>
void RankPairingHeap<T, PriorityType, Compare>::place_half_tree(Node* node) {

    if (first_root == nullptr) {
        // this is the first value
        first_root = node;
    }
    else if (compare(top().second, node->value.second)) {
        // this is the new maximum
        other_roots.insert(first_root);
        first_root = node;
    }
    else {
        // this is not the new maximum
        other_roots.insert(node);
    }
}

template <typename T, typename PriorityType, typename Compare>
inline void RankPairingHeap<T, PriorityType, Compare>::push_or_reprioritize(const T& value, const PriorityType& priority) {

    // look for the value in the heap
    auto current_location = current_nodes.find(value);
    if (current_location != current_nodes.end()) {
        // we've seen this value before
        if (current_location->second) {
            // it hasn't been popped yet, give it the new priority
            reprioritize(current_location->second, priority);
        }
    }
    else {
        // we haven't seen this value before, make a new node
        Node* node = new Node(value, priority);

        // add it to the heap
        place_half_tree(node);

        // bookkeeping
        current_nodes[value] = node;
        num_items++;
    }
}

template <typename T, typename PriorityType, typename Compare>
inline const pair<T, PriorityType>& RankPairingHeap<T, PriorityType, Compare>::top() const {
    return first_root->value;
}

template <typename T, typename PriorityType, typename Compare>
inline void RankPairingHeap<T, PriorityType, Compare>::reprioritize(Node* node, const PriorityType& priority) {

    if (compare(node->value.second, priority)) {
        // we're giving it a higher priority than it currently has

        node->value.second = priority;

        if (!node->parent) {
            // this is the root of a half tree
            if (compare(top().second, priority)) {
                // this is now the highest priority root, so we need to move it to the front
                other_roots.insert(first_root);
                other_roots.erase(node);
                first_root = node;
            }
        }
        else {
            // remove this node from the tree and put its right subtree in its place
            Node* next_parent = node->parent;
            node->parent = nullptr;
            if (next_parent->left == node) {
                next_parent->left = node->right;
            }
            else {
                next_parent->right = node->right;
            }
            if (node->right) {
                node->right->parent = next_parent;
            }
            node->right = nullptr;

            place_half_tree(node);

            // restore the type-2 rank property above the node
            while (next_parent) {

                // make it a (1,1), (1, 2), or, (0, i) node
                if (next_parent->right && next_parent->left) {
                    uint64_t next_rank = max(next_parent->left->rank, next_parent->right->rank);
                    if (next_rank - min(next_parent->left->rank, next_parent->right->rank) <= 1) {
                        next_rank++;
                    }
                    if (next_rank >= next_parent->rank) {
                        break;
                    }
                    else {
                        next_parent->rank = next_rank;
                    }
                }
                else if (next_parent->right) {
                    next_parent->rank = next_parent->right->rank + 1;
                }
                else if (next_parent->left) {
                    next_parent->rank = next_parent->left->rank + 1;
                }
                else {
                    next_parent->rank = 0;
                }

                next_parent = next_parent->parent;
            }
        }
    }
}

template <typename T, typename PriorityType, typename Compare>
inline void RankPairingHeap<T, PriorityType, Compare>::pop() {

    // bookkeeping
    num_items--;
    // mark this value as popped
    current_nodes[top().first] = nullptr;

    // collect the other roots for later processing
    vector<Node*> new_roots;

    // disassemble the first tree and collect the right spine
    if (first_root->left) {

        // have to have one iteration outside the loop since we travel
        // left the first time
        Node* prev_spine_node = first_root->left;
        new_roots.push_back(prev_spine_node);
        prev_spine_node->parent = nullptr;

        while (prev_spine_node->right) {

            new_roots.push_back(prev_spine_node->right);
            prev_spine_node->right = nullptr;

            prev_spine_node = new_roots.back();
            prev_spine_node->parent = nullptr;
        }
    }

    // collect the other current roots too
    for (Node* half_tree_root : other_roots) {
        new_roots.push_back(half_tree_root);
    }
    other_roots.clear();

    // get rid of the first root
    first_root->left = nullptr; // so it won't delete the other nodes
    delete first_root;
    first_root = nullptr;

    // one-pass algorithm over the roots described in paper
    vector<Node*> buckets;
    for (Node* half_tree_root : new_roots) {

        // compact the ranks to make 1-nodes
        half_tree_root->rank = half_tree_root->left ? half_tree_root->left->rank + 1 : 0;

        // ensure that we have enough buckets
        while (buckets.size() <= half_tree_root->rank) {
            buckets.push_back(nullptr);
        }

        // what's in the bucket right now?

        uint64_t bucket_num = half_tree_root->rank;
        Node* other_root = buckets[bucket_num];
        if (other_root) {
            // there's already a tree in this bucket
            if (compare(half_tree_root->value.second, other_root->value.second)) {
                // the current tree wins, link and place it
                link(other_root, half_tree_root);
                place_half_tree(other_root);
            }
            else {
                // the other tree wins, link and place it
                link(half_tree_root, other_root);
                place_half_tree(half_tree_root);
            }
            // empty the bucket
            buckets[bucket_num] = nullptr;
        }
        else {
            // the bucket is empty, fill it with the current half tree
            buckets[bucket_num] = half_tree_root;
        }
    }

    // get any half trees still in the buckets and add them to the main tree
    for (Node* half_tree_root : buckets) {
        if (half_tree_root) {
            place_half_tree(half_tree_root);
        }
    }
}

template <typename T, typename PriorityType, typename Compare>
inline bool RankPairingHeap<T, PriorityType, Compare>::empty() const {
    return first_root == nullptr;
}

template <typename T, typename PriorityType, typename Compare>
inline size_t RankPairingHeap<T, PriorityType, Compare>::size() const {
    return num_items;
}

}

#endif /* structures_rank_pairing_heap_hpp */
<commit_msg>add stdint incllude to heap<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.

//  rank_pairing_heap.hpp
//
// Contains an implementation of the rank-pairing heap described in Haeupler, et al. (2011).
//

#ifndef structures_rank_pairing_heap_hpp
#define structures_rank_pairing_heap_hpp

#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <cstdlib>
#include <cstdint>

namespace structures {

using namespace std;

/**
 * A priority queue data structure that allows amortized O(1) priority increases.
 * Each value is only allows to be popped one time. Values must also be hashable.
 */
template <typename T, typename PriorityType, typename Compare = less<PriorityType>>
class RankPairingHeap {
public:

    /// Construct an empty heap.
    RankPairingHeap();

    /// Construct an empty heap using a non-default comparator.
    RankPairingHeap(const Compare& compare);

    /// Destructor.
    ~RankPairingHeap();

    /// Return the highest priority item on the heap and its priority.
    inline const pair<T, PriorityType>& top() const;

    /// Add the value to the heap if it has not been added yet. If it has been added
    /// but has not been popped, set its priority to the maximum of the current priority
    /// and the given priority. If it has been popped already, do nothing.
    inline void push_or_reprioritize(const T& value, const PriorityType& priority);

    /// Remove the highest priority item from the heap.
    inline void pop();

    /// Return true if there are no items in the heap, else false.
    inline bool empty() const;

    /// Return the number of items on the heap.
    inline size_t size() const;

private:

    class Node;

    /// Add a half-tree to the primary tree through the tournament procedure.
    inline void place_half_tree(Node* node);

    /// Link two half-tree roots and update rank.
    inline void link(Node* winner, Node* loser);

    /// Give an item the maximum of the given priority and its current priority.
    inline void reprioritize(Node* node, const PriorityType& priority);

    /// Find the nodes in the heap by value
    unordered_map<T, Node*> current_nodes;

    /// Roots of the half trees
    Node* first_root = nullptr;
    unordered_set<Node*> other_roots;

    /// Tracker to enable size query
    size_t num_items = 0;

    /// Comparator we are using to select maximum
    Compare compare;

};

/*
 * Represents a value in the heap and a node in the binary tree structure.
 */
template <typename T, typename PriorityType, typename Compare>
class RankPairingHeap<T, PriorityType, Compare>::Node {
public:
    Node(const T& value, const PriorityType& priority) : value(value, priority) {}
    ~Node() {
        delete left;
        delete right;
    }
    pair<T, PriorityType> value;

    uint64_t rank = 0;
    Node* parent = nullptr;
    Node* left = nullptr;
    Node* right = nullptr;
};

template <typename T, typename PriorityType, typename Compare>
RankPairingHeap<T, PriorityType, Compare>::RankPairingHeap() {
    // nothing to do
}

template <typename T, typename PriorityType, typename Compare>
RankPairingHeap<T, PriorityType, Compare>::RankPairingHeap(const Compare& compare) : compare(compare) {
    // nothing to do
}

template <typename T, typename PriorityType, typename Compare>
RankPairingHeap<T, PriorityType, Compare>::~RankPairingHeap() {
    // clean up the heap trees
    delete first_root;
    for (Node* half_tree_root : other_roots) {
        delete half_tree_root;
    }
}

template <typename T, typename PriorityType, typename Compare>
void RankPairingHeap<T, PriorityType, Compare>::link(Node* winner, Node* loser) {
    // tied contests increase the winner's rank
    if (winner->rank == loser->rank) {
        winner->rank++;
    }
    // place winner's left subtree on loser's right subtree
    loser->right = winner->left;
    if (loser->right) {
        loser->right->parent = loser;
    }
    // winner's left subtree is now the loser
    winner->left = loser;
    loser->parent = winner;
}

template <typename T, typename PriorityType, typename Compare>
void RankPairingHeap<T, PriorityType, Compare>::place_half_tree(Node* node) {

    if (first_root == nullptr) {
        // this is the first value
        first_root = node;
    }
    else if (compare(top().second, node->value.second)) {
        // this is the new maximum
        other_roots.insert(first_root);
        first_root = node;
    }
    else {
        // this is not the new maximum
        other_roots.insert(node);
    }
}

template <typename T, typename PriorityType, typename Compare>
inline void RankPairingHeap<T, PriorityType, Compare>::push_or_reprioritize(const T& value, const PriorityType& priority) {

    // look for the value in the heap
    auto current_location = current_nodes.find(value);
    if (current_location != current_nodes.end()) {
        // we've seen this value before
        if (current_location->second) {
            // it hasn't been popped yet, give it the new priority
            reprioritize(current_location->second, priority);
        }
    }
    else {
        // we haven't seen this value before, make a new node
        Node* node = new Node(value, priority);

        // add it to the heap
        place_half_tree(node);

        // bookkeeping
        current_nodes[value] = node;
        num_items++;
    }
}

template <typename T, typename PriorityType, typename Compare>
inline const pair<T, PriorityType>& RankPairingHeap<T, PriorityType, Compare>::top() const {
    return first_root->value;
}

template <typename T, typename PriorityType, typename Compare>
inline void RankPairingHeap<T, PriorityType, Compare>::reprioritize(Node* node, const PriorityType& priority) {

    if (compare(node->value.second, priority)) {
        // we're giving it a higher priority than it currently has

        node->value.second = priority;

        if (!node->parent) {
            // this is the root of a half tree
            if (compare(top().second, priority)) {
                // this is now the highest priority root, so we need to move it to the front
                other_roots.insert(first_root);
                other_roots.erase(node);
                first_root = node;
            }
        }
        else {
            // remove this node from the tree and put its right subtree in its place
            Node* next_parent = node->parent;
            node->parent = nullptr;
            if (next_parent->left == node) {
                next_parent->left = node->right;
            }
            else {
                next_parent->right = node->right;
            }
            if (node->right) {
                node->right->parent = next_parent;
            }
            node->right = nullptr;

            place_half_tree(node);

            // restore the type-2 rank property above the node
            while (next_parent) {

                // make it a (1,1), (1, 2), or, (0, i) node
                if (next_parent->right && next_parent->left) {
                    uint64_t next_rank = max(next_parent->left->rank, next_parent->right->rank);
                    if (next_rank - min(next_parent->left->rank, next_parent->right->rank) <= 1) {
                        next_rank++;
                    }
                    if (next_rank >= next_parent->rank) {
                        break;
                    }
                    else {
                        next_parent->rank = next_rank;
                    }
                }
                else if (next_parent->right) {
                    next_parent->rank = next_parent->right->rank + 1;
                }
                else if (next_parent->left) {
                    next_parent->rank = next_parent->left->rank + 1;
                }
                else {
                    next_parent->rank = 0;
                }

                next_parent = next_parent->parent;
            }
        }
    }
}

template <typename T, typename PriorityType, typename Compare>
inline void RankPairingHeap<T, PriorityType, Compare>::pop() {

    // bookkeeping
    num_items--;
    // mark this value as popped
    current_nodes[top().first] = nullptr;

    // collect the other roots for later processing
    vector<Node*> new_roots;

    // disassemble the first tree and collect the right spine
    if (first_root->left) {

        // have to have one iteration outside the loop since we travel
        // left the first time
        Node* prev_spine_node = first_root->left;
        new_roots.push_back(prev_spine_node);
        prev_spine_node->parent = nullptr;

        while (prev_spine_node->right) {

            new_roots.push_back(prev_spine_node->right);
            prev_spine_node->right = nullptr;

            prev_spine_node = new_roots.back();
            prev_spine_node->parent = nullptr;
        }
    }

    // collect the other current roots too
    for (Node* half_tree_root : other_roots) {
        new_roots.push_back(half_tree_root);
    }
    other_roots.clear();

    // get rid of the first root
    first_root->left = nullptr; // so it won't delete the other nodes
    delete first_root;
    first_root = nullptr;

    // one-pass algorithm over the roots described in paper
    vector<Node*> buckets;
    for (Node* half_tree_root : new_roots) {

        // compact the ranks to make 1-nodes
        half_tree_root->rank = half_tree_root->left ? half_tree_root->left->rank + 1 : 0;

        // ensure that we have enough buckets
        while (buckets.size() <= half_tree_root->rank) {
            buckets.push_back(nullptr);
        }

        // what's in the bucket right now?

        uint64_t bucket_num = half_tree_root->rank;
        Node* other_root = buckets[bucket_num];
        if (other_root) {
            // there's already a tree in this bucket
            if (compare(half_tree_root->value.second, other_root->value.second)) {
                // the current tree wins, link and place it
                link(other_root, half_tree_root);
                place_half_tree(other_root);
            }
            else {
                // the other tree wins, link and place it
                link(half_tree_root, other_root);
                place_half_tree(half_tree_root);
            }
            // empty the bucket
            buckets[bucket_num] = nullptr;
        }
        else {
            // the bucket is empty, fill it with the current half tree
            buckets[bucket_num] = half_tree_root;
        }
    }

    // get any half trees still in the buckets and add them to the main tree
    for (Node* half_tree_root : buckets) {
        if (half_tree_root) {
            place_half_tree(half_tree_root);
        }
    }
}

template <typename T, typename PriorityType, typename Compare>
inline bool RankPairingHeap<T, PriorityType, Compare>::empty() const {
    return first_root == nullptr;
}

template <typename T, typename PriorityType, typename Compare>
inline size_t RankPairingHeap<T, PriorityType, Compare>::size() const {
    return num_items;
}

}

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


<commit_before><commit_msg>fix error messages<commit_after><|endoftext|>


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

#include "IECore/ObjectWriter.h"
#include "IECore/FileIndexedIO.h"
#include "IECore/FileNameParameter.h"
#include "IECore/CompoundParameter.h"
#include "IECore/ObjectParameter.h"
#include "IECore/CompoundData.h"
#include "IECore/Object.h"
#include "IECore/IECore.h"
#include "IECore/HeaderGenerator.h"

using namespace IECore;
using namespace std;
using namespace boost;

const Writer::WriterDescription<ObjectWriter> ObjectWriter::g_writerDescription( "cob" );

ObjectWriter::ObjectWriter()
	:	Writer( "ObjectWriter", "Writes instances of a single Object to a file with a .cob extension", ObjectTypeId )
{
	constructParameters();
}

ObjectWriter::ObjectWriter( ObjectPtr object, const std::string &fileName )
	:	Writer( "ObjectWriter", "Writes instances of a single Object to a file with a .cob extension", ObjectTypeId )
{
	constructParameters();
	m_objectParameter->setValue( object );
	m_fileNameParameter->setTypedValue( fileName );
}

bool ObjectWriter::canWrite( ConstObjectPtr object, const std::string &fileName )
{
	return true;
}

void ObjectWriter::doWrite()
{
	IndexedIOInterfacePtr io = new FileIndexedIO( fileName(), "/", IndexedIO::Exclusive | IndexedIO::Write);

	// write the header
	CompoundDataPtr header = static_pointer_cast<CompoundData>( m_headerParameter->getValue()->copy() );

	header->writable()["typeName"] = new StringData( object()->typeName() );

	CompoundObjectPtr genericHeader = HeaderGenerator::header();
	for ( CompoundObject::ObjectMap::const_iterator it = genericHeader->members().begin(); it != genericHeader->members().end(); it++ )
	{
		if ( it->second->isInstanceOf( Data::staticTypeId() ) )
		{
			header->writable()[ it->first ] = static_pointer_cast< Data >( it->second );
		}
	}

	((ObjectPtr)header)->save( io, "header" );

	// write the object
	object()->save( io, "object" );
}

void ObjectWriter::constructParameters()
{
	m_headerParameter = new ObjectParameter(
		"header",
		"A CompoundData object containing elements to be added to the file header.",
		new CompoundData,
		CompoundData::staticTypeId()
	);

	parameters()->addParameter( m_headerParameter );
}
<commit_msg>Added todo, and an assert<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2008, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <cassert>

#include "IECore/ObjectWriter.h"
#include "IECore/FileIndexedIO.h"
#include "IECore/FileNameParameter.h"
#include "IECore/CompoundParameter.h"
#include "IECore/ObjectParameter.h"
#include "IECore/CompoundData.h"
#include "IECore/Object.h"
#include "IECore/IECore.h"
#include "IECore/HeaderGenerator.h"

using namespace IECore;
using namespace std;
using namespace boost;

const Writer::WriterDescription<ObjectWriter> ObjectWriter::g_writerDescription( "cob" );

ObjectWriter::ObjectWriter()
	:	Writer( "ObjectWriter", "Writes instances of a single Object to a file with a .cob extension", ObjectTypeId )
{
	constructParameters();
}

ObjectWriter::ObjectWriter( ObjectPtr object, const std::string &fileName )
	:	Writer( "ObjectWriter", "Writes instances of a single Object to a file with a .cob extension", ObjectTypeId )
{
	constructParameters();
	m_objectParameter->setValue( object );
	m_fileNameParameter->setTypedValue( fileName );
}

bool ObjectWriter::canWrite( ConstObjectPtr object, const std::string &fileName )
{
	return true;
}

void ObjectWriter::doWrite()
{
	IndexedIOInterfacePtr io = new FileIndexedIO( fileName(), "/", IndexedIO::Exclusive | IndexedIO::Write);

	/// \todo Establish why we only accept CompoundData / Data here when HeaderGenerator::header(), for example,
	/// returns a CompoundObject

	// write the header
	CompoundDataPtr header = static_pointer_cast<CompoundData>( m_headerParameter->getValue()->copy() );

	header->writable()["typeName"] = new StringData( object()->typeName() );

	CompoundObjectPtr genericHeader = HeaderGenerator::header();
	for ( CompoundObject::ObjectMap::const_iterator it = genericHeader->members().begin(); it != genericHeader->members().end(); it++ )
	{
		assert( it->second );
		if ( it->second->isInstanceOf( Data::staticTypeId() ) )
		{
			header->writable()[ it->first ] = static_pointer_cast< Data >( it->second );
		}
	}

	((ObjectPtr)header)->save( io, "header" );

	// write the object
	object()->save( io, "object" );
}

void ObjectWriter::constructParameters()
{
	m_headerParameter = new ObjectParameter(
		"header",
		"A CompoundData object containing elements to be added to the file header.",
		new CompoundData,
		CompoundData::staticTypeId()
	);

	parameters()->addParameter( m_headerParameter );
}
<|endoftext|>


<commit_before>#include"CCThreadPoolItem.h"

namespace nTool
{
	template<class Ret>
	void CThreadPoolItem<Ret>::loop_()
	{
		while(waiting_(),!destructor_)
			exec_->exec();
	}

	template<class Ret>
	CThreadPoolItem<Ret>::CThreadPoolItem()
		:destructor_{false},joinable_{false},wait_{0},thr_{&CThreadPoolItem<Ret>::loop_,this}{}

	template<class Ret>
	template<class Func,class ... Args>
	void CThreadPoolItem<Ret>::assign(Func &&func,Args &&...args)
	{
		exec_=std::make_unique<CThreadPoolItemExecutorJoin>(commun_.get(),std::forward<Func>(func),std::forward<Args>(args)...);
		joinable_=true;
		wake_();
	}

	template<class Ret>
	template<class Func,class ... Args>
	void CThreadPoolItem<Ret>::assign_and_detach(Func &&func,Args &&...args)
	{
		exec_=std::make_unique<CThreadPoolItemExecutorDetach>(commun_.get(),std::forward<Func>(func),std::forward<Args>(args)...);
		joinable_=false;
		wake_();
	}

	template<class Ret>
	template<class Func,class ... Args>
	void CThreadPoolItem<Ret>::assign_and_ret(Func &&func,Args &&...args)
	{
		exec_=std::make_unique<CThreadPoolItemExecutorRet<Ret>>(commun_.get(),std::forward<Func>(func),std::forward<Args>(args)...);
		joinable_=false;
		wake_();
	}

	template<class Ret>
	CThreadPoolItem<Ret>::~CThreadPoolItem()
	{
		if(exec_->is_running())
			exec_->wait();
		destructor_=true;
		wake_();
	}

	template<class Ret>
	IThreadPoolItemExecutorBase<Ret>::~IThreadPoolItemExecutorBase(){}

	template<class Func,class ... Args>
	CThreadPoolItemExecutorDetach::CThreadPoolItemExecutorDetach(CThreadPoolCommunBase *commun,Func &&func,Args &&...args)
		:commun_{commun},complete_{0},func_{std::bind(std::forward<Func>(func),std::forward<Args>(args)...)}{}

	template<class Func,class ... Args>
	CThreadPoolItemExecutorJoin::CThreadPoolItemExecutorJoin(CThreadPoolCommunBase *commun,Func &&func,Args &&...args)
		:commun_{commun},complete_{0},func_{std::bind(std::forward<Func>(func),std::forward<Args>(args)...)},running_{true}{}

	template<class Ret>
	template<class Func,class ... Args>
	CThreadPoolItemExecutorRet<Ret>::CThreadPoolItemExecutorRet(CThreadPoolCommunBase *commun,Func &&func,Args &&...args)
		:commun_{commun},task_{std::forward<Func>(func),std::forward<Args>(args)...}{}

	template<class Ret>
	decltype(std::declval<CTask<Ret>>().get()) CThreadPoolItemExecutorRet<Ret>::get()
	{
		const auto temp{task_.get()};
		commun_->communPoolDetach();
		return temp;
	}
}<commit_msg>check exec_ is valid<commit_after>#include"CCThreadPoolItem.h"

namespace nTool
{
	template<class Ret>
	void CThreadPoolItem<Ret>::loop_()
	{
		while(waiting_(),!destructor_)
			exec_->exec();
	}

	template<class Ret>
	CThreadPoolItem<Ret>::CThreadPoolItem()
		:destructor_{false},joinable_{false},wait_{0},thr_{&CThreadPoolItem<Ret>::loop_,this}{}

	template<class Ret>
	template<class Func,class ... Args>
	void CThreadPoolItem<Ret>::assign(Func &&func,Args &&...args)
	{
		exec_=std::make_unique<CThreadPoolItemExecutorJoin>(commun_.get(),std::forward<Func>(func),std::forward<Args>(args)...);
		joinable_=true;
		wake_();
	}

	template<class Ret>
	template<class Func,class ... Args>
	void CThreadPoolItem<Ret>::assign_and_detach(Func &&func,Args &&...args)
	{
		exec_=std::make_unique<CThreadPoolItemExecutorDetach>(commun_.get(),std::forward<Func>(func),std::forward<Args>(args)...);
		joinable_=false;
		wake_();
	}

	template<class Ret>
	template<class Func,class ... Args>
	void CThreadPoolItem<Ret>::assign_and_ret(Func &&func,Args &&...args)
	{
		exec_=std::make_unique<CThreadPoolItemExecutorRet<Ret>>(commun_.get(),std::forward<Func>(func),std::forward<Args>(args)...);
		joinable_=false;
		wake_();
	}

	template<class Ret>
	CThreadPoolItem<Ret>::~CThreadPoolItem()
	{
		if(exec_&&exec_->is_running())
			exec_->wait();
		destructor_=true;
		wake_();
	}

	template<class Ret>
	IThreadPoolItemExecutorBase<Ret>::~IThreadPoolItemExecutorBase(){}

	template<class Func,class ... Args>
	CThreadPoolItemExecutorDetach::CThreadPoolItemExecutorDetach(CThreadPoolCommunBase *commun,Func &&func,Args &&...args)
		:commun_{commun},complete_{0},func_{std::bind(std::forward<Func>(func),std::forward<Args>(args)...)}{}

	template<class Func,class ... Args>
	CThreadPoolItemExecutorJoin::CThreadPoolItemExecutorJoin(CThreadPoolCommunBase *commun,Func &&func,Args &&...args)
		:commun_{commun},complete_{0},func_{std::bind(std::forward<Func>(func),std::forward<Args>(args)...)},running_{true}{}

	template<class Ret>
	template<class Func,class ... Args>
	CThreadPoolItemExecutorRet<Ret>::CThreadPoolItemExecutorRet(CThreadPoolCommunBase *commun,Func &&func,Args &&...args)
		:commun_{commun},task_{std::forward<Func>(func),std::forward<Args>(args)...}{}

	template<class Ret>
	decltype(std::declval<CTask<Ret>>().get()) CThreadPoolItemExecutorRet<Ret>::get()
	{
		const auto temp{task_.get()};
		commun_->communPoolDetach();
		return temp;
	}
}<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "modules/audio_coding/neteq/buffer_level_filter.h"

#include <stdint.h>
#include <algorithm>

#include "rtc_base/numerics/safe_conversions.h"

namespace webrtc {

BufferLevelFilter::BufferLevelFilter() {
  Reset();
}

void BufferLevelFilter::Reset() {
  filtered_current_level_ = 0;
  level_factor_ = 253;
}

void BufferLevelFilter::Update(size_t buffer_size_samples,
                               int time_stretched_samples) {
  // Filter:
  // |filtered_current_level_| = |level_factor_| * |filtered_current_level_| +
  //                            (1 - |level_factor_|) * |buffer_size_samples|
  // |level_factor_| and |filtered_current_level_| are in Q8.
  // |buffer_size_samples| is in Q0.
  filtered_current_level_ =
      ((level_factor_ * filtered_current_level_) >> 8) +
      ((256 - level_factor_) * rtc::dchecked_cast<int>(buffer_size_samples));

  // Account for time-scale operations (accelerate and pre-emptive expand) and
  // make sure that the filtered value remains non-negative.
  filtered_current_level_ = rtc::saturated_cast<int>(std::max<int64_t>(
      0,
      filtered_current_level_ - (int64_t{time_stretched_samples} * (1 << 8))));
}

void BufferLevelFilter::SetTargetBufferLevel(int target_buffer_level) {
  if (target_buffer_level <= 1) {
    level_factor_ = 251;
  } else if (target_buffer_level <= 3) {
    level_factor_ = 252;
  } else if (target_buffer_level <= 7) {
    level_factor_ = 253;
  } else {
    level_factor_ = 254;
  }
}

}  // namespace webrtc
<commit_msg>Fixing possible overflow in NetEq buffle level filter.<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "modules/audio_coding/neteq/buffer_level_filter.h"

#include <stdint.h>
#include <algorithm>

#include "rtc_base/numerics/safe_conversions.h"

namespace webrtc {

BufferLevelFilter::BufferLevelFilter() {
  Reset();
}

void BufferLevelFilter::Reset() {
  filtered_current_level_ = 0;
  level_factor_ = 253;
}

void BufferLevelFilter::Update(size_t buffer_size_samples,
                               int time_stretched_samples) {
  // Filter:
  // |filtered_current_level_| = |level_factor_| * |filtered_current_level_| +
  //                            (1 - |level_factor_|) * |buffer_size_samples|
  // |level_factor_| and |filtered_current_level_| are in Q8.
  // |buffer_size_samples| is in Q0.
  const int64_t filtered_current_level =
      ((level_factor_ * int64_t{filtered_current_level_}) >> 8) +
      ((256 - level_factor_) * rtc::dchecked_cast<int>(buffer_size_samples));

  // Account for time-scale operations (accelerate and pre-emptive expand) and
  // make sure that the filtered value remains non-negative.
  filtered_current_level_ = rtc::saturated_cast<int>(std::max<int64_t>(
      0,
      filtered_current_level - (int64_t{time_stretched_samples} * (1 << 8))));
}

void BufferLevelFilter::SetTargetBufferLevel(int target_buffer_level) {
  if (target_buffer_level <= 1) {
    level_factor_ = 251;
  } else if (target_buffer_level <= 3) {
    level_factor_ = 252;
  } else if (target_buffer_level <= 7) {
    level_factor_ = 253;
  } else {
    level_factor_ = 254;
  }
}

}  // namespace webrtc
<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <string.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h"
#include "tensorflow/contrib/lite/toco/model.h"
#include "tensorflow/contrib/lite/toco/runtime/types.h"
#include "tensorflow/contrib/lite/toco/tooling_util.h"
#include "tensorflow/core/platform/logging.h"

namespace toco {

bool CopyMinMaxFromFirstInput(const Operator& op, Model* model) {
  auto& output_array = model->GetArray(op.outputs[0]);
  if (output_array.minmax) {
    return false;
  }
  const auto& input_array = model->GetArray(op.inputs[0]);
  if (!input_array.minmax) {
    return false;
  }
  const auto& input_minmax = input_array.GetMinMax();
  CHECK(!output_array.minmax);
  auto& output_minmax = output_array.GetOrCreateMinMax();
  output_minmax.min = input_minmax.min;
  output_minmax.max = input_minmax.max;
  return true;
}

bool ResolveConstantUnaryOperator::Run(Model* model, std::size_t op_index) {
  const auto unary_it = model->operators.begin() + op_index;
  const auto* unary_op = unary_it->get();
  // Test for unary ops of types that we know how to resolve.
  switch (unary_op->type) {
    case OperatorType::kCast:
    case OperatorType::kLog:
    case OperatorType::kNeg:
    case OperatorType::kRsqrt:
    case OperatorType::kSqrt:
    case OperatorType::kSquare:
    case OperatorType::kSum:
    case OperatorType::kReduceMin:  //  Reduction Min
    case OperatorType::kReduceMax:  //  Reduction Max
    case OperatorType::kReshape:
    case OperatorType::kRelu6:
    case OperatorType::kRelu1:
    case OperatorType::kRelu:
      break;
    default:
      return false;
  }

  // Check if the input is a constant parameter.
  if (!IsConstantParameterArray(*model, unary_op->inputs[0])) {
    return false;
  }

  // if the unary op involves a tensor required by a rnn state, ignore it
  for (const auto& rnn_state : model->flags.rnn_states()) {
    if (unary_op->inputs[0] == rnn_state.back_edge_source_array()) {
      return false;
    }
    if (unary_op->inputs[0] == rnn_state.state_array()) {
      return false;
    }
  }

  auto& output_array = model->GetArray(unary_op->outputs[0]);
  if (!output_array.has_shape()) {
    // Yield until the output array dims have been resolved.
    return false;
  }

  // At the moment we don't want to care about fused activation functions.
  // The idea is that we should do the present constants-propagation before
  // activation functions get fused.
  if (unary_op->fused_activation_function !=
      FusedActivationFunctionType::kNone) {
    AddMessageF(
        "Not resolving constant %s "
        " because it has a fused activation function",
        LogName(*unary_op));
    return false;
  }

  // The min-max is only copied for ops that copy data without arithmetic.
  // In future trivial transpose, etc, can be handled here.
  if (unary_op->type == OperatorType::kReshape) {
    CopyMinMaxFromFirstInput(*unary_op, model);
  }

  const auto& input_array = model->GetArray(unary_op->inputs[0]);
  // We have already tested above for existence of buffers (synonymous to being
  // a constant param).
  CHECK(input_array.buffer);
  std::vector<DataType<ArrayDataType::kFloat>> const* input_float_data;
  if (unary_op->type == OperatorType::kCast) {
    CastOperator const* cast_op = static_cast<CastOperator const*>(unary_op);
    if (cast_op->dst_data_type != ArrayDataType::kFloat) {
      AddMessageF(
          "Not resolving constant %s because we currently only support casting "
          "to float",
          LogName(*unary_op));
      return false;
    }
    if (cast_op->src_data_type != input_array.buffer->type) {
      AddMessageF(
          "Not resolving constant %s because cast op source type does not "
          "match input type",
          LogName(*unary_op));
    }
  } else {
    if (input_array.buffer->type != ArrayDataType::kFloat) {
      return false;
    }
    input_float_data = &(input_array.GetBuffer<ArrayDataType::kFloat>().data);
  }

  // Create a float buffer on the output array, which are always constant.
  const Shape& output_shape = output_array.shape();
  const int output_dims_count = output_shape.dimensions_count();
  const int output_buffer_size = RequiredBufferSizeForShape(output_shape);
  auto& output_float_data =
      output_array.GetMutableBuffer<ArrayDataType::kFloat>().data;
  output_float_data.resize(output_buffer_size);

  const Shape& input_shape = input_array.shape();
  const int input_buffer_size = RequiredBufferSizeForShape(input_shape);
  if (unary_op->type == OperatorType::kCast) {
    for (int i = 0; i < output_buffer_size; i++) {
      float outval = 0.0f;
      if (input_array.buffer->type == ArrayDataType::kFloat) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kFloat>().data[i]);
      } else if (input_array.buffer->type == ArrayDataType::kUint8) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kUint8>().data[i]);
      } else if (input_array.buffer->type == ArrayDataType::kInt32) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kInt32>().data[i]);
      } else if (input_array.buffer->type == ArrayDataType::kInt64) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kInt64>().data[i]);
      } else {
        LOG(FATAL) << "Unsupported cast op input type";
      }
      output_float_data[i] = outval;
    }
  } else if (unary_op->type == OperatorType::kReshape) {
    CHECK(input_buffer_size == output_buffer_size);
    output_float_data = *input_float_data;
  } else if (unary_op->type == OperatorType::kSum) {
    CHECK_EQ(unary_op->inputs.size(), 2) << "Sum needs 2 inputs";
    if (!IsConstantParameterArray(*model, unary_op->inputs[1])) {
      AddMessageF("Axis input is non-constant");
      return false;
    }
    auto& axis_array = model->GetArray(unary_op->inputs[1]);
    CHECK(axis_array.data_type == ArrayDataType::kInt32);
    int axis = axis_array.GetBuffer<ArrayDataType::kInt32>().data[0];
    CHECK_LT(axis, input_shape.dimensions_count()) << "Axis out of bounds";

    // We currently only handle reduction on axis 0.
    CHECK_EQ(axis, 0) << "Only reduction along axis 0 is supported";
    // We currently only handle 1-D and 2-D input tensors.
    CHECK_LE(input_shape.dimensions_count(), 2) << "Rank >2 not yet supported";
    // We only support keep_dims=true; shape prop will need to change otherwise.
    auto sum_op = static_cast<const TensorFlowSumOperator*>(unary_op);
    CHECK(sum_op->keep_dims) << "Only keep_dims=true is supported";

    std::vector<int> indices(input_shape.dimensions_count());
    for (int i = 0; i < input_shape.dims(1); ++i) {
      indices[1] = i;
      float sum = 0.f;
      for (int j = 0; j < input_shape.dims(0); ++j) {
        indices[0] = j;
        sum += (*input_float_data)[Offset(input_shape, indices)];
      }
      output_float_data[i] = sum;
    }
  } else if (unary_op->type == OperatorType::kReduceMin) {
    // At the moment only full reduction across all dimensions is supported.
    // TODO(starka): Output should not be padded.
    for (int i = 0; i < output_dims_count; i++) {
      CHECK_EQ(output_shape.dims(i), 1);
    }
    float min = (*input_float_data)[0];
    for (int i = 0; i < input_buffer_size; i++) {
      min = std::min(min, (*input_float_data)[i]);
    }
    output_float_data[0] = min;
  } else if (unary_op->type == OperatorType::kReduceMax) {
    // At the moment only full reduction across all dimensions is supported.
    // TODO(starka): Output should not be padded.
    for (int i = 0; i < output_dims_count; i++) {
      CHECK_EQ(output_shape.dims(i), 1);
    }
    float max = (*input_float_data)[0];
    for (int i = 0; i < input_buffer_size; i++) {
      max = std::max(max, (*input_float_data)[i]);
    }
    output_float_data[0] = max;
  } else if (unary_op->type == OperatorType::kNeg ||
             unary_op->type == OperatorType::kLog ||
             unary_op->type == OperatorType::kRsqrt ||
             unary_op->type == OperatorType::kSqrt ||
             unary_op->type == OperatorType::kSquare) {
    // Element-wise ops. Should have perfectly matching sizes here.
    for (int i = 0; i < output_dims_count; i++) {
      CHECK_EQ(output_shape.dims(i), input_shape.dims(i));
    }

    for (int i = 0; i < output_buffer_size; i++) {
      const float val = (*input_float_data)[i];
      float outval = 0.f;
      if (unary_op->type == OperatorType::kNeg) {
        outval = -val;
      } else if (unary_op->type == OperatorType::kLog) {
        outval = std::log(val);
      } else if (unary_op->type == OperatorType::kRsqrt) {
        outval = 1.0f / std::sqrt(val);
      } else if (unary_op->type == OperatorType::kSqrt) {
        outval = std::sqrt(val);
      } else if (unary_op->type == OperatorType::kSquare) {
        outval = val * val;
      } else {
        LOG(FATAL) << "should not get here.";
      }
      output_float_data[i] = outval;
    }
  } else if (unary_op->type == OperatorType::kRelu6 ||
             unary_op->type == OperatorType::kRelu1 ||
             unary_op->type == OperatorType::kRelu) {
    for (size_t i = 0; i < output_buffer_size; ++i) {
      const float value = (*input_float_data)[i];
      float new_value = 0.0f;
      switch (unary_op->type) {
        case OperatorType::kRelu: {
          static constexpr float kLower = 0;
          new_value = value < kLower ? kLower : value;
          break;
        }
        case OperatorType::kRelu1: {
          static constexpr float kUpper = 1;
          static constexpr float kLower = -1;
          new_value = value > kUpper ? kUpper : value < kLower ? kLower : value;
          break;
        }
        case OperatorType::kRelu6: {
          static constexpr float kUpper = 6;
          static constexpr float kLower = 0;
          new_value = value > kUpper ? kUpper : value < kLower ? kLower : value;
          break;
        }
        default:
          LOG(FATAL) << "Unsupported activation function "
                     << LogName(*unary_op);
          return false;
      }
      output_float_data[i] = new_value;
    }
  } else {
    LOG(FATAL) << "should not get here.";
  }
  for (const auto& input : unary_op->inputs) {
    if (CountOpsWithInput(*model, input) == 1) {
      model->EraseArray(input);
    }
  }
  AddMessageF("Resolved constant %s to the equivalent constant array",
              LogName(*unary_op));
  model->operators.erase(unary_it);
  return true;
}

}  // namespace toco
<commit_msg>Adding support for resolving constant Exp ops.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <string.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h"
#include "tensorflow/contrib/lite/toco/model.h"
#include "tensorflow/contrib/lite/toco/runtime/types.h"
#include "tensorflow/contrib/lite/toco/tooling_util.h"
#include "tensorflow/core/platform/logging.h"

namespace toco {

bool CopyMinMaxFromFirstInput(const Operator& op, Model* model) {
  auto& output_array = model->GetArray(op.outputs[0]);
  if (output_array.minmax) {
    return false;
  }
  const auto& input_array = model->GetArray(op.inputs[0]);
  if (!input_array.minmax) {
    return false;
  }
  const auto& input_minmax = input_array.GetMinMax();
  CHECK(!output_array.minmax);
  auto& output_minmax = output_array.GetOrCreateMinMax();
  output_minmax.min = input_minmax.min;
  output_minmax.max = input_minmax.max;
  return true;
}

bool ResolveConstantUnaryOperator::Run(Model* model, std::size_t op_index) {
  const auto unary_it = model->operators.begin() + op_index;
  const auto* unary_op = unary_it->get();
  // Test for unary ops of types that we know how to resolve.
  switch (unary_op->type) {
    case OperatorType::kCast:
    case OperatorType::kExp:
    case OperatorType::kLog:
    case OperatorType::kNeg:
    case OperatorType::kRsqrt:
    case OperatorType::kSqrt:
    case OperatorType::kSquare:
    case OperatorType::kSum:
    case OperatorType::kReduceMin:  //  Reduction Min
    case OperatorType::kReduceMax:  //  Reduction Max
    case OperatorType::kReshape:
    case OperatorType::kRelu6:
    case OperatorType::kRelu1:
    case OperatorType::kRelu:
      break;
    default:
      return false;
  }

  // Check if the input is a constant parameter.
  if (!IsConstantParameterArray(*model, unary_op->inputs[0])) {
    return false;
  }

  // if the unary op involves a tensor required by a rnn state, ignore it
  for (const auto& rnn_state : model->flags.rnn_states()) {
    if (unary_op->inputs[0] == rnn_state.back_edge_source_array()) {
      return false;
    }
    if (unary_op->inputs[0] == rnn_state.state_array()) {
      return false;
    }
  }

  auto& output_array = model->GetArray(unary_op->outputs[0]);
  if (!output_array.has_shape()) {
    // Yield until the output array dims have been resolved.
    return false;
  }

  // At the moment we don't want to care about fused activation functions.
  // The idea is that we should do the present constants-propagation before
  // activation functions get fused.
  if (unary_op->fused_activation_function !=
      FusedActivationFunctionType::kNone) {
    AddMessageF(
        "Not resolving constant %s "
        " because it has a fused activation function",
        LogName(*unary_op));
    return false;
  }

  // The min-max is only copied for ops that copy data without arithmetic.
  // In future trivial transpose, etc, can be handled here.
  if (unary_op->type == OperatorType::kReshape) {
    CopyMinMaxFromFirstInput(*unary_op, model);
  }

  const auto& input_array = model->GetArray(unary_op->inputs[0]);
  // We have already tested above for existence of buffers (synonymous to being
  // a constant param).
  CHECK(input_array.buffer);
  std::vector<DataType<ArrayDataType::kFloat>> const* input_float_data;
  if (unary_op->type == OperatorType::kCast) {
    CastOperator const* cast_op = static_cast<CastOperator const*>(unary_op);
    if (cast_op->dst_data_type != ArrayDataType::kFloat) {
      AddMessageF(
          "Not resolving constant %s because we currently only support casting "
          "to float",
          LogName(*unary_op));
      return false;
    }
    if (cast_op->src_data_type != input_array.buffer->type) {
      AddMessageF(
          "Not resolving constant %s because cast op source type does not "
          "match input type",
          LogName(*unary_op));
    }
  } else {
    if (input_array.buffer->type != ArrayDataType::kFloat) {
      return false;
    }
    input_float_data = &(input_array.GetBuffer<ArrayDataType::kFloat>().data);
  }

  // Create a float buffer on the output array, which are always constant.
  const Shape& output_shape = output_array.shape();
  const int output_dims_count = output_shape.dimensions_count();
  const int output_buffer_size = RequiredBufferSizeForShape(output_shape);
  auto& output_float_data =
      output_array.GetMutableBuffer<ArrayDataType::kFloat>().data;
  output_float_data.resize(output_buffer_size);

  const Shape& input_shape = input_array.shape();
  const int input_buffer_size = RequiredBufferSizeForShape(input_shape);
  if (unary_op->type == OperatorType::kCast) {
    for (int i = 0; i < output_buffer_size; i++) {
      float outval = 0.0f;
      if (input_array.buffer->type == ArrayDataType::kFloat) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kFloat>().data[i]);
      } else if (input_array.buffer->type == ArrayDataType::kUint8) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kUint8>().data[i]);
      } else if (input_array.buffer->type == ArrayDataType::kInt32) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kInt32>().data[i]);
      } else if (input_array.buffer->type == ArrayDataType::kInt64) {
        outval = static_cast<float>(
            input_array.GetBuffer<ArrayDataType::kInt64>().data[i]);
      } else {
        LOG(FATAL) << "Unsupported cast op input type";
      }
      output_float_data[i] = outval;
    }
  } else if (unary_op->type == OperatorType::kReshape) {
    CHECK(input_buffer_size == output_buffer_size);
    output_float_data = *input_float_data;
  } else if (unary_op->type == OperatorType::kSum) {
    CHECK_EQ(unary_op->inputs.size(), 2) << "Sum needs 2 inputs";
    if (!IsConstantParameterArray(*model, unary_op->inputs[1])) {
      AddMessageF("Axis input is non-constant");
      return false;
    }
    auto& axis_array = model->GetArray(unary_op->inputs[1]);
    CHECK(axis_array.data_type == ArrayDataType::kInt32);
    int axis = axis_array.GetBuffer<ArrayDataType::kInt32>().data[0];
    CHECK_LT(axis, input_shape.dimensions_count()) << "Axis out of bounds";

    // We currently only handle reduction on axis 0.
    CHECK_EQ(axis, 0) << "Only reduction along axis 0 is supported";
    // We currently only handle 1-D and 2-D input tensors.
    CHECK_LE(input_shape.dimensions_count(), 2) << "Rank >2 not yet supported";
    // We only support keep_dims=true; shape prop will need to change otherwise.
    auto sum_op = static_cast<const TensorFlowSumOperator*>(unary_op);
    CHECK(sum_op->keep_dims) << "Only keep_dims=true is supported";

    std::vector<int> indices(input_shape.dimensions_count());
    for (int i = 0; i < input_shape.dims(1); ++i) {
      indices[1] = i;
      float sum = 0.f;
      for (int j = 0; j < input_shape.dims(0); ++j) {
        indices[0] = j;
        sum += (*input_float_data)[Offset(input_shape, indices)];
      }
      output_float_data[i] = sum;
    }
  } else if (unary_op->type == OperatorType::kReduceMin) {
    // At the moment only full reduction across all dimensions is supported.
    // TODO(starka): Output should not be padded.
    for (int i = 0; i < output_dims_count; i++) {
      CHECK_EQ(output_shape.dims(i), 1);
    }
    float min = (*input_float_data)[0];
    for (int i = 0; i < input_buffer_size; i++) {
      min = std::min(min, (*input_float_data)[i]);
    }
    output_float_data[0] = min;
  } else if (unary_op->type == OperatorType::kReduceMax) {
    // At the moment only full reduction across all dimensions is supported.
    // TODO(starka): Output should not be padded.
    for (int i = 0; i < output_dims_count; i++) {
      CHECK_EQ(output_shape.dims(i), 1);
    }
    float max = (*input_float_data)[0];
    for (int i = 0; i < input_buffer_size; i++) {
      max = std::max(max, (*input_float_data)[i]);
    }
    output_float_data[0] = max;
  } else if (unary_op->type == OperatorType::kExp ||
             unary_op->type == OperatorType::kNeg ||
             unary_op->type == OperatorType::kLog ||
             unary_op->type == OperatorType::kRsqrt ||
             unary_op->type == OperatorType::kSqrt ||
             unary_op->type == OperatorType::kSquare) {
    // Element-wise ops. Should have perfectly matching sizes here.
    for (int i = 0; i < output_dims_count; i++) {
      CHECK_EQ(output_shape.dims(i), input_shape.dims(i));
    }

    for (int i = 0; i < output_buffer_size; i++) {
      const float val = (*input_float_data)[i];
      float outval = 0.f;
      if (unary_op->type == OperatorType::kExp) {
        outval = std::exp(val);
      } else if (unary_op->type == OperatorType::kNeg) {
        outval = -val;
      } else if (unary_op->type == OperatorType::kLog) {
        outval = std::log(val);
      } else if (unary_op->type == OperatorType::kRsqrt) {
        outval = 1.0f / std::sqrt(val);
      } else if (unary_op->type == OperatorType::kSqrt) {
        outval = std::sqrt(val);
      } else if (unary_op->type == OperatorType::kSquare) {
        outval = val * val;
      } else {
        LOG(FATAL) << "should not get here.";
      }
      output_float_data[i] = outval;
    }
  } else if (unary_op->type == OperatorType::kRelu6 ||
             unary_op->type == OperatorType::kRelu1 ||
             unary_op->type == OperatorType::kRelu) {
    for (size_t i = 0; i < output_buffer_size; ++i) {
      const float value = (*input_float_data)[i];
      float new_value = 0.0f;
      switch (unary_op->type) {
        case OperatorType::kRelu: {
          static constexpr float kLower = 0;
          new_value = value < kLower ? kLower : value;
          break;
        }
        case OperatorType::kRelu1: {
          static constexpr float kUpper = 1;
          static constexpr float kLower = -1;
          new_value = value > kUpper ? kUpper : value < kLower ? kLower : value;
          break;
        }
        case OperatorType::kRelu6: {
          static constexpr float kUpper = 6;
          static constexpr float kLower = 0;
          new_value = value > kUpper ? kUpper : value < kLower ? kLower : value;
          break;
        }
        default:
          LOG(FATAL) << "Unsupported activation function "
                     << LogName(*unary_op);
          return false;
      }
      output_float_data[i] = new_value;
    }
  } else {
    LOG(FATAL) << "should not get here.";
  }
  for (const auto& input : unary_op->inputs) {
    if (CountOpsWithInput(*model, input) == 1) {
      model->EraseArray(input);
    }
  }
  AddMessageF("Resolved constant %s to the equivalent constant array",
              LogName(*unary_op));
  model->operators.erase(unary_it);
  return true;
}

}  // namespace toco
<|endoftext|>


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

#include "chrome/browser/net/resolve_proxy_msg_helper.h"

#include "base/waitable_event.h"
#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"

// This ProxyConfigService always returns "http://pac" as the PAC url to use.
class MockProxyConfigService: public net::ProxyConfigService {
 public:
  virtual int GetProxyConfig(net::ProxyConfig* results) {
    results->pac_url = GURL("http://pac");
    return net::OK;
  }
};

// This PAC resolver always returns the hostname of the query URL as the
// proxy to use. The Block() method will make GetProxyForURL() hang until
// Unblock() is called.
class MockProxyResolver : public net::ProxyResolver {
 public:
  MockProxyResolver() : ProxyResolver(true), event_(false, false),
                        is_blocked_(false) {
  }

  virtual int GetProxyForURL(const GURL& query_url,
                             const GURL& /*pac_url*/,
                             net::ProxyInfo* results) {
    if (is_blocked_)
      event_.Wait();
    results->UseNamedProxy(query_url.host());
    return net::OK;
  }

  void Block() {
    is_blocked_ = true;
    event_.Reset();
  }

  void Unblock() {
    is_blocked_ = false;
    event_.Signal();
  }

 private:
  base::WaitableEvent event_;
  bool is_blocked_;
};

// This struct holds the values that were passed to
// Delegate::OnResolveProxyCompleted(). The caller should use WaitUntilDone()
// to block until the result has been populated.
struct ResultFuture {
 public:
  ResultFuture()
      : reply_msg(NULL),
        error_code(0),
        started_(false, false),
        completed_(false, false) {
  }

  // Wait until the request has completed. In other words we have invoked:
  // ResolveProxyMsgHelper::Delegate::OnResolveProxyCompleted.
  void WaitUntilDone() {
    completed_.Wait();
  }

  // Wait until the request has been sent to ResolveProxyMsgHelper.
  void WaitUntilStarted() {
    started_.Wait();
  }

  bool TimedWaitUntilDone(const base::TimeDelta& max_time) {
    return completed_.TimedWait(max_time);
  }

  // These fields are only valid after returning from WaitUntilDone().
  IPC::Message* reply_msg;
  int error_code;
  std::string proxy_list;

 private:
  friend class AsyncRequestRunner;
  base::WaitableEvent started_;
  base::WaitableEvent completed_;
};

// This class lives on the io thread. It starts async requests using the
// class under test (ResolveProxyMsgHelper), and signals the result future on
// completion.
class AsyncRequestRunner : public ResolveProxyMsgHelper::Delegate {
 public:
  AsyncRequestRunner(net::ProxyService* proxy_service) {
    resolve_proxy_msg_helper_.reset(
        new ResolveProxyMsgHelper(this, proxy_service));
  }

  void Start(ResultFuture* future, const GURL& url, IPC::Message* reply_msg) {
    futures_.push_back(future);
    resolve_proxy_msg_helper_->Start(url, reply_msg);

    // Notify of request start.
    future->started_.Signal();
  }

  virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
                                       int error_code,
                                       const std::string& proxy_list) {
    // Update the result future for this request (top of queue), and signal it.
    ResultFuture* future = futures_.front();
    futures_.pop_front();

    future->reply_msg = reply_msg;
    future->error_code = error_code;
    future->proxy_list = proxy_list;

    // Notify of request completion.
    future->completed_.Signal();
  }

 private:
  std::deque<ResultFuture*> futures_;
  scoped_ptr<ResolveProxyMsgHelper> resolve_proxy_msg_helper_;
};

// Helper class to start async requests on an io thread, and return a
// result future. The caller then uses ResultFuture::WaitUntilDone() to
// get at the results. It "bridges" the originating thread with the helper
// io thread.
class RunnerBridge {
 public:
  RunnerBridge() : io_thread_("io_thread"), done_(true, false) {
    // Start an io thread where we will run the async requests.
    base::Thread::Options options;
    options.message_loop_type = MessageLoop::TYPE_IO;
    io_thread_.StartWithOptions(options);

    // Construct the state that lives on io thread.
    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        this, &RunnerBridge::DoConstruct));
    done_.Wait();
  }

  // Start an async request on the io thread.
  ResultFuture* Start(const GURL& url, IPC::Message* reply_msg) {
    ResultFuture* future = new ResultFuture();

    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        async_runner_, &AsyncRequestRunner::Start, future, url, reply_msg));

    return future;
  }

  void DestroyAsyncRunner() {
    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        this, &RunnerBridge::DoDestroyAsyncRunner));
    done_.Wait();
  }

  ~RunnerBridge() {
    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        this, &RunnerBridge::DoDestroy));
    done_.Wait();
  }

  MockProxyResolver* proxy_resolver() {
    return proxy_resolver_;
  }

  // Called from io thread.
  void DoConstruct() {
    proxy_resolver_ = new MockProxyResolver();
    proxy_service_ = new net::ProxyService(new MockProxyConfigService(),
                                           proxy_resolver_);
    async_runner_ = new AsyncRequestRunner(proxy_service_);
    done_.Signal();
  }

  // Called from io thread.
  void DoDestroy() {
    delete async_runner_;
    delete proxy_service_;
    done_.Signal();
  }

  // Called from io thread.
  void DoDestroyAsyncRunner() {
    delete async_runner_;
    async_runner_ = NULL;
    done_.Signal();
  }

 private:
  base::Thread io_thread_;
  base::WaitableEvent done_;

  net::ProxyService* proxy_service_;
  MockProxyResolver* proxy_resolver_;  // Owned by proxy_service_.

  AsyncRequestRunner* async_runner_;
};

// Avoid the need to have an AddRef / Release
template<>
void RunnableMethodTraits<RunnerBridge>::RetainCallee(RunnerBridge*) {}
template<>
void RunnableMethodTraits<RunnerBridge>::ReleaseCallee(RunnerBridge*) {}

template<>
void RunnableMethodTraits<AsyncRequestRunner>::RetainCallee(AsyncRequestRunner*) {}
template<>
void RunnableMethodTraits<AsyncRequestRunner>::ReleaseCallee(AsyncRequestRunner*) {}

// Issue three sequential requests -- each should succeed.
TEST(ResolveProxyMsgHelperTest, Sequential) {
  RunnerBridge runner;

  GURL url1("http://www.google1.com/");
  GURL url2("http://www.google2.com/");
  GURL url3("http://www.google3.com/");

  scoped_ptr<IPC::Message> msg1(new IPC::Message());
  scoped_ptr<IPC::Message> msg2(new IPC::Message());
  scoped_ptr<IPC::Message> msg3(new IPC::Message());

  // Execute each request sequentially (so there are never 2 requests
  // outstanding at the same time).

  scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get()));
  result1->WaitUntilDone();

  scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get()));
  result2->WaitUntilDone();

  scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));
  result3->WaitUntilDone();

  // Check that each request gave the expected result.

  EXPECT_EQ(msg1.get(), result1->reply_msg);
  EXPECT_EQ(net::OK, result1->error_code);
  EXPECT_EQ("PROXY www.google1.com:80", result1->proxy_list);

  EXPECT_EQ(msg2.get(), result2->reply_msg);
  EXPECT_EQ(net::OK, result2->error_code);
  EXPECT_EQ("PROXY www.google2.com:80", result2->proxy_list);

  EXPECT_EQ(msg3.get(), result3->reply_msg);
  EXPECT_EQ(net::OK, result3->error_code);
  EXPECT_EQ("PROXY www.google3.com:80", result3->proxy_list);
}

// Issue a request while one is already in progress -- should be queued.
TEST(ResolveProxyMsgHelperTest, QueueRequests) {
  RunnerBridge runner;

  GURL url1("http://www.google1.com/");
  GURL url2("http://www.google2.com/");
  GURL url3("http://www.google3.com/");

  scoped_ptr<IPC::Message> msg1(new IPC::Message());
  scoped_ptr<IPC::Message> msg2(new IPC::Message());
  scoped_ptr<IPC::Message> msg3(new IPC::Message());

  // Make the proxy resolver hang on the next request.
  runner.proxy_resolver()->Block();

  // Start three requests. Since the proxy resolver is hung, the second two
  // will be pending.

  scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get()));
  scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get()));
  scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));

  // Wait for the final request to have been scheduled. Otherwise we may rush
  // to calling Unblock() without actually having blocked anything.
  result3->WaitUntilStarted();

  // Unblock the proxy service so requests 1-3 can complete.
  runner.proxy_resolver()->Unblock();

  // Wait for all the requests to finish (they run in FIFO order).
  result3->WaitUntilDone();

  // Check that each call invoked the callback with the right parameters.

  EXPECT_EQ(msg1.get(), result1->reply_msg);
  EXPECT_EQ(net::OK, result1->error_code);
  EXPECT_EQ("PROXY www.google1.com:80", result1->proxy_list);

  EXPECT_EQ(msg2.get(), result2->reply_msg);
  EXPECT_EQ(net::OK, result2->error_code);
  EXPECT_EQ("PROXY www.google2.com:80", result2->proxy_list);

  EXPECT_EQ(msg3.get(), result3->reply_msg);
  EXPECT_EQ(net::OK, result3->error_code);
  EXPECT_EQ("PROXY www.google3.com:80", result3->proxy_list);
}

// Delete the helper while a request is in progress, and others are pending.
TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) {
  RunnerBridge runner;

  GURL url1("http://www.google1.com/");
  GURL url2("http://www.google2.com/");
  GURL url3("http://www.google3.com/");

  // NOTE: these are not scoped ptr, since they will be deleted by the
  // request's cancellation.
  IPC::Message* msg1 = new IPC::Message();
  IPC::Message* msg2 = new IPC::Message();
  IPC::Message* msg3 = new IPC::Message();

  // Make the next request block.
  runner.proxy_resolver()->Block();

  // Start three requests; since the first one blocked, the other two should
  // be pending.

  scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1));
  scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2));
  scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3));

  result3->WaitUntilStarted();

  // Delete the underlying ResolveProxyMsgHelper -- this should cancel all
  // the requests which are outstanding.
  runner.DestroyAsyncRunner();

  // Unblocking the proxy resolver means the three requests can complete --
  // however they should not try to notify the delegate since we have already
  // deleted the helper.
  runner.proxy_resolver()->Unblock();

  // Check that none of the requests were sent to the delegate.
  EXPECT_FALSE(
      result1->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
  EXPECT_FALSE(
      result2->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
  EXPECT_FALSE(
      result3->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));

  // It should also be the case that msg1, msg2, msg3 were deleted by the
  // cancellation. (Else will show up as a leak in Purify).
}

<commit_msg>Add file that got missing from main checkin.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/net/resolve_proxy_msg_helper.h"

#include "base/waitable_event.h"
#include "net/base/net_errors.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"

// This ProxyConfigService always returns "http://pac" as the PAC url to use.
class MockProxyConfigService: public net::ProxyConfigService {
 public:
  virtual int GetProxyConfig(net::ProxyConfig* results) {
    results->pac_url = GURL("http://pac");
    return net::OK;
  }
};

// This PAC resolver always returns the hostname of the query URL as the
// proxy to use. The Block() method will make GetProxyForURL() hang until
// Unblock() is called.
class MockProxyResolver : public net::ProxyResolver {
 public:
  MockProxyResolver() : ProxyResolver(true), event_(false, false),
                        is_blocked_(false) {
  }

  virtual int GetProxyForURL(const GURL& query_url,
                             const GURL& /*pac_url*/,
                             net::ProxyInfo* results) {
    if (is_blocked_)
      event_.Wait();
    results->UseNamedProxy(query_url.host());
    return net::OK;
  }

  void Block() {
    is_blocked_ = true;
    event_.Reset();
  }

  void Unblock() {
    is_blocked_ = false;
    event_.Signal();
  }

 private:
  base::WaitableEvent event_;
  bool is_blocked_;
};

// This struct holds the values that were passed to
// Delegate::OnResolveProxyCompleted(). The caller should use WaitUntilDone()
// to block until the result has been populated.
struct ResultFuture {
 public:
  ResultFuture()
      : reply_msg(NULL),
        error_code(0),
        started_(false, false),
        completed_(false, false) {
  }

  // Wait until the request has completed. In other words we have invoked:
  // ResolveProxyMsgHelper::Delegate::OnResolveProxyCompleted.
  void WaitUntilDone() {
    completed_.Wait();
  }

  // Wait until the request has been sent to ResolveProxyMsgHelper.
  void WaitUntilStarted() {
    started_.Wait();
  }

  bool TimedWaitUntilDone(const base::TimeDelta& max_time) {
    return completed_.TimedWait(max_time);
  }

  // These fields are only valid after returning from WaitUntilDone().
  IPC::Message* reply_msg;
  int error_code;
  std::string proxy_list;

 private:
  friend class AsyncRequestRunner;
  base::WaitableEvent started_;
  base::WaitableEvent completed_;
};

// This class lives on the io thread. It starts async requests using the
// class under test (ResolveProxyMsgHelper), and signals the result future on
// completion.
class AsyncRequestRunner : public ResolveProxyMsgHelper::Delegate {
 public:
  AsyncRequestRunner(net::ProxyService* proxy_service) {
    resolve_proxy_msg_helper_.reset(
        new ResolveProxyMsgHelper(this, proxy_service));
  }

  void Start(ResultFuture* future, const GURL& url, IPC::Message* reply_msg) {
    futures_.push_back(future);
    resolve_proxy_msg_helper_->Start(url, reply_msg);

    // Notify of request start.
    future->started_.Signal();
  }

  virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
                                       int error_code,
                                       const std::string& proxy_list) {
    // Update the result future for this request (top of queue), and signal it.
    ResultFuture* future = futures_.front();
    futures_.pop_front();

    future->reply_msg = reply_msg;
    future->error_code = error_code;
    future->proxy_list = proxy_list;

    // Notify of request completion.
    future->completed_.Signal();
  }

 private:
  std::deque<ResultFuture*> futures_;
  scoped_ptr<ResolveProxyMsgHelper> resolve_proxy_msg_helper_;
};

// Helper class to start async requests on an io thread, and return a
// result future. The caller then uses ResultFuture::WaitUntilDone() to
// get at the results. It "bridges" the originating thread with the helper
// io thread.
class RunnerBridge {
 public:
  RunnerBridge() : io_thread_("io_thread"), done_(true, false) {
    // Start an io thread where we will run the async requests.
    base::Thread::Options options;
    options.message_loop_type = MessageLoop::TYPE_IO;
    io_thread_.StartWithOptions(options);

    // Construct the state that lives on io thread.
    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        this, &RunnerBridge::DoConstruct));
    done_.Wait();
  }

  // Start an async request on the io thread.
  ResultFuture* Start(const GURL& url, IPC::Message* reply_msg) {
    ResultFuture* future = new ResultFuture();

    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        async_runner_, &AsyncRequestRunner::Start, future, url, reply_msg));

    return future;
  }

  void DestroyAsyncRunner() {
    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        this, &RunnerBridge::DoDestroyAsyncRunner));
    done_.Wait();
  }

  ~RunnerBridge() {
    io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
        this, &RunnerBridge::DoDestroy));
    done_.Wait();
  }

  MockProxyResolver* proxy_resolver() {
    return proxy_resolver_;
  }

  // Called from io thread.
  void DoConstruct() {
    proxy_resolver_ = new MockProxyResolver();
    proxy_service_ = new net::ProxyService(new MockProxyConfigService(),
                                           proxy_resolver_);
    async_runner_ = new AsyncRequestRunner(proxy_service_);
    done_.Signal();
  }

  // Called from io thread.
  void DoDestroy() {
    delete async_runner_;
    delete proxy_service_;
    done_.Signal();
  }

  // Called from io thread.
  void DoDestroyAsyncRunner() {
    delete async_runner_;
    async_runner_ = NULL;
    done_.Signal();
  }

 private:
  base::Thread io_thread_;
  base::WaitableEvent done_;

  net::ProxyService* proxy_service_;
  MockProxyResolver* proxy_resolver_;  // Owned by proxy_service_.

  AsyncRequestRunner* async_runner_;
};

// Avoid the need to have an AddRef / Release
template<>
void RunnableMethodTraits<RunnerBridge>::RetainCallee(RunnerBridge*) {}
template<>
void RunnableMethodTraits<RunnerBridge>::ReleaseCallee(RunnerBridge*) {}

template<>
void RunnableMethodTraits<AsyncRequestRunner>::RetainCallee(AsyncRequestRunner*) {}
template<>
void RunnableMethodTraits<AsyncRequestRunner>::ReleaseCallee(AsyncRequestRunner*) {}

// Issue three sequential requests -- each should succeed.
TEST(ResolveProxyMsgHelperTest, Sequential) {
  RunnerBridge runner;

  GURL url1("http://www.google1.com/");
  GURL url2("http://www.google2.com/");
  GURL url3("http://www.google3.com/");

  scoped_ptr<IPC::Message> msg1(new IPC::Message());
  scoped_ptr<IPC::Message> msg2(new IPC::Message());
  scoped_ptr<IPC::Message> msg3(new IPC::Message());

  // Execute each request sequentially (so there are never 2 requests
  // outstanding at the same time).

  scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get()));
  result1->WaitUntilDone();

  scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get()));
  result2->WaitUntilDone();

  scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));
  result3->WaitUntilDone();

  // Check that each request gave the expected result.

  EXPECT_EQ(msg1.get(), result1->reply_msg);
  EXPECT_EQ(net::OK, result1->error_code);
  EXPECT_EQ("PROXY www.google1.com:80", result1->proxy_list);

  EXPECT_EQ(msg2.get(), result2->reply_msg);
  EXPECT_EQ(net::OK, result2->error_code);
  EXPECT_EQ("PROXY www.google2.com:80", result2->proxy_list);

  EXPECT_EQ(msg3.get(), result3->reply_msg);
  EXPECT_EQ(net::OK, result3->error_code);
  EXPECT_EQ("PROXY www.google3.com:80", result3->proxy_list);
}

// Issue a request while one is already in progress -- should be queued.
TEST(ResolveProxyMsgHelperTest, QueueRequests) {
  RunnerBridge runner;

  GURL url1("http://www.google1.com/");
  GURL url2("http://www.google2.com/");
  GURL url3("http://www.google3.com/");

  scoped_ptr<IPC::Message> msg1(new IPC::Message());
  scoped_ptr<IPC::Message> msg2(new IPC::Message());
  scoped_ptr<IPC::Message> msg3(new IPC::Message());

  // Make the proxy resolver hang on the next request.
  runner.proxy_resolver()->Block();

  // Start three requests. Since the proxy resolver is hung, the second two
  // will be pending.

  scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get()));
  scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get()));
  scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));

  // Wait for the final request to have been scheduled. Otherwise we may rush
  // to calling Unblock() without actually having blocked anything.
  result3->WaitUntilStarted();

  // Unblock the proxy service so requests 1-3 can complete.
  runner.proxy_resolver()->Unblock();

  // Wait for all the requests to finish (they run in FIFO order).
  result3->WaitUntilDone();

  // Check that each call invoked the callback with the right parameters.

  EXPECT_EQ(msg1.get(), result1->reply_msg);
  EXPECT_EQ(net::OK, result1->error_code);
  EXPECT_EQ("PROXY www.google1.com:80", result1->proxy_list);

  EXPECT_EQ(msg2.get(), result2->reply_msg);
  EXPECT_EQ(net::OK, result2->error_code);
  EXPECT_EQ("PROXY www.google2.com:80", result2->proxy_list);

  EXPECT_EQ(msg3.get(), result3->reply_msg);
  EXPECT_EQ(net::OK, result3->error_code);
  EXPECT_EQ("PROXY www.google3.com:80", result3->proxy_list);
}

// Delete the helper while a request is in progress, and others are pending.
TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) {
  RunnerBridge runner;

  GURL url1("http://www.google1.com/");
  GURL url2("http://www.google2.com/");
  GURL url3("http://www.google3.com/");

  // NOTE: these are not scoped ptr, since they will be deleted by the
  // request's cancellation.
  IPC::Message* msg1 = new IPC::Message();
  IPC::Message* msg2 = new IPC::Message();
  IPC::Message* msg3 = new IPC::Message();

  // Make the next request block.
  runner.proxy_resolver()->Block();

  // Start three requests; since the first one blocked, the other two should
  // be pending.

  scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1));
  scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2));
  scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3));

  result3->WaitUntilStarted();

  // Delete the underlying ResolveProxyMsgHelper -- this should cancel all
  // the requests which are outstanding.
  runner.DestroyAsyncRunner();

  // Unblocking the proxy resolver means the three requests can complete --
  // however they should not try to notify the delegate since we have already
  // deleted the helper.
  runner.proxy_resolver()->Unblock();

  // Check that none of the requests were sent to the delegate.
  EXPECT_FALSE(
      result1->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
  EXPECT_FALSE(
      result2->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
  EXPECT_FALSE(
      result3->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));

  // It should also be the case that msg1, msg2, msg3 were deleted by the
  // cancellation. (Else will show up as a leak in Purify).
}

<|endoftext|>