Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream: Added: - python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc. Includes 204 .py files with full test coverage for all 27 algorithms - ci/ (163 files) — Build/test infrastructure build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml Directly maps to our [INFRA-CI] and [INFRA-BUILD] items - .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md - docs/ (491 files) — Official CCCL documentation CI references, CMake guides, Python compute docs, libcudacxx PTX docs - test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar) - Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml - CLAUDE.md symlink → AGENTS.md (NVIDIA's standard) cccl_upstream now mirrors full NVIDIA/cccl structure: Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks) After: 53M (+python +ci +docs +.agent +test +configs) This completes the CCCL base needed for: - [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds - [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations - [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh - Agent workflow: .agent/skills/ for consistent style and test patterns
266 lines
7.6 KiB
JavaScript
266 lines
7.6 KiB
JavaScript
"use strict";
|
|
|
|
(function () {
|
|
const maxBreadcrumbResults = 10;
|
|
|
|
const decodeEntities = (value) =>
|
|
String(value || "")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/&/g, "&")
|
|
.replace(/"/g, '"')
|
|
.replace(/'|'/g, "'")
|
|
.replace(/ /g, " ")
|
|
.replace(/…/g, "...");
|
|
|
|
const getDocLinkSuffix = () =>
|
|
(typeof DOCUMENTATION_OPTIONS !== "undefined" &&
|
|
DOCUMENTATION_OPTIONS.LINK_SUFFIX) ||
|
|
".html";
|
|
|
|
const pageInfoCache = new Map();
|
|
|
|
const getDocTitle = (docName) => {
|
|
if (
|
|
typeof Search === "undefined" ||
|
|
!Search._index ||
|
|
!Array.isArray(Search._index.docnames) ||
|
|
!Array.isArray(Search._index.titles)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const index = Search._index.docnames.indexOf(docName);
|
|
return index >= 0 ? Search._index.titles[index] : null;
|
|
};
|
|
|
|
const getResultDocNameFromHref = (href) => {
|
|
if (!href) {
|
|
return null;
|
|
}
|
|
|
|
const withoutAnchor = String(href).split("#", 1)[0];
|
|
const linkSuffix = getDocLinkSuffix();
|
|
const suffixIndex = withoutAnchor.lastIndexOf(linkSuffix);
|
|
if (suffixIndex < 0) {
|
|
return null;
|
|
}
|
|
|
|
let docName = withoutAnchor.slice(0, suffixIndex);
|
|
if (docName.startsWith("./")) {
|
|
docName = docName.slice(2);
|
|
}
|
|
|
|
const contentRoot =
|
|
document?.documentElement?.dataset?.content_root || "";
|
|
if (contentRoot && docName.startsWith(contentRoot)) {
|
|
docName = docName.slice(contentRoot.length);
|
|
}
|
|
|
|
return docName.replace(/^\/+/, "");
|
|
};
|
|
|
|
const getPageInfo = async (docName) => {
|
|
if (pageInfoCache.has(docName)) {
|
|
return pageInfoCache.get(docName);
|
|
}
|
|
|
|
const infoPromise = (async () => {
|
|
const pageUrl = `${docName}${getDocLinkSuffix()}`;
|
|
const response = await fetch(pageUrl);
|
|
const html = await response.text();
|
|
const parsed = new DOMParser().parseFromString(html, "text/html");
|
|
|
|
const pageHeading = parsed.querySelector("h1");
|
|
const breadcrumbLinks = Array.from(
|
|
parsed.querySelectorAll(".breadcrumb-item a.nav-link"),
|
|
);
|
|
const breadcrumbs = breadcrumbLinks
|
|
.map((breadcrumbLink) => {
|
|
const rawHref = breadcrumbLink.getAttribute("href");
|
|
if (!rawHref) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
href: new URL(rawHref, response.url).href,
|
|
title: breadcrumbLink.textContent?.trim() || null,
|
|
};
|
|
})
|
|
.filter((breadcrumb) => breadcrumb && breadcrumb.title);
|
|
|
|
return {
|
|
pageTitle:
|
|
pageHeading?.textContent?.replace(/#\s*$/, "").trim() ||
|
|
getDocTitle(docName) ||
|
|
null,
|
|
breadcrumbs,
|
|
};
|
|
})().catch(() => null);
|
|
|
|
pageInfoCache.set(docName, infoPromise);
|
|
return infoPromise;
|
|
};
|
|
|
|
const addBreadcrumbTrail = async (listItem) => {
|
|
if (
|
|
!listItem ||
|
|
listItem.dataset.ccclBreadcrumbsAttached === "true" ||
|
|
listItem.dataset.ccclBreadcrumbsPending === "true"
|
|
) {
|
|
return;
|
|
}
|
|
listItem.dataset.ccclBreadcrumbsPending = "true";
|
|
|
|
const primaryLink = listItem.querySelector("a");
|
|
if (!primaryLink) {
|
|
delete listItem.dataset.ccclBreadcrumbsPending;
|
|
return;
|
|
}
|
|
|
|
const primaryTitle = primaryLink.textContent?.trim() || "";
|
|
const href = primaryLink.getAttribute("href");
|
|
const docName = getResultDocNameFromHref(href);
|
|
if (!docName) {
|
|
delete listItem.dataset.ccclBreadcrumbsPending;
|
|
return;
|
|
}
|
|
|
|
const pageInfo = await getPageInfo(docName);
|
|
if (!pageInfo) {
|
|
delete listItem.dataset.ccclBreadcrumbsPending;
|
|
return;
|
|
}
|
|
|
|
const pageTitle = pageInfo.pageTitle || getDocTitle(docName);
|
|
const breadcrumbs = [...(pageInfo.breadcrumbs || [])];
|
|
if (pageTitle && primaryTitle && pageTitle !== primaryTitle) {
|
|
breadcrumbs.push({
|
|
href: `${docName}${getDocLinkSuffix()}`,
|
|
title: pageTitle,
|
|
});
|
|
}
|
|
|
|
if (breadcrumbs.length === 0) {
|
|
delete listItem.dataset.ccclBreadcrumbsPending;
|
|
return;
|
|
}
|
|
|
|
const breadcrumbContainer = document.createElement("div");
|
|
breadcrumbContainer.className = "cccl-search-breadcrumbs";
|
|
|
|
breadcrumbs.forEach((breadcrumb) => {
|
|
const breadcrumbItem = document.createElement("span");
|
|
breadcrumbItem.className = "breadcrumb-item";
|
|
const breadcrumbLink = document.createElement("a");
|
|
breadcrumbLink.href = breadcrumb.href;
|
|
breadcrumbLink.textContent = breadcrumb.title;
|
|
breadcrumbItem.appendChild(breadcrumbLink);
|
|
breadcrumbContainer.appendChild(breadcrumbItem);
|
|
});
|
|
|
|
listItem.insertBefore(breadcrumbContainer, primaryLink.nextSibling);
|
|
listItem.dataset.ccclBreadcrumbsAttached = "true";
|
|
delete listItem.dataset.ccclBreadcrumbsPending;
|
|
};
|
|
|
|
const installResultDecorator = () => {
|
|
if (
|
|
typeof Search === "undefined" ||
|
|
Search.__ccclResultDecoratorInstalled ||
|
|
typeof MutationObserver === "undefined"
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const originalPerformSearch = Search.performSearch;
|
|
Search.performSearch = (...args) => {
|
|
const result = originalPerformSearch(...args);
|
|
const output = Search.output;
|
|
if (!output) {
|
|
return result;
|
|
}
|
|
|
|
const decorateTopResults = () => {
|
|
Array.from(output.querySelectorAll("li"))
|
|
.slice(0, maxBreadcrumbResults)
|
|
.forEach(addBreadcrumbTrail);
|
|
};
|
|
|
|
if (Search.__ccclResultsObserver) {
|
|
Search.__ccclResultsObserver.disconnect();
|
|
}
|
|
|
|
decorateTopResults();
|
|
|
|
const observer = new MutationObserver((mutations) => {
|
|
decorateTopResults();
|
|
});
|
|
|
|
observer.observe(output, { childList: true, subtree: true });
|
|
Search.__ccclResultsObserver = observer;
|
|
Search.__ccclResultDecoratorInstalled = true;
|
|
return result;
|
|
};
|
|
};
|
|
|
|
const installPostprocess = () => {
|
|
if (typeof Search === "undefined" || Search.__ccclDedupInstalled) {
|
|
return;
|
|
}
|
|
|
|
const originalPerformSearch = Search._performSearch;
|
|
Search._performSearch = (...args) => {
|
|
const results = originalPerformSearch(...args);
|
|
|
|
// Sphinx keeps results in low->high score order and displays via pop().
|
|
// Walk from the end so we see the best-ranked result first, but prefer
|
|
// canonical page links without anchors when collapsing duplicates.
|
|
const chosen = new Map();
|
|
for (let i = results.length - 1; i >= 0; --i) {
|
|
const result = results[i];
|
|
const title = String(result[1] || "").toLowerCase();
|
|
const filename = String(result[5] || "");
|
|
const key = `${filename}\0${title}`;
|
|
const anchor = String(result[2] || "");
|
|
const existing = chosen.get(key);
|
|
if (!existing) {
|
|
chosen.set(key, result);
|
|
continue;
|
|
}
|
|
|
|
const existingAnchor = String(existing[2] || "");
|
|
const prefersCurrent = existingAnchor && !anchor;
|
|
if (prefersCurrent) {
|
|
chosen.set(key, result);
|
|
}
|
|
}
|
|
|
|
const deduped = [];
|
|
const emitted = new Set();
|
|
for (let i = 0; i < results.length; ++i) {
|
|
const result = results[i];
|
|
const title = String(result[1] || "").toLowerCase();
|
|
const filename = String(result[5] || "");
|
|
const key = `${filename}\0${title}`;
|
|
if (emitted.has(key)) {
|
|
continue;
|
|
}
|
|
const winner = chosen.get(key);
|
|
if (winner) {
|
|
winner[1] = decodeEntities(winner[1]);
|
|
winner[3] = decodeEntities(winner[3]);
|
|
deduped.push(winner);
|
|
emitted.add(key);
|
|
}
|
|
}
|
|
return deduped;
|
|
};
|
|
|
|
Search.__ccclDedupInstalled = true;
|
|
};
|
|
|
|
installPostprocess();
|
|
installResultDecorator();
|
|
})();
|