From 07158d73706fb0096cb6b08ec915b625e51f7ecf Mon Sep 17 00:00:00 2001
From: Yoshiya Hinosawa <stibium121@gmail.com>
Date: Thu, 29 Apr 2021 17:56:54 +0900
Subject: [PATCH 1/4] feat(std/node): add writeBuffer of internal binding fs
 module

---
 node/internal_binding/README.md  |  8 ++++
 node/internal_binding/fs.ts      | 74 ++++++++++++++++++++++++++++++++
 node/internal_binding/fs_test.ts | 52 ++++++++++++++++++++++
 3 files changed, 134 insertions(+)
 create mode 100644 node/internal_binding/README.md
 create mode 100644 node/internal_binding/fs.ts
 create mode 100644 node/internal_binding/fs_test.ts

diff --git a/node/internal_binding/README.md b/node/internal_binding/README.md
new file mode 100644
index 000000000000..11692d1eba87
--- /dev/null
+++ b/node/internal_binding/README.md
@@ -0,0 +1,8 @@
+The modules in this directory implement (simulate) C++ bindings inplemented in
+src/ directory of Node.js
+
+These bindings are created in the node.js source code by using
+`NODE_MODULE_CONTEXT_AWARE_INTERNAL`.
+
+See:
+https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/src/README.md
diff --git a/node/internal_binding/fs.ts b/node/internal_binding/fs.ts
new file mode 100644
index 000000000000..0aae7c98d4d5
--- /dev/null
+++ b/node/internal_binding/fs.ts
@@ -0,0 +1,74 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+import { assert } from "../../testing/asserts.ts";
+// This module implements functions in https://github.com/nodejs/node/blob/master/src/node_file.cc
+
+/**
+ * Write to the given file from the given buffer asynchronously.
+ *
+ * Implements async part of WriteBuffer in src/node_file.cc
+ * See: https://github.com/nodejs/node/blob/e9ed113/src/node_file.cc#L1818
+ *
+ * @param fs file descriptor
+ * @param buffer the data to write
+ * @param offset where in the buffer to start from
+ * @param length how much to write
+ * @param position if integer, position to write at in the file. if null, write from the current position
+ * @param callback callback function
+ */
+export function writeBuffer(
+  fd: number,
+  buffer: Uint8Array,
+  offset: number,
+  length: number,
+  position: number | null,
+  callback?: (err: Error, n: number) => void,
+): void {
+  throw new Error("unimplemented!");
+}
+
+/**
+ * Write to the given file from the given buffer synchronously.
+ *
+ * Implements sync part of WriteBuffer in src/node_file.cc
+ * See: https://github.com/nodejs/node/blob/e9ed113/src/node_file.cc#L1818
+ *
+ * @param fs file descriptor
+ * @param buffer the data to write
+ * @param offset where in the buffer to start from
+ * @param length how much to write
+ * @param position if integer, position to write at in the file. if null, write from the current position
+ * @param context context object for passing error number
+ */
+export function writeBufferSync(
+  fd: number,
+  buffer: Uint8Array,
+  offset: number,
+  length: number,
+  position: number | null,
+  ctx: { errno?: number },
+) {
+  assert(offset >= 0, "offset should be greater or equal to 0");
+  assert(
+    offset + length <= buffer.byteLength,
+    `buffer doesn't have enough data: byteLength = ${buffer.byteLength}, offset + length = ${offset +
+      length}`,
+  );
+  if (position) {
+    Deno.seekSync(fd, position, Deno.SeekMode.Current);
+  }
+  const subarray = buffer.subarray(offset, offset + length);
+  try {
+    return Deno.writeSync(fd, subarray);
+  } catch (e) {
+    ctx.errno = extractOsErrorNumberFromErrorMessage(e);
+    return 0;
+  }
+}
+
+function extractOsErrorNumberFromErrorMessage(e: Error): number {
+  const match = e.message.match(/\(os error (\d+)\)/);
+  if (match) {
+    return +match[1];
+  }
+  return 255; // Unknown error
+}
diff --git a/node/internal_binding/fs_test.ts b/node/internal_binding/fs_test.ts
new file mode 100644
index 000000000000..f1c12f85a069
--- /dev/null
+++ b/node/internal_binding/fs_test.ts
@@ -0,0 +1,52 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+import { writeBufferSync } from "./fs.ts";
+import { assertEquals } from "../../testing/asserts.ts";
+
+Deno.test("[node/internal_binding/fs] writeBufferSync", async () => {
+  const tempfile = await Deno.makeTempFile();
+  await Deno.writeTextFile(tempfile, "01234567890123456789");
+
+  const file = await Deno.open(tempfile, { write: true });
+  await Deno.seek(file.rid, 5, Deno.SeekMode.Start);
+
+  try {
+    const ctx: { errno?: number } = {};
+    const bytesWritten = writeBufferSync(
+      file.rid,
+      new Uint8Array([65, 66, 67, 68, 69] /* abcde */),
+      1,
+      3,
+      5,
+      ctx,
+    );
+
+    assertEquals(await Deno.readTextFile(tempfile), "0123456789BCD3456789");
+    assertEquals(bytesWritten, 3);
+    assertEquals(typeof ctx.errno, "undefined");
+  } finally {
+    Deno.close(file.rid);
+    Deno.remove(tempfile);
+  }
+});
+Deno.test("[node/internal_binding/fs] writeBufferSync", async () => {
+  const tempfile = await Deno.makeTempFile();
+  await Deno.writeTextFile(tempfile, "01234567890123456789");
+  const file = await Deno.open(tempfile, { read: true, write: false });
+
+  try {
+    const ctx: { errno?: number } = {};
+    const bytesWritten = writeBufferSync(
+      file.rid,
+      new Uint8Array([65, 66, 67, 68, 69] /* abcde */),
+      1,
+      3,
+      5,
+      ctx,
+    );
+    assertEquals(bytesWritten, 0);
+    assertEquals(ctx.errno, 9);
+  } finally {
+    Deno.close(file.rid);
+    Deno.remove(tempfile);
+  }
+});

From 285f154063cab9370176708988edd13b4d527ed6 Mon Sep 17 00:00:00 2001
From: Yoshiya Hinosawa <stibium121@gmail.com>
Date: Thu, 29 Apr 2021 18:08:08 +0900
Subject: [PATCH 2/4] fix: fix lint

---
 node/internal_binding/fs.ts | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/node/internal_binding/fs.ts b/node/internal_binding/fs.ts
index 0aae7c98d4d5..a66bbafcebb1 100644
--- a/node/internal_binding/fs.ts
+++ b/node/internal_binding/fs.ts
@@ -16,12 +16,12 @@ import { assert } from "../../testing/asserts.ts";
  * @param callback callback function
  */
 export function writeBuffer(
-  fd: number,
-  buffer: Uint8Array,
-  offset: number,
-  length: number,
-  position: number | null,
-  callback?: (err: Error, n: number) => void,
+  _fd: number,
+  _buffer: Uint8Array,
+  _offset: number,
+  _length: number,
+  _position: number | null,
+  _callback?: (err: Error, n: number) => void,
 ): void {
   throw new Error("unimplemented!");
 }

From d5b960b4c5c2bd2471e874303e80470f4a96aec5 Mon Sep 17 00:00:00 2001
From: Yoshiya Hinosawa <stibium121@gmail.com>
Date: Fri, 30 Apr 2021 15:37:02 +0900
Subject: [PATCH 3/4] fix: fix errno in windows

---
 node/internal_binding/fs_test.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/node/internal_binding/fs_test.ts b/node/internal_binding/fs_test.ts
index f1c12f85a069..297a3efa6794 100644
--- a/node/internal_binding/fs_test.ts
+++ b/node/internal_binding/fs_test.ts
@@ -44,7 +44,11 @@ Deno.test("[node/internal_binding/fs] writeBufferSync", async () => {
       ctx,
     );
     assertEquals(bytesWritten, 0);
-    assertEquals(ctx.errno, 9);
+    if (Deno.build.os === "windows") {
+      assertEquals(ctx.errno, 5); // Access is denied
+    } else {
+      assertEquals(ctx.errno, 9); // Bad file descriptor
+    }
   } finally {
     Deno.close(file.rid);
     Deno.remove(tempfile);

From caba10e984651490a8ca51d7973915eebdb83174 Mon Sep 17 00:00:00 2001
From: Yoshiya Hinosawa <stibium121@gmail.com>
Date: Fri, 30 Apr 2021 15:52:27 +0900
Subject: [PATCH 4/4] fix: fix Deno.remove call

---
 node/internal_binding/fs_test.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/node/internal_binding/fs_test.ts b/node/internal_binding/fs_test.ts
index 297a3efa6794..41f0ac9460b2 100644
--- a/node/internal_binding/fs_test.ts
+++ b/node/internal_binding/fs_test.ts
@@ -25,7 +25,7 @@ Deno.test("[node/internal_binding/fs] writeBufferSync", async () => {
     assertEquals(typeof ctx.errno, "undefined");
   } finally {
     Deno.close(file.rid);
-    Deno.remove(tempfile);
+    await Deno.remove(tempfile);
   }
 });
 Deno.test("[node/internal_binding/fs] writeBufferSync", async () => {
@@ -51,6 +51,6 @@ Deno.test("[node/internal_binding/fs] writeBufferSync", async () => {
     }
   } finally {
     Deno.close(file.rid);
-    Deno.remove(tempfile);
+    await Deno.remove(tempfile);
   }
 });