{"action":"created","comment":{"url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/comments/38784392","id":38784392,"diff_hunk":"@@ -0,0 +1,96 @@\n+/*\n+ * Copyright (C) 2015 Ben Hamilton\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+package com.zaxxer.nuprocess;\n+\n+import java.nio.ByteBuffer;\n+import java.nio.CharBuffer;\n+import java.nio.charset.Charset;\n+import java.nio.charset.CharsetEncoder;\n+import java.nio.charset.CoderResult;\n+\n+/**\n+ * Implementation of {@link NuProcessHandler#onStdinReady(ByteBuffer)}\n+ * which handles encoding of stdin bytes to Java UTF-16 string data.\n+ *\n+ * Calls back into a {@link NuProcessEncoderHandler} with a\n+ * {@link CharBuffer} whose contents will be encoded with a specified\n+ * {@link Charset} or {@link CharsetEncoder} then passed to the\n+ * stdin of a process.\n+ *\n+ * This class is not intended to be subclassed.\n+ *\n+ * @author Ben Hamilton\n+ */\n+public final class NuProcessEncoder {\n+  private final NuProcessEncoderHandler handler;\n+  private final CharsetEncoder encoder;\n+  private final CharBuffer charBuffer;\n+\n+  /**\n+   * Creates an encoder which uses a single {@link Charset} to encode input data.\n+   *\n+   * @param handler {@link NuProcessEncoderHandler} called back with a string buffer\n+   *                to be encoded and fed to stdin\n+   * @param charset {@link Charset} used to encode stdin data to bytes\n+   */\n+  public NuProcessEncoder(NuProcessEncoderHandler handler, Charset charset) {\n+    this(handler, charset.newEncoder());\n+  }\n+\n+  /**\n+   * Creates an encoder which uses a {@link CharsetEncoder} to encode input data.\n+   *\n+   * @param handler {@link NuProcessEncoderHandler} called back with a string buffer\n+   *                into which the caller writes string data to be written to stdin\n+   * @param encoder {@link CharsetEncoder} used to encode stdin string data to bytes\n+   */\n+  public NuProcessEncoder(\n+      NuProcessEncoderHandler handler,\n+      CharsetEncoder encoder) {","path":"src/main/java/com/zaxxer/nuprocess/NuProcessEncoder.java","position":63,"original_position":63,"commit_id":"7174d143c58bd7eb0a151cbe23b775daee3ec580","original_commit_id":"7174d143c58bd7eb0a151cbe23b775daee3ec580","user":{"login":"bhamiltoncx","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bhamiltoncx","html_url":"https://github.com/bhamiltoncx","followers_url":"https://api.github.com/users/bhamiltoncx/followers","following_url":"https://api.github.com/users/bhamiltoncx/following{/other_user}","gists_url":"https://api.github.com/users/bhamiltoncx/gists{/gist_id}","starred_url":"https://api.github.com/users/bhamiltoncx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bhamiltoncx/subscriptions","organizations_url":"https://api.github.com/users/bhamiltoncx/orgs","repos_url":"https://api.github.com/users/bhamiltoncx/repos","events_url":"https://api.github.com/users/bhamiltoncx/events{/privacy}","received_events_url":"https://api.github.com/users/bhamiltoncx/received_events","type":"User","site_admin":false},"body":"Fixed.","created_at":"2015-09-04T19:05:45Z","updated_at":"2015-09-04T19:05:45Z","html_url":"https://github.com/brettwooldridge/NuProcess/pull/30#discussion_r38784392","pull_request_url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30","_links":{"self":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/comments/38784392"},"html":{"href":"https://github.com/brettwooldridge/NuProcess/pull/30#discussion_r38784392"},"pull_request":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30"}}},"pull_request":{"url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30","id":42300045,"html_url":"https://github.com/brettwooldridge/NuProcess/pull/30","diff_url":"https://github.com/brettwooldridge/NuProcess/pull/30.diff","patch_url":"https://github.com/brettwooldridge/NuProcess/pull/30.patch","issue_url":"https://api.github.com/repos/brettwooldridge/NuProcess/issues/30","number":30,"state":"open","locked":false,"title":"New classes NuProcessDecoder and NuProcessEncoder to handle conversion of bytes to string data","user":{"login":"bhamiltoncx","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bhamiltoncx","html_url":"https://github.com/bhamiltoncx","followers_url":"https://api.github.com/users/bhamiltoncx/followers","following_url":"https://api.github.com/users/bhamiltoncx/following{/other_user}","gists_url":"https://api.github.com/users/bhamiltoncx/gists{/gist_id}","starred_url":"https://api.github.com/users/bhamiltoncx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bhamiltoncx/subscriptions","organizations_url":"https://api.github.com/users/bhamiltoncx/orgs","repos_url":"https://api.github.com/users/bhamiltoncx/repos","events_url":"https://api.github.com/users/bhamiltoncx/events{/privacy}","received_events_url":"https://api.github.com/users/bhamiltoncx/received_events","type":"User","site_admin":false},"body":"Thanks for the nice library! I'm an engineer on the Buck Build team at Facebook. Buck currently has some pretty crummy and non-performant abstractions around process management, and `NuProcess` is just what the doctor ordered.\r\n\r\nWhen using `NuProcess`, I found it's very common to want to use `CharsetDecoder` to decode stdout/stderr bytes provided to `NuProcessHandler` into `String` data (and to encode String data to bytes for stdin).\r\n\r\nRight now, it's pretty inconvenient to do that: if you don't consume all the bytes in the `ByteBuffer` passed to `onStdout()` (say, because a UTF-8 code point boundary straddles two reads), the buffer is wiped before the next invocation. That means you have to do your own byte buffering in order to decode the data.\r\n\r\nSince this seems like a pretty common use-case, this pull request adds a new concrete implementation of `NuProcessHandler` called `NuProcessDecoder` which handles streaming decoding and encoding of stdin, stdout, and stderr bytes to and from Java strings.\r\n\r\nPreviously, stdout and stderr shared a single buffer in `BasePosixProcess`. After this diff, they are separate buffers.\r\n\r\nIn addition, we now optionally support keeping unused data in the buffers after `onStdout()` or `onStderr()` returns, using `ByteBuffer.compact()` to move the unused data to the front of the buffer so it can be consumed on the next invocation of that method. This new behavior is off by default, and can be enabled by returning `true` from `NuProcessHandler.shouldCompactBuffersAfterUse()`.\r\n\r\nSince we can now have unused data in the stdout and stderr buffers at exit, I added two new callbacks `onPreExitStdout()` and `onPreExitStderr()` to allow callers one last chance to consume stdout/stderr data at process exit time. This is necessary for `CharsetDecoder` to finalize any decoding to Unicode.\r\n\r\nI included docs and unit tests, but I only tested this on OS X 10.10. I think it ought to work on Windows as well, but I hope you (or CI) can test that for me.","created_at":"2015-08-12T23:14:58Z","updated_at":"2015-09-04T19:05:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f3eef43f3e6bc8f1981663d3b3db9cd061b75e7e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30/commits","review_comments_url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30/comments","review_comment_url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/comments{/number}","comments_url":"https://api.github.com/repos/brettwooldridge/NuProcess/issues/30/comments","statuses_url":"https://api.github.com/repos/brettwooldridge/NuProcess/statuses/7174d143c58bd7eb0a151cbe23b775daee3ec580","head":{"label":"bhamiltoncx:process-decoder","ref":"process-decoder","sha":"7174d143c58bd7eb0a151cbe23b775daee3ec580","user":{"login":"bhamiltoncx","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bhamiltoncx","html_url":"https://github.com/bhamiltoncx","followers_url":"https://api.github.com/users/bhamiltoncx/followers","following_url":"https://api.github.com/users/bhamiltoncx/following{/other_user}","gists_url":"https://api.github.com/users/bhamiltoncx/gists{/gist_id}","starred_url":"https://api.github.com/users/bhamiltoncx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bhamiltoncx/subscriptions","organizations_url":"https://api.github.com/users/bhamiltoncx/orgs","repos_url":"https://api.github.com/users/bhamiltoncx/repos","events_url":"https://api.github.com/users/bhamiltoncx/events{/privacy}","received_events_url":"https://api.github.com/users/bhamiltoncx/received_events","type":"User","site_admin":false},"repo":{"id":40569026,"name":"NuProcess","full_name":"bhamiltoncx/NuProcess","owner":{"login":"bhamiltoncx","id":671827,"avatar_url":"https://avatars.githubusercontent.com/u/671827?v=3","gravatar_id":"","url":"https://api.github.com/users/bhamiltoncx","html_url":"https://github.com/bhamiltoncx","followers_url":"https://api.github.com/users/bhamiltoncx/followers","following_url":"https://api.github.com/users/bhamiltoncx/following{/other_user}","gists_url":"https://api.github.com/users/bhamiltoncx/gists{/gist_id}","starred_url":"https://api.github.com/users/bhamiltoncx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bhamiltoncx/subscriptions","organizations_url":"https://api.github.com/users/bhamiltoncx/orgs","repos_url":"https://api.github.com/users/bhamiltoncx/repos","events_url":"https://api.github.com/users/bhamiltoncx/events{/privacy}","received_events_url":"https://api.github.com/users/bhamiltoncx/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bhamiltoncx/NuProcess","description":"Low-overhead, non-blocking I/O, external Process implementation for Java","fork":true,"url":"https://api.github.com/repos/bhamiltoncx/NuProcess","forks_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/forks","keys_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/teams","hooks_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/hooks","issue_events_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/issues/events{/number}","events_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/events","assignees_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/assignees{/user}","branches_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/branches{/branch}","tags_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/tags","blobs_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/git/refs{/sha}","trees_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/statuses/{sha}","languages_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/languages","stargazers_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/stargazers","contributors_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/contributors","subscribers_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/subscribers","subscription_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/subscription","commits_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/commits{/sha}","git_commits_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/git/commits{/sha}","comments_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/comments{/number}","issue_comment_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/issues/comments{/number}","contents_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/contents/{+path}","compare_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/merges","archive_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/downloads","issues_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/issues{/number}","pulls_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/pulls{/number}","milestones_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/milestones{/number}","notifications_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/labels{/name}","releases_url":"https://api.github.com/repos/bhamiltoncx/NuProcess/releases{/id}","created_at":"2015-08-11T22:50:56Z","updated_at":"2015-08-11T22:50:57Z","pushed_at":"2015-09-03T20:52:23Z","git_url":"git://github.com/bhamiltoncx/NuProcess.git","ssh_url":"git@github.com:bhamiltoncx/NuProcess.git","clone_url":"https://github.com/bhamiltoncx/NuProcess.git","svn_url":"https://github.com/bhamiltoncx/NuProcess","homepage":null,"size":535,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"brettwooldridge:master","ref":"master","sha":"0d0087b4078b351b9cc778da272ca26c63af5d64","user":{"login":"brettwooldridge","id":1538661,"avatar_url":"https://avatars.githubusercontent.com/u/1538661?v=3","gravatar_id":"","url":"https://api.github.com/users/brettwooldridge","html_url":"https://github.com/brettwooldridge","followers_url":"https://api.github.com/users/brettwooldridge/followers","following_url":"https://api.github.com/users/brettwooldridge/following{/other_user}","gists_url":"https://api.github.com/users/brettwooldridge/gists{/gist_id}","starred_url":"https://api.github.com/users/brettwooldridge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brettwooldridge/subscriptions","organizations_url":"https://api.github.com/users/brettwooldridge/orgs","repos_url":"https://api.github.com/users/brettwooldridge/repos","events_url":"https://api.github.com/users/brettwooldridge/events{/privacy}","received_events_url":"https://api.github.com/users/brettwooldridge/received_events","type":"User","site_admin":false},"repo":{"id":11944439,"name":"NuProcess","full_name":"brettwooldridge/NuProcess","owner":{"login":"brettwooldridge","id":1538661,"avatar_url":"https://avatars.githubusercontent.com/u/1538661?v=3","gravatar_id":"","url":"https://api.github.com/users/brettwooldridge","html_url":"https://github.com/brettwooldridge","followers_url":"https://api.github.com/users/brettwooldridge/followers","following_url":"https://api.github.com/users/brettwooldridge/following{/other_user}","gists_url":"https://api.github.com/users/brettwooldridge/gists{/gist_id}","starred_url":"https://api.github.com/users/brettwooldridge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brettwooldridge/subscriptions","organizations_url":"https://api.github.com/users/brettwooldridge/orgs","repos_url":"https://api.github.com/users/brettwooldridge/repos","events_url":"https://api.github.com/users/brettwooldridge/events{/privacy}","received_events_url":"https://api.github.com/users/brettwooldridge/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/brettwooldridge/NuProcess","description":"Low-overhead, non-blocking I/O, external Process implementation for Java","fork":false,"url":"https://api.github.com/repos/brettwooldridge/NuProcess","forks_url":"https://api.github.com/repos/brettwooldridge/NuProcess/forks","keys_url":"https://api.github.com/repos/brettwooldridge/NuProcess/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brettwooldridge/NuProcess/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brettwooldridge/NuProcess/teams","hooks_url":"https://api.github.com/repos/brettwooldridge/NuProcess/hooks","issue_events_url":"https://api.github.com/repos/brettwooldridge/NuProcess/issues/events{/number}","events_url":"https://api.github.com/repos/brettwooldridge/NuProcess/events","assignees_url":"https://api.github.com/repos/brettwooldridge/NuProcess/assignees{/user}","branches_url":"https://api.github.com/repos/brettwooldridge/NuProcess/branches{/branch}","tags_url":"https://api.github.com/repos/brettwooldridge/NuProcess/tags","blobs_url":"https://api.github.com/repos/brettwooldridge/NuProcess/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brettwooldridge/NuProcess/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brettwooldridge/NuProcess/git/refs{/sha}","trees_url":"https://api.github.com/repos/brettwooldridge/NuProcess/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brettwooldridge/NuProcess/statuses/{sha}","languages_url":"https://api.github.com/repos/brettwooldridge/NuProcess/languages","stargazers_url":"https://api.github.com/repos/brettwooldridge/NuProcess/stargazers","contributors_url":"https://api.github.com/repos/brettwooldridge/NuProcess/contributors","subscribers_url":"https://api.github.com/repos/brettwooldridge/NuProcess/subscribers","subscription_url":"https://api.github.com/repos/brettwooldridge/NuProcess/subscription","commits_url":"https://api.github.com/repos/brettwooldridge/NuProcess/commits{/sha}","git_commits_url":"https://api.github.com/repos/brettwooldridge/NuProcess/git/commits{/sha}","comments_url":"https://api.github.com/repos/brettwooldridge/NuProcess/comments{/number}","issue_comment_url":"https://api.github.com/repos/brettwooldridge/NuProcess/issues/comments{/number}","contents_url":"https://api.github.com/repos/brettwooldridge/NuProcess/contents/{+path}","compare_url":"https://api.github.com/repos/brettwooldridge/NuProcess/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brettwooldridge/NuProcess/merges","archive_url":"https://api.github.com/repos/brettwooldridge/NuProcess/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brettwooldridge/NuProcess/downloads","issues_url":"https://api.github.com/repos/brettwooldridge/NuProcess/issues{/number}","pulls_url":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls{/number}","milestones_url":"https://api.github.com/repos/brettwooldridge/NuProcess/milestones{/number}","notifications_url":"https://api.github.com/repos/brettwooldridge/NuProcess/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brettwooldridge/NuProcess/labels{/name}","releases_url":"https://api.github.com/repos/brettwooldridge/NuProcess/releases{/id}","created_at":"2013-08-07T07:50:14Z","updated_at":"2015-08-17T08:26:12Z","pushed_at":"2015-09-03T17:44:00Z","git_url":"git://github.com/brettwooldridge/NuProcess.git","ssh_url":"git@github.com:brettwooldridge/NuProcess.git","clone_url":"https://github.com/brettwooldridge/NuProcess.git","svn_url":"https://github.com/brettwooldridge/NuProcess","homepage":null,"size":1525,"stargazers_count":54,"watchers_count":54,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":14,"mirror_url":null,"open_issues_count":9,"forks":14,"open_issues":9,"watchers":54,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30"},"html":{"href":"https://github.com/brettwooldridge/NuProcess/pull/30"},"issue":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/issues/30"},"comments":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/issues/30/comments"},"review_comments":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30/comments"},"review_comment":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/pulls/30/commits"},"statuses":{"href":"https://api.github.com/repos/brettwooldridge/NuProcess/statuses/7174d143c58bd7eb0a151cbe23b775daee3ec580"}}}}