1/*
2 * Copyright 2016-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16// Copyright 2004-present Facebook. All Rights Reserved.
17#pragma once
18
19namespace folly {
20namespace io {
21namespace detail {
22
23/*
24 * Helper classes for use with CursorBase::readWhile()
25 */
26class CursorStringAppender {
27 public:
28 void append(ByteRange bytes) {
29 str_.append(reinterpret_cast<char const*>(bytes.data()), bytes.size());
30 }
31 std::string extractString() {
32 return std::move(str_);
33 }
34
35 private:
36 std::string str_;
37};
38
39class CursorNoopAppender {
40 public:
41 void append(ByteRange) {}
42};
43
44template <class Derived, class BufType>
45std::string CursorBase<Derived, BufType>::readTerminatedString(
46 char termChar,
47 size_t maxLength) {
48 size_t bytesRead{0};
49 auto keepReading = [&bytesRead, termChar, maxLength](uint8_t byte) {
50 if (byte == termChar) {
51 return false;
52 }
53 ++bytesRead;
54 if (bytesRead >= maxLength) {
55 throw std::length_error("string overflow");
56 }
57 return true;
58 };
59
60 auto result = readWhile(keepReading);
61 // skip over the terminator character
62 if (isAtEnd()) {
63 throw_exception<std::out_of_range>("terminator not found");
64 }
65 skip(1);
66
67 return result;
68}
69
70template <class Derived, class BufType>
71template <typename Predicate>
72std::string CursorBase<Derived, BufType>::readWhile(
73 const Predicate& predicate) {
74 CursorStringAppender s;
75 readWhile(predicate, s);
76 return s.extractString();
77}
78
79template <class Derived, class BufType>
80template <typename Predicate, typename Output>
81void CursorBase<Derived, BufType>::readWhile(
82 const Predicate& predicate,
83 Output& out) {
84 while (true) {
85 auto peeked = peekBytes();
86 if (peeked.empty()) {
87 return;
88 }
89 for (size_t idx = 0; idx < peeked.size(); ++idx) {
90 if (!predicate(peeked[idx])) {
91 peeked.reset(peeked.data(), idx);
92 out.append(peeked);
93 skip(idx);
94 return;
95 }
96 }
97 out.append(peeked);
98 skip(peeked.size());
99 }
100}
101
102template <class Derived, class BufType>
103template <typename Predicate>
104void CursorBase<Derived, BufType>::skipWhile(const Predicate& predicate) {
105 CursorNoopAppender appender;
106 readWhile(predicate, appender);
107}
108} // namespace detail
109} // namespace io
110} // namespace folly
111