Coverage Report

Created: 2025-05-12 17:08

/root/doris/cloud/src/meta-service/codec.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
#include "codec.h"
19
20
#include <cstdint>
21
22
namespace doris::cloud {
23
24
1.21M
void encode_bytes(std::string_view bytes, std::string* b) {
25
    //                     possible_num_0x00        prefix_suffix
26
1.21M
    b->reserve(b->size() + (bytes.size() / 256 * 2) + 3);
27
1.21M
    b->push_back(static_cast<char>(EncodingTag::BYTES_TAG));
28
1.21M
    std::size_t escape_pos = 0;
29
1.21M
    std::size_t start_pos = 0;
30
2.28M
    while (true) {
31
2.28M
        escape_pos = bytes.find(EncodingTag::BYTE_ESCAPE, escape_pos);
32
2.28M
        if (escape_pos == std::string::npos) {
33
1.21M
            break;
34
1.21M
        }
35
1.07M
        b->insert(b->end(), bytes.begin() + start_pos, bytes.begin() + escape_pos);
36
1.07M
        b->push_back(EncodingTag::BYTE_ESCAPE);
37
1.07M
        b->push_back(EncodingTag::ESCAPED_00);
38
1.07M
        ++escape_pos;
39
1.07M
        start_pos = escape_pos;
40
1.07M
    }
41
1.21M
    b->insert(b->end(), bytes.begin() + start_pos, bytes.end());
42
1.21M
    b->push_back(EncodingTag::BYTE_ESCAPE);
43
1.21M
    b->push_back(EncodingTag::BYTES_ENDING);
44
1.21M
}
45
46
/**
47
 * Decodes byte sequence which is generated with `encode_bytes`
48
 *
49
 * @param in intput for decoding
50
 * @param out output
51
 * @return 0 for success
52
 */
53
80.4k
int decode_bytes(std::string_view* in, std::string* out) {
54
80.4k
    if (in->at(0) != EncodingTag::BYTES_TAG) return -1;
55
80.4k
    using byte = unsigned char;
56
80.4k
    in->remove_prefix(1); // Remove bytes marker
57
1.15M
    while (true) {
58
1.15M
        size_t pos = in->find(EncodingTag::BYTE_ESCAPE);
59
1.15M
        if (pos == std::string::npos) { // At least one should be found
60
            // No EncodingTag::BYTE_ESCAPE found, array without ending
61
0
            return -2;
62
0
        }
63
1.15M
        if ((pos + 1) >= in->size()) {
64
            // Malformed bytes encoding
65
0
            return -3;
66
0
        }
67
1.15M
        byte c = static_cast<byte>((*in)[pos + 1]);
68
1.15M
        if (c == EncodingTag::BYTES_ENDING) {
69
80.4k
            if (out != nullptr) {
70
80.4k
                out->append(in->data(), pos);
71
80.4k
            }
72
80.4k
            in->remove_prefix(pos + 2);
73
80.4k
            break;
74
1.07M
        } else if (c == EncodingTag::ESCAPED_00) {
75
1.07M
            if (out != nullptr) {
76
1.07M
                out->append(in->data(), pos + 1);
77
1.07M
            }
78
1.07M
            in->remove_prefix(pos + 2);
79
1.07M
        } else {
80
            // undefined escaping marker
81
0
            return -4;
82
0
        }
83
1.15M
    }
84
80.4k
    return 0;
85
80.4k
}
86
87
/**
88
 * Encodes int64 to 8-byte big endian
89
 * FIXME: use entire 8-bytes
90
 */
91
825k
void encode_int64(int64_t val, std::string* b) {
92
    // static_assert(std::endian::little); // Since c++20
93
825k
    std::string dat(9, '\x00');
94
825k
    dat[0] = val < 0 ? EncodingTag::NEGATIVE_FIXED_INT_TAG : EncodingTag::POSITIVE_FIXED_INT_TAG;
95
825k
    int64_t& v = *reinterpret_cast<int64_t*>(dat.data() + 1);
96
825k
    v = val < 0 ? -val : val;
97
    // clang-format off
98
    // assert: highest bit (sign) is never 1
99
825k
    v = ((v & 0xffffffff00000000) >> 32) | ((v & 0x00000000ffffffff) << 32);
100
825k
    v = ((v & 0xffff0000ffff0000) >> 16) | ((v & 0x0000ffff0000ffff) << 16);
101
825k
    v = ((v & 0xff00ff00ff00ff00) >> 8)  | ((v & 0x00ff00ff00ff00ff) << 8);
102
    // clang-format on
103
825k
    b->reserve(b->size() + dat.size());
104
825k
    b->insert(b->end(), dat.begin(), dat.end());
105
825k
}
106
107
65.1k
int decode_int64(std::string_view* in, int64_t* val) {
108
    // static_assert(std::endian::little); // Since c++20
109
65.1k
    if (in->size() < 9) return -1; // Insufficient length to decode
110
65.1k
    if (in->at(0) != EncodingTag::NEGATIVE_FIXED_INT_TAG &&
111
65.1k
        in->at(0) != EncodingTag::POSITIVE_FIXED_INT_TAG) {
112
        // Invalid tag
113
0
        return -2;
114
0
    }
115
65.1k
    bool is_negative = in->at(0) == EncodingTag::NEGATIVE_FIXED_INT_TAG;
116
65.1k
    uint64_t v = *reinterpret_cast<const uint64_t*>(in->data() + 1);
117
    // clang-format off
118
    // assert: highest bit (sign) is never 1
119
65.1k
    v = ((v & 0xffffffff00000000) >> 32) | ((v & 0x00000000ffffffff) << 32);
120
65.1k
    v = ((v & 0xffff0000ffff0000) >> 16) | ((v & 0x0000ffff0000ffff) << 16);
121
65.1k
    v = ((v & 0xff00ff00ff00ff00) >> 8)  | ((v & 0x00ff00ff00ff00ff) << 8);
122
    // clang-format on
123
124
    // We haven't used entire 64 bits of unsigned int64 yet, hence we treat
125
    // EncodingTag::NEGATIVE_FIXED_INT_TAG and EncodingTag::POSITIVE_FIXED_INT_TAG
126
    // the same here
127
65.1k
    *val = static_cast<int64_t>(v);
128
65.1k
    *val = is_negative ? -*val : *val;
129
130
65.1k
    in->remove_prefix(9);
131
132
65.1k
    return 0;
133
65.1k
}
134
135
} // namespace doris::cloud