@evshiron/exifr
Version:
📷 The fastest and most versatile JavaScript EXIF reading library.
110 lines (91 loc) • 3.38 kB
JavaScript
import * as platform from './platform.mjs'
import {BufferView} from './BufferView.mjs'
//import {throwError} from './helpers.mjs'
import {Buffer} from './platform.mjs'
export class DynamicBufferView extends BufferView {
ranges = new Ranges
constructor(...args) {
super(...args)
if (this.byteLength !== 0)
this.ranges.add(0, this.byteLength)
}
_tryExtend(offset, length, abChunk) {
if (offset === 0 && this.byteLength === 0 && abChunk) {
// we can receive ArrayBuffer or Buffer
let dataView = new DataView(abChunk.buffer || abChunk, abChunk.byteOffset, abChunk.byteLength)
this._swapDataView(dataView)
} else {
let end = offset + length
if (end > this.byteLength) {
let {dataView} = this._extend(end)
this._swapDataView(dataView)
}
}
}
_extend(newLength) {
let uintView
if (platform.hasBuffer)
uintView = Buffer.allocUnsafe(newLength)
else
uintView = new Uint8Array(newLength)
let dataView = new DataView(uintView.buffer, uintView.byteOffset, uintView.byteLength)
uintView.set(new Uint8Array(this.buffer, this.byteOffset, this.byteLength), 0)
return {uintView, dataView}
}
subarray(offset, length, canExtend = false) {
length = length || this._lengthToEnd(offset)
if (canExtend) this._tryExtend(offset, length)
this.ranges.add(offset, length)
return super.subarray(offset, length)
}
// TODO: write tests for extending .set()
set(arg, offset, canExtend = false) {
if (canExtend) this._tryExtend(offset, arg.byteLength, arg)
let chunk = super.set(arg, offset)
this.ranges.add(offset, chunk.byteLength)
return chunk
}
async ensureChunk(offset, length) {
if (!this.chunked) return
if (this.ranges.available(offset, length)) return
await this.readChunk(offset, length)
}
// Returns bool indicating wheter buffer contains useful data (read from file) at given offset/length
// or if its so far only allocated & unitialized memory ready to be written into.
available(offset, length) {
return this.ranges.available(offset, length)
}
}
export class Ranges {
list = []
get length() {
return this.list.length
}
// TODO: add padding - because it's better to do just one disk read instead of two
// even though there are a few unused bytes between the two needed ranges
add(offset, length, padding = 0) {
let end = offset + length
let within = this.list.filter(range => isBetween(offset, range.offset, end) || isBetween(offset, range.end, end))
if (within.length > 0) {
offset = Math.min(offset, ...within.map(range => range.offset))
end = Math.max(end, ...within.map(range => range.end))
length = end - offset
let range = within.shift()
range.offset = offset
range.length = length
range.end = end
this.list = this.list.filter(range => !within.includes(range))
} else {
this.list.push({offset, length, end})
}
}
// Returns bool indicating wheter buffer contains useful data (read from file) at given offset/length
// or if its so far only allocated & unitialized memory ready to be written into.
available(offset, length) {
let end = offset + length
return this.list.some(range => range.offset <= offset && end <= range.end)
}
}
function isBetween(min, val, max) {
return min <= val && val <= max
}