Files
archy/apps/web5-dwn/node_modules/ipfs-unixfs-exporter/src/utils/find-cid-in-shard.ts
T
Dorian 0d073fa89e Add comprehensive installation and setup documentation
- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
2026-01-27 17:18:21 +00:00

127 lines
3.3 KiB
TypeScript

import { decode, type PBLink, type PBNode } from '@ipld/dag-pb'
import { murmur3128 } from '@multiformats/murmur3'
import { Bucket, type BucketPosition, createHAMT } from 'hamt-sharding'
import type { ExporterOptions, ShardTraversalContext, ReadableStorage } from '../index.js'
import type { CID } from 'multiformats/cid'
// FIXME: this is copy/pasted from ipfs-unixfs-importer/src/options.js
const hashFn = async function (buf: Uint8Array): Promise<Uint8Array> {
return (await murmur3128.encode(buf))
// Murmur3 outputs 128 bit but, accidentally, IPFS Go's
// implementation only uses the first 64, so we must do the same
// for parity..
.slice(0, 8)
// Invert buffer because that's how Go impl does it
.reverse()
}
const addLinksToHamtBucket = async (links: PBLink[], bucket: Bucket<boolean>, rootBucket: Bucket<boolean>): Promise<void> => {
await Promise.all(
links.map(async link => {
if (link.Name == null) {
// TODO(@rvagg): what do? this is technically possible
throw new Error('Unexpected Link without a Name')
}
if (link.Name.length === 2) {
const pos = parseInt(link.Name, 16)
bucket._putObjectAt(pos, new Bucket({
hash: rootBucket._options.hash,
bits: rootBucket._options.bits
}, bucket, pos))
return
}
await rootBucket.put(link.Name.substring(2), true)
})
)
}
const toPrefix = (position: number): string => {
return position
.toString(16)
.toUpperCase()
.padStart(2, '0')
.substring(0, 2)
}
const toBucketPath = (position: BucketPosition<boolean>): Array<Bucket<boolean>> => {
let bucket = position.bucket
const path = []
while (bucket._parent != null) {
path.push(bucket)
bucket = bucket._parent
}
path.push(bucket)
return path.reverse()
}
const findShardCid = async (node: PBNode, name: string, blockstore: ReadableStorage, context?: ShardTraversalContext, options?: ExporterOptions): Promise<CID | undefined> => {
if (context == null) {
const rootBucket = createHAMT<boolean>({
hashFn
})
context = {
rootBucket,
hamtDepth: 1,
lastBucket: rootBucket
}
}
await addLinksToHamtBucket(node.Links, context.lastBucket, context.rootBucket)
const position = await context.rootBucket._findNewBucketAndPos(name)
let prefix = toPrefix(position.pos)
const bucketPath = toBucketPath(position)
if (bucketPath.length > context.hamtDepth) {
context.lastBucket = bucketPath[context.hamtDepth]
prefix = toPrefix(context.lastBucket._posAtParent)
}
const link = node.Links.find(link => {
if (link.Name == null) {
return false
}
const entryPrefix = link.Name.substring(0, 2)
const entryName = link.Name.substring(2)
if (entryPrefix !== prefix) {
// not the entry or subshard we're looking for
return false
}
if (entryName !== '' && entryName !== name) {
// not the entry we're looking for
return false
}
return true
})
if (link == null) {
return
}
if (link.Name != null && link.Name.substring(2) === name) {
return link.Hash
}
context.hamtDepth++
const block = await blockstore.get(link.Hash, options)
node = decode(block)
return findShardCid(node, name, blockstore, context, options)
}
export default findShardCid