From 063ccaad42b209140afa1658c7d60f7604a5ca63 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Sep 2025 11:32:51 -0700 Subject: [PATCH] rustdoc-search: reduce async machinery in value lookups This commit is a mirrored change from stringdex that makes `at()` not always return a promise, which is fine because we can still `await` it. (cherry picked from commit 43d45ef2da29aa438c32b5c872bc7a367ef2a2ab) --- src/librustdoc/html/static/js/search.js | 8 +- src/librustdoc/html/static/js/stringdex.d.ts | 2 +- src/librustdoc/html/static/js/stringdex.js | 99 +++++++++++--------- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 3b84ae2bed06..339b101d4711 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1758,12 +1758,8 @@ class DocSearch { const l = crateNames.length; const names = []; for (let i = 0; i < l; ++i) { - names.push(crateNames.at(i).then(name => { - if (name === undefined) { - return ""; - } - return this.utf8decoder.decode(name); - })); + const name = await crateNames.at(i); + names.push(name === undefined ? "" : this.utf8decoder.decode(name)); } return Promise.all(names); } diff --git a/src/librustdoc/html/static/js/stringdex.d.ts b/src/librustdoc/html/static/js/stringdex.d.ts index 2eb1fdf95d84..71c6bfdf4814 100644 --- a/src/librustdoc/html/static/js/stringdex.d.ts +++ b/src/librustdoc/html/static/js/stringdex.d.ts @@ -29,7 +29,7 @@ declare namespace stringdex { */ interface DataColumn { isEmpty(id: number): boolean; - at(id: number): Promise; + at(id: number): Promise|Uint8Array|undefined; search(name: Uint8Array|string): Promise; searchLev(name: Uint8Array|string): AsyncGenerator; length: number, diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index b7f605a10351..4c4659628778 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -2261,7 +2261,7 @@ function loadDatabase(hooks) { this.hashes = hashes; this.emptyset = emptyset; this.name = name; - /** @type {{"hash": Uint8Array, "data": Promise?, "end": number}[]} */ + /** @type {{"hash": Uint8Array, "data": Uint8Array[]?, "end": number}[]} */ this.buckets = []; this.bucket_keys = []; const l = counts.length; @@ -2295,65 +2295,76 @@ function loadDatabase(hooks) { /** * Look up a cell by row ID. * @param {number} id - * @returns {Promise} + * @returns {Promise|Uint8Array|undefined} */ - async at(id) { + at(id) { if (this.emptyset.contains(id)) { - return Promise.resolve(EMPTY_UINT8); + return EMPTY_UINT8; } else { let idx = -1; while (this.bucket_keys[idx + 1] <= id) { idx += 1; } if (idx === -1 || idx >= this.bucket_keys.length) { - return Promise.resolve(undefined); + return undefined; } else { const start = this.bucket_keys[idx]; - const {hash, end} = this.buckets[idx]; + const bucket = this.buckets[idx]; let data = this.buckets[idx].data; if (data === null) { - const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( - this.name, - hash, - ); - // After the `await` resolves, another task might fill - // in the data. If so, we should use that. - data = this.buckets[idx].data; - if (data !== null) { - return (await data)[id - start]; - } - const dataSansEmptyset = [...dataSansEmptysetOrig]; - /** @type {(Uint8Array[])|null} */ - let dataWithEmptyset = null; - let pos = start; - let insertCount = 0; - while (pos < end) { - if (this.emptyset.contains(pos)) { - if (dataWithEmptyset === null) { - dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); - } else if (insertCount !== 0) { - dataWithEmptyset.push( - ...dataSansEmptyset.splice(0, insertCount), - ); - } - insertCount = 0; - dataWithEmptyset.push(EMPTY_UINT8); - } else { - insertCount += 1; - } - pos += 1; - } - data = Promise.resolve( - dataWithEmptyset === null ? - dataSansEmptyset : - dataWithEmptyset.concat(dataSansEmptyset), - ); - this.buckets[idx].data = data; + return this.atAsyncFetch(id, start, bucket); + } else { + return data[id - start]; } - return (await data)[id - start]; } } } + /** + * Look up a cell by row ID. + * @param {number} id + * @param {number} start + * @param {{hash: Uint8Array, data: Uint8Array[] | null, end: number}} bucket + * @returns {Promise} + */ + async atAsyncFetch(id, start, bucket) { + const {hash, end} = bucket; + const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( + this.name, + hash, + ); + // After the `await` resolves, another task might fill + // in the data. If so, we should use that. + let data = bucket.data; + if (data !== null) { + return data[id - start]; + } + const dataSansEmptyset = [...dataSansEmptysetOrig]; + /** @type {(Uint8Array[])|null} */ + let dataWithEmptyset = null; + let pos = start; + let insertCount = 0; + while (pos < end) { + if (this.emptyset.contains(pos)) { + if (dataWithEmptyset === null) { + dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); + } else if (insertCount !== 0) { + dataWithEmptyset.push( + ...dataSansEmptyset.splice(0, insertCount), + ); + } + insertCount = 0; + dataWithEmptyset.push(EMPTY_UINT8); + } else { + insertCount += 1; + } + pos += 1; + } + data = dataWithEmptyset === null ? + dataSansEmptyset : + dataWithEmptyset.concat(dataSansEmptyset); + bucket.data = data; + return data[id - start]; + } /** * Search by exact substring * @param {Uint8Array|string} name