| Index: chrome/browser/sync/engine/change_reorder_buffer.cc
|
| ===================================================================
|
| --- chrome/browser/sync/engine/change_reorder_buffer.cc (revision 0)
|
| +++ chrome/browser/sync/engine/change_reorder_buffer.cc (revision 0)
|
| @@ -0,0 +1,199 @@
|
| +// Copyright (c) 2006-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/sync/engine/change_reorder_buffer.h"
|
| +
|
| +#include <limits>
|
| +#include <queue>
|
| +#include <set>
|
| +#include <utility> // for pair<>
|
| +#include <vector>
|
| +
|
| +#include "chrome/browser/sync/syncable/syncable.h"
|
| +
|
| +using std::numeric_limits;
|
| +using std::pair;
|
| +using std::queue;
|
| +using std::set;
|
| +using std::vector;
|
| +
|
| +namespace sync_api {
|
| +
|
| +// Traversal provides a way to collect a set of nodes from the syncable
|
| +// directory structure and then traverse them, along with any intermediate
|
| +// nodes, in a top-down fashion, starting from a single common ancestor. A
|
| +// Traversal starts out empty and is grown by means of the ExpandToInclude
|
| +// method. Once constructed, the top(), begin_children(), and end_children()
|
| +// methods can be used to explore the nodes in root-to-leaf order.
|
| +class ChangeReorderBuffer::Traversal {
|
| + public:
|
| + typedef pair<int64, int64> ParentChildLink;
|
| + typedef set<ParentChildLink> LinkSet;
|
| +
|
| + Traversal() : top_(kInvalidId) { }
|
| +
|
| + // Expand the traversal so that it includes the node indicated by
|
| + // |child_handle|.
|
| + void ExpandToInclude(syncable::BaseTransaction* trans,
|
| + int64 child_handle) {
|
| + // If |top_| is invalid, this is the first insertion -- easy.
|
| + if (top_ == kInvalidId) {
|
| + top_ = child_handle;
|
| + return;
|
| + }
|
| +
|
| + int64 node_to_include = child_handle;
|
| + while (node_to_include != kInvalidId && node_to_include != top_) {
|
| + int64 node_parent = 0;
|
| +
|
| + syncable::Entry node(trans, syncable::GET_BY_HANDLE, node_to_include);
|
| + CHECK(node.good());
|
| + if (node.Get(syncable::ID).IsRoot()) {
|
| + // If we've hit the root, and the root isn't already in the tree
|
| + // (it would have to be |top_| if it were), start a new expansion
|
| + // upwards from |top_| to unite the original traversal with the
|
| + // path we just added that goes from |child_handle| to the root.
|
| + node_to_include = top_;
|
| + top_ = node.Get(syncable::META_HANDLE);
|
| + } else {
|
| + // Otherwise, get the parent ID so that we can add a ParentChildLink.
|
| + syncable::Entry parent(trans, syncable::GET_BY_ID,
|
| + node.Get(syncable::PARENT_ID));
|
| + CHECK(parent.good());
|
| + node_parent = parent.Get(syncable::META_HANDLE);
|
| +
|
| + ParentChildLink link(node_parent, node_to_include);
|
| +
|
| + // If the link exists in the LinkSet |links_|, we don't need to search
|
| + // any higher; we are done.
|
| + if (links_.find(link) != links_.end())
|
| + return;
|
| +
|
| + // Otherwise, extend |links_|, and repeat on the parent.
|
| + links_.insert(link);
|
| + node_to_include = node_parent;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Return the top node of the traversal. Use this as a starting point
|
| + // for walking the tree.
|
| + int64 top() const { return top_; }
|
| +
|
| + // Return an iterator corresponding to the first child (in the traversal)
|
| + // of the node specified by |parent_id|. Iterate this return value until
|
| + // it is equal to the value returned by end_children(parent_id). The
|
| + // enumeration thus provided is unordered.
|
| + LinkSet::const_iterator begin_children(int64 parent_id) const {
|
| + return links_.upper_bound(
|
| + ParentChildLink(parent_id, numeric_limits<int64>::min()));
|
| + }
|
| +
|
| + // Return an iterator corresponding to the last child in the traversal
|
| + // of the node specified by |parent_id|.
|
| + LinkSet::const_iterator end_children(int64 parent_id) const {
|
| + return begin_children(parent_id + 1);
|
| + }
|
| +
|
| + private:
|
| + // The topmost point in the directory hierarchy that is in the traversal,
|
| + // and thus the first node to be traversed. If the traversal is empty,
|
| + // this is kInvalidId. If the traversal contains exactly one member, |top_|
|
| + // will be the solitary member, and |links_| will be empty.
|
| + int64 top_;
|
| + // A set of single-level links that compose the traversal below |top_|. The
|
| + // (parent, child) ordering of values enables efficient lookup of children
|
| + // given the parent handle, which is used for top-down traversal. |links_|
|
| + // is expected to be connected -- every node that appears as a parent in a
|
| + // link must either appear as a child of another link, or else be the
|
| + // topmost node, |top_|.
|
| + LinkSet links_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Traversal);
|
| +};
|
| +
|
| +void ChangeReorderBuffer::GetAllChangesInTreeOrder(
|
| + const BaseTransaction* sync_trans,
|
| + vector<ChangeRecord>* changelist) {
|
| + syncable::BaseTransaction* trans = sync_trans->GetWrappedTrans();
|
| +
|
| + // Step 1: Iterate through the operations, doing three things:
|
| + // (a) Push deleted items straight into the |changelist|.
|
| + // (b) Construct a traversal spanning all non-deleted items.
|
| + // (c) Construct a set of all parent nodes of any position changes.
|
| + set<int64> parents_of_position_changes;
|
| + Traversal traversal;
|
| +
|
| + OperationMap::const_iterator i;
|
| + for (i = operations_.begin(); i != operations_.end(); ++i) {
|
| + if (i->second == OP_DELETE) {
|
| + ChangeRecord record;
|
| + record.id = i->first;
|
| + record.action = ChangeRecord::ACTION_DELETE;
|
| + changelist->push_back(record);
|
| + } else {
|
| + traversal.ExpandToInclude(trans, i->first);
|
| + if (i->second == OP_ADD ||
|
| + i->second == OP_UPDATE_POSITION_AND_PROPERTIES) {
|
| + ReadNode node(sync_trans);
|
| + CHECK(node.InitByIdLookup(i->first));
|
| + parents_of_position_changes.insert(node.GetParentId());
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Step 2: Breadth-first expansion of the traversal, enumerating children in
|
| + // the syncable sibling order if there were any position updates.
|
| + queue<int64> to_visit;
|
| + to_visit.push(traversal.top());
|
| + while (!to_visit.empty()) {
|
| + int64 next = to_visit.front();
|
| + to_visit.pop();
|
| +
|
| + // If the node has an associated action, output a change record.
|
| + i = operations_.find(next);
|
| + if (i != operations_.end()) {
|
| + ChangeRecord record;
|
| + record.id = next;
|
| + if (i->second == OP_ADD)
|
| + record.action = ChangeRecord::ACTION_ADD;
|
| + else
|
| + record.action = ChangeRecord::ACTION_UPDATE;
|
| + changelist->push_back(record);
|
| + }
|
| +
|
| + // Now add the children of |next| to |to_visit|.
|
| + if (parents_of_position_changes.find(next) ==
|
| + parents_of_position_changes.end()) {
|
| + // No order changes on this parent -- traverse only the nodes listed
|
| + // in the traversal (and not in sibling order).
|
| + Traversal::LinkSet::const_iterator j = traversal.begin_children(next);
|
| + Traversal::LinkSet::const_iterator end = traversal.end_children(next);
|
| + for (; j != end; ++j) {
|
| + CHECK(j->first == next);
|
| + to_visit.push(j->second);
|
| + }
|
| + } else {
|
| + // There were ordering changes on the children of this parent, so
|
| + // enumerate all the children in the sibling order.
|
| + syncable::Entry parent(trans, syncable::GET_BY_HANDLE, next);
|
| + syncable::Id id = trans->directory()->
|
| + GetFirstChildId(trans, parent.Get(syncable::ID));
|
| + while (!id.IsRoot()) {
|
| + syncable::Entry child(trans, syncable::GET_BY_ID, id);
|
| + CHECK(child.good());
|
| + int64 handle = child.Get(syncable::META_HANDLE);
|
| + to_visit.push(handle);
|
| + // If there is no operation on this child node, record it as as an
|
| + // update, so that the listener gets notified of all nodes in the new
|
| + // ordering.
|
| + if (operations_.find(handle) == operations_.end())
|
| + operations_[handle] = OP_UPDATE_POSITION_AND_PROPERTIES;
|
| + id = child.Get(syncable::NEXT_ID);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace sync_api
|
|
|
| Property changes on: chrome\browser\sync\engine\change_reorder_buffer.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|