Skip to content

Commit

Permalink
[ 7098] Add utility to generate random alphanumeric strings
Browse files Browse the repository at this point in the history
Adds a function which randomly generates an alphanumeric character and concatenates
a string which is returned to the user. Unit tests included.
  • Loading branch information
alanking committed Sep 16, 2023
1 parent 9ba8dab commit 5ca82b7
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 1 deletion.
11 changes: 11 additions & 0 deletions lib/core/include/irods/irods_random.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
#ifndef IRODS_RANDOM_HPP
#define IRODS_RANDOM_HPP

#include <string>

namespace irods {
void getRandomBytes( void * buf, int bytes );

/// \brief Generate a string of size \p _length consisting of randomly generated alphanumeric characters.
///
/// \param[in] _length Desired length of the generated string.
///
/// \throws irods::exception If \p _length exceeds max_size or on allocation failure.
///
/// \since 4.3.1
auto generate_random_alphanumeric_string(std::size_t _length) -> std::string;

template <typename T>
T
getRandom() {
Expand Down
52 changes: 51 additions & 1 deletion lib/core/src/irods_random.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
#include "irods/irods_random.hpp"

#include "irods/irods_exception.hpp"
#include "irods/rodsErrorTable.h"

#include <boost/random.hpp>
#include <fmt/format.h>
#include <openssl/rand.h>
#include "irods/irods_random.hpp"

#include <ctime>
#include <random>
#include <string>

namespace
{
auto generate_random_alphanumeric_character() -> char
{
constexpr char min_char = '0';
constexpr char max_char = 'z';

static std::random_device rd;
static std::default_random_engine e{rd()};
static std::uniform_int_distribution<> d{min_char, max_char};

char c{};
while (!std::isalnum(c)) {
c = d(e);
}
return c;
} // generate_random_alphanumeric_character
} // anonymous namespace

void irods::getRandomBytes( void * buf, int bytes ) {
if ( RAND_bytes( ( unsigned char * )buf, bytes ) != 1 ) {
Expand All @@ -13,3 +40,26 @@ void irods::getRandomBytes( void * buf, int bytes ) {
}
}
}

auto irods::generate_random_alphanumeric_string(std::size_t _length) -> std::string
{
try {
std::string s;
s.reserve(_length);

for (std::size_t i = 0; i < _length; ++i) {
s += generate_random_alphanumeric_character();
}

return s;
}
catch (const std::length_error& e) {
THROW(SYS_INVALID_INPUT_PARAM, fmt::format("{}: Invalid length. [{}]", __func__, e.what()));
}
catch (const std::exception& e) {
THROW(SYS_LIBRARY_ERROR, fmt::format("{}: Failed to generate string. [{}]", __func__, e.what()));
}
catch (...) {
THROW(SYS_UNKNOWN_ERROR, "Failed to generate string: [Unknown error]");
}
} // generate_random_alphanumeric_string
1 change: 1 addition & 0 deletions unit_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ set(
parallel_transfer_engine
process_stash
query_builder
random
rcConnect
rcTicketAdmin
rc_check_auth_credentials
Expand Down
9 changes: 9 additions & 0 deletions unit_tests/cmake/test_config/irods_random.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
set(IRODS_TEST_TARGET irods_random)

set(IRODS_TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/test_random.cpp)

set(IRODS_TEST_INCLUDE_PATH ${IRODS_EXTERNALS_FULLPATH_FMT}/include)

set(IRODS_TEST_LINK_LIBRARIES irods_common
${IRODS_EXTERNALS_FULLPATH_FMT}/lib/libfmt.so)
34 changes: 34 additions & 0 deletions unit_tests/src/test_random.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <catch2/catch.hpp>

#include "irods/irods_exception.hpp"
#include "irods/irods_random.hpp"

#include <algorithm>
#include <cctype>
#include <string>

TEST_CASE("test_generate_random_alphanumeric_string")
{
SECTION("zero length")
{
CHECK(irods::generate_random_alphanumeric_string(0).empty());
}

SECTION("one character")
{
constexpr std::size_t character_count = 1;
const auto s = irods::generate_random_alphanumeric_string(character_count);
REQUIRE(!s.empty());
CHECK(s.size() == character_count);
CHECK(std::all_of(std::begin(s), std::end(s), [](const auto& c) { return std::isalnum(c); }));
}

SECTION("a lot of characters")
{
constexpr std::size_t character_count = 1'000'000;
const auto s = irods::generate_random_alphanumeric_string(character_count);
REQUIRE(!s.empty());
CHECK(s.size() == character_count);
CHECK(std::all_of(std::begin(s), std::end(s), [](const auto& c) { return std::isalnum(c); }));
}
}
1 change: 1 addition & 0 deletions unit_tests/unit_tests_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"irods_parallel_transfer_engine",
"irods_process_stash",
"irods_query_builder",
"irods_random",
"irods_rcConnect",
"irods_rcTicketAdmin",
"irods_rc_check_auth_credentials",
Expand Down

0 comments on commit 5ca82b7

Please sign in to comment.