Update to 0.2.2 am: 9791b30a05 am: c725abe846 am: 518a22fe12

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/getrandom/+/1614806

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id27700affec6b3c53b1c96b7a13cedd2389444a0
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index d17f1ee..64fafa1 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "33b859bae16ae0b1c782e48db2ed96eb2306a2fc"
+    "sha1": "d79de0c95c01860268e071bcb6b0d019e18cd608"
   }
 }
diff --git a/Android.bp b/Android.bp
index 9b9b8a0..d494d22 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,40 +1,5 @@
-// This file is generated by cargo2android.py --device --run --dependencies --tests.
-
-package {
-    default_applicable_licenses: ["external_rust_crates_getrandom_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-//
-// large-scale-change included anything that looked like it might be a license
-// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
-//
-// Please consider removing redundant or irrelevant files from 'license_text:'.
-// See: http://go/android-license-faq
-license {
-    name: "external_rust_crates_getrandom_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-        "SPDX-license-identifier-MIT",
-    ],
-    license_text: [
-        "LICENSE-APACHE",
-        "LICENSE-MIT",
-    ],
-}
+// This file is generated by cargo2android.py --device --run --dependencies --tests --features=std.
+// Do not modify this file as changes will be overridden on upgrade.
 
 rust_defaults {
     name: "getrandom_defaults",
@@ -43,6 +8,7 @@
     test_suites: ["general-tests"],
     auto_gen_config: true,
     edition: "2018",
+    features: ["std"],
     rustlibs: [
         "libcfg_if",
         "liblibc",
@@ -52,6 +18,9 @@
 rust_test_host {
     name: "getrandom_host_test_src_lib",
     defaults: ["getrandom_defaults"],
+    test_options: {
+        unit_test: true,
+    },
 }
 
 rust_test {
@@ -60,12 +29,12 @@
 }
 
 rust_defaults {
-    name: "getrandom_defaults_common",
-    crate_name: "common",
-    srcs: ["tests/common.rs"],
+    name: "getrandom_defaults_getrandom",
+    crate_name: "getrandom",
     test_suites: ["general-tests"],
     auto_gen_config: true,
     edition: "2018",
+    features: ["std"],
     rustlibs: [
         "libcfg_if",
         "libgetrandom",
@@ -74,13 +43,48 @@
 }
 
 rust_test_host {
-    name: "getrandom_host_test_tests_common",
-    defaults: ["getrandom_defaults_common"],
+    name: "getrandom_host_test_tests_custom",
+    defaults: ["getrandom_defaults_getrandom"],
+    srcs: ["tests/custom.rs"],
+    test_options: {
+        unit_test: true,
+    },
 }
 
 rust_test {
-    name: "getrandom_device_test_tests_common",
-    defaults: ["getrandom_defaults_common"],
+    name: "getrandom_device_test_tests_custom",
+    defaults: ["getrandom_defaults_getrandom"],
+    srcs: ["tests/custom.rs"],
+}
+
+rust_test_host {
+    name: "getrandom_host_test_tests_normal",
+    defaults: ["getrandom_defaults_getrandom"],
+    srcs: ["tests/normal.rs"],
+    test_options: {
+        unit_test: true,
+    },
+}
+
+rust_test {
+    name: "getrandom_device_test_tests_normal",
+    defaults: ["getrandom_defaults_getrandom"],
+    srcs: ["tests/normal.rs"],
+}
+
+rust_test_host {
+    name: "getrandom_host_test_tests_rdrand",
+    defaults: ["getrandom_defaults_getrandom"],
+    srcs: ["tests/rdrand.rs"],
+    test_options: {
+        unit_test: true,
+    },
+}
+
+rust_test {
+    name: "getrandom_device_test_tests_rdrand",
+    defaults: ["getrandom_defaults_getrandom"],
+    srcs: ["tests/rdrand.rs"],
 }
 
 rust_library {
@@ -89,6 +93,7 @@
     crate_name: "getrandom",
     srcs: ["src/lib.rs"],
     edition: "2018",
+    features: ["std"],
     rustlibs: [
         "libcfg_if",
         "liblibc",
@@ -96,5 +101,5 @@
 }
 
 // dependent_library ["feature_list"]
-//   cfg-if-0.1.10
-//   libc-0.2.72
+//   cfg-if-1.0.0
+//   libc-0.2.87
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29b447c..c3ca728 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,82 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.2.2] - 2021-01-19
+### Changed
+- Forward `rustc-dep-of-std` to dependencies. [#198]
+- Highlight feature-dependend functionality in documentation using the `doc_cfg` feature. [#200]
+
+[#198]: https://github.com/rust-random/getrandom/pull/198
+[#200]: https://github.com/rust-random/getrandom/pull/200
+
+## [0.2.1] - 2021-01-03
+### Changed
+- Update `cfg-if` to v1.0. [#166]
+- Update `wasi` to v0.10. [#167]
+
+### Fixed
+- Multithreaded WASM support. [#165]
+
+### Removed
+- Windows XP support. [#177]
+- Direct `stdweb` support. [#178]
+- CloudABI support. [#184]
+
+[#165]: https://github.com/rust-random/getrandom/pull/165
+[#166]: https://github.com/rust-random/getrandom/pull/166
+[#167]: https://github.com/rust-random/getrandom/pull/167
+[#177]: https://github.com/rust-random/getrandom/pull/177
+[#178]: https://github.com/rust-random/getrandom/pull/178
+[#184]: https://github.com/rust-random/getrandom/pull/184
+
+## [0.2.0] - 2020-09-10
+### Features for using getrandom on unsupported targets
+
+The following (off by default) Cargo features have been added:
+- `"rdrand"` - use the RDRAND instruction on `no_std` `x86`/`x86_64` targets [#133]
+- `"js"` - use JavaScript calls on `wasm32-unknown-unknown` [#149]
+  - Replaces the `stdweb` and `wasm-bindgen` features (which are removed)
+- `"custom"` - allows a user to specify a custom implementation [#109]
+
+### Breaking Changes
+- Unsupported targets no longer compile [#107]
+- Change/Add `Error` constants [#120]
+- Only impl `std` traits when the `"std"` Cargo feature is specified [#106]
+- Remove offical support for Hermit, L4Re, and UEFI [#133]
+- Remove optional `"log"` dependancy [#131]
+- Update minimum supported Linux kernel to 2.6.32 [#153]
+- Update MSRV to 1.34 [#159]
+
+[#106]: https://github.com/rust-random/getrandom/pull/106
+[#107]: https://github.com/rust-random/getrandom/pull/107
+[#109]: https://github.com/rust-random/getrandom/pull/109
+[#120]: https://github.com/rust-random/getrandom/pull/120
+[#131]: https://github.com/rust-random/getrandom/pull/131
+[#133]: https://github.com/rust-random/getrandom/pull/133
+[#149]: https://github.com/rust-random/getrandom/pull/149
+[#153]: https://github.com/rust-random/getrandom/pull/153
+[#159]: https://github.com/rust-random/getrandom/pull/159
+
+## [0.1.16] - 2020-12-31
+### Changed
+- Update `cfg-if` to v1.0. [#173]
+- Implement `std::error::Error` for the `Error` type on additional targets. [#169]
+
+### Fixed
+- Multithreaded WASM support. [#171]
+
+[#173]: https://github.com/rust-random/getrandom/pull/173
+[#171]: https://github.com/rust-random/getrandom/pull/171
+[#169]: https://github.com/rust-random/getrandom/pull/169
+
+## [0.1.15] - 2020-09-10
+### Changed
+- Added support for Internet Explorer 11 [#139]
+- Fix Webpack require warning with `wasm-bindgen` [#137]
+
+[#137]: https://github.com/rust-random/getrandom/pull/137
+[#139]: https://github.com/rust-random/getrandom/pull/139
+
 ## [0.1.14] - 2020-01-07
 ### Changed
 - Remove use of spin-locks in the `use_file` module. [#125]
diff --git a/Cargo.toml b/Cargo.toml
index 1c1f718..2c0c056 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,16 +13,19 @@
 [package]
 edition = "2018"
 name = "getrandom"
-version = "0.1.14"
+version = "0.2.2"
 authors = ["The Rand Project Developers"]
-exclude = ["utils/*", ".*", "appveyor.yml"]
+exclude = [".*"]
 description = "A small cross-platform library for retrieving random data from system source"
 documentation = "https://docs.rs/getrandom"
 categories = ["os", "no-std"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-random/getrandom"
+[package.metadata.docs.rs]
+features = ["std", "custom"]
+rustdoc-args = ["--cfg", "docsrs"]
 [dependencies.cfg-if]
-version = "0.1.2"
+version = "1"
 
 [dependencies.compiler_builtins]
 version = "0.1"
@@ -33,31 +36,25 @@
 optional = true
 package = "rustc-std-workspace-core"
 
-[dependencies.log]
-version = "0.4"
+[features]
+custom = []
+js = ["wasm-bindgen", "js-sys"]
+rdrand = []
+rustc-dep-of-std = ["compiler_builtins", "core", "libc/rustc-dep-of-std", "wasi/rustc-dep-of-std"]
+std = []
+test-in-browser = []
+[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dependencies.js-sys]
+version = "0.3"
 optional = true
 
-[features]
-dummy = []
-rustc-dep-of-std = ["compiler_builtins", "core"]
-std = []
-test-in-browser = ["wasm-bindgen"]
+[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dependencies.wasm-bindgen]
+version = "0.2.62"
+optional = true
+default-features = false
+[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dev-dependencies.wasm-bindgen-test]
+version = "0.3.18"
 [target."cfg(target_os = \"wasi\")".dependencies.wasi]
-version = "0.9"
+version = "0.10"
 [target."cfg(unix)".dependencies.libc]
 version = "0.2.64"
 default-features = false
-[target.wasm32-unknown-unknown.dependencies.stdweb]
-version = "0.4.18"
-optional = true
-
-[target.wasm32-unknown-unknown.dependencies.wasm-bindgen]
-version = "0.2.29"
-optional = true
-[target.wasm32-unknown-unknown.dev-dependencies.wasm-bindgen-test]
-version = "0.2"
-[badges.appveyor]
-repository = "rust-random/getrandom"
-
-[badges.travis-ci]
-repository = "rust-random/getrandom"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index d10183f..dabf016 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "getrandom"
-version = "0.1.14"
+version = "0.2.2" # Also update html_root_url in lib.rs when bumping this
 edition = "2018"
 authors = ["The Rand Project Developers"]
 license = "MIT OR Apache-2.0"
@@ -8,15 +8,10 @@
 documentation = "https://docs.rs/getrandom"
 repository = "https://github.com/rust-random/getrandom"
 categories = ["os", "no-std"]
-exclude = ["utils/*", ".*", "appveyor.yml"]
-
-[badges]
-travis-ci = { repository = "rust-random/getrandom" }
-appveyor = { repository = "rust-random/getrandom" }
+exclude = [".*"]
 
 [dependencies]
-log = { version = "0.4", optional = true }
-cfg-if = "0.1.2"
+cfg-if = "1"
 
 # When built as part of libstd
 compiler_builtins = { version = "0.1", optional = true }
@@ -26,20 +21,33 @@
 libc = { version = "0.2.64", default-features = false }
 
 [target.'cfg(target_os = "wasi")'.dependencies]
-wasi = "0.9"
+wasi = "0.10"
 
-[target.wasm32-unknown-unknown.dependencies]
-wasm-bindgen = { version = "0.2.29", optional = true }
-stdweb = { version = "0.4.18", optional = true }
-
-[target.wasm32-unknown-unknown.dev-dependencies]
-wasm-bindgen-test = "0.2"
+[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
+wasm-bindgen = { version = "0.2.62", default-features = false, optional = true }
+js-sys = { version = "0.3", optional = true }
+[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies]
+wasm-bindgen-test = "0.3.18"
 
 [features]
+# Implement std-only traits for getrandom::Error
 std = []
-# Enables dummy implementation for unsupported targets
-dummy = []
+# Feature to enable fallback RDRAND-based implementation on x86/x86_64
+rdrand = []
+# Feature to enable JavaScript bindings on wasm32-unknown-unknown
+js = ["wasm-bindgen", "js-sys"]
+# Feature to enable custom RNG implementations
+custom = []
 # Unstable feature to support being a libstd dependency
-rustc-dep-of-std = ["compiler_builtins", "core"]
-# Unstable feature for testing
-test-in-browser = ["wasm-bindgen"]
+rustc-dep-of-std = [
+  "compiler_builtins",
+  "core",
+  "libc/rustc-dep-of-std",
+  "wasi/rustc-dep-of-std",
+]
+# Unstable/test-only feature to run wasm-bindgen tests in a browser
+test-in-browser = []
+
+[package.metadata.docs.rs]
+features = ["std", "custom"]
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/METADATA b/METADATA
index 27f871d..60dca65 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/getrandom/getrandom-0.1.14.crate"
+    value: "https://static.crates.io/crates/getrandom/getrandom-0.2.2.crate"
   }
-  version: "0.1.14"
+  version: "0.2.2"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2020
-    month: 6
-    day: 18
+    year: 2021
+    month: 3
+    day: 3
   }
 }
diff --git a/README.md b/README.md
index 01bbfb5..df2307b 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,18 @@
 # getrandom
 
-[![Build Status](https://travis-ci.org/rust-random/getrandom.svg?branch=master)](https://travis-ci.org/rust-random/getrandom)
-[![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-random/getrandom?svg=true)](https://ci.appveyor.com/project/rust-random/getrandom)
-[![Crate](https://img.shields.io/crates/v/getrandom.svg)](https://crates.io/crates/getrandom)
-[![Documentation](https://docs.rs/getrandom/badge.svg)](https://docs.rs/getrandom)
-[![Dependency status](https://deps.rs/repo/github/rust-random/getrandom/status.svg)](https://deps.rs/repo/github/rust-random/getrandom)
+[![Build Status]][GitHub Actions] [![Crate]][crates.io] [![Documentation]][docs.rs] [![Dependency Status]][deps.rs] [![Downloads]][crates.io] [![License]][LICENSE-MIT]
+
+[GitHub Actions]: https://github.com/rust-random/getrandom/actions?query=workflow:Tests+branch:master
+[Build Status]: https://github.com/rust-random/getrandom/workflows/Tests/badge.svg?branch=master
+[crates.io]: https://crates.io/crates/getrandom
+[Crate]: https://img.shields.io/crates/v/getrandom
+[docs.rs]: https://docs.rs/getrandom
+[Documentation]: https://docs.rs/getrandom/badge.svg
+[deps.rs]: https://deps.rs/repo/github/rust-random/getrandom
+[Dependency Status]: https://deps.rs/repo/github/rust-random/getrandom/status.svg
+[Downloads]: https://img.shields.io/crates/d/getrandom
+[LICENSE-MIT]: https://raw.githubusercontent.com/rust-random/getrandom/master/LICENSE-MIT
+[License]: https://img.shields.io/crates/l/getrandom
 
 
 A Rust library for retrieving random data from (operating) system source. It is
@@ -24,7 +32,7 @@
 
 ```toml
 [dependencies]
-getrandom = "0.1"
+getrandom = "0.2"
 ```
 
 Then invoke the `getrandom` function:
@@ -37,36 +45,14 @@
 }
 ```
 
-## Features
-
-This library is `no_std` for every supported target. However, getting randomness
-usually requires calling some external system API. This means most platforms
-will require linking against system libraries (i.e. `libc` for Unix,
-`Advapi32.dll` for Windows, Security framework on iOS, etc...).
-
-The `log` library is supported as an optional dependency. If enabled, error
-reporting will be improved on some platforms.
-
-For the `wasm32-unknown-unknown` target, one of the following features should be
-enabled:
-
--   [`wasm-bindgen`](https://crates.io/crates/wasm_bindgen)
--   [`stdweb`](https://crates.io/crates/stdweb)
-
-By default, compiling `getrandom` for an unsupported target will result in
-a compilation error. If you want to build an application which uses `getrandom`
-for such target, you can either:
-- Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml`
-to switch to a custom implementation with a support of your target.
-- Enable the `dummy` feature to have getrandom use an implementation that always
-fails at run-time on unsupported targets.
-
-[replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section
-[patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section
+For more information about supported targets, entropy sources, `no_std` targets,
+crate features, WASM support and Custom RNGs see the
+[`getrandom` documentation](https://docs.rs/getrandom/latest) and
+[`getrandom::Error` documentation](https://docs.rs/getrandom/latest/getrandom/struct.Error.html).
 
 ## Minimum Supported Rust Version
 
-This crate requires Rust 1.32.0 or later.
+This crate requires Rust 1.34.0 or later.
 
 # License
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index bc55ed7..efa80d7 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,14 +1,32 @@
-// Generated by cargo2android.py for tests in Android.bp
+// Generated by update_crate_tests.py for tests that depend on this crate.
 {
   "presubmit": [
     {
+      "name": "keystore2_test"
+    },
+    {
+      "name": "getrandom_device_test_tests_custom"
+    },
+    {
+      "name": "rand_xorshift_device_test_tests_mod"
+    },
+    {
+      "name": "getrandom_device_test_tests_normal"
+    },
+    {
+      "name": "rand_xorshift_device_test_src_lib"
+    },
+    {
+      "name": "getrandom_device_test_tests_rdrand"
+    },
+    {
+      "name": "rand_core_device_test_src_lib"
+    },
+    {
       "name": "getrandom_device_test_src_lib"
     },
     {
-      "name": "getrandom_device_test_tests_common"
-    },
-    {
-      "name": "rand_core_device_test_src_lib"
+      "name": "vpnprofilestore_test"
     }
   ]
 }
diff --git a/benches/mod.rs b/benches/mod.rs
index 07953f1..a93e720 100644
--- a/benches/mod.rs
+++ b/benches/mod.rs
@@ -1,5 +1,4 @@
 #![feature(test)]
-extern crate getrandom;
 extern crate test;
 
 #[bench]
diff --git a/build.rs b/build.rs
index 1beb4ed..95f4b90 100644
--- a/build.rs
+++ b/build.rs
@@ -4,14 +4,9 @@
 
 fn main() {
     let target = env::var("TARGET").expect("TARGET was not set");
-    if target.contains("-uwp-windows-") {
+    if target.contains("windows") {
         // for BCryptGenRandom
         println!("cargo:rustc-link-lib=bcrypt");
-        // to work around unavailability of `target_vendor` on Rust 1.33
-        println!("cargo:rustc-cfg=getrandom_uwp");
-    } else if target.contains("windows") {
-        // for RtlGenRandom (aka SystemFunction036)
-        println!("cargo:rustc-link-lib=advapi32");
     } else if target.contains("apple-ios") {
         // for SecRandomCopyBytes and kSecRandomDefault
         println!("cargo:rustc-link-lib=framework=Security");
diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs
index eb564ff..f26f260 100644
--- a/src/bsd_arandom.rs
+++ b/src/bsd_arandom.rs
@@ -7,8 +7,7 @@
 // except according to those terms.
 
 //! Implementation for FreeBSD and NetBSD
-use crate::util_libc::sys_fill_exact;
-use crate::Error;
+use crate::{util_libc::sys_fill_exact, Error};
 use core::ptr;
 
 fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
@@ -25,7 +24,6 @@
         )
     };
     if ret == -1 {
-        error!("sysctl kern.arandom: syscall failed");
         -1
     } else {
         len as libc::ssize_t
@@ -45,5 +43,10 @@
             return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
         }
     }
-    sys_fill_exact(dest, kern_arnd)
+    // Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and
+    // older NetBSD kernels will fail on longer buffers.
+    for chunk in dest.chunks_mut(256) {
+        sys_fill_exact(chunk, kern_arnd)?
+    }
+    Ok(())
 }
diff --git a/src/cloudabi.rs b/src/cloudabi.rs
deleted file mode 100644
index d3d0928..0000000
--- a/src/cloudabi.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation for CloudABI
-use crate::Error;
-use core::num::NonZeroU32;
-
-extern "C" {
-    fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16;
-}
-
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    let errno = unsafe { cloudabi_sys_random_get(dest.as_mut_ptr(), dest.len()) };
-    if let Some(code) = NonZeroU32::new(errno as u32) {
-        error!("cloudabi_sys_random_get: failed with {}", errno);
-        Err(Error::from(code))
-    } else {
-        Ok(()) // Zero means success for CloudABI
-    }
-}
diff --git a/src/custom.rs b/src/custom.rs
new file mode 100644
index 0000000..0d3123c
--- /dev/null
+++ b/src/custom.rs
@@ -0,0 +1,102 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! An implementation which calls out to an externally defined function.
+use crate::Error;
+use core::num::NonZeroU32;
+
+/// Register a function to be invoked by `getrandom` on unsupported targets.
+///
+/// ## Writing a custom `getrandom` implementation
+///
+/// The function to register must have the same signature as
+/// [`getrandom::getrandom`](crate::getrandom). The function can be defined
+/// wherever you want, either in root crate or a dependant crate.
+///
+/// For example, if we wanted a `failure-getrandom` crate containing an
+/// implementation that always fails, we would first depend on `getrandom`
+/// (for the [`Error`] type) in `failure-getrandom/Cargo.toml`:
+/// ```toml
+/// [dependencies]
+/// getrandom = "0.2"
+/// ```
+/// Note that the crate containing this function does **not** need to enable the
+/// `"custom"` Cargo feature.
+///
+/// Next, in `failure-getrandom/src/lib.rs`, we define our function:
+/// ```rust
+/// use core::num::NonZeroU32;
+/// use getrandom::Error;
+///
+/// // Some application-specific error code
+/// const MY_CUSTOM_ERROR_CODE: u32 = Error::CUSTOM_START + 42;
+/// pub fn always_fail(buf: &mut [u8]) -> Result<(), Error> {
+///     let code = NonZeroU32::new(MY_CUSTOM_ERROR_CODE).unwrap();
+///     Err(Error::from(code))
+/// }
+/// ```
+///
+/// ## Registering a custom `getrandom` implementation
+///
+/// Functions can only be registered in the root binary crate. Attempting to
+/// register a function in a non-root crate will result in a linker error.
+/// This is similar to
+/// [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) or
+/// [`#[global_allocator]`](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html),
+/// where helper crates define handlers/allocators but only the binary crate
+/// actually _uses_ the functionality.
+///
+/// To register the function, we first depend on `failure-getrandom` _and_
+/// `getrandom` in `Cargo.toml`:
+/// ```toml
+/// [dependencies]
+/// failure-getrandom = "0.1"
+/// getrandom = { version = "0.2", features = ["custom"] }
+/// ```
+///
+/// Then, we register the function in `src/main.rs`:
+/// ```rust
+/// # mod failure_getrandom { pub fn always_fail(_: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!() } }
+/// use failure_getrandom::always_fail;
+/// use getrandom::register_custom_getrandom;
+///
+/// register_custom_getrandom!(always_fail);
+/// ```
+///
+/// Now any user of `getrandom` (direct or indirect) on this target will use the
+/// registered function. As noted in the
+/// [top-level documentation](index.html#custom-implementations) this
+/// registration only has an effect on unsupported targets.
+#[macro_export]
+#[cfg_attr(docsrs, doc(cfg(feature = "custom")))]
+macro_rules! register_custom_getrandom {
+    ($path:path) => {
+        // We use an extern "C" function to get the guarantees of a stable ABI.
+        #[no_mangle]
+        extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
+            let f: fn(&mut [u8]) -> Result<(), ::getrandom::Error> = $path;
+            let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) };
+            match f(slice) {
+                Ok(()) => 0,
+                Err(e) => e.code().get(),
+            }
+        }
+    };
+}
+
+#[allow(dead_code)]
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+    extern "C" {
+        fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
+    }
+    let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
+    match NonZeroU32::new(ret) {
+        None => Ok(()),
+        Some(code) => Err(Error::from(code)),
+    }
+}
diff --git a/src/dummy.rs b/src/dummy.rs
deleted file mode 100644
index 0c24ba0..0000000
--- a/src/dummy.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! A dummy implementation for unsupported targets which always fails
-use crate::{error::UNSUPPORTED, Error};
-
-pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> {
-    Err(UNSUPPORTED)
-}
diff --git a/src/error.rs b/src/error.rs
index 31ae24d..48abdc1 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -5,10 +5,9 @@
 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-use core::fmt;
-use core::num::NonZeroU32;
+use core::{fmt, num::NonZeroU32};
 
-/// A small and `no_std` compatible error type.
+/// A small and `no_std` compatible error type
 ///
 /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
 /// if so, which error code the OS gave the application. If such an error is
@@ -16,16 +15,44 @@
 ///
 /// Internally this type is a NonZeroU32, with certain values reserved for
 /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
+///
+/// *If this crate's `"std"` Cargo feature is enabled*, then:
+/// - [`getrandom::Error`][Error] implements
+///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
+/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
+///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub struct Error(NonZeroU32);
 
+const fn internal_error(n: u16) -> Error {
+    // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
+    let code = Error::INTERNAL_START + (n as u32);
+    Error(unsafe { NonZeroU32::new_unchecked(code) })
+}
+
 impl Error {
-    #[deprecated(since = "0.1.7")]
-    /// Unknown error.
-    pub const UNKNOWN: Error = UNSUPPORTED;
-    #[deprecated(since = "0.1.7")]
-    /// System entropy source is unavailable.
-    pub const UNAVAILABLE: Error = UNSUPPORTED;
+    /// This target/platform is not supported by `getrandom`.
+    pub const UNSUPPORTED: Error = internal_error(0);
+    /// The platform-specific `errno` returned a non-positive value.
+    pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
+    /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
+    pub const IOS_SEC_RANDOM: Error = internal_error(3);
+    /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
+    pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
+    /// RDRAND instruction failed due to a hardware issue.
+    pub const FAILED_RDRAND: Error = internal_error(5);
+    /// RDRAND instruction unsupported on this target.
+    pub const NO_RDRAND: Error = internal_error(6);
+    /// The browser does not have support for `self.crypto`.
+    pub const WEB_CRYPTO: Error = internal_error(7);
+    /// The browser does not have support for `crypto.getRandomValues`.
+    pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
+    /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
+    pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
+    /// NodeJS does not have support for the `crypto` module.
+    pub const NODE_CRYPTO: Error = internal_error(12);
+    /// NodeJS does not have support for `crypto.randomFillSync`.
+    pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
 
     /// Codes below this point represent OS Errors (i.e. positive i32 values).
     /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
@@ -38,9 +65,11 @@
 
     /// Extract the raw OS error code (if this error came from the OS)
     ///
-    /// This method is identical to `std::io::Error::raw_os_error()`, except
+    /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
     /// that it works in `no_std` contexts. If this method returns `None`, the
     /// error value can still be formatted via the `Display` implementation.
+    ///
+    /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
     #[inline]
     pub fn raw_os_error(self) -> Option<i32> {
         if self.0.get() < Self::INTERNAL_START {
@@ -55,7 +84,7 @@
     /// This code can either come from the underlying OS, or be a custom error.
     /// Use [`Error::raw_os_error()`] to disambiguate.
     #[inline]
-    pub fn code(self) -> NonZeroU32 {
+    pub const fn code(self) -> NonZeroU32 {
         self.0
     }
 }
@@ -125,41 +154,19 @@
     }
 }
 
-// TODO: Convert to a function when min_version >= 1.33
-macro_rules! internal_error {
-    ($n:expr) => {
-        Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) })
-    };
-}
-
-/// Internal Error constants
-pub(crate) const UNSUPPORTED: Error = internal_error!(0);
-pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1);
-pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2);
-pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3);
-pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4);
-pub(crate) const FAILED_RDRAND: Error = internal_error!(5);
-pub(crate) const NO_RDRAND: Error = internal_error!(6);
-pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7);
-pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8);
-pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9);
-pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10);
-pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11);
-
 fn internal_desc(error: Error) -> Option<&'static str> {
     match error {
-        UNSUPPORTED => Some("getrandom: this target is not supported"),
-        ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
-        UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"),
-        SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"),
-        RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"),
-        FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
-        NO_RDRAND => Some("RDRAND: instruction not supported"),
-        BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"),
-        BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"),
-        STDWEB_NO_RNG => Some("stdweb: no randomness source available"),
-        STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"),
-        RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"),
+        Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
+        Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
+        Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
+        Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
+        Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
+        Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
+        Error::WEB_CRYPTO => Some("Web API self.crypto is unavailable"),
+        Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"),
+        Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
+        Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),
+        Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"),
         _ => None,
     }
 }
diff --git a/src/error_impls.rs b/src/error_impls.rs
index 007472e..61f46d2 100644
--- a/src/error_impls.rs
+++ b/src/error_impls.rs
@@ -5,24 +5,13 @@
 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+#![cfg_attr(docsrs, doc(cfg(feature = "std")))]
 extern crate std;
 
-use crate::{error::UNKNOWN_IO_ERROR, Error};
+use crate::Error;
 use core::convert::From;
-use core::num::NonZeroU32;
 use std::io;
 
-impl From<io::Error> for Error {
-    fn from(err: io::Error) -> Self {
-        if let Some(errno) = err.raw_os_error() {
-            if let Some(code) = NonZeroU32::new(errno as u32) {
-                return Error::from(code);
-            }
-        }
-        UNKNOWN_IO_ERROR
-    }
-}
-
 impl From<Error> for io::Error {
     fn from(err: Error) -> Self {
         match err.raw_os_error() {
diff --git a/src/ios.rs b/src/ios.rs
index 30c008c..5e38474 100644
--- a/src/ios.rs
+++ b/src/ios.rs
@@ -7,24 +7,19 @@
 // except according to those terms.
 
 //! Implementation for iOS
-use crate::{error::SEC_RANDOM_FAILED, Error};
-
-// TODO: Make extern once extern_types feature is stabilized. See:
-//   https://github.com/rust-lang/rust/issues/43467
-#[repr(C)]
-struct SecRandom([u8; 0]);
+use crate::Error;
+use core::{ffi::c_void, ptr::null};
 
 #[link(name = "Security", kind = "framework")]
 extern "C" {
-    static kSecRandomDefault: *const SecRandom;
-
-    fn SecRandomCopyBytes(rnd: *const SecRandom, count: usize, bytes: *mut u8) -> i32;
+    fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32;
 }
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
+    // Apple's documentation guarantees kSecRandomDefault is a synonym for NULL.
+    let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) };
     if ret == -1 {
-        Err(SEC_RANDOM_FAILED)
+        Err(Error::IOS_SEC_RANDOM)
     } else {
         Ok(())
     }
diff --git a/src/js.rs b/src/js.rs
new file mode 100644
index 0000000..d48c7d3
--- /dev/null
+++ b/src/js.rs
@@ -0,0 +1,106 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use crate::Error;
+
+extern crate std;
+use std::thread_local;
+
+use js_sys::Uint8Array;
+use wasm_bindgen::prelude::*;
+
+// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
+const BROWSER_CRYPTO_BUFFER_SIZE: usize = 256;
+
+enum RngSource {
+    Node(NodeCrypto),
+    Browser(BrowserCrypto, Uint8Array),
+}
+
+// JsValues are always per-thread, so we initialize RngSource for each thread.
+//   See: https://github.com/rustwasm/wasm-bindgen/pull/955
+thread_local!(
+    static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
+);
+
+pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+    RNG_SOURCE.with(|result| {
+        let source = result.as_ref().map_err(|&e| e)?;
+
+        match source {
+            RngSource::Node(n) => {
+                if n.random_fill_sync(dest).is_err() {
+                    return Err(Error::NODE_RANDOM_FILL_SYNC);
+                }
+            }
+            RngSource::Browser(crypto, buf) => {
+                // getRandomValues does not work with all types of WASM memory,
+                // so we initially write to browser memory to avoid exceptions.
+                for chunk in dest.chunks_mut(BROWSER_CRYPTO_BUFFER_SIZE) {
+                    // The chunk can be smaller than buf's length, so we call to
+                    // JS to create a smaller view of buf without allocation.
+                    let sub_buf = buf.subarray(0, chunk.len() as u32);
+
+                    if crypto.get_random_values(&sub_buf).is_err() {
+                        return Err(Error::WEB_GET_RANDOM_VALUES);
+                    }
+                    sub_buf.copy_to(chunk);
+                }
+            }
+        };
+        Ok(())
+    })
+}
+
+fn getrandom_init() -> Result<RngSource, Error> {
+    if let Ok(self_) = Global::get_self() {
+        // If `self` is defined then we're in a browser somehow (main window
+        // or web worker). We get `self.crypto` (called `msCrypto` on IE), so we
+        // can call `crypto.getRandomValues`. If `crypto` isn't defined, we
+        // assume we're in an older web browser and the OS RNG isn't available.
+
+        let crypto: BrowserCrypto = match (self_.crypto(), self_.ms_crypto()) {
+            (crypto, _) if !crypto.is_undefined() => crypto,
+            (_, crypto) if !crypto.is_undefined() => crypto,
+            _ => return Err(Error::WEB_CRYPTO),
+        };
+
+        let buf = Uint8Array::new_with_length(BROWSER_CRYPTO_BUFFER_SIZE as u32);
+        return Ok(RngSource::Browser(crypto, buf));
+    }
+
+    let crypto = MODULE.require("crypto").map_err(|_| Error::NODE_CRYPTO)?;
+    Ok(RngSource::Node(crypto))
+}
+
+#[wasm_bindgen]
+extern "C" {
+    type Global;
+    #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)]
+    fn get_self() -> Result<Self_, JsValue>;
+
+    type Self_;
+    #[wasm_bindgen(method, getter, js_name = "msCrypto")]
+    fn ms_crypto(me: &Self_) -> BrowserCrypto;
+    #[wasm_bindgen(method, getter)]
+    fn crypto(me: &Self_) -> BrowserCrypto;
+
+    type BrowserCrypto;
+    #[wasm_bindgen(method, js_name = getRandomValues, catch)]
+    fn get_random_values(me: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
+
+    #[wasm_bindgen(js_name = module)]
+    static MODULE: NodeModule;
+
+    type NodeModule;
+    #[wasm_bindgen(method, catch)]
+    fn require(this: &NodeModule, s: &str) -> Result<NodeCrypto, JsValue>;
+
+    type NodeCrypto;
+    #[wasm_bindgen(method, js_name = randomFillSync, catch)]
+    fn random_fill_sync(crypto: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
+}
diff --git a/src/lib.rs b/src/lib.rs
index c305406..b1a5b10 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,89 +6,111 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Interface to the random number generator of the operating system.
+//! Interface to the operating system's random number generator.
 //!
-//! # Platform sources
+//! # Supported targets
 //!
-//! | OS               | interface
-//! |------------------|---------------------------------------------------------
-//! | Linux, Android   | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
-//! | Windows          | [`RtlGenRandom`][3]
-//! | macOS            | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
-//! | iOS              | [`SecRandomCopyBytes`][4]
-//! | FreeBSD          | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
-//! | OpenBSD          | [`getentropy`][6]
-//! | NetBSD           | [`kern.arandom`][7]
-//! | Dragonfly BSD    | [`/dev/random`][8]
-//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
-//! | Fuchsia OS       | [`cprng_draw`][11]
-//! | Redox            | [`rand:`][12]
-//! | CloudABI         | [`cloudabi_sys_random_get`][13]
-//! | Haiku            | `/dev/random` (identical to `/dev/urandom`)
-//! | L4RE, SGX, UEFI  | [RDRAND][18]
-//! | Hermit           | [RDRAND][18] as [`sys_rand`][22] is currently broken.
-//! | VxWorks          | `randABytes` after checking entropy pool initialization with `randSecure`
-//! | Web browsers     | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and asm.js][16])
-//! | Node.js          | [`crypto.randomBytes`][15] (see [Support for WebAssembly and asm.js][16])
-//! | WASI             | [`__wasi_random_get`][17]
+//! | Target            | Target Triple      | Implementation
+//! | ----------------- | ------------------ | --------------
+//! | Linux, Android    | `*ā€‘linuxā€‘*`        | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` |
+//! | Windows           | `*ā€‘windowsā€‘*`      | [`BCryptGenRandom`][3] |
+//! | macOS             | `*ā€‘appleā€‘darwin`   | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
+//! | iOS               | `*ā€‘appleā€‘ios`      | [`SecRandomCopyBytes`][4]
+//! | FreeBSD           | `*ā€‘freebsd`        | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
+//! | OpenBSD           | `*ā€‘openbsd`        | [`getentropy`][6]
+//! | NetBSD            | `*ā€‘netbsd`         | [`kern.arandom`][7]
+//! | Dragonfly BSD     | `*ā€‘dragonfly`      | [`/dev/random`][8]
+//! | Solaris, illumos  | `*ā€‘solaris`, `*ā€‘illumos` | [`getrandom()`][9] if available, otherwise [`/dev/random`][10]
+//! | Fuchsia OS        | `*ā€‘fuchsia`        | [`cprng_draw`][11]
+//! | Redox             | `*ā€‘redox`          | [`rand:`][12]
+//! | Haiku             | `*ā€‘haiku`          | `/dev/random` (identical to `/dev/urandom`)
+//! | SGX               | `x86_64ā€‘*ā€‘sgx`     | [RDRAND][18]
+//! | VxWorks           | `*ā€‘wrsā€‘vxworksā€‘*`  | `randABytes` after checking entropy pool initialization with `randSecure`
+//! | Emscripten        | `*ā€‘emscripten`     | `/dev/random` (identical to `/dev/urandom`)
+//! | WASI              | `wasm32ā€‘wasi`      | [`__wasi_random_get`][17]
+//! | Web Browser       | `wasm32ā€‘*ā€‘unknown` | [`Crypto.getRandomValues()`][14], see [WebAssembly support][16]
+//! | Node.js           | `wasm32ā€‘*ā€‘unknown` | [`crypto.randomBytes`][15], see [WebAssembly support][16]
 //!
-//! Getrandom doesn't have a blanket implementation for all Unix-like operating
-//! systems that reads from `/dev/urandom`. This ensures all supported operating
-//! systems are using the recommended interface and respect maximum buffer
-//! sizes.
+//! There is no blanket implementation on `unix` targets that reads from
+//! `/dev/urandom`. This ensures all supported targets are using the recommended
+//! interface and respect maximum buffer sizes.
+//!
+//! Pull Requests that add support for new targets to `getrandom` are always welcome.
 //!
 //! ## Unsupported targets
 //!
-//! By default, compiling `getrandom` for an unsupported target will result in
-//! a compilation error. If you want to build an application which uses `getrandom`
-//! for such target, you can either:
-//! - Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml`
-//! to switch to a custom implementation with a support of your target.
-//! - Enable the `dummy` feature to have getrandom use an implementation that always
-//! fails at run-time on unsupported targets.
+//! By default, `getrandom` will not compile on unsupported targets, but certain
+//! features allow a user to select a "fallback" implementation if no supported
+//! implementation exists.
 //!
-//! [replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section
-//! [patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section
+//! All of the below mechanisms only affect unsupported
+//! targets. Supported targets will _always_ use their supported implementations.
+//! This prevents a crate from overriding a secure source of randomness
+//! (either accidentally or intentionally).
 //!
-//! ## Support for WebAssembly and asm.js
+//! ### RDRAND on x86
 //!
-//! Getrandom supports all of Rust's current `wasm32` targets, and it works with
-//! both Node.js and web browsers. The three Emscripten targets
-//! `asmjs-unknown-emscripten`, `wasm32-unknown-emscripten`, and
-//! `wasm32-experimental-emscripten` use Emscripten's `/dev/random` emulation.
-//! The WASI target `wasm32-wasi` uses the [`__wasi_random_get`][17] function
-//! defined by the WASI standard.
+//! *If the `"rdrand"` Cargo feature is enabled*, `getrandom` will fallback to using
+//! the [`RDRAND`][18] instruction to get randomness on `no_std` `x86`/`x86_64`
+//! targets. This feature has no effect on other CPU architectures.
 //!
-//! Getrandom also supports `wasm32-unknown-unknown` by directly calling
-//! JavaScript methods. Rust currently has two ways to do this: [bindgen] and
-//! [stdweb]. Getrandom supports using either one by enabling the
-//! `wasm-bindgen` or `stdweb` crate features. Note that if both features are
-//! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls
-//! to `getrandom` will always fail at runtime.
+//! ### WebAssembly support
 //!
-//! [bindgen]: https://github.com/rust-lang/rust-bindgen
-//! [stdweb]: https://github.com/koute/stdweb
+//! This crate fully supports the
+//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and
+//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/)
+//! targets. However, the `wasm32-unknown-unknown` target is not automatically
+//! supported since, from the target name alone, we cannot deduce which
+//! JavaScript interface is in use (or if JavaScript is available at all).
+//!
+//! Instead, *if the `"js"` Cargo feature is enabled*, this crate will assume
+//! that you are building for an environment containing JavaScript, and will
+//! call the appropriate methods. Both web browser (main window and Web Workers)
+//! and Node.js environments are supported, invoking the methods
+//! [described above](#supported-targets) using the
+//! [wasm-bindgen](https://github.com/rust-lang/rust-bindgen) toolchain.
+//!
+//! This feature has no effect on targets other than `wasm32-unknown-unknown`.
+//!
+//! ### Custom implementations
+//!
+//! The [`register_custom_getrandom!`] macro allows a user to mark their own
+//! function as the backing implementation for [`getrandom`]. See the macro's
+//! documentation for more information about writing and registering your own
+//! custom implementations.
+//!
+//! Note that registering a custom implementation only has an effect on targets
+//! that would otherwise not compile. Any supported targets (including those
+//! using `"rdrand"` and `"js"` Cargo features) continue using their normal
+//! implementations even if a function is registered.
+//!
+//! ### Indirect Dependencies
+//!
+//! If `getrandom` is not a direct dependency of your crate, you can still
+//! enable any of the above fallback behaviors by enabling the relevant
+//! feature in your root crate's `Cargo.toml`:
+//! ```toml
+//! [dependencies]
+//! getrandom = { version = "0.2", features = ["js"] }
+//! ```
 //!
 //! ## Early boot
 //!
-//! It is possible that early in the boot process the OS hasn't had enough time
-//! yet to collect entropy to securely seed its RNG, especially on virtual
-//! machines.
+//! Sometimes, early in the boot process, the OS has not collected enough
+//! entropy to securely seed its RNG. This is especially common on virtual
+//! machines, where standard "random" events are hard to come by.
 //!
-//! Some operating systems always block the thread until the RNG is securely
+//! Some operating system interfaces always block until the RNG is securely
 //! seeded. This can take anywhere from a few seconds to more than a minute.
-//! Others make a best effort to use a seed from before the shutdown and don't
-//! document much.
+//! A few (Linux, NetBSD and Solaris) offer a choice between blocking and
+//! getting an error; in these cases, we always choose to block.
 //!
-//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and
-//! getting an error; in these cases we always choose to block.
-//!
-//! On Linux (when the `getrandom` system call is not available) and on NetBSD
-//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected
-//! enough entropy yet. To avoid returning low-entropy bytes, we first read from
+//! On Linux (when the `getrandom` system call is not available), reading from
+//! `/dev/urandom` never blocks, even when the OS hasn't collected enough
+//! entropy yet. To avoid returning low-entropy bytes, we first poll
 //! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
 //!
-//! # Error handling
+//! ## Error handling
 //!
 //! We always choose failure over returning insecure "random" bytes. In general,
 //! on supported platforms, failure is highly unlikely, though not impossible.
@@ -96,12 +118,9 @@
 //! `getrandom`, hence after the first successful call one can be reasonably
 //! confident that no errors will occur.
 //!
-//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type
-//! for more information on what data is returned on failure.
-//!
 //! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
 //! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
-//! [3]: https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom
+//! [3]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
 //! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
 //! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
 //! [6]: https://man.openbsd.org/getentropy.2
@@ -111,153 +130,88 @@
 //! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
 //! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
 //! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
-//! [13]: https://github.com/nuxinl/cloudabi#random_get
 //! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
 //! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
-//! [16]: #support-for-webassembly-and-asmjs
+//! [16]: #webassembly-support
 //! [17]: https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md#__wasi_random_get
 //! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
 //! [19]: https://www.unix.com/man-page/mojave/2/getentropy/
 //! [20]: https://www.unix.com/man-page/mojave/4/random/
 //! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
-//! [22]: https://github.com/hermitcore/libhermit-rs/blob/09c38b0371cee6f56a541400ba453e319e43db53/src/syscalls/random.rs#L21
 
 #![doc(
     html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
     html_favicon_url = "https://www.rust-lang.org/favicon.ico",
-    html_root_url = "https://rust-random.github.io/rand/"
+    html_root_url = "https://docs.rs/getrandom/0.2.2"
 )]
 #![no_std]
-#![cfg_attr(feature = "stdweb", recursion_limit = "128")]
 #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
 
 #[macro_use]
 extern crate cfg_if;
 
-cfg_if! {
-    if #[cfg(feature = "log")] {
-        #[allow(unused)]
-        #[macro_use]
-        extern crate log;
-    } else {
-        #[allow(unused)]
-        macro_rules! error {
-            ($($x:tt)*) => {};
-        }
-        #[allow(unused)]
-        macro_rules! warn {
-            ($($x:tt)*) => {};
-        }
-        #[allow(unused)]
-        macro_rules! info {
-            ($($x:tt)*) => {};
-        }
-    }
-}
-
 mod error;
-pub use crate::error::Error;
-
-#[allow(dead_code)]
 mod util;
+// To prevent a breaking change when targets are added, we always export the
+// register_custom_getrandom macro, so old Custom RNG crates continue to build.
+#[cfg(feature = "custom")]
+mod custom;
+#[cfg(feature = "std")]
+mod error_impls;
 
-#[cfg(target_os = "vxworks")]
-#[allow(dead_code)]
-mod util_libc;
-
-cfg_if! {
-    // Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls.
-    if #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten",
-                 target_os = "freebsd", target_os = "haiku",     target_os = "illumos",
-                 target_os = "linux",   target_os = "macos",     target_os = "netbsd",
-                 target_os = "openbsd", target_os = "redox",     target_os = "solaris"))] {
-        #[allow(dead_code)]
-        mod util_libc;
-        // Keep std-only trait definitions for backwards compatibility
-        mod error_impls;
-    } else if #[cfg(feature = "std")] {
-        mod error_impls;
-    }
-}
-
-// These targets read from a file as a fallback method.
-#[cfg(any(
-    target_os = "android",
-    target_os = "linux",
-    target_os = "macos",
-    target_os = "solaris",
-    target_os = "illumos",
-))]
-mod use_file;
+pub use crate::error::Error;
 
 // System-specific implementations.
 //
 // These should all provide getrandom_inner with the same signature as getrandom.
 cfg_if! {
-    if #[cfg(target_os = "android")] {
+    if #[cfg(any(target_os = "dragonfly", target_os = "emscripten",
+                 target_os = "haiku",     target_os = "redox"))] {
+        mod util_libc;
+        #[path = "use_file.rs"] mod imp;
+    } else if #[cfg(any(target_os = "android", target_os = "linux"))] {
+        mod util_libc;
+        mod use_file;
         #[path = "linux_android.rs"] mod imp;
-    } else if #[cfg(target_os = "cloudabi")] {
-        #[path = "cloudabi.rs"] mod imp;
-    } else if #[cfg(target_os = "dragonfly")] {
-        #[path = "use_file.rs"] mod imp;
-    } else if #[cfg(target_os = "emscripten")] {
-        #[path = "use_file.rs"] mod imp;
-    } else if #[cfg(target_os = "freebsd")] {
+    } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
+        mod util_libc;
+        mod use_file;
+        #[path = "solaris_illumos.rs"] mod imp;
+    } else if #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] {
+        mod util_libc;
         #[path = "bsd_arandom.rs"] mod imp;
     } else if #[cfg(target_os = "fuchsia")] {
         #[path = "fuchsia.rs"] mod imp;
-    } else if #[cfg(target_os = "haiku")] {
-        #[path = "use_file.rs"] mod imp;
-    } else if #[cfg(target_os = "illumos")] {
-        #[path = "solaris_illumos.rs"] mod imp;
     } else if #[cfg(target_os = "ios")] {
         #[path = "ios.rs"] mod imp;
-    } else if #[cfg(target_os = "linux")] {
-        #[path = "linux_android.rs"] mod imp;
     } else if #[cfg(target_os = "macos")] {
+        mod util_libc;
+        mod use_file;
         #[path = "macos.rs"] mod imp;
-    } else if #[cfg(target_os = "netbsd")] {
-        #[path = "bsd_arandom.rs"] mod imp;
     } else if #[cfg(target_os = "openbsd")] {
+        mod util_libc;
         #[path = "openbsd.rs"] mod imp;
-    } else if #[cfg(target_os = "redox")] {
-        #[path = "use_file.rs"] mod imp;
-    } else if #[cfg(target_os = "solaris")] {
-        #[path = "solaris_illumos.rs"] mod imp;
     } else if #[cfg(target_os = "wasi")] {
         #[path = "wasi.rs"] mod imp;
     } else if #[cfg(target_os = "vxworks")] {
+        mod util_libc;
         #[path = "vxworks.rs"] mod imp;
-    } else if #[cfg(all(windows, getrandom_uwp))] {
-        #[path = "windows_uwp.rs"] mod imp;
     } else if #[cfg(windows)] {
         #[path = "windows.rs"] mod imp;
-    } else if #[cfg(all(target_arch = "x86_64", any(
-                  target_os = "hermit",
-                  target_os = "l4re",
-                  target_os = "uefi",
-                  target_env = "sgx",
-              )))] {
+    } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
         #[path = "rdrand.rs"] mod imp;
-    } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
-        cfg_if! {
-            if #[cfg(feature = "wasm-bindgen")] {
-                #[path = "wasm32_bindgen.rs"] mod imp;
-            } else if #[cfg(feature = "stdweb")] {
-                #[path = "wasm32_stdweb.rs"] mod imp;
-            } else {
-                // Always have an implementation for wasm32-unknown-unknown.
-                // See https://github.com/rust-random/getrandom/issues/87
-                #[path = "dummy.rs"] mod imp;
-            }
-        }
-    } else if #[cfg(feature = "dummy")] {
-        #[path = "dummy.rs"] mod imp;
+    } else if #[cfg(all(feature = "rdrand",
+                        any(target_arch = "x86_64", target_arch = "x86")))] {
+        #[path = "rdrand.rs"] mod imp;
+    } else if #[cfg(all(feature = "js",
+                        target_arch = "wasm32", target_os = "unknown"))] {
+        #[path = "js.rs"] mod imp;
+    } else if #[cfg(feature = "custom")] {
+        use custom as imp;
     } else {
-        compile_error!("\
-            target is not supported, for more information see: \
-            https://docs.rs/getrandom/#unsupported-targets\
-        ");
+        compile_error!("target is not supported, for more information see: \
+                        https://docs.rs/getrandom/#unsupported-targets");
     }
 }
 
@@ -274,7 +228,7 @@
 /// In general, `getrandom` will be fast enough for interactive usage, though
 /// significantly slower than a user-space CSPRNG; for the latter consider
 /// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
-pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> {
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
     if dest.is_empty() {
         return Ok(());
     }
diff --git a/src/linux_android.rs b/src/linux_android.rs
index a29feb5..5508fdd 100644
--- a/src/linux_android.rs
+++ b/src/linux_android.rs
@@ -7,9 +7,11 @@
 // except according to those terms.
 
 //! Implementation for Linux / Android
-use crate::util::LazyBool;
-use crate::util_libc::{last_os_error, sys_fill_exact};
-use crate::{use_file, Error};
+use crate::{
+    util::LazyBool,
+    util_libc::{last_os_error, sys_fill_exact},
+    {use_file, Error},
+};
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
     static HAS_GETRANDOM: LazyBool = LazyBool::new();
diff --git a/src/macos.rs b/src/macos.rs
index c3bc533..585a35a 100644
--- a/src/macos.rs
+++ b/src/macos.rs
@@ -7,8 +7,11 @@
 // except according to those terms.
 
 //! Implementation for macOS
-use crate::util_libc::{last_os_error, Weak};
-use crate::{use_file, Error};
+use crate::{
+    use_file,
+    util_libc::{last_os_error, Weak},
+    Error,
+};
 use core::mem;
 
 type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int;
@@ -20,9 +23,7 @@
         for chunk in dest.chunks_mut(256) {
             let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) };
             if ret != 0 {
-                let err = last_os_error();
-                error!("getentropy syscall failed");
-                return Err(err);
+                return Err(last_os_error());
             }
         }
         Ok(())
diff --git a/src/openbsd.rs b/src/openbsd.rs
index e1ac179..c8d28b3 100644
--- a/src/openbsd.rs
+++ b/src/openbsd.rs
@@ -7,16 +7,13 @@
 // except according to those terms.
 
 //! Implementation for OpenBSD
-use crate::util_libc::last_os_error;
-use crate::Error;
+use crate::{util_libc::last_os_error, Error};
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
     for chunk in dest.chunks_mut(256) {
         let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };
         if ret == -1 {
-            let err = last_os_error();
-            error!("libc::getentropy call failed");
-            return Err(err);
+            return Err(last_os_error());
         }
     }
     Ok(())
diff --git a/src/rdrand.rs b/src/rdrand.rs
index e441682..1df21e5 100644
--- a/src/rdrand.rs
+++ b/src/rdrand.rs
@@ -7,24 +7,30 @@
 // except according to those terms.
 
 //! Implementation for SGX using RDRAND instruction
-use crate::error::{FAILED_RDRAND, NO_RDRAND};
-#[cfg(not(target_feature = "rdrand"))]
-use crate::util::LazyBool;
 use crate::Error;
-use core::arch::x86_64::_rdrand64_step;
 use core::mem;
 
+cfg_if! {
+    if #[cfg(target_arch = "x86_64")] {
+        use core::arch::x86_64 as arch;
+        use arch::_rdrand64_step as rdrand_step;
+    } else if #[cfg(target_arch = "x86")] {
+        use core::arch::x86 as arch;
+        use arch::_rdrand32_step as rdrand_step;
+    }
+}
+
 // Recommendation from "Intel® Digital Random Number Generator (DRNG) Software
 // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
 // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1.
 const RETRY_LIMIT: usize = 10;
-const WORD_SIZE: usize = mem::size_of::<u64>();
+const WORD_SIZE: usize = mem::size_of::<usize>();
 
 #[target_feature(enable = "rdrand")]
 unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> {
     for _ in 0..RETRY_LIMIT {
         let mut el = mem::zeroed();
-        if _rdrand64_step(&mut el) == 1 {
+        if rdrand_step(&mut el) == 1 {
             // AMD CPUs from families 14h to 16h (pre Ryzen) sometimes fail to
             // set CF on bogus random data, so we check these values explicitly.
             // See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505
@@ -33,11 +39,10 @@
             if el != 0 && el != !0 {
                 return Ok(el.to_ne_bytes());
             }
-            error!("RDRAND returned {:X}, CPU RNG may be broken", el);
             // Keep looping in case this was a false positive.
         }
     }
-    Err(FAILED_RDRAND)
+    Err(Error::FAILED_RDRAND)
 }
 
 // "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653.
@@ -55,16 +60,18 @@
 // https://github.com/rust-lang-nursery/stdsimd/issues/464
 #[cfg(not(target_feature = "rdrand"))]
 fn is_rdrand_supported() -> bool {
-    use core::arch::x86_64::__cpuid;
-    // SAFETY: All x86_64 CPUs support CPUID leaf 1
+    use crate::util::LazyBool;
+
+    // SAFETY: All Rust x86 targets are new enough to have CPUID, and if CPUID
+    // is supported, CPUID leaf 1 is always supported.
     const FLAG: u32 = 1 << 30;
     static HAS_RDRAND: LazyBool = LazyBool::new();
-    HAS_RDRAND.unsync_init(|| unsafe { (__cpuid(1).ecx & FLAG) != 0 })
+    HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 })
 }
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
     if !is_rdrand_supported() {
-        return Err(NO_RDRAND);
+        return Err(Error::NO_RDRAND);
     }
 
     // SAFETY: After this point, rdrand is supported, so calling the rdrand
diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs
index 9473123..2d1b767 100644
--- a/src/solaris_illumos.rs
+++ b/src/solaris_illumos.rs
@@ -17,8 +17,11 @@
 //! To make sure we can compile on both Solaris and its derivatives, as well as
 //! function, we check for the existence of getrandom(2) in libc by calling
 //! libc::dlsym.
-use crate::util_libc::{sys_fill_exact, Weak};
-use crate::{use_file, Error};
+use crate::{
+    use_file,
+    util_libc::{sys_fill_exact, Weak},
+    Error,
+};
 use core::mem;
 
 #[cfg(target_os = "illumos")]
diff --git a/src/use_file.rs b/src/use_file.rs
index 6e50955..465c069 100644
--- a/src/use_file.rs
+++ b/src/use_file.rs
@@ -7,11 +7,15 @@
 // except according to those terms.
 
 //! Implementations that just need to read from a file
-use crate::util::LazyUsize;
-use crate::util_libc::{open_readonly, sys_fill_exact};
-use crate::Error;
-use core::cell::UnsafeCell;
-use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use crate::{
+    util::LazyUsize,
+    util_libc::{open_readonly, sys_fill_exact},
+    Error,
+};
+use core::{
+    cell::UnsafeCell,
+    sync::atomic::{AtomicUsize, Ordering::Relaxed},
+};
 
 #[cfg(target_os = "redox")]
 const FILE_PATH: &str = "rand:\0";
@@ -99,7 +103,7 @@
         // A negative timeout means an infinite timeout.
         let res = unsafe { libc::poll(&mut pfd, 1, -1) };
         if res >= 0 {
-            assert_eq!(res, 1); // We only used one fd, and cannot timeout.
+            debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout.
             return Ok(());
         }
         let err = crate::util_libc::last_os_error();
diff --git a/src/util.rs b/src/util.rs
index 8dbd8ae..06e23c2 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -5,7 +5,7 @@
 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-
+#![allow(dead_code)]
 use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
 
 // This structure represents a lazily initialized static usize value. Useful
diff --git a/src/util_libc.rs b/src/util_libc.rs
index 1cdc13e..6823609 100644
--- a/src/util_libc.rs
+++ b/src/util_libc.rs
@@ -5,11 +5,9 @@
 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-use crate::error::ERRNO_NOT_POSITIVE;
-use crate::util::LazyUsize;
-use crate::Error;
-use core::num::NonZeroU32;
-use core::ptr::NonNull;
+#![allow(dead_code)]
+use crate::{util::LazyUsize, Error};
+use core::{num::NonZeroU32, ptr::NonNull};
 
 cfg_if! {
     if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
@@ -42,7 +40,7 @@
     if errno > 0 {
         Error::from(NonZeroU32::new(errno as u32).unwrap())
     } else {
-        ERRNO_NOT_POSITIVE
+        Error::ERRNO_NOT_POSITIVE
     }
 }
 
@@ -108,14 +106,10 @@
 
 // SAFETY: path must be null terminated, FD must be manually closed.
 pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
-    debug_assert!(path.as_bytes().last() == Some(&0));
+    debug_assert_eq!(path.as_bytes().last(), Some(&0));
     let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
     if fd < 0 {
         return Err(last_os_error());
     }
-    // O_CLOEXEC works on all Unix targets except for older Linux kernels (pre
-    // 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set.
-    #[cfg(target_os = "linux")]
-    libc::ioctl(fd, libc::FIOCLEX);
     Ok(fd)
 }
diff --git a/src/vxworks.rs b/src/vxworks.rs
index a2fe52a..6cb5d52 100644
--- a/src/vxworks.rs
+++ b/src/vxworks.rs
@@ -7,8 +7,7 @@
 // except according to those terms.
 
 //! Implementation for VxWorks
-use crate::error::{Error, RAND_SECURE_FATAL};
-use crate::util_libc::last_os_error;
+use crate::{util_libc::last_os_error, Error};
 use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
@@ -16,7 +15,7 @@
     while !RNG_INIT.load(Relaxed) {
         let ret = unsafe { libc::randSecure() };
         if ret < 0 {
-            return Err(RAND_SECURE_FATAL);
+            return Err(Error::VXWORKS_RAND_SECURE);
         } else if ret > 0 {
             RNG_INIT.store(true, Relaxed);
             break;
diff --git a/src/wasm32_bindgen.rs b/src/wasm32_bindgen.rs
deleted file mode 100644
index 86839a0..0000000
--- a/src/wasm32_bindgen.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation for WASM via wasm-bindgen
-extern crate std;
-
-use core::cell::RefCell;
-use core::mem;
-use std::thread_local;
-
-use wasm_bindgen::prelude::*;
-
-use crate::error::{BINDGEN_CRYPTO_UNDEF, BINDGEN_GRV_UNDEF};
-use crate::Error;
-
-#[derive(Clone, Debug)]
-enum RngSource {
-    Node(NodeCrypto),
-    Browser(BrowserCrypto),
-}
-
-// JsValues are always per-thread, so we initialize RngSource for each thread.
-//   See: https://github.com/rustwasm/wasm-bindgen/pull/955
-thread_local!(
-    static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
-);
-
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    assert_eq!(mem::size_of::<usize>(), 4);
-
-    RNG_SOURCE.with(|f| {
-        let mut source = f.borrow_mut();
-        if source.is_none() {
-            *source = Some(getrandom_init()?);
-        }
-
-        match source.as_ref().unwrap() {
-            RngSource::Node(n) => n.random_fill_sync(dest),
-            RngSource::Browser(n) => {
-                // see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
-                //
-                // where it says:
-                //
-                // > A QuotaExceededError DOMException is thrown if the
-                // > requested length is greater than 65536 bytes.
-                for chunk in dest.chunks_mut(65536) {
-                    n.get_random_values(chunk)
-                }
-            }
-        };
-        Ok(())
-    })
-}
-
-fn getrandom_init() -> Result<RngSource, Error> {
-    if let Ok(self_) = Global::get_self() {
-        // If `self` is defined then we're in a browser somehow (main window
-        // or web worker). Here we want to try to use
-        // `crypto.getRandomValues`, but if `crypto` isn't defined we assume
-        // we're in an older web browser and the OS RNG isn't available.
-
-        let crypto = self_.crypto();
-        if crypto.is_undefined() {
-            return Err(BINDGEN_CRYPTO_UNDEF);
-        }
-
-        // Test if `crypto.getRandomValues` is undefined as well
-        let crypto: BrowserCrypto = crypto.into();
-        if crypto.get_random_values_fn().is_undefined() {
-            return Err(BINDGEN_GRV_UNDEF);
-        }
-
-        return Ok(RngSource::Browser(crypto));
-    }
-
-    return Ok(RngSource::Node(node_require("crypto")));
-}
-
-#[wasm_bindgen]
-extern "C" {
-    type Global;
-    #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)]
-    fn get_self() -> Result<Self_, JsValue>;
-
-    type Self_;
-    #[wasm_bindgen(method, getter, structural)]
-    fn crypto(me: &Self_) -> JsValue;
-
-    #[derive(Clone, Debug)]
-    type BrowserCrypto;
-
-    // TODO: these `structural` annotations here ideally wouldn't be here to
-    // avoid a JS shim, but for now with feature detection they're
-    // unavoidable.
-    #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)]
-    fn get_random_values_fn(me: &BrowserCrypto) -> JsValue;
-    #[wasm_bindgen(method, js_name = getRandomValues, structural)]
-    fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]);
-
-    #[wasm_bindgen(js_name = require)]
-    fn node_require(s: &str) -> NodeCrypto;
-
-    #[derive(Clone, Debug)]
-    type NodeCrypto;
-
-    #[wasm_bindgen(method, js_name = randomFillSync, structural)]
-    fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]);
-}
diff --git a/src/wasm32_stdweb.rs b/src/wasm32_stdweb.rs
deleted file mode 100644
index 6e5e78a..0000000
--- a/src/wasm32_stdweb.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation for WASM via stdweb
-extern crate std;
-
-use core::mem;
-
-use stdweb::js;
-use stdweb::unstable::TryInto;
-use stdweb::web::error::Error as WebError;
-
-use crate::error::{STDWEB_NO_RNG, STDWEB_RNG_FAILED};
-use crate::Error;
-use std::sync::Once;
-
-#[derive(Clone, Copy, Debug)]
-enum RngSource {
-    Browser,
-    Node,
-}
-
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    assert_eq!(mem::size_of::<usize>(), 4);
-    static ONCE: Once = Once::new();
-    static mut RNG_SOURCE: Result<RngSource, Error> = Ok(RngSource::Node);
-
-    // SAFETY: RNG_SOURCE is only written once, before being read.
-    ONCE.call_once(|| unsafe {
-        RNG_SOURCE = getrandom_init();
-    });
-    getrandom_fill(unsafe { RNG_SOURCE }?, dest)
-}
-
-fn getrandom_init() -> Result<RngSource, Error> {
-    let result = js! {
-        try {
-            if (
-                typeof self === "object" &&
-                typeof self.crypto === "object" &&
-                typeof self.crypto.getRandomValues === "function"
-            ) {
-                return { success: true, ty: 1 };
-            }
-
-            if (typeof require("crypto").randomBytes === "function") {
-                return { success: true, ty: 2 };
-            }
-
-            return { success: false, error: new Error("not supported") };
-        } catch(err) {
-            return { success: false, error: err };
-        }
-    };
-
-    if js! { return @{ result.as_ref() }.success } == true {
-        let ty = js! { return @{ result }.ty };
-
-        if ty == 1 {
-            Ok(RngSource::Browser)
-        } else if ty == 2 {
-            Ok(RngSource::Node)
-        } else {
-            unreachable!()
-        }
-    } else {
-        let _err: WebError = js! { return @{ result }.error }.try_into().unwrap();
-        error!("getrandom unavailable: {}", _err);
-        Err(STDWEB_NO_RNG)
-    }
-}
-
-fn getrandom_fill(source: RngSource, dest: &mut [u8]) -> Result<(), Error> {
-    for chunk in dest.chunks_mut(65536) {
-        let len = chunk.len() as u32;
-        let ptr = chunk.as_mut_ptr() as i32;
-
-        let result = match source {
-            RngSource::Browser => js! {
-                try {
-                    let array = new Uint8Array(@{ len });
-                    self.crypto.getRandomValues(array);
-                    HEAPU8.set(array, @{ ptr });
-
-                    return { success: true };
-                } catch(err) {
-                    return { success: false, error: err };
-                }
-            },
-            RngSource::Node => js! {
-                try {
-                    let bytes = require("crypto").randomBytes(@{ len });
-                    HEAPU8.set(new Uint8Array(bytes), @{ ptr });
-
-                    return { success: true };
-                } catch(err) {
-                    return { success: false, error: err };
-                }
-            },
-        };
-
-        if js! { return @{ result.as_ref() }.success } != true {
-            let _err: WebError = js! { return @{ result }.error }.try_into().unwrap();
-            error!("getrandom failed: {}", _err);
-            return Err(STDWEB_RNG_FAILED);
-        }
-    }
-    Ok(())
-}
diff --git a/src/windows.rs b/src/windows.rs
index e1b8df6..56b3d07 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -6,20 +6,41 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Implementation for Windows
-use crate::{error::RTL_GEN_RANDOM_FAILED, Error};
+use crate::Error;
+use core::{ffi::c_void, num::NonZeroU32, ptr};
+
+const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
 
 extern "system" {
-    #[link_name = "SystemFunction036"]
-    fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8;
+    fn BCryptGenRandom(
+        hAlgorithm: *mut c_void,
+        pBuffer: *mut u8,
+        cbBuffer: u32,
+        dwFlags: u32,
+    ) -> u32;
 }
 
 pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
     // Prevent overflow of u32
     for chunk in dest.chunks_mut(u32::max_value() as usize) {
-        let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr(), chunk.len() as u32) };
-        if ret == 0 {
-            return Err(RTL_GEN_RANDOM_FAILED);
+        let ret = unsafe {
+            BCryptGenRandom(
+                ptr::null_mut(),
+                chunk.as_mut_ptr(),
+                chunk.len() as u32,
+                BCRYPT_USE_SYSTEM_PREFERRED_RNG,
+            )
+        };
+        // NTSTATUS codes use the two highest bits for severity status.
+        if ret >> 30 == 0b11 {
+            // We zeroize the highest bit, so the error code will reside
+            // inside the range designated for OS codes.
+            let code = ret ^ (1 << 31);
+            // SAFETY: the second highest bit is always equal to one,
+            // so it's impossible to get zero. Unfortunately the type
+            // system does not have a way to express this yet.
+            let code = unsafe { NonZeroU32::new_unchecked(code) };
+            return Err(Error::from(code));
         }
     }
     Ok(())
diff --git a/src/windows_uwp.rs b/src/windows_uwp.rs
deleted file mode 100644
index 586c6f6..0000000
--- a/src/windows_uwp.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2018 Developers of the Rand project.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation for Windows UWP targets. After deprecation of Windows XP
-//! and Vista, this can supersede the `RtlGenRandom`-based implementation.
-use crate::Error;
-use core::{ffi::c_void, num::NonZeroU32, ptr};
-
-const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
-
-extern "system" {
-    fn BCryptGenRandom(
-        hAlgorithm: *mut c_void,
-        pBuffer: *mut u8,
-        cbBuffer: u32,
-        dwFlags: u32,
-    ) -> u32;
-}
-
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
-    // Prevent overflow of u32
-    for chunk in dest.chunks_mut(u32::max_value() as usize) {
-        let ret = unsafe {
-            BCryptGenRandom(
-                ptr::null_mut(),
-                chunk.as_mut_ptr(),
-                chunk.len() as u32,
-                BCRYPT_USE_SYSTEM_PREFERRED_RNG,
-            )
-        };
-        // NTSTATUS codes use two highest bits for severity status
-        match ret >> 30 {
-            0b01 => {
-                info!("BCryptGenRandom: information code 0x{:08X}", ret);
-            }
-            0b10 => {
-                warn!("BCryptGenRandom: warning code 0x{:08X}", ret);
-            }
-            0b11 => {
-                error!("BCryptGenRandom: failed with 0x{:08X}", ret);
-                // We zeroize the highest bit, so the error code will reside
-                // inside the range of designated for OS codes.
-                let code = ret ^ (1 << 31);
-                // SAFETY: the second highest bit is always equal to one,
-                // so it's impossible to get zero. Unfortunately compiler
-                // is not smart enough to figure out it yet.
-                let code = unsafe { NonZeroU32::new_unchecked(code) };
-                return Err(Error::from(code));
-            }
-            _ => (),
-        }
-    }
-    Ok(())
-}
diff --git a/tests/common.rs b/tests/common/mod.rs
similarity index 62%
rename from tests/common.rs
rename to tests/common/mod.rs
index afefa03..006f230 100644
--- a/tests/common.rs
+++ b/tests/common/mod.rs
@@ -1,26 +1,24 @@
-#[cfg(feature = "wasm-bindgen")]
-use wasm_bindgen_test::*;
+use super::getrandom_impl;
 
-use getrandom::getrandom;
+#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
+use wasm_bindgen_test::wasm_bindgen_test as test;
 
 #[cfg(feature = "test-in-browser")]
-wasm_bindgen_test_configure!(run_in_browser);
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
 
-#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)]
 #[test]
 fn test_zero() {
     // Test that APIs are happy with zero-length requests
-    getrandom(&mut [0u8; 0]).unwrap();
+    getrandom_impl(&mut [0u8; 0]).unwrap();
 }
 
-#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)]
 #[test]
 fn test_diff() {
     let mut v1 = [0u8; 1000];
-    getrandom(&mut v1).unwrap();
+    getrandom_impl(&mut v1).unwrap();
 
     let mut v2 = [0u8; 1000];
-    getrandom(&mut v2).unwrap();
+    getrandom_impl(&mut v2).unwrap();
 
     let mut n_diff_bits = 0;
     for i in 0..v1.len() {
@@ -31,18 +29,18 @@
     assert!(n_diff_bits >= v1.len() as u32);
 }
 
-#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)]
 #[test]
 fn test_huge() {
     let mut huge = [0u8; 100_000];
-    getrandom(&mut huge).unwrap();
+    getrandom_impl(&mut huge).unwrap();
 }
 
-#[cfg(any(unix, windows, target_os = "redox", target_os = "fuchsia"))]
+// On WASM, the thread API always fails/panics
+#[cfg(not(target_arch = "wasm32"))]
 #[test]
 fn test_multithreading() {
-    use std::sync::mpsc::channel;
-    use std::thread;
+    extern crate std;
+    use std::{sync::mpsc::channel, thread, vec};
 
     let mut txs = vec![];
     for _ in 0..20 {
@@ -55,7 +53,7 @@
             let mut v = [0u8; 1000];
 
             for _ in 0..100 {
-                getrandom(&mut v).unwrap();
+                getrandom_impl(&mut v).unwrap();
                 thread::yield_now();
             }
         });
diff --git a/tests/custom.rs b/tests/custom.rs
new file mode 100644
index 0000000..62eae1d
--- /dev/null
+++ b/tests/custom.rs
@@ -0,0 +1,50 @@
+// Test that a custom handler works on wasm32-unknown-unknown
+#![cfg(all(
+    target_arch = "wasm32",
+    target_os = "unknown",
+    feature = "custom",
+    not(feature = "js")
+))]
+
+use wasm_bindgen_test::wasm_bindgen_test as test;
+#[cfg(feature = "test-in-browser")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+use core::{
+    num::NonZeroU32,
+    sync::atomic::{AtomicU8, Ordering},
+};
+use getrandom::{getrandom, register_custom_getrandom, Error};
+
+fn len7_err() -> Error {
+    NonZeroU32::new(Error::INTERNAL_START + 7).unwrap().into()
+}
+
+fn super_insecure_rng(buf: &mut [u8]) -> Result<(), Error> {
+    // Length 7 buffers return a custom error
+    if buf.len() == 7 {
+        return Err(len7_err());
+    }
+    // Otherwise, increment an atomic counter
+    static COUNTER: AtomicU8 = AtomicU8::new(0);
+    for b in buf {
+        *b = COUNTER.fetch_add(1, Ordering::Relaxed);
+    }
+    Ok(())
+}
+
+register_custom_getrandom!(super_insecure_rng);
+
+#[test]
+fn custom_rng_output() {
+    let mut buf = [0u8; 4];
+    assert_eq!(getrandom(&mut buf), Ok(()));
+    assert_eq!(buf, [0, 1, 2, 3]);
+    assert_eq!(getrandom(&mut buf), Ok(()));
+    assert_eq!(buf, [4, 5, 6, 7]);
+}
+
+#[test]
+fn rng_err_output() {
+    assert_eq!(getrandom(&mut [0; 7]), Err(len7_err()));
+}
diff --git a/tests/normal.rs b/tests/normal.rs
new file mode 100644
index 0000000..5fff13b
--- /dev/null
+++ b/tests/normal.rs
@@ -0,0 +1,11 @@
+// Don't test on custom wasm32-unknown-unknown
+#![cfg(not(all(
+    target_arch = "wasm32",
+    target_os = "unknown",
+    feature = "custom",
+    not(feature = "js")
+)))]
+
+// Use the normal getrandom implementation on this architecture.
+use getrandom::getrandom as getrandom_impl;
+mod common;
diff --git a/tests/rdrand.rs b/tests/rdrand.rs
new file mode 100644
index 0000000..4ff85c4
--- /dev/null
+++ b/tests/rdrand.rs
@@ -0,0 +1,15 @@
+// We only test the RDRAND-based RNG source on supported architectures.
+#![cfg(any(target_arch = "x86_64", target_arch = "x86"))]
+
+// rdrand.rs expects to be part of the getrandom main crate, so we need these
+// additional imports to get rdrand.rs to compile.
+use getrandom::Error;
+#[macro_use]
+extern crate cfg_if;
+#[path = "../src/rdrand.rs"]
+mod rdrand;
+#[path = "../src/util.rs"]
+mod util;
+
+use rdrand::getrandom_inner as getrandom_impl;
+mod common;