diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index a06b23367e6da..5501133318cd7 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -318,6 +318,35 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { }) } + /// Reads bytes until a `0x0000` is encountered. Will error if the end of the allocation + /// is reached before a `0x0000` is found. + /// + /// Most likely, you want to call `Memory::read_wide_str` instead of this method. + pub fn read_wide_str( + &self, + cx: &impl HasDataLayout, + ptr : Pointer + ) -> InterpResult<'tcx, &[u8]> + { + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); + let offset = ptr.offset.bytes() as usize; + // The iterator below yields pairs of adjacent bytes, in order to find 0x0000. + Ok(match + self.bytes[offset..].iter().step_by(2) + .zip(self.bytes[(offset+1)..].iter().step_by(2)) + .position(|(&l, &r)| l == 0 && r == 0) { + Some(size) => { + let size_with_null = Size::from_bytes((size + 2) as u64); + // Go through `get_bytes` for checks and AllocationExtra hooks. + // We read the null, so we include it in the request, but we want it removed + // from the result, so we do subslicing. + &self.get_bytes(cx, ptr, size_with_null)?[..size] + } + // This includes the case where `offset` is out-of-bounds to begin with. + None => throw_unsup!(UnterminatedCString(ptr.erase_tag())), + }) + } + /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a /// relocation. If `allow_ptr_and_undef` is `false`, also enforces that the memory in the /// given range contains neither relocations nor undef bytes. diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index cb676821fd438..39ad6edef24d3 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -792,6 +792,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { self.get_raw(ptr.alloc_id)?.read_c_str(self, ptr) } + /// Reads a 0x0000-terminated sequence of bytes from memory. Returns them as a slice. + /// Needed for reading wide-strings in Windows-OS + /// + /// Performs appropriate bounds checks. + pub fn read_wide_str(&self, ptr: Scalar) -> InterpResult<'tcx, &[u8]> { + let ptr = self.force_ptr(ptr)?; // We need to read at least 1 byte, so we *need* a ptr. + self.get_raw(ptr.alloc_id)?.read_wide_str(self, ptr) + } + /// Writes the given stream of bytes into memory. /// /// Performs appropriate bounds checks.