{"action":"created","comment":{"url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/comments/45881308","id":45881308,"diff_hunk":"@@ -24,12 +24,428 @@\n // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.\n \n+#include \"UnitSPIRV.h\"\n+\n+#include \"TestFixture.h\"\n+#include \"gmock/gmock.h\"\n+\n+// Returns true if two spv_parsed_operand_t values are equal.\n+// To use this operator, this definition must appear in the same namespace\n+// as spv_parsed_operand_t.\n+static bool operator==(const spv_parsed_operand_t& a,\n+                       const spv_parsed_operand_t& b) {\n+  return a.offset == b.offset && a.num_words == b.num_words &&\n+         a.type == b.type && a.number_kind == b.number_kind &&\n+         a.number_bit_width == b.number_bit_width;\n+}\n+\n namespace {\n \n+using ::spvtest::Concatenate;\n+using ::spvtest::MakeInstruction;\n+using ::spvtest::MakeVector;\n+using ::testing::AnyOf;\n+using ::testing::Eq;\n+\n+// An easily-constructible and comparable object for the contents of an\n+// spv_parsed_instruction_t.\n+struct ParsedInstruction {\n+  ParsedInstruction(const spv_parsed_instruction_t& inst)\n+      : words(inst.words, inst.words + inst.num_words),\n+        opcode(inst.opcode),\n+        ext_inst_type(inst.ext_inst_type),\n+        type_id(inst.type_id),\n+        result_id(inst.result_id),\n+        operands(inst.operands, inst.operands + inst.num_operands) {}\n+\n+  std::vector<uint32_t> words;\n+  SpvOp opcode;\n+  spv_ext_inst_type_t ext_inst_type;\n+  uint32_t type_id;\n+  uint32_t result_id;\n+  std::vector<spv_parsed_operand_t> operands;\n+\n+  bool operator==(const ParsedInstruction& b) const {\n+    return words == b.words && opcode == b.opcode &&\n+           ext_inst_type == b.ext_inst_type && type_id == b.type_id &&\n+           result_id == b.result_id && operands == b.operands;\n+  }\n+};\n+\n+// Prints a ParsedInstruction object to the given output stream, and returns\n+// the stream.\n+std::ostream& operator<<(std::ostream& os, const ParsedInstruction& inst) {\n+  os << \"\\nParsedInstruction( {\";\n+  spvtest::PrintTo(spvtest::WordVector(inst.words), &os);\n+  os << \"}, opcode: \" << int(inst.opcode)\n+     << \" ext_inst_type: \" << int(inst.ext_inst_type)\n+     << \" type_id: \" << inst.type_id << \" result_id: \" << inst.result_id;\n+  for (const auto& operand : inst.operands) {\n+    os << \" { offset: \" << operand.offset << \" num_words: \" << operand.num_words\n+       << \" type: \" << int(operand.type)\n+       << \" number_kind: \" << int(operand.number_kind)\n+       << \" number_bit_width: \" << int(operand.number_bit_width) << \"}\";\n+  }\n+  return os;\n+}\n+\n+// Sanity check for the equality operator on ParsedInstruction.\n+TEST(ParsedInstruction, ZeroInitializedAreEqual) {\n+  spv_parsed_instruction_t pi = {};\n+  ParsedInstruction a(pi);\n+  ParsedInstruction b(pi);\n+  EXPECT_THAT(a, ::testing::TypedEq<ParsedInstruction>(b));\n+}\n+\n+// A template class with static member functions that forward to non-static\n+// member functions corresponding to the header and instruction callbacks\n+// for spvBinaryParse.\n+template <typename Client, typename T>\n+class BinaryParseClient : public T {\n+ public:\n+  static spv_result_t Header(void* user_data, spv_endianness_t endian,\n+                             uint32_t magic, uint32_t version,\n+                             uint32_t generator, uint32_t id_bound,\n+                             uint32_t reserved) {\n+    return static_cast<Client*>(user_data)\n+        ->HandleHeader(endian, magic, version, generator, id_bound, reserved);\n+  }\n+\n+  static spv_result_t Instruction(\n+      void* user_data, const spv_parsed_instruction_t* parsed_instruction) {\n+    return static_cast<Client*>(user_data)\n+        ->HandleInstruction(parsed_instruction);\n+  }\n+};\n+\n+using Words = std::vector<uint32_t>;\n+using Endians = std::vector<spv_endianness_t>;\n+using Sentences = std::vector<Words>;  // Maybe this is too cute?\n+using Instructions = std::vector<ParsedInstruction>;\n+\n+// A binary parse client that captures the results of parsing a binary,\n+// and whose callbacks can be made to succeed for a specified number of\n+// times, and then always fail with a given failure code.\n+template <typename T>\n+class CaptureParseResults\n+    : public BinaryParseClient<CaptureParseResults<T>, T> {\n+ public:\n+  // Capture the header from the parser callback.\n+  // If the number of callback successes has not yet been exhausted, then\n+  // returns SPV_SUCCESS, which itself counts as a callback success.\n+  // Otherwise returns the stored failure code.\n+  virtual spv_result_t HandleHeader(spv_endianness_t endian, uint32_t magic,\n+                                    uint32_t version, uint32_t generator,\n+                                    uint32_t id_bound, uint32_t reserved) {\n+    endians_.push_back(endian);\n+    headers_.push_back({magic, version, generator, id_bound, reserved});\n+    return ComputeResultCode();\n+  }\n+\n+  // Capture the parsed instruction data from the parser callback.\n+  // If the number of callback successes has not yet been exhausted, then\n+  // returns SPV_SUCCESS, which itself counts as a callback success.\n+  // Otherwise returns the stored failure code.\n+  virtual spv_result_t HandleInstruction(\n+      const spv_parsed_instruction_t* parsed_instruction) {\n+    EXPECT_NE(nullptr, parsed_instruction);\n+    instructions_.emplace_back(*parsed_instruction);\n+    return ComputeResultCode();\n+  }\n+\n+  // Getters\n+  const Endians& endians() const { return endians_; }\n+  const Sentences& headers() const { return headers_; }\n+  const Instructions& instructions() const { return instructions_; }\n+\n+ protected:\n+  // Returns the appropriate result code based on whether we still have more\n+  // successes to return.  Decrements the number of successes still remaining,\n+  // if needed.\n+  spv_result_t ComputeResultCode() {\n+    if (num_passing_callbacks_ < 0) return SPV_SUCCESS;\n+    if (num_passing_callbacks_ == 0) return fail_code_;\n+    num_passing_callbacks_--;\n+    return SPV_SUCCESS;\n+  }\n+\n+  // How many callbacks should succeed before they start failing?\n+  // If this is negative, then all callbacks should pass.\n+  int num_passing_callbacks_ = -1;  // By default, never fail.\n+  // The result code to use on callback failure.\n+  spv_result_t fail_code_ = SPV_ERROR_INVALID_BINARY;\n+\n+  // Accumulated results for calls to HandleHeader.\n+  Endians endians_;\n+  Sentences headers_;\n+\n+  // Accumulated results for calls to HandleHeader.\n+  Instructions instructions_;\n+};\n+\n+// Returns the expected SPIR-V module header words for the Khronos\n+// Assembler generator, and with a given Id bound.\n+Words ExpectedHeaderForBound(uint32_t bound) {\n+  return {SpvMagicNumber, SpvVersion,\n+          SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), bound, 0};\n+}\n+\n+// Returns a parsed operand for a non-number value at the given word offset\n+// within an instruction.\n+spv_parsed_operand_t MakeSimpleOperand(uint16_t offset,\n+                                       spv_operand_type_t type) {\n+  return {offset, 1, type, SPV_NUMBER_NONE, 0};\n+}\n+\n+// Returns a parsed operand for a literal unsigned integer value at the given\n+// word offset within an instruction.\n+spv_parsed_operand_t MakeLiteralNumberOperand(uint16_t offset) {\n+  return {offset, 1, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_NUMBER_UNSIGNED_INT,\n+          32};\n+}\n+\n+// Returns a parsed operand for a literal string value at the given\n+// word offset within an instruction.\n+spv_parsed_operand_t MakeLiteralStringOperand(uint16_t offset,\n+                                              uint16_t length) {\n+  return {offset, length, SPV_OPERAND_TYPE_LITERAL_STRING, SPV_NUMBER_NONE, 0};\n+}\n+\n+// Returns a ParsedInstruction for an OpTypeVoid instruction that would\n+// generate the given result Id.\n+ParsedInstruction MakeParsedVoidTypeInstruction(uint32_t result_id) {\n+  const auto void_inst = MakeInstruction(SpvOpTypeVoid, {result_id});\n+  const auto void_operands = std::vector<spv_parsed_operand_t>{\n+      MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID)};\n+  const spv_parsed_instruction_t parsed_void_inst = {\n+      void_inst.data(),\n+      uint16_t(void_inst.size()),\n+      SpvOpTypeVoid,\n+      SPV_EXT_INST_TYPE_NONE,\n+      0,  // type id\n+      result_id,\n+      void_operands.data(),\n+      uint16_t(void_operands.size())};\n+  return ParsedInstruction(parsed_void_inst);\n+}\n+\n+// Returns a ParsedInstruction for an OpTypeInt instruction that generates\n+// the given result Id for a 32-bit signed integer scalar type.\n+ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) {\n+  const auto i32_inst = MakeInstruction(SpvOpTypeInt, {result_id, 32, 1});\n+  const auto i32_operands = std::vector<spv_parsed_operand_t>{\n+      MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID),\n+      MakeLiteralNumberOperand(2), MakeLiteralNumberOperand(3)};\n+  spv_parsed_instruction_t parsed_i32_inst = {i32_inst.data(),\n+                                              uint16_t(i32_inst.size()),\n+                                              SpvOpTypeInt,\n+                                              SPV_EXT_INST_TYPE_NONE,\n+                                              0,  // type id\n+                                              result_id,\n+                                              i32_operands.data(),\n+                                              uint16_t(i32_operands.size())};\n+  return ParsedInstruction(parsed_i32_inst);\n+}\n+\n+using BinaryParseTest =\n+    spvtest::TextToBinaryTestBase<CaptureParseResults<::testing::Test>>;","path":"test/BinaryParse.cpp","position":228,"original_position":228,"commit_id":"a72e1fe0be247959b6dc844b6543fe28afac9037","original_commit_id":"a72e1fe0be247959b6dc844b6543fe28afac9037","user":{"login":"dneto0","id":13054574,"avatar_url":"https://avatars.githubusercontent.com/u/13054574?v=3","gravatar_id":"","url":"https://api.github.com/users/dneto0","html_url":"https://github.com/dneto0","followers_url":"https://api.github.com/users/dneto0/followers","following_url":"https://api.github.com/users/dneto0/following{/other_user}","gists_url":"https://api.github.com/users/dneto0/gists{/gist_id}","starred_url":"https://api.github.com/users/dneto0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dneto0/subscriptions","organizations_url":"https://api.github.com/users/dneto0/orgs","repos_url":"https://api.github.com/users/dneto0/repos","events_url":"https://api.github.com/users/dneto0/events{/privacy}","received_events_url":"https://api.github.com/users/dneto0/received_events","type":"User","site_admin":false},"body":"If you think different, I look forward to an alternative suggestion via pull request.","created_at":"2015-11-25T15:42:01Z","updated_at":"2015-11-25T15:42:01Z","html_url":"https://github.com/KhronosGroup/SPIRV-Tools/pull/28#discussion_r45881308","pull_request_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28","_links":{"self":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/comments/45881308"},"html":{"href":"https://github.com/KhronosGroup/SPIRV-Tools/pull/28#discussion_r45881308"},"pull_request":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28"}}},"pull_request":{"url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28","id":51691738,"html_url":"https://github.com/KhronosGroup/SPIRV-Tools/pull/28","diff_url":"https://github.com/KhronosGroup/SPIRV-Tools/pull/28.diff","patch_url":"https://github.com/KhronosGroup/SPIRV-Tools/pull/28.patch","issue_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues/28","number":28,"state":"open","locked":false,"title":"Several unit tests for spvBinaryParse","user":{"login":"dneto0","id":13054574,"avatar_url":"https://avatars.githubusercontent.com/u/13054574?v=3","gravatar_id":"","url":"https://api.github.com/users/dneto0","html_url":"https://github.com/dneto0","followers_url":"https://api.github.com/users/dneto0/followers","following_url":"https://api.github.com/users/dneto0/following{/other_user}","gists_url":"https://api.github.com/users/dneto0/gists{/gist_id}","starred_url":"https://api.github.com/users/dneto0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dneto0/subscriptions","organizations_url":"https://api.github.com/users/dneto0/orgs","repos_url":"https://api.github.com/users/dneto0/repos","events_url":"https://api.github.com/users/dneto0/events{/privacy}","received_events_url":"https://api.github.com/users/dneto0/received_events","type":"User","site_admin":false},"body":"Add a non-zero spv_result_t value SPV_REQUESTED_TERMINATION\r\nwhich should be used to signal an ok result, but signals\r\nearly termination for a process, such as binary parsing.\r\n\r\nTests include:\r\n - correct contents sent to header and instruction callbacks\r\n - non-zero status from a callback should terminate parsing,\r\n   but the parser should not generate its own diagnostic.\r\n\r\nTODO: Check diagnostics generated by the parser itself.","created_at":"2015-11-24T17:36:04Z","updated_at":"2015-11-25T15:42:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e55b1bb6724c89700296727db9a75b6899632a38","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28/commits","review_comments_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28/comments","review_comment_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/comments{/number}","comments_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues/28/comments","statuses_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/statuses/a72e1fe0be247959b6dc844b6543fe28afac9037","head":{"label":"dneto0:unit-test-binary-parse","ref":"unit-test-binary-parse","sha":"a72e1fe0be247959b6dc844b6543fe28afac9037","user":{"login":"dneto0","id":13054574,"avatar_url":"https://avatars.githubusercontent.com/u/13054574?v=3","gravatar_id":"","url":"https://api.github.com/users/dneto0","html_url":"https://github.com/dneto0","followers_url":"https://api.github.com/users/dneto0/followers","following_url":"https://api.github.com/users/dneto0/following{/other_user}","gists_url":"https://api.github.com/users/dneto0/gists{/gist_id}","starred_url":"https://api.github.com/users/dneto0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dneto0/subscriptions","organizations_url":"https://api.github.com/users/dneto0/orgs","repos_url":"https://api.github.com/users/dneto0/repos","events_url":"https://api.github.com/users/dneto0/events{/privacy}","received_events_url":"https://api.github.com/users/dneto0/received_events","type":"User","site_admin":false},"repo":{"id":46365138,"name":"SPIRV-Tools","full_name":"dneto0/SPIRV-Tools","owner":{"login":"dneto0","id":13054574,"avatar_url":"https://avatars.githubusercontent.com/u/13054574?v=3","gravatar_id":"","url":"https://api.github.com/users/dneto0","html_url":"https://github.com/dneto0","followers_url":"https://api.github.com/users/dneto0/followers","following_url":"https://api.github.com/users/dneto0/following{/other_user}","gists_url":"https://api.github.com/users/dneto0/gists{/gist_id}","starred_url":"https://api.github.com/users/dneto0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dneto0/subscriptions","organizations_url":"https://api.github.com/users/dneto0/orgs","repos_url":"https://api.github.com/users/dneto0/repos","events_url":"https://api.github.com/users/dneto0/events{/privacy}","received_events_url":"https://api.github.com/users/dneto0/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dneto0/SPIRV-Tools","description":"","fork":true,"url":"https://api.github.com/repos/dneto0/SPIRV-Tools","forks_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/forks","keys_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/teams","hooks_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/hooks","issue_events_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/issues/events{/number}","events_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/events","assignees_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/assignees{/user}","branches_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/branches{/branch}","tags_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/tags","blobs_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/statuses/{sha}","languages_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/languages","stargazers_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/stargazers","contributors_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/contributors","subscribers_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/subscribers","subscription_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/subscription","commits_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/contents/{+path}","compare_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/merges","archive_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/downloads","issues_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/issues{/number}","pulls_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/pulls{/number}","milestones_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/milestones{/number}","notifications_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/labels{/name}","releases_url":"https://api.github.com/repos/dneto0/SPIRV-Tools/releases{/id}","created_at":"2015-11-17T17:57:28Z","updated_at":"2015-11-17T17:57:29Z","pushed_at":"2015-11-24T20:52:44Z","git_url":"git://github.com/dneto0/SPIRV-Tools.git","ssh_url":"git@github.com:dneto0/SPIRV-Tools.git","clone_url":"https://github.com/dneto0/SPIRV-Tools.git","svn_url":"https://github.com/dneto0/SPIRV-Tools","homepage":null,"size":845,"stargazers_count":0,"watchers_count":0,"language":"C++","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":"KhronosGroup:master","ref":"master","sha":"c9e0845c7f4b55597349ed2d3de57646fa680aeb","user":{"login":"KhronosGroup","id":1608701,"avatar_url":"https://avatars.githubusercontent.com/u/1608701?v=3","gravatar_id":"","url":"https://api.github.com/users/KhronosGroup","html_url":"https://github.com/KhronosGroup","followers_url":"https://api.github.com/users/KhronosGroup/followers","following_url":"https://api.github.com/users/KhronosGroup/following{/other_user}","gists_url":"https://api.github.com/users/KhronosGroup/gists{/gist_id}","starred_url":"https://api.github.com/users/KhronosGroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KhronosGroup/subscriptions","organizations_url":"https://api.github.com/users/KhronosGroup/orgs","repos_url":"https://api.github.com/users/KhronosGroup/repos","events_url":"https://api.github.com/users/KhronosGroup/events{/privacy}","received_events_url":"https://api.github.com/users/KhronosGroup/received_events","type":"Organization","site_admin":false},"repo":{"id":45981801,"name":"SPIRV-Tools","full_name":"KhronosGroup/SPIRV-Tools","owner":{"login":"KhronosGroup","id":1608701,"avatar_url":"https://avatars.githubusercontent.com/u/1608701?v=3","gravatar_id":"","url":"https://api.github.com/users/KhronosGroup","html_url":"https://github.com/KhronosGroup","followers_url":"https://api.github.com/users/KhronosGroup/followers","following_url":"https://api.github.com/users/KhronosGroup/following{/other_user}","gists_url":"https://api.github.com/users/KhronosGroup/gists{/gist_id}","starred_url":"https://api.github.com/users/KhronosGroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KhronosGroup/subscriptions","organizations_url":"https://api.github.com/users/KhronosGroup/orgs","repos_url":"https://api.github.com/users/KhronosGroup/repos","events_url":"https://api.github.com/users/KhronosGroup/events{/privacy}","received_events_url":"https://api.github.com/users/KhronosGroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/KhronosGroup/SPIRV-Tools","description":"","fork":false,"url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools","forks_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/forks","keys_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/teams","hooks_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/hooks","issue_events_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues/events{/number}","events_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/events","assignees_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/assignees{/user}","branches_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/branches{/branch}","tags_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/tags","blobs_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/statuses/{sha}","languages_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/languages","stargazers_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/stargazers","contributors_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/contributors","subscribers_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/subscribers","subscription_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/subscription","commits_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/contents/{+path}","compare_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/merges","archive_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/downloads","issues_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues{/number}","pulls_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls{/number}","milestones_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/milestones{/number}","notifications_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/labels{/name}","releases_url":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/releases{/id}","created_at":"2015-11-11T12:56:25Z","updated_at":"2015-11-24T00:31:51Z","pushed_at":"2015-11-25T00:11:06Z","git_url":"git://github.com/KhronosGroup/SPIRV-Tools.git","ssh_url":"git@github.com:KhronosGroup/SPIRV-Tools.git","clone_url":"https://github.com/KhronosGroup/SPIRV-Tools.git","svn_url":"https://github.com/KhronosGroup/SPIRV-Tools","homepage":null,"size":835,"stargazers_count":34,"watchers_count":34,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":7,"mirror_url":null,"open_issues_count":5,"forks":7,"open_issues":5,"watchers":34,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28"},"html":{"href":"https://github.com/KhronosGroup/SPIRV-Tools/pull/28"},"issue":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues/28"},"comments":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/issues/28/comments"},"review_comments":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28/comments"},"review_comment":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/pulls/28/commits"},"statuses":{"href":"https://api.github.com/repos/KhronosGroup/SPIRV-Tools/statuses/a72e1fe0be247959b6dc844b6543fe28afac9037"}}}}