Your state machine code looks good. What is unusual is that it unescapes the QString in place, but that works; it won't scale well with long strings though, because each call to remove() presumably moves all the remainder of the string, giving a quadratic time complexity in the length of the string. If this becomes a performance bottleneck of your program, consider changing the interface of your decoder so that it can be fed the encoded string in chunks, and outputs chunks of the decoded string (see QTextDecoder to get an idea).

Here is an optimization for your current code: instead of accumulating digits in nrStr before converting them to a uint, you could progressively compute the number nr: replace nrStr.clear() with nr = 0, and replace nrStr.append(ch) with nr = nr * 10 + ch.toLatin1() - '0'.