Repository: denoland/deno
Pull request: fix(compile): correct buffered reading of assets and files

Closes #27006

Changed files:
filename: cli/standalone/virtual_fs.rs
patch: @@ -634,7 +634,7 @@ impl FileBackedVfsFile {
   }
 
   fn read_to_buf(&self, buf: &mut [u8]) -> FsResult<usize> {
-    let pos = {
+    let read_pos = {
       let mut pos = self.pos.lock();
       let read_pos = *pos;
       // advance the position due to the read
@@ -643,12 +643,12 @@ impl FileBackedVfsFile {
     };
     self
       .vfs
-      .read_file(&self.file, pos, buf)
+      .read_file(&self.file, read_pos, buf)
       .map_err(|err| err.into())
   }
 
   fn read_to_end(&self) -> FsResult<Vec<u8>> {
-    let pos = {
+    let read_pos = {
       let mut pos = self.pos.lock();
       let read_pos = *pos;
       // todo(dsherret): should this always set it to the end of the file?
@@ -658,12 +658,12 @@ impl FileBackedVfsFile {
       }
       read_pos
     };
-    if pos > self.file.len {
+    if read_pos > self.file.len {
       return Ok(Vec::new());
     }
-    let size = (self.file.len - pos) as usize;
+    let size = (self.file.len - read_pos) as usize;
     let mut buf = vec![0; size];
-    self.vfs.read_file(&self.file, pos, &mut buf)?;
+    self.vfs.read_file(&self.file, read_pos, &mut buf)?;
     Ok(buf)
   }
 }
@@ -893,8 +893,9 @@ impl FileBackedVfs {
     buf: &mut [u8],
   ) -> std::io::Result<usize> {
     let read_range = self.get_read_range(file, pos, buf.len() as u64)?;
-    buf.copy_from_slice(&self.vfs_data[read_range]);
-    Ok(buf.len())
+    let read_len = read_range.len();
+    buf[..read_len].copy_from_slice(&self.vfs_data[read_range]);
+    Ok(read_len)
   }
 
   fn get_read_range(
@@ -903,15 +904,15 @@ impl FileBackedVfs {
     pos: u64,
     len: u64,
   ) -> std::io::Result<Range<usize>> {
-    let data = &self.vfs_data;
-    let start = self.fs_root.start_file_offset + file.offset + pos;
-    let end = start + len;
-    if end > data.len() as u64 {
+    if pos > file.len {
       return Err(std::io::Error::new(
         std::io::ErrorKind::UnexpectedEof,
         "unexpected EOF",
       ));
     }
+    let file_offset = self.fs_root.start_file_offset + file.offset;
+    let start = file_offset + pos;
+    let end = file_offset + std::cmp::min(pos + len, file.len);
     Ok(start as usize..end as usize)
   }
 

filename: tests/specs/compile/include/buffered_reads/__test__.jsonc
patch: @@ -0,0 +1,27 @@
+{
+  "tempDir": true,
+  "steps": [{
+    "args": "run -A setup.js",
+    "output": "[WILDCARD]"
+  }, {
+    "if": "unix",
+    "args": "compile --allow-read=data --include data --output main main.ts",
+    "output": "[WILDCARD]"
+  }, {
+    "if": "unix",
+    "commandName": "./main",
+    "args": [],
+    "output": "[WILDCARD]",
+    "exitCode": 0
+  }, {
+    "if": "windows",
+    "args": "compile --allow-read=data --include data --output main.exe main.ts",
+    "output": "[WILDCARD]"
+  }, {
+    "if": "windows",
+    "commandName": "./main.exe",
+    "args": [],
+    "output": "[WILDCARD]",
+    "exitCode": 0
+  }]
+}

filename: tests/specs/compile/include/buffered_reads/main.ts
patch: @@ -0,0 +1,57 @@
+// buffer larger than file
+{
+  using file = Deno.openSync(import.meta.dirname + "/data/1.txt");
+  const data = new Uint8Array(13);
+  const len = file.readSync(data);
+  if (len !== 13) {
+    throw new Error("Unexpected read length");
+  }
+  if (file.readSync(new Uint8Array(1024)) !== null) {
+    throw new Error("Unexpected.");
+  }
+  const textData = new TextDecoder().decode(data);
+  if (textData !== "Hello, world!") {
+    throw new Error("Unexpected file data (1): " + textData);
+  }
+}
+
+// buffer smaller than file
+{
+  using file = Deno.openSync(import.meta.dirname + "/data/1.txt");
+  const finalData = new Uint8Array(13);
+  const data = new Uint8Array(2);
+  let pos = 0;
+  while (true) {
+    const len = file.readSync(data);
+    if (len === 0 || len == null) {
+      break;
+    }
+    finalData.set(data.subarray(0, len), pos);
+    pos += len;
+  }
+  const textData = new TextDecoder().decode(finalData);
+  if (textData !== "Hello, world!") {
+    throw new Error("Unexpected file data (2): " + textData);
+  }
+}
+
+// large amount of data, small reads
+{
+  const bytes = new Uint8Array((1024 ** 2) * 20);
+  using file = Deno.openSync(import.meta.dirname + "/data/2.dat");
+  const buffer = new Uint8Array(2);
+  let pos = 0;
+  while (true) {
+    const len = file.readSync(buffer);
+    if (len === 0 || len == null) {
+      break;
+    }
+    bytes.set(buffer.subarray(0, len), pos);
+    pos += len;
+  }
+  for (let i = 0; i < bytes.length; i++) {
+    if (bytes[i] !== i % 256) {
+      throw new Error("Unexpected data.");
+    }
+  }
+}

filename: tests/specs/compile/include/buffered_reads/setup.js
patch: @@ -0,0 +1,7 @@
+Deno.mkdirSync("data");
+Deno.writeTextFileSync("data/1.txt", "Hello, world!");
+const bytes = new Uint8Array((1024 ** 2) * 20);
+for (let i = 0; i < bytes.length; i++) {
+  bytes[i] = i % 256;
+}
+Deno.writeFileSync("data/2.dat", bytes);