From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 68A806E478; Fri, 24 Jul 2026 13:08:36 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 68A806E478 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1784887716; bh=f4ZBetm8l0ekJJTP0PfxVzDJgVZAa/rN/9JE0rE5PPA=; h=Date:To:Cc:References:In-Reply-To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=MYWbt4kNMq5wqcfO44bHEGhIer5qBC6HI3BJ0sRx/D1aUpwyH/jDk9MzqVDArl1k5 e3y3yx9aSBwgqru9ZiGMwgVio4RAyxFjpgt9b5otu9ipU0HhLi3XFM5bUqbzk2dIaP 4/AGBkTdzEoeHEGuHQF4bgIazE22vysy1AVTlDnw= Received: from send172.i.mail.ru (send172.i.mail.ru [95.163.59.11]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 468F06E478 for ; Fri, 24 Jul 2026 13:08:35 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 468F06E478 Received: by exim-smtp-6c76488b9f-bshh5 with esmtpa (envelope-from ) id 1wnCpV-000000006yh-2J47; Fri, 24 Jul 2026 13:08:34 +0300 Content-Type: multipart/alternative; boundary="------------Hh6Mjj4hDybj1UIznoqrMUvc" Message-ID: <761d882f-e7e0-46c4-9d67-1ec48882ef15@tarantool.org> Date: Fri, 24 Jul 2026 13:08:31 +0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: Sergey Kaplun , Evgeniy Temirgaleev Cc: tarantool-patches@dev.tarantool.org References: <20260722140036.3606677-1-skaplun@tarantool.org> In-Reply-To: <20260722140036.3606677-1-skaplun@tarantool.org> X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD93ED3C6BF6FFA701FD3C8ECD4FF8890A5B344E43CA398808A182A05F538085040E35140F34A50D0153DE06ABAFEAF670510CC39835D87AD297EC1506C8F585ED7ACCB5262BB541CA2 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7C2204D4F9A221771EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637F28F4AF03526AA8F8638F802B75D45FF914D58D5BE9E6BC1A93B80C6DEB9DEE97C6FB206A91F05B222589014638841802E070BE324C7D3C41528019AEA0E02C1F6B57BC7E64490618DEB871D839B73339E8FC8737B5C2249957A4DEDD2346B42CC7F00164DA146DAFE8445B8C89999729449624AB7ADAF37F6B57BC7E64490611E7FA7ABCAF51C92176DF2183F8FC7C0A29E2F051442AF778941B15DA834481F9449624AB7ADAF372E808ACE2090B5E14AD6D5ED66289B5259CC434672EE63711DD303D21008E298D5E8D9A59859A8B6B372FE9A2E580EFC725E5C173C3A84C30584FF81F342DA0735872C767BF85DA2F004C90652538430E4A6367B16DE6309 X-C1DE0DAB: 0D63561A33F958A5D77FB4A02AD66AE65002B1117B3ED69678ABB1A30063FABFC66B2B37046EC955823CB91A9FED034534781492E4B8EEAD17AEC49845D0B908 X-C8649E89: 1C3962B70DF3F0AD73CAD6646DEDE191716CD42B3DD1D34CAB70F9BE574AE9C625B6776AC983F447FC0B9F89525902EE6F57B2FD27647F25E66C117BDB76D659F76FB87905345A0D56E04969B5DEB94D14E8293B2F9090B04EFD18855B95FDB5229D3508133D8AB2B8341EE9D5BE9A0A3B667D2F50A0AB4D840E123DD75D7CE3F9361D880786748D6536EB022892E5344C41F94D744909CE2512F26BEC029E55448553D2254B8D95CD72808BE417F3B9E0E7457915DAA85F X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu53w8ahmwBjZKM/YPHZyZHvz5uv+WouB9+ObcCpyrx6l7KImUglyhkEat/+ysWwi0gdhEs0JGjl6ggRWTy1haxBpVdbIX1nthFXMZebaIdHP2ghjoIc/363UZI6Kf1ptIMVWmxowtcrDwUxSdVBQQFd3g= X-DA7885C5: C628952C4C6B3328F255D290C0D534F9907F37371EE3CF9CCBC9F9E84572C657DF5115BF873215135B1A4C17EAA7BC4BEF2421ABFA55128DAF83EF9164C44C7E X-Mailru-Sender: 689FA8AB762F7393520AF17B8A65FDE2374EA0962642A0E21F915E5F4F3D08136401EF388FC776E3EF86D5F70DA33880E41E8EF7A07863ECB274557F927329BE2DDF8182D28ACDB545BD1C3CC395C826B4A721A3011E896F X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH luajit] test: add tests for the leb128 encode/decode X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Bronnikov via Tarantool-patches Reply-To: Sergey Bronnikov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" This is a multi-part message in MIME format. --------------Hh6Mjj4hDybj1UIznoqrMUvc Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Hello, thanks for the patch! LGTM Sergey On 7/22/26 17:00, Sergey Kaplun wrote: > This patch is a follow-up to the commit > 7058e9215f8a50d00701302ecbb510503d21148d ("utils: introduce leb128 > reader and writer"). > > The leb128 encoding is used inside our profilers (memprof/sysprof). But > there are no tests for this module, so its coverage is low, since only > `lj_utils_write_uleb128()` is used. Also, there is no correctness check > for this implementation. > > This patch adapts the LuaVela tests [1] to cover this module. Since the > initial version of the module wasn't adapted as is, the guarded version > of the reader functions has different behaviour for zero-sized requested > reads. Hence, this case is removed for now. The readers with a requested > size are unused in our code, so this is not crucial at the moment. > > Also, this patch fixes the UBSan warning in the > `lj_utils_write_leb128()` on checking the remaining value to be written > by fixing the cast to uint64_t. > > [1]:https://github.com/luavela/luavela/blob/288e8a95218ed47dad93016259533406e30b109b/tests/impl/uJIT-tests-C/suite/test_leb128.c > --- > > The main idea of the patch is to increase coverage for the > lj_utils_leb128.c. The UBSan "bug" (not really, since this part is > unused, anyway) is fixed as a side effect. You may compare the coverage > between master [1] and this branch [2] with coveralls. > > Branch:https://github.com/tarantool/luajit/tree/skaplun/gh-noticket-leb128-testsing > > [1]:https://coveralls.io/builds/80394140/source?filename=src%2Flj_utils_leb128.c > [2]:https://coveralls.io/builds/80728053/source?filename=src%2Flj_utils_leb128.c > > src/lj_utils_leb128.c | 2 +- > test/tarantool-c-tests/unit-leb128.test.c | 366 ++++++++++++++++++++++ > 2 files changed, 367 insertions(+), 1 deletion(-) > create mode 100644 test/tarantool-c-tests/unit-leb128.test.c > > diff --git a/src/lj_utils_leb128.c b/src/lj_utils_leb128.c > index d66961da..5ad2bd3c 100644 > --- a/src/lj_utils_leb128.c > +++ b/src/lj_utils_leb128.c > @@ -105,7 +105,7 @@ size_t LJ_FASTCALL lj_utils_write_leb128(uint8_t *buffer, int64_t value) > size_t i = 0; > > /* LEB_SIGN_BIT propagation to check the remaining value. */ > - while ((uint64_t)(value + LEB_SIGN_BIT) >= MIN_TWOBYTE_VALUE) { > + while ((uint64_t)value + LEB_SIGN_BIT >= MIN_TWOBYTE_VALUE) { > buffer[i++] = (uint8_t)((value & PAYLOAD_MASK) | LINK_BIT); > value >>= SHIFT_STEP; > } > diff --git a/test/tarantool-c-tests/unit-leb128.test.c b/test/tarantool-c-tests/unit-leb128.test.c > new file mode 100644 > index 00000000..142476fb > --- /dev/null > +++ b/test/tarantool-c-tests/unit-leb128.test.c > @@ -0,0 +1,366 @@ > +/* > +** Testing of LEB128/ULEB128 encoding. > +** > +** Major portions taken verbatim or adapted from the LuaVela. > +** Copyright (C) 2020-2026 LuaVela Authors. > +** Copyright (C) 2015-2020 IPONWEB Ltd. > +*/ > + > +#include > +#include > + > +#include "test.h" > + > +#include "lj_utils.h" > + > +#define BUFFER_SIZE 16 > + > +static int test_write_uleb128(void *state) > +{ > + size_t bytes_written = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x0); > + > + bytes_written = lj_utils_write_uleb128(buffer, 64); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x40); > + > + bytes_written = lj_utils_write_uleb128(buffer, 128); > + assert_true(bytes_written == 2); > + assert_true(buffer[0] == 0x80 && buffer[1] == 0x01); > + > + bytes_written = lj_utils_write_uleb128(buffer, UINT64_MAX); > + assert_true(bytes_written == 10); > + assert_true(buffer[0] == 0xff && buffer[1] == 0xff && > + buffer[2] == 0xff && buffer[3] == 0xff && > + buffer[4] == 0xff && buffer[5] == 0xff && > + buffer[6] == 0xff && buffer[7] == 0xff && > + buffer[8] == 0xff && buffer[9] == 0x01); > + > + return TEST_EXIT_SUCCESS; > +} > + > +static int test_write_leb128(void *state) > +{ > + size_t bytes_written = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + bytes_written = lj_utils_write_leb128(buffer, 0); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x0); > + > + bytes_written = lj_utils_write_leb128(buffer, -624485); > + assert_true(bytes_written == 3); > + assert_true(buffer[0] == 0x9b && buffer[1] == 0xf1 && > + buffer[2] == 0x59); > + > + bytes_written = lj_utils_write_leb128(buffer, INT64_MIN); > + assert_true(bytes_written == 10); > + assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 && > + buffer[2] == 0x80 && buffer[3] == 0x80 && > + buffer[4] == 0x80 && buffer[5] == 0x80 && > + buffer[6] == 0x80 && buffer[7] == 0x80 && > + buffer[8] == 0x80 && buffer[9] == 0x7f); > + > + bytes_written = lj_utils_write_leb128(buffer, INT64_MAX); > + assert_true(bytes_written == 10); > + assert_true(buffer[0] == 0xff && buffer[1] == 0xff && > + buffer[2] == 0xff && buffer[3] == 0xff && > + buffer[4] == 0xff && buffer[5] == 0xff && > + buffer[6] == 0xff && buffer[7] == 0xff && > + buffer[8] == 0xff && buffer[9] == 0x00); > + > + return TEST_EXIT_SUCCESS; > +} > + > +/* Test miscellaneous writes, both signed and unsigned. */ > +static int test_misc_writes(void *state) > +{ > + size_t bytes_written = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + bytes_written = lj_utils_write_leb128(buffer, 0x10); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x10); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0x10); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x10); > + > + bytes_written = lj_utils_write_leb128(buffer, -0x3b); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x45); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0x45); > + assert_true(bytes_written == 1); > + assert_true(buffer[0] == 0x45); > + > + bytes_written = lj_utils_write_leb128(buffer, 0x190e); > + assert_true(bytes_written == 2); > + assert_true(buffer[0] == 0x8e && buffer[1] == 0x32); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0x190e); > + assert_true(bytes_written == 2); > + assert_true(buffer[0] == 0x8e && buffer[1] == 0x32); > + > + bytes_written = lj_utils_write_leb128(buffer, -0x143f); > + assert_true(bytes_written == 2); > + assert_true(buffer[0] == 0xc1 && buffer[1] == 0x57); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0x2bc1); > + assert_true(bytes_written == 2); > + assert_true(buffer[0] == 0xc1 && buffer[1] == 0x57); > + > + bytes_written = lj_utils_write_leb128(buffer, 0x7e00000); > + assert_true(bytes_written == 4); > + assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 && > + buffer[2] == 0x80 && buffer[3] == 0x3f); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0x7e00000); > + assert_true(bytes_written == 4); > + assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 && > + buffer[2] == 0x80 && buffer[3] == 0x3f); > + > + bytes_written = lj_utils_write_leb128(buffer, -0x6200000); > + assert_true(bytes_written == 4); > + assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 && > + buffer[2] == 0x80 && buffer[3] == 0x4f); > + > + bytes_written = lj_utils_write_uleb128(buffer, 0x9e00000); > + assert_true(bytes_written == 4); > + assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 && > + buffer[2] == 0x80 && buffer[3] == 0x4f); > + > + return TEST_EXIT_SUCCESS; > +} > + > +static int test_read_uleb128(void *state) > +{ > + uint64_t value = 0; > + size_t bytes_read = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + bytes_read = lj_utils_read_uleb128(&value, buffer); > + assert_true(bytes_read == 1); > + assert_true(value == 0); > + > + buffer[0] = 0x40; > + bytes_read = lj_utils_read_uleb128(&value, buffer); > + assert_true(bytes_read == 1); > + assert_true(value == 64); > + > + buffer[0] = 0x80; > + buffer[1] = 0x01; > + bytes_read = lj_utils_read_uleb128(&value, buffer); > + assert_true(bytes_read == 2); > + assert_true(value == 128); > + > + memset(buffer, 0xff, 9); > + buffer[9] = 0x01; > + bytes_read = lj_utils_read_uleb128(&value, buffer); > + assert_true(bytes_read == 10); > + assert_true(value == UINT64_MAX); > + > + return TEST_EXIT_SUCCESS; > +} > + > +static int test_read_leb128(void *state) > +{ > + int64_t value = 0; > + size_t bytes_read = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + bytes_read = lj_utils_read_leb128(&value, buffer); > + assert_true(bytes_read == 1); > + assert_true(value == 0); > + > + buffer[0] = 0x9b; > + buffer[1] = 0xf1; > + buffer[2] = 0x59; > + bytes_read = lj_utils_read_leb128(&value, buffer); > + assert_true(bytes_read == 3); > + assert_true(value == (int64_t)-624485); > + > + memset(buffer, 0x80, 9); > + buffer[9] = 0x7f; > + bytes_read = lj_utils_read_leb128(&value, buffer); > + assert_true(bytes_read == 10); > + assert_true(value == INT64_MIN); > + > + memset(buffer, 0xff, 9); > + buffer[9] = 0x00; > + bytes_read = lj_utils_read_leb128(&value, buffer); > + assert_true(bytes_read == 10); > + assert_true(value == INT64_MAX); > + > + return TEST_EXIT_SUCCESS; > +} > + > +/* Test miscellaneous reads, both signed and unsigned. */ > +static int test_misc_reads(void *state) > +{ > + int64_t i_value = 0; > + uint64_t u_value = 0; > + > + size_t bytes_read = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + buffer[0] = 0x10; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 1); > + assert_true(i_value == (int64_t)0x10); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 1); > + assert_true(u_value == (uint64_t)0x10); > + > + buffer[0] = 0x45; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 1); > + assert_true(i_value == (int64_t)-0x3b); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 1); > + assert_true(u_value == (uint64_t)0x45); > + > + buffer[0] = 0x8e; > + buffer[1] = 0x32; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 2); > + assert_true(i_value == (int64_t)0x190e); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 2); > + assert_true(u_value == (uint64_t)0x190e); > + > + buffer[0] = 0xc1; > + buffer[1] = 0x57; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 2); > + assert_true(i_value == (int64_t)-0x143f); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 2); > + assert_true(u_value == (uint64_t)0x2bc1); > + > + buffer[0] = 0xc1; > + buffer[1] = 0x57; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 2); > + assert_true(i_value == (int64_t)-0x143f); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 2); > + assert_true(u_value == (uint64_t)0x2bc1); > + > + buffer[0] = 0x80; > + buffer[1] = 0x80; > + buffer[2] = 0x80; > + buffer[3] = 0x3f; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 4); > + assert_true(i_value == (int64_t)0x7e00000); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 4); > + assert_true(u_value == (uint64_t)0x7e00000); > + > + buffer[0] = 0x80; > + buffer[1] = 0x80; > + buffer[2] = 0x80; > + buffer[3] = 0x4f; > + > + bytes_read = lj_utils_read_leb128(&i_value, buffer); > + assert_true(bytes_read == 4); > + assert_true(i_value == (int64_t)-0x6200000); > + > + bytes_read = lj_utils_read_uleb128(&u_value, buffer); > + assert_true(bytes_read == 4); > + assert_true(u_value == (uint64_t)0x9e00000); > + > + return TEST_EXIT_SUCCESS; > +} > + > +static int test_read_n(void *state) > +{ > + int64_t i_value = 0; > + uint64_t u_value = 0; > + > + size_t bytes_read = 0; > + uint8_t buffer[BUFFER_SIZE] = {0}; > + > + UNUSED(state); > + > + bytes_read = lj_utils_read_leb128_n(&i_value, buffer, 1); > + assert_true(bytes_read == 1); > + assert_true(i_value == 0); > + > + bytes_read = lj_utils_read_uleb128_n(&u_value, buffer, 1); > + assert_true(bytes_read == 1); > + assert_true(u_value == 0); > + > + buffer[0] = 0x80; > + buffer[1] = 0x80; > + buffer[2] = 0x80; > + buffer[3] = 0x3f; > + > + assert_true(lj_utils_read_leb128_n(&i_value, buffer, 3) == 0); > + assert_true(lj_utils_read_uleb128_n(&u_value, buffer, 3) == 0); > + /* Values are untouched in case of failure. */ > + assert_true(i_value == 0); > + assert_true(u_value == 0); > + > + bytes_read = lj_utils_read_leb128_n(&i_value, buffer, 4); > + assert_true(bytes_read == 4); > + assert_true(i_value == (int64_t)0x7e00000); > + > + bytes_read = lj_utils_read_uleb128_n(&u_value, buffer, 4); > + assert_true(bytes_read == 4); > + assert_true(u_value == (uint64_t)0x7e00000); > + > + bytes_read = lj_utils_read_leb128_n(&i_value, buffer, 5); > + assert_true(bytes_read == 4); > + assert_true(i_value == (int64_t)0x7e00000); > + > + bytes_read = lj_utils_read_uleb128_n(&u_value, buffer, 5); > + assert_true(bytes_read == 4); > + assert_true(u_value == (uint64_t)0x7e00000); > + > + return TEST_EXIT_SUCCESS; > +} > + > +int main(void) > +{ > + const struct test_unit tgroup[] = { > + test_unit_def(test_write_uleb128), > + test_unit_def(test_write_leb128), > + test_unit_def(test_misc_writes), > + test_unit_def(test_read_uleb128), > + test_unit_def(test_read_leb128), > + test_unit_def(test_misc_reads), > + test_unit_def(test_read_n), > + }; > + const int test_result = test_run_group(tgroup, NULL); > + return test_result; > +} --------------Hh6Mjj4hDybj1UIznoqrMUvc Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit

Hello,

thanks for the patch! LGTM

Sergey

On 7/22/26 17:00, Sergey Kaplun wrote:
This patch is a follow-up to the commit
7058e9215f8a50d00701302ecbb510503d21148d ("utils: introduce leb128
reader and writer").

The leb128 encoding is used inside our profilers (memprof/sysprof). But
there are no tests for this module, so its coverage is low, since only
`lj_utils_write_uleb128()` is used. Also, there is no correctness check
for this implementation.

This patch adapts the LuaVela tests [1] to cover this module. Since the
initial version of the module wasn't adapted as is, the guarded version
of the reader functions has different behaviour for zero-sized requested
reads. Hence, this case is removed for now. The readers with a requested
size are unused in our code, so this is not crucial at the moment.

Also, this patch fixes the UBSan warning in the
`lj_utils_write_leb128()` on checking the remaining value to be written
by fixing the cast to uint64_t.

[1]: https://github.com/luavela/luavela/blob/288e8a95218ed47dad93016259533406e30b109b/tests/impl/uJIT-tests-C/suite/test_leb128.c
---

The main idea of the patch is to increase coverage for the
lj_utils_leb128.c. The UBSan "bug" (not really, since this part is
unused, anyway) is fixed as a side effect. You may compare the coverage
between master [1] and this branch [2] with coveralls.

Branch: https://github.com/tarantool/luajit/tree/skaplun/gh-noticket-leb128-testsing

[1]: https://coveralls.io/builds/80394140/source?filename=src%2Flj_utils_leb128.c
[2]: https://coveralls.io/builds/80728053/source?filename=src%2Flj_utils_leb128.c

 src/lj_utils_leb128.c                     |   2 +-
 test/tarantool-c-tests/unit-leb128.test.c | 366 ++++++++++++++++++++++
 2 files changed, 367 insertions(+), 1 deletion(-)
 create mode 100644 test/tarantool-c-tests/unit-leb128.test.c

diff --git a/src/lj_utils_leb128.c b/src/lj_utils_leb128.c
index d66961da..5ad2bd3c 100644
--- a/src/lj_utils_leb128.c
+++ b/src/lj_utils_leb128.c
@@ -105,7 +105,7 @@ size_t LJ_FASTCALL lj_utils_write_leb128(uint8_t *buffer, int64_t value)
   size_t i = 0;
 
   /* LEB_SIGN_BIT propagation to check the remaining value. */
-  while ((uint64_t)(value + LEB_SIGN_BIT) >= MIN_TWOBYTE_VALUE) {
+  while ((uint64_t)value + LEB_SIGN_BIT >= MIN_TWOBYTE_VALUE) {
     buffer[i++] = (uint8_t)((value & PAYLOAD_MASK) | LINK_BIT);
     value >>= SHIFT_STEP;
   }
diff --git a/test/tarantool-c-tests/unit-leb128.test.c b/test/tarantool-c-tests/unit-leb128.test.c
new file mode 100644
index 00000000..142476fb
--- /dev/null
+++ b/test/tarantool-c-tests/unit-leb128.test.c
@@ -0,0 +1,366 @@
+/*
+** Testing of LEB128/ULEB128 encoding.
+**
+** Major portions taken verbatim or adapted from the LuaVela.
+** Copyright (C) 2020-2026 LuaVela Authors.
+** Copyright (C) 2015-2020 IPONWEB Ltd.
+*/
+
+#include <stdint.h>
+#include <string.h>
+
+#include "test.h"
+
+#include "lj_utils.h"
+
+#define BUFFER_SIZE 16
+
+static int test_write_uleb128(void *state)
+{
+	size_t bytes_written = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x0);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 64);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x40);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 128);
+	assert_true(bytes_written == 2);
+	assert_true(buffer[0] == 0x80 && buffer[1] == 0x01);
+
+	bytes_written = lj_utils_write_uleb128(buffer, UINT64_MAX);
+	assert_true(bytes_written == 10);
+	assert_true(buffer[0] == 0xff && buffer[1] == 0xff &&
+		    buffer[2] == 0xff && buffer[3] == 0xff &&
+		    buffer[4] == 0xff && buffer[5] == 0xff &&
+		    buffer[6] == 0xff && buffer[7] == 0xff &&
+		    buffer[8] == 0xff && buffer[9] == 0x01);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+static int test_write_leb128(void *state)
+{
+	size_t bytes_written = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	bytes_written = lj_utils_write_leb128(buffer, 0);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x0);
+
+	bytes_written = lj_utils_write_leb128(buffer, -624485);
+	assert_true(bytes_written == 3);
+	assert_true(buffer[0] == 0x9b && buffer[1] == 0xf1 &&
+		    buffer[2] == 0x59);
+
+	bytes_written = lj_utils_write_leb128(buffer, INT64_MIN);
+	assert_true(bytes_written == 10);
+	assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 &&
+		    buffer[2] == 0x80 && buffer[3] == 0x80 &&
+		    buffer[4] == 0x80 && buffer[5] == 0x80 &&
+		    buffer[6] == 0x80 && buffer[7] == 0x80 &&
+		    buffer[8] == 0x80 && buffer[9] == 0x7f);
+
+	bytes_written = lj_utils_write_leb128(buffer, INT64_MAX);
+	assert_true(bytes_written == 10);
+	assert_true(buffer[0] == 0xff && buffer[1] == 0xff &&
+		    buffer[2] == 0xff && buffer[3] == 0xff &&
+		    buffer[4] == 0xff && buffer[5] == 0xff &&
+		    buffer[6] == 0xff && buffer[7] == 0xff &&
+		    buffer[8] == 0xff && buffer[9] == 0x00);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+/* Test miscellaneous writes, both signed and unsigned. */
+static int test_misc_writes(void *state)
+{
+	size_t bytes_written = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	bytes_written = lj_utils_write_leb128(buffer, 0x10);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x10);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0x10);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x10);
+
+	bytes_written = lj_utils_write_leb128(buffer, -0x3b);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x45);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0x45);
+	assert_true(bytes_written == 1);
+	assert_true(buffer[0] == 0x45);
+
+	bytes_written = lj_utils_write_leb128(buffer, 0x190e);
+	assert_true(bytes_written == 2);
+	assert_true(buffer[0] == 0x8e && buffer[1] == 0x32);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0x190e);
+	assert_true(bytes_written == 2);
+	assert_true(buffer[0] == 0x8e && buffer[1] == 0x32);
+
+	bytes_written = lj_utils_write_leb128(buffer, -0x143f);
+	assert_true(bytes_written == 2);
+	assert_true(buffer[0] == 0xc1 && buffer[1] == 0x57);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0x2bc1);
+	assert_true(bytes_written == 2);
+	assert_true(buffer[0] == 0xc1 && buffer[1] == 0x57);
+
+	bytes_written = lj_utils_write_leb128(buffer, 0x7e00000);
+	assert_true(bytes_written == 4);
+	assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 &&
+		    buffer[2] == 0x80 && buffer[3] == 0x3f);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0x7e00000);
+	assert_true(bytes_written == 4);
+	assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 &&
+		    buffer[2] == 0x80 && buffer[3] == 0x3f);
+
+	bytes_written = lj_utils_write_leb128(buffer, -0x6200000);
+	assert_true(bytes_written == 4);
+	assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 &&
+		    buffer[2] == 0x80 && buffer[3] == 0x4f);
+
+	bytes_written = lj_utils_write_uleb128(buffer, 0x9e00000);
+	assert_true(bytes_written == 4);
+	assert_true(buffer[0] == 0x80 && buffer[1] == 0x80 &&
+		    buffer[2] == 0x80 && buffer[3] == 0x4f);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+static int test_read_uleb128(void *state)
+{
+	uint64_t value = 0;
+	size_t bytes_read = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	bytes_read = lj_utils_read_uleb128(&value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(value == 0);
+
+	buffer[0] = 0x40;
+	bytes_read = lj_utils_read_uleb128(&value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(value == 64);
+
+	buffer[0] = 0x80;
+	buffer[1] = 0x01;
+	bytes_read = lj_utils_read_uleb128(&value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(value == 128);
+
+	memset(buffer, 0xff, 9);
+	buffer[9] = 0x01;
+	bytes_read = lj_utils_read_uleb128(&value, buffer);
+	assert_true(bytes_read == 10);
+	assert_true(value == UINT64_MAX);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+static int test_read_leb128(void *state)
+{
+	int64_t value = 0;
+	size_t bytes_read = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	bytes_read = lj_utils_read_leb128(&value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(value == 0);
+
+	buffer[0] = 0x9b;
+	buffer[1] = 0xf1;
+	buffer[2] = 0x59;
+	bytes_read = lj_utils_read_leb128(&value, buffer);
+	assert_true(bytes_read == 3);
+	assert_true(value == (int64_t)-624485);
+
+	memset(buffer, 0x80, 9);
+	buffer[9] = 0x7f;
+	bytes_read = lj_utils_read_leb128(&value, buffer);
+	assert_true(bytes_read == 10);
+	assert_true(value == INT64_MIN);
+
+	memset(buffer, 0xff, 9);
+	buffer[9] = 0x00;
+	bytes_read = lj_utils_read_leb128(&value, buffer);
+	assert_true(bytes_read == 10);
+	assert_true(value == INT64_MAX);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+/* Test miscellaneous reads, both signed and unsigned. */
+static int test_misc_reads(void *state)
+{
+	int64_t i_value = 0;
+	uint64_t u_value = 0;
+
+	size_t bytes_read = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	buffer[0] = 0x10;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(i_value == (int64_t)0x10);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(u_value == (uint64_t)0x10);
+
+	buffer[0] = 0x45;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(i_value == (int64_t)-0x3b);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 1);
+	assert_true(u_value == (uint64_t)0x45);
+
+	buffer[0] = 0x8e;
+	buffer[1] = 0x32;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(i_value == (int64_t)0x190e);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(u_value == (uint64_t)0x190e);
+
+	buffer[0] = 0xc1;
+	buffer[1] = 0x57;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(i_value == (int64_t)-0x143f);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(u_value == (uint64_t)0x2bc1);
+
+	buffer[0] = 0xc1;
+	buffer[1] = 0x57;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(i_value == (int64_t)-0x143f);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 2);
+	assert_true(u_value == (uint64_t)0x2bc1);
+
+	buffer[0] = 0x80;
+	buffer[1] = 0x80;
+	buffer[2] = 0x80;
+	buffer[3] = 0x3f;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 4);
+	assert_true(i_value == (int64_t)0x7e00000);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 4);
+	assert_true(u_value == (uint64_t)0x7e00000);
+
+	buffer[0] = 0x80;
+	buffer[1] = 0x80;
+	buffer[2] = 0x80;
+	buffer[3] = 0x4f;
+
+	bytes_read = lj_utils_read_leb128(&i_value, buffer);
+	assert_true(bytes_read == 4);
+	assert_true(i_value == (int64_t)-0x6200000);
+
+	bytes_read = lj_utils_read_uleb128(&u_value, buffer);
+	assert_true(bytes_read == 4);
+	assert_true(u_value == (uint64_t)0x9e00000);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+static int test_read_n(void *state)
+{
+	int64_t i_value = 0;
+	uint64_t u_value = 0;
+
+	size_t bytes_read = 0;
+	uint8_t buffer[BUFFER_SIZE] = {0};
+
+	UNUSED(state);
+
+	bytes_read = lj_utils_read_leb128_n(&i_value, buffer, 1);
+	assert_true(bytes_read == 1);
+	assert_true(i_value == 0);
+
+	bytes_read = lj_utils_read_uleb128_n(&u_value, buffer, 1);
+	assert_true(bytes_read == 1);
+	assert_true(u_value == 0);
+
+	buffer[0] = 0x80;
+	buffer[1] = 0x80;
+	buffer[2] = 0x80;
+	buffer[3] = 0x3f;
+
+	assert_true(lj_utils_read_leb128_n(&i_value, buffer, 3) == 0);
+	assert_true(lj_utils_read_uleb128_n(&u_value, buffer, 3) == 0);
+	/* Values are untouched in case of failure. */
+	assert_true(i_value == 0);
+	assert_true(u_value == 0);
+
+	bytes_read = lj_utils_read_leb128_n(&i_value, buffer, 4);
+	assert_true(bytes_read == 4);
+	assert_true(i_value == (int64_t)0x7e00000);
+
+	bytes_read = lj_utils_read_uleb128_n(&u_value, buffer, 4);
+	assert_true(bytes_read == 4);
+	assert_true(u_value == (uint64_t)0x7e00000);
+
+	bytes_read = lj_utils_read_leb128_n(&i_value, buffer, 5);
+	assert_true(bytes_read == 4);
+	assert_true(i_value == (int64_t)0x7e00000);
+
+	bytes_read = lj_utils_read_uleb128_n(&u_value, buffer, 5);
+	assert_true(bytes_read == 4);
+	assert_true(u_value == (uint64_t)0x7e00000);
+
+	return TEST_EXIT_SUCCESS;
+}
+
+int main(void)
+{
+	const struct test_unit tgroup[] = {
+		test_unit_def(test_write_uleb128),
+		test_unit_def(test_write_leb128),
+		test_unit_def(test_misc_writes),
+		test_unit_def(test_read_uleb128),
+		test_unit_def(test_read_leb128),
+		test_unit_def(test_misc_reads),
+		test_unit_def(test_read_n),
+	};
+	const int test_result = test_run_group(tgroup, NULL);
+	return test_result;
+}
--------------Hh6Mjj4hDybj1UIznoqrMUvc--