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"
10 const unsigned kPositionBase
= 64;
11 const char kPositionAlphabet
[] =
12 ".0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
15 namespace enhanced_bookmarks
{
17 ItemPosition::ItemPosition() {
20 ItemPosition::ItemPosition(const PositionVector
& position
)
21 : position_(position
) {
24 ItemPosition::~ItemPosition() {
28 ItemPosition
ItemPosition::CreateInitialPosition() {
29 PositionVector
position(1, kPositionBase
/ 2);
30 return ItemPosition(position
);
34 ItemPosition
ItemPosition::CreateBefore(const ItemPosition
& other
) {
35 DCHECK(other
.IsValid());
36 return ItemPosition(CreateBeforeImpl(other
.position_
, 0));
40 ItemPosition::PositionVector
ItemPosition::CreateBeforeImpl(
41 const PositionVector
& other
,
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!
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.
69 before
.insert(before
.begin(), before
.size(), 0);
77 ItemPosition
ItemPosition::CreateAfter(const ItemPosition
& other
) {
78 DCHECK(other
.IsValid());
79 return ItemPosition(CreateAfterImpl(other
.position_
, 0));
83 ItemPosition::PositionVector
ItemPosition::CreateAfterImpl(
84 const PositionVector
& other
,
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;
98 after
[index
] += (kPositionBase
- after
[index
] + 1) / 2;
99 if (after
[index
] != kPositionBase
)
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);
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_
));
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
]);
132 if (before
[i
] < after
[i
] - 1) {
133 // Split the difference between the two characters.
134 between
.push_back((before
[i
] + after
[i
]) / 2);
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());
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());
153 std::string
ItemPosition::ToString() const {
154 DCHECK_GT(arraysize(kPositionAlphabet
), kPositionBase
);
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
]]);
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