Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,10 +441,10 @@ module.exports = class Hyperdrive extends ReadyResource {

for await (const entry of this.diff(length, folder, opts)) {
if (!entry.left) continue
const b = entry.left.value.blob
if (!b) continue
const blob = entry.left.value.blob
if (!blob) continue
const blobs = await this.getBlobs()
dls.push(blobs.core.download({ start: b.blockOffset, length: b.blockLength }))
dls.push(await Download.downloadEntry(blobs, blob))
}

return new Download(this, null, { downloads: dls })
Expand Down Expand Up @@ -484,16 +484,16 @@ module.exports = class Hyperdrive extends ReadyResource {
const blobs = await this.getBlobs()
const entry = !path || path.endsWith('/') ? null : await this.entry(path)
if (entry) {
const b = entry.value.blob
if (!b) return false
return await blobs.core.has(b.blockOffset, b.blockOffset + b.blockLength)
const blob = entry.value.blob
if (!blob) return false
return this._hasEntry(blobs, blob)
}
let isDir = false
for await (const entry of this.list(path)) {
isDir = true
const b = entry.value.blob
if (!b) continue
const has = await blobs.core.has(b.blockOffset, b.blockOffset + b.blockLength)
const blob = entry.value.blob
if (!blob) continue
const has = await this._hasEntry(blobs, blob)
if (!has) return false
}
return isDir
Expand Down Expand Up @@ -659,6 +659,22 @@ module.exports = class Hyperdrive extends ReadyResource {
}
}

async _hasEntry(blobs, blob) {
if (blob.blockMap) {
const hasMap = await blobs.core.has(blob.blockOffset, blob.blockOffset + blob.blockLength)
if (!hasMap) return false

const map = await blobs.getBlockMap(blob)
for (const block of map.blocks) {
const has = await blobs.core.has(block.index)
if (!has) return false
}
return true
} else {
return blobs.core.has(blob.blockOffset, blob.blockOffset + blob.blockLength)
}
}

static normalizePath(name) {
return std(name, false)
}
Expand Down
76 changes: 64 additions & 12 deletions lib/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,31 @@ module.exports = class Download extends ReadyResource {
this.folder = folder
this.options = options || {}
this.downloads = this.options.downloads || []
this.destroyed = false
this._cancelled = new Promise((resolve) => {
this._cancel = resolve
})
this.ready().catch(noop)
}

async _open() {
const drive = this.drive
const blobs = await drive.getBlobs()

const entry =
!this.folder || this.folder.endsWith('/')
? null
: await drive.entry(this.folder, this.options)

if (entry) {
const b = entry.value.blob
if (!b) return
const blobs = await drive.getBlobs()
const download = blobs.core.download({
start: b.blockOffset,
length: b.blockLength
})
const blob = entry.value.blob
if (!blob) return

const mapDownload = await this._cancellableDownload(
this.constructor.downloadEntryMap(blobs, blob)
)
if (!mapDownload) return

const download = await this.constructor.downloadEntry(blobs, blob)
this.downloads.push(download)
return
}
Expand All @@ -38,16 +44,61 @@ module.exports = class Download extends ReadyResource {
// ignore
}

const maps = []
const entries = []
for await (const entry of drive.list(this.folder, this.options)) {
const b = entry.value.blob
if (!b) continue
const blob = entry.value.blob
if (!blob) continue
if (blob.blockMap && blob.blockLength) {
maps.push(blobs.core.download({ start: blob.blockOffset, length: blob.blockLength }))
}
entries.push(entry) // cache entries
}

const results = await Promise.all(maps.map((m) => this._cancellableDownload(m)))
if (results.includes(false)) return

const blobs = await drive.getBlobs()
this.downloads.push(blobs.core.download({ start: b.blockOffset, length: b.blockLength }))
for (const entry of entries) {
const blob = entry.value.blob
const download = await this.constructor.downloadEntry(blobs, blob)
this.downloads.push(download)
}
}
}

static downloadEntryMap(blobs, blob) {
return blobs.core.download({ start: blob.blockOffset, length: blob.blockLength })
}

static async downloadEntry(blobs, blob) {
if (blob.blockMap) {
const map = await blobs.getBlockMap(blob)
const blocks = []
for (const block of map.blocks) blocks.push(block.index)

const download = blobs.core.download({ blocks })
await download.ready()
return download
} else {
const download = blobs.core.download({
start: blob.blockOffset,
length: blob.blockLength
})
await download.ready()
return download
}
}

async _cancellableDownload(download) {
return Promise.race([
download.done().then(() => true),
this._cancelled.then(() => {
download.destroy()
return false
})
])
}

_close() {
for (const d of this.downloads) {
d.destroy()
Expand All @@ -56,6 +107,7 @@ module.exports = class Download extends ReadyResource {

destroy() {
this.destroyed = true
this._cancel()
this._safeBackgroundDestroy()
}

Expand Down
Loading
Loading