{"action":"created","comment":{"url":"https://api.github.com/repos/libuv/libuv/pulls/comments/22438524","id":22438524,"diff_hunk":"@@ -788,123 +786,205 @@ void fs__mkdtemp(uv_fs_t* req) {\n \n \n void fs__scandir(uv_fs_t* req) {\n-  WCHAR* pathw = req->pathw;\n-  size_t len = wcslen(pathw);\n-  int result;\n-  WCHAR* name;\n-  HANDLE dir;\n-  WIN32_FIND_DATAW ent = { 0 };\n-  WCHAR* path2;\n-  const WCHAR* fmt;\n-  uv__dirent_t** dents;\n-  int dent_size;\n-\n-  if (len == 0) {\n-    fmt = L\"./*\";\n-  } else if (pathw[len - 1] == L'/' || pathw[len - 1] == L'\\\\') {\n-    fmt = L\"%ls*\";\n-  } else {\n-    fmt = L\"%ls\\\\*\";\n-  }\n+  static const size_t DIRENTS_INITIAL_SIZE = 32;\n \n-  /* Figure out whether path is a file or a directory. */\n-  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {\n-    req->result = UV_ENOTDIR;\n-    req->sys_errno_ = ERROR_SUCCESS;\n-    return;\n-  }\n-\n-  path2 = (WCHAR*)malloc(sizeof(WCHAR) * (len + 4));\n-  if (!path2) {\n-    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);\n-    return;\n-  }\n+  HANDLE dir_handle = INVALID_HANDLE_VALUE;\n \n-  _snwprintf(path2, len + 3, fmt, pathw);\n-  dir = FindFirstFileW(path2, &ent);\n-  free(path2);\n+  uv__dirent_t** dirents = NULL;\n+  size_t dirents_size = 0;\n+  size_t dirents_used = 0;\n \n-  if(dir == INVALID_HANDLE_VALUE) {\n-    SET_REQ_WIN32_ERROR(req, GetLastError());\n-    return;\n-  }\n-\n-  result = 0;\n-  dents = NULL;\n-  dent_size = 0;\n+  IO_STATUS_BLOCK iosb;\n+  NTSTATUS status;\n \n-  do {\n-    uv__dirent_t* dent;\n-    int utf8_len;\n+  /* Buffer to hold directory entries returned by NtQueryDirectoryFile.\n+   * It's important that this buffer can hold at least one entry, regardless\n+   * of the length of the file names present in the enumerated directory.\n+   * A file name is at most 256 WCHARs long.\n+   * According to MSDN, the buffer must be aligned at an 8-byte boundary.\n+   */\n+  __declspec(align(8)) char buffer[8192];\n+\n+  STATIC_ASSERT(sizeof buffer >=\n+                sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));\n+\n+  /* Open the directory. */\n+  dir_handle =\n+      CreateFileW(req->pathw,\n+                  FILE_LIST_DIRECTORY | SYNCHRONIZE,\n+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n+                  NULL,\n+                  OPEN_EXISTING,\n+                  FILE_FLAG_BACKUP_SEMANTICS,\n+                  NULL);\n+  if (dir_handle == INVALID_HANDLE_VALUE)\n+    goto win32_error;\n+\n+  /* Read the first chunk. */\n+  status = pNtQueryDirectoryFile(dir_handle,\n+                                 NULL,\n+                                 NULL,\n+                                 NULL,\n+                                 &iosb,\n+                                 &buffer,\n+                                 sizeof buffer,\n+                                 FileDirectoryInformation,\n+                                 FALSE,\n+                                 NULL,\n+                                 TRUE);\n+\n+  /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.\n+   * This should be reported back as UV_ENOTDIR.\n+   */\n+  if (status == STATUS_INVALID_PARAMETER)\n+    goto not_a_directory_error;\n+\n+  while (NT_SUCCESS(status)) {\n+    char* position = buffer;\n+    size_t next_entry_offset = 0;\n+\n+    do {\n+      FILE_DIRECTORY_INFORMATION* info;\n+      uv__dirent_t* dirent;\n+\n+      size_t wchar_len;\n+      size_t utf8_len;\n+\n+      /* Obtain a pointer to the current directory entry. */\n+      position += next_entry_offset;\n+      info = (FILE_DIRECTORY_INFORMATION*) position;\n+\n+      /* Fetch the offset to the next directory entry. */\n+      next_entry_offset = info->NextEntryOffset;\n+\n+      /* Compute the length of the filename in WCHARs. */\n+      wchar_len = info->FileNameLength / sizeof(WCHAR);\n+\n+      /* Skip over '.' and '..' entries. */\n+      if (wchar_len == 1 && info->FileName[0] == L'.')\n+        continue;\n+      if (wchar_len == 2 && info->FileName[0] == L'.' &&\n+          info->FileName[1] == L'.')\n+        continue;\n+\n+      /* Compute the space required to store the filename as UTF-8. */\n+      utf8_len = WideCharToMultiByte(\n+          CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL);\n+      if (utf8_len == 0)\n+        goto win32_error;\n+\n+      /* Resize the dirent array if needed. */\n+      if (dirents_used >= dirents_size) {\n+        size_t new_dirents_size =\n+            dirents_size == 0 ? DIRENTS_INITIAL_SIZE : dirents_size << 1;\n+        uv__dirent_t** new_dirents = (uv__dirent_t**) realloc(\n+            dirents, new_dirents_size * sizeof *dirents);\n+\n+        if (new_dirents == NULL)\n+          goto out_of_memory_error;\n+\n+        dirents_size = new_dirents_size;\n+        dirents = new_dirents;\n+      }\n \n-    name = ent.cFileName;\n+      /* Allocate space for the uv dirent structure. The dirent structure\n+       * includes room for the first character of the filename, but `utf8_len`\n+       * doesn't count the NULL terminator at this point.\n+       */\n+      dirent = (uv__dirent_t*) malloc(sizeof *dirent + utf8_len);\n+      if (dirent == NULL)\n+        goto out_of_memory_error;\n+\n+      dirents[dirents_used++] = dirent;\n+\n+      /* Convert file name to UTF-8. */\n+      if (WideCharToMultiByte(CP_UTF8,\n+                              0,\n+                              &info->FileName[0],\n+                              wchar_len,\n+                              &dirent->d_name[0],\n+                              utf8_len,\n+                              NULL,\n+                              NULL) == 0)\n+        goto win32_error;\n+\n+      /* Add a null terminator to the filename. */\n+      dirent->d_name[utf8_len] = '\\0';\n+\n+      /* Fill out the type field. */\n+      if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)\n+        dirent->d_type = UV__DT_CHAR;\n+      else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)\n+        dirent->d_type = UV__DT_LINK;\n+      else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)\n+        dirent->d_type = UV__DT_DIR;\n+      else\n+        dirent->d_type = UV__DT_FILE;\n+    } while (next_entry_offset != 0);\n+\n+    /* Read the next chunk. */\n+    status = pNtQueryDirectoryFile(dir_handle,\n+                                   NULL,\n+                                   NULL,\n+                                   NULL,\n+                                   &iosb,\n+                                   &buffer,\n+                                   sizeof buffer,\n+                                   FileDirectoryInformation,\n+                                   FALSE,\n+                                   NULL,\n+                                   FALSE);\n+\n+    /* After the first pNtQueryDirectoryFile call, the function may return\n+     * STATUS_SUCCESS even if the buffer was too small to hold at least one\n+     * directory entry.\n+     */\n+    if (status == STATUS_SUCCESS && iosb.Information == 0)\n+      status = STATUS_BUFFER_OVERFLOW;\n+  }\n \n-    if (!(name[0] != L'.' || (name[1] && (name[1] != L'.' || name[2]))))\n-      continue;\n+  if (status != STATUS_NO_MORE_FILES)\n+    goto nt_error;\n \n-    /* Grow dents buffer, if needed */\n-    if (result >= dent_size) {\n-      uv__dirent_t** tmp;\n+  CloseHandle(dir_handle);\n \n-      dent_size += uv__fs_dirent_slide;\n-      tmp = realloc(dents, dent_size * sizeof(*dents));\n-      if (tmp == NULL) {\n-        SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);\n-        goto fatal;\n-      }\n-      dents = tmp;\n-    }\n+  /* Store the result in the request object. */\n+  req->ptr = dirents;\n+  if (dirents != NULL)\n+    req->flags |= UV_FS_FREE_PTR;\n \n-    /* Allocate enough space to fit utf8 encoding of file name */\n-    len = wcslen(name);\n-    utf8_len = uv_utf16_to_utf8(name, len, NULL, 0);\n-    if (!utf8_len) {\n-      SET_REQ_WIN32_ERROR(req, GetLastError());\n-      goto fatal;\n-    }\n+  SET_REQ_RESULT(req, dirents_used);\n \n-    dent = malloc(sizeof(*dent) + utf8_len + 1);\n-    if (dent == NULL) {\n-      SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);\n-      goto fatal;\n-    }\n+  /* `nbufs` will be used as index by uv_fs_scandir_next. */\n+  req->nbufs = 0;\n \n-    /* Copy file name */\n-    utf8_len = uv_utf16_to_utf8(name, len, dent->d_name, utf8_len);\n-    if (!utf8_len) {\n-      free(dent);\n-      SET_REQ_WIN32_ERROR(req, GetLastError());\n-      goto fatal;\n-    }\n-    dent->d_name[utf8_len] = '\\0';\n+  return;\n \n-    /* Copy file type */\n-    if ((ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)\n-      dent->d_type = UV__DT_DIR;\n-    else if ((ent.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)\n-      dent->d_type = UV__DT_LINK;\n-    else\n-      dent->d_type = UV__DT_FILE;\n+nt_error:\n+  SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));\n+  goto cleanup;\n \n-    dents[result++] = dent;\n-  } while(FindNextFileW(dir, &ent));\n+win32_error:\n+  SET_REQ_WIN32_ERROR(req, GetLastError());\n+  goto cleanup;\n \n-  FindClose(dir);\n+not_a_directory_error:\n+  SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);\n+  goto cleanup;\n \n-  if (dents != NULL)\n-    req->flags |= UV_FS_FREE_PTR;\n+out_of_memory_error:\n+  SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);\n+  goto cleanup;\n \n-  /* NOTE: nbufs will be used as index */\n-  req->nbufs = 0;\n-  req->ptr = dents;\n-  SET_REQ_RESULT(req, result);\n-  return;\n+cleanup:\n+  if (dir_handle != INVALID_HANDLE_VALUE)\n+    CloseHandle(dir_handle);\n \n-fatal:\n-  /* Deallocate dents */\n-  for (result--; result >= 0; result--)\n-    free(dents[result]);\n-  free(dents);\n+  if (dirents != NULL) {\n+    for (size_t i = 0; i < dirents_used; i++)","path":"src/win/fs.c","position":308,"original_position":308,"commit_id":"0ec199f6440f737a0b02c2e1f5438f002b2f306e","original_commit_id":"0ec199f6440f737a0b02c2e1f5438f002b2f306e","user":{"login":"saghul","id":317464,"avatar_url":"https://avatars.githubusercontent.com/u/317464?v=3","gravatar_id":"","url":"https://api.github.com/users/saghul","html_url":"https://github.com/saghul","followers_url":"https://api.github.com/users/saghul/followers","following_url":"https://api.github.com/users/saghul/following{/other_user}","gists_url":"https://api.github.com/users/saghul/gists{/gist_id}","starred_url":"https://api.github.com/users/saghul/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saghul/subscriptions","organizations_url":"https://api.github.com/users/saghul/orgs","repos_url":"https://api.github.com/users/saghul/repos","events_url":"https://api.github.com/users/saghul/events{/privacy}","received_events_url":"https://api.github.com/users/saghul/received_events","type":"User","site_admin":false},"body":"GCC will probably complain here since that's a C99 extension.","created_at":"2015-01-04T11:23:20Z","updated_at":"2015-01-04T11:23:20Z","html_url":"https://github.com/libuv/libuv/pull/105#discussion_r22438524","pull_request_url":"https://api.github.com/repos/libuv/libuv/pulls/105","_links":{"self":{"href":"https://api.github.com/repos/libuv/libuv/pulls/comments/22438524"},"html":{"href":"https://github.com/libuv/libuv/pull/105#discussion_r22438524"},"pull_request":{"href":"https://api.github.com/repos/libuv/libuv/pulls/105"}}},"pull_request":{"url":"https://api.github.com/repos/libuv/libuv/pulls/105","id":26789974,"html_url":"https://github.com/libuv/libuv/pull/105","diff_url":"https://github.com/libuv/libuv/pull/105.diff","patch_url":"https://github.com/libuv/libuv/pull/105.patch","issue_url":"https://api.github.com/repos/libuv/libuv/issues/105","number":105,"state":"open","locked":false,"title":"win: use NtQueryDirectoryFile to implement uv_fs_scandir","user":{"login":"piscisaureus","id":218257,"avatar_url":"https://avatars.githubusercontent.com/u/218257?v=3","gravatar_id":"","url":"https://api.github.com/users/piscisaureus","html_url":"https://github.com/piscisaureus","followers_url":"https://api.github.com/users/piscisaureus/followers","following_url":"https://api.github.com/users/piscisaureus/following{/other_user}","gists_url":"https://api.github.com/users/piscisaureus/gists{/gist_id}","starred_url":"https://api.github.com/users/piscisaureus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piscisaureus/subscriptions","organizations_url":"https://api.github.com/users/piscisaureus/orgs","repos_url":"https://api.github.com/users/piscisaureus/repos","events_url":"https://api.github.com/users/piscisaureus/events{/privacy}","received_events_url":"https://api.github.com/users/piscisaureus/received_events","type":"User","site_admin":false},"body":"This replaces the current `uv_fs_scandir` implementation by one that talks to the NT kernel directly.\r\nThe previous implementation used the win32 `FindFirstFile()` and `FindNextFile` APIs which have quite some overhead.\r\n\r\n@orangemocha, you seem best fit to review this.\r\nOtherwise I'll defer to the usual suspects @saghul or @bnoordhuis.\r\n","created_at":"2015-01-04T02:52:16Z","updated_at":"2015-01-04T11:23:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"95cd53f8aceaa46c46c0b8ab453a09e9d888fbae","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/libuv/libuv/pulls/105/commits","review_comments_url":"https://api.github.com/repos/libuv/libuv/pulls/105/comments","review_comment_url":"https://api.github.com/repos/libuv/libuv/pulls/comments/{number}","comments_url":"https://api.github.com/repos/libuv/libuv/issues/105/comments","statuses_url":"https://api.github.com/repos/libuv/libuv/statuses/0ec199f6440f737a0b02c2e1f5438f002b2f306e","head":{"label":"piscisaureus:nt-scandir","ref":"nt-scandir","sha":"0ec199f6440f737a0b02c2e1f5438f002b2f306e","user":{"login":"piscisaureus","id":218257,"avatar_url":"https://avatars.githubusercontent.com/u/218257?v=3","gravatar_id":"","url":"https://api.github.com/users/piscisaureus","html_url":"https://github.com/piscisaureus","followers_url":"https://api.github.com/users/piscisaureus/followers","following_url":"https://api.github.com/users/piscisaureus/following{/other_user}","gists_url":"https://api.github.com/users/piscisaureus/gists{/gist_id}","starred_url":"https://api.github.com/users/piscisaureus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piscisaureus/subscriptions","organizations_url":"https://api.github.com/users/piscisaureus/orgs","repos_url":"https://api.github.com/users/piscisaureus/repos","events_url":"https://api.github.com/users/piscisaureus/events{/privacy}","received_events_url":"https://api.github.com/users/piscisaureus/received_events","type":"User","site_admin":false},"repo":{"id":1693627,"name":"libuv","full_name":"piscisaureus/libuv","owner":{"login":"piscisaureus","id":218257,"avatar_url":"https://avatars.githubusercontent.com/u/218257?v=3","gravatar_id":"","url":"https://api.github.com/users/piscisaureus","html_url":"https://github.com/piscisaureus","followers_url":"https://api.github.com/users/piscisaureus/followers","following_url":"https://api.github.com/users/piscisaureus/following{/other_user}","gists_url":"https://api.github.com/users/piscisaureus/gists{/gist_id}","starred_url":"https://api.github.com/users/piscisaureus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piscisaureus/subscriptions","organizations_url":"https://api.github.com/users/piscisaureus/orgs","repos_url":"https://api.github.com/users/piscisaureus/repos","events_url":"https://api.github.com/users/piscisaureus/events{/privacy}","received_events_url":"https://api.github.com/users/piscisaureus/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/piscisaureus/libuv","description":"Don't look here, look at joyent/libuv instead, that's where my patches go.","fork":true,"url":"https://api.github.com/repos/piscisaureus/libuv","forks_url":"https://api.github.com/repos/piscisaureus/libuv/forks","keys_url":"https://api.github.com/repos/piscisaureus/libuv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/piscisaureus/libuv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/piscisaureus/libuv/teams","hooks_url":"https://api.github.com/repos/piscisaureus/libuv/hooks","issue_events_url":"https://api.github.com/repos/piscisaureus/libuv/issues/events{/number}","events_url":"https://api.github.com/repos/piscisaureus/libuv/events","assignees_url":"https://api.github.com/repos/piscisaureus/libuv/assignees{/user}","branches_url":"https://api.github.com/repos/piscisaureus/libuv/branches{/branch}","tags_url":"https://api.github.com/repos/piscisaureus/libuv/tags","blobs_url":"https://api.github.com/repos/piscisaureus/libuv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/piscisaureus/libuv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/piscisaureus/libuv/git/refs{/sha}","trees_url":"https://api.github.com/repos/piscisaureus/libuv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/piscisaureus/libuv/statuses/{sha}","languages_url":"https://api.github.com/repos/piscisaureus/libuv/languages","stargazers_url":"https://api.github.com/repos/piscisaureus/libuv/stargazers","contributors_url":"https://api.github.com/repos/piscisaureus/libuv/contributors","subscribers_url":"https://api.github.com/repos/piscisaureus/libuv/subscribers","subscription_url":"https://api.github.com/repos/piscisaureus/libuv/subscription","commits_url":"https://api.github.com/repos/piscisaureus/libuv/commits{/sha}","git_commits_url":"https://api.github.com/repos/piscisaureus/libuv/git/commits{/sha}","comments_url":"https://api.github.com/repos/piscisaureus/libuv/comments{/number}","issue_comment_url":"https://api.github.com/repos/piscisaureus/libuv/issues/comments/{number}","contents_url":"https://api.github.com/repos/piscisaureus/libuv/contents/{+path}","compare_url":"https://api.github.com/repos/piscisaureus/libuv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/piscisaureus/libuv/merges","archive_url":"https://api.github.com/repos/piscisaureus/libuv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/piscisaureus/libuv/downloads","issues_url":"https://api.github.com/repos/piscisaureus/libuv/issues{/number}","pulls_url":"https://api.github.com/repos/piscisaureus/libuv/pulls{/number}","milestones_url":"https://api.github.com/repos/piscisaureus/libuv/milestones{/number}","notifications_url":"https://api.github.com/repos/piscisaureus/libuv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/piscisaureus/libuv/labels{/name}","releases_url":"https://api.github.com/repos/piscisaureus/libuv/releases{/id}","created_at":"2011-05-02T22:13:10Z","updated_at":"2013-09-06T20:20:03Z","pushed_at":"2015-01-04T02:59:48Z","git_url":"git://github.com/piscisaureus/libuv.git","ssh_url":"git@github.com:piscisaureus/libuv.git","clone_url":"https://github.com/piscisaureus/libuv.git","svn_url":"https://github.com/piscisaureus/libuv","homepage":"http://tinyclouds.org/iocp-links.html","size":13235,"stargazers_count":1,"watchers_count":1,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"libuv:v1.x","ref":"v1.x","sha":"af96f458e2583a9cb2e7f2f20b3b3ff04bca6c24","user":{"login":"libuv","id":4030929,"avatar_url":"https://avatars.githubusercontent.com/u/4030929?v=3","gravatar_id":"","url":"https://api.github.com/users/libuv","html_url":"https://github.com/libuv","followers_url":"https://api.github.com/users/libuv/followers","following_url":"https://api.github.com/users/libuv/following{/other_user}","gists_url":"https://api.github.com/users/libuv/gists{/gist_id}","starred_url":"https://api.github.com/users/libuv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/libuv/subscriptions","organizations_url":"https://api.github.com/users/libuv/orgs","repos_url":"https://api.github.com/users/libuv/repos","events_url":"https://api.github.com/users/libuv/events{/privacy}","received_events_url":"https://api.github.com/users/libuv/received_events","type":"Organization","site_admin":false},"repo":{"id":14812739,"name":"libuv","full_name":"libuv/libuv","owner":{"login":"libuv","id":4030929,"avatar_url":"https://avatars.githubusercontent.com/u/4030929?v=3","gravatar_id":"","url":"https://api.github.com/users/libuv","html_url":"https://github.com/libuv","followers_url":"https://api.github.com/users/libuv/followers","following_url":"https://api.github.com/users/libuv/following{/other_user}","gists_url":"https://api.github.com/users/libuv/gists{/gist_id}","starred_url":"https://api.github.com/users/libuv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/libuv/subscriptions","organizations_url":"https://api.github.com/users/libuv/orgs","repos_url":"https://api.github.com/users/libuv/repos","events_url":"https://api.github.com/users/libuv/events{/privacy}","received_events_url":"https://api.github.com/users/libuv/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/libuv/libuv","description":"Cross-platform asychronous I/O","fork":true,"url":"https://api.github.com/repos/libuv/libuv","forks_url":"https://api.github.com/repos/libuv/libuv/forks","keys_url":"https://api.github.com/repos/libuv/libuv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/libuv/libuv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/libuv/libuv/teams","hooks_url":"https://api.github.com/repos/libuv/libuv/hooks","issue_events_url":"https://api.github.com/repos/libuv/libuv/issues/events{/number}","events_url":"https://api.github.com/repos/libuv/libuv/events","assignees_url":"https://api.github.com/repos/libuv/libuv/assignees{/user}","branches_url":"https://api.github.com/repos/libuv/libuv/branches{/branch}","tags_url":"https://api.github.com/repos/libuv/libuv/tags","blobs_url":"https://api.github.com/repos/libuv/libuv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/libuv/libuv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/libuv/libuv/git/refs{/sha}","trees_url":"https://api.github.com/repos/libuv/libuv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/libuv/libuv/statuses/{sha}","languages_url":"https://api.github.com/repos/libuv/libuv/languages","stargazers_url":"https://api.github.com/repos/libuv/libuv/stargazers","contributors_url":"https://api.github.com/repos/libuv/libuv/contributors","subscribers_url":"https://api.github.com/repos/libuv/libuv/subscribers","subscription_url":"https://api.github.com/repos/libuv/libuv/subscription","commits_url":"https://api.github.com/repos/libuv/libuv/commits{/sha}","git_commits_url":"https://api.github.com/repos/libuv/libuv/git/commits{/sha}","comments_url":"https://api.github.com/repos/libuv/libuv/comments{/number}","issue_comment_url":"https://api.github.com/repos/libuv/libuv/issues/comments/{number}","contents_url":"https://api.github.com/repos/libuv/libuv/contents/{+path}","compare_url":"https://api.github.com/repos/libuv/libuv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/libuv/libuv/merges","archive_url":"https://api.github.com/repos/libuv/libuv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/libuv/libuv/downloads","issues_url":"https://api.github.com/repos/libuv/libuv/issues{/number}","pulls_url":"https://api.github.com/repos/libuv/libuv/pulls{/number}","milestones_url":"https://api.github.com/repos/libuv/libuv/milestones{/number}","notifications_url":"https://api.github.com/repos/libuv/libuv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/libuv/libuv/labels{/name}","releases_url":"https://api.github.com/repos/libuv/libuv/releases{/id}","created_at":"2013-11-30T00:29:56Z","updated_at":"2015-01-04T08:53:55Z","pushed_at":"2015-01-02T18:35:19Z","git_url":"git://github.com/libuv/libuv.git","ssh_url":"git@github.com:libuv/libuv.git","clone_url":"https://github.com/libuv/libuv.git","svn_url":"https://github.com/libuv/libuv","homepage":"http://libuv.org/","size":16378,"stargazers_count":625,"watchers_count":625,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":57,"mirror_url":null,"open_issues_count":29,"forks":57,"open_issues":29,"watchers":625,"default_branch":"v1.x"}},"_links":{"self":{"href":"https://api.github.com/repos/libuv/libuv/pulls/105"},"html":{"href":"https://github.com/libuv/libuv/pull/105"},"issue":{"href":"https://api.github.com/repos/libuv/libuv/issues/105"},"comments":{"href":"https://api.github.com/repos/libuv/libuv/issues/105/comments"},"review_comments":{"href":"https://api.github.com/repos/libuv/libuv/pulls/105/comments"},"review_comment":{"href":"https://api.github.com/repos/libuv/libuv/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/libuv/libuv/pulls/105/commits"},"statuses":{"href":"https://api.github.com/repos/libuv/libuv/statuses/0ec199f6440f737a0b02c2e1f5438f002b2f306e"}}}}