Skip to content

Commit 69fd146

Browse files
author
Ray Schamp
committed
feat(loading): Cache all loaded assets with BuiltinHelper
- Add `cache` method to BuiltinHelper, to store retrieved assets - Cache assets retrieved with other helpers with BuiltinHelper - Flatten the BuiltinHelper cache structure, so only the md5 is needed to retrieve an asset. A collision between two types would be interesting but unlikely - Add a synchronous `get` method to ScratchStorage that passes through to the BuiltinHelper for cached assets - Fix WebHelper, so it sets the dataFormat on the Asset it returns
1 parent 8d0d3ea commit 69fd146

File tree

4 files changed

+56
-37
lines changed

4 files changed

+56
-37
lines changed

src/Asset.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ class Asset {
1414

1515
/** @type {string} */
1616
this.assetId = assetId;
17+
18+
this.setData(data, dataFormat);
19+
20+
/** @type {Asset[]} */
21+
this.dependencies = [];
22+
}
23+
24+
setData (data, dataFormat) {
1725
if (data && !dataFormat) {
1826
throw new Error('Data provided without specifying its format');
1927
}
@@ -23,9 +31,6 @@ class Asset {
2331

2432
/** @type {Buffer} */
2533
this.data = data;
26-
27-
/** @type {Asset[]} */
28-
this.dependencies = [];
2934
}
3035

3136
/**
@@ -41,12 +46,8 @@ class Asset {
4146
* @returns {string} - A data URI representing the asset's data.
4247
*/
4348
encodeDataURI (contentType) {
44-
return [
45-
'data:',
46-
contentType || this.assetType.contentType,
47-
';base64,',
48-
this.data.toString('base64')
49-
].join('');
49+
contentType = contentType || this.assetType.contentType;
50+
return `data:${contentType};base64,${this.data.toString('base64')}`;
5051
}
5152
}
5253

src/BuiltinHelper.js

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,9 @@ class BuiltinHelper extends Helper {
5454
*/
5555
this.assets = {};
5656

57-
const numAssets = BuiltinAssets.length;
58-
for (let assetIndex = 0; assetIndex < numAssets; ++assetIndex) {
59-
const assetRecord = BuiltinAssets[assetIndex];
60-
const typeName = assetRecord.type.name;
61-
62-
/** @type {AssetIdMap} */
63-
const typeBucket = this.assets[typeName] = this.assets[typeName] || {};
64-
65-
if (!assetRecord.id) {
66-
const hash = crypto.createHash('md5');
67-
hash.update(assetRecord.data);
68-
assetRecord.id = hash.digest('hex');
69-
}
70-
71-
typeBucket[assetRecord.id] = assetRecord;
72-
}
57+
BuiltinAssets.forEach(
58+
assetRecord => this.cache(assetRecord.type, assetRecord.format, assetRecord.data, assetRecord.id)
59+
);
7360
}
7461

7562
/**
@@ -83,23 +70,42 @@ class BuiltinHelper extends Helper {
8370
}
8471
}
8572

73+
get (assetId) {
74+
let asset = null;
75+
if (this.assets.hasOwnProperty(assetId)) {
76+
/** @type{BuiltinAssetRecord} */
77+
const assetRecord = this.assets[assetId];
78+
asset = new Asset(assetRecord.type, assetRecord.id, assetRecord.format, assetRecord.data);
79+
}
80+
return asset;
81+
}
82+
83+
cache (assetType, dataFormat, data, id) {
84+
if (!dataFormat) throw new Error('Data cached without specifying its format');
85+
if (id) {
86+
if (this.assets.hasOwnProperty(id)) return id;
87+
} else {
88+
const hash = crypto.createHash('md5');
89+
hash.update(data);
90+
id = hash.digest('hex');
91+
}
92+
this.assets[id] = {
93+
type: assetType,
94+
format: dataFormat,
95+
id: id,
96+
data: data
97+
};
98+
return id;
99+
}
100+
86101
/**
87102
* Fetch an asset but don't process dependencies.
88103
* @param {AssetType} assetType - The type of asset to fetch.
89104
* @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
90105
* @return {Promise.<Asset>} A promise for the contents of the asset.
91106
*/
92107
load (assetType, assetId) {
93-
let asset = null;
94-
if (this.assets.hasOwnProperty(assetType.name)) {
95-
const typeBucket = this.assets[assetType.name];
96-
if (typeBucket.hasOwnProperty(assetId)) {
97-
/** @type{BuiltinAssetRecord} */
98-
const assetRecord = typeBucket[assetId];
99-
asset = new Asset(assetRecord.type, assetRecord.id, assetRecord.format, assetRecord.data);
100-
}
101-
}
102-
return Promise.resolve(asset);
108+
return Promise.resolve(this.get(assetId));
103109
}
104110
}
105111

src/ScratchStorage.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ class ScratchStorage {
4949
return AssetType;
5050
}
5151

52+
get (assetId) {
53+
return this.builtinHelper.get(assetId);
54+
}
55+
5256
/**
5357
* Register a web-based source for assets. Sources will be checked in order of registration.
5458
* @param {Array.<AssetType>} types - The types of asset provided by this source.
@@ -100,13 +104,21 @@ class ScratchStorage {
100104
return new Promise((fulfill, reject) => {
101105
const tryNextHelper = () => {
102106
if (helperIndex < helpers.length) {
103-
helpers[helperIndex++].load(assetType, assetId)
107+
const helper = helpers[helperIndex++];
108+
helper.load(assetType, assetId)
104109
.then(
105110
asset => {
106111
if (asset === null) {
107112
tryNextHelper();
108113
} else {
109114
// TODO? this.localHelper.cache(assetType, assetId, asset);
115+
if (helper !== this.builtinHelper) {
116+
asset.assetId = this.builtinHelper.cache(
117+
assetType,
118+
asset.dataFormat,
119+
asset.data
120+
);
121+
}
110122
// Note that other attempts may have caused errors, effectively suppressed here.
111123
fulfill(asset);
112124
}

src/WebHelper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class WebHelper extends Helper {
7777
}
7878
tryNextSource();
7979
} else {
80-
asset.data = response.body;
80+
asset.setData(response.body, assetType.runtimeFormat);
8181
fulfill(asset);
8282
}
8383
},

0 commit comments

Comments
 (0)