{"action":"created","comment":{"url":"https://api.github.com/repos/benlaurie/objecthash/pulls/comments/46135802","id":46135802,"diff_hunk":"@@ -0,0 +1,318 @@\n+package org.links.objecthash;\n+\n+import java.nio.ByteBuffer;\n+import java.security.MessageDigest;\n+import java.security.NoSuchAlgorithmException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.Comparator;\n+import java.util.Iterator;\n+import java.util.List;\n+import java.util.TreeSet;\n+import java.util.logging.Level;\n+import java.util.logging.Logger;\n+\n+import org.json.JSONArray;\n+import org.json.JSONException;\n+import org.json.JSONObject;\n+import org.json.JSONTokener;\n+\n+/**\n+ * TODO(phad): docs.\n+ */\n+public class ObjectHash implements Comparable<ObjectHash> {\n+  private static final int SHA256_BLOCK_SIZE = 32;\n+  private static final String SHA256 = \"SHA-256\";\n+  private static final Logger LOG = Logger.getLogger(ObjectHash.class.getName());\n+\n+  private byte[] hash;\n+  private MessageDigest digester;\n+\n+  private enum JsonType {\n+    BOOLEAN,\n+    ARRAY,\n+    OBJECT,\n+    INT,\n+    FLOAT,\n+    STRING,\n+    NULL,\n+    UNKNOWN\n+  }\n+\n+  private ObjectHash() throws NoSuchAlgorithmException {\n+    this.hash = new byte[SHA256_BLOCK_SIZE];\n+    this.digester = MessageDigest.getInstance(SHA256);\n+  }\n+\n+  private void hashAny(Object obj) throws NoSuchAlgorithmException,\n+                                          JSONException {\n+    digester.reset();\n+    JsonType outerType = getType(obj);\n+    LOG.info(\"Next element: \" + outerType);\n+    switch (outerType) {\n+      case ARRAY: {\n+        hashList((JSONArray) obj);\n+        break;\n+      }\n+      case OBJECT: {\n+        hashObject((JSONObject) obj);\n+        break;\n+      }\n+      case INT: {\n+        hashInteger(obj);\n+        break;\n+      }\n+      case STRING: {\n+        hashString((String) obj);\n+        break;\n+      }\n+      case NULL: {\n+        hashNull();\n+        break;\n+      }\n+      case BOOLEAN: {\n+        hashBoolean((Boolean) obj);\n+        break;\n+      }\n+      case FLOAT: {\n+        hashDouble((Double) obj);\n+        break;\n+      }\n+      default: {\n+        throw new IllegalArgumentException(\"Illegal type in JSON: \"\n+                                           + obj.getClass());\n+      }\n+    }\n+\n+  }\n+\n+  private void hashTaggedBytes(char tag, byte[] bytes)\n+      throws NoSuchAlgorithmException {\n+    digester.reset();\n+    digester.update((byte) tag);\n+    digester.update(bytes);\n+    hash = digester.digest();\n+  }\n+\n+  private void hashString(String str) throws NoSuchAlgorithmException {\n+    hashTaggedBytes('u', str.getBytes());\n+  }\n+\n+  private void hashInteger(Object value) throws NoSuchAlgorithmException {\n+    String str = value.toString();\n+    hashTaggedBytes('i', str.getBytes());\n+  }\n+\n+  private void hashDouble(Double value) throws NoSuchAlgorithmException {\n+    String normalized = normalizeFloat(value);\n+    hashTaggedBytes('f', normalized.getBytes());\n+  }\n+\n+  private void hashNull() throws NoSuchAlgorithmException {\n+    hashTaggedBytes('n', \"\".getBytes());\n+  }\n+\n+  private void hashBoolean(boolean bool) throws NoSuchAlgorithmException {\n+    hashTaggedBytes('b', (bool ? \"1\" : \"0\").getBytes());\n+  }\n+\n+  private void hashList(JSONArray list) throws NoSuchAlgorithmException,\n+                                               JSONException {\n+    digester.reset();\n+    digester.update((byte) ('l'));\n+    for (int n = 0; n < list.length(); ++n) {\n+      ObjectHash innerObject = new ObjectHash();\n+      innerObject.hashAny(list.get(n));\n+      digester.update(innerObject.hash());\n+    }\n+    hash = digester.digest();\n+  }\n+\n+  private String debugString(Iterable<ByteBuffer> buffers) {\n+    StringBuilder sb = new StringBuilder();\n+    for (ByteBuffer buff : buffers) {\n+      sb.append('\\n');\n+      sb.append(toHex(buff.array()));\n+    }\n+    return sb.toString();\n+  }\n+\n+  private void hashObject(JSONObject obj) throws NoSuchAlgorithmException, \n+                                                 JSONException {\n+    List<ByteBuffer> buffers = new ArrayList<ByteBuffer>();\n+    Comparator<ByteBuffer> ordering = new Comparator<ByteBuffer>() {\n+        @Override\n+        public int compare(ByteBuffer left, ByteBuffer right) {\n+          for (int idx = 0; idx < left.array().length; ++idx) {\n+            if (left.array()[idx] < right.array()[idx]) {\n+              return -1;\n+            } else if (left.array()[idx] > right.array()[idx]) {\n+              return 1;\n+            }\n+          }\n+          return 0;\n+        }          \n+    };\n+    Iterator<String> keys = obj.keys();\n+    while (keys.hasNext()) {\n+      ByteBuffer buff = ByteBuffer.allocate(2 * SHA256_BLOCK_SIZE);\n+      String key = keys.next();\n+      // TODO(phad): would be nice to chain all these calls builder-stylee.\n+      ObjectHash hKey = new ObjectHash();\n+      hKey.hashString(key);\n+      ObjectHash hVal = new ObjectHash();\n+      hVal.hashAny(obj.get(key));\n+      buff.put(hKey.hash());\n+      buff.put(hVal.hash());\n+      buffers.add(buff);\n+    }\n+    LOG.info(\"Accumulated ByteBuffers: \" + debugString(buffers));\n+    Collections.sort(buffers, ordering);\n+    LOG.info(\"Sorted ByteBuffers: \" + debugString(buffers));\n+    digester.reset();\n+    digester.update((byte) 'd');\n+    for (ByteBuffer buff : buffers) {\n+      digester.update(buff.array());\n+    }\n+    hash = digester.digest();\n+  }\n+\n+  private static int parseHex(char digit) {\n+    assert ((digit >= '0' && digit <= '9') || (digit >= 'a' && digit <= 'f'));\n+    if (digit >= '0' && digit <= '9') {\n+      return digit - '0';\n+    } else {\n+      return 10 + digit - 'a';\n+    }\n+  }\n+\n+  public static ObjectHash fromHex(String hex) throws NoSuchAlgorithmException {\n+    ObjectHash h = new ObjectHash();\n+    hex = hex.toLowerCase();\n+    if (hex.length() % 2 == 1) {\n+      hex = '0' + hex;\n+    }\n+    // TODO(phad): maybe just use Int.valueOf(s).intValue()\n+    int pos = SHA256_BLOCK_SIZE;\n+    for (int idx = hex.length(); idx > 0; idx -= 2) {\n+      h.hash[--pos] = (byte) (16 * parseHex(hex.charAt(idx - 2))\n+                              + parseHex(hex.charAt(idx - 1)));\n+    }\n+    return h;\n+  }\n+\n+  private static JsonType getType(Object jsonObj) {\n+    if (jsonObj == JSONObject.NULL) {\n+      return JsonType.NULL;\n+    } else  if (jsonObj instanceof JSONArray) {\n+      return JsonType.ARRAY;\n+    } else if (jsonObj instanceof JSONObject) {\n+      return JsonType.OBJECT;\n+    } else if (jsonObj instanceof String) {\n+      return JsonType.STRING;\n+    } else if (jsonObj instanceof Integer || jsonObj instanceof Long) {\n+      return JsonType.INT;\n+    } else if (jsonObj instanceof Double) {\n+      return JsonType.FLOAT;\n+    } else if (jsonObj instanceof Boolean) {\n+      return JsonType.BOOLEAN;\n+    } else {\n+      LOG.log(Level.WARNING, \"jsonObj is_a \" + jsonObj.getClass());\n+      return JsonType.UNKNOWN;\n+    }\n+  }\n+\n+  public static ObjectHash commonJsonHash(String json)\n+      throws JSONException, NoSuchAlgorithmException {\n+    // TODO(phad): implement 'commonizing' of JSON values.\n+    return new ObjectHash();\n+  }\n+\n+  public static ObjectHash pythonJsonHash(String json)\n+      throws JSONException, NoSuchAlgorithmException {\n+    LOG.info(\"\\n=============================================\\n\"\n+             + \"= JSON: \" + json + \"\\n\"\n+             + \"=============================================\");\n+    ObjectHash h = new ObjectHash();\n+    h.hashAny(new JSONTokener(json).nextValue());\n+    return h;\n+  }\n+\n+  @Override\n+  public String toString() {\n+    return this.toHex();\n+  }\n+\n+  @Override\n+  public boolean equals(Object other) {\n+   if (this == other) return true;\n+   if (other == null) return false;\n+   if (this.getClass() != other.getClass()) return false;\n+   return this.toHex().equals(((ObjectHash) other).toHex());\n+  }\n+\n+  @Override\n+  public int compareTo(ObjectHash other) {\n+    return toHex().compareTo(other.toHex());\n+  }\n+\n+  public byte[] hash() {\n+    return hash;\n+  }\n+\n+  private static String toHex(byte[] buffer) {\n+    StringBuffer hexString = new StringBuffer();\n+    for (int idx = 0; idx < buffer.length; ++idx) {\n+      String hex = Integer.toHexString(0xff & buffer[idx]);\n+      if (hex.length() == 1) {\n+        hexString.append('0');\n+      }\n+      hexString.append(hex);\n+    }\n+    return hexString.toString();\n+  }\n+\n+  public String toHex() {\n+    return toHex(hash);\n+  }\n+\n+  static String normalizeFloat(double f) {\n+    // Early out for zero.\n+    if (f == 0.0) {\n+      return \"+0:\";\n+    }\n+    StringBuffer sb = new StringBuffer();\n+    // Sign\n+    sb.append(f < 0.0 ? '-' : '+');\n+    if (f < 0.0) f = -f;\n+    // Exponent\n+    int e = 0;\n+    while (f > 1) {\n+      f /= 2;\n+      e += 1;\n+    }\n+    while (f < 0.5) {\n+      f *= 2;\n+      e -= 1;\n+    }\n+    sb.append(e);\n+    sb.append(':');\n+    // Mantissa\n+    if (f > 1 || f <= 0.5) {\n+      throw new IllegalStateException(\"wrong range for mantissa\");\n+    }\n+    while (f != 0) {\n+      if (f >= 1) {","path":"src/main/java/org/links/objecthash/ObjectHash.java","position":292,"original_position":305,"commit_id":"33d37ae0ca4b37782e7712f59f1656326b723f0b","original_commit_id":"42b4e922fb63c7cdf7d3338d96b1b801425abb7a","user":{"login":"phad","id":874173,"avatar_url":"https://avatars.githubusercontent.com/u/874173?v=3","gravatar_id":"","url":"https://api.github.com/users/phad","html_url":"https://github.com/phad","followers_url":"https://api.github.com/users/phad/followers","following_url":"https://api.github.com/users/phad/following{/other_user}","gists_url":"https://api.github.com/users/phad/gists{/gist_id}","starred_url":"https://api.github.com/users/phad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phad/subscriptions","organizations_url":"https://api.github.com/users/phad/orgs","repos_url":"https://api.github.com/users/phad/repos","events_url":"https://api.github.com/users/phad/events{/privacy}","received_events_url":"https://api.github.com/users/phad/received_events","type":"User","site_admin":false},"body":"It sure didn't terminate when I forgot to negate 'f' at first when sign is -ve ...","created_at":"2015-11-30T12:10:07Z","updated_at":"2015-11-30T12:10:07Z","html_url":"https://github.com/benlaurie/objecthash/pull/6#discussion_r46135802","pull_request_url":"https://api.github.com/repos/benlaurie/objecthash/pulls/6","_links":{"self":{"href":"https://api.github.com/repos/benlaurie/objecthash/pulls/comments/46135802"},"html":{"href":"https://github.com/benlaurie/objecthash/pull/6#discussion_r46135802"},"pull_request":{"href":"https://api.github.com/repos/benlaurie/objecthash/pulls/6"}}},"pull_request":{"url":"https://api.github.com/repos/benlaurie/objecthash/pulls/6","id":51935147,"html_url":"https://github.com/benlaurie/objecthash/pull/6","diff_url":"https://github.com/benlaurie/objecthash/pull/6.diff","patch_url":"https://github.com/benlaurie/objecthash/pull/6.patch","issue_url":"https://api.github.com/repos/benlaurie/objecthash/issues/6","number":6,"state":"open","locked":false,"title":"Java object hash","user":{"login":"phad","id":874173,"avatar_url":"https://avatars.githubusercontent.com/u/874173?v=3","gravatar_id":"","url":"https://api.github.com/users/phad","html_url":"https://github.com/phad","followers_url":"https://api.github.com/users/phad/followers","following_url":"https://api.github.com/users/phad/following{/other_user}","gists_url":"https://api.github.com/users/phad/gists{/gist_id}","starred_url":"https://api.github.com/users/phad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phad/subscriptions","organizations_url":"https://api.github.com/users/phad/orgs","repos_url":"https://api.github.com/users/phad/repos","events_url":"https://api.github.com/users/phad/events{/privacy}","received_events_url":"https://api.github.com/users/phad/received_events","type":"User","site_admin":false},"body":"First cut of ObjectHash in java.\r\nLimited type support initially (Object, Array and String only).\r\nI chose to use SimpleBuildTool for building this ... http://www.scala-sbt.org/\r\n","created_at":"2015-11-26T17:22:59Z","updated_at":"2015-11-30T12:10:07Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5f5920537f92c69f925f64276123c47d20007490","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/benlaurie/objecthash/pulls/6/commits","review_comments_url":"https://api.github.com/repos/benlaurie/objecthash/pulls/6/comments","review_comment_url":"https://api.github.com/repos/benlaurie/objecthash/pulls/comments{/number}","comments_url":"https://api.github.com/repos/benlaurie/objecthash/issues/6/comments","statuses_url":"https://api.github.com/repos/benlaurie/objecthash/statuses/33d37ae0ca4b37782e7712f59f1656326b723f0b","head":{"label":"phad:java_object_hash","ref":"java_object_hash","sha":"33d37ae0ca4b37782e7712f59f1656326b723f0b","user":{"login":"phad","id":874173,"avatar_url":"https://avatars.githubusercontent.com/u/874173?v=3","gravatar_id":"","url":"https://api.github.com/users/phad","html_url":"https://github.com/phad","followers_url":"https://api.github.com/users/phad/followers","following_url":"https://api.github.com/users/phad/following{/other_user}","gists_url":"https://api.github.com/users/phad/gists{/gist_id}","starred_url":"https://api.github.com/users/phad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phad/subscriptions","organizations_url":"https://api.github.com/users/phad/orgs","repos_url":"https://api.github.com/users/phad/repos","events_url":"https://api.github.com/users/phad/events{/privacy}","received_events_url":"https://api.github.com/users/phad/received_events","type":"User","site_admin":false},"repo":{"id":46922580,"name":"objecthash","full_name":"phad/objecthash","owner":{"login":"phad","id":874173,"avatar_url":"https://avatars.githubusercontent.com/u/874173?v=3","gravatar_id":"","url":"https://api.github.com/users/phad","html_url":"https://github.com/phad","followers_url":"https://api.github.com/users/phad/followers","following_url":"https://api.github.com/users/phad/following{/other_user}","gists_url":"https://api.github.com/users/phad/gists{/gist_id}","starred_url":"https://api.github.com/users/phad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phad/subscriptions","organizations_url":"https://api.github.com/users/phad/orgs","repos_url":"https://api.github.com/users/phad/repos","events_url":"https://api.github.com/users/phad/events{/privacy}","received_events_url":"https://api.github.com/users/phad/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/phad/objecthash","description":"A way to cryptographically hash objects (in the JSON-ish sense) that works cross-language. And, therefore, cross-encoding.","fork":true,"url":"https://api.github.com/repos/phad/objecthash","forks_url":"https://api.github.com/repos/phad/objecthash/forks","keys_url":"https://api.github.com/repos/phad/objecthash/keys{/key_id}","collaborators_url":"https://api.github.com/repos/phad/objecthash/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/phad/objecthash/teams","hooks_url":"https://api.github.com/repos/phad/objecthash/hooks","issue_events_url":"https://api.github.com/repos/phad/objecthash/issues/events{/number}","events_url":"https://api.github.com/repos/phad/objecthash/events","assignees_url":"https://api.github.com/repos/phad/objecthash/assignees{/user}","branches_url":"https://api.github.com/repos/phad/objecthash/branches{/branch}","tags_url":"https://api.github.com/repos/phad/objecthash/tags","blobs_url":"https://api.github.com/repos/phad/objecthash/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/phad/objecthash/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/phad/objecthash/git/refs{/sha}","trees_url":"https://api.github.com/repos/phad/objecthash/git/trees{/sha}","statuses_url":"https://api.github.com/repos/phad/objecthash/statuses/{sha}","languages_url":"https://api.github.com/repos/phad/objecthash/languages","stargazers_url":"https://api.github.com/repos/phad/objecthash/stargazers","contributors_url":"https://api.github.com/repos/phad/objecthash/contributors","subscribers_url":"https://api.github.com/repos/phad/objecthash/subscribers","subscription_url":"https://api.github.com/repos/phad/objecthash/subscription","commits_url":"https://api.github.com/repos/phad/objecthash/commits{/sha}","git_commits_url":"https://api.github.com/repos/phad/objecthash/git/commits{/sha}","comments_url":"https://api.github.com/repos/phad/objecthash/comments{/number}","issue_comment_url":"https://api.github.com/repos/phad/objecthash/issues/comments{/number}","contents_url":"https://api.github.com/repos/phad/objecthash/contents/{+path}","compare_url":"https://api.github.com/repos/phad/objecthash/compare/{base}...{head}","merges_url":"https://api.github.com/repos/phad/objecthash/merges","archive_url":"https://api.github.com/repos/phad/objecthash/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/phad/objecthash/downloads","issues_url":"https://api.github.com/repos/phad/objecthash/issues{/number}","pulls_url":"https://api.github.com/repos/phad/objecthash/pulls{/number}","milestones_url":"https://api.github.com/repos/phad/objecthash/milestones{/number}","notifications_url":"https://api.github.com/repos/phad/objecthash/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/phad/objecthash/labels{/name}","releases_url":"https://api.github.com/repos/phad/objecthash/releases{/id}","created_at":"2015-11-26T11:22:34Z","updated_at":"2015-11-26T11:22:35Z","pushed_at":"2015-11-30T11:46:01Z","git_url":"git://github.com/phad/objecthash.git","ssh_url":"git@github.com:phad/objecthash.git","clone_url":"https://github.com/phad/objecthash.git","svn_url":"https://github.com/phad/objecthash","homepage":null,"size":48,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"benlaurie:master","ref":"master","sha":"ca197d491634f8c2edb36ff2dee986b1c720096b","user":{"login":"benlaurie","id":40044,"avatar_url":"https://avatars.githubusercontent.com/u/40044?v=3","gravatar_id":"","url":"https://api.github.com/users/benlaurie","html_url":"https://github.com/benlaurie","followers_url":"https://api.github.com/users/benlaurie/followers","following_url":"https://api.github.com/users/benlaurie/following{/other_user}","gists_url":"https://api.github.com/users/benlaurie/gists{/gist_id}","starred_url":"https://api.github.com/users/benlaurie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benlaurie/subscriptions","organizations_url":"https://api.github.com/users/benlaurie/orgs","repos_url":"https://api.github.com/users/benlaurie/repos","events_url":"https://api.github.com/users/benlaurie/events{/privacy}","received_events_url":"https://api.github.com/users/benlaurie/received_events","type":"User","site_admin":false},"repo":{"id":46190665,"name":"objecthash","full_name":"benlaurie/objecthash","owner":{"login":"benlaurie","id":40044,"avatar_url":"https://avatars.githubusercontent.com/u/40044?v=3","gravatar_id":"","url":"https://api.github.com/users/benlaurie","html_url":"https://github.com/benlaurie","followers_url":"https://api.github.com/users/benlaurie/followers","following_url":"https://api.github.com/users/benlaurie/following{/other_user}","gists_url":"https://api.github.com/users/benlaurie/gists{/gist_id}","starred_url":"https://api.github.com/users/benlaurie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benlaurie/subscriptions","organizations_url":"https://api.github.com/users/benlaurie/orgs","repos_url":"https://api.github.com/users/benlaurie/repos","events_url":"https://api.github.com/users/benlaurie/events{/privacy}","received_events_url":"https://api.github.com/users/benlaurie/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/benlaurie/objecthash","description":"A way to cryptographically hash objects (in the JSON-ish sense) that works cross-language. And, therefore, cross-encoding.","fork":false,"url":"https://api.github.com/repos/benlaurie/objecthash","forks_url":"https://api.github.com/repos/benlaurie/objecthash/forks","keys_url":"https://api.github.com/repos/benlaurie/objecthash/keys{/key_id}","collaborators_url":"https://api.github.com/repos/benlaurie/objecthash/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/benlaurie/objecthash/teams","hooks_url":"https://api.github.com/repos/benlaurie/objecthash/hooks","issue_events_url":"https://api.github.com/repos/benlaurie/objecthash/issues/events{/number}","events_url":"https://api.github.com/repos/benlaurie/objecthash/events","assignees_url":"https://api.github.com/repos/benlaurie/objecthash/assignees{/user}","branches_url":"https://api.github.com/repos/benlaurie/objecthash/branches{/branch}","tags_url":"https://api.github.com/repos/benlaurie/objecthash/tags","blobs_url":"https://api.github.com/repos/benlaurie/objecthash/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/benlaurie/objecthash/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/benlaurie/objecthash/git/refs{/sha}","trees_url":"https://api.github.com/repos/benlaurie/objecthash/git/trees{/sha}","statuses_url":"https://api.github.com/repos/benlaurie/objecthash/statuses/{sha}","languages_url":"https://api.github.com/repos/benlaurie/objecthash/languages","stargazers_url":"https://api.github.com/repos/benlaurie/objecthash/stargazers","contributors_url":"https://api.github.com/repos/benlaurie/objecthash/contributors","subscribers_url":"https://api.github.com/repos/benlaurie/objecthash/subscribers","subscription_url":"https://api.github.com/repos/benlaurie/objecthash/subscription","commits_url":"https://api.github.com/repos/benlaurie/objecthash/commits{/sha}","git_commits_url":"https://api.github.com/repos/benlaurie/objecthash/git/commits{/sha}","comments_url":"https://api.github.com/repos/benlaurie/objecthash/comments{/number}","issue_comment_url":"https://api.github.com/repos/benlaurie/objecthash/issues/comments{/number}","contents_url":"https://api.github.com/repos/benlaurie/objecthash/contents/{+path}","compare_url":"https://api.github.com/repos/benlaurie/objecthash/compare/{base}...{head}","merges_url":"https://api.github.com/repos/benlaurie/objecthash/merges","archive_url":"https://api.github.com/repos/benlaurie/objecthash/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/benlaurie/objecthash/downloads","issues_url":"https://api.github.com/repos/benlaurie/objecthash/issues{/number}","pulls_url":"https://api.github.com/repos/benlaurie/objecthash/pulls{/number}","milestones_url":"https://api.github.com/repos/benlaurie/objecthash/milestones{/number}","notifications_url":"https://api.github.com/repos/benlaurie/objecthash/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/benlaurie/objecthash/labels{/name}","releases_url":"https://api.github.com/repos/benlaurie/objecthash/releases{/id}","created_at":"2015-11-14T20:22:00Z","updated_at":"2015-11-20T16:22:20Z","pushed_at":"2015-11-30T11:46:01Z","git_url":"git://github.com/benlaurie/objecthash.git","ssh_url":"git@github.com:benlaurie/objecthash.git","clone_url":"https://github.com/benlaurie/objecthash.git","svn_url":"https://github.com/benlaurie/objecthash","homepage":null,"size":79,"stargazers_count":5,"watchers_count":5,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":1,"forks":4,"open_issues":1,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/benlaurie/objecthash/pulls/6"},"html":{"href":"https://github.com/benlaurie/objecthash/pull/6"},"issue":{"href":"https://api.github.com/repos/benlaurie/objecthash/issues/6"},"comments":{"href":"https://api.github.com/repos/benlaurie/objecthash/issues/6/comments"},"review_comments":{"href":"https://api.github.com/repos/benlaurie/objecthash/pulls/6/comments"},"review_comment":{"href":"https://api.github.com/repos/benlaurie/objecthash/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/benlaurie/objecthash/pulls/6/commits"},"statuses":{"href":"https://api.github.com/repos/benlaurie/objecthash/statuses/33d37ae0ca4b37782e7712f59f1656326b723f0b"}}}}