aboutsummaryrefslogtreecommitdiff
path: root/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h
blob: 971b92eda6d7c143021a41cf8815a4faf5c48127 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// Copyright 2021 The Abseil Authors
//
// 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
//
//     https://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.

#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_
#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_

#include <cassert>
#include <iostream>

#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cord_rep_btree.h"

namespace absl {
ABSL_NAMESPACE_BEGIN
namespace cord_internal {

// CordRepBtreeNavigator is a bi-directional navigator allowing callers to
// navigate all the (leaf) data edges in a CordRepBtree instance.
//
// A CordRepBtreeNavigator instance is by default empty. Callers initialize a
// navigator instance by calling one of `InitFirst()`, `InitLast()` or
// `InitOffset()`, which establishes a current position. Callers can then
// navigate using the `Next`, `Previous`, `Skip` and `Seek` methods.
//
// The navigator instance does not take or adopt a reference on the provided
// `tree` on any of the initialization calls. Callers are responsible for
// guaranteeing the lifecycle of the provided tree. A navigator instance can
// be reset to the empty state by calling `Reset`.
//
// A navigator only keeps positional state on the 'current data edge', it does
// explicitly not keep any 'offset' state. The class does accept and return
// offsets in the `Read()`, `Skip()` and 'Seek()` methods as these would
// otherwise put a big burden on callers. Callers are expected to maintain
// (returned) offset info if they require such granular state.
class CordRepBtreeNavigator {
 public:
  // The logical position as returned by the Seek() and Skip() functions.
  // Returns the current leaf edge for the desired seek or skip position and
  // the offset of that position inside that edge.
  struct Position {
    CordRep* edge;
    size_t offset;
  };

  // The read result as returned by the Read() function.
  // `tree` contains the resulting tree which is identical to the result
  // of calling CordRepBtree::SubTree(...) on the tree being navigated.
  // `n` contains the number of bytes used from the last navigated to
  // edge of the tree.
  struct ReadResult {
    CordRep* tree;
    size_t n;
  };

  // Returns true if this instance is not empty.
  explicit operator bool() const;

  // Returns the tree for this instance or nullptr if empty.
  CordRepBtree* btree() const;

  // Returns the data edge of the current position.
  // Requires this instance to not be empty.
  CordRep* Current() const;

  // Resets this navigator to `tree`, returning the first data edge in the tree.
  CordRep* InitFirst(CordRepBtree* tree);

  // Resets this navigator to `tree`, returning the last data edge in the tree.
  CordRep* InitLast(CordRepBtree* tree);

  // Resets this navigator to `tree` returning the data edge at position
  // `offset` and the relative offset of `offset` into that data edge.
  // Returns `Position.edge = nullptr` if the provided offset is greater
  // than or equal to the length of the tree, in which case the state of
  // the navigator instance remains unchanged.
  Position InitOffset(CordRepBtree* tree, size_t offset);

  // Navigates to the next data edge.
  // Returns the next data edge or nullptr if there is no next data edge, in
  // which case the current position remains unchanged.
  CordRep* Next();

  // Navigates to the previous data edge.
  // Returns the previous data edge or nullptr if there is no previous data
  // edge, in which case the current position remains unchanged.
  CordRep* Previous();

  // Navigates to the data edge at position `offset`. Returns the navigated to
  // data edge in `Position.edge` and the relative offset of `offset` into that
  // data edge in `Position.offset`. Returns `Position.edge = nullptr` if the
  // provide offset is greater than or equal to the tree's length.
  Position Seek(size_t offset);

  // Reads `n` bytes of data starting at offset `edge_offset` of the current
  // data edge, and returns the result in `ReadResult.tree`. `ReadResult.n`
  // contains the 'bytes used` from the last / current data edge in the tree.
  // This allows users that mix regular navigation (using string views) and
  // 'read into cord' navigation to keep track of the current state, and which
  // bytes have been consumed from a navigator.
  // This function returns `ReadResult.tree = nullptr` if the requested length
  // exceeds the length of the tree starting at the current data edge.
  ReadResult Read(size_t edge_offset, size_t n);

  // Skips `n` bytes forward from the current data edge, returning the navigated
  // to data edge in `Position.edge` and `Position.offset` containing the offset
  // inside that data edge. Note that the state of the navigator is left
  // unchanged if `n` is smaller than the length of the current data edge.
  Position Skip(size_t n);

  // Resets this instance to the default / empty state.
  void Reset();

 private:
  // Slow path for Next() if Next() reached the end of a leaf node. Backtracks
  // up the stack until it finds a node that has a 'next' position available,
  // and then does a 'front dive' towards the next leaf node.
  CordRep* NextUp();

  // Slow path for Previous() if Previous() reached the beginning of a leaf
  // node. Backtracks up the stack until it finds a node that has a 'previous'
  // position available, and then does a 'back dive' towards the previous leaf
  // node.
  CordRep* PreviousUp();

  // Generic implementation of InitFirst() and InitLast().
  template <CordRepBtree::EdgeType edge_type>
  CordRep* Init(CordRepBtree* tree);

  // `height_` contains the height of the current tree, or -1 if empty.
  int height_ = -1;

  // `index_` and `node_` contain the navigation state as the 'path' to the
  // current data edge which is at `node_[0]->Edge(index_[0])`. The contents
  // of these are undefined until the instance is initialized (`height_ >= 0`).
  uint8_t index_[CordRepBtree::kMaxHeight];
  CordRepBtree* node_[CordRepBtree::kMaxHeight];
};

// Returns true if this instance is not empty.
inline CordRepBtreeNavigator::operator bool() const { return height_ >= 0; }

inline CordRepBtree* CordRepBtreeNavigator::btree() const {
  return height_ >= 0 ? node_[height_] : nullptr;
}

inline CordRep* CordRepBtreeNavigator::Current() const {
  assert(height_ >= 0);
  return node_[0]->Edge(index_[0]);
}

inline void CordRepBtreeNavigator::Reset() { height_ = -1; }

inline CordRep* CordRepBtreeNavigator::InitFirst(CordRepBtree* tree) {
  return Init<CordRepBtree::kFront>(tree);
}

inline CordRep* CordRepBtreeNavigator::InitLast(CordRepBtree* tree) {
  return Init<CordRepBtree::kBack>(tree);
}

template <CordRepBtree::EdgeType edge_type>
inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) {
  assert(tree != nullptr);
  assert(tree->size() > 0);
  int height = height_ = tree->height();
  size_t index = tree->index(edge_type);
  node_[height] = tree;
  index_[height] = static_cast<uint8_t>(index);
  while (--height >= 0) {
    tree = tree->Edge(index)->btree();
    node_[height] = tree;
    index = tree->index(edge_type);
    index_[height] = static_cast<uint8_t>(index);
  }
  return node_[0]->Edge(index);
}

inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::Seek(
    size_t offset) {
  assert(btree() != nullptr);
  int height = height_;
  CordRepBtree* edge = node_[height];
  if (ABSL_PREDICT_FALSE(offset >= edge->length)) return {nullptr, 0};
  CordRepBtree::Position index = edge->IndexOf(offset);
  index_[height] = static_cast<uint8_t>(index.index);
  while (--height >= 0) {
    edge = edge->Edge(index.index)->btree();
    node_[height] = edge;
    index = edge->IndexOf(index.n);
    index_[height] = static_cast<uint8_t>(index.index);
  }
  return {edge->Edge(index.index), index.n};
}

inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset(
    CordRepBtree* tree, size_t offset) {
  assert(tree != nullptr);
  if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0};
  height_ = tree->height();
  node_[height_] = tree;
  return Seek(offset);
}

inline CordRep* CordRepBtreeNavigator::Next() {
  CordRepBtree* edge = node_[0];
  return index_[0] == edge->back() ? NextUp() : edge->Edge(++index_[0]);
}

inline CordRep* CordRepBtreeNavigator::Previous() {
  CordRepBtree* edge = node_[0];
  return index_[0] == edge->begin() ? PreviousUp() : edge->Edge(--index_[0]);
}

inline CordRep* CordRepBtreeNavigator::NextUp() {
  assert(index_[0] == node_[0]->back());
  CordRepBtree* edge;
  size_t index;
  int height = 0;
  do {
    if (++height > height_) return nullptr;
    edge = node_[height];
    index = index_[height] + 1;
  } while (index == edge->end());
  index_[height] = static_cast<uint8_t>(index);
  do {
    node_[--height] = edge = edge->Edge(index)->btree();
    index_[height] = static_cast<uint8_t>(index = edge->begin());
  } while (height > 0);
  return edge->Edge(index);
}

inline CordRep* CordRepBtreeNavigator::PreviousUp() {
  assert(index_[0] == node_[0]->begin());
  CordRepBtree* edge;
  size_t index;
  int height = 0;
  do {
    if (++height > height_) return nullptr;
    edge = node_[height];
    index = index_[height];
  } while (index == edge->begin());
  index_[height] = static_cast<uint8_t>(--index);
  do {
    node_[--height] = edge = edge->Edge(index)->btree();
    index_[height] = static_cast<uint8_t>(index = edge->back());
  } while (height > 0);
  return edge->Edge(index);
}

}  // namespace cord_internal
ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_