If I rewrote the loop like that, wouldn't I sometimes have multiple position values in scope, and wouldn't that be confusing? e.g. when reading 3 chars during percent-decoding there would be 3 positions.

The ideal (IMO) would be to use a recursive function to do the processing, but I'd need different functions to handle the different parse states, and unfortunately mutually recursive function aren't tail-call optimised by the compiler or JVM.