Vectorize website settings icons in omnibox
[chromium-blink-merge.git] / components / enhanced_bookmarks / item_position.cc
blobc1e8003f12454243eb1a4468767f5f15090a1f35
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/enhanced_bookmarks/item_position.h"
7 #include "base/logging.h"
9 namespace {
10 const unsigned kPositionBase = 64;
11 const char kPositionAlphabet[] =
12 ".0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
13 } // namespace
15 namespace enhanced_bookmarks {
17 ItemPosition::ItemPosition() {
20 ItemPosition::ItemPosition(const PositionVector& position)
21 : position_(position) {
24 ItemPosition::~ItemPosition() {
27 // static
28 ItemPosition ItemPosition::CreateInitialPosition() {
29 PositionVector position(1, kPositionBase / 2);
30 return ItemPosition(position);
33 // static
34 ItemPosition ItemPosition::CreateBefore(const ItemPosition& other) {
35 DCHECK(other.IsValid());
36 return ItemPosition(CreateBeforeImpl(other.position_, 0));
39 // static
40 ItemPosition::PositionVector ItemPosition::CreateBeforeImpl(
41 const PositionVector& other,
42 size_t from_index) {
43 DCHECK_LT(from_index, other.size());
44 PositionVector before(other.begin() + from_index, other.end());
46 // Decrement the last character instead of going half-way to 0 in order to
47 // make sure chaining CreateBefore calls result in logarithmic rather than
48 // linear length growth.
49 before[before.size() - 1] /= 2;
50 if (before[before.size() - 1] != 0) {
51 // If the last digit didn't change to 0, we're done!
52 return before;
55 // Reset trailing zeros, then decrement the last non-zero digit.
56 int index = before.size() - 1;
57 while (index >= 0 && before[index] == 0) {
58 before[index--] = kPositionBase / 2;
61 // Negative index means all digits were zeros. Put that many zeros to the
62 // front of the string to get one that is comes before the input given.
63 // This will cause the returned string to be twice as long as the input one,
64 // (and about twice as long as needed for a valid return value), however that
65 // means this function can be called many times more before we need to
66 // increase the string size again. Increasing it with the minimum length
67 // would result in a linear string size growth.
68 if (index < 0) {
69 before.insert(before.begin(), before.size(), 0);
70 } else {
71 before[index] /= 2;
73 return before;
76 // static
77 ItemPosition ItemPosition::CreateAfter(const ItemPosition& other) {
78 DCHECK(other.IsValid());
79 return ItemPosition(CreateAfterImpl(other.position_, 0));
82 // static
83 ItemPosition::PositionVector ItemPosition::CreateAfterImpl(
84 const PositionVector& other,
85 size_t from_index) {
86 DCHECK_LE(from_index, other.size());
87 if (from_index == other.size()) {
88 return PositionVector(1, kPositionBase / 2);
91 PositionVector after(other.begin() + from_index, other.end());
93 // Instead of splitting the position with infinity, increment the last digit
94 // possible, and reset all digits after. This makes sure chaining createAfter
95 // will result in a logarithmic rather than linear length growth.
96 size_t index = after.size() - 1;
97 do {
98 after[index] += (kPositionBase - after[index] + 1) / 2;
99 if (after[index] != kPositionBase)
100 return after;
101 after[index] = kPositionBase / 2;
102 } while (index-- > 0);
104 // All digits must have been at the maximal value already, so the string
105 // length has to increase. Double it's size to ensure CreateAfter can be
106 // called exponentially more times every time this needs to happen.
107 after.insert(after.begin(), after.size(), kPositionBase - 1);
109 return after;
112 // static
113 ItemPosition ItemPosition::CreateBetween(const ItemPosition& before,
114 const ItemPosition& after) {
115 DCHECK(before.IsValid() && after.IsValid());
116 return ItemPosition(CreateBetweenImpl(before.position_, after.position_));
119 // static
120 ItemPosition::PositionVector ItemPosition::CreateBetweenImpl(
121 const PositionVector& before,
122 const PositionVector& after) {
123 DCHECK(before < after);
125 PositionVector between;
126 for (size_t i = 0; i < before.size(); i++) {
127 if (before[i] == after[i]) {
128 // Add the common prefix to the return value.
129 between.push_back(before[i]);
130 continue;
132 if (before[i] < after[i] - 1) {
133 // Split the difference between the two characters.
134 between.push_back((before[i] + after[i]) / 2);
135 return between;
137 // The difference between before[i] and after[i] is one character. A valid
138 // return is that character, plus something that comes after the remaining
139 // characters of before.
140 between.push_back(before[i]);
141 PositionVector suffix = CreateAfterImpl(before, i + 1);
142 between.insert(between.end(), suffix.begin(), suffix.end());
143 return between;
146 // |before| must be a prefix of |after|, so return that prefix followed by
147 // something that comes before the remaining digits of |after|.
148 PositionVector suffix = CreateBeforeImpl(after, before.size());
149 between.insert(between.end(), suffix.begin(), suffix.end());
150 return between;
153 std::string ItemPosition::ToString() const {
154 DCHECK_GT(arraysize(kPositionAlphabet), kPositionBase);
156 std::string str;
157 str.reserve(position_.size());
158 for (size_t i = 0; i < position_.size(); i++) {
159 unsigned char val = position_[i];
160 CHECK_LT(val, kPositionBase);
161 str.push_back(kPositionAlphabet[position_[i]]);
163 return str;
166 bool ItemPosition::IsValid() const {
167 return !position_.empty() && position_[position_.size() - 1] != 0;
170 bool ItemPosition::Equals(const ItemPosition& other) const {
171 return position_ == other.position_;
174 bool ItemPosition::LessThan(const ItemPosition& other) const {
175 return position_ < other.position_;
178 } // namespace enhanced_bookmarks