diff --git a/demo/asyncBuffer.ts b/demo/asyncBuffer.ts new file mode 100644 index 0000000..12ddde3 --- /dev/null +++ b/demo/asyncBuffer.ts @@ -0,0 +1,50 @@ +import type { AsyncBuffer, Awaitable } from "../src/types.js" + +/** + * Returns a caches layer on top of an AsyncBuffer. + * This is useful for caching slices of a file that are read multiple times, + * possibly over a network. + * + * TODO: require data to be loaded with preload(), reads outside of preload rejected. + * + * @param {AsyncBuffer} file file-like object to cache + * @returns {AsyncBuffer} cached file-like object + */ +export function cachedAsyncBuffer(file: AsyncBuffer): AsyncBuffer { + // indexed by 'start,end' + const cache = new Map>() + return { + byteLength: file.byteLength, + slice(start: number, end?: number): Awaitable { + // ensure both "100-200" and "100-" are both cached the same + const key = cacheKey(start, end, file.byteLength) + const cached = cache.get(key) + if (cached) return cached + // cache miss, read from file + const promise = file.slice(start, end) + cache.set(key, promise) + return promise + }, + } +} + + +/** + * Returns canonical cache key for a byte range. + * Cache key is a string of the form 'start,end'. + * Attempts to normalize int-range and suffix-range requests to the same key. + */ +function cacheKey(start: number, end: number | undefined, fileSize: number | undefined): string { + if (start < 0) { + if (end !== undefined) throw new Error(`invalid suffix range [${start}, ${end}]`) + if (fileSize === undefined) return `${start},` + return `${fileSize + start},${fileSize}` + } else if (end !== undefined) { + if (start > end) throw new Error(`invalid empty range [${start}, ${end}]`) + return `${start},${end}` + } else if (fileSize === undefined) { + return `${start},` + } else { + return `${start},${fileSize}` + } +} diff --git a/demo/bundle.min.js b/demo/bundle.min.js index a2b95fc..1b87e0b 100644 --- a/demo/bundle.min.js +++ b/demo/bundle.min.js @@ -1,4 +1,4 @@ -!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e="undefined"!=typeof document?document.currentScript:null;function t(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var n,r,l={exports:{}},a={};function o(){if(n)return a;n=1;var e=Symbol.for("react.element"),t=Symbol.for("react.portal"),r=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),o=Symbol.for("react.profiler"),i=Symbol.for("react.provider"),u=Symbol.for("react.context"),s=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),f=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),p=Symbol.iterator;var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h=Object.assign,g={};function v(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||m}function y(){}function b(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||m}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},y.prototype=v.prototype;var w=b.prototype=new y;w.constructor=b,h(w,v.prototype),w.isPureReactComponent=!0;var _=Array.isArray,k=Object.prototype.hasOwnProperty,S={current:null},E={key:!0,ref:!0,__self:!0,__source:!0};function x(t,n,r){var l,a={},o=null,i=null;if(null!=n)for(l in void 0!==n.ref&&(i=n.ref),void 0!==n.key&&(o=""+n.key),n)k.call(n,l)&&!E.hasOwnProperty(l)&&(a[l]=n[l]);var u=arguments.length-2;if(1===u)a.children=r;else if(1>>1,a=e[r];if(!(0>>1;rl(u,n))sl(c,u)?(e[r]=c,e[s]=n,r=s):(e[r]=u,e[i]=n,r=i);else{if(!(sl(c,n)))break e;e[r]=c,e[s]=n,r=s}}}return t}function l(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if("object"==typeof performance&&"function"==typeof performance.now){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,i=o.now();e.unstable_now=function(){return o.now()-i}}var u=[],s=[],c=1,f=null,d=3,p=!1,m=!1,h=!1,g="function"==typeof setTimeout?setTimeout:null,v="function"==typeof clearTimeout?clearTimeout:null,y="undefined"!=typeof setImmediate?setImmediate:null;function b(e){for(var l=n(s);null!==l;){if(null===l.callback)r(s);else{if(!(l.startTime<=e))break;r(s),l.sortIndex=l.expirationTime,t(u,l)}l=n(s)}}function w(e){if(h=!1,b(e),!m)if(null!==n(u))m=!0,z(_);else{var t=n(s);null!==t&&I(w,t.startTime-e)}}function _(t,l){m=!1,h&&(h=!1,v(x),x=-1),p=!0;var a=d;try{for(b(l),f=n(u);null!==f&&(!(f.expirationTime>l)||t&&!L());){var o=f.callback;if("function"==typeof o){f.callback=null,d=f.priorityLevel;var i=o(f.expirationTime<=l);l=e.unstable_now(),"function"==typeof i?f.callback=i:f===n(u)&&r(u),b(l)}else r(u);f=n(u)}if(null!==f)var c=!0;else{var g=n(s);null!==g&&I(w,g.startTime-l),c=!1}return c}finally{f=null,d=a,p=!1}}"undefined"!=typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var k,S=!1,E=null,x=-1,C=5,N=-1;function L(){return!(e.unstable_now()-Ne||125o?(r.sortIndex=a,t(s,r),null===n(u)&&r===n(s)&&(h?(v(x),x=-1):h=!0,I(w,a-o))):(r.sortIndex=i,t(u,r),m||p||(m=!0,z(_))),r},e.unstable_shouldYield=L,e.unstable_wrapCallback=function(e){var t=d;return function(){var n=d;d=t;try{return e.apply(this,arguments)}finally{d=n}}}}(b)),b)),y.exports} +!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e="undefined"!=typeof document?document.currentScript:null;function t(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var n,r,l={exports:{}},a={};function o(){if(n)return a;n=1;var e=Symbol.for("react.element"),t=Symbol.for("react.portal"),r=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),o=Symbol.for("react.profiler"),i=Symbol.for("react.provider"),u=Symbol.for("react.context"),s=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),f=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),p=Symbol.iterator;var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h=Object.assign,g={};function v(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||m}function y(){}function b(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||m}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},y.prototype=v.prototype;var w=b.prototype=new y;w.constructor=b,h(w,v.prototype),w.isPureReactComponent=!0;var _=Array.isArray,k=Object.prototype.hasOwnProperty,S={current:null},E={key:!0,ref:!0,__self:!0,__source:!0};function x(t,n,r){var l,a={},o=null,i=null;if(null!=n)for(l in void 0!==n.ref&&(i=n.ref),void 0!==n.key&&(o=""+n.key),n)k.call(n,l)&&!E.hasOwnProperty(l)&&(a[l]=n[l]);var u=arguments.length-2;if(1===u)a.children=r;else if(1>>1,a=e[r];if(!(0>>1;rl(u,n))sl(c,u)?(e[r]=c,e[s]=n,r=s):(e[r]=u,e[i]=n,r=i);else{if(!(sl(c,n)))break e;e[r]=c,e[s]=n,r=s}}}return t}function l(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if("object"==typeof performance&&"function"==typeof performance.now){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,i=o.now();e.unstable_now=function(){return o.now()-i}}var u=[],s=[],c=1,f=null,d=3,p=!1,m=!1,h=!1,g="function"==typeof setTimeout?setTimeout:null,v="function"==typeof clearTimeout?clearTimeout:null,y="undefined"!=typeof setImmediate?setImmediate:null;function b(e){for(var l=n(s);null!==l;){if(null===l.callback)r(s);else{if(!(l.startTime<=e))break;r(s),l.sortIndex=l.expirationTime,t(u,l)}l=n(s)}}function w(e){if(h=!1,b(e),!m)if(null!==n(u))m=!0,z(_);else{var t=n(s);null!==t&&I(w,t.startTime-e)}}function _(t,l){m=!1,h&&(h=!1,v(x),x=-1),p=!0;var a=d;try{for(b(l),f=n(u);null!==f&&(!(f.expirationTime>l)||t&&!L());){var o=f.callback;if("function"==typeof o){f.callback=null,d=f.priorityLevel;var i=o(f.expirationTime<=l);l=e.unstable_now(),"function"==typeof i?f.callback=i:f===n(u)&&r(u),b(l)}else r(u);f=n(u)}if(null!==f)var c=!0;else{var g=n(s);null!==g&&I(w,g.startTime-l),c=!1}return c}finally{f=null,d=a,p=!1}}"undefined"!=typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var k,S=!1,E=null,x=-1,C=5,N=-1;function L(){return!(e.unstable_now()-Ne||125o?(r.sortIndex=a,t(s,r),null===n(u)&&r===n(s)&&(h?(v(x),x=-1):h=!0,I(w,a-o))):(r.sortIndex=i,t(u,r),m||p||(m=!0,z(_))),r},e.unstable_shouldYield=L,e.unstable_wrapCallback=function(e){var t=d;return function(){var n=d;d=t;try{return e.apply(this,arguments)}finally{d=n}}}}(b)),b)),y.exports} /** * @license React * react-dom.production.min.js @@ -7,5 +7,5 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */function _(){if(c)return v;c=1;var e=i(),t=w();function n(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n