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