MySQL 9.6.0
Source Code Documentation
variable_length_integers.h
Go to the documentation of this file.
1// Copyright (c) 2023, 2025, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
24#ifndef MYSQL_SERIALIZATION_VARIABLE_LENGTH_INTEGERS_H
25#define MYSQL_SERIALIZATION_VARIABLE_LENGTH_INTEGERS_H
26
27/// @file
28/// Experimental API header
29/// @details This file contains low-level internal functions used to store/load
30/// variable-length integers to/from the memory
31///
32/// Please refer to the readme.md of the mysql_serialization library to find
33/// more information about the format
34
35#include <bit>
36#include <concepts>
37#include <limits>
38#include "my_byteorder.h"
40
41/// @addtogroup GroupLibsMysqlSerialization
42/// @{
43
45
46/// @brief Calculates the number of bytes necessary to store data
47/// @tparam Type Integer type
48/// @param data The number to be stored into the memory
49/// @return The number of bytes necessary to store data.
51 const std::unsigned_integral auto &data) {
52 // @details When bit_width(data) == N, the output buffer uses:
53 // * 1 byte, if N==0;
54 // * 1 + ceil((N-1)/7) bytes, if 1<=N<=63;
55 // * 9 bytes, if N==64.
56 // For the case 1<=N<=63, the function follows a straight line. It
57 // is a little above that line when N==0 and a little below that
58 // line when N==63. Therefore, it can be approximated by a line with
59 // slightly lower slope. The slope 575/4096 gives correct results
60 // for all values between 0 and 64, inclusive, and can be computed
61 // with just 1 multiplication and 1 shift.
62 int bits_in_number = std::bit_width(data);
63 return ((bits_in_number * 575) >> 12) + 1;
64}
65
66/// @copydoc get_size_integer_varlen_unsigned
67/// @details Version for signed integers
68size_t get_size_integer_varlen_signed(const std::signed_integral auto &data) {
69 // sign_mask = (data < 0) ? ~0 : 0
70 auto sign_mask = data >> (sizeof(data) * 8 - 1);
71 return get_size_integer_varlen_unsigned(uint64_t(data ^ sign_mask) << 1);
72}
73
74/// @copydoc get_size_integer_varlen_unsigned
75/// @details Enabled for unsigned integers
76size_t get_size_integer_varlen(const std::unsigned_integral auto &data) {
78}
79
80/// @copydoc get_size_integer_varlen_unsigned
81/// @details Enabled for signed integers
82size_t get_size_integer_varlen(const std::signed_integral auto &data) {
84}
85
86/// @brief Writes variable-length integer to the stream
87/// @param[in] stream Encoded data stream
88/// @param[out] data Integer to write
89/// @return Number of bytes written to the stream
90size_t write_varlen_bytes_unsigned(unsigned char *stream,
91 const std::unsigned_integral auto &data) {
92 uint64_t data_cpy = data;
93 int byte_count = get_size_integer_varlen_unsigned(data);
94 stream[0] = ((1 << (byte_count - 1)) - 1) |
95 static_cast<uint8_t>(data_cpy << byte_count);
96 // memcpy won't accept 0 bytes
97 if (byte_count == 1) {
98 return byte_count;
99 }
100 // If byte_count <= 8, shift right by 8 - byte_count.
101 // If byte_count == 9, shift right by 8 - 9 + 1 = 0.
102 data_cpy >>= (8 - byte_count + ((byte_count + 7) >> 4));
103 // reverse endianess for BIG ENDIAN archs
104 data_cpy = htole64(data_cpy);
105 memcpy(&stream[1], &data_cpy, byte_count - 1);
106 return byte_count;
107}
108
109/// @copydoc write_varlen_bytes_unsigned
110/// @details Version for signed integers
111size_t write_varlen_bytes_signed(unsigned char *stream,
112 const std::signed_integral auto &data) {
113 // convert negatives into positive numbers
114 // sign_mask is 0 if data >= 0 and ~0 if data < 0
115 auto sign_mask = (data >> (sizeof(data) * 8 - 1));
116 uint64_t data_cpy = (data ^ sign_mask);
117 // insert sign bit as least significant bit
118 data_cpy = (data_cpy << 1) | (sign_mask & 1);
119 return write_varlen_bytes_unsigned(stream, data_cpy);
120}
121
122/// @copydoc write_varlen_bytes_unsigned
123/// @details Enabled for unsigned integers
124size_t write_varlen_bytes(unsigned char *stream,
125 const std::unsigned_integral auto &data) {
126 return write_varlen_bytes_unsigned(stream, data);
127}
128
129/// @copydoc write_varlen_bytes_unsigned
130/// @details Enabled for signed integers
131size_t write_varlen_bytes(unsigned char *stream,
132 const std::signed_integral auto &data) {
133 return write_varlen_bytes_signed(stream, data);
134}
135
136/// @brief Reads variable-length integer from the stream
137/// @param[in] stream Encoded data
138/// @param[in] stream_bytes Number of bytes in the stream
139/// @param[out] data Result value
140/// @return Number of bytes read from the stream or 0 on error. Error occurs
141/// if the stream ends before or in the middle of the encoded numbers.
142template <typename Type>
143size_t read_varlen_bytes_unsigned(const unsigned char *stream,
144 std::size_t stream_bytes, Type &data)
145 requires std::unsigned_integral<Type>
146{
147 if (stream_bytes == 0) {
148 return stream_bytes;
149 }
150 uint8_t first_byte = stream[0];
151 std::size_t num_bytes = std::countr_one(first_byte) + 1;
152 if (num_bytes > stream_bytes) {
153 return 0;
154 }
155 Type data_cpy = first_byte >> num_bytes;
156 if (num_bytes == 1) {
157 data = data_cpy;
158 return num_bytes;
159 }
160 uint64_t data_tmp = 0;
161 memcpy(&data_tmp, &stream[1], num_bytes - 1);
162 data_tmp = le64toh(data_tmp);
163 // If num_bytes <= 8, shift left by 8 - num_bytes.
164 // If num_bytes == 9, shift left by 8 - 9 + 1 = 0.
165 data_tmp <<= (8 - num_bytes + ((num_bytes + 7) >> 4));
166 if (data_tmp > std::numeric_limits<Type>::max()) {
167 return 0;
168 }
169 data_cpy |= data_tmp;
170 data = data_cpy;
171 return num_bytes;
172}
173
174/// @copydoc read_varlen_bytes_unsigned
175template <typename Type>
176size_t read_varlen_bytes_signed(const unsigned char *stream,
177 std::size_t stream_bytes, Type &data)
178 requires std::signed_integral<Type>
179{
180 using Type_unsigned = std::make_unsigned_t<Type>;
181 Type_unsigned data_tmp = 0;
182 std::size_t num_bytes =
183 read_varlen_bytes_unsigned(stream, stream_bytes, data_tmp);
184 // 0 if positive, ~0 if negative
185 // static_cast is needed to avoid compilation warning on Windows.
186 Type_unsigned sign_mask = -static_cast<Type>(data_tmp & 1);
187 // the result if it is nonnegative, or -(result + 1) if it is negative.
188 data_tmp = data_tmp >> 1;
189 // the result
190 data_tmp = data_tmp ^ sign_mask;
191 data = Type(data_tmp);
192 return num_bytes;
193}
194
195/// @copydoc read_varlen_bytes_unsigned
196size_t read_varlen_bytes(const unsigned char *stream, std::size_t stream_bytes,
197 std::unsigned_integral auto &data) {
198 return read_varlen_bytes_unsigned(stream, stream_bytes, data);
199}
200
201/// @copydoc read_varlen_bytes_unsigned
202size_t read_varlen_bytes(const unsigned char *stream, std::size_t stream_bytes,
203 std::signed_integral auto &data) {
204 return read_varlen_bytes_signed(stream, stream_bytes, data);
205}
206
207} // namespace mysql::serialization::detail
208
209/// @}
210
211#endif // MYSQL_SERIALIZATION_VARIABLE_LENGTH_INTEGERS_H
Experimental API header Conversions between different number representations.
uint64_t htole64(uint64_t x)
Converting a 64 bit integer from host's byte order to little-endian byte order.
Definition: byte_order_helpers.h:70
uint64_t le64toh(uint64_t x)
Converting a 64 bit integer from little-endian byte order to host byteorder.
Definition: byte_order_helpers.h:50
mrs::interface::RestHandler::HttpResult::Type Type
Definition: handler_content_file.cc:42
Functions for reading and storing in machine-independent format.
MediaType
Definition: media_type.h:33
ValueType max(X &&first)
Definition: gtid.h:103
Definition: variable_length_integers.h:44
size_t get_size_integer_varlen_unsigned(const std::unsigned_integral auto &data)
Calculates the number of bytes necessary to store data.
Definition: variable_length_integers.h:50
size_t write_varlen_bytes_signed(unsigned char *stream, const std::signed_integral auto &data)
Writes variable-length integer to the stream.
Definition: variable_length_integers.h:111
size_t read_varlen_bytes_unsigned(const unsigned char *stream, std::size_t stream_bytes, Type &data)
Reads variable-length integer from the stream.
Definition: variable_length_integers.h:143
size_t write_varlen_bytes(unsigned char *stream, const std::unsigned_integral auto &data)
Writes variable-length integer to the stream.
Definition: variable_length_integers.h:124
size_t get_size_integer_varlen(const std::unsigned_integral auto &data)
Calculates the number of bytes necessary to store data.
Definition: variable_length_integers.h:76
size_t read_varlen_bytes_signed(const unsigned char *stream, std::size_t stream_bytes, Type &data)
Reads variable-length integer from the stream.
Definition: variable_length_integers.h:176
size_t read_varlen_bytes(const unsigned char *stream, std::size_t stream_bytes, std::unsigned_integral auto &data)
Reads variable-length integer from the stream.
Definition: variable_length_integers.h:196
size_t get_size_integer_varlen_signed(const std::signed_integral auto &data)
Calculates the number of bytes necessary to store data.
Definition: variable_length_integers.h:68
size_t write_varlen_bytes_unsigned(unsigned char *stream, const std::unsigned_integral auto &data)
Writes variable-length integer to the stream.
Definition: variable_length_integers.h:90