| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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 |
| // |
| // http://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. |
| |
| #include "codec.h" |
| |
| #include <cstdint> |
| |
| namespace doris::cloud { |
| |
| void encode_bytes(std::string_view bytes, std::string* b) { |
| // possible_num_0x00 prefix_suffix |
| b->reserve(b->size() + (bytes.size() / 256 * 2) + 3); |
| b->push_back(static_cast<char>(EncodingTag::BYTES_TAG)); |
| std::size_t escape_pos = 0; |
| std::size_t start_pos = 0; |
| while (true) { |
| escape_pos = bytes.find(EncodingTag::BYTE_ESCAPE, escape_pos); |
| if (escape_pos == std::string::npos) { |
| break; |
| } |
| b->insert(b->end(), bytes.begin() + start_pos, bytes.begin() + escape_pos); |
| b->push_back(EncodingTag::BYTE_ESCAPE); |
| b->push_back(EncodingTag::ESCAPED_00); |
| ++escape_pos; |
| start_pos = escape_pos; |
| } |
| b->insert(b->end(), bytes.begin() + start_pos, bytes.end()); |
| b->push_back(EncodingTag::BYTE_ESCAPE); |
| b->push_back(EncodingTag::BYTES_ENDING); |
| } |
| |
| /** |
| * Decodes byte sequence which is generated with `encode_bytes` |
| * |
| * @param in intput for decoding |
| * @param out output |
| * @return 0 for success |
| */ |
| int decode_bytes(std::string_view* in, std::string* out) { |
| if (in->at(0) != EncodingTag::BYTES_TAG) return -1; |
| using byte = unsigned char; |
| in->remove_prefix(1); // Remove bytes marker |
| while (true) { |
| size_t pos = in->find(EncodingTag::BYTE_ESCAPE); |
| if (pos == std::string::npos) { // At least one should be found |
| // No EncodingTag::BYTE_ESCAPE found, array without ending |
| return -2; |
| } |
| if ((pos + 1) >= in->size()) { |
| // Malformed bytes encoding |
| return -3; |
| } |
| byte c = static_cast<byte>((*in)[pos + 1]); |
| if (c == EncodingTag::BYTES_ENDING) { |
| if (out != nullptr) { |
| out->append(in->data(), pos); |
| } |
| in->remove_prefix(pos + 2); |
| break; |
| } else if (c == EncodingTag::ESCAPED_00) { |
| if (out != nullptr) { |
| out->append(in->data(), pos + 1); |
| } |
| in->remove_prefix(pos + 2); |
| } else { |
| // undefined escaping marker |
| return -4; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * Encodes int64 to 8-byte big endian |
| * FIXME: use entire 8-bytes |
| */ |
| void encode_int64(int64_t val, std::string* b) { |
| // static_assert(std::endian::little); // Since c++20 |
| std::string dat(9, '\x00'); |
| dat[0] = val < 0 ? EncodingTag::NEGATIVE_FIXED_INT_TAG : EncodingTag::POSITIVE_FIXED_INT_TAG; |
| int64_t& v = *reinterpret_cast<int64_t*>(dat.data() + 1); |
| v = val < 0 ? -val : val; |
| // clang-format off |
| // assert: highest bit (sign) is never 1 |
| v = ((v & 0xffffffff00000000) >> 32) | ((v & 0x00000000ffffffff) << 32); |
| v = ((v & 0xffff0000ffff0000) >> 16) | ((v & 0x0000ffff0000ffff) << 16); |
| v = ((v & 0xff00ff00ff00ff00) >> 8) | ((v & 0x00ff00ff00ff00ff) << 8); |
| // clang-format on |
| b->reserve(b->size() + dat.size()); |
| b->insert(b->end(), dat.begin(), dat.end()); |
| } |
| |
| int decode_int64(std::string_view* in, int64_t* val) { |
| // static_assert(std::endian::little); // Since c++20 |
| if (in->size() < 9) return -1; // Insufficient length to decode |
| if (in->at(0) != EncodingTag::NEGATIVE_FIXED_INT_TAG && |
| in->at(0) != EncodingTag::POSITIVE_FIXED_INT_TAG) { |
| // Invalid tag |
| return -2; |
| } |
| bool is_negative = in->at(0) == EncodingTag::NEGATIVE_FIXED_INT_TAG; |
| uint64_t v = *reinterpret_cast<const uint64_t*>(in->data() + 1); |
| // clang-format off |
| // assert: highest bit (sign) is never 1 |
| v = ((v & 0xffffffff00000000) >> 32) | ((v & 0x00000000ffffffff) << 32); |
| v = ((v & 0xffff0000ffff0000) >> 16) | ((v & 0x0000ffff0000ffff) << 16); |
| v = ((v & 0xff00ff00ff00ff00) >> 8) | ((v & 0x00ff00ff00ff00ff) << 8); |
| // clang-format on |
| |
| // We haven't used entire 64 bits of unsigned int64 yet, hence we treat |
| // EncodingTag::NEGATIVE_FIXED_INT_TAG and EncodingTag::POSITIVE_FIXED_INT_TAG |
| // the same here |
| *val = static_cast<int64_t>(v); |
| *val = is_negative ? -*val : *val; |
| |
| in->remove_prefix(9); |
| |
| return 0; |
| } |
| |
| uint32_t encode_versionstamp(const Versionstamp& vs, std::string* b) { |
| uint32_t index = b->size(); |
| b->reserve(b->size() + 11); // 1 byte for tag + 10 bytes for versionstamp |
| b->push_back(static_cast<char>(EncodingTag::VERSIONSTAMP_TAG)); |
| b->insert(b->end(), vs.data().begin(), vs.data().end()); |
| return index + 1; // return the index of the versionstamp in the buffer. |
| } |
| |
| int decode_versionstamp(std::string_view* in, Versionstamp* vs) { |
| if (in->size() < 11) return -1; // Insufficient length to decode versionstamp |
| if (in->at(0) != EncodingTag::VERSIONSTAMP_TAG) return -2; // Invalid tag |
| *vs = reinterpret_cast<const uint8_t*>(in->data() + 1); |
| in->remove_prefix(11); |
| return 0; |
| } |
| |
| int decode_tailing_versionstamp(std::string_view* in, Versionstamp* vs) { |
| if (in->size() < 11) return -1; // Insufficient length to decode versionstamp |
| size_t pos = in->size() - 11; |
| if (in->at(pos) != EncodingTag::VERSIONSTAMP_TAG) return -2; // Invalid tag |
| *vs = reinterpret_cast<const uint8_t*>(in->data() + pos + 1); |
| in->remove_suffix(11); |
| return 0; |
| } |
| |
| void encode_versionstamp_end(std::string* b) { |
| b->push_back(static_cast<char>(EncodingTag::VERSIONSTAMP_END_TAG)); |
| } |
| |
| int decode_versionstamp_end(std::string_view* in) { |
| if (in->empty() || static_cast<unsigned char>(in->at(0)) != EncodingTag::VERSIONSTAMP_END_TAG) { |
| return -1; // Invalid end tag |
| } |
| in->remove_prefix(1); // Remove the end tag |
| return 0; |
| } |
| |
| int decode_tailing_versionstamp_end(std::string_view* in) { |
| if (in->empty() || |
| static_cast<unsigned char>(in->back()) != EncodingTag::VERSIONSTAMP_END_TAG) { |
| return -1; // Invalid end tag |
| } |
| in->remove_suffix(1); // Remove the end tag |
| return 0; |
| } |
| |
| } // namespace doris::cloud |