Iterates through a source string given the provided input UConverter specific to the encoding for that string. Calls a provided callback for each codepoint consumed. Provides the callback with the codepoint and the number of bytes consumed from the input string to produce it. If there are invalid encoding loci in the source string, they will be provided as a 0xFFFD codepoint to the callback, unles
| 118 | // callback: function(UChar32 codepoint, int num_bytes_consumed_from_source_str, |
| 119 | // bool fatal_format_error) |
| 120 | void IterateUnicodeString(const string& str, UConverter* converter, |
| 121 | std::function<void(UChar32, int, bool)> callback) { |
| 122 | const char* source = str.data(); |
| 123 | const char* limit = str.data() + str.length(); |
| 124 | UErrorCode status = U_ZERO_ERROR; |
| 125 | |
| 126 | UConverterToUCallback oldAction = nullptr; |
| 127 | const void* oldContext = nullptr; |
| 128 | bool format_error = false; |
| 129 | |
| 130 | // Subtle. You can't make a function pointer from a std::function. :-( |
| 131 | // Instead, we pass the boolean pointer as the "context" object. |
| 132 | ucnv_setToUCallBack(converter, unicode_error_callback, &format_error, |
| 133 | &oldAction, &oldContext, &status); |
| 134 | if (U_FAILURE(status)) { |
| 135 | LOG(ERROR) << "Could not set unicode error callback on converter"; |
| 136 | return; |
| 137 | } |
| 138 | |
| 139 | while (source < limit) { |
| 140 | const char* source_pre_fetch = source; |
| 141 | // Note: ucnv_getNextUChar returns 0xFFFD on an encoding error. |
| 142 | UChar32 next_char = ucnv_getNextUChar(converter, &source, limit, &status); |
| 143 | if (U_FAILURE(status)) { |
| 144 | source = limit; |
| 145 | } |
| 146 | int bytes_consumed = source - source_pre_fetch; |
| 147 | callback(next_char, bytes_consumed, format_error); |
| 148 | format_error = false; |
| 149 | } |
| 150 | |
| 151 | ucnv_setToUCallBack(converter, oldAction, oldContext, nullptr, nullptr, |
| 152 | &status); |
| 153 | } |
| 154 | |
| 155 | // Lifecycle wrapper for UConverter making it easier to use with thread_local. |
| 156 | // TODO(gbillock): Consider whether to use the higher-level convert API and |