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 18, 2023
1 parent 66e4401 commit 97978f8
Show file tree
Hide file tree
Showing 6 changed files with 105 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 = static_cast<char>(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 @@ -46,6 +46,7 @@ set(
filesystem
fixed_buffer_resource
fully_qualified_username
generate_random_alphanumeric_string
get_delay_rule_info
get_file_descriptor_info
hierarchy_parser
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set(IRODS_TEST_TARGET irods_generate_random_alphanumeric_string)

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

set(IRODS_TEST_INCLUDE_PATH ${IRODS_EXTERNALS_FULLPATH_BOOST}/include
${IRODS_EXTERNALS_FULLPATH_FMT}/include)

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

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

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

TEST_CASE("length of 0 results in an empty string")
{
CHECK(irods::generate_random_alphanumeric_string(0).empty());
}

TEST_CASE("length of 1 effectively calls generate_random_alphanumeric_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); }));
}

TEST_CASE("only alphanumeric characters appear in string of statistically significant length")
{
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 @@ -16,6 +16,7 @@
"irods_filesystem",
"irods_fixed_buffer_resource",
"irods_fully_qualified_username",
"irods_generate_random_alphanumeric_string",
"irods_get_delay_rule_info",
"irods_get_file_descriptor_info",
"irods_hierarchy_parser",
Expand Down

0 comments on commit 97978f8

Please sign in to comment.