GO_FORWARD :: 1; GO_BACKWARD :: -1; BufferIter :: struct { buffer: *Buffer; pos: int; end: int; item: u32; utf8_codepoint_size: int; direction: int; codepoint_index: int; } IsValid :: proc(iter: BufferIter): bool { assert(iter.direction == GO_FORWARD || iter.direction == GO_BACKWARD); result := false; if iter.direction == GO_BACKWARD { result = iter.pos >= iter.end; } else { result = iter.pos < iter.end; } if result { assert(!IsUTF8ContinuationByte(GetChar(iter.buffer, iter.pos))); assert(IsPosInBounds(iter.buffer, iter.pos)); } return result; } Advance :: proc(iter: *BufferIter) { assert(iter.direction == GO_FORWARD || iter.direction == GO_BACKWARD); iter.codepoint_index += 1; if iter.direction == GO_FORWARD { iter.pos += iter.utf8_codepoint_size; } else { iter.pos = AdjustUTF8PosUnsafe(iter.buffer, iter.pos - 1, GO_BACKWARD); } if !IsValid(*iter) return; iter.item = GetUTF32(iter.buffer, iter.pos, &iter.utf8_codepoint_size); } Iterate :: proc(buffer: *Buffer, pos: int, end: int, direction: int = GO_FORWARD): BufferIter { assert(!IsUTF8ContinuationByte(GetChar(buffer, pos))); assert(!IsUTF8ContinuationByte(GetChar(buffer, end))); assert(direction == GO_FORWARD || direction == GO_BACKWARD); result: BufferIter = {buffer = buffer, pos = pos, end = end, direction = direction, codepoint_index = -1}; Advance(&result); return result; }