/root/doris/cloud/src/common/util.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | // clang-format off |
19 | | #include "util.h" |
20 | | |
21 | | #include <bthread/butex.h> |
22 | | #include <butil/iobuf.h> |
23 | | #include <google/protobuf/util/json_util.h> |
24 | | |
25 | | // FIXME: we should not rely other modules that may rely on this common module |
26 | | #include "common/logging.h" |
27 | | #include "common/config.h" |
28 | | #include "meta-store/keys.h" |
29 | | #include "meta-store/codec.h" |
30 | | #include "meta-store/txn_kv.h" |
31 | | #include "meta-store/txn_kv_error.h" |
32 | | |
33 | | #include <iomanip> |
34 | | #include <sstream> |
35 | | #include <unordered_map> |
36 | | #include <variant> |
37 | | // clang-format on |
38 | | |
39 | | namespace doris::cloud { |
40 | | |
41 | | /** |
42 | | * This is a naïve implementation of hex, DONOT use it on retical path. |
43 | | */ |
44 | 172k | std::string hex(std::string_view str) { |
45 | 172k | std::stringstream ss; |
46 | 13.2M | for (auto& i : str) { |
47 | 13.2M | ss << std::hex << std::setw(2) << std::setfill('0') << ((int16_t)i & 0xff); |
48 | 13.2M | } |
49 | 172k | return ss.str(); |
50 | 172k | } |
51 | | |
52 | | /** |
53 | | * This is a naïve implementation of unhex. |
54 | | */ |
55 | 589 | std::string unhex(std::string_view hex_str) { |
56 | | // clang-format off |
57 | 589 | const static std::unordered_map<char, char> table = { |
58 | 589 | {'0', 0}, {'1', 1}, {'2', 2}, {'3', 3}, {'4', 4}, |
59 | 589 | {'5', 5}, {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, |
60 | 589 | {'a', 10}, {'b', 11}, {'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}, |
61 | 589 | {'A', 10}, {'B', 11}, {'C', 12}, {'D', 13}, {'E', 14}, {'F', 15}}; |
62 | 589 | [[maybe_unused]] static int8_t lut[std::max({'9', 'f', 'F'}) + 1]; |
63 | 589 | lut[(int)'0'] = 0; lut[(int)'1'] = 1; lut[(int)'2'] = 2; lut[(int)'3'] = 3; lut[(int)'4'] = 4; lut[(int)'5'] = 5; lut[(int)'6'] = 6; lut[(int)'7'] = 7; lut[(int)'8'] = 8; lut[(int)'9'] = 9; |
64 | 589 | lut[(int)'a'] = 10; lut[(int)'b'] = 11; lut[(int)'c'] = 12; lut[(int)'d'] = 13; lut[(int)'e'] = 14; lut[(int)'f'] = 15; |
65 | 589 | lut[(int)'A'] = 10; lut[(int)'B'] = 11; lut[(int)'C'] = 12; lut[(int)'D'] = 13; lut[(int)'E'] = 14; lut[(int)'F'] = 15; |
66 | | // clang-format on |
67 | 589 | size_t len = hex_str.length(); |
68 | 589 | len &= ~0x01UL; |
69 | 589 | std::string buf(len >> 1, '\0'); |
70 | 13.4k | for (size_t i = 0; i < len; ++i) { |
71 | 12.8k | const auto it = table.find(hex_str[i]); |
72 | 12.8k | if (it == table.end()) break; |
73 | 12.8k | buf[i >> 1] |= i & 0x1 ? (it->second & 0x0f) : (it->second & 0x0f) << 4; |
74 | 12.8k | } |
75 | 589 | return buf; |
76 | 589 | } |
77 | | |
78 | | static std::string explain_fields(std::string_view text, const std::vector<std::string>& fields, |
79 | 38 | const std::vector<int>& pos, bool unicode = false) { |
80 | 38 | if (fields.size() != pos.size() || fields.size() == 0 || pos.size() == 0) { |
81 | 0 | return std::string(text.data(), text.size()); |
82 | 0 | } |
83 | 38 | size_t last_hyphen_pos = pos.back() + 1; |
84 | 38 | std::stringstream ss; |
85 | 38 | std::string blank_line(last_hyphen_pos + 1, ' '); |
86 | | |
87 | | // clang-format off |
88 | 38 | static const std::string hyphen("\xe2\x94\x80"); // ─ e2 94 80 |
89 | 38 | static const std::string bar ("\xe2\x94\x82"); // │ e2 94 82 |
90 | 38 | static const std::string angle ("\xe2\x94\x8c"); // ┌ e2 94 8c |
91 | 38 | static const std::string arrow ("\xe2\x96\xbc"); // ▼ e2 96 bc |
92 | | // clang-format on |
93 | | |
94 | | // Each line with hyphens |
95 | 260 | for (size_t i = 0; i < fields.size(); ++i) { |
96 | 222 | std::string line = blank_line; |
97 | 222 | line[pos[i]] = '/'; |
98 | 222 | int nbar = i; |
99 | 800 | for (size_t j = 0; j < i; ++j) { |
100 | 578 | line[pos[j]] = '|'; |
101 | 578 | } |
102 | 222 | int nhyphen = 0; |
103 | 12.3k | for (size_t j = pos[i] + 1; j <= last_hyphen_pos; ++j) { |
104 | 12.1k | line[j] = '-'; |
105 | 12.1k | ++nhyphen; |
106 | 12.1k | } |
107 | | |
108 | 222 | if (unicode) { |
109 | 204 | int i = line.size(); |
110 | 204 | line.resize(line.size() + 2 * (1 /*angle*/ + nbar + nhyphen), ' '); |
111 | 204 | int j = line.size(); |
112 | 20.0k | while (--i >= 0) { |
113 | 19.8k | if (line[i] == '-') { |
114 | 11.1k | line[--j] = hyphen[2]; |
115 | 11.1k | line[--j] = hyphen[1]; |
116 | 11.1k | line[--j] = hyphen[0]; |
117 | 11.1k | } else if (line[i] == '|') { |
118 | 533 | line[--j] = bar[2]; |
119 | 533 | line[--j] = bar[1]; |
120 | 533 | line[--j] = bar[0]; |
121 | 8.09k | } else if (line[i] == '/') { |
122 | 204 | line[--j] = angle[2]; |
123 | 204 | line[--j] = angle[1]; |
124 | 204 | line[--j] = angle[0]; |
125 | 7.89k | } else { |
126 | 7.89k | --j; |
127 | 7.89k | continue; |
128 | 7.89k | } |
129 | 11.9k | line[i] = i != j ? ' ' : line[i]; // Replace if needed |
130 | 11.9k | } |
131 | 204 | } |
132 | | |
133 | 222 | ss << line << " " << i << ". " << fields[i] << "\n"; |
134 | 222 | } |
135 | | |
136 | | // Mark position indicator |
137 | 38 | std::string line = blank_line; |
138 | 260 | for (size_t i = 0; i < fields.size(); ++i) { |
139 | 222 | line[pos[i]] = '|'; |
140 | 222 | } |
141 | | |
142 | 38 | if (unicode) { |
143 | 35 | int i = line.size(); |
144 | 35 | line.resize(line.size() + 2 * fields.size(), ' '); |
145 | 35 | int j = line.size(); |
146 | 3.17k | while (--i >= 0) { |
147 | 3.13k | if (line[i] != '|') { |
148 | 2.93k | --j; |
149 | 2.93k | continue; |
150 | 2.93k | } |
151 | 204 | line[--j] = bar[2]; |
152 | 204 | line[--j] = bar[1]; |
153 | 204 | line[--j] = bar[0]; |
154 | 204 | line[i] = i != j ? ' ' : line[i]; // Replace if needed |
155 | 204 | } |
156 | 35 | } |
157 | | |
158 | 38 | ss << line << "\n"; |
159 | | |
160 | 38 | line = blank_line; |
161 | 260 | for (size_t i = 0; i < fields.size(); ++i) { |
162 | 222 | line[pos[i]] = 'v'; |
163 | 222 | } |
164 | | |
165 | 38 | if (unicode) { |
166 | 35 | int i = line.size(); |
167 | 35 | line.resize(line.size() + 2 * fields.size(), ' '); |
168 | 35 | int j = line.size(); |
169 | 3.17k | while (--i >= 0) { |
170 | 3.13k | if (line[i] != 'v') { |
171 | 2.93k | --j; |
172 | 2.93k | continue; |
173 | 2.93k | } |
174 | 204 | line[--j] = arrow[2]; |
175 | 204 | line[--j] = arrow[1]; |
176 | 204 | line[--j] = arrow[0]; |
177 | 204 | line[i] = i != j ? ' ' : line[i]; // Replace if needed |
178 | 204 | } |
179 | 35 | } |
180 | | |
181 | 38 | ss << line << "\n"; |
182 | | |
183 | | // Original text to explain |
184 | 38 | ss << text << "\n"; |
185 | | |
186 | 38 | return ss.str(); |
187 | 38 | } |
188 | | |
189 | 38 | std::string prettify_key(std::string_view key_hex, bool unicode) { |
190 | | // Decoded result container |
191 | | // val tag pos |
192 | | // .---------------^----------------. .^. .^. |
193 | 38 | std::vector<std::tuple<std::variant<int64_t, std::string>, int, int>> fields; |
194 | 38 | std::string unhex_key = unhex(key_hex); |
195 | 38 | int key_space = unhex_key[0]; |
196 | 38 | std::string_view key_copy = unhex_key; |
197 | 38 | key_copy.remove_prefix(1); // Remove the first key space byte |
198 | 38 | int ret = decode_key(&key_copy, &fields); |
199 | 38 | if (ret != 0) return ""; |
200 | | |
201 | 38 | std::vector<std::string> fields_str; |
202 | 38 | std::vector<int> fields_pos; |
203 | 38 | fields_str.reserve(fields.size() + 1); |
204 | 38 | fields_pos.reserve(fields.size() + 1); |
205 | | // Key space byte |
206 | 38 | fields_str.push_back("key space: " + std::to_string(key_space)); |
207 | 38 | fields_pos.push_back(0); |
208 | | |
209 | 184 | for (auto& i : fields) { |
210 | 184 | fields_str.emplace_back(std::get<1>(i) == EncodingTag::BYTES_TAG |
211 | 184 | ? std::get<std::string>(std::get<0>(i)) |
212 | 184 | : std::to_string(std::get<int64_t>(std::get<0>(i)))); |
213 | 184 | fields_pos.push_back((std::get<2>(i) + 1) * 2); |
214 | 184 | } |
215 | | |
216 | 38 | return explain_fields(key_hex, fields_str, fields_pos, unicode); |
217 | 38 | } |
218 | | |
219 | 1.45k | std::string proto_to_json(const ::google::protobuf::Message& msg, bool add_whitespace) { |
220 | 1.45k | std::string json; |
221 | 1.45k | google::protobuf::util::JsonPrintOptions opts; |
222 | 1.45k | opts.add_whitespace = add_whitespace; |
223 | 1.45k | opts.preserve_proto_field_names = true; |
224 | 1.45k | google::protobuf::util::MessageToJsonString(msg, &json, opts); |
225 | 1.45k | return json; |
226 | 1.45k | } |
227 | | |
228 | 17.4k | TxnErrorCode key_exists(Transaction* txn, std::string_view key, bool snapshot) { |
229 | 17.4k | std::string end_key {key}; |
230 | 17.4k | encode_int64(INT64_MAX, &end_key); |
231 | 17.4k | std::unique_ptr<RangeGetIterator> it; |
232 | 17.4k | TxnErrorCode err = txn->get(key, end_key, &it, snapshot, 1); |
233 | 17.4k | if (err != TxnErrorCode::TXN_OK) { |
234 | 120 | return err; |
235 | 120 | } |
236 | 17.3k | return it->has_next() ? TxnErrorCode::TXN_OK : TxnErrorCode::TXN_KEY_NOT_FOUND; |
237 | 17.4k | } |
238 | | |
239 | | } // namespace doris::cloud |