If you're fine with an extra copy, I think the UTF8PROC_CHARBOUND flag does this in https://github.com/JuliaStrings/utf8proc ?
edit* Yikes https://github.com/JuliaStrings/utf8proc/blob/master/utf8pro... Still this is a good template to maybe cut a (safter) state-based iterator out of their logic...