Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ add_library(migraphx
load_save.cpp
logger.cpp
make_op.cpp
md5.cpp
memory_coloring.cpp
module.cpp
msgpack.cpp
Expand Down
40 changes: 40 additions & 0 deletions src/include/migraphx/md5.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MIGRAPHX_GUARD_RTGLIB_MD5_HPP
#define MIGRAPHX_GUARD_RTGLIB_MD5_HPP

#include <string>
#include <string_view>
#include <migraphx/config.hpp>

namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {

/// Compute the MD5 digest of a string and return it as a lowercase hex string.
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since md5 is a public API and MD5 is not suitable for cryptographic security, consider documenting that this function is intended for non-cryptographic checksums (e.g., cache keys) so callers don't misinterpret it as secure hashing.

Suggested change
/// Compute the MD5 digest of a string and return it as a lowercase hex string.
/// Compute the MD5 digest of a string and return it as a lowercase hex string.
/// This function is intended only for non-cryptographic checksums such as cache
/// keys or content fingerprints. MD5 is not cryptographically secure and must
/// not be used for passwords, signatures, or other security-sensitive hashing.

Copilot uses AI. Check for mistakes.
std::string MIGRAPHX_EXPORT md5(const std::string_view& str);

} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx

#endif
37 changes: 37 additions & 0 deletions src/include/migraphx/stringutils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@
#define MIGRAPHX_GUARD_MIGRAPHLIB_STRINGUTILS_HPP

#include <algorithm>
#include <array>
#include <cstdint>
#include <numeric>
#include <string>
#include <sstream>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <migraphx/algorithm.hpp>
#include <migraphx/as_number.hpp>
#include <migraphx/ranges.hpp>

namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {
Expand Down Expand Up @@ -236,6 +241,38 @@
return ss.str();
}

/// Concatenate the lowercase hex representation of each integer in a range.
/// Each element emits 2*sizeof(element) characters. When lsb is false the
/// most-significant byte is emitted first (standard hex notation); when true,
/// the least-significant byte is first (matches byte-stream hash digests).
template <class Range>
inline std::string to_hex_string(const Range& r, bool lsb = false)
{
constexpr std::array<char, 16> hex_digits = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
return std::accumulate(r.begin(), r.end(), std::string{}, [&](std::string acc, const auto& x) {
using type = std::make_unsigned_t<std::decay_t<decltype(x)>>;
const auto u = bit_cast<type>(x);
const auto to_byte = [&](std::ptrdiff_t b) -> std::uint8_t {
Comment on lines +244 to +256
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_hex_string uses bit_cast(...), but this header doesn't include <migraphx/bit_cast.hpp>, so including <migraphx/stringutils.hpp> on its own will fail to compile (and the repo’s header-include tests compile each header standalone). Add the missing include (or avoid bit_cast here) to make the header self-contained.

Copilot uses AI. Check for mistakes.
return (u >> (b * 8u)) & 0xffu;

Check warning on line 257 in src/include/migraphx/stringutils.hpp

View workflow job for this annotation

GitHub Actions / tidy

use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise,-warnings-as-errors]

Check warning on line 257 in src/include/migraphx/stringutils.hpp

View workflow job for this annotation

GitHub Actions / tidy

use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise,-warnings-as-errors]

Check warning on line 257 in src/include/migraphx/stringutils.hpp

View workflow job for this annotation

GitHub Actions / tidy

use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise,-warnings-as-errors]
};
const auto append_hex = [&](std::string s, std::uint8_t byte) {
s.push_back(hex_digits[byte >> 4u]);
s.push_back(hex_digits[byte & 0x0fu]);
return s;
};
const auto bytes = range(sizeof(type));
if(lsb)
{
return transform_accumulate(
bytes.begin(), bytes.end(), std::move(acc), append_hex, to_byte);
}
const auto rbytes = reverse(bytes);
return transform_accumulate(
rbytes.begin(), rbytes.end(), std::move(acc), append_hex, to_byte);
});
Comment on lines +253 to +273
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_hex_string builds the result via std::accumulate and (inside) transform_accumulate, both of which pass/return std::string by value. This causes repeated string copies and can become quadratic for larger inputs. Consider appending into a single std::string (and reserving the final size when possible).

Suggested change
return std::accumulate(r.begin(), r.end(), std::string{}, [&](std::string acc, const auto& x) {
using T = std::make_unsigned_t<std::decay_t<decltype(x)>>;
const auto u = static_cast<T>(x);
const auto to_byte = [&](std::ptrdiff_t b) -> std::uint8_t {
return (u >> (b * 8u)) & 0xffu;
};
const auto append_hex = [&](std::string s, std::uint8_t byte) {
s.push_back(hex_digits[byte >> 4u]);
s.push_back(hex_digits[byte & 0x0fu]);
return s;
};
const auto bytes = range(sizeof(T));
if(lsb)
{
return transform_accumulate(
bytes.begin(), bytes.end(), std::move(acc), append_hex, to_byte);
}
const auto rbytes = reverse(bytes);
return transform_accumulate(
rbytes.begin(), rbytes.end(), std::move(acc), append_hex, to_byte);
});
using T = std::make_unsigned_t<std::decay_t<decltype(*r.begin())>>;
std::string result;
result.reserve(r.size() * sizeof(T) * 2);
const auto append_hex = [&](std::uint8_t byte) {
result.push_back(hex_digits[byte >> 4u]);
result.push_back(hex_digits[byte & 0x0fu]);
};
for(const auto& x : r)
{
const auto u = static_cast<T>(x);
const auto to_byte = [&](std::ptrdiff_t b) -> std::uint8_t {
return (u >> (b * 8u)) & 0xffu;
};
const auto bytes = range(sizeof(T));
if(lsb)
{
for(auto b : bytes)
append_hex(to_byte(b));
}
else
{
for(auto b : reverse(bytes))
append_hex(to_byte(b));
}
}
return result;

Copilot uses AI. Check for mistakes.
}

} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx

Expand Down
159 changes: 159 additions & 0 deletions src/md5.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2026 Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <migraphx/md5.hpp>
#include <migraphx/bit_cast.hpp>
#include <migraphx/stringutils.hpp>
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file uses range(...) and transform_partial_sum(...), but relies on those being transitively included via migraphx/stringutils.hpp. To make the dependency explicit (and avoid future breakage if stringutils.hpp stops including them), include the headers that define these utilities directly (e.g., migraphx/ranges.hpp and migraphx/algorithm.hpp).

Suggested change
#include <migraphx/stringutils.hpp>
#include <migraphx/stringutils.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/algorithm.hpp>

Copilot uses AI. Check for mistakes.
#include <algorithm>
#include <array>
#include <cstdint>
#include <string>

namespace migraphx {
inline namespace MIGRAPHX_INLINE_NS {

namespace {

constexpr std::size_t block_size = 64;

// Per-round shift amounts (RFC 1321 section 3.4).
constexpr std::array<std::uint32_t, 64> shifts = {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9,
14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};

// Sine-derived constants: floor(2^32 * abs(sin(i + 1))), i = 0..63.
constexpr std::array<std::uint32_t, 64> sine_table = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};

constexpr std::uint32_t rotate_left(std::uint32_t x, std::uint32_t n)
{
return (x << n) | (x >> (32u - n));
}

template <class It>
constexpr std::uint32_t load_le32(It p)
{
return std::uint32_t{p[0]} | (std::uint32_t{p[1]} << 8u) | (std::uint32_t{p[2]} << 16u) |
(std::uint32_t{p[3]} << 24u);
}

std::array<std::uint32_t, 4> process_block(std::array<std::uint32_t, 4> state,
std::array<std::uint8_t, block_size> block)
Comment on lines +68 to +69
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process_block takes the 64-byte block by value, which copies the entire block for every compression call. Since this runs once per 64 bytes of input, it can add avoidable overhead for large strings; take block by const& instead.

Suggested change
std::array<std::uint32_t, 4> process_block(std::array<std::uint32_t, 4> state,
std::array<std::uint8_t, block_size> block)
std::array<std::uint32_t, 4> process_block(
std::array<std::uint32_t, 4> state, const std::array<std::uint8_t, block_size>& block)

Copilot uses AI. Check for mistakes.
{
std::array<std::uint32_t, 16> m{};
const auto word_indices = range(m.size());
std::transform(word_indices.begin(), word_indices.end(), m.begin(), [&](std::ptrdiff_t i) {
return load_le32(block.begin() + (i * 4));
});

// Each round writes the freshly computed value into slot a, then
// std::rotate shifts v right by one so the structured bindings realign
// onto the canonical MD5 register carousel for the next iteration:
// a <- d, b <- a (the just-computed value), c <- b, d <- c.
std::array<std::uint32_t, 4> v = state;
auto& [a, b, c, d] = v;

for(std::uint32_t i = 0; i < 64; ++i)
{
std::array<std::uint32_t, 2> fg{};
if(i < 16)
{
fg = {(b & c) | ((~b) & d), i};
}
else if(i < 32)
{
fg = {(d & b) | ((~d) & c), (5u * i + 1u) % 16u};
}
else if(i < 48)
{
fg = {b ^ c ^ d, (3u * i + 5u) % 16u};
}
else
{
fg = {c ^ (b | (~d)), (7u * i) % 16u};
}

a = b + rotate_left(a + fg[0] + sine_table[i] + m[fg[1]], shifts[i]);
std::rotate(v.begin(), v.end() - 1, v.end());
}

return {state[0] + a, state[1] + b, state[2] + c, state[3] + d};
}

std::uint8_t to_uint8(char c) { return bit_cast<std::uint8_t>(c); }

} // namespace

std::string md5(const std::string_view& str)
{
std::array<std::uint32_t, 4> state = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};

const std::size_t full_blocks = str.size() / block_size;
const std::size_t remainder = str.size() % block_size;

std::array<std::uint8_t, block_size> block{};
for(std::size_t i = 0; i < full_blocks; ++i)
{
const auto chunk_begin = str.begin() + (i * block_size);

Check warning on line 125 in src/md5.cpp

View workflow job for this annotation

GitHub Actions / tidy

'const auto chunk_begin' can be declared as 'const auto *const chunk_begin' [readability-qualified-auto,-warnings-as-errors]
std::transform(chunk_begin, chunk_begin + block_size, block.begin(), &to_uint8);
Comment on lines +125 to +126
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These transforms feed char data into to_uint8(int8_t). On platforms where char is unsigned, bytes >= 0x80 can be altered by the intermediate int8_t conversion, yielding incorrect MD5s for non-ASCII data. Consider using a lambda that casts char -> unsigned char -> std::uint8_t directly.

Copilot uses AI. Check for mistakes.
state = process_block(state, block);
}

// Final block(s): remaining bytes, a 0x80 terminator, zero fill, and the
// message bit length in the last 8 bytes (little-endian). Two blocks are
// needed when the bit-length field no longer fits in the current block.
std::array<std::array<std::uint8_t, block_size>, 2> tail{};
const auto tail_src_begin = str.begin() + (full_blocks * block_size);

Check warning on line 134 in src/md5.cpp

View workflow job for this annotation

GitHub Actions / tidy

'const auto tail_src_begin' can be declared as 'const auto *const tail_src_begin' [readability-qualified-auto,-warnings-as-errors]
std::transform(tail_src_begin, str.end(), tail[0].begin(), &to_uint8);
tail[0][remainder] = 0x80;

const bool need_two = (remainder >= block_size - 8);
const std::uint64_t bit_length = std::uint64_t{str.size()} * 8u;
const auto& last = need_two ? tail[1] : tail[0];
const auto bit_indices = range(8);
transform_partial_sum(
bit_indices.begin(),
bit_indices.end(),
last.end() - 8,
[](std::uint64_t acc, std::uint64_t) { return acc >> 8u; },
Comment on lines +138 to +146
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transform_partial_sum writes through the output iterator, but last is bound as const auto& so last.end() - 8 is a const_iterator and the assignment in transform_partial_sum won’t compile. Make last a non-const reference (e.g., auto& last = ...) so the length field can be written into tail[0/1].

Copilot uses AI. Check for mistakes.
[&](auto) { return bit_length; });
Comment on lines +145 to +147
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

last is bound as a const reference (const auto& last = ...), but transform_partial_sum writes through the output iterator last.end() - 8. For std::array this yields a const_iterator, so this will not compile (and even if it did, it would be writing into a const object). Make last a non-const reference (or select the destination buffer via pointer/index) so the length bytes can be written.

Copilot uses AI. Check for mistakes.

state = process_block(state, tail[0]);
if(need_two)
{
state = process_block(state, tail[1]);
}

return to_hex_string(state, true);
}

} // namespace MIGRAPHX_INLINE_NS
} // namespace migraphx
Loading
Loading