diff --git a/server/main.wasm b/server/build.wasm
similarity index 100%
rename from server/main.wasm
rename to server/build.wasm
diff --git a/server/index.html b/server/index.html
index c9acc26..1132633 100644
--- a/server/index.html
+++ b/server/index.html
@@ -1,13 +1,105 @@
+ The WebAssembly Go Playground
+
+
+
+
+
+
+
-
+
+
+
The WebAssembly Go Playground
+
+
+
+
+
+
+
+
+
diff --git a/server/jquery-linedtextarea.js b/server/jquery-linedtextarea.js
new file mode 100644
index 0000000..4431e95
--- /dev/null
+++ b/server/jquery-linedtextarea.js
@@ -0,0 +1,50 @@
+/**
+ * Adapted from jQuery Lined Textarea Plugin
+ * http://alan.blog-city.com/jquerylinedtextarea.htm
+ *
+ * Released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ */
+(function($) {
+ $.fn.linedtextarea = function() {
+ /*
+ * Helper function to make sure the line numbers are always kept up to
+ * the current system
+ */
+ var fillOutLines = function(linesDiv, h, lineNo) {
+ while (linesDiv.height() < h) {
+ linesDiv.append("
" + lineNo + "
");
+ lineNo++;
+ }
+ return lineNo;
+ };
+
+ return this.each(function() {
+ var lineNo = 1;
+ var textarea = $(this);
+
+ /* Wrap the text area in the elements we need */
+ textarea.wrap("");
+ textarea.width("97%");
+ textarea.parent().prepend("");
+ var linesDiv = textarea.parent().find(".lines");
+
+ var scroll = function(tn) {
+ var domTextArea = $(this)[0];
+ var scrollTop = domTextArea.scrollTop;
+ var clientHeight = domTextArea.clientHeight;
+ linesDiv.css({
+ 'margin-top' : (-scrollTop) + "px"
+ });
+ lineNo = fillOutLines(linesDiv, scrollTop + clientHeight,
+ lineNo);
+ };
+ /* React to the scroll event */
+ textarea.scroll(scroll);
+ $(window).resize(function() { textarea.scroll(); });
+ /* We call scroll once to add the line numbers */
+ textarea.scroll();
+ });
+ };
+
+})(jQuery);
diff --git a/server/playground.js b/server/playground.js
new file mode 100644
index 0000000..0ab0b2f
--- /dev/null
+++ b/server/playground.js
@@ -0,0 +1,544 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+In the absence of any formal way to specify interfaces in JavaScript,
+here's a skeleton implementation of a playground transport.
+
+ function Transport() {
+ // Set up any transport state (eg, make a websocket connection).
+ return {
+ Run: function(body, output, options) {
+ // Compile and run the program 'body' with 'options'.
+ // Call the 'output' callback to display program output.
+ return {
+ Kill: function() {
+ // Kill the running program.
+ }
+ };
+ }
+ };
+ }
+
+ // The output callback is called multiple times, and each time it is
+ // passed an object of this form.
+ var write = {
+ Kind: 'string', // 'start', 'stdout', 'stderr', 'end'
+ Body: 'string' // content of write or end status message
+ }
+
+ // The first call must be of Kind 'start' with no body.
+ // Subsequent calls may be of Kind 'stdout' or 'stderr'
+ // and must have a non-null Body string.
+ // The final call should be of Kind 'end' with an optional
+ // Body string, signifying a failure ("killed", for example).
+
+ // The output callback must be of this form.
+ // See PlaygroundOutput (below) for an implementation.
+ function outputCallback(write) {
+ }
+*/
+
+// HTTPTransport is the default transport.
+// enableVet enables running vet if a program was compiled and ran successfully.
+// If vet returned any errors, display them before the output of a program.
+function HTTPTransport(enableVet) {
+ 'use strict';
+
+ function playback(output, data) {
+ // Backwards compatibility: default values do not affect the output.
+ var events = data.Events || [];
+ var errors = data.Errors || "";
+ var status = data.Status || 0;
+ var isTest = data.IsTest || false;
+ var testsFailed = data.TestsFailed || 0;
+
+ var timeout;
+ output({Kind: 'start'});
+ function next() {
+ if (!events || events.length === 0) {
+ if (isTest) {
+ if (testsFailed > 0) {
+ output({Kind: 'system', Body: '\n'+testsFailed+' test'+(testsFailed>1?'s':'')+' failed.'});
+ } else {
+ output({Kind: 'system', Body: '\nAll tests passed.'});
+ }
+ } else {
+ if (status > 0) {
+ output({Kind: 'end', Body: 'status ' + status + '.'});
+ } else {
+ if (errors !== "") {
+ // errors are displayed only in the case of timeout.
+ output({Kind: 'end', Body: errors + '.'});
+ } else {
+ output({Kind: 'end'});
+ }
+ }
+ }
+ return;
+ }
+ var e = events.shift();
+ if (e.Delay === 0) {
+ output({Kind: e.Kind, Body: e.Message});
+ next();
+ return;
+ }
+ timeout = setTimeout(function() {
+ output({Kind: e.Kind, Body: e.Message});
+ next();
+ }, e.Delay / 1000000);
+ }
+ next();
+ return {
+ Stop: function() {
+ clearTimeout(timeout);
+ }
+ };
+ }
+
+ function error(output, msg) {
+ output({Kind: 'start'});
+ output({Kind: 'stderr', Body: msg});
+ output({Kind: 'end'});
+ }
+
+ function buildFailed(output, msg) {
+ output({Kind: 'start'});
+ output({Kind: 'stderr', Body: msg});
+ output({Kind: 'system', Body: '\nGo build failed.'});
+ }
+
+ var seq = 0;
+ return {
+ Run: function(body, output, options) {
+ seq++;
+ var cur = seq;
+ var playing;
+ $.ajax('/compile', {
+ type: 'POST',
+ data: {'version': 2, 'body': body, 'withVet': enableVet},
+ dataType: 'json',
+ success: function(data) {
+ if (seq != cur) return;
+ if (!data) return;
+ if (playing != null) playing.Stop();
+ if (data.Errors) {
+ if (data.Errors === 'process took too long') {
+ // Playback the output that was captured before the timeout.
+ playing = playback(output, data);
+ } else {
+ buildFailed(output, data.Errors);
+ }
+ return;
+ }
+ if (!data.Events) {
+ data.Events = [];
+ }
+ if (data.VetErrors) {
+ // Inject errors from the vet as the first events in the output.
+ data.Events.unshift({Message: 'Go vet exited.\n\n', Kind: 'system', Delay: 0});
+ data.Events.unshift({Message: data.VetErrors, Kind: 'stderr', Delay: 0});
+ }
+
+ if (!enableVet || data.VetOK || data.VetErrors) {
+ playing = playback(output, data);
+ return;
+ }
+
+ // In case the server support doesn't support
+ // compile+vet in same request signaled by the
+ // 'withVet' parameter above, also try the old way.
+ // TODO: remove this when it falls out of use.
+ // It is 2019-05-13 now.
+ $.ajax("/vet", {
+ data: {"body": body},
+ type: "POST",
+ dataType: "json",
+ success: function(dataVet) {
+ if (dataVet.Errors) {
+ // inject errors from the vet as the first events in the output
+ data.Events.unshift({Message: 'Go vet exited.\n\n', Kind: 'system', Delay: 0});
+ data.Events.unshift({Message: dataVet.Errors, Kind: 'stderr', Delay: 0});
+ }
+ playing = playback(output, data);
+ },
+ error: function() {
+ playing = playback(output, data);
+ }
+ });
+ },
+ error: function() {
+ error(output, 'Error communicating with remote server.');
+ }
+ });
+ return {
+ Kill: function() {
+ if (playing != null) playing.Stop();
+ output({Kind: 'end', Body: 'killed'});
+ }
+ };
+ }
+ };
+}
+
+function SocketTransport() {
+ 'use strict';
+
+ var id = 0;
+ var outputs = {};
+ var started = {};
+ var websocket;
+ if (window.location.protocol == "http:") {
+ websocket = new WebSocket('ws://' + window.location.host + '/socket');
+ } else if (window.location.protocol == "https:") {
+ websocket = new WebSocket('wss://' + window.location.host + '/socket');
+ }
+
+ websocket.onclose = function() {
+ console.log('websocket connection closed');
+ };
+
+ websocket.onmessage = function(e) {
+ var m = JSON.parse(e.data);
+ var output = outputs[m.Id];
+ if (output === null)
+ return;
+ if (!started[m.Id]) {
+ output({Kind: 'start'});
+ started[m.Id] = true;
+ }
+ output({Kind: m.Kind, Body: m.Body});
+ };
+
+ function send(m) {
+ websocket.send(JSON.stringify(m));
+ }
+
+ return {
+ Run: function(body, output, options) {
+ var thisID = id+'';
+ id++;
+ outputs[thisID] = output;
+ send({Id: thisID, Kind: 'run', Body: body, Options: options});
+ return {
+ Kill: function() {
+ send({Id: thisID, Kind: 'kill'});
+ }
+ };
+ }
+ };
+}
+
+function PlaygroundOutput(el) {
+ 'use strict';
+
+ return function(write) {
+ if (write.Kind == 'start') {
+ el.innerHTML = '';
+ return;
+ }
+
+ var cl = 'system';
+ if (write.Kind == 'stdout' || write.Kind == 'stderr')
+ cl = write.Kind;
+
+ var m = write.Body;
+ if (write.Kind == 'end') {
+ m = '\nProgram exited' + (m?(': '+m):'.');
+ }
+
+ if (m.indexOf('IMAGE:') === 0) {
+ // TODO(adg): buffer all writes before creating image
+ var url = 'data:image/png;base64,' + m.substr(6);
+ var img = document.createElement('img');
+ img.src = url;
+ el.appendChild(img);
+ return;
+ }
+
+ // ^L clears the screen.
+ var s = m.split('\x0c');
+ if (s.length > 1) {
+ el.innerHTML = '';
+ m = s.pop();
+ }
+
+ m = m.replace(/&/g, '&');
+ m = m.replace(//g, '>');
+
+ var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight;
+
+ var span = document.createElement('span');
+ span.className = cl;
+ span.innerHTML = m;
+ el.appendChild(span);
+
+ if (needScroll)
+ el.scrollTop = el.scrollHeight - el.offsetHeight;
+ };
+}
+
+(function() {
+ function lineHighlight(error) {
+ var regex = /prog.go:([0-9]+)/g;
+ var r = regex.exec(error);
+ while (r) {
+ $(".lines div").eq(r[1]-1).addClass("lineerror");
+ r = regex.exec(error);
+ }
+ }
+ function highlightOutput(wrappedOutput) {
+ return function(write) {
+ if (write.Body) lineHighlight(write.Body);
+ wrappedOutput(write);
+ };
+ }
+ function lineClear() {
+ $(".lineerror").removeClass("lineerror");
+ }
+
+ // opts is an object with these keys
+ // codeEl - code editor element
+ // outputEl - program output element
+ // runEl - run button element
+ // fmtEl - fmt button element (optional)
+ // fmtImportEl - fmt "imports" checkbox element (optional)
+ // shareEl - share button element (optional)
+ // shareURLEl - share URL text input element (optional)
+ // shareRedirect - base URL to redirect to on share (optional)
+ // toysEl - toys select element (optional)
+ // enableHistory - enable using HTML5 history API (optional)
+ // transport - playground transport to use (default is HTTPTransport)
+ // enableShortcuts - whether to enable shortcuts (Ctrl+S/Cmd+S to save) (default is false)
+ // enableVet - enable running vet and displaying its errors
+ function playground(opts) {
+ var code = $(opts.codeEl);
+ var transport = opts['transport'] || new HTTPTransport(opts['enableVet']);
+ var running;
+
+ // autoindent helpers.
+ function insertTabs(n) {
+ // find the selection start and end
+ var start = code[0].selectionStart;
+ var end = code[0].selectionEnd;
+ // split the textarea content into two, and insert n tabs
+ var v = code[0].value;
+ var u = v.substr(0, start);
+ for (var i=0; i 0) {
+ curpos--;
+ if (el.value[curpos] == "\t") {
+ tabs++;
+ } else if (tabs > 0 || el.value[curpos] == "\n") {
+ break;
+ }
+ }
+ setTimeout(function() {
+ insertTabs(tabs);
+ }, 1);
+ }
+
+ // NOTE(cbro): e is a jQuery event, not a DOM event.
+ function handleSaveShortcut(e) {
+ if (e.isDefaultPrevented()) return false;
+ if (!e.metaKey && !e.ctrlKey) return false;
+ if (e.key != "S" && e.key != "s") return false;
+
+ e.preventDefault();
+
+ // Share and save
+ share(function(url) {
+ window.location.href = url + ".go?download=true";
+ });
+
+ return true;
+ }
+
+ function keyHandler(e) {
+ if (opts.enableShortcuts && handleSaveShortcut(e)) return;
+
+ if (e.keyCode == 9 && !e.ctrlKey) { // tab (but not ctrl-tab)
+ insertTabs(1);
+ e.preventDefault();
+ return false;
+ }
+ if (e.keyCode == 13) { // enter
+ if (e.shiftKey) { // +shift
+ run();
+ e.preventDefault();
+ return false;
+ } if (e.ctrlKey) { // +control
+ fmt();
+ e.preventDefault();
+ } else {
+ autoindent(e.target);
+ }
+ }
+ return true;
+ }
+ code.unbind('keydown').bind('keydown', keyHandler);
+ var outdiv = $(opts.outputEl).empty();
+ var output = $('').appendTo(outdiv);
+
+ function body() {
+ return $(opts.codeEl).val();
+ }
+ function setBody(text) {
+ $(opts.codeEl).val(text);
+ }
+ function origin(href) {
+ return (""+href).split("/").slice(0, 3).join("/");
+ }
+
+ var pushedEmpty = (window.location.pathname == "/");
+ function inputChanged() {
+ if (pushedEmpty) {
+ return;
+ }
+ pushedEmpty = true;
+ $(opts.shareURLEl).hide();
+ window.history.pushState(null, "", "/");
+ }
+ function popState(e) {
+ if (e === null) {
+ return;
+ }
+ if (e && e.state && e.state.code) {
+ setBody(e.state.code);
+ }
+ }
+ var rewriteHistory = false;
+ if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
+ rewriteHistory = true;
+ code[0].addEventListener('input', inputChanged);
+ window.addEventListener('popstate', popState);
+ }
+
+ function setError(error) {
+ if (running) running.Kill();
+ lineClear();
+ lineHighlight(error);
+ output.empty().addClass("error").text(error);
+ }
+ function loading() {
+ lineClear();
+ if (running) running.Kill();
+ output.removeClass("error").text('Waiting for remote server...');
+ }
+ function run() {
+ loading();
+ running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
+ }
+
+ function fmt() {
+ loading();
+ var data = {"body": body()};
+ if ($(opts.fmtImportEl).is(":checked")) {
+ data["imports"] = "true";
+ }
+ $.ajax("/fmt", {
+ data: data,
+ type: "POST",
+ dataType: "json",
+ success: function(data) {
+ if (data.Error) {
+ setError(data.Error);
+ } else {
+ setBody(data.Body);
+ setError("");
+ }
+ }
+ });
+ }
+
+ var shareURL; // jQuery element to show the shared URL.
+ var sharing = false; // true if there is a pending request.
+ var shareCallbacks = [];
+ function share(opt_callback) {
+ if (opt_callback) shareCallbacks.push(opt_callback);
+
+ if (sharing) return;
+ sharing = true;
+
+ var sharingData = body();
+ $.ajax("/share", {
+ processData: false,
+ data: sharingData,
+ type: "POST",
+ contentType: "text/plain; charset=utf-8",
+ complete: function(xhr) {
+ sharing = false;
+ if (xhr.status != 200) {
+ alert("Server error; try again.");
+ return;
+ }
+ if (opts.shareRedirect) {
+ window.location = opts.shareRedirect + xhr.responseText;
+ }
+ var path = "/p/" + xhr.responseText;
+ var url = origin(window.location) + path;
+
+ for (var i = 0; i < shareCallbacks.length; i++) {
+ shareCallbacks[i](url);
+ }
+ shareCallbacks = [];
+
+ if (shareURL) {
+ shareURL.show().val(url).focus().select();
+
+ if (rewriteHistory) {
+ var historyData = {"code": sharingData};
+ window.history.pushState(historyData, "", path);
+ pushedEmpty = false;
+ }
+ }
+ }
+ });
+ }
+
+ $(opts.runEl).click(run);
+ $(opts.fmtEl).click(fmt);
+
+ if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
+ if (opts.shareURLEl) {
+ shareURL = $(opts.shareURLEl).hide();
+ }
+ $(opts.shareEl).click(function() {
+ share();
+ });
+ }
+
+ if (opts.toysEl !== null) {
+ $(opts.toysEl).bind('change', function() {
+ var toy = $(this).val();
+ $.ajax("/doc/play/"+toy, {
+ processData: false,
+ type: "GET",
+ complete: function(xhr) {
+ if (xhr.status != 200) {
+ alert("Server error; try again.");
+ return;
+ }
+ setBody(xhr.responseText);
+ }
+ });
+ });
+ }
+ }
+
+ window.playground = playground;
+})();
diff --git a/prebuilt/internal/bytealg.a b/server/prebuilt/internal/bytealg.a
similarity index 100%
rename from prebuilt/internal/bytealg.a
rename to server/prebuilt/internal/bytealg.a
diff --git a/prebuilt/internal/cpu.a b/server/prebuilt/internal/cpu.a
similarity index 100%
rename from prebuilt/internal/cpu.a
rename to server/prebuilt/internal/cpu.a
diff --git a/prebuilt/runtime.a b/server/prebuilt/runtime.a
similarity index 100%
rename from prebuilt/runtime.a
rename to server/prebuilt/runtime.a
diff --git a/prebuilt/runtime/internal/atomic.a b/server/prebuilt/runtime/internal/atomic.a
similarity index 100%
rename from prebuilt/runtime/internal/atomic.a
rename to server/prebuilt/runtime/internal/atomic.a
diff --git a/prebuilt/runtime/internal/math.a b/server/prebuilt/runtime/internal/math.a
similarity index 100%
rename from prebuilt/runtime/internal/math.a
rename to server/prebuilt/runtime/internal/math.a
diff --git a/prebuilt/runtime/internal/sys.a b/server/prebuilt/runtime/internal/sys.a
similarity index 100%
rename from prebuilt/runtime/internal/sys.a
rename to server/prebuilt/runtime/internal/sys.a
diff --git a/server/style.css b/server/style.css
new file mode 100644
index 0000000..1cd00e8
--- /dev/null
+++ b/server/style.css
@@ -0,0 +1,176 @@
+html {
+ height: 100%;
+}
+body {
+ color: black;
+ padding: 0;
+ margin: 0;
+ width: 100%;
+ height: 100%;
+}
+a {
+ color: #009;
+}
+#wrap,
+#about {
+ padding: 5px;
+ margin: 0;
+
+ position: absolute;
+ top: 50px;
+ bottom: 25%;
+ left: 0;
+ right: 0;
+
+ background: #FFD;
+}
+#about {
+ display: none;
+ z-index: 1;
+ padding: 10px 40px;
+ font-size: 16px;
+ font-family: sans-serif;
+ overflow: auto;
+}
+#about p {
+ max-width: 520px;
+}
+#about ul {
+ max-width: 480px;
+}
+#about li {
+ margin-bottom: 1em;
+}
+#code, #output, pre, .lines {
+ /* The default monospace font on OS X is ugly, so specify Menlo
+ * instead. On other systems the default monospace font will be used. */
+ font-family: Menlo, monospace;
+ font-size: 11pt;
+}
+
+#code {
+ color: black;
+ background: inherit;
+
+ width: 100%;
+ height: 100%;
+ padding: 0; margin: 0;
+ border: none;
+ outline: none;
+ resize: none;
+ wrap: off;
+ float: right;
+}
+#output {
+ position: absolute;
+ top: 75%;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 8px;
+}
+#output .system, #output .loading {
+ color: #999;
+}
+#output .stderr, #output .error {
+ color: #900;
+}
+#output pre {
+ margin: 0;
+}
+#banner {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 50px;
+ background-color: #E0EBF5;
+}
+#head {
+ float: left;
+ padding: 15px 10px;
+
+ font-size: 20px;
+ font-family: sans-serif;
+}
+#controls {
+ float: left;
+ padding: 10px 15px;
+ min-width: 245px;
+}
+#controls > input {
+ border-radius: 5px;
+}
+#aboutControls {
+ float: right;
+ padding: 10px 15px;
+}
+input[type=button],
+#importsBox {
+ height: 30px;
+ border: 1px solid #375EAB;
+ font-size: 16px;
+ font-family: sans-serif;
+ background: #375EAB;
+ color: white;
+ position: static;
+ top: 1px;
+ border-radius: 5px;
+}
+#importsBox {
+ position: relative;
+ display: inline;
+ padding: 5px 0;
+ margin-right: 5px;
+}
+#importsBox input {
+ position: relative;
+ top: -2px;
+ left: 1px;
+ height: 10px;
+ width: 10px;
+}
+#shareURL {
+ width: 280px;
+ font-size: 16px;
+ border: 1px solid #ccc;
+ background: #eee;
+ color: black;
+ height: 23px;
+}
+#embedLabel {
+ font-family: sans-serif;
+}
+.lines {
+ float: left;
+ overflow: hidden;
+ text-align: right;
+}
+.lines div {
+ padding-right: 5px;
+ color: lightgray;
+}
+.lineerror {
+ color: red;
+ background: #FDD;
+}
+.exit {
+ color: lightgray;
+}
+
+.embedded #banner {
+ display: none;
+}
+.embedded #wrap {
+ top: 0;
+}
+#embedRun {
+ display: none;
+}
+.embedded #embedRun {
+ display: block;
+ position: absolute;
+ z-index: 1;
+ top: 10px;
+ right: 10px;
+}
diff --git a/server/wasm_exec.js b/server/wasm_exec.js
index b26a17d..b23fdd3 100644
--- a/server/wasm_exec.js
+++ b/server/wasm_exec.js
@@ -17,7 +17,6 @@
const decoder = new TextDecoder('utf-8');
const filesystem = {
- '/main.go': encoder.encode("package main\nfunc main() {}"),
'/importcfg.link': encoder.encode(
"packagefile command-line-arguments=main.a\n" +
"packagefile runtime=prebuilt/runtime.a\n" +
@@ -29,6 +28,26 @@
),
};
+ let workingDirectory = '/';
+
+ let absPath = (path) => {
+ if (path[0] == '/') {
+ return path;
+ }
+ return workingDirectory + path.replace(/^\.\/?/, '');
+ };
+
+ global.readFromGoFilesystem = (path) => filesystem[absPath(path)];
+ global.writeToGoFilesystem = (path, content) => {
+ if (typeof content === 'string') {
+ filesystem[absPath(path)] = encoder.encode(content);
+ } else {
+ filesystem[absPath(path)] = content;
+ }
+ };
+ global.goStdout = (buf) => {};
+ global.goStderr = (buf) => {};
+
const openFiles = new Map();
let nextFd = 1000;
@@ -60,15 +79,6 @@
});
};
- let workingDirectory = '/';
-
- let absPath = (path) => {
- if (path[0] == '/') {
- return path;
- }
- return workingDirectory + path.replace(/^\.\/?/, '');
- };
-
const constants = {
O_WRONLY: 1 << 0,
O_RDWR: 1 << 1,
@@ -82,33 +92,30 @@
global.fs = {
constants,
writeSync(fd, buf) {
- if (fd <= 3) {
- outputBuf += decoder.decode(buf);
- const nl = outputBuf.lastIndexOf("\n");
- if (nl != -1) {
- console.log(outputBuf.substr(0, nl));
- outputBuf = outputBuf.substr(nl + 1);
+ if (fd === 2) {
+ global.goStdout(buf);
+ } else if (fd === 3) {
+ global.goStderr(buf);
+ } else {
+ const file = openFiles[fd];
+ const source = filesystem[file.path];
+ let destLength = source.length + buf.length;
+ if (file.offset < source.length) {
+ destLength = file.offset + buf.length;
+ if (destLength < source.length) {
+ destLength = source.length;
+ }
}
- return buf.length;
- }
- const file = openFiles[fd];
- const source = filesystem[file.path];
- let destLength = source.length + buf.length;
- if (file.offset < source.length) {
- destLength = file.offset + buf.length;
- if (destLength < source.length) {
- destLength = source.length;
+ const dest = new Uint8Array(destLength);
+ for (let i = 0; i < source.length; ++i) {
+ dest[i] = source[i];
}
+ for (let i = 0; i < buf.length; ++i) {
+ dest[file.offset + i] = buf[i];
+ }
+ openFiles[fd].offset += buf.length;
+ filesystem[file.path] = dest;
}
- const dest = new Uint8Array(destLength);
- for (let i = 0; i < source.length; ++i) {
- dest[i] = source[i];
- }
- for (let i = 0; i < buf.length; ++i) {
- dest[file.offset + i] = buf[i];
- }
- openFiles[fd].offset += buf.length;
- filesystem[file.path] = dest;
},
write(fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length) {
@@ -144,7 +151,6 @@
callback(null, fd);
},
read(fd, buffer, offset, length, position, callback) {
- console.log('read(' + fd + ', ' + length + ', ' + position + ')');
if (offset !== 0) {
throw new Error('read not fully implemented: ' + offset);
}