-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add a function to validate topic names (#92)
* add a function to valid topic names * fix typo in macro name * link gtest tests against pthread on Linux * use custom isalnum that isn't affected by locale * relocate files out of impl folders * fix includes and remove _impl from names * refactor macros to all start with RMW_TOPIC_ * make linkage internal for isalnum_no_locale * cpplint fixup
- Loading branch information
Showing
8 changed files
with
523 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright 2017 Open Source Robotics Foundation, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#ifndef RMW__VALIDATE_TOPIC_NAME_H_ | ||
#define RMW__VALIDATE_TOPIC_NAME_H_ | ||
|
||
#if __cplusplus | ||
extern "C" | ||
{ | ||
#endif | ||
|
||
#include "rmw/macros.h" | ||
#include "rmw/types.h" | ||
|
||
#define RMW_TOPIC_VALID 0 | ||
#define RMW_TOPIC_INVALID_IS_EMPTY_STRING 1 | ||
#define RMW_TOPIC_INVALID_NOT_ABSOLUTE 2 | ||
#define RMW_TOPIC_INVALID_ENDS_WITH_FORWARD_SLASH 3 | ||
#define RMW_TOPIC_INVALID_CONTAINS_UNALLOWED_CHARACTERS 4 | ||
#define RMW_TOPIC_INVALID_CONTAINS_REPEATED_FORWARD_SLASH 5 | ||
#define RMW_TOPIC_INVALID_NAME_TOKEN_STARTS_WITH_NUMBER 6 | ||
#define RMW_TOPIC_INVALID_TOO_LONG 7 | ||
|
||
#define RMW_TOPIC_MAX_NAME_LENGTH 255 /* impl constraint */ - 8 /* reserved for prefixes */ | ||
|
||
/// Determine if a given fully qualified topic name is valid. | ||
/** Validity of a FQN for topic is determined based on rules defined here: | ||
* | ||
* http://design.ros2.org/articles/topic_and_service_names.html | ||
* | ||
* Note that this function expects any URL suffixes as described in the above | ||
* document to have already been removed. | ||
* | ||
* If either the C string or validation_result pointer are null, then | ||
* `RMW_RET_INVALID_ARGUMENT` will be returned. | ||
* The topic_name should be a valid, null-terminated C string. | ||
* The validation_result int pointer should point to valid memory so a result | ||
* can be stored in it as an output variable. | ||
* The invalid_index size_t pointer should point to valid memory so in the | ||
* event of a validation error, the location in the input string can be stored | ||
* therein. | ||
* | ||
* The invalid_index will not be assigned a value if the topic is valid. | ||
* | ||
* The int which validation_result points to will have a one of a few possible | ||
* results values (defined with macros) stored into it: | ||
* | ||
* - RMW_VALID_TOPIC | ||
* - RMW_TOPIC_INVALID_IS_EMPTY_STRING | ||
* - RMW_TOPIC_INVALID_NOT_ABSOLUTE | ||
* - RMW_TOPIC_INVALID_ENDS_WITH_FORWARD_SLASH | ||
* - RMW_TOPIC_INVALID_CONTAINS_UNALLOWED_CHARACTERS | ||
* - RMW_TOPIC_INVALID_CONTAINS_REPEATED_FORWARD_SLASH | ||
* - RMW_TOPIC_INVALID_NAME_TOKEN_STARTS_WITH_NUMBER | ||
* - RMW_TOPIC_INVALID_TOO_LONG | ||
* | ||
* The result value can be converted to a description with the | ||
* rmw_validation_result_string() function. | ||
* | ||
* The `RMW_TOPIC_INVALID_TOO_LONG` is guaranteed to be checked last, such | ||
* that if you get that result, then you can assume all other checks succeeded. | ||
* This is done so that the length limit can be treated as a warning rather | ||
* than an error if desired. | ||
* | ||
* \param[in] topic_name topic name to be validated | ||
* \param[out] validation_result int in which the result of the check is stored | ||
* \param[out] invalid_index size_t index of the input string where an error occurred | ||
* \returns `RMW_RET_OK` on successfully running the check, or | ||
* \returns `RMW_RET_INVALID_ARGUMENT` on invalid parameters, or | ||
* \returns `RMW_RET_ERROR` when an unspecified error occurs. | ||
*/ | ||
RMW_PUBLIC | ||
RMW_WARN_UNUSED | ||
rmw_ret_t | ||
rmw_validate_topic_name( | ||
const char * topic_name, | ||
int * validation_result, | ||
size_t * invalid_index); | ||
|
||
/// Return a string to describe the validation result, or NULL if unknown. | ||
RMW_PUBLIC | ||
RMW_WARN_UNUSED | ||
const char * | ||
rmw_validation_result_string(int validation_result); | ||
|
||
#if __cplusplus | ||
} | ||
#endif | ||
|
||
#endif // RMW__VALIDATE_TOPIC_NAME_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright 2017 Open Source Robotics Foundation, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#ifndef ISALNUM_NO_LOCALE_H_ | ||
#define ISALNUM_NO_LOCALE_H_ | ||
|
||
#if __cplusplus | ||
extern "C" | ||
{ | ||
#endif | ||
|
||
/// Custom isalnum which is not affected by locale. | ||
static inline | ||
bool | ||
isalnum_no_locale(char c) | ||
{ | ||
// if in '0', ..., '9', then ok | ||
if (c >= 0x30 /*0*/ && c <= 0x39 /*9*/) { | ||
return true; | ||
} | ||
// if in 'A', ..., 'Z', then ok | ||
if (c >= 0x41 /*A*/ && c <= 0x5a /*Z*/) { | ||
return true; | ||
} | ||
// if in 'a', ..., 'z', then ok | ||
if (c >= 0x61 /*a*/ && c <= 0x7a /*z*/) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
#if __cplusplus | ||
} | ||
#endif | ||
|
||
#endif // ISALNUM_NO_LOCALE_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// Copyright 2017 Open Source Robotics Foundation, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include <rmw/validate_topic_name.h> | ||
|
||
#include <ctype.h> | ||
#include <string.h> | ||
|
||
#include "./isalnum_no_locale.h" | ||
|
||
rmw_ret_t | ||
rmw_validate_topic_name( | ||
const char * topic_name, | ||
int * validation_result, | ||
size_t * invalid_index) | ||
{ | ||
if (!topic_name) { | ||
return RMW_RET_INVALID_ARGUMENT; | ||
} | ||
if (!validation_result) { | ||
return RMW_RET_INVALID_ARGUMENT; | ||
} | ||
if (!invalid_index) { | ||
return RMW_RET_INVALID_ARGUMENT; | ||
} | ||
size_t topic_name_length = strlen(topic_name); | ||
if (topic_name_length == 0) { | ||
*validation_result = RMW_TOPIC_INVALID_IS_EMPTY_STRING; | ||
*invalid_index = 0; | ||
return RMW_RET_OK; | ||
} | ||
if (topic_name[0] != '/') { | ||
*validation_result = RMW_TOPIC_INVALID_NOT_ABSOLUTE; | ||
*invalid_index = 0; | ||
return RMW_RET_OK; | ||
} | ||
// note topic_name_length is >= 1 at this point | ||
if (topic_name[topic_name_length - 1] == '/') { | ||
// catches both "/foo/" and "/" | ||
*validation_result = RMW_TOPIC_INVALID_ENDS_WITH_FORWARD_SLASH; | ||
*invalid_index = topic_name_length - 1; | ||
return RMW_RET_OK; | ||
} | ||
// check for unallowed characters | ||
for (size_t i = 0; i < topic_name_length; ++i) { | ||
if (isalnum_no_locale(topic_name[i])) { | ||
// if it is an alpha numeric character, i.e. [0-9|A-Z|a-z], continue | ||
continue; | ||
} else if (topic_name[i] == '_') { | ||
// if it is an underscore, continue | ||
continue; | ||
} else if (topic_name[i] == '/') { | ||
// if it is a forward slash, continue | ||
continue; | ||
} else { | ||
// if it is none of these, then it is an unallowed character in a FQN topic name | ||
*validation_result = RMW_TOPIC_INVALID_CONTAINS_UNALLOWED_CHARACTERS; | ||
*invalid_index = i; | ||
return RMW_RET_OK; | ||
} | ||
} | ||
// check for double '/' and tokens that start with a number | ||
for (size_t i = 0; i < topic_name_length; ++i) { | ||
if (i == topic_name_length - 1) { | ||
// if this is the last character, then nothing to check | ||
continue; | ||
} | ||
// past this point, assuming i+1 is a valid index | ||
if (topic_name[i] == '/') { | ||
if (topic_name[i + 1] == '/') { | ||
*validation_result = RMW_TOPIC_INVALID_CONTAINS_REPEATED_FORWARD_SLASH; | ||
*invalid_index = i + 1; | ||
return RMW_RET_OK; | ||
} | ||
if (isdigit(topic_name[i + 1]) != 0) { | ||
// this is the case where a '/' if followed by a number, i.e. [0-9] | ||
*validation_result = RMW_TOPIC_INVALID_NAME_TOKEN_STARTS_WITH_NUMBER; | ||
*invalid_index = i + 1; | ||
return RMW_RET_OK; | ||
} | ||
} | ||
} | ||
// check if the topic name is too long last, since it might be a soft invalidation | ||
if (topic_name_length > RMW_TOPIC_MAX_NAME_LENGTH) { | ||
*validation_result = RMW_TOPIC_INVALID_TOO_LONG; | ||
*invalid_index = RMW_TOPIC_MAX_NAME_LENGTH - 1; | ||
return RMW_RET_OK; | ||
} | ||
// everything was ok, set result to valid topic, avoid setting invalid_index, and return | ||
*validation_result = RMW_TOPIC_VALID; | ||
return RMW_RET_OK; | ||
} | ||
|
||
const char * | ||
rmw_validation_result_string(int validation_result) | ||
{ | ||
switch (validation_result) { | ||
case RMW_TOPIC_VALID: | ||
return "topic name is valid"; | ||
case RMW_TOPIC_INVALID_IS_EMPTY_STRING: | ||
return "topic name must not be empty"; | ||
case RMW_TOPIC_INVALID_NOT_ABSOLUTE: | ||
return "topic name must be absolute, it must lead with a '/'"; | ||
case RMW_TOPIC_INVALID_ENDS_WITH_FORWARD_SLASH: | ||
return "topic name must not end with a '/'"; | ||
case RMW_TOPIC_INVALID_CONTAINS_UNALLOWED_CHARACTERS: | ||
return "topic name must not contain characters other than alphanumerics, '_', or '/'"; | ||
case RMW_TOPIC_INVALID_CONTAINS_REPEATED_FORWARD_SLASH: | ||
return "topic name must not contain repeated '/'"; | ||
case RMW_TOPIC_INVALID_NAME_TOKEN_STARTS_WITH_NUMBER: | ||
return "topic name must not have a token that starts with a number"; | ||
case RMW_TOPIC_INVALID_TOO_LONG: | ||
return "topic length should not exceed '" RMW_STRINGIFY(RMW_TOPIC_MAX_NAME_LENGTH) "'"; | ||
default: | ||
return NULL; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright 2017 Open Source Robotics Foundation, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include <string> | ||
|
||
#include "gmock/gmock.h" | ||
|
||
#include "../src/isalnum_no_locale.h" | ||
|
||
TEST(test_isalnum_no_locale, valid_characters_ok) { | ||
std::string valid("0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"); | ||
for (auto & c : valid) { | ||
ASSERT_TRUE(isalnum_no_locale(c)); | ||
} | ||
} | ||
|
||
TEST(test_isalnum_no_locale, invalid_characters_fail) { | ||
std::string invalid("/" /*0-9*/ ":;<=>?@" /*A-Z*/ "[\\]^_`" /*a-z*/); | ||
for (auto & c : invalid) { | ||
ASSERT_FALSE(isalnum_no_locale(c)); | ||
} | ||
} |
Oops, something went wrong.