pax_global_header00006660000000000000000000000064135764044610014524gustar00rootroot0000000000000052 comment=871335ca905ebf060c48f497fca66beab9541890 golang-github-mreiferson-go-snappystream-0.2.3/000077500000000000000000000000001357640446100215535ustar00rootroot00000000000000golang-github-mreiferson-go-snappystream-0.2.3/.travis.yml000066400000000000000000000002661357640446100236700ustar00rootroot00000000000000language: go go: - 1.2.2 - 1.3.1 env: - GOARCH=amd64 - GOARCH=386 install: - go get code.google.com/p/snappy-go/snappy script: - go test -v notifications: email: false golang-github-mreiferson-go-snappystream-0.2.3/LICENSE000066400000000000000000000017771357640446100225740ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-mreiferson-go-snappystream-0.2.3/README.md000066400000000000000000000010611357640446100230300ustar00rootroot00000000000000## go-snappystream a Go package for framed snappy streams. [![Build Status](https://secure.travis-ci.org/mreiferson/go-snappystream.png?branch=master)](http://travis-ci.org/mreiferson/go-snappystream) [![GoDoc](https://godoc.org/github.com/mreiferson/go-snappystream?status.svg)](https://godoc.org/github.com/mreiferson/go-snappystream) This package wraps [snappy-go][1] and supplies a `Reader` and `Writer` for the snappy [framed stream format][2]. [1]: https://code.google.com/p/snappy-go/ [2]: https://snappy.googlecode.com/svn/trunk/framing_format.txt golang-github-mreiferson-go-snappystream-0.2.3/fixturedata_test.go000066400000000000000000004736751357640446100255070ustar00rootroot00000000000000package snappystream var testDataMan = []byte(` .TH XARGS 1L \" -*- nroff -*- .SH NAME xargs \- build and execute command lines from standard input .SH SYNOPSIS .B xargs [\-0prtx] [\-e[eof-str]] [\-i[replace-str]] [\-l[max-lines]] [\-n max-args] [\-s max-chars] [\-P max-procs] [\-\-null] [\-\-eof[=eof-str]] [\-\-replace[=replace-str]] [\-\-max-lines[=max-lines]] [\-\-interactive] [\-\-max-chars=max-chars] [\-\-verbose] [\-\-exit] [\-\-max-procs=max-procs] [\-\-max-args=max-args] [\-\-no-run-if-empty] [\-\-version] [\-\-help] [command [initial-arguments]] .SH DESCRIPTION This manual page documents the GNU version of .BR xargs . .B xargs reads arguments from the standard input, delimited by blanks (which can be protected with double or single quotes or a backslash) or newlines, and executes the .I command (default is /bin/echo) one or more times with any .I initial-arguments followed by arguments read from standard input. Blank lines on the standard input are ignored. .P .B xargs exits with the following status: .nf 0 if it succeeds 123 if any invocation of the command exited with status 1-125 124 if the command exited with status 255 125 if the command is killed by a signal 126 if the command cannot be run 127 if the command is not found 1 if some other error occurred. .fi .SS OPTIONS .TP .I "\-\-null, \-0" Input filenames are terminated by a null character instead of by whitespace, and the quotes and backslash are not special (every character is taken literally). Disables the end of file string, which is treated like any other argument. Useful when arguments might contain white space, quote marks, or backslashes. The GNU find \-print0 option produces input suitable for this mode. .TP .I "\-\-eof[=eof-str], \-e[eof-str]" Set the end of file string to \fIeof-str\fR. If the end of file string occurs as a line of input, the rest of the input is ignored. If \fIeof-str\fR is omitted, there is no end of file string. If this option is not given, the end of file string defaults to "_". .TP .I "\-\-help" Print a summary of the options to .B xargs and exit. .TP .I "\-\-replace[=replace-str], \-i[replace-str]" Replace occurences of \fIreplace-str\fR in the initial arguments with names read from standard input. Also, unquoted blanks do not terminate arguments. If \fIreplace-str\fR is omitted, it defaults to "{}" (like for 'find \-exec'). Implies \fI\-x\fP and \fI\-l 1\fP. .TP .I "\-\-max-lines[=max-lines], -l[max-lines]" Use at most \fImax-lines\fR nonblank input lines per command line; \fImax-lines\fR defaults to 1 if omitted. Trailing blanks cause an input line to be logically continued on the next input line. Implies \fI\-x\fR. .TP .I "\-\-max-args=max-args, \-n max-args" Use at most \fImax-args\fR arguments per command line. Fewer than \fImax-args\fR arguments will be used if the size (see the \-s option) is exceeded, unless the \-x option is given, in which case \fBxargs\fR will exit. .TP .I "\-\-interactive, \-p" Prompt the user about whether to run each command line and read a line from the terminal. Only run the command line if the response starts with 'y' or 'Y'. Implies \fI\-t\fR. .TP .I "\-\-no-run-if-empty, \-r" If the standard input does not contain any nonblanks, do not run the command. Normally, the command is run once even if there is no input. .TP .I "\-\-max-chars=max-chars, \-s max-chars" Use at most \fImax-chars\fR characters per command line, including the command and initial arguments and the terminating nulls at the ends of the argument strings. The default is as large as possible, up to 20k characters. .TP .I "\-\-verbose, \-t" Print the command line on the standard error output before executing it. .TP .I "\-\-version" Print the version number of .B xargs and exit. .TP .I "\-\-exit, \-x" Exit if the size (see the \fI\-s\fR option) is exceeded. .TP .I "\-\-max-procs=max-procs, \-P max-procs" Run up to \fImax-procs\fR processes at a time; the default is 1. If \fImax-procs\fR is 0, \fBxargs\fR will run as many processes as possible at a time. Use the \fI\-n\fR option with \fI\-P\fR; otherwise chances are that only one exec will be done. .SH "SEE ALSO" \fBfind\fP(1L), \fBlocate\fP(1L), \fBlocatedb\fP(5L), \fBupdatedb\fP(1) \fBFinding Files\fP (on-line in Info, or printed)`) // curl -s https://api.github.com/users/mreiferson/repos var testDataJSON = []byte(` [ { "id": 19041094, "name": "2014-talks", "full_name": "mreiferson/2014-talks", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/2014-talks", "description": "This is the official repository for slides and talks from GopherCon 2014", "fork": true, "url": "https://api.github.com/repos/mreiferson/2014-talks", "forks_url": "https://api.github.com/repos/mreiferson/2014-talks/forks", "keys_url": "https://api.github.com/repos/mreiferson/2014-talks/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/2014-talks/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/2014-talks/teams", "hooks_url": "https://api.github.com/repos/mreiferson/2014-talks/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/2014-talks/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/2014-talks/events", "assignees_url": "https://api.github.com/repos/mreiferson/2014-talks/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/2014-talks/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/2014-talks/tags", "blobs_url": "https://api.github.com/repos/mreiferson/2014-talks/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/2014-talks/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/2014-talks/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/2014-talks/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/2014-talks/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/2014-talks/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/2014-talks/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/2014-talks/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/2014-talks/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/2014-talks/subscription", "commits_url": "https://api.github.com/repos/mreiferson/2014-talks/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/2014-talks/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/2014-talks/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/2014-talks/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/2014-talks/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/2014-talks/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/2014-talks/merges", "archive_url": "https://api.github.com/repos/mreiferson/2014-talks/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/2014-talks/downloads", "issues_url": "https://api.github.com/repos/mreiferson/2014-talks/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/2014-talks/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/2014-talks/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/2014-talks/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/2014-talks/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/2014-talks/releases{/id}", "created_at": "2014-04-22T18:28:59Z", "updated_at": "2014-04-26T03:10:39Z", "pushed_at": "2014-04-25T14:46:35Z", "git_url": "git://github.com/mreiferson/2014-talks.git", "ssh_url": "git@github.com:mreiferson/2014-talks.git", "clone_url": "https://github.com/mreiferson/2014-talks.git", "svn_url": "https://github.com/mreiferson/2014-talks", "homepage": null, "size": 3596, "stargazers_count": 0, "watchers_count": 0, "language": null, "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 3329246, "name": "asyncdynamo", "full_name": "mreiferson/asyncdynamo", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/asyncdynamo", "description": "async Amazon DynamoDB library for Tornado", "fork": true, "url": "https://api.github.com/repos/mreiferson/asyncdynamo", "forks_url": "https://api.github.com/repos/mreiferson/asyncdynamo/forks", "keys_url": "https://api.github.com/repos/mreiferson/asyncdynamo/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/asyncdynamo/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/asyncdynamo/teams", "hooks_url": "https://api.github.com/repos/mreiferson/asyncdynamo/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/asyncdynamo/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/asyncdynamo/events", "assignees_url": "https://api.github.com/repos/mreiferson/asyncdynamo/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/asyncdynamo/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/asyncdynamo/tags", "blobs_url": "https://api.github.com/repos/mreiferson/asyncdynamo/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/asyncdynamo/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/asyncdynamo/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/asyncdynamo/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/asyncdynamo/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/asyncdynamo/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/asyncdynamo/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/asyncdynamo/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/asyncdynamo/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/asyncdynamo/subscription", "commits_url": "https://api.github.com/repos/mreiferson/asyncdynamo/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/asyncdynamo/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/asyncdynamo/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/asyncdynamo/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/asyncdynamo/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/asyncdynamo/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/asyncdynamo/merges", "archive_url": "https://api.github.com/repos/mreiferson/asyncdynamo/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/asyncdynamo/downloads", "issues_url": "https://api.github.com/repos/mreiferson/asyncdynamo/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/asyncdynamo/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/asyncdynamo/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/asyncdynamo/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/asyncdynamo/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/asyncdynamo/releases{/id}", "created_at": "2012-02-01T21:32:54Z", "updated_at": "2014-04-03T21:58:44Z", "pushed_at": "2012-02-01T21:06:23Z", "git_url": "git://github.com/mreiferson/asyncdynamo.git", "ssh_url": "git@github.com:mreiferson/asyncdynamo.git", "clone_url": "https://github.com/mreiferson/asyncdynamo.git", "svn_url": "https://github.com/mreiferson/asyncdynamo", "homepage": "", "size": 73, "stargazers_count": 1, "watchers_count": 1, "language": "Python", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 2622445, "name": "asyncmongo", "full_name": "mreiferson/asyncmongo", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/asyncmongo", "description": "An asynchronous library for accessing mongo with tornado.ioloop", "fork": true, "url": "https://api.github.com/repos/mreiferson/asyncmongo", "forks_url": "https://api.github.com/repos/mreiferson/asyncmongo/forks", "keys_url": "https://api.github.com/repos/mreiferson/asyncmongo/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/asyncmongo/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/asyncmongo/teams", "hooks_url": "https://api.github.com/repos/mreiferson/asyncmongo/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/asyncmongo/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/asyncmongo/events", "assignees_url": "https://api.github.com/repos/mreiferson/asyncmongo/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/asyncmongo/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/asyncmongo/tags", "blobs_url": "https://api.github.com/repos/mreiferson/asyncmongo/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/asyncmongo/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/asyncmongo/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/asyncmongo/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/asyncmongo/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/asyncmongo/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/asyncmongo/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/asyncmongo/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/asyncmongo/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/asyncmongo/subscription", "commits_url": "https://api.github.com/repos/mreiferson/asyncmongo/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/asyncmongo/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/asyncmongo/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/asyncmongo/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/asyncmongo/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/asyncmongo/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/asyncmongo/merges", "archive_url": "https://api.github.com/repos/mreiferson/asyncmongo/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/asyncmongo/downloads", "issues_url": "https://api.github.com/repos/mreiferson/asyncmongo/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/asyncmongo/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/asyncmongo/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/asyncmongo/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/asyncmongo/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/asyncmongo/releases{/id}", "created_at": "2011-10-21T19:01:05Z", "updated_at": "2013-01-04T11:58:26Z", "pushed_at": "2011-10-21T19:02:46Z", "git_url": "git://github.com/mreiferson/asyncmongo.git", "ssh_url": "git@github.com:mreiferson/asyncmongo.git", "clone_url": "https://github.com/mreiferson/asyncmongo.git", "svn_url": "https://github.com/mreiferson/asyncmongo", "homepage": "http://github.com/bitly/asyncmongo", "size": 563, "stargazers_count": 1, "watchers_count": 1, "language": "Python", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 4554560, "name": "blog.perplexedlabs.com", "full_name": "mreiferson/blog.perplexedlabs.com", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/blog.perplexedlabs.com", "description": "archive of posts", "fork": false, "url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com", "forks_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/forks", "keys_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/teams", "hooks_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/events", "assignees_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/tags", "blobs_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/subscription", "commits_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/merges", "archive_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/downloads", "issues_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/blog.perplexedlabs.com/releases{/id}", "created_at": "2012-06-05T01:38:40Z", "updated_at": "2014-04-27T23:44:56Z", "pushed_at": "2014-04-27T23:44:56Z", "git_url": "git://github.com/mreiferson/blog.perplexedlabs.com.git", "ssh_url": "git@github.com:mreiferson/blog.perplexedlabs.com.git", "clone_url": "https://github.com/mreiferson/blog.perplexedlabs.com.git", "svn_url": "https://github.com/mreiferson/blog.perplexedlabs.com", "homepage": "http://blog.perplexedlabs.com/", "size": 668, "stargazers_count": 1, "watchers_count": 1, "language": null, "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 2861903, "name": "btpath", "full_name": "mreiferson/btpath", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/btpath", "description": "A* implementation/test app (1997)", "fork": false, "url": "https://api.github.com/repos/mreiferson/btpath", "forks_url": "https://api.github.com/repos/mreiferson/btpath/forks", "keys_url": "https://api.github.com/repos/mreiferson/btpath/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/btpath/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/btpath/teams", "hooks_url": "https://api.github.com/repos/mreiferson/btpath/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/btpath/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/btpath/events", "assignees_url": "https://api.github.com/repos/mreiferson/btpath/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/btpath/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/btpath/tags", "blobs_url": "https://api.github.com/repos/mreiferson/btpath/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/btpath/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/btpath/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/btpath/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/btpath/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/btpath/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/btpath/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/btpath/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/btpath/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/btpath/subscription", "commits_url": "https://api.github.com/repos/mreiferson/btpath/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/btpath/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/btpath/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/btpath/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/btpath/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/btpath/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/btpath/merges", "archive_url": "https://api.github.com/repos/mreiferson/btpath/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/btpath/downloads", "issues_url": "https://api.github.com/repos/mreiferson/btpath/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/btpath/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/btpath/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/btpath/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/btpath/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/btpath/releases{/id}", "created_at": "2011-11-27T17:23:02Z", "updated_at": "2013-01-04T17:58:42Z", "pushed_at": "2011-11-29T01:36:49Z", "git_url": "git://github.com/mreiferson/btpath.git", "ssh_url": "git@github.com:mreiferson/btpath.git", "clone_url": "https://github.com/mreiferson/btpath.git", "svn_url": "https://github.com/mreiferson/btpath", "homepage": "", "size": 88, "stargazers_count": 1, "watchers_count": 1, "language": "C++", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 15747148, "name": "chef-nsq", "full_name": "mreiferson/chef-nsq", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/chef-nsq", "description": "Chef Cookbook for NSQ", "fork": true, "url": "https://api.github.com/repos/mreiferson/chef-nsq", "forks_url": "https://api.github.com/repos/mreiferson/chef-nsq/forks", "keys_url": "https://api.github.com/repos/mreiferson/chef-nsq/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/chef-nsq/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/chef-nsq/teams", "hooks_url": "https://api.github.com/repos/mreiferson/chef-nsq/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/chef-nsq/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/chef-nsq/events", "assignees_url": "https://api.github.com/repos/mreiferson/chef-nsq/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/chef-nsq/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/chef-nsq/tags", "blobs_url": "https://api.github.com/repos/mreiferson/chef-nsq/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/chef-nsq/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/chef-nsq/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/chef-nsq/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/chef-nsq/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/chef-nsq/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/chef-nsq/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/chef-nsq/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/chef-nsq/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/chef-nsq/subscription", "commits_url": "https://api.github.com/repos/mreiferson/chef-nsq/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/chef-nsq/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/chef-nsq/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/chef-nsq/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/chef-nsq/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/chef-nsq/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/chef-nsq/merges", "archive_url": "https://api.github.com/repos/mreiferson/chef-nsq/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/chef-nsq/downloads", "issues_url": "https://api.github.com/repos/mreiferson/chef-nsq/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/chef-nsq/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/chef-nsq/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/chef-nsq/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/chef-nsq/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/chef-nsq/releases{/id}", "created_at": "2014-01-08T20:27:41Z", "updated_at": "2014-04-28T14:15:50Z", "pushed_at": "2014-04-28T04:31:58Z", "git_url": "git://github.com/mreiferson/chef-nsq.git", "ssh_url": "git@github.com:mreiferson/chef-nsq.git", "clone_url": "https://github.com/mreiferson/chef-nsq.git", "svn_url": "https://github.com/mreiferson/chef-nsq", "homepage": null, "size": 132, "stargazers_count": 0, "watchers_count": 0, "language": "Ruby", "has_issues": false, "has_downloads": true, "has_wiki": false, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 5287337, "name": "dablooms", "full_name": "mreiferson/dablooms", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/dablooms", "description": "scaling, counting, bloom filter library", "fork": true, "url": "https://api.github.com/repos/mreiferson/dablooms", "forks_url": "https://api.github.com/repos/mreiferson/dablooms/forks", "keys_url": "https://api.github.com/repos/mreiferson/dablooms/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/dablooms/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/dablooms/teams", "hooks_url": "https://api.github.com/repos/mreiferson/dablooms/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/dablooms/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/dablooms/events", "assignees_url": "https://api.github.com/repos/mreiferson/dablooms/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/dablooms/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/dablooms/tags", "blobs_url": "https://api.github.com/repos/mreiferson/dablooms/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/dablooms/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/dablooms/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/dablooms/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/dablooms/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/dablooms/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/dablooms/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/dablooms/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/dablooms/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/dablooms/subscription", "commits_url": "https://api.github.com/repos/mreiferson/dablooms/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/dablooms/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/dablooms/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/dablooms/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/dablooms/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/dablooms/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/dablooms/merges", "archive_url": "https://api.github.com/repos/mreiferson/dablooms/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/dablooms/downloads", "issues_url": "https://api.github.com/repos/mreiferson/dablooms/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/dablooms/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/dablooms/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/dablooms/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/dablooms/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/dablooms/releases{/id}", "created_at": "2012-08-03T16:03:50Z", "updated_at": "2013-03-08T15:37:44Z", "pushed_at": "2013-03-08T15:37:44Z", "git_url": "git://github.com/mreiferson/dablooms.git", "ssh_url": "git@github.com:mreiferson/dablooms.git", "clone_url": "https://github.com/mreiferson/dablooms.git", "svn_url": "https://github.com/mreiferson/dablooms", "homepage": "", "size": 186, "stargazers_count": 1, "watchers_count": 1, "language": "C", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 2861959, "name": "dod", "full_name": "mreiferson/dod", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/dod", "description": "Do or Die - an incomplete real-time strategy game inspired by Warcraft (1997)", "fork": false, "url": "https://api.github.com/repos/mreiferson/dod", "forks_url": "https://api.github.com/repos/mreiferson/dod/forks", "keys_url": "https://api.github.com/repos/mreiferson/dod/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/dod/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/dod/teams", "hooks_url": "https://api.github.com/repos/mreiferson/dod/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/dod/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/dod/events", "assignees_url": "https://api.github.com/repos/mreiferson/dod/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/dod/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/dod/tags", "blobs_url": "https://api.github.com/repos/mreiferson/dod/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/dod/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/dod/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/dod/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/dod/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/dod/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/dod/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/dod/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/dod/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/dod/subscription", "commits_url": "https://api.github.com/repos/mreiferson/dod/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/dod/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/dod/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/dod/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/dod/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/dod/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/dod/merges", "archive_url": "https://api.github.com/repos/mreiferson/dod/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/dod/downloads", "issues_url": "https://api.github.com/repos/mreiferson/dod/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/dod/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/dod/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/dod/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/dod/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/dod/releases{/id}", "created_at": "2011-11-27T17:33:19Z", "updated_at": "2014-05-13T00:56:53Z", "pushed_at": "2011-11-29T02:08:57Z", "git_url": "git://github.com/mreiferson/dod.git", "ssh_url": "git@github.com:mreiferson/dod.git", "clone_url": "https://github.com/mreiferson/dod.git", "svn_url": "https://github.com/mreiferson/dod", "homepage": "", "size": 2044, "stargazers_count": 1, "watchers_count": 1, "language": "C++", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 4515792, "name": "doozer", "full_name": "mreiferson/doozer", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/doozer", "description": "Go client driver for doozerd, a consistent, distributed data store", "fork": true, "url": "https://api.github.com/repos/mreiferson/doozer", "forks_url": "https://api.github.com/repos/mreiferson/doozer/forks", "keys_url": "https://api.github.com/repos/mreiferson/doozer/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/doozer/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/doozer/teams", "hooks_url": "https://api.github.com/repos/mreiferson/doozer/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/doozer/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/doozer/events", "assignees_url": "https://api.github.com/repos/mreiferson/doozer/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/doozer/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/doozer/tags", "blobs_url": "https://api.github.com/repos/mreiferson/doozer/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/doozer/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/doozer/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/doozer/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/doozer/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/doozer/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/doozer/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/doozer/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/doozer/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/doozer/subscription", "commits_url": "https://api.github.com/repos/mreiferson/doozer/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/doozer/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/doozer/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/doozer/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/doozer/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/doozer/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/doozer/merges", "archive_url": "https://api.github.com/repos/mreiferson/doozer/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/doozer/downloads", "issues_url": "https://api.github.com/repos/mreiferson/doozer/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/doozer/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/doozer/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/doozer/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/doozer/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/doozer/releases{/id}", "created_at": "2012-06-01T03:41:14Z", "updated_at": "2013-03-16T15:23:56Z", "pushed_at": "2013-03-16T15:23:55Z", "git_url": "git://github.com/mreiferson/doozer.git", "ssh_url": "git@github.com:mreiferson/doozer.git", "clone_url": "https://github.com/mreiferson/doozer.git", "svn_url": "https://github.com/mreiferson/doozer", "homepage": "https://github.com/ha/doozerd", "size": 2584, "stargazers_count": 0, "watchers_count": 0, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 3391437, "name": "doozer-c", "full_name": "mreiferson/doozer-c", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/doozer-c", "description": "async C client library for doozerd", "fork": true, "url": "https://api.github.com/repos/mreiferson/doozer-c", "forks_url": "https://api.github.com/repos/mreiferson/doozer-c/forks", "keys_url": "https://api.github.com/repos/mreiferson/doozer-c/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/doozer-c/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/doozer-c/teams", "hooks_url": "https://api.github.com/repos/mreiferson/doozer-c/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/doozer-c/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/doozer-c/events", "assignees_url": "https://api.github.com/repos/mreiferson/doozer-c/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/doozer-c/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/doozer-c/tags", "blobs_url": "https://api.github.com/repos/mreiferson/doozer-c/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/doozer-c/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/doozer-c/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/doozer-c/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/doozer-c/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/doozer-c/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/doozer-c/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/doozer-c/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/doozer-c/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/doozer-c/subscription", "commits_url": "https://api.github.com/repos/mreiferson/doozer-c/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/doozer-c/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/doozer-c/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/doozer-c/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/doozer-c/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/doozer-c/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/doozer-c/merges", "archive_url": "https://api.github.com/repos/mreiferson/doozer-c/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/doozer-c/downloads", "issues_url": "https://api.github.com/repos/mreiferson/doozer-c/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/doozer-c/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/doozer-c/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/doozer-c/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/doozer-c/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/doozer-c/releases{/id}", "created_at": "2012-02-08T21:15:33Z", "updated_at": "2014-04-03T21:58:49Z", "pushed_at": "2012-11-21T16:46:25Z", "git_url": "git://github.com/mreiferson/doozer-c.git", "ssh_url": "git@github.com:mreiferson/doozer-c.git", "clone_url": "https://github.com/mreiferson/doozer-c.git", "svn_url": "https://github.com/mreiferson/doozer-c", "homepage": "", "size": 158, "stargazers_count": 0, "watchers_count": 0, "language": "C", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 4515795, "name": "doozerd", "full_name": "mreiferson/doozerd", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/doozerd", "description": "A consistent distributed data store.", "fork": true, "url": "https://api.github.com/repos/mreiferson/doozerd", "forks_url": "https://api.github.com/repos/mreiferson/doozerd/forks", "keys_url": "https://api.github.com/repos/mreiferson/doozerd/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/doozerd/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/doozerd/teams", "hooks_url": "https://api.github.com/repos/mreiferson/doozerd/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/doozerd/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/doozerd/events", "assignees_url": "https://api.github.com/repos/mreiferson/doozerd/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/doozerd/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/doozerd/tags", "blobs_url": "https://api.github.com/repos/mreiferson/doozerd/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/doozerd/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/doozerd/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/doozerd/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/doozerd/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/doozerd/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/doozerd/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/doozerd/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/doozerd/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/doozerd/subscription", "commits_url": "https://api.github.com/repos/mreiferson/doozerd/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/doozerd/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/doozerd/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/doozerd/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/doozerd/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/doozerd/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/doozerd/merges", "archive_url": "https://api.github.com/repos/mreiferson/doozerd/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/doozerd/downloads", "issues_url": "https://api.github.com/repos/mreiferson/doozerd/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/doozerd/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/doozerd/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/doozerd/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/doozerd/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/doozerd/releases{/id}", "created_at": "2012-06-01T03:41:32Z", "updated_at": "2013-12-28T19:22:30Z", "pushed_at": "2013-12-28T19:22:30Z", "git_url": "git://github.com/mreiferson/doozerd.git", "ssh_url": "git@github.com:mreiferson/doozerd.git", "clone_url": "https://github.com/mreiferson/doozerd.git", "svn_url": "https://github.com/mreiferson/doozerd", "homepage": "", "size": 3135, "stargazers_count": 0, "watchers_count": 0, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 8172002, "name": "e", "full_name": "mreiferson/e", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/e", "description": "Library containing high-performance datastructures and utilities for C++", "fork": true, "url": "https://api.github.com/repos/mreiferson/e", "forks_url": "https://api.github.com/repos/mreiferson/e/forks", "keys_url": "https://api.github.com/repos/mreiferson/e/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/e/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/e/teams", "hooks_url": "https://api.github.com/repos/mreiferson/e/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/e/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/e/events", "assignees_url": "https://api.github.com/repos/mreiferson/e/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/e/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/e/tags", "blobs_url": "https://api.github.com/repos/mreiferson/e/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/e/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/e/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/e/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/e/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/e/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/e/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/e/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/e/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/e/subscription", "commits_url": "https://api.github.com/repos/mreiferson/e/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/e/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/e/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/e/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/e/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/e/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/e/merges", "archive_url": "https://api.github.com/repos/mreiferson/e/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/e/downloads", "issues_url": "https://api.github.com/repos/mreiferson/e/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/e/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/e/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/e/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/e/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/e/releases{/id}", "created_at": "2013-02-13T02:42:55Z", "updated_at": "2013-02-18T21:10:07Z", "pushed_at": "2013-02-13T02:45:16Z", "git_url": "git://github.com/mreiferson/e.git", "ssh_url": "git@github.com:mreiferson/e.git", "clone_url": "https://github.com/mreiferson/e.git", "svn_url": "https://github.com/mreiferson/e", "homepage": "", "size": 437, "stargazers_count": 0, "watchers_count": 0, "language": "C++", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 2792604, "name": "encfs-macfusion2", "full_name": "mreiferson/encfs-macfusion2", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/encfs-macfusion2", "description": "enhanced version of encfs-macfusion2 plugin http://code.google.com/p/encfs-macfusion2/", "fork": false, "url": "https://api.github.com/repos/mreiferson/encfs-macfusion2", "forks_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/forks", "keys_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/teams", "hooks_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/events", "assignees_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/tags", "blobs_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/subscription", "commits_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/merges", "archive_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/downloads", "issues_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/encfs-macfusion2/releases{/id}", "created_at": "2011-11-17T01:58:01Z", "updated_at": "2013-10-22T06:29:03Z", "pushed_at": "2011-11-17T02:13:15Z", "git_url": "git://github.com/mreiferson/encfs-macfusion2.git", "ssh_url": "git@github.com:mreiferson/encfs-macfusion2.git", "clone_url": "https://github.com/mreiferson/encfs-macfusion2.git", "svn_url": "https://github.com/mreiferson/encfs-macfusion2", "homepage": "", "size": 195, "stargazers_count": 4, "watchers_count": 4, "language": "Objective-C", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 1, "mirror_url": null, "open_issues_count": 0, "forks": 1, "open_issues": 0, "watchers": 4, "default_branch": "master" }, { "id": 5263991, "name": "file2http", "full_name": "mreiferson/file2http", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/file2http", "description": "spray a line-oriented file at an HTTP endpoint", "fork": true, "url": "https://api.github.com/repos/mreiferson/file2http", "forks_url": "https://api.github.com/repos/mreiferson/file2http/forks", "keys_url": "https://api.github.com/repos/mreiferson/file2http/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/file2http/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/file2http/teams", "hooks_url": "https://api.github.com/repos/mreiferson/file2http/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/file2http/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/file2http/events", "assignees_url": "https://api.github.com/repos/mreiferson/file2http/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/file2http/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/file2http/tags", "blobs_url": "https://api.github.com/repos/mreiferson/file2http/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/file2http/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/file2http/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/file2http/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/file2http/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/file2http/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/file2http/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/file2http/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/file2http/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/file2http/subscription", "commits_url": "https://api.github.com/repos/mreiferson/file2http/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/file2http/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/file2http/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/file2http/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/file2http/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/file2http/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/file2http/merges", "archive_url": "https://api.github.com/repos/mreiferson/file2http/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/file2http/downloads", "issues_url": "https://api.github.com/repos/mreiferson/file2http/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/file2http/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/file2http/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/file2http/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/file2http/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/file2http/releases{/id}", "created_at": "2012-08-01T19:56:16Z", "updated_at": "2013-01-11T13:21:40Z", "pushed_at": "2012-12-21T15:44:32Z", "git_url": "git://github.com/mreiferson/file2http.git", "ssh_url": "git@github.com:mreiferson/file2http.git", "clone_url": "https://github.com/mreiferson/file2http.git", "svn_url": "https://github.com/mreiferson/file2http", "homepage": "", "size": 96, "stargazers_count": 1, "watchers_count": 1, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 15291117, "name": "gablog", "full_name": "mreiferson/gablog", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/gablog", "description": "Gopher Academy Blog -- fork of go.blog", "fork": true, "url": "https://api.github.com/repos/mreiferson/gablog", "forks_url": "https://api.github.com/repos/mreiferson/gablog/forks", "keys_url": "https://api.github.com/repos/mreiferson/gablog/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/gablog/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/gablog/teams", "hooks_url": "https://api.github.com/repos/mreiferson/gablog/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/gablog/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/gablog/events", "assignees_url": "https://api.github.com/repos/mreiferson/gablog/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/gablog/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/gablog/tags", "blobs_url": "https://api.github.com/repos/mreiferson/gablog/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/gablog/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/gablog/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/gablog/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/gablog/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/gablog/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/gablog/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/gablog/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/gablog/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/gablog/subscription", "commits_url": "https://api.github.com/repos/mreiferson/gablog/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/gablog/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/gablog/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/gablog/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/gablog/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/gablog/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/gablog/merges", "archive_url": "https://api.github.com/repos/mreiferson/gablog/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/gablog/downloads", "issues_url": "https://api.github.com/repos/mreiferson/gablog/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/gablog/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/gablog/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/gablog/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/gablog/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/gablog/releases{/id}", "created_at": "2013-12-18T18:38:37Z", "updated_at": "2013-12-20T22:39:33Z", "pushed_at": "2013-12-20T22:21:52Z", "git_url": "git://github.com/mreiferson/gablog.git", "ssh_url": "git@github.com:mreiferson/gablog.git", "clone_url": "https://github.com/mreiferson/gablog.git", "svn_url": "https://github.com/mreiferson/gablog", "homepage": "http://blog.gopheracademy.com", "size": 7911, "stargazers_count": 0, "watchers_count": 0, "language": "CSS", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 12223286, "name": "git-open-pull", "full_name": "mreiferson/git-open-pull", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/git-open-pull", "description": "convert a github issue into a pull request", "fork": true, "url": "https://api.github.com/repos/mreiferson/git-open-pull", "forks_url": "https://api.github.com/repos/mreiferson/git-open-pull/forks", "keys_url": "https://api.github.com/repos/mreiferson/git-open-pull/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/git-open-pull/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/git-open-pull/teams", "hooks_url": "https://api.github.com/repos/mreiferson/git-open-pull/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/git-open-pull/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/git-open-pull/events", "assignees_url": "https://api.github.com/repos/mreiferson/git-open-pull/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/git-open-pull/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/git-open-pull/tags", "blobs_url": "https://api.github.com/repos/mreiferson/git-open-pull/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/git-open-pull/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/git-open-pull/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/git-open-pull/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/git-open-pull/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/git-open-pull/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/git-open-pull/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/git-open-pull/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/git-open-pull/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/git-open-pull/subscription", "commits_url": "https://api.github.com/repos/mreiferson/git-open-pull/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/git-open-pull/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/git-open-pull/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/git-open-pull/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/git-open-pull/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/git-open-pull/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/git-open-pull/merges", "archive_url": "https://api.github.com/repos/mreiferson/git-open-pull/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/git-open-pull/downloads", "issues_url": "https://api.github.com/repos/mreiferson/git-open-pull/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/git-open-pull/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/git-open-pull/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/git-open-pull/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/git-open-pull/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/git-open-pull/releases{/id}", "created_at": "2013-08-19T17:45:47Z", "updated_at": "2014-03-03T19:50:11Z", "pushed_at": "2014-03-03T19:50:09Z", "git_url": "git://github.com/mreiferson/git-open-pull.git", "ssh_url": "git@github.com:mreiferson/git-open-pull.git", "clone_url": "https://github.com/mreiferson/git-open-pull.git", "svn_url": "https://github.com/mreiferson/git-open-pull", "homepage": "https://github.com/jehiah/git-open-pull", "size": 155, "stargazers_count": 0, "watchers_count": 0, "language": "Shell", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 9547968, "name": "go-hostpool", "full_name": "mreiferson/go-hostpool", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-hostpool", "description": "Intelligently and flexibly pool among multiple hosts from your Go application", "fork": true, "url": "https://api.github.com/repos/mreiferson/go-hostpool", "forks_url": "https://api.github.com/repos/mreiferson/go-hostpool/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-hostpool/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-hostpool/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-hostpool/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-hostpool/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-hostpool/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-hostpool/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-hostpool/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-hostpool/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-hostpool/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-hostpool/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-hostpool/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-hostpool/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-hostpool/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-hostpool/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-hostpool/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-hostpool/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-hostpool/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-hostpool/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-hostpool/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-hostpool/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-hostpool/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-hostpool/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-hostpool/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-hostpool/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-hostpool/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-hostpool/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-hostpool/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-hostpool/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-hostpool/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-hostpool/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-hostpool/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-hostpool/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-hostpool/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-hostpool/releases{/id}", "created_at": "2013-04-19T15:06:04Z", "updated_at": "2013-04-30T14:17:45Z", "pushed_at": "2013-04-30T14:17:44Z", "git_url": "git://github.com/mreiferson/go-hostpool.git", "ssh_url": "git@github.com:mreiferson/go-hostpool.git", "clone_url": "https://github.com/mreiferson/go-hostpool.git", "svn_url": "https://github.com/mreiferson/go-hostpool", "homepage": null, "size": 98, "stargazers_count": 0, "watchers_count": 0, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 3488675, "name": "go-httpclient", "full_name": "mreiferson/go-httpclient", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-httpclient", "description": "a Go HTTP client with timeouts", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-httpclient", "forks_url": "https://api.github.com/repos/mreiferson/go-httpclient/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-httpclient/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-httpclient/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-httpclient/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-httpclient/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-httpclient/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-httpclient/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-httpclient/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-httpclient/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-httpclient/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-httpclient/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-httpclient/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-httpclient/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-httpclient/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-httpclient/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-httpclient/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-httpclient/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-httpclient/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-httpclient/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-httpclient/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-httpclient/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-httpclient/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-httpclient/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-httpclient/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-httpclient/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-httpclient/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-httpclient/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-httpclient/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-httpclient/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-httpclient/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-httpclient/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-httpclient/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-httpclient/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-httpclient/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-httpclient/releases{/id}", "created_at": "2012-02-19T21:51:42Z", "updated_at": "2014-07-19T16:41:18Z", "pushed_at": "2014-04-25T16:53:03Z", "git_url": "git://github.com/mreiferson/go-httpclient.git", "ssh_url": "git@github.com:mreiferson/go-httpclient.git", "clone_url": "https://github.com/mreiferson/go-httpclient.git", "svn_url": "https://github.com/mreiferson/go-httpclient", "homepage": "", "size": 362, "stargazers_count": 167, "watchers_count": 167, "language": "Go", "has_issues": true, "has_downloads": true, "has_wiki": false, "forks_count": 21, "mirror_url": null, "open_issues_count": 0, "forks": 21, "open_issues": 0, "watchers": 167, "default_branch": "master" }, { "id": 3924124, "name": "go-install-as", "full_name": "mreiferson/go-install-as", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-install-as", "description": "a Go tool to install a package with a specific import path", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-install-as", "forks_url": "https://api.github.com/repos/mreiferson/go-install-as/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-install-as/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-install-as/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-install-as/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-install-as/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-install-as/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-install-as/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-install-as/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-install-as/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-install-as/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-install-as/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-install-as/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-install-as/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-install-as/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-install-as/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-install-as/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-install-as/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-install-as/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-install-as/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-install-as/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-install-as/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-install-as/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-install-as/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-install-as/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-install-as/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-install-as/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-install-as/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-install-as/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-install-as/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-install-as/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-install-as/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-install-as/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-install-as/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-install-as/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-install-as/releases{/id}", "created_at": "2012-04-04T00:17:37Z", "updated_at": "2014-06-29T20:11:46Z", "pushed_at": "2012-09-24T16:08:50Z", "git_url": "git://github.com/mreiferson/go-install-as.git", "ssh_url": "git@github.com:mreiferson/go-install-as.git", "clone_url": "https://github.com/mreiferson/go-install-as.git", "svn_url": "https://github.com/mreiferson/go-install-as", "homepage": "", "size": 107, "stargazers_count": 53, "watchers_count": 53, "language": "Shell", "has_issues": true, "has_downloads": true, "has_wiki": false, "forks_count": 2, "mirror_url": null, "open_issues_count": 0, "forks": 2, "open_issues": 0, "watchers": 53, "default_branch": "master" }, { "id": 4744067, "name": "go-notify", "full_name": "mreiferson/go-notify", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-notify", "description": "a Go package to observe notable events in a decoupled fashion", "fork": true, "url": "https://api.github.com/repos/mreiferson/go-notify", "forks_url": "https://api.github.com/repos/mreiferson/go-notify/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-notify/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-notify/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-notify/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-notify/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-notify/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-notify/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-notify/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-notify/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-notify/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-notify/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-notify/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-notify/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-notify/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-notify/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-notify/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-notify/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-notify/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-notify/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-notify/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-notify/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-notify/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-notify/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-notify/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-notify/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-notify/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-notify/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-notify/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-notify/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-notify/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-notify/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-notify/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-notify/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-notify/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-notify/releases{/id}", "created_at": "2012-06-21T20:30:43Z", "updated_at": "2013-01-10T18:07:58Z", "pushed_at": "2012-06-21T20:30:22Z", "git_url": "git://github.com/mreiferson/go-notify.git", "ssh_url": "git@github.com:mreiferson/go-notify.git", "clone_url": "https://github.com/mreiferson/go-notify.git", "svn_url": "https://github.com/mreiferson/go-notify", "homepage": null, "size": 68, "stargazers_count": 1, "watchers_count": 1, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 12449360, "name": "go-nsq", "full_name": "mreiferson/go-nsq", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-nsq", "description": "the official Go package for NSQ", "fork": true, "url": "https://api.github.com/repos/mreiferson/go-nsq", "forks_url": "https://api.github.com/repos/mreiferson/go-nsq/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-nsq/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-nsq/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-nsq/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-nsq/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-nsq/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-nsq/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-nsq/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-nsq/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-nsq/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-nsq/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-nsq/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-nsq/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-nsq/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-nsq/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-nsq/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-nsq/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-nsq/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-nsq/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-nsq/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-nsq/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-nsq/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-nsq/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-nsq/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-nsq/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-nsq/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-nsq/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-nsq/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-nsq/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-nsq/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-nsq/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-nsq/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-nsq/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-nsq/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-nsq/releases{/id}", "created_at": "2013-08-29T02:07:54Z", "updated_at": "2014-06-29T13:56:36Z", "pushed_at": "2014-07-20T16:44:32Z", "git_url": "git://github.com/mreiferson/go-nsq.git", "ssh_url": "git@github.com:mreiferson/go-nsq.git", "clone_url": "https://github.com/mreiferson/go-nsq.git", "svn_url": "https://github.com/mreiferson/go-nsq", "homepage": "", "size": 1783, "stargazers_count": 0, "watchers_count": 0, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": false, "forks_count": 1, "mirror_url": null, "open_issues_count": 0, "forks": 1, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 16654468, "name": "go-options", "full_name": "mreiferson/go-options", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-options", "description": "a Go package to structure and resolve options", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-options", "forks_url": "https://api.github.com/repos/mreiferson/go-options/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-options/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-options/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-options/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-options/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-options/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-options/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-options/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-options/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-options/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-options/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-options/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-options/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-options/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-options/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-options/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-options/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-options/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-options/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-options/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-options/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-options/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-options/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-options/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-options/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-options/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-options/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-options/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-options/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-options/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-options/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-options/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-options/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-options/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-options/releases{/id}", "created_at": "2014-02-08T22:19:33Z", "updated_at": "2014-02-16T00:39:59Z", "pushed_at": "2014-02-16T00:39:58Z", "git_url": "git://github.com/mreiferson/go-options.git", "ssh_url": "git@github.com:mreiferson/go-options.git", "clone_url": "https://github.com/mreiferson/go-options.git", "svn_url": "https://github.com/mreiferson/go-options", "homepage": null, "size": 128, "stargazers_count": 1, "watchers_count": 1, "language": "Go", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 3924909, "name": "go-simplejson", "full_name": "mreiferson/go-simplejson", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-simplejson", "description": "a Go package to interact with arbitrary JSON", "fork": true, "url": "https://api.github.com/repos/mreiferson/go-simplejson", "forks_url": "https://api.github.com/repos/mreiferson/go-simplejson/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-simplejson/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-simplejson/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-simplejson/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-simplejson/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-simplejson/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-simplejson/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-simplejson/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-simplejson/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-simplejson/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-simplejson/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-simplejson/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-simplejson/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-simplejson/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-simplejson/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-simplejson/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-simplejson/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-simplejson/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-simplejson/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-simplejson/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-simplejson/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-simplejson/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-simplejson/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-simplejson/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-simplejson/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-simplejson/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-simplejson/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-simplejson/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-simplejson/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-simplejson/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-simplejson/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-simplejson/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-simplejson/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-simplejson/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-simplejson/releases{/id}", "created_at": "2012-04-04T02:36:33Z", "updated_at": "2014-06-25T01:24:01Z", "pushed_at": "2014-06-30T15:13:50Z", "git_url": "git://github.com/mreiferson/go-simplejson.git", "ssh_url": "git@github.com:mreiferson/go-simplejson.git", "clone_url": "https://github.com/mreiferson/go-simplejson.git", "svn_url": "https://github.com/mreiferson/go-simplejson", "homepage": "", "size": 210, "stargazers_count": 1, "watchers_count": 1, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": false, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 8614089, "name": "go-simplelog", "full_name": "mreiferson/go-simplelog", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-simplelog", "description": "a simple logging package for Go (inspired by Tornado)", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-simplelog", "forks_url": "https://api.github.com/repos/mreiferson/go-simplelog/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-simplelog/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-simplelog/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-simplelog/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-simplelog/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-simplelog/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-simplelog/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-simplelog/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-simplelog/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-simplelog/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-simplelog/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-simplelog/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-simplelog/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-simplelog/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-simplelog/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-simplelog/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-simplelog/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-simplelog/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-simplelog/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-simplelog/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-simplelog/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-simplelog/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-simplelog/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-simplelog/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-simplelog/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-simplelog/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-simplelog/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-simplelog/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-simplelog/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-simplelog/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-simplelog/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-simplelog/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-simplelog/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-simplelog/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-simplelog/releases{/id}", "created_at": "2013-03-06T21:53:48Z", "updated_at": "2013-10-11T22:49:05Z", "pushed_at": "2013-03-31T23:20:11Z", "git_url": "git://github.com/mreiferson/go-simplelog.git", "ssh_url": "git@github.com:mreiferson/go-simplelog.git", "clone_url": "https://github.com/mreiferson/go-simplelog.git", "svn_url": "https://github.com/mreiferson/go-simplelog", "homepage": null, "size": 140, "stargazers_count": 1, "watchers_count": 1, "language": "Go", "has_issues": true, "has_downloads": true, "has_wiki": false, "forks_count": 1, "mirror_url": null, "open_issues_count": 0, "forks": 1, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 12498288, "name": "go-snappystream", "full_name": "mreiferson/go-snappystream", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-snappystream", "description": "a Go package for framed snappy streams", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-snappystream", "forks_url": "https://api.github.com/repos/mreiferson/go-snappystream/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-snappystream/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-snappystream/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-snappystream/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-snappystream/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-snappystream/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-snappystream/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-snappystream/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-snappystream/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-snappystream/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-snappystream/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-snappystream/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-snappystream/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-snappystream/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-snappystream/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-snappystream/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-snappystream/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-snappystream/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-snappystream/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-snappystream/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-snappystream/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-snappystream/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-snappystream/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-snappystream/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-snappystream/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-snappystream/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-snappystream/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-snappystream/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-snappystream/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-snappystream/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-snappystream/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-snappystream/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-snappystream/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-snappystream/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-snappystream/releases{/id}", "created_at": "2013-08-31T00:41:11Z", "updated_at": "2014-07-20T07:52:45Z", "pushed_at": "2013-09-17T21:00:14Z", "git_url": "git://github.com/mreiferson/go-snappystream.git", "ssh_url": "git@github.com:mreiferson/go-snappystream.git", "clone_url": "https://github.com/mreiferson/go-snappystream.git", "svn_url": "https://github.com/mreiferson/go-snappystream", "homepage": null, "size": 184, "stargazers_count": 21, "watchers_count": 21, "language": "Go", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 1, "mirror_url": null, "open_issues_count": 0, "forks": 1, "open_issues": 0, "watchers": 21, "default_branch": "master" }, { "id": 5183238, "name": "go-stat", "full_name": "mreiferson/go-stat", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-stat", "description": "performant instrumentation/profiling for Go", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-stat", "forks_url": "https://api.github.com/repos/mreiferson/go-stat/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-stat/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-stat/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-stat/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-stat/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-stat/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-stat/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-stat/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-stat/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-stat/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-stat/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-stat/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-stat/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-stat/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-stat/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-stat/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-stat/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-stat/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-stat/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-stat/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-stat/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-stat/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-stat/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-stat/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-stat/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-stat/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-stat/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-stat/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-stat/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-stat/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-stat/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-stat/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-stat/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-stat/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-stat/releases{/id}", "created_at": "2012-07-25T19:03:42Z", "updated_at": "2014-01-10T04:39:14Z", "pushed_at": "2012-07-25T19:04:37Z", "git_url": "git://github.com/mreiferson/go-stat.git", "ssh_url": "git@github.com:mreiferson/go-stat.git", "clone_url": "https://github.com/mreiferson/go-stat.git", "svn_url": "https://github.com/mreiferson/go-stat", "homepage": null, "size": 96, "stargazers_count": 1, "watchers_count": 1, "language": "Go", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 8662365, "name": "go-ujson", "full_name": "mreiferson/go-ujson", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/go-ujson", "description": "a pure Go port of ultrajson", "fork": false, "url": "https://api.github.com/repos/mreiferson/go-ujson", "forks_url": "https://api.github.com/repos/mreiferson/go-ujson/forks", "keys_url": "https://api.github.com/repos/mreiferson/go-ujson/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/go-ujson/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/go-ujson/teams", "hooks_url": "https://api.github.com/repos/mreiferson/go-ujson/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/go-ujson/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/go-ujson/events", "assignees_url": "https://api.github.com/repos/mreiferson/go-ujson/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/go-ujson/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/go-ujson/tags", "blobs_url": "https://api.github.com/repos/mreiferson/go-ujson/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/go-ujson/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/go-ujson/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/go-ujson/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/go-ujson/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/go-ujson/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/go-ujson/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/go-ujson/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/go-ujson/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/go-ujson/subscription", "commits_url": "https://api.github.com/repos/mreiferson/go-ujson/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/go-ujson/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/go-ujson/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/go-ujson/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/go-ujson/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/go-ujson/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/go-ujson/merges", "archive_url": "https://api.github.com/repos/mreiferson/go-ujson/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/go-ujson/downloads", "issues_url": "https://api.github.com/repos/mreiferson/go-ujson/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/go-ujson/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/go-ujson/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/go-ujson/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/go-ujson/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/go-ujson/releases{/id}", "created_at": "2013-03-08T23:57:54Z", "updated_at": "2014-06-26T01:50:40Z", "pushed_at": "2013-11-10T19:49:16Z", "git_url": "git://github.com/mreiferson/go-ujson.git", "ssh_url": "git@github.com:mreiferson/go-ujson.git", "clone_url": "https://github.com/mreiferson/go-ujson.git", "svn_url": "https://github.com/mreiferson/go-ujson", "homepage": "", "size": 140, "stargazers_count": 31, "watchers_count": 31, "language": "Go", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 8, "mirror_url": null, "open_issues_count": 0, "forks": 8, "open_issues": 0, "watchers": 31, "default_branch": "master" }, { "id": 12815437, "name": "godep", "full_name": "mreiferson/godep", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/godep", "description": "dependency tool for go", "fork": true, "url": "https://api.github.com/repos/mreiferson/godep", "forks_url": "https://api.github.com/repos/mreiferson/godep/forks", "keys_url": "https://api.github.com/repos/mreiferson/godep/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/godep/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/godep/teams", "hooks_url": "https://api.github.com/repos/mreiferson/godep/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/godep/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/godep/events", "assignees_url": "https://api.github.com/repos/mreiferson/godep/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/godep/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/godep/tags", "blobs_url": "https://api.github.com/repos/mreiferson/godep/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/godep/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/godep/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/godep/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/godep/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/godep/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/godep/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/godep/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/godep/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/godep/subscription", "commits_url": "https://api.github.com/repos/mreiferson/godep/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/godep/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/godep/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/godep/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/godep/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/godep/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/godep/merges", "archive_url": "https://api.github.com/repos/mreiferson/godep/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/godep/downloads", "issues_url": "https://api.github.com/repos/mreiferson/godep/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/godep/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/godep/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/godep/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/godep/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/godep/releases{/id}", "created_at": "2013-09-13T17:36:10Z", "updated_at": "2014-03-21T02:53:20Z", "pushed_at": "2014-01-05T18:07:02Z", "git_url": "git://github.com/mreiferson/godep.git", "ssh_url": "git@github.com:mreiferson/godep.git", "clone_url": "https://github.com/mreiferson/godep.git", "svn_url": "https://github.com/mreiferson/godep", "homepage": "http://godoc.org/github.com/kr/godep", "size": 196, "stargazers_count": 0, "watchers_count": 0, "language": "Go", "has_issues": false, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" }, { "id": 2862096, "name": "hajiworld", "full_name": "mreiferson/hajiworld", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/hajiworld", "description": "super mario clone (1999)", "fork": false, "url": "https://api.github.com/repos/mreiferson/hajiworld", "forks_url": "https://api.github.com/repos/mreiferson/hajiworld/forks", "keys_url": "https://api.github.com/repos/mreiferson/hajiworld/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/hajiworld/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/hajiworld/teams", "hooks_url": "https://api.github.com/repos/mreiferson/hajiworld/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/hajiworld/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/hajiworld/events", "assignees_url": "https://api.github.com/repos/mreiferson/hajiworld/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/hajiworld/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/hajiworld/tags", "blobs_url": "https://api.github.com/repos/mreiferson/hajiworld/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/hajiworld/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/hajiworld/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/hajiworld/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/hajiworld/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/hajiworld/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/hajiworld/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/hajiworld/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/hajiworld/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/hajiworld/subscription", "commits_url": "https://api.github.com/repos/mreiferson/hajiworld/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/hajiworld/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/hajiworld/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/hajiworld/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/hajiworld/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/hajiworld/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/hajiworld/merges", "archive_url": "https://api.github.com/repos/mreiferson/hajiworld/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/hajiworld/downloads", "issues_url": "https://api.github.com/repos/mreiferson/hajiworld/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/hajiworld/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/hajiworld/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/hajiworld/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/hajiworld/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/hajiworld/releases{/id}", "created_at": "2011-11-27T18:05:02Z", "updated_at": "2014-01-08T14:10:43Z", "pushed_at": "2011-11-29T02:49:49Z", "git_url": "git://github.com/mreiferson/hajiworld.git", "ssh_url": "git@github.com:mreiferson/hajiworld.git", "clone_url": "https://github.com/mreiferson/hajiworld.git", "svn_url": "https://github.com/mreiferson/hajiworld", "homepage": "", "size": 27872, "stargazers_count": 1, "watchers_count": 1, "language": "C++", "has_issues": true, "has_downloads": true, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 1, "default_branch": "master" }, { "id": 14853562, "name": "homebrew", "full_name": "mreiferson/homebrew", "owner": { "login": "mreiferson", "id": 187441, "avatar_url": "https://avatars.githubusercontent.com/u/187441?", "gravatar_id": "dd56a8e1de66aeedb987397511f830e7", "url": "https://api.github.com/users/mreiferson", "html_url": "https://github.com/mreiferson", "followers_url": "https://api.github.com/users/mreiferson/followers", "following_url": "https://api.github.com/users/mreiferson/following{/other_user}", "gists_url": "https://api.github.com/users/mreiferson/gists{/gist_id}", "starred_url": "https://api.github.com/users/mreiferson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mreiferson/subscriptions", "organizations_url": "https://api.github.com/users/mreiferson/orgs", "repos_url": "https://api.github.com/users/mreiferson/repos", "events_url": "https://api.github.com/users/mreiferson/events{/privacy}", "received_events_url": "https://api.github.com/users/mreiferson/received_events", "type": "User", "site_admin": false }, "private": false, "html_url": "https://github.com/mreiferson/homebrew", "description": "The missing package manager for OS X.", "fork": true, "url": "https://api.github.com/repos/mreiferson/homebrew", "forks_url": "https://api.github.com/repos/mreiferson/homebrew/forks", "keys_url": "https://api.github.com/repos/mreiferson/homebrew/keys{/key_id}", "collaborators_url": "https://api.github.com/repos/mreiferson/homebrew/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/mreiferson/homebrew/teams", "hooks_url": "https://api.github.com/repos/mreiferson/homebrew/hooks", "issue_events_url": "https://api.github.com/repos/mreiferson/homebrew/issues/events{/number}", "events_url": "https://api.github.com/repos/mreiferson/homebrew/events", "assignees_url": "https://api.github.com/repos/mreiferson/homebrew/assignees{/user}", "branches_url": "https://api.github.com/repos/mreiferson/homebrew/branches{/branch}", "tags_url": "https://api.github.com/repos/mreiferson/homebrew/tags", "blobs_url": "https://api.github.com/repos/mreiferson/homebrew/git/blobs{/sha}", "git_tags_url": "https://api.github.com/repos/mreiferson/homebrew/git/tags{/sha}", "git_refs_url": "https://api.github.com/repos/mreiferson/homebrew/git/refs{/sha}", "trees_url": "https://api.github.com/repos/mreiferson/homebrew/git/trees{/sha}", "statuses_url": "https://api.github.com/repos/mreiferson/homebrew/statuses/{sha}", "languages_url": "https://api.github.com/repos/mreiferson/homebrew/languages", "stargazers_url": "https://api.github.com/repos/mreiferson/homebrew/stargazers", "contributors_url": "https://api.github.com/repos/mreiferson/homebrew/contributors", "subscribers_url": "https://api.github.com/repos/mreiferson/homebrew/subscribers", "subscription_url": "https://api.github.com/repos/mreiferson/homebrew/subscription", "commits_url": "https://api.github.com/repos/mreiferson/homebrew/commits{/sha}", "git_commits_url": "https://api.github.com/repos/mreiferson/homebrew/git/commits{/sha}", "comments_url": "https://api.github.com/repos/mreiferson/homebrew/comments{/number}", "issue_comment_url": "https://api.github.com/repos/mreiferson/homebrew/issues/comments/{number}", "contents_url": "https://api.github.com/repos/mreiferson/homebrew/contents/{+path}", "compare_url": "https://api.github.com/repos/mreiferson/homebrew/compare/{base}...{head}", "merges_url": "https://api.github.com/repos/mreiferson/homebrew/merges", "archive_url": "https://api.github.com/repos/mreiferson/homebrew/{archive_format}{/ref}", "downloads_url": "https://api.github.com/repos/mreiferson/homebrew/downloads", "issues_url": "https://api.github.com/repos/mreiferson/homebrew/issues{/number}", "pulls_url": "https://api.github.com/repos/mreiferson/homebrew/pulls{/number}", "milestones_url": "https://api.github.com/repos/mreiferson/homebrew/milestones{/number}", "notifications_url": "https://api.github.com/repos/mreiferson/homebrew/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/mreiferson/homebrew/labels{/name}", "releases_url": "https://api.github.com/repos/mreiferson/homebrew/releases{/id}", "created_at": "2013-12-02T05:20:40Z", "updated_at": "2014-02-17T17:19:19Z", "pushed_at": "2014-02-17T17:06:03Z", "git_url": "git://github.com/mreiferson/homebrew.git", "ssh_url": "git@github.com:mreiferson/homebrew.git", "clone_url": "https://github.com/mreiferson/homebrew.git", "svn_url": "https://github.com/mreiferson/homebrew", "homepage": "http://brew.sh", "size": 29725, "stargazers_count": 0, "watchers_count": 0, "language": "Ruby", "has_issues": false, "has_downloads": false, "has_wiki": true, "forks_count": 0, "mirror_url": null, "open_issues_count": 0, "forks": 0, "open_issues": 0, "watchers": 0, "default_branch": "master" } ] `) golang-github-mreiferson-go-snappystream-0.2.3/reader.go000066400000000000000000000214571357640446100233550ustar00rootroot00000000000000package snappystream import ( "bytes" "fmt" "hash/crc32" "io" "io/ioutil" "github.com/mreiferson/go-snappystream/snappy-go" ) // errMssingStreamID is returned from a reader when the source stream does not // begin with a stream identifier block (4.1 Stream identifier). Its occurance // signifies that the source byte stream is not snappy framed. var errMissingStreamID = fmt.Errorf("missing stream identifier") type reader struct { reader io.Reader err error seenStreamID bool verifyChecksum bool buf bytes.Buffer hdr []byte src []byte dst []byte } // NewReader returns an io.Reader interface to the snappy framed stream format. // // It transparently handles reading the stream identifier (but does not proxy this // to the caller), decompresses blocks, and (optionally) validates checksums. // // Internally, three buffers are maintained. The first two are for reading // off the wrapped io.Reader and for holding the decompressed block (both are grown // automatically and re-used and will never exceed the largest block size, 65536). The // last buffer contains the *unread* decompressed bytes (and can grow indefinitely). // // The second param determines whether or not the reader will verify block // checksums and can be enabled/disabled with the constants VerifyChecksum and SkipVerifyChecksum // // For each Read, the returned length will be up to the lesser of len(b) or 65536 // decompressed bytes, regardless of the length of *compressed* bytes read // from the wrapped io.Reader. func NewReader(r io.Reader, verifyChecksum bool) io.Reader { return &reader{ reader: r, verifyChecksum: verifyChecksum, hdr: make([]byte, 4), src: make([]byte, 4096), dst: make([]byte, 4096), } } // WriteTo implements the io.WriterTo interface used by io.Copy. It writes // decoded data from the underlying reader to w. WriteTo returns the number of // bytes written along with any error encountered. func (r *reader) WriteTo(w io.Writer) (int64, error) { if r.err != nil { return 0, r.err } n, err := r.buf.WriteTo(w) if err != nil { // r.err doesn't need to be set because a write error occurred and the // stream hasn't been corrupted. return n, err } // pass a bufferFallbackWriter to nextFrame so that write errors may be // recovered from, allowing the unwritten stream to be read successfully. wfallback := &bufferFallbackWriter{ w: w, buf: &r.buf, } for { var m int m, err = r.nextFrame(wfallback) if wfallback.writerErr != nil && err == nil { // a partial write was made before an error occurred and not all m // bytes were writen to w. but decoded bytes were successfully // buffered and reading can resume later. n += wfallback.n return n, wfallback.writerErr } n += int64(m) if err == io.EOF { return n, nil } if err != nil { r.err = err return n, err } } panic("unreachable") } // bufferFallbackWriter writes to an underlying io.Writer until an error // occurs. If a error occurs in the underlying io.Writer the value is saved // for later inspection while the bufferFallbackWriter silently starts // buffering all data written to it. From the caller's perspective // bufferFallbackWriter has the same Write behavior has a bytes.Buffer. // // bufferFallbackWriter is useful for the reader.WriteTo method because it // allows internal decoding routines to avoid interruption (and subsequent // stream corruption) due to writing errors. type bufferFallbackWriter struct { w io.Writer buf *bytes.Buffer n int64 // number of bytes successfully written to w writerErr error // any error that ocurred writing to w } // Write attempts to write b to the underlying io.Writer. If the underlying // writer fails or has failed previously unwritten bytes are buffered // internally. Write never returns an error but may panic with // bytes.ErrTooLarge if the buffer grows too large. func (w *bufferFallbackWriter) Write(b []byte) (int, error) { if w.writerErr != nil { return w.buf.Write(b) } n, err := w.w.Write(b) w.n += int64(n) if err != nil { // begin buffering input. bytes.Buffer does not return errors and so we // do not need complex error handling here. w.writerErr = err w.Write(b[n:]) return len(b), nil } return n, nil } func (r *reader) read(b []byte) (int, error) { n, err := r.buf.Read(b) r.err = err return n, err } func (r *reader) Read(b []byte) (int, error) { if r.err != nil { return 0, r.err } if r.buf.Len() < len(b) { _, r.err = r.nextFrame(&r.buf) if r.err == io.EOF { // fill b with any remaining bytes in the buffer. return r.read(b) } if r.err != nil { return 0, r.err } } return r.read(b) } func (r *reader) nextFrame(w io.Writer) (int, error) { for { // read the 4-byte snappy frame header _, err := io.ReadFull(r.reader, r.hdr) if err != nil { return 0, err } // a stream identifier may appear anywhere and contains no information. // it must appear at the beginning of the stream. when found, validate // it and continue to the next block. if r.hdr[0] == blockStreamIdentifier { err := r.readStreamID() if err != nil { return 0, err } r.seenStreamID = true continue } if !r.seenStreamID { return 0, errMissingStreamID } switch typ := r.hdr[0]; { case typ == blockCompressed || typ == blockUncompressed: return r.decodeBlock(w) case typ == blockPadding || (0x80 <= typ && typ <= 0xfd): // skip blocks whose data must not be inspected (4.4 Padding, and 4.6 // Reserved skippable chunks). err := r.discardBlock() if err != nil { return 0, err } continue default: // typ must be unskippable range 0x02-0x7f. Read the block in full // and return an error (4.5 Reserved unskippable chunks). err = r.discardBlock() if err != nil { return 0, err } return 0, fmt.Errorf("unrecognized unskippable frame %#x", r.hdr[0]) } } panic("unreachable") } // decodeDataBlock assumes r.hdr[0] to be either blockCompressed or // blockUncompressed. func (r *reader) decodeBlock(w io.Writer) (int, error) { // read compressed block data and determine if uncompressed data is too // large. buf, err := r.readBlock() if err != nil { return 0, err } declen := len(buf[4:]) if r.hdr[0] == blockCompressed { declen, err = snappy.DecodedLen(buf[4:]) if err != nil { return 0, err } } if declen > MaxBlockSize { return 0, fmt.Errorf("decoded block data too large %d > %d", declen, MaxBlockSize) } // decode data and verify its integrity using the little-endian crc32 // preceding encoded data crc32le, blockdata := buf[:4], buf[4:] if r.hdr[0] == blockCompressed { r.dst, err = snappy.Decode(r.dst, blockdata) if err != nil { return 0, err } blockdata = r.dst } if r.verifyChecksum { checksum := unmaskChecksum(uint32(crc32le[0]) | uint32(crc32le[1])<<8 | uint32(crc32le[2])<<16 | uint32(crc32le[3])<<24) actualChecksum := crc32.Checksum(blockdata, crcTable) if checksum != actualChecksum { return 0, fmt.Errorf("checksum does not match %x != %x", checksum, actualChecksum) } } return w.Write(blockdata) } func (r *reader) readStreamID() error { // the length of the block is fixed so don't decode it from the header. if !bytes.Equal(r.hdr, streamID[:4]) { return fmt.Errorf("invalid stream identifier length") } // read the identifier block data "sNaPpY" block := r.src[:6] _, err := noeof(io.ReadFull(r.reader, block)) if err != nil { return err } if !bytes.Equal(block, streamID[4:]) { return fmt.Errorf("invalid stream identifier block") } return nil } func (r *reader) discardBlock() error { length := uint64(decodeLength(r.hdr[1:])) _, err := noeof64(io.CopyN(ioutil.Discard, r.reader, int64(length))) return err } func (r *reader) readBlock() ([]byte, error) { // check bounds on encoded length (+4 for checksum) length := decodeLength(r.hdr[1:]) if length > (maxEncodedBlockSize + 4) { return nil, fmt.Errorf("encoded block data too large %d > %d", length, (maxEncodedBlockSize + 4)) } if int(length) > len(r.src) { r.src = make([]byte, length) } buf := r.src[:length] _, err := noeof(io.ReadFull(r.reader, buf)) if err != nil { return nil, err } return buf, nil } // decodeLength decodes a 24-bit (3-byte) little-endian length from b. func decodeLength(b []byte) uint32 { return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 } func unmaskChecksum(c uint32) uint32 { x := c - 0xa282ead8 return ((x >> 17) | (x << 15)) } // noeof is used after reads in situations where EOF signifies invalid // formatting or corruption. func noeof(n int, err error) (int, error) { if err == io.EOF { return n, io.ErrUnexpectedEOF } return n, err } // noeof64 is used after long reads (e.g. io.Copy) in situations where io.EOF // signifies invalid formatting or corruption. func noeof64(n int64, err error) (int64, error) { if err == io.EOF { return n, io.ErrUnexpectedEOF } return n, err } golang-github-mreiferson-go-snappystream-0.2.3/reader_test.go000066400000000000000000000403771357640446100244160ustar00rootroot00000000000000package snappystream import ( "bytes" "crypto/rand" "fmt" "io" "io/ioutil" "strings" "testing" "github.com/mreiferson/go-snappystream/snappy-go" ) // This test checks that padding and reserved skippable blocks are ignored by // the reader. func TestReader_skippable(t *testing.T) { var buf bytes.Buffer // write some blocks with injected padding/skippable blocks w := NewWriter(&buf) write := func(p []byte) (int, error) { return w.Write(p) } writepad := func(b byte, n int) (int, error) { return buf.Write(opaqueChunk(b, n)) } _, err := write([]byte("hello")) if err != nil { t.Fatalf("write error: %v", err) } _, err = writepad(0xfe, 100) // normal padding if err != nil { t.Fatalf("write error: %v", err) } _, err = write([]byte(" ")) if err != nil { t.Fatalf("write error: %v", err) } _, err = writepad(0xa0, 100) // reserved skippable block if err != nil { t.Fatalf("write error: %v", err) } _, err = writepad(0xfe, MaxBlockSize) // normal padding if err != nil { t.Fatalf("write error: %v", err) } _, err = write([]byte("padding")) if err != nil { t.Fatalf("write error: %v", err) } p, err := ioutil.ReadAll(NewReader(&buf, true)) if err != nil { t.Fatalf("read error: %v", err) } if string(p) != "hello padding" { t.Fatalf("read: unexpected content %q", string(p)) } } // This test checks that reserved unskippable blocks are cause decoder errors. func TestReader_unskippable(t *testing.T) { var buf bytes.Buffer // write some blocks with injected padding/skippable blocks w := NewWriter(&buf) write := func(p []byte) (int, error) { return w.Write(p) } writepad := func(b byte, n int) (int, error) { return buf.Write(opaqueChunk(b, n)) } _, err := write([]byte("unskippable")) if err != nil { t.Fatalf("write error: %v", err) } _, err = writepad(0x50, 100) // unskippable reserved block if err != nil { t.Fatalf("write error: %v", err) } _, err = write([]byte(" blocks")) if err != nil { t.Fatalf("write error: %v", err) } _, err = ioutil.ReadAll(NewReader(&buf, true)) if err == nil { t.Fatalf("read success") } } func TestReaderStreamID(t *testing.T) { data := []byte("a snappy-framed data stream") var buf bytes.Buffer w := NewWriter(&buf) _, err := w.Write(data) if err != nil { t.Fatal(err) } stream := buf.Bytes() // sanity check: the stream can be decoded and starts with streamID r := NewReader(bytes.NewReader(stream), true) _, err = ioutil.ReadAll(r) if err != nil { t.Fatalf("read: %v", err) } if !bytes.HasPrefix(stream, streamID) { t.Fatal("missing stream id") } // streamNoID is valid except for a missing the streamID block streamNoID := bytes.TrimPrefix(stream, streamID) r = NewReader(bytes.NewReader(streamNoID), true) n, err := r.Read(make([]byte, 1)) if err == nil { t.Fatalf("read: expected an error reading input missing a stream identifier block") } if n != 0 { t.Fatalf("read: read non-zero number of bytes %d", n) } n, err = r.Read(make([]byte, 1)) if err == nil { t.Fatalf("read: successful read after missing stream id error") } if n != 0 { t.Fatalf("read: read non-zero number of bytes %d after missing stream id error", n) } } // This test validates the reader successfully decods a padding of maximal // size, 2^24 - 1. func TestReader_maxPad(t *testing.T) { buf := bytes.NewReader(bytes.Join([][]byte{ streamID, compressedChunk(t, []byte("a maximal padding chunk")), opaqueChunk(0xfe, (1<<24)-1), // normal padding compressedChunk(t, []byte(" is decoded successfully")), }, nil)) r := NewReader(buf, true) p, err := ioutil.ReadAll(r) if err != nil { t.Fatalf("read error: %v", err) } if string(p) != "a maximal padding chunk is decoded successfully" { t.Fatalf("read: unexpected content %q", string(p)) } } // This test validates the reader successfully decodes a skippable chunk of // maximal size, 2^24 - 1. func TestReader_maxSkippable(t *testing.T) { buf := bytes.NewReader(bytes.Join([][]byte{ streamID, compressedChunk(t, []byte("a maximal skippable chunk")), opaqueChunk(0xce, (1<<24)-1), // reserved skippable chunk compressedChunk(t, []byte(" is decoded successfully")), }, nil)) r := NewReader(buf, true) p, err := ioutil.ReadAll(r) if err != nil { t.Fatalf("read error: %v", err) } if string(p) != "a maximal skippable chunk is decoded successfully" { t.Fatalf("read: unexpected content %q", string(p)) } } // TestReader_maxBlock validates bounds checking on encoded and decoded data // (4.2 Compressed Data). func TestReader_maxBlock(t *testing.T) { // decompressing a block with compressed length greater than MaxBlockSize // should succeed. buf := bytes.NewReader(bytes.Join([][]byte{ streamID, compressedChunkGreaterN(t, MaxBlockSize), }, nil)) r := NewReader(buf, true) b, err := ioutil.ReadAll(r) if err != nil { t.Fatal(err) } if len(b) != MaxBlockSize { t.Fatalf("bad read (%d bytes)", len(b)) } // decompressing should fail if the block with decompressed length greater // than MaxBlockSize. buf = bytes.NewReader(bytes.Join([][]byte{ streamID, compressedChunk(t, make([]byte, MaxBlockSize+1)), }, nil)) r = NewReader(buf, true) b, err = ioutil.ReadAll(r) if err == nil { t.Fatal("unexpected success") } if len(b) > 0 { t.Fatalf("unexpected read %q", b) } } // This test validates the reader's behavior encountering unskippable chunks of // maximal size, 2^24 - 1. The desired error to in this case is one reporting // an unskippable chunk, not a length error. func TestReader_maxUnskippable(t *testing.T) { // the first block should be decoded successfully. prefix := "a maximal unskippable chunk" buf := bytes.NewReader(bytes.Join([][]byte{ streamID, compressedChunk(t, []byte(prefix)), opaqueChunk(0x03, (1<<24)-1), // low end of the unskippable range compressedChunk(t, []byte(" failure must be reported as such")), }, nil)) p := make([]byte, len(prefix)) r := NewReader(buf, true) n, err := r.Read(p) if err != nil { t.Fatalf("read error: %v", err) } if n != len(p) { t.Fatalf("read: short read %d", n) } if string(p) != prefix { t.Fatalf("read: bad value %q", p) } n, err = r.Read(p) if err == nil { t.Fatalf("read: expected error") } if n > 0 { t.Fatalf("read: read %d more bytes than expected", n) } if !strings.Contains(err.Error(), "unskippable") { t.Fatalf("read error: %v", err) } } // This test validates errors returned when data blocks exceed size limits. func TestReader_blockTooLarge(t *testing.T) { // the compressed chunk size is within the allowed encoding size // (maxEncodedBlockSize). but the uncompressed data is larger than allowed. badstream := bytes.Join([][]byte{ streamID, compressedChunk(t, make([]byte, (1<<24)-5)), }, nil) r := NewReader(bytes.NewBuffer(badstream), true) p := make([]byte, 1) n, err := r.Read(p) if err == nil { t.Fatalf("read: expected error") } if n != 0 { t.Fatalf("read: read data from the stream") } // the compressed chunk size is within the allowed encoding size // (maxEncodedBlockSize). but the uncompressed data is larger than allowed. badstream = bytes.Join([][]byte{ streamID, uncompressedChunk(t, make([]byte, (1<<24)-5)), }, nil) r = NewReader(bytes.NewBuffer(badstream), true) p = make([]byte, 1) n, err = r.Read(p) if err == nil { t.Fatalf("read: expected error") } if n != 0 { t.Fatalf("read: read data from the stream") } } // This test validates the reader's handling of corrupt chunks. func TestReader_corruption(t *testing.T) { // corruptID is a corrupt stream identifier corruptID := append([]byte(nil), streamID...) corruptID = bytes.Replace(streamID, []byte("p"), []byte("P"), -1) // corrupt "sNaPpY" data badstream := corruptID r := NewReader(bytes.NewBuffer(badstream), true) p := make([]byte, 1) n, err := r.Read(p) if err == nil { t.Fatalf("read: expected error") } if err == io.EOF { t.Fatalf("read: unexpected eof") } if n != 0 { t.Fatalf("read: read data from the stream") } corruptID = append([]byte(nil), streamID...) // corrupt the length corruptID[1] = 0x00 badstream = corruptID r = NewReader(bytes.NewBuffer(badstream), true) p = make([]byte, 1) n, err = r.Read(p) if err == nil { t.Fatalf("read: expected error") } if err == io.EOF { t.Fatalf("read: unexpected eof") } if n != 0 { t.Fatalf("read: read data from the stream") } // chunk is a valid compressed block chunk := compressedChunk(t, []byte("a data block")) // corrupt is a corrupt chunk corrupt := append([]byte(nil), chunk...) copy(corrupt[8:], make([]byte, 10)) // corrupt snappy-encoded data badstream = bytes.Join([][]byte{ streamID, corrupt, }, nil) r = NewReader(bytes.NewBuffer(badstream), true) p = make([]byte, 1) n, err = r.Read(p) if err == nil { t.Fatalf("read: expected error") } if err == io.EOF { t.Fatalf("read: unexpected eof") } if n != 0 { t.Fatalf("read: read data from the stream") } corrupt = append([]byte(nil), chunk...) copy(corrupt[4:8], make([]byte, 4)) // crc checksum failure badstream = bytes.Join([][]byte{ streamID, corrupt, }, nil) r = NewReader(bytes.NewBuffer(badstream), true) p = make([]byte, 1) n, err = r.Read(p) if err == nil { t.Fatalf("read: expected error") } if err == io.EOF { t.Fatalf("read: unexpected eof") } if n != 0 { t.Fatalf("read: read data from the stream") } } // This test ensures that reader returns io.ErrUnexpectedEOF at the appropriate // times. io.EOF must be reserved for the case when all data has been // successfully decoded. func TestReader_unexpectedEOF(t *testing.T) { var decodeBuffer [64 << 10]byte for _, test := range [][]byte{ // truncated streamIDs streamID[:4], streamID[:len(streamID)-1], // truncated data blocks bytes.Join([][]byte{ streamID, compressedChunk(t, bytes.Repeat([]byte("abc"), 100))[:2], }, nil), bytes.Join([][]byte{ streamID, compressedChunk(t, bytes.Repeat([]byte("abc"), 100))[:7], }, nil), // truncated padding bytes.Join([][]byte{ streamID, opaqueChunk(0xfe, 100)[:1], }, nil), bytes.Join([][]byte{ streamID, opaqueChunk(0xfe, 100)[:8], }, nil), // truncated skippable chunk bytes.Join([][]byte{ streamID, opaqueChunk(0xcf, 100)[:3], }, nil), bytes.Join([][]byte{ streamID, opaqueChunk(0xcf, 100)[:7], }, nil), // truncated unskippable chunk bytes.Join([][]byte{ streamID, opaqueChunk(0x03, 100)[:3], }, nil), bytes.Join([][]byte{ streamID, opaqueChunk(0x03, 100)[:5], }, nil), } { r := NewReader(bytes.NewReader(test), true) n, err := r.Read(decodeBuffer[:]) if err == nil { t.Errorf("read bad streamID: expected error") } if err != io.ErrUnexpectedEOF { t.Errorf("read bad streamID: %v", err) } if n != 0 { t.Errorf("read bad streamID: expected read length %d", n) } } } var errNotEnoughEntropy = fmt.Errorf("inadequate entropy in PRNG") // compressedChunkGreaterN like compressedChunk produces a single, compressed, // snappy-framed block. The returned block will have decoded length at most n // and encoded length greater than n. func compressedChunkGreaterN(t *testing.T, n int) []byte { decoded := make([]byte, n) var numTries int var encoded []byte for len(encoded) <= n && numTries < 3 { numTries++ nrd, err := io.ReadFull(rand.Reader, decoded) if err != nil { t.Errorf("crypto/rand: %v", err) return nil } if nrd != n { t.Errorf("crypto/rand: bad read (%d bytes)", nrd) return nil } encoded, err = snappy.Encode(encoded[:cap(encoded)], decoded) if err != nil { t.Errorf("snappy: %v", err) return nil } } if len(encoded) <= n { t.Error(errNotEnoughEntropy) return nil } return compressedChunk(t, decoded) } // compressedChunk encodes b returning a single, compressed, snappy-framed // block. compressedChunk can encode source data larger than allowed in the // specification. func compressedChunk(t *testing.T, src []byte) []byte { encoded, err := snappy.Encode(nil, src) if err != nil { t.Errorf("snappy: %v", err) return nil } if len(encoded) > (1<<24)-5 { // account for the 4-byte checksum t.Errorf("block data too large %d", len(src)) return nil } chunk := make([]byte, len(encoded)+8) writeHeader(chunk[:8], blockCompressed, encoded, src) copy(chunk[8:], encoded) return chunk } // uncompressedChunk encodes b returning a single, uncompressed, snappy-framed // block. uncompressedChunk can encode chunks larger than allowed by the // specification. func uncompressedChunk(t *testing.T, src []byte) []byte { if len(src) > (1<<24)-5 { // account for the 4-byte checksum t.Errorf("block data too large %d", len(src)) return nil } chunk := make([]byte, len(src)+8) writeHeader(chunk[:8], blockUncompressed, src, src) copy(chunk[8:], src) return chunk } // opaqueChunk returns an opaque b chunk (e.g. padding 0xfe) with length n // (total length, n+4 bytes). practically useless but good enough for testing. // the first 4-bytes of data are random to ensure checksums are not being // verified. func opaqueChunk(b byte, n int) []byte { if b == 0 { b = 0xfe } length := uint32(n) lengthle := []byte{byte(length), byte(length >> 8), byte(length >> 16)} checksum := make([]byte, 4) _, err := rand.Read(checksum) if err != nil { panic(err) } padbytes := make([]byte, n-4) // let this panic if n < 4 _, err = rand.Read(padbytes) if err != nil { panic(err) } var h []byte h = append(h, b) h = append(h, lengthle...) h = append(h, checksum...) h = append(h, padbytes...) return h } func TestReaderWriteTo(t *testing.T) { var encbuf bytes.Buffer var decbuf bytes.Buffer msg := "hello copy interface" w := NewWriter(&encbuf) n, err := io.WriteString(w, msg) if err != nil { t.Fatalf("encode: %v", err) } if n != len(msg) { t.Fatalf("encode: %v", err) } r := NewReader(&encbuf, true) n64, err := r.(*reader).WriteTo(&decbuf) if err != nil { t.Fatalf("decode: %v", err) } if n64 != int64(len(msg)) { t.Fatalf("decode: decoded %d bytes %q", n64, decbuf.Bytes()) } decmsg := decbuf.String() if decmsg != msg { t.Fatalf("decode: %q", decmsg) } } func TestReaderWriteToPreviousError(t *testing.T) { // construct an io.Reader that returns an error on the first read and a // valid snappy-framed stream on subsequent reads. var stream io.Reader stream = encodedString("hello") stream = readErrorFirst(stream, fmt.Errorf("one time error")) stream = NewReader(stream, true) var buf bytes.Buffer // attempt the first read from the stream. n, err := stream.(*reader).WriteTo(&buf) if err == nil { t.Fatalf("error expected") } if n != 0 { t.Fatalf("bytes written to buffer: %q", buf.Bytes()) } // attempt a second read from the stream. n, err = stream.(*reader).WriteTo(&buf) if err == nil { t.Fatalf("error expected") } if n != 0 { t.Fatalf("bytes written to buffer: %q", buf.Bytes()) } } // readerErrorFirst is an io.Reader that returns an error on the first read. // readerErrorFirst is used to test that a reader does not attempt to read // after a read error occurs. type readerErrorFirst struct { r io.Reader err error count int } func readErrorFirst(r io.Reader, err error) io.Reader { return &readerErrorFirst{ r: r, err: err, } } func (r *readerErrorFirst) Read(b []byte) (int, error) { r.count++ if r.count == 1 { return 0, r.err } return r.r.Read(b) } func TestReaderWriteToWriteError(t *testing.T) { origmsg := "hello" stream := NewReader(encodedString(origmsg), true) // attempt to write the stream to an io.Writer that will not accept input. n, err := stream.(*reader).WriteTo(unwritable(fmt.Errorf("cannot write to this writer"))) if err == nil { t.Fatalf("error expected") } if n != 0 { t.Fatalf("reported %d written to an unwritable writer", n) } // the decoded message can still be read successfully because the encoded // stream was not corrupt/broken. var buf bytes.Buffer n, err = stream.(*reader).WriteTo(&buf) if err != nil { t.Errorf("unexpected error: %v", err) } if n != int64(len(origmsg)) { t.Errorf("read %d bytes", n) } if buf.String() != origmsg { t.Errorf("read %q", buf) } } // writerUnwritable is an io.Writer that always returns an error. type writerUnwritable struct { err error } func (w *writerUnwritable) Write([]byte) (int, error) { return 0, w.err } func unwritable(err error) io.Writer { return &writerUnwritable{err} } func encodedString(s string) io.Reader { var buf bytes.Buffer w := NewWriter(&buf) io.WriteString(w, s) return &buf } golang-github-mreiferson-go-snappystream-0.2.3/readwrite_test.go000066400000000000000000000273141357640446100251360ustar00rootroot00000000000000package snappystream import ( "bytes" "crypto/rand" "io" "io/ioutil" "testing" ) const TestFileSize = 10 << 20 // 10MB // dummyBytesReader returns an io.Reader that avoids buffering optimizations // in io.Copy. This can be considered a 'worst-case' io.Reader as far as writer // frame alignment goes. // // Note: io.Copy uses a 32KB buffer internally as of Go 1.3, but that isn't // part of its public API (undocumented). func dummyBytesReader(p []byte) io.Reader { return ioutil.NopCloser(bytes.NewReader(p)) } func testWriteThenRead(t *testing.T, name string, bs []byte) { var buf bytes.Buffer w := NewWriter(&buf) n, err := io.Copy(w, dummyBytesReader(bs)) if err != nil { t.Errorf("write %v: %v", name, err) return } if n != int64(len(bs)) { t.Errorf("write %v: wrote %d bytes (!= %d)", name, n, len(bs)) return } enclen := buf.Len() r := NewReader(&buf, true) gotbs, err := ioutil.ReadAll(r) if err != nil { t.Errorf("read %v: %v", name, err) return } n = int64(len(gotbs)) if n != int64(len(bs)) { t.Errorf("read %v: read %d bytes (!= %d)", name, n, len(bs)) return } if !bytes.Equal(gotbs, bs) { t.Errorf("%v: unequal decompressed content", name) return } c := float64(len(bs)) / float64(enclen) t.Logf("%v compression ratio %.03g (%d byte reduction)", name, c, len(bs)-enclen) } func testBufferedWriteThenRead(t *testing.T, name string, bs []byte) { var buf bytes.Buffer w := NewBufferedWriter(&buf) n, err := io.Copy(w, dummyBytesReader(bs)) if err != nil { t.Errorf("write %v: %v", name, err) return } if n != int64(len(bs)) { t.Errorf("write %v: wrote %d bytes (!= %d)", name, n, len(bs)) return } err = w.Close() if err != nil { t.Errorf("close %v: %v", name, err) return } enclen := buf.Len() r := NewReader(&buf, true) gotbs, err := ioutil.ReadAll(r) if err != nil { t.Errorf("read %v: %v", name, err) return } n = int64(len(gotbs)) if n != int64(len(bs)) { t.Errorf("read %v: read %d bytes (!= %d)", name, n, len(bs)) return } if !bytes.Equal(gotbs, bs) { t.Errorf("%v: unequal decompressed content", name) return } c := float64(len(bs)) / float64(enclen) t.Logf("%v compression ratio %.03g (%d byte reduction)", name, c, len(bs)-enclen) } func TestWriterReader(t *testing.T) { testWriteThenRead(t, "simple", []byte("test")) testWriteThenRead(t, "manpage", testDataMan) testWriteThenRead(t, "json", testDataJSON) p := make([]byte, TestFileSize) testWriteThenRead(t, "constant", p) _, err := rand.Read(p) if err != nil { t.Fatal(err) } testWriteThenRead(t, "random", p) } func TestBufferedWriterReader(t *testing.T) { testBufferedWriteThenRead(t, "simple", []byte("test")) testBufferedWriteThenRead(t, "manpage", testDataMan) testBufferedWriteThenRead(t, "json", testDataJSON) p := make([]byte, TestFileSize) testBufferedWriteThenRead(t, "constant", p) _, err := rand.Read(p) if err != nil { t.Fatal(err) } testBufferedWriteThenRead(t, "random", p) } func TestWriterChunk(t *testing.T) { var buf bytes.Buffer in := make([]byte, 128000) w := NewWriter(&buf) r := NewReader(&buf, VerifyChecksum) n, err := w.Write(in) if err != nil { t.Fatalf(err.Error()) } if n != len(in) { t.Fatalf("wrote wrong amount %d != %d", n, len(in)) } out := make([]byte, len(in)) n, err = io.ReadFull(r, out) if err != nil { t.Fatal(err) } if n != len(in) { t.Fatalf("read wrong amount %d != %d", n, len(in)) } if !bytes.Equal(out, in) { t.Fatalf("bytes not equal %v != %v", out, in) } } func BenchmarkWriterManpage(b *testing.B) { benchmarkWriterBytes(b, testDataMan) } func BenchmarkBufferedWriterManpage(b *testing.B) { benchmarkBufferedWriterBytes(b, testDataMan) } func BenchmarkBufferedWriterManpageNoCopy(b *testing.B) { benchmarkBufferedWriterBytesNoCopy(b, testDataMan) } func BenchmarkWriterJSON(b *testing.B) { benchmarkWriterBytes(b, testDataJSON) } func BenchmarkBufferedWriterJSON(b *testing.B) { benchmarkBufferedWriterBytes(b, testDataJSON) } func BenchmarkBufferedWriterJSONNoCopy(b *testing.B) { benchmarkBufferedWriterBytesNoCopy(b, testDataJSON) } // BenchmarkWriterRandom tests performance encoding effectively uncompressable // data. func BenchmarkWriterRandom(b *testing.B) { benchmarkWriterBytes(b, randBytes(b, TestFileSize)) } func BenchmarkBufferedWriterRandom(b *testing.B) { benchmarkBufferedWriterBytes(b, randBytes(b, TestFileSize)) } func BenchmarkBufferedWriterRandomNoCopy(b *testing.B) { benchmarkBufferedWriterBytesNoCopy(b, randBytes(b, TestFileSize)) } // BenchmarkWriterConstant tests performance encoding maximally compressible // data. func BenchmarkWriterConstant(b *testing.B) { benchmarkWriterBytes(b, make([]byte, TestFileSize)) } func BenchmarkBufferedWriterConstant(b *testing.B) { benchmarkBufferedWriterBytes(b, make([]byte, TestFileSize)) } func BenchmarkBufferedWriterConstantNoCopy(b *testing.B) { benchmarkBufferedWriterBytesNoCopy(b, make([]byte, TestFileSize)) } func benchmarkWriterBytes(b *testing.B, p []byte) { enc := func() io.WriteCloser { // wrap the normal writer so that it has a noop Close method. writer // does not implement ReaderFrom so this does not impact performance. return &nopWriteCloser{NewWriter(ioutil.Discard)} } benchmarkEncode(b, enc, p) } func benchmarkBufferedWriterBytes(b *testing.B, p []byte) { enc := func() io.WriteCloser { // the writer's ReaderFrom implemention will be used in the benchmark. return NewBufferedWriter(ioutil.Discard) } benchmarkEncode(b, enc, p) } func benchmarkBufferedWriterBytesNoCopy(b *testing.B, p []byte) { enc := func() io.WriteCloser { // the writer is wrapped as to hide it's ReaderFrom implemention. return &writeCloserNoCopy{NewBufferedWriter(ioutil.Discard)} } benchmarkEncode(b, enc, p) } // benchmarkEncode benchmarks the speed at which bytes can be copied from // bs into writers created by enc. func benchmarkEncode(b *testing.B, enc func() io.WriteCloser, bs []byte) { size := int64(len(bs)) b.SetBytes(size) b.StartTimer() for i := 0; i < b.N; i++ { w := enc() n, err := io.Copy(w, dummyBytesReader(bs)) if err != nil { b.Fatal(err) } if n != size { b.Fatalf("wrote wrong amount %d != %d", n, size) } err = w.Close() if err != nil { b.Fatalf("close: %v", err) } } b.StopTimer() } func BenchmarkReaderManpage(b *testing.B) { encodeAndBenchmarkReader(b, testDataMan) } func BenchmarkReaderManpage_buffered(b *testing.B) { encodeAndBenchmarkReader_buffered(b, testDataMan) } func BenchmarkReaderManpageNoCopy(b *testing.B) { encodeAndBenchmarkReaderNoCopy(b, testDataMan) } func BenchmarkReaderJSON(b *testing.B) { encodeAndBenchmarkReader(b, testDataJSON) } func BenchmarkReaderJSON_buffered(b *testing.B) { encodeAndBenchmarkReader_buffered(b, testDataJSON) } func BenchmarkReaderJSONNoCopy(b *testing.B) { encodeAndBenchmarkReaderNoCopy(b, testDataJSON) } // BenchmarkReaderRandom tests decoding of effectively uncompressable data. func BenchmarkReaderRandom(b *testing.B) { encodeAndBenchmarkReader(b, randBytes(b, TestFileSize)) } func BenchmarkReaderRandom_buffered(b *testing.B) { encodeAndBenchmarkReader_buffered(b, randBytes(b, TestFileSize)) } func BenchmarkReaderRandomNoCopy(b *testing.B) { encodeAndBenchmarkReaderNoCopy(b, randBytes(b, TestFileSize)) } // BenchmarkReaderConstant tests decoding of maximally compressible data. func BenchmarkReaderConstant(b *testing.B) { encodeAndBenchmarkReader(b, make([]byte, TestFileSize)) } func BenchmarkReaderConstant_buffered(b *testing.B) { encodeAndBenchmarkReader_buffered(b, make([]byte, TestFileSize)) } func BenchmarkReaderConstantNoCopy(b *testing.B) { encodeAndBenchmarkReaderNoCopy(b, make([]byte, TestFileSize)) } // encodeAndBenchmarkReader is a helper that benchmarks the package // reader's performance given p encoded as a snappy framed stream. // // encodeAndBenchmarkReader benchmarks decoding of streams containing // (multiple) short frames. func encodeAndBenchmarkReader(b *testing.B, p []byte) { enc, err := encodeStreamBytes(p, false) if err != nil { b.Fatalf("pre-benchmark compression: %v", err) } dec := func(r io.Reader) io.Reader { return NewReader(r, VerifyChecksum) } benchmarkDecode(b, dec, int64(len(p)), enc) } // encodeAndBenchmarkReader_buffered is a helper that benchmarks the // package reader's performance given p encoded as a snappy framed stream. // // encodeAndBenchmarkReader_buffered benchmarks decoding of streams that // contain at most one short frame (at the end). func encodeAndBenchmarkReader_buffered(b *testing.B, p []byte) { enc, err := encodeStreamBytes(p, true) if err != nil { b.Fatalf("pre-benchmark compression: %v", err) } dec := func(r io.Reader) io.Reader { return NewReader(r, VerifyChecksum) } benchmarkDecode(b, dec, int64(len(p)), enc) } // encodeAndBenchmarkReaderNoCopy is a helper that benchmarks the // package reader's performance given p encoded as a snappy framed stream. // encodeAndBenchmarReaderNoCopy avoids use of the reader's io.WriterTo // interface. // // encodeAndBenchmarkReaderNoCopy benchmarks decoding of streams that // contain at most one short frame (at the end). func encodeAndBenchmarkReaderNoCopy(b *testing.B, p []byte) { enc, err := encodeStreamBytes(p, true) if err != nil { b.Fatalf("pre-benchmark compression: %v", err) } dec := func(r io.Reader) io.Reader { return ioutil.NopCloser(NewReader(r, VerifyChecksum)) } benchmarkDecode(b, dec, int64(len(p)), enc) } // benchmarkDecode runs a benchmark that repeatedly decoded snappy // framed bytes enc. The length of the decoded result in each iteration must // equal size. func benchmarkDecode(b *testing.B, dec func(io.Reader) io.Reader, size int64, enc []byte) { b.SetBytes(int64(len(enc))) // BUG this is probably wrong b.ResetTimer() for i := 0; i < b.N; i++ { r := dec(bytes.NewReader(enc)) n, err := io.Copy(ioutil.Discard, r) if err != nil { b.Fatalf(err.Error()) } if n != size { b.Fatalf("read wrong amount %d != %d", n, size) } } b.StopTimer() } // encodeStreamBytes is like encodeStream but operates on a byte slice. // encodeStreamBytes ensures that long streams are not maximally compressed if // buffer is false. func encodeStreamBytes(b []byte, buffer bool) ([]byte, error) { return encodeStream(dummyBytesReader(b), buffer) } // encodeStream encodes data read from r as a snappy framed stream and returns // the result as a byte slice. if buffer is true the bytes from r are buffered // to improve the resulting slice's compression ratio. func encodeStream(r io.Reader, buffer bool) ([]byte, error) { var buf bytes.Buffer if !buffer { w := NewWriter(&buf) _, err := io.Copy(w, r) if err != nil { return nil, err } return buf.Bytes(), nil } w := NewBufferedWriter(&buf) _, err := io.Copy(w, r) if err != nil { return nil, err } err = w.Close() if err != nil { return nil, err } return buf.Bytes(), nil } // randBytes reads size bytes from the computer's cryptographic random source. // the resulting bytes have approximately maximal entropy and are effectively // uncompressible with any algorithm. func randBytes(b *testing.B, size int) []byte { randp := make([]byte, size) _, err := io.ReadFull(rand.Reader, randp) if err != nil { b.Fatal(err) } return randp } // writeCloserNoCopy is an io.WriteCloser that simply wraps another // io.WriteCloser. This is useful for masking implementations for interfaces // like ReaderFrom which may be opted into use inside functions like io.Copy. type writeCloserNoCopy struct { io.WriteCloser } // nopWriteCloser is an io.WriteCloser that has a noop Close method. This type // has the effect of masking the underlying writer's Close implementation if it // has one, or satisfying interface implementations for writers that do not // need to be closing. type nopWriteCloser struct { io.Writer } func (w *nopWriteCloser) Close() error { return nil } golang-github-mreiferson-go-snappystream-0.2.3/snappy-go/000077500000000000000000000000001357640446100234705ustar00rootroot00000000000000golang-github-mreiferson-go-snappystream-0.2.3/snappy-go/decode.go000066400000000000000000000054521357640446100252500ustar00rootroot00000000000000// Copyright 2011 The Snappy-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. package snappy import ( "encoding/binary" "errors" ) // ErrCorrupt reports that the input is invalid. var ErrCorrupt = errors.New("snappy: corrupt input") // DecodedLen returns the length of the decoded block. func DecodedLen(src []byte) (int, error) { v, _, err := decodedLen(src) return v, err } // decodedLen returns the length of the decoded block and the number of bytes // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) if n == 0 { return 0, 0, ErrCorrupt } if uint64(int(v)) != v { return 0, 0, errors.New("snappy: decoded block is too large") } return int(v), n, nil } // Decode returns the decoded form of src. The returned slice may be a sub- // slice of dst if dst was large enough to hold the entire decoded block. // Otherwise, a newly allocated slice will be returned. // It is valid to pass a nil dst. func Decode(dst, src []byte) ([]byte, error) { dLen, s, err := decodedLen(src) if err != nil { return nil, err } if len(dst) < dLen { dst = make([]byte, dLen) } var d, offset, length int for s < len(src) { switch src[s] & 0x03 { case tagLiteral: x := uint(src[s] >> 2) switch { case x < 60: s += 1 case x == 60: s += 2 if s > len(src) { return nil, ErrCorrupt } x = uint(src[s-1]) case x == 61: s += 3 if s > len(src) { return nil, ErrCorrupt } x = uint(src[s-2]) | uint(src[s-1])<<8 case x == 62: s += 4 if s > len(src) { return nil, ErrCorrupt } x = uint(src[s-3]) | uint(src[s-2])<<8 | uint(src[s-1])<<16 case x == 63: s += 5 if s > len(src) { return nil, ErrCorrupt } x = uint(src[s-4]) | uint(src[s-3])<<8 | uint(src[s-2])<<16 | uint(src[s-1])<<24 } length = int(x + 1) if length <= 0 { return nil, errors.New("snappy: unsupported literal length") } if length > len(dst)-d || length > len(src)-s { return nil, ErrCorrupt } copy(dst[d:], src[s:s+length]) d += length s += length continue case tagCopy1: s += 2 if s > len(src) { return nil, ErrCorrupt } length = 4 + int(src[s-2])>>2&0x7 offset = int(src[s-2])&0xe0<<3 | int(src[s-1]) case tagCopy2: s += 3 if s > len(src) { return nil, ErrCorrupt } length = 1 + int(src[s-3])>>2 offset = int(src[s-2]) | int(src[s-1])<<8 case tagCopy4: return nil, errors.New("snappy: unsupported COPY_4 tag") } end := d + length if offset > d || end > len(dst) { return nil, ErrCorrupt } for ; d < end; d++ { dst[d] = dst[d-offset] } } if d != dLen { return nil, ErrCorrupt } return dst[:d], nil } golang-github-mreiferson-go-snappystream-0.2.3/snappy-go/encode.go000066400000000000000000000117051357640446100252600ustar00rootroot00000000000000// Copyright 2011 The Snappy-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. package snappy import ( "encoding/binary" ) // We limit how far copy back-references can go, the same as the C++ code. const maxOffset = 1 << 15 // emitLiteral writes a literal chunk and returns the number of bytes written. func emitLiteral(dst, lit []byte) int { i, n := 0, uint(len(lit)-1) switch { case n < 60: dst[0] = uint8(n)<<2 | tagLiteral i = 1 case n < 1<<8: dst[0] = 60<<2 | tagLiteral dst[1] = uint8(n) i = 2 case n < 1<<16: dst[0] = 61<<2 | tagLiteral dst[1] = uint8(n) dst[2] = uint8(n >> 8) i = 3 case n < 1<<24: dst[0] = 62<<2 | tagLiteral dst[1] = uint8(n) dst[2] = uint8(n >> 8) dst[3] = uint8(n >> 16) i = 4 case int64(n) < 1<<32: dst[0] = 63<<2 | tagLiteral dst[1] = uint8(n) dst[2] = uint8(n >> 8) dst[3] = uint8(n >> 16) dst[4] = uint8(n >> 24) i = 5 default: panic("snappy: source buffer is too long") } if copy(dst[i:], lit) != len(lit) { panic("snappy: destination buffer is too short") } return i + len(lit) } // emitCopy writes a copy chunk and returns the number of bytes written. func emitCopy(dst []byte, offset, length int) int { i := 0 for length > 0 { x := length - 4 if 0 <= x && x < 1<<3 && offset < 1<<11 { dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1 dst[i+1] = uint8(offset) i += 2 break } x = length if x > 1<<6 { x = 1 << 6 } dst[i+0] = uint8(x-1)<<2 | tagCopy2 dst[i+1] = uint8(offset) dst[i+2] = uint8(offset >> 8) i += 3 length -= x } return i } // Encode returns the encoded form of src. The returned slice may be a sub- // slice of dst if dst was large enough to hold the entire encoded block. // Otherwise, a newly allocated slice will be returned. // It is valid to pass a nil dst. func Encode(dst, src []byte) ([]byte, error) { if n := MaxEncodedLen(len(src)); len(dst) < n { dst = make([]byte, n) } // The block starts with the varint-encoded length of the decompressed bytes. d := binary.PutUvarint(dst, uint64(len(src))) // Return early if src is short. if len(src) <= 4 { if len(src) != 0 { d += emitLiteral(dst[d:], src) } return dst[:d], nil } // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. const maxTableSize = 1 << 14 shift, tableSize := uint(32-8), 1<<8 for tableSize < maxTableSize && tableSize < len(src) { shift-- tableSize *= 2 } var table [maxTableSize]int // Iterate over the source bytes. var ( s int // The iterator position. t int // The last position with the same hash as s. lit int // The start position of any pending literal bytes. ) for s+3 < len(src) { // Update the hash table. b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3] h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24 p := &table[(h*0x1e35a7bd)>>shift] // We need to to store values in [-1, inf) in table. To save // some initialization time, (re)use the table's zero value // and shift the values against this zero: add 1 on writes, // subtract 1 on reads. t, *p = *p-1, s+1 // If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte. if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] { s++ continue } // Otherwise, we have a match. First, emit any pending literal bytes. if lit != s { d += emitLiteral(dst[d:], src[lit:s]) } // Extend the match to be as long as possible. s0 := s s, t = s+4, t+4 for s < len(src) && src[s] == src[t] { s++ t++ } // Emit the copied bytes. d += emitCopy(dst[d:], s-t, s-s0) lit = s } // Emit any final pending literal bytes and return. if lit != len(src) { d += emitLiteral(dst[d:], src[lit:]) } return dst[:d], nil } // MaxEncodedLen returns the maximum length of a snappy block, given its // uncompressed length. func MaxEncodedLen(srcLen int) int { // Compressed data can be defined as: // compressed := item* literal* // item := literal* copy // // The trailing literal sequence has a space blowup of at most 62/60 // since a literal of length 60 needs one tag byte + one extra byte // for length information. // // Item blowup is trickier to measure. Suppose the "copy" op copies // 4 bytes of data. Because of a special check in the encoding code, // we produce a 4-byte copy only if the offset is < 65536. Therefore // the copy op takes 3 bytes to encode, and this type of item leads // to at most the 62/60 blowup for representing literals. // // Suppose the "copy" op copies 5 bytes of data. If the offset is big // enough, it will take 5 bytes to encode the copy op. Therefore the // worst case here is a one-byte literal followed by a five-byte copy. // That is, 6 bytes of input turn into 7 bytes of "compressed" data. // // This last factor dominates the blowup, so the final estimate is: return 32 + srcLen + srcLen/6 } golang-github-mreiferson-go-snappystream-0.2.3/snappy-go/snappy.go000066400000000000000000000032171357640446100253340ustar00rootroot00000000000000// Copyright 2011 The Snappy-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. // Package snappy implements the snappy block-based compression format. // It aims for very high speeds and reasonable compression. // // The C++ snappy implementation is at http://code.google.com/p/snappy/ package snappy /* Each encoded block begins with the varint-encoded length of the decoded data, followed by a sequence of chunks. Chunks begin and end on byte boundaries. The first byte of each chunk is broken into its 2 least and 6 most significant bits called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. Zero means a literal tag. All other values mean a copy tag. For literal tags: - If m < 60, the next 1 + m bytes are literal bytes. - Otherwise, let n be the little-endian unsigned integer denoted by the next m - 59 bytes. The next 1 + n bytes after that are literal bytes. For copy tags, length bytes are copied from offset bytes ago, in the style of Lempel-Ziv compression algorithms. In particular: - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 of the offset. The next byte is bits 0-7 of the offset. - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). The length is 1 + m. The offset is the little-endian unsigned integer denoted by the next 2 bytes. - For l == 3, this tag is a legacy format that is no longer supported. */ const ( tagLiteral = 0x00 tagCopy1 = 0x01 tagCopy2 = 0x02 tagCopy4 = 0x03 ) golang-github-mreiferson-go-snappystream-0.2.3/snappy-go/snappy_test.go000066400000000000000000000177551357640446100264070ustar00rootroot00000000000000// Copyright 2011 The Snappy-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. package snappy import ( "bytes" "flag" "fmt" "io" "io/ioutil" "math/rand" "net/http" "os" "path/filepath" "strings" "testing" ) var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks") func roundtrip(b, ebuf, dbuf []byte) error { e, err := Encode(ebuf, b) if err != nil { return fmt.Errorf("encoding error: %v", err) } d, err := Decode(dbuf, e) if err != nil { return fmt.Errorf("decoding error: %v", err) } if !bytes.Equal(b, d) { return fmt.Errorf("roundtrip mismatch:\n\twant %v\n\tgot %v", b, d) } return nil } func TestEmpty(t *testing.T) { if err := roundtrip(nil, nil, nil); err != nil { t.Fatal(err) } } func TestSmallCopy(t *testing.T) { for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} { for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} { for i := 0; i < 32; i++ { s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb" if err := roundtrip([]byte(s), ebuf, dbuf); err != nil { t.Errorf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err) } } } } } func TestSmallRand(t *testing.T) { rand.Seed(27354294) for n := 1; n < 20000; n += 23 { b := make([]byte, n) for i, _ := range b { b[i] = uint8(rand.Uint32()) } if err := roundtrip(b, nil, nil); err != nil { t.Fatal(err) } } } func TestSmallRegular(t *testing.T) { for n := 1; n < 20000; n += 23 { b := make([]byte, n) for i, _ := range b { b[i] = uint8(i%10 + 'a') } if err := roundtrip(b, nil, nil); err != nil { t.Fatal(err) } } } func benchDecode(b *testing.B, src []byte) { encoded, err := Encode(nil, src) if err != nil { b.Fatal(err) } // Bandwidth is in amount of uncompressed data. b.SetBytes(int64(len(src))) b.ResetTimer() for i := 0; i < b.N; i++ { Decode(src, encoded) } } func benchEncode(b *testing.B, src []byte) { // Bandwidth is in amount of uncompressed data. b.SetBytes(int64(len(src))) dst := make([]byte, MaxEncodedLen(len(src))) b.ResetTimer() for i := 0; i < b.N; i++ { Encode(dst, src) } } func readFile(b *testing.B, filename string) []byte { src, err := ioutil.ReadFile(filename) if err != nil { b.Fatalf("failed reading %s: %s", filename, err) } if len(src) == 0 { b.Fatalf("%s has zero length", filename) } return src } // expand returns a slice of length n containing repeated copies of src. func expand(src []byte, n int) []byte { dst := make([]byte, n) for x := dst; len(x) > 0; { i := copy(x, src) x = x[i:] } return dst } func benchWords(b *testing.B, n int, decode bool) { // Note: the file is OS-language dependent so the resulting values are not // directly comparable for non-US-English OS installations. data := expand(readFile(b, "/usr/share/dict/words"), n) if decode { benchDecode(b, data) } else { benchEncode(b, data) } } func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) } func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) } func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) } func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) } func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) } func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) } func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) } func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) } // testFiles' values are copied directly from // https://code.google.com/p/snappy/source/browse/trunk/snappy_unittest.cc. // The label field is unused in snappy-go. var testFiles = []struct { label string filename string }{ {"html", "html"}, {"urls", "urls.10K"}, {"jpg", "house.jpg"}, {"pdf", "mapreduce-osdi-1.pdf"}, {"html4", "html_x_4"}, {"cp", "cp.html"}, {"c", "fields.c"}, {"lsp", "grammar.lsp"}, {"xls", "kennedy.xls"}, {"txt1", "alice29.txt"}, {"txt2", "asyoulik.txt"}, {"txt3", "lcet10.txt"}, {"txt4", "plrabn12.txt"}, {"bin", "ptt5"}, {"sum", "sum"}, {"man", "xargs.1"}, {"pb", "geo.protodata"}, {"gaviota", "kppkn.gtb"}, } // The test data files are present at this canonical URL. const baseURL = "https://snappy.googlecode.com/svn/trunk/testdata/" func downloadTestdata(basename string) (errRet error) { filename := filepath.Join("testdata", basename) f, err := os.Create(filename) if err != nil { return fmt.Errorf("failed to create %s: %s", filename, err) } defer f.Close() defer func() { if errRet != nil { os.Remove(filename) } }() resp, err := http.Get(baseURL + basename) if err != nil { return fmt.Errorf("failed to download %s: %s", baseURL+basename, err) } defer resp.Body.Close() _, err = io.Copy(f, resp.Body) if err != nil { return fmt.Errorf("failed to write %s: %s", filename, err) } return nil } func benchFile(b *testing.B, n int, decode bool) { filename := filepath.Join("testdata", testFiles[n].filename) if stat, err := os.Stat(filename); err != nil || stat.Size() == 0 { if !*download { b.Fatal("test data not found; skipping benchmark without the -download flag") } // Download the official snappy C++ implementation reference test data // files for benchmarking. if err := os.Mkdir("testdata", 0777); err != nil && !os.IsExist(err) { b.Fatalf("failed to create testdata: %s", err) } for _, tf := range testFiles { if err := downloadTestdata(tf.filename); err != nil { b.Fatalf("failed to download testdata: %s", err) } } } data := readFile(b, filename) if decode { benchDecode(b, data) } else { benchEncode(b, data) } } // Naming convention is kept similar to what snappy's C++ implementation uses. func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) } func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) } func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) } func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) } func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) } func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) } func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) } func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) } func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) } func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) } func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) } func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) } func Benchmark_UFlat12(b *testing.B) { benchFile(b, 12, true) } func Benchmark_UFlat13(b *testing.B) { benchFile(b, 13, true) } func Benchmark_UFlat14(b *testing.B) { benchFile(b, 14, true) } func Benchmark_UFlat15(b *testing.B) { benchFile(b, 15, true) } func Benchmark_UFlat16(b *testing.B) { benchFile(b, 16, true) } func Benchmark_UFlat17(b *testing.B) { benchFile(b, 17, true) } func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) } func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) } func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) } func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) } func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) } func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) } func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) } func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) } func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) } func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) } func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) } func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) } func Benchmark_ZFlat12(b *testing.B) { benchFile(b, 12, false) } func Benchmark_ZFlat13(b *testing.B) { benchFile(b, 13, false) } func Benchmark_ZFlat14(b *testing.B) { benchFile(b, 14, false) } func Benchmark_ZFlat15(b *testing.B) { benchFile(b, 15, false) } func Benchmark_ZFlat16(b *testing.B) { benchFile(b, 16, false) } func Benchmark_ZFlat17(b *testing.B) { benchFile(b, 17, false) } golang-github-mreiferson-go-snappystream-0.2.3/snappystream.go000066400000000000000000000031761357640446100246370ustar00rootroot00000000000000// snappystream wraps snappy-go and supplies a Reader and Writer // for the snappy framed stream format: // https://snappy.googlecode.com/svn/trunk/framing_format.txt package snappystream import ( "hash/crc32" "github.com/mreiferson/go-snappystream/snappy-go" ) // Ext is the file extension for files whose content is a snappy framed stream. const Ext = ".sz" // MediaType is the MIME type used to represent snappy framed content. const MediaType = "application/x-snappy-framed" // ContentEncoding is the appropriate HTTP Content-Encoding header value for // requests containing a snappy framed entity body. const ContentEncoding = "x-snappy-framed" // MaxBlockSize is the maximum number of decoded bytes allowed to be // represented in a snappy framed block (sections 4.2 and 4.3). const MaxBlockSize = 65536 // maxEncodedBlockSize is the maximum number of encoded bytes in a framed // block. var maxEncodedBlockSize = uint32(snappy.MaxEncodedLen(MaxBlockSize)) const VerifyChecksum = true const SkipVerifyChecksum = false // Block types defined by the snappy framed format specification. const ( blockCompressed = 0x00 blockUncompressed = 0x01 blockPadding = 0xfe blockStreamIdentifier = 0xff ) // streamID is the stream identifier block that begins a valid snappy framed // stream. var streamID = []byte{0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59} // maskChecksum implements the checksum masking algorithm described by the spec. func maskChecksum(c uint32) uint32 { return ((c >> 15) | (c << 17)) + 0xa282ead8 } var crcTable *crc32.Table func init() { crcTable = crc32.MakeTable(crc32.Castagnoli) } golang-github-mreiferson-go-snappystream-0.2.3/writer.go000066400000000000000000000136001357640446100234160ustar00rootroot00000000000000package snappystream import ( "bufio" "errors" "fmt" "hash/crc32" "io" "github.com/mreiferson/go-snappystream/snappy-go" ) var errClosed = fmt.Errorf("closed") // BufferedWriter is an io.WriteCloser with behavior similar to writers // returned by NewWriter but it buffers written data, maximizing block size (to // improve the output compression ratio) at the cost of speed. Benefits over // NewWriter are most noticible when individual writes are small and when // streams are long. // // Failure to call a BufferedWriter's Close or Flush methods after it is done // being written to will likely result in missing data frames which will be // undetectable in the decoding process. // // NOTE: BufferedWriter cannot be instantiated via struct literal and must // use NewBufferedWriter (i.e. its zero value is not usable). type BufferedWriter struct { err error w *writer bw *bufio.Writer } // NewBufferedWriter allocates and returns a BufferedWriter with an internal // buffer of MaxBlockSize bytes. If an error occurs writing a block to w, all // future writes will fail with the same error. After all data has been // written, the client should call the Flush method to guarantee all data has // been forwarded to the underlying io.Writer. func NewBufferedWriter(w io.Writer) *BufferedWriter { _w := NewWriter(w).(*writer) return &BufferedWriter{ w: _w, bw: bufio.NewWriterSize(_w, MaxBlockSize), } } // ReadFrom implements the io.ReaderFrom interface used by io.Copy. It encodes // data read from r as a snappy framed stream that is written to the underlying // writer. ReadFrom returns the number number of bytes read, along with any // error encountered (other than io.EOF). func (w *BufferedWriter) ReadFrom(r io.Reader) (int64, error) { if w.err != nil { return 0, w.err } var n int64 n, w.err = w.bw.ReadFrom(r) return n, w.err } // Write buffers p internally, encoding and writing a block to the underlying // buffer if the buffer grows beyond MaxBlockSize bytes. The returned int // will be 0 if there was an error and len(p) otherwise. func (w *BufferedWriter) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } _, w.err = w.bw.Write(p) if w.err != nil { return 0, w.err } return len(p), nil } // Flush encodes and writes a block with the contents of w's internal buffer to // the underlying writer even if the buffer does not contain a full block of // data (MaxBlockSize bytes). func (w *BufferedWriter) Flush() error { if w.err == nil { w.err = w.bw.Flush() } return w.err } // Close flushes w's internal buffer and tears down internal data structures. // After a successful call to Close method calls on w return an error. Close // makes no attempt to close the underlying writer. func (w *BufferedWriter) Close() error { if w.err != nil { return w.err } w.err = w.bw.Flush() w.w = nil w.bw = nil if w.err != nil { return w.err } w.err = errClosed return nil } type writer struct { writer io.Writer err error hdr []byte dst []byte sentStreamID bool } // NewWriter returns an io.Writer that writes its input to an underlying // io.Writer encoded as a snappy framed stream. A stream identifier block is // written to w preceding the first data block. The returned writer will never // emit a block with length in bytes greater than MaxBlockSize+4 nor one // containing more than MaxBlockSize bytes of (uncompressed) data. // // For each Write, the returned length will only ever be len(p) or 0, // regardless of the length of *compressed* bytes written to the wrapped // io.Writer. If the returned length is 0 then error will be non-nil. If // len(p) exceeds 65536, the slice will be automatically chunked into smaller // blocks which are all emitted before the call returns. func NewWriter(w io.Writer) io.Writer { return &writer{ writer: w, hdr: make([]byte, 8), dst: make([]byte, 4096), } } func (w *writer) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } total := 0 sz := MaxBlockSize var n int for i := 0; i < len(p); i += n { if i+sz > len(p) { sz = len(p) - i } n, w.err = w.write(p[i : i+sz]) if w.err != nil { return 0, w.err } total += n } return total, nil } // write attempts to encode p as a block and write it to the underlying writer. // The returned int may not equal p's length if compression below // MaxBlockSize-4 could not be achieved. func (w *writer) write(p []byte) (int, error) { var err error if len(p) > MaxBlockSize { return 0, errors.New(fmt.Sprintf("block too large %d > %d", len(p), MaxBlockSize)) } w.dst = w.dst[:cap(w.dst)] // Encode does dumb resize w/o context. reslice avoids alloc. w.dst, err = snappy.Encode(w.dst, p) if err != nil { return 0, err } block := w.dst n := len(p) compressed := true // check for data which is better left uncompressed. this is determined if // the encoded content is longer than the source. if len(w.dst) >= len(p) { compressed = false block = p[:n] } if !w.sentStreamID { _, err := w.writer.Write(streamID) if err != nil { return 0, err } w.sentStreamID = true } // set the block type if compressed { writeHeader(w.hdr, blockCompressed, block, p[:n]) } else { writeHeader(w.hdr, blockUncompressed, block, p[:n]) } _, err = w.writer.Write(w.hdr) if err != nil { return 0, err } _, err = w.writer.Write(block) if err != nil { return 0, err } return n, nil } // writeHeader panics if len(hdr) is less than 8. func writeHeader(hdr []byte, btype byte, enc, dec []byte) { hdr[0] = btype // 3 byte little endian length of encoded content length := uint32(len(enc)) + 4 // +4 for checksum hdr[1] = byte(length) hdr[2] = byte(length >> 8) hdr[3] = byte(length >> 16) // 4 byte little endian CRC32 checksum of decoded content checksum := maskChecksum(crc32.Checksum(dec, crcTable)) hdr[4] = byte(checksum) hdr[5] = byte(checksum >> 8) hdr[6] = byte(checksum >> 16) hdr[7] = byte(checksum >> 24) } golang-github-mreiferson-go-snappystream-0.2.3/writer_test.go000066400000000000000000000053201357640446100244550ustar00rootroot00000000000000package snappystream import ( "bytes" "io/ioutil" "log" "testing" ) // This test ensures that all BufferedWriter methods fail after Close has been // called. func TestBufferedWriterClose(t *testing.T) { w := NewBufferedWriter(ioutil.Discard) err := w.Close() if err != nil { log.Fatalf("closing empty BufferedWriter: %v", err) } err = w.Close() if err == nil { log.Fatalf("successful close after close") } err = w.Flush() if err == nil { log.Fatalf("successful flush after close") } _, err = w.Write([]byte("abc")) if err == nil { log.Fatalf("successful write after close") } } // This test simply checks that buffering has an effect in a situation where it // is know it should. func TestBufferedWriter_compression(t *testing.T) { p := []byte("hello snappystream!") n := 10 var shortbuf bytes.Buffer w := NewWriter(&shortbuf) for i := 0; i < n; i++ { n, err := w.Write(p) if err != nil { t.Fatalf("writer error: %v", err) } if n != len(p) { t.Fatalf("short write: %d", n) } } var buf bytes.Buffer bw := NewBufferedWriter(&buf) for i := 0; i < n; i++ { n, err := bw.Write(p) if err != nil { t.Fatalf("buffered writer error: %v", err) } if n != len(p) { t.Fatalf("short write: %d", n) } } err := bw.Close() if err != nil { t.Fatalf("closing buffer: %v", err) } uncompressed := int64(n) * int64(len(p)) compressed := shortbuf.Len() bufcompressed := buf.Len() if compressed <= bufcompressed { t.Fatalf("no benefit from buffering (%d <= %d)", shortbuf.Len(), buf.Len()) } c := float64(uncompressed) / float64(compressed) bufc := float64(uncompressed) / float64(bufcompressed) improved := bufc / c t.Logf("BufferedWriter compression ratio %g (%.03g factor improvement over %g)", bufc, improved, c) } // This tests ensures flushing after every write is equivalent to using // NewWriter directly. func TestBufferedWriterFlush(t *testing.T) { p := []byte("hello snappystream!") n := 10 var shortbuf bytes.Buffer w := NewWriter(&shortbuf) for i := 0; i < n; i++ { n, err := w.Write(p) if err != nil { t.Fatalf("writer error: %v", err) } if n != len(p) { t.Fatalf("short write: %d", n) } } var buf bytes.Buffer bw := NewBufferedWriter(&buf) for i := 0; i < n; i++ { n, err := bw.Write(p) if err != nil { t.Fatalf("buffered writer error: %v", err) } if n != len(p) { t.Fatalf("short write: %d", n) } err = bw.Flush() if err != nil { t.Fatalf("flush: %v", err) } } err := bw.Close() if err != nil { t.Fatalf("closing buffer: %v", err) } if shortbuf.Len() != buf.Len() { t.Fatalf("unexpected size: %d != %d", shortbuf.Len(), buf.Len()) } if !bytes.Equal(shortbuf.Bytes(), buf.Bytes()) { t.Fatalf("unexpected bytes") } }