diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 485218999..390950ab9 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -20,6 +20,7 @@ set(sources Keccak256.cpp Keccak256.h LazyInit.h + LEB128.h picosha2.h Result.h SetOnce.h diff --git a/libsolutil/LEB128.h b/libsolutil/LEB128.h new file mode 100644 index 000000000..a156a09bd --- /dev/null +++ b/libsolutil/LEB128.h @@ -0,0 +1,59 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + + +#include + +namespace solidity::util +{ + +inline bytes lebEncode(uint64_t _n) +{ + bytes encoded; + while (_n > 0x7f) + { + encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f))); + _n >>= 7; + } + encoded.emplace_back(_n); + return encoded; +} + +// signed right shift is an arithmetic right shift +static_assert((-1 >> 1) == -1, "Arithmetic shift not supported."); + +inline bytes lebEncodeSigned(int64_t _n) +{ + // Based on https://github.com/llvm/llvm-project/blob/master/llvm/include/llvm/Support/LEB128.h + bytes result; + bool more; + do + { + uint8_t v = _n & 0x7f; + _n >>= 7; + more = !((((_n == 0) && ((v & 0x40) == 0)) || ((_n == -1) && ((v & 0x40) != 0)))); + if (more) + v |= 0x80; // Mark this byte to show that more bytes will follow. + result.emplace_back(v); + } + while (more); + return result; +} + +} diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 5746dfaaf..148ed2786 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -239,28 +240,6 @@ static map const builtins = { {"i64.extend_i32_u", 0xad}, }; -bytes lebEncode(uint64_t _n) -{ - bytes encoded; - while (_n > 0x7f) - { - encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f))); - _n >>= 7; - } - encoded.emplace_back(_n); - return encoded; -} - -bytes lebEncodeSigned(int64_t _n) -{ - if (_n >= 0 && _n < 0x40) - return toBytes(uint8_t(uint64_t(_n) & 0xff)); - else if (-_n > 0 && -_n < 0x40) - return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff)); - else - return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80); -} - bytes prefixSize(bytes _data) { size_t size = _data.size(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 052929c09..caa587415 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -35,6 +35,7 @@ set(libsolutil_sources libsolutil/JSON.cpp libsolutil/Keccak256.cpp libsolutil/LazyInit.cpp + libsolutil/LEB128.cpp libsolutil/StringUtils.cpp libsolutil/SwarmHash.cpp libsolutil/UTF8.cpp diff --git a/test/libsolutil/LEB128.cpp b/test/libsolutil/LEB128.cpp new file mode 100644 index 000000000..96ce11c7a --- /dev/null +++ b/test/libsolutil/LEB128.cpp @@ -0,0 +1,101 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Unit tests for LEB128. + */ + +#include + +#include + +using namespace std; + +namespace solidity::util::test +{ + +BOOST_AUTO_TEST_SUITE(LEB128Test) + +BOOST_AUTO_TEST_CASE(encode_unsigned) +{ + bytes zero = solidity::util::lebEncode(0); + BOOST_REQUIRE(zero.size() == 1); + BOOST_REQUIRE(zero[0] == 0x00); + + bytes one = solidity::util::lebEncode(1); + BOOST_REQUIRE(one.size() == 1); + BOOST_REQUIRE(one[0] == 0x01); + + bytes large = solidity::util::lebEncode(624485); + BOOST_REQUIRE(large.size() == 3); + BOOST_REQUIRE(large[0] == 0xE5); + BOOST_REQUIRE(large[1] == 0x8E); + BOOST_REQUIRE(large[2] == 0x26); + + bytes larger = solidity::util::lebEncodeSigned(123456123456); + BOOST_REQUIRE(larger.size() == 6); + BOOST_REQUIRE(larger[0] == 0xC0); + BOOST_REQUIRE(larger[1] == 0xE4); + BOOST_REQUIRE(larger[2] == 0xBB); + BOOST_REQUIRE(larger[3] == 0xF4); + BOOST_REQUIRE(larger[4] == 0xCB); + BOOST_REQUIRE(larger[5] == 0x03); +} + +BOOST_AUTO_TEST_CASE(encode_signed) +{ + bytes zero = solidity::util::lebEncodeSigned(0); + BOOST_REQUIRE(zero.size() == 1); + BOOST_REQUIRE(zero[0] == 0x00); + + bytes one = solidity::util::lebEncodeSigned(1); + BOOST_REQUIRE(one.size() == 1); + BOOST_REQUIRE(one[0] == 0x01); + + bytes negative_one = solidity::util::lebEncodeSigned(-1); + BOOST_REQUIRE(negative_one.size() == 1); + BOOST_REQUIRE(negative_one[0] == 0x7f); + + bytes negative_two = solidity::util::lebEncodeSigned(-2); + BOOST_REQUIRE(negative_two.size() == 1); + BOOST_REQUIRE(negative_two[0] == 0x7e); + + bytes large = solidity::util::lebEncodeSigned(624485); + BOOST_REQUIRE(large.size() == 3); + BOOST_REQUIRE(large[0] == 0xE5); + BOOST_REQUIRE(large[1] == 0x8E); + BOOST_REQUIRE(large[2] == 0x26); + + bytes negative_large = solidity::util::lebEncodeSigned(-123456); + BOOST_REQUIRE(negative_large.size() == 3); + BOOST_REQUIRE(negative_large[0] == 0xC0); + BOOST_REQUIRE(negative_large[1] == 0xBB); + BOOST_REQUIRE(negative_large[2] == 0x78); + + bytes negative_larger = solidity::util::lebEncodeSigned(-123456123456); + BOOST_REQUIRE(negative_larger.size() == 6); + BOOST_REQUIRE(negative_larger[0] == 0xC0); + BOOST_REQUIRE(negative_larger[1] == 0x9B); + BOOST_REQUIRE(negative_larger[2] == 0xC4); + BOOST_REQUIRE(negative_larger[3] == 0x8B); + BOOST_REQUIRE(negative_larger[4] == 0xB4); + BOOST_REQUIRE(negative_larger[5] == 0x7C); +} + +BOOST_AUTO_TEST_SUITE_END() + +}