string_util_internal.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_STRINGS_STRING_UTIL_INTERNAL_H_
  5. #define BASE_STRINGS_STRING_UTIL_INTERNAL_H_
  6. #include <algorithm>
  7. #include "base/logging.h"
  8. #include "base/notreached.h"
  9. #include "base/strings/string_piece.h"
  10. #include "base/third_party/icu/icu_utf.h"
  11. namespace base {
  12. namespace internal {
  13. // Used by ReplaceStringPlaceholders to track the position in the string of
  14. // replaced parameters.
  15. struct ReplacementOffset {
  16. ReplacementOffset(uintptr_t parameter, size_t offset)
  17. : parameter(parameter), offset(offset) {}
  18. // Index of the parameter.
  19. uintptr_t parameter;
  20. // Starting position in the string.
  21. size_t offset;
  22. };
  23. static bool CompareParameter(const ReplacementOffset& elem1,
  24. const ReplacementOffset& elem2) {
  25. return elem1.parameter < elem2.parameter;
  26. }
  27. // Assuming that a pointer is the size of a "machine word", then
  28. // uintptr_t is an integer type that is also a machine word.
  29. using MachineWord = uintptr_t;
  30. inline bool IsMachineWordAligned(const void* pointer) {
  31. return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1));
  32. }
  33. template <typename StringType>
  34. StringType ToLowerASCIIImpl(BasicStringPiece<StringType> str) {
  35. StringType ret;
  36. ret.reserve(str.size());
  37. for (size_t i = 0; i < str.size(); i++)
  38. ret.push_back(ToLowerASCII(str[i]));
  39. return ret;
  40. }
  41. template <typename StringType>
  42. StringType ToUpperASCIIImpl(BasicStringPiece<StringType> str) {
  43. StringType ret;
  44. ret.reserve(str.size());
  45. for (size_t i = 0; i < str.size(); i++)
  46. ret.push_back(ToUpperASCII(str[i]));
  47. return ret;
  48. }
  49. template <class StringType>
  50. int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
  51. BasicStringPiece<StringType> b) {
  52. // Find the first characters that aren't equal and compare them. If the end
  53. // of one of the strings is found before a nonequal character, the lengths
  54. // of the strings are compared.
  55. size_t i = 0;
  56. while (i < a.length() && i < b.length()) {
  57. typename StringType::value_type lower_a = ToLowerASCII(a[i]);
  58. typename StringType::value_type lower_b = ToLowerASCII(b[i]);
  59. if (lower_a < lower_b)
  60. return -1;
  61. if (lower_a > lower_b)
  62. return 1;
  63. i++;
  64. }
  65. // End of one string hit before finding a different character. Expect the
  66. // common case to be "strings equal" at this point so check that first.
  67. if (a.length() == b.length())
  68. return 0;
  69. if (a.length() < b.length())
  70. return -1;
  71. return 1;
  72. }
  73. template <typename Str>
  74. TrimPositions TrimStringT(BasicStringPiece<Str> input,
  75. BasicStringPiece<Str> trim_chars,
  76. TrimPositions positions,
  77. Str* output) {
  78. // Find the edges of leading/trailing whitespace as desired. Need to use
  79. // a StringPiece version of input to be able to call find* on it with the
  80. // StringPiece version of trim_chars (normally the trim_chars will be a
  81. // constant so avoid making a copy).
  82. const size_t last_char = input.length() - 1;
  83. const size_t first_good_char =
  84. (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
  85. const size_t last_good_char = (positions & TRIM_TRAILING)
  86. ? input.find_last_not_of(trim_chars)
  87. : last_char;
  88. // When the string was all trimmed, report that we stripped off characters
  89. // from whichever position the caller was interested in. For empty input, we
  90. // stripped no characters, but we still need to clear |output|.
  91. if (input.empty() || first_good_char == Str::npos ||
  92. last_good_char == Str::npos) {
  93. bool input_was_empty = input.empty(); // in case output == &input
  94. output->clear();
  95. return input_was_empty ? TRIM_NONE : positions;
  96. }
  97. // Trim.
  98. output->assign(input.data() + first_good_char,
  99. last_good_char - first_good_char + 1);
  100. // Return where we trimmed from.
  101. return static_cast<TrimPositions>(
  102. (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) |
  103. (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING));
  104. }
  105. template <typename Str>
  106. BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
  107. BasicStringPiece<Str> trim_chars,
  108. TrimPositions positions) {
  109. size_t begin =
  110. (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
  111. size_t end = (positions & TRIM_TRAILING)
  112. ? input.find_last_not_of(trim_chars) + 1
  113. : input.size();
  114. return input.substr(std::min(begin, input.size()), end - begin);
  115. }
  116. template <typename STR>
  117. STR CollapseWhitespaceT(BasicStringPiece<STR> text,
  118. bool trim_sequences_with_line_breaks) {
  119. STR result;
  120. result.resize(text.size());
  121. // Set flags to pretend we're already in a trimmed whitespace sequence, so we
  122. // will trim any leading whitespace.
  123. bool in_whitespace = true;
  124. bool already_trimmed = true;
  125. int chars_written = 0;
  126. for (auto c : text) {
  127. if (IsUnicodeWhitespace(c)) {
  128. if (!in_whitespace) {
  129. // Reduce all whitespace sequences to a single space.
  130. in_whitespace = true;
  131. result[chars_written++] = L' ';
  132. }
  133. if (trim_sequences_with_line_breaks && !already_trimmed &&
  134. ((c == '\n') || (c == '\r'))) {
  135. // Whitespace sequences containing CR or LF are eliminated entirely.
  136. already_trimmed = true;
  137. --chars_written;
  138. }
  139. } else {
  140. // Non-whitespace characters are copied straight across.
  141. in_whitespace = false;
  142. already_trimmed = false;
  143. result[chars_written++] = c;
  144. }
  145. }
  146. if (in_whitespace && !already_trimmed) {
  147. // Any trailing whitespace is eliminated.
  148. --chars_written;
  149. }
  150. result.resize(chars_written);
  151. return result;
  152. }
  153. template <class Char>
  154. bool DoIsStringASCII(const Char* characters, size_t length) {
  155. // Bitmasks to detect non ASCII characters for character sizes of 8, 16 and 32
  156. // bits.
  157. constexpr MachineWord NonASCIIMasks[] = {
  158. 0, MachineWord(0x8080808080808080ULL), MachineWord(0xFF80FF80FF80FF80ULL),
  159. 0, MachineWord(0xFFFFFF80FFFFFF80ULL),
  160. };
  161. if (!length)
  162. return true;
  163. constexpr MachineWord non_ascii_bit_mask = NonASCIIMasks[sizeof(Char)];
  164. static_assert(non_ascii_bit_mask, "Error: Invalid Mask");
  165. MachineWord all_char_bits = 0;
  166. const Char* end = characters + length;
  167. // Prologue: align the input.
  168. while (!IsMachineWordAligned(characters) && characters < end)
  169. all_char_bits |= *characters++;
  170. if (all_char_bits & non_ascii_bit_mask)
  171. return false;
  172. // Compare the values of CPU word size.
  173. constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
  174. constexpr int batch_count = 16;
  175. while (characters <= end - batch_count * chars_per_word) {
  176. all_char_bits = 0;
  177. for (int i = 0; i < batch_count; ++i) {
  178. all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
  179. characters += chars_per_word;
  180. }
  181. if (all_char_bits & non_ascii_bit_mask)
  182. return false;
  183. }
  184. // Process the remaining words.
  185. all_char_bits = 0;
  186. while (characters <= end - chars_per_word) {
  187. all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
  188. characters += chars_per_word;
  189. }
  190. // Process the remaining bytes.
  191. while (characters < end)
  192. all_char_bits |= *characters++;
  193. return !(all_char_bits & non_ascii_bit_mask);
  194. }
  195. template <bool (*Validator)(uint32_t)>
  196. inline static bool DoIsStringUTF8(StringPiece str) {
  197. const char* src = str.data();
  198. int32_t src_len = static_cast<int32_t>(str.length());
  199. int32_t char_index = 0;
  200. while (char_index < src_len) {
  201. int32_t code_point;
  202. CBU8_NEXT(src, char_index, src_len, code_point);
  203. if (!Validator(code_point))
  204. return false;
  205. }
  206. return true;
  207. }
  208. // Implementation note: Normally this function will be called with a hardcoded
  209. // constant for the lowercase_ascii parameter. Constructing a StringPiece from
  210. // a C constant requires running strlen, so the result will be two passes
  211. // through the buffers, one to file the length of lowercase_ascii, and one to
  212. // compare each letter.
  213. //
  214. // This function could have taken a const char* to avoid this and only do one
  215. // pass through the string. But the strlen is faster than the case-insensitive
  216. // compares and lets us early-exit in the case that the strings are different
  217. // lengths (will often be the case for non-matches). So whether one approach or
  218. // the other will be faster depends on the case.
  219. //
  220. // The hardcoded strings are typically very short so it doesn't matter, and the
  221. // string piece gives additional flexibility for the caller (doesn't have to be
  222. // null terminated) so we choose the StringPiece route.
  223. template <typename Str>
  224. static inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,
  225. StringPiece lowercase_ascii) {
  226. return std::equal(
  227. str.begin(), str.end(), lowercase_ascii.begin(), lowercase_ascii.end(),
  228. [](auto lhs, auto rhs) { return ToLowerASCII(lhs) == rhs; });
  229. }
  230. template <typename Str>
  231. bool StartsWithT(BasicStringPiece<Str> str,
  232. BasicStringPiece<Str> search_for,
  233. CompareCase case_sensitivity) {
  234. if (search_for.size() > str.size())
  235. return false;
  236. BasicStringPiece<Str> source = str.substr(0, search_for.size());
  237. switch (case_sensitivity) {
  238. case CompareCase::SENSITIVE:
  239. return source == search_for;
  240. case CompareCase::INSENSITIVE_ASCII:
  241. return std::equal(
  242. search_for.begin(), search_for.end(), source.begin(),
  243. CaseInsensitiveCompareASCII<typename Str::value_type>());
  244. default:
  245. NOTREACHED();
  246. return false;
  247. }
  248. }
  249. template <typename Str>
  250. bool EndsWithT(BasicStringPiece<Str> str,
  251. BasicStringPiece<Str> search_for,
  252. CompareCase case_sensitivity) {
  253. if (search_for.size() > str.size())
  254. return false;
  255. BasicStringPiece<Str> source =
  256. str.substr(str.size() - search_for.size(), search_for.size());
  257. switch (case_sensitivity) {
  258. case CompareCase::SENSITIVE:
  259. return source == search_for;
  260. case CompareCase::INSENSITIVE_ASCII:
  261. return std::equal(
  262. source.begin(), source.end(), search_for.begin(),
  263. CaseInsensitiveCompareASCII<typename Str::value_type>());
  264. default:
  265. NOTREACHED();
  266. return false;
  267. }
  268. }
  269. // A Matcher for DoReplaceMatchesAfterOffset() that matches substrings.
  270. template <class StringType>
  271. struct SubstringMatcher {
  272. BasicStringPiece<StringType> find_this;
  273. size_t Find(const StringType& input, size_t pos) {
  274. return input.find(find_this.data(), pos, find_this.length());
  275. }
  276. size_t MatchSize() { return find_this.length(); }
  277. };
  278. // A Matcher for DoReplaceMatchesAfterOffset() that matches single characters.
  279. template <class StringType>
  280. struct CharacterMatcher {
  281. BasicStringPiece<StringType> find_any_of_these;
  282. size_t Find(const StringType& input, size_t pos) {
  283. return input.find_first_of(find_any_of_these.data(), pos,
  284. find_any_of_these.length());
  285. }
  286. constexpr size_t MatchSize() { return 1; }
  287. };
  288. enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST };
  289. // Runs in O(n) time in the length of |str|, and transforms the string without
  290. // reallocating when possible. Returns |true| if any matches were found.
  291. //
  292. // This is parameterized on a |Matcher| traits type, so that it can be the
  293. // implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset().
  294. template <class StringType, class Matcher>
  295. bool DoReplaceMatchesAfterOffset(StringType* str,
  296. size_t initial_offset,
  297. Matcher matcher,
  298. BasicStringPiece<StringType> replace_with,
  299. ReplaceType replace_type) {
  300. using CharTraits = typename StringType::traits_type;
  301. const size_t find_length = matcher.MatchSize();
  302. if (!find_length)
  303. return false;
  304. // If the find string doesn't appear, there's nothing to do.
  305. size_t first_match = matcher.Find(*str, initial_offset);
  306. if (first_match == StringType::npos)
  307. return false;
  308. // If we're only replacing one instance, there's no need to do anything
  309. // complicated.
  310. const size_t replace_length = replace_with.length();
  311. if (replace_type == ReplaceType::REPLACE_FIRST) {
  312. str->replace(first_match, find_length, replace_with.data(), replace_length);
  313. return true;
  314. }
  315. // If the find and replace strings are the same length, we can simply use
  316. // replace() on each instance, and finish the entire operation in O(n) time.
  317. if (find_length == replace_length) {
  318. auto* buffer = &((*str)[0]);
  319. for (size_t offset = first_match; offset != StringType::npos;
  320. offset = matcher.Find(*str, offset + replace_length)) {
  321. CharTraits::copy(buffer + offset, replace_with.data(), replace_length);
  322. }
  323. return true;
  324. }
  325. // Since the find and replace strings aren't the same length, a loop like the
  326. // one above would be O(n^2) in the worst case, as replace() will shift the
  327. // entire remaining string each time. We need to be more clever to keep things
  328. // O(n).
  329. //
  330. // When the string is being shortened, it's possible to just shift the matches
  331. // down in one pass while finding, and truncate the length at the end of the
  332. // search.
  333. //
  334. // If the string is being lengthened, more work is required. The strategy used
  335. // here is to make two find() passes through the string. The first pass counts
  336. // the number of matches to determine the new size. The second pass will
  337. // either construct the new string into a new buffer (if the existing buffer
  338. // lacked capacity), or else -- if there is room -- create a region of scratch
  339. // space after |first_match| by shifting the tail of the string to a higher
  340. // index, and doing in-place moves from the tail to lower indices thereafter.
  341. size_t str_length = str->length();
  342. size_t expansion = 0;
  343. if (replace_length > find_length) {
  344. // This operation lengthens the string; determine the new length by counting
  345. // matches.
  346. const size_t expansion_per_match = (replace_length - find_length);
  347. size_t num_matches = 0;
  348. for (size_t match = first_match; match != StringType::npos;
  349. match = matcher.Find(*str, match + find_length)) {
  350. expansion += expansion_per_match;
  351. ++num_matches;
  352. }
  353. const size_t final_length = str_length + expansion;
  354. if (str->capacity() < final_length) {
  355. // If we'd have to allocate a new buffer to grow the string, build the
  356. // result directly into the new allocation via append().
  357. StringType src(str->get_allocator());
  358. str->swap(src);
  359. str->reserve(final_length);
  360. size_t pos = 0;
  361. for (size_t match = first_match;; match = matcher.Find(src, pos)) {
  362. str->append(src, pos, match - pos);
  363. str->append(replace_with.data(), replace_length);
  364. pos = match + find_length;
  365. // A mid-loop test/break enables skipping the final Find() call; the
  366. // number of matches is known, so don't search past the last one.
  367. if (!--num_matches)
  368. break;
  369. }
  370. // Handle substring after the final match.
  371. str->append(src, pos, str_length - pos);
  372. return true;
  373. }
  374. // Prepare for the copy/move loop below -- expand the string to its final
  375. // size by shifting the data after the first match to the end of the resized
  376. // string.
  377. size_t shift_src = first_match + find_length;
  378. size_t shift_dst = shift_src + expansion;
  379. // Big |expansion| factors (relative to |str_length|) require padding up to
  380. // |shift_dst|.
  381. if (shift_dst > str_length)
  382. str->resize(shift_dst);
  383. str->replace(shift_dst, str_length - shift_src, *str, shift_src,
  384. str_length - shift_src);
  385. str_length = final_length;
  386. }
  387. // We can alternate replacement and move operations. This won't overwrite the
  388. // unsearched region of the string so long as |write_offset| <= |read_offset|;
  389. // that condition is always satisfied because:
  390. //
  391. // (a) If the string is being shortened, |expansion| is zero and
  392. // |write_offset| grows slower than |read_offset|.
  393. //
  394. // (b) If the string is being lengthened, |write_offset| grows faster than
  395. // |read_offset|, but |expansion| is big enough so that |write_offset|
  396. // will only catch up to |read_offset| at the point of the last match.
  397. auto* buffer = &((*str)[0]);
  398. size_t write_offset = first_match;
  399. size_t read_offset = first_match + expansion;
  400. do {
  401. if (replace_length) {
  402. CharTraits::copy(buffer + write_offset, replace_with.data(),
  403. replace_length);
  404. write_offset += replace_length;
  405. }
  406. read_offset += find_length;
  407. // min() clamps StringType::npos (the largest unsigned value) to str_length.
  408. size_t match = std::min(matcher.Find(*str, read_offset), str_length);
  409. size_t length = match - read_offset;
  410. if (length) {
  411. CharTraits::move(buffer + write_offset, buffer + read_offset, length);
  412. write_offset += length;
  413. read_offset += length;
  414. }
  415. } while (read_offset < str_length);
  416. // If we're shortening the string, truncate it now.
  417. str->resize(write_offset);
  418. return true;
  419. }
  420. template <class StringType>
  421. bool ReplaceCharsT(BasicStringPiece<StringType> input,
  422. BasicStringPiece<StringType> find_any_of_these,
  423. BasicStringPiece<StringType> replace_with,
  424. StringType* output) {
  425. // Commonly, this is called with output and input being the same string; in
  426. // that case, skip the copy.
  427. if (input.data() != output->data() || input.size() != output->size())
  428. output->assign(input.data(), input.size());
  429. return DoReplaceMatchesAfterOffset(
  430. output, 0, CharacterMatcher<StringType>{find_any_of_these}, replace_with,
  431. ReplaceType::REPLACE_ALL);
  432. }
  433. template <class string_type>
  434. inline typename string_type::value_type* WriteIntoT(string_type* str,
  435. size_t length_with_null) {
  436. DCHECK_GE(length_with_null, 1u);
  437. str->reserve(length_with_null);
  438. str->resize(length_with_null - 1);
  439. return &((*str)[0]);
  440. }
  441. // Generic version for all JoinString overloads. |list_type| must be a sequence
  442. // (base::span or std::initializer_list) of strings/StringPieces (std::string,
  443. // string16, StringPiece or StringPiece16). |string_type| is either std::string
  444. // or string16.
  445. template <typename list_type, typename string_type>
  446. static string_type JoinStringT(list_type parts,
  447. BasicStringPiece<string_type> sep) {
  448. if (base::empty(parts))
  449. return string_type();
  450. // Pre-allocate the eventual size of the string. Start with the size of all of
  451. // the separators (note that this *assumes* parts.size() > 0).
  452. size_t total_size = (parts.size() - 1) * sep.size();
  453. for (const auto& part : parts)
  454. total_size += part.size();
  455. string_type result;
  456. result.reserve(total_size);
  457. auto iter = parts.begin();
  458. DCHECK(iter != parts.end());
  459. result.append(iter->data(), iter->size());
  460. ++iter;
  461. for (; iter != parts.end(); ++iter) {
  462. result.append(sep.data(), sep.size());
  463. result.append(iter->data(), iter->size());
  464. }
  465. // Sanity-check that we pre-allocated correctly.
  466. DCHECK_EQ(total_size, result.size());
  467. return result;
  468. }
  469. template <class StringType>
  470. StringType DoReplaceStringPlaceholders(
  471. BasicStringPiece<StringType> format_string,
  472. const std::vector<StringType>& subst,
  473. std::vector<size_t>* offsets) {
  474. size_t substitutions = subst.size();
  475. DCHECK_LT(substitutions, 10U);
  476. size_t sub_length = 0;
  477. for (const auto& cur : subst)
  478. sub_length += cur.length();
  479. StringType formatted;
  480. formatted.reserve(format_string.length() + sub_length);
  481. std::vector<ReplacementOffset> r_offsets;
  482. for (auto i = format_string.begin(); i != format_string.end(); ++i) {
  483. if ('$' == *i) {
  484. if (i + 1 != format_string.end()) {
  485. ++i;
  486. if ('$' == *i) {
  487. while (i != format_string.end() && '$' == *i) {
  488. formatted.push_back('$');
  489. ++i;
  490. }
  491. --i;
  492. } else {
  493. if (*i < '1' || *i > '9') {
  494. DLOG(ERROR) << "Invalid placeholder: $" << *i;
  495. continue;
  496. }
  497. uintptr_t index = *i - '1';
  498. if (offsets) {
  499. ReplacementOffset r_offset(index,
  500. static_cast<int>(formatted.size()));
  501. r_offsets.insert(
  502. std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
  503. &CompareParameter),
  504. r_offset);
  505. }
  506. if (index < substitutions)
  507. formatted.append(subst.at(index));
  508. }
  509. }
  510. } else {
  511. formatted.push_back(*i);
  512. }
  513. }
  514. if (offsets) {
  515. for (const auto& cur : r_offsets)
  516. offsets->push_back(cur.offset);
  517. }
  518. return formatted;
  519. }
  520. // The following code is compatible with the OpenBSD lcpy interface. See:
  521. // http://www.gratisoft.us/todd/papers/strlcpy.html
  522. // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
  523. template <typename CHAR>
  524. size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
  525. for (size_t i = 0; i < dst_size; ++i) {
  526. if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL.
  527. return i;
  528. }
  529. // We were left off at dst_size. We over copied 1 byte. Null terminate.
  530. if (dst_size != 0)
  531. dst[dst_size - 1] = 0;
  532. // Count the rest of the |src|, and return it's length in characters.
  533. while (src[dst_size])
  534. ++dst_size;
  535. return dst_size;
  536. }
  537. } // namespace internal
  538. } // namespace base
  539. #endif // BASE_STRINGS_STRING_UTIL_INTERNAL_H_