blob: 90cde01ad94d2b4389ef673fed34ed3bb24176a1 [file] [log] [blame]
// Copyright 2020 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.
#ifndef COMPONENTS_FEED_CORE_V2_STREAM_MODEL_FEATURE_TREE_H_
#define COMPONENTS_FEED_CORE_V2_STREAM_MODEL_FEATURE_TREE_H_
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/util/type_safety/id_type.h"
#include "components/feed/core/proto/v2/store.pb.h"
#include "components/feed/core/v2/proto_util.h"
#include "components/feed/core/v2/types.h"
namespace feed {
namespace stream_model {
// Uniquely identifies a feedwire::ContentId. Provided by |ContentMap|.
using ContentTag = util::IdTypeU32<class ContentTagClass>;
using ContentRevision = feed::ContentRevision;
// Owns instances of feedstore::Content pointed to by the feature tree, and
// maps ContentId into ContentTag.
class ContentMap {
public:
ContentMap();
~ContentMap();
ContentMap(const ContentMap&) = delete;
ContentMap& operator=(const ContentMap&) = delete;
ContentTag GetContentTag(const feedwire::ContentId& id);
const feedstore::Content* FindContent(ContentRevision content_revision);
ContentRevision LookupContentRevision(const feedstore::Content& content);
ContentRevision AddContent(feedstore::Content content);
void Clear();
private:
ContentTag::Generator tag_generator_;
ContentRevision::Generator revision_generator_;
std::map<feedwire::ContentId, ContentTag, ContentIdCompareFunctor> mapping_;
// These two containers work together to store and index content.
// Each unique piece of content is stored once, and never removed.
std::map<feedstore::Content, ContentRevision, ContentCompareFunctor> content_;
std::vector<const feedstore::Content*> revision_to_content_;
};
// A node in FeatureTree.
struct StreamNode {
StreamNode();
~StreamNode();
StreamNode(const StreamNode&);
StreamNode& operator=(const StreamNode&);
// If true, this nodes has been removed and should be ignored.
bool tombstoned = false;
// Whether this node has a parent.
bool has_parent = false;
// If this node has content, this identifies it.
ContentRevision content_revision;
// Child relations are stored in linked-list fashion.
// The ID of the last child, or null.
ContentTag last_child;
// The ID of the sibling before this one.
ContentTag previous_sibling;
};
// The feature tree which underlies StreamModel.
// This tree is different than most, the rules are as follows:
// * A node may or may not have a parent, so this is more of a forest than a
// tree.
// * When nodes are removed, their set of children are remembered. If the node
// is added again, it retains its old children.
// * A node can be added multiple times, but subsequent adds will not change
// the node's parent.
// * There is only one 'stream root' acknowledged, even though there can be many
// roots. The stream root is the last root node added of type STREAM. The
// stream root identifies the tree whose nodes are used to compute
// |GetVisibleContent()|.
// * A tree can be constructed with a base tree. This copies features from base,
// but refers to content stored in base by reference.
class FeatureTree {
public:
// Constructor. |id_map| is retained by FeatureTree, and must have a greater
// scope than FeatureTree.
explicit FeatureTree(ContentMap* id_map);
// Create a |FeatureTree| which starts as a copy of |base|.
// Copies structure from |base|, and keeps a reference for content access.
explicit FeatureTree(const FeatureTree* base);
~FeatureTree();
FeatureTree(const FeatureTree& src) = delete;
FeatureTree& operator=(const FeatureTree& src) = delete;
// Mutations.
void ApplyStreamStructure(const feedstore::StreamStructure& structure);
// Adds |content| to the tree.
void AddContent(feedstore::Content content);
// Same as |AddContent()|, but can avoid a copy of |content| if it already
// exists.
void CopyAndAddContent(const feedstore::Content& content);
// Data access.
const StreamNode* FindNode(ContentTag id) const;
StreamNode* FindNode(ContentTag id);
const feedstore::Content* FindContent(ContentRevision id) const;
ContentTag GetContentTag(const feedwire::ContentId& id) {
return content_map_->GetContentTag(id);
}
// Returns the list of content that should be visible.
std::vector<ContentRevision> GetVisibleContent();
std::string DumpStateForTesting();
private:
StreamNode* GetOrMakeNode(ContentTag id);
void ResolveRoot();
void ResizeNodesIfNeeded(ContentTag id);
void RemoveFromParent(ContentTag node_id);
bool RemoveFromParent(StreamNode* parent, ContentTag node_id);
const FeatureTree* base_ = nullptr; // Unowned.
ContentMap* content_map_; // Unowned.
// Finding the root:
// We pick the root node as the last STREAM node which has no parent.
// In most cases, we can identify the root as the tree is built.
// But in some cases, we need to search all nodes to find the root.
// |computed_root_| is true if |root_tag_| is guaranteed to identify the root.
bool computed_root_ = true;
ContentTag root_tag_;
// All nodes in the forest, included removed nodes.
// This datastructure was selected to make copies efficient.
std::vector<StreamNode> nodes_;
};
} // namespace stream_model
} // namespace feed
#endif // COMPONENTS_FEED_CORE_V2_STREAM_MODEL_FEATURE_TREE_H_