Erik Schmauss | 9585763 | 2018-03-14 16:13:07 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 2 | /******************************************************************************* |
| 3 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 4 | * Module Name: utstrsuppt - Support functions for string-to-integer conversion |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 5 | * |
| 6 | ******************************************************************************/ |
| 7 | |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 8 | #include <acpi/acpi.h> |
| 9 | #include "accommon.h" |
| 10 | |
| 11 | #define _COMPONENT ACPI_UTILITIES |
| 12 | ACPI_MODULE_NAME("utstrsuppt") |
| 13 | |
| 14 | /* Local prototypes */ |
| 15 | static acpi_status |
| 16 | acpi_ut_insert_digit(u64 *accumulated_value, u32 base, int ascii_digit); |
| 17 | |
| 18 | static acpi_status |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 19 | acpi_ut_strtoul_multiply64(u64 multiplicand, u32 base, u64 *out_product); |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 20 | |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 21 | static acpi_status acpi_ut_strtoul_add64(u64 addend1, u32 digit, u64 *out_sum); |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 22 | |
| 23 | /******************************************************************************* |
| 24 | * |
| 25 | * FUNCTION: acpi_ut_convert_octal_string |
| 26 | * |
| 27 | * PARAMETERS: string - Null terminated input string |
| 28 | * return_value_ptr - Where the converted value is returned |
| 29 | * |
| 30 | * RETURN: Status and 64-bit converted integer |
| 31 | * |
| 32 | * DESCRIPTION: Performs a base 8 conversion of the input string to an |
| 33 | * integer value, either 32 or 64 bits. |
| 34 | * |
| 35 | * NOTE: Maximum 64-bit unsigned octal value is 01777777777777777777777 |
| 36 | * Maximum 32-bit unsigned octal value is 037777777777 |
| 37 | * |
| 38 | ******************************************************************************/ |
| 39 | |
| 40 | acpi_status acpi_ut_convert_octal_string(char *string, u64 *return_value_ptr) |
| 41 | { |
| 42 | u64 accumulated_value = 0; |
| 43 | acpi_status status = AE_OK; |
| 44 | |
| 45 | /* Convert each ASCII byte in the input string */ |
| 46 | |
| 47 | while (*string) { |
| 48 | |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 49 | /* Character must be ASCII 0-7, otherwise terminate with no error */ |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 50 | |
| 51 | if (!(ACPI_IS_OCTAL_DIGIT(*string))) { |
| 52 | break; |
| 53 | } |
| 54 | |
| 55 | /* Convert and insert this octal digit into the accumulator */ |
| 56 | |
| 57 | status = acpi_ut_insert_digit(&accumulated_value, 8, *string); |
| 58 | if (ACPI_FAILURE(status)) { |
| 59 | status = AE_OCTAL_OVERFLOW; |
| 60 | break; |
| 61 | } |
| 62 | |
| 63 | string++; |
| 64 | } |
| 65 | |
| 66 | /* Always return the value that has been accumulated */ |
| 67 | |
| 68 | *return_value_ptr = accumulated_value; |
| 69 | return (status); |
| 70 | } |
| 71 | |
| 72 | /******************************************************************************* |
| 73 | * |
| 74 | * FUNCTION: acpi_ut_convert_decimal_string |
| 75 | * |
| 76 | * PARAMETERS: string - Null terminated input string |
| 77 | * return_value_ptr - Where the converted value is returned |
| 78 | * |
| 79 | * RETURN: Status and 64-bit converted integer |
| 80 | * |
| 81 | * DESCRIPTION: Performs a base 10 conversion of the input string to an |
| 82 | * integer value, either 32 or 64 bits. |
| 83 | * |
| 84 | * NOTE: Maximum 64-bit unsigned decimal value is 18446744073709551615 |
| 85 | * Maximum 32-bit unsigned decimal value is 4294967295 |
| 86 | * |
| 87 | ******************************************************************************/ |
| 88 | |
| 89 | acpi_status acpi_ut_convert_decimal_string(char *string, u64 *return_value_ptr) |
| 90 | { |
| 91 | u64 accumulated_value = 0; |
| 92 | acpi_status status = AE_OK; |
| 93 | |
| 94 | /* Convert each ASCII byte in the input string */ |
| 95 | |
| 96 | while (*string) { |
| 97 | |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 98 | /* Character must be ASCII 0-9, otherwise terminate with no error */ |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 99 | |
| 100 | if (!isdigit(*string)) { |
| 101 | break; |
| 102 | } |
| 103 | |
| 104 | /* Convert and insert this decimal digit into the accumulator */ |
| 105 | |
| 106 | status = acpi_ut_insert_digit(&accumulated_value, 10, *string); |
| 107 | if (ACPI_FAILURE(status)) { |
| 108 | status = AE_DECIMAL_OVERFLOW; |
| 109 | break; |
| 110 | } |
| 111 | |
| 112 | string++; |
| 113 | } |
| 114 | |
| 115 | /* Always return the value that has been accumulated */ |
| 116 | |
| 117 | *return_value_ptr = accumulated_value; |
| 118 | return (status); |
| 119 | } |
| 120 | |
| 121 | /******************************************************************************* |
| 122 | * |
| 123 | * FUNCTION: acpi_ut_convert_hex_string |
| 124 | * |
| 125 | * PARAMETERS: string - Null terminated input string |
| 126 | * return_value_ptr - Where the converted value is returned |
| 127 | * |
| 128 | * RETURN: Status and 64-bit converted integer |
| 129 | * |
| 130 | * DESCRIPTION: Performs a base 16 conversion of the input string to an |
| 131 | * integer value, either 32 or 64 bits. |
| 132 | * |
| 133 | * NOTE: Maximum 64-bit unsigned hex value is 0xFFFFFFFFFFFFFFFF |
| 134 | * Maximum 32-bit unsigned hex value is 0xFFFFFFFF |
| 135 | * |
| 136 | ******************************************************************************/ |
| 137 | |
| 138 | acpi_status acpi_ut_convert_hex_string(char *string, u64 *return_value_ptr) |
| 139 | { |
| 140 | u64 accumulated_value = 0; |
| 141 | acpi_status status = AE_OK; |
| 142 | |
| 143 | /* Convert each ASCII byte in the input string */ |
| 144 | |
| 145 | while (*string) { |
| 146 | |
| 147 | /* Must be ASCII A-F, a-f, or 0-9, otherwise terminate with no error */ |
| 148 | |
| 149 | if (!isxdigit(*string)) { |
| 150 | break; |
| 151 | } |
| 152 | |
| 153 | /* Convert and insert this hex digit into the accumulator */ |
| 154 | |
| 155 | status = acpi_ut_insert_digit(&accumulated_value, 16, *string); |
| 156 | if (ACPI_FAILURE(status)) { |
| 157 | status = AE_HEX_OVERFLOW; |
| 158 | break; |
| 159 | } |
| 160 | |
| 161 | string++; |
| 162 | } |
| 163 | |
| 164 | /* Always return the value that has been accumulated */ |
| 165 | |
| 166 | *return_value_ptr = accumulated_value; |
| 167 | return (status); |
| 168 | } |
| 169 | |
| 170 | /******************************************************************************* |
| 171 | * |
| 172 | * FUNCTION: acpi_ut_remove_leading_zeros |
| 173 | * |
| 174 | * PARAMETERS: string - Pointer to input ASCII string |
| 175 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 176 | * RETURN: Next character after any leading zeros. This character may be |
| 177 | * used by the caller to detect end-of-string. |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 178 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 179 | * DESCRIPTION: Remove any leading zeros in the input string. Return the |
| 180 | * next character after the final ASCII zero to enable the caller |
| 181 | * to check for the end of the string (NULL terminator). |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 182 | * |
| 183 | ******************************************************************************/ |
| 184 | |
| 185 | char acpi_ut_remove_leading_zeros(char **string) |
| 186 | { |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 187 | |
| 188 | while (**string == ACPI_ASCII_ZERO) { |
| 189 | *string += 1; |
| 190 | } |
| 191 | |
| 192 | return (**string); |
| 193 | } |
| 194 | |
| 195 | /******************************************************************************* |
| 196 | * |
Bob Moore | c2e56e5 | 2017-09-20 10:00:49 +0800 | [diff] [blame] | 197 | * FUNCTION: acpi_ut_remove_whitespace |
| 198 | * |
| 199 | * PARAMETERS: string - Pointer to input ASCII string |
| 200 | * |
| 201 | * RETURN: Next character after any whitespace. This character may be |
| 202 | * used by the caller to detect end-of-string. |
| 203 | * |
| 204 | * DESCRIPTION: Remove any leading whitespace in the input string. Return the |
| 205 | * next character after the final ASCII zero to enable the caller |
| 206 | * to check for the end of the string (NULL terminator). |
| 207 | * |
| 208 | ******************************************************************************/ |
| 209 | |
| 210 | char acpi_ut_remove_whitespace(char **string) |
| 211 | { |
| 212 | |
| 213 | while (isspace((u8)**string)) { |
| 214 | *string += 1; |
| 215 | } |
| 216 | |
| 217 | return (**string); |
| 218 | } |
| 219 | |
| 220 | /******************************************************************************* |
| 221 | * |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 222 | * FUNCTION: acpi_ut_detect_hex_prefix |
| 223 | * |
| 224 | * PARAMETERS: string - Pointer to input ASCII string |
| 225 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 226 | * RETURN: TRUE if a "0x" prefix was found at the start of the string |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 227 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 228 | * DESCRIPTION: Detect and remove a hex "0x" prefix |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 229 | * |
| 230 | ******************************************************************************/ |
| 231 | |
| 232 | u8 acpi_ut_detect_hex_prefix(char **string) |
| 233 | { |
Erik Schmauss | 089b2be | 2018-08-10 14:42:57 -0700 | [diff] [blame] | 234 | char *initial_position = *string; |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 235 | |
Erik Schmauss | 089b2be | 2018-08-10 14:42:57 -0700 | [diff] [blame] | 236 | acpi_ut_remove_hex_prefix(string); |
| 237 | if (*string != initial_position) { |
| 238 | return (TRUE); /* String is past leading 0x */ |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 239 | } |
| 240 | |
| 241 | return (FALSE); /* Not a hex string */ |
| 242 | } |
| 243 | |
| 244 | /******************************************************************************* |
| 245 | * |
Erik Schmauss | 089b2be | 2018-08-10 14:42:57 -0700 | [diff] [blame] | 246 | * FUNCTION: acpi_ut_remove_hex_prefix |
| 247 | * |
| 248 | * PARAMETERS: string - Pointer to input ASCII string |
| 249 | * |
| 250 | * RETURN: none |
| 251 | * |
| 252 | * DESCRIPTION: Remove a hex "0x" prefix |
| 253 | * |
| 254 | ******************************************************************************/ |
| 255 | |
| 256 | void acpi_ut_remove_hex_prefix(char **string) |
| 257 | { |
| 258 | if ((**string == ACPI_ASCII_ZERO) && |
| 259 | (tolower((int)*(*string + 1)) == 'x')) { |
| 260 | *string += 2; /* Go past the leading 0x */ |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | /******************************************************************************* |
| 265 | * |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 266 | * FUNCTION: acpi_ut_detect_octal_prefix |
| 267 | * |
| 268 | * PARAMETERS: string - Pointer to input ASCII string |
| 269 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 270 | * RETURN: True if an octal "0" prefix was found at the start of the |
| 271 | * string |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 272 | * |
| 273 | * DESCRIPTION: Detect and remove an octal prefix (zero) |
| 274 | * |
| 275 | ******************************************************************************/ |
| 276 | |
| 277 | u8 acpi_ut_detect_octal_prefix(char **string) |
| 278 | { |
| 279 | |
| 280 | if (**string == ACPI_ASCII_ZERO) { |
| 281 | *string += 1; /* Go past the leading 0 */ |
| 282 | return (TRUE); |
| 283 | } |
| 284 | |
| 285 | return (FALSE); /* Not an octal string */ |
| 286 | } |
| 287 | |
| 288 | /******************************************************************************* |
| 289 | * |
| 290 | * FUNCTION: acpi_ut_insert_digit |
| 291 | * |
| 292 | * PARAMETERS: accumulated_value - Current value of the integer value |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 293 | * accumulator. The new value is |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 294 | * returned here. |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 295 | * base - Radix, either 8/10/16 |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 296 | * ascii_digit - ASCII single digit to be inserted |
| 297 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 298 | * RETURN: Status and result of the convert/insert operation. The only |
| 299 | * possible returned exception code is numeric overflow of |
| 300 | * either the multiply or add conversion operations. |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 301 | * |
| 302 | * DESCRIPTION: Generic conversion and insertion function for all bases: |
| 303 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 304 | * 1) Multiply the current accumulated/converted value by the |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 305 | * base in order to make room for the new character. |
| 306 | * |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 307 | * 2) Convert the new character to binary and add it to the |
| 308 | * current accumulated value. |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 309 | * |
| 310 | * Note: The only possible exception indicates an integer |
| 311 | * overflow (AE_NUMERIC_OVERFLOW) |
| 312 | * |
| 313 | ******************************************************************************/ |
| 314 | |
| 315 | static acpi_status |
| 316 | acpi_ut_insert_digit(u64 *accumulated_value, u32 base, int ascii_digit) |
| 317 | { |
| 318 | acpi_status status; |
| 319 | u64 product; |
| 320 | |
| 321 | /* Make room in the accumulated value for the incoming digit */ |
| 322 | |
| 323 | status = acpi_ut_strtoul_multiply64(*accumulated_value, base, &product); |
| 324 | if (ACPI_FAILURE(status)) { |
| 325 | return (status); |
| 326 | } |
| 327 | |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 328 | /* Add in the new digit, and store the sum to the accumulated value */ |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 329 | |
| 330 | status = |
| 331 | acpi_ut_strtoul_add64(product, |
| 332 | acpi_ut_ascii_char_to_hex(ascii_digit), |
| 333 | accumulated_value); |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 334 | |
Bob Moore | 72a2935 | 2017-09-20 10:00:43 +0800 | [diff] [blame] | 335 | return (status); |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 336 | } |
| 337 | |
| 338 | /******************************************************************************* |
| 339 | * |
| 340 | * FUNCTION: acpi_ut_strtoul_multiply64 |
| 341 | * |
| 342 | * PARAMETERS: multiplicand - Current accumulated converted integer |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 343 | * base - Base/Radix |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 344 | * out_product - Where the product is returned |
| 345 | * |
| 346 | * RETURN: Status and 64-bit product |
| 347 | * |
| 348 | * DESCRIPTION: Multiply two 64-bit values, with checking for 64-bit overflow as |
| 349 | * well as 32-bit overflow if necessary (if the current global |
| 350 | * integer width is 32). |
| 351 | * |
| 352 | ******************************************************************************/ |
| 353 | |
| 354 | static acpi_status |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 355 | acpi_ut_strtoul_multiply64(u64 multiplicand, u32 base, u64 *out_product) |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 356 | { |
Rafael J. Wysocki | 4b688b1 | 2017-12-01 15:26:06 +0100 | [diff] [blame] | 357 | u64 product; |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 358 | u64 quotient; |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 359 | |
| 360 | /* Exit if either operand is zero */ |
| 361 | |
| 362 | *out_product = 0; |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 363 | if (!multiplicand || !base) { |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 364 | return (AE_OK); |
| 365 | } |
| 366 | |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 367 | /* |
| 368 | * Check for 64-bit overflow before the actual multiplication. |
| 369 | * |
| 370 | * Notes: 64-bit division is often not supported on 32-bit platforms |
| 371 | * (it requires a library function), Therefore ACPICA has a local |
| 372 | * 64-bit divide function. Also, Multiplier is currently only used |
| 373 | * as the radix (8/10/16), to the 64/32 divide will always work. |
| 374 | */ |
| 375 | acpi_ut_short_divide(ACPI_UINT64_MAX, base, "ient, NULL); |
| 376 | if (multiplicand > quotient) { |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 377 | return (AE_NUMERIC_OVERFLOW); |
| 378 | } |
| 379 | |
Rafael J. Wysocki | 4b688b1 | 2017-12-01 15:26:06 +0100 | [diff] [blame] | 380 | product = multiplicand * base; |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 381 | |
| 382 | /* Check for 32-bit overflow if necessary */ |
| 383 | |
Rafael J. Wysocki | 4b688b1 | 2017-12-01 15:26:06 +0100 | [diff] [blame] | 384 | if ((acpi_gbl_integer_bit_width == 32) && (product > ACPI_UINT32_MAX)) { |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 385 | return (AE_NUMERIC_OVERFLOW); |
| 386 | } |
| 387 | |
Rafael J. Wysocki | 4b688b1 | 2017-12-01 15:26:06 +0100 | [diff] [blame] | 388 | *out_product = product; |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 389 | return (AE_OK); |
| 390 | } |
| 391 | |
| 392 | /******************************************************************************* |
| 393 | * |
| 394 | * FUNCTION: acpi_ut_strtoul_add64 |
| 395 | * |
| 396 | * PARAMETERS: addend1 - Current accumulated converted integer |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 397 | * digit - New hex value/char |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 398 | * out_sum - Where sum is returned (Accumulator) |
| 399 | * |
| 400 | * RETURN: Status and 64-bit sum |
| 401 | * |
| 402 | * DESCRIPTION: Add two 64-bit values, with checking for 64-bit overflow as |
| 403 | * well as 32-bit overflow if necessary (if the current global |
| 404 | * integer width is 32). |
| 405 | * |
| 406 | ******************************************************************************/ |
| 407 | |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 408 | static acpi_status acpi_ut_strtoul_add64(u64 addend1, u32 digit, u64 *out_sum) |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 409 | { |
| 410 | u64 sum; |
| 411 | |
| 412 | /* Check for 64-bit overflow before the actual addition */ |
| 413 | |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 414 | if ((addend1 > 0) && (digit > (ACPI_UINT64_MAX - addend1))) { |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 415 | return (AE_NUMERIC_OVERFLOW); |
| 416 | } |
| 417 | |
Bob Moore | db53f7f | 2017-11-17 15:42:16 -0800 | [diff] [blame] | 418 | sum = addend1 + digit; |
Bob Moore | fe97d28 | 2017-09-20 10:00:36 +0800 | [diff] [blame] | 419 | |
| 420 | /* Check for 32-bit overflow if necessary */ |
| 421 | |
| 422 | if ((acpi_gbl_integer_bit_width == 32) && (sum > ACPI_UINT32_MAX)) { |
| 423 | return (AE_NUMERIC_OVERFLOW); |
| 424 | } |
| 425 | |
| 426 | *out_sum = sum; |
| 427 | return (AE_OK); |
| 428 | } |