1 /** 2 * Copyright © 2025 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "json_parser_utils.hpp" 18 19 #include <charconv> 20 #include <regex> 21 22 namespace phosphor::power::json_parser_utils 23 { 24 25 const std::map<std::string, std::string> NO_VARIABLES{}; 26 27 static std::regex VARIABLE_REGEX{R"(\$\{([A-Za-z0-9_]+)\})"}; 28 29 uint8_t parseBitPosition(const nlohmann::json& element, 30 const std::map<std::string, std::string>& variables) 31 { 32 int value = parseInteger(element, variables); 33 if ((value < 0) || (value > 7)) 34 { 35 throw std::invalid_argument{"Element is not a bit position"}; 36 } 37 return static_cast<uint8_t>(value); 38 } 39 40 uint8_t parseBitValue(const nlohmann::json& element, 41 const std::map<std::string, std::string>& variables) 42 { 43 int value = parseInteger(element, variables); 44 if ((value < 0) || (value > 1)) 45 { 46 throw std::invalid_argument{"Element is not a bit value"}; 47 } 48 return static_cast<uint8_t>(value); 49 } 50 51 bool parseBoolean(const nlohmann::json& element, 52 const std::map<std::string, std::string>& variables) 53 { 54 if (element.is_boolean()) 55 { 56 return element.get<bool>(); 57 } 58 59 if (element.is_string() && !variables.empty()) 60 { 61 std::string value = parseString(element, true, variables); 62 if (value == "true") 63 { 64 return true; 65 } 66 else if (value == "false") 67 { 68 return false; 69 } 70 } 71 72 throw std::invalid_argument{"Element is not a boolean"}; 73 } 74 75 double parseDouble(const nlohmann::json& element, 76 const std::map<std::string, std::string>& variables) 77 { 78 if (element.is_number()) 79 { 80 return element.get<double>(); 81 } 82 83 if (element.is_string() && !variables.empty()) 84 { 85 std::string strValue = parseString(element, true, variables); 86 const char* first = strValue.data(); 87 const char* last = strValue.data() + strValue.size(); 88 double value; 89 auto [ptr, ec] = std::from_chars(first, last, value); 90 if ((ptr == last) && (ec == std::errc())) 91 { 92 return value; 93 } 94 } 95 96 throw std::invalid_argument{"Element is not a double"}; 97 } 98 99 uint8_t parseHexByte(const nlohmann::json& element, 100 const std::map<std::string, std::string>& variables) 101 { 102 std::string value = parseString(element, true, variables); 103 bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) && 104 (value.size() < 5) && 105 (value.find_first_not_of("0123456789abcdefABCDEF", 2) == 106 std::string::npos); 107 if (!isHex) 108 { 109 throw std::invalid_argument{"Element is not hexadecimal string"}; 110 } 111 return static_cast<uint8_t>(std::stoul(value, nullptr, 0)); 112 } 113 114 std::vector<uint8_t> parseHexByteArray( 115 const nlohmann::json& element, 116 const std::map<std::string, std::string>& variables) 117 { 118 verifyIsArray(element); 119 std::vector<uint8_t> values; 120 for (auto& valueElement : element) 121 { 122 values.emplace_back(parseHexByte(valueElement, variables)); 123 } 124 return values; 125 } 126 127 int8_t parseInt8(const nlohmann::json& element, 128 const std::map<std::string, std::string>& variables) 129 { 130 int value = parseInteger(element, variables); 131 if ((value < INT8_MIN) || (value > INT8_MAX)) 132 { 133 throw std::invalid_argument{"Element is not an 8-bit signed integer"}; 134 } 135 return static_cast<int8_t>(value); 136 } 137 138 int parseInteger(const nlohmann::json& element, 139 const std::map<std::string, std::string>& variables) 140 { 141 if (element.is_number_integer()) 142 { 143 return element.get<int>(); 144 } 145 146 if (element.is_string() && !variables.empty()) 147 { 148 std::string strValue = parseString(element, true, variables); 149 const char* first = strValue.data(); 150 const char* last = strValue.data() + strValue.size(); 151 int value; 152 auto [ptr, ec] = std::from_chars(first, last, value); 153 if ((ptr == last) && (ec == std::errc())) 154 { 155 return value; 156 } 157 } 158 159 throw std::invalid_argument{"Element is not an integer"}; 160 } 161 162 std::string parseString(const nlohmann::json& element, bool isEmptyValid, 163 const std::map<std::string, std::string>& variables) 164 { 165 if (!element.is_string()) 166 { 167 throw std::invalid_argument{"Element is not a string"}; 168 } 169 std::string value = element.get<std::string>(); 170 internal::expandVariables(value, variables); 171 if (value.empty() && !isEmptyValid) 172 { 173 throw std::invalid_argument{"Element contains an empty string"}; 174 } 175 return value; 176 } 177 178 uint8_t parseUint8(const nlohmann::json& element, 179 const std::map<std::string, std::string>& variables) 180 { 181 int value = parseInteger(element, variables); 182 if ((value < 0) || (value > UINT8_MAX)) 183 { 184 throw std::invalid_argument{"Element is not an 8-bit unsigned integer"}; 185 } 186 return static_cast<uint8_t>(value); 187 } 188 189 unsigned int parseUnsignedInteger( 190 const nlohmann::json& element, 191 const std::map<std::string, std::string>& variables) 192 { 193 int value = parseInteger(element, variables); 194 if (value < 0) 195 { 196 throw std::invalid_argument{"Element is not an unsigned integer"}; 197 } 198 return static_cast<unsigned int>(value); 199 } 200 201 namespace internal 202 { 203 204 void expandVariables(std::string& value, 205 const std::map<std::string, std::string>& variables) 206 { 207 if (variables.empty()) 208 { 209 return; 210 } 211 212 std::smatch results; 213 while (std::regex_search(value, results, VARIABLE_REGEX)) 214 { 215 if (results.size() != 2) 216 { 217 throw std::runtime_error{ 218 "Unexpected regular expression match result while parsing string"}; 219 } 220 const std::string& variable = results[1]; 221 auto it = variables.find(variable); 222 if (it == variables.end()) 223 { 224 throw std::invalid_argument{"Undefined variable: " + variable}; 225 } 226 value.replace(results.position(0), results.length(0), it->second); 227 } 228 } 229 230 } // namespace internal 231 232 } // namespace phosphor::power::json_parser_utils 233