Skip to content

Commit 1019bda

Browse files
davidaureliofacebook-github-bot
authored andcommitted
Extract delta client
Summary: Extracts the delta client from the bundle downloader. This will allow us to extract an interface, and provide a different implementation for C++ delta bundling (where we will pass deltas directly to native code). Reviewed By: pakoito Differential Revision: D6900904 fbshipit-source-id: 358705615eecc15afa0de3e50478468ad840d250
1 parent 6e44356 commit 1019bda

File tree

2 files changed

+126
-114
lines changed

2 files changed

+126
-114
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.facebook.react.devsupport;
2+
3+
import android.util.JsonReader;
4+
import android.util.JsonToken;
5+
import java.io.File;
6+
import java.io.FileOutputStream;
7+
import java.io.IOException;
8+
import java.io.InputStreamReader;
9+
import java.util.LinkedHashMap;
10+
import javax.annotation.Nullable;
11+
import okio.BufferedSource;
12+
13+
public class BundleDeltaClient {
14+
15+
final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<Number, byte[]>();
16+
final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<Number, byte[]>();
17+
final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<Number, byte[]>();
18+
@Nullable String mDeltaId;
19+
20+
static boolean isDeltaUrl(String bundleUrl) {
21+
return bundleUrl.indexOf(".delta?") != -1;
22+
}
23+
24+
public void reset() {
25+
mDeltaId = null;
26+
mDeltaModules.clear();
27+
mPreModules.clear();
28+
mPostModules.clear();
29+
}
30+
31+
public String toDeltaUrl(String bundleURL) {
32+
if (isDeltaUrl(bundleURL) && mDeltaId != null) {
33+
return bundleURL + "&deltaBundleId=" + mDeltaId;
34+
}
35+
return bundleURL;
36+
}
37+
38+
public synchronized boolean storeDeltaInFile(BufferedSource body, File outputFile)
39+
throws IOException {
40+
41+
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
42+
43+
jsonReader.beginObject();
44+
45+
int numChangedModules = 0;
46+
47+
while (jsonReader.hasNext()) {
48+
String name = jsonReader.nextName();
49+
if (name.equals("id")) {
50+
mDeltaId = jsonReader.nextString();
51+
} else if (name.equals("pre")) {
52+
numChangedModules += patchDelta(jsonReader, mPreModules);
53+
} else if (name.equals("post")) {
54+
numChangedModules += patchDelta(jsonReader, mPostModules);
55+
} else if (name.equals("delta")) {
56+
numChangedModules += patchDelta(jsonReader, mDeltaModules);
57+
} else {
58+
jsonReader.skipValue();
59+
}
60+
}
61+
62+
jsonReader.endObject();
63+
jsonReader.close();
64+
65+
if (numChangedModules == 0) {
66+
// If we receive an empty delta, we don't need to save the file again (it'll have the
67+
// same content).
68+
return false;
69+
}
70+
71+
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
72+
73+
try {
74+
for (byte[] code : mPreModules.values()) {
75+
fileOutputStream.write(code);
76+
fileOutputStream.write('\n');
77+
}
78+
79+
for (byte[] code : mDeltaModules.values()) {
80+
fileOutputStream.write(code);
81+
fileOutputStream.write('\n');
82+
}
83+
84+
for (byte[] code : mPostModules.values()) {
85+
fileOutputStream.write(code);
86+
fileOutputStream.write('\n');
87+
}
88+
} finally {
89+
fileOutputStream.flush();
90+
fileOutputStream.close();
91+
}
92+
93+
return true;
94+
}
95+
96+
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
97+
throws IOException {
98+
jsonReader.beginArray();
99+
100+
int numModules = 0;
101+
while (jsonReader.hasNext()) {
102+
jsonReader.beginArray();
103+
104+
int moduleId = jsonReader.nextInt();
105+
106+
if (jsonReader.peek() == JsonToken.NULL) {
107+
jsonReader.skipValue();
108+
map.remove(moduleId);
109+
} else {
110+
map.put(moduleId, jsonReader.nextString().getBytes());
111+
}
112+
113+
jsonReader.endArray();
114+
numModules++;
115+
}
116+
117+
jsonReader.endArray();
118+
119+
return numModules;
120+
}
121+
}

ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java

Lines changed: 5 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,14 @@
99

1010
package com.facebook.react.devsupport;
1111

12-
import android.util.JsonReader;
13-
import android.util.JsonToken;
1412
import android.util.Log;
1513
import com.facebook.common.logging.FLog;
1614
import com.facebook.infer.annotation.Assertions;
1715
import com.facebook.react.common.DebugServerException;
1816
import com.facebook.react.common.ReactConstants;
1917
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
2018
import java.io.File;
21-
import java.io.FileOutputStream;
2219
import java.io.IOException;
23-
import java.io.InputStreamReader;
24-
import java.util.LinkedHashMap;
2520
import java.util.Map;
2621
import java.util.regex.Matcher;
2722
import java.util.regex.Pattern;
@@ -47,11 +42,8 @@ public class BundleDownloader {
4742

4843
private final OkHttpClient mClient;
4944

50-
private final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<>();
51-
private final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<>();
52-
private final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<>();
45+
private final BundleDeltaClient mBundleDeltaClient = new BundleDeltaClient();
5346

54-
private @Nullable String mDeltaId;
5547
private @Nullable Call mDownloadBundleFromURLCall;
5648

5749
public static class BundleInfo {
@@ -110,15 +102,9 @@ public void downloadBundleFromURL(
110102
final String bundleURL,
111103
final @Nullable BundleInfo bundleInfo) {
112104

113-
String finalUrl = bundleURL;
114-
115-
if (isDeltaUrl(bundleURL) && mDeltaId != null) {
116-
finalUrl += "&deltaBundleId=" + mDeltaId;
117-
}
118-
119105
final Request request =
120106
new Request.Builder()
121-
.url(finalUrl)
107+
.url(mBundleDeltaClient.toDeltaUrl(bundleURL))
122108
// FIXME: there is a bug that makes MultipartStreamReader to never find the end of the
123109
// multipart message. This temporarily disables the multipart mode to work around it,
124110
// but
@@ -253,11 +239,11 @@ private void processBundleResult(
253239

254240
boolean bundleUpdated;
255241

256-
if (isDeltaUrl(url)) {
242+
if (BundleDeltaClient.isDeltaUrl(url)) {
257243
// If the bundle URL has the delta extension, we need to use the delta patching logic.
258-
bundleUpdated = storeDeltaInFile(body, tmpFile);
244+
bundleUpdated = mBundleDeltaClient.storeDeltaInFile(body, tmpFile);
259245
} else {
260-
resetDeltaCache();
246+
mBundleDeltaClient.reset();
261247
bundleUpdated = storePlainJSInFile(body, tmpFile);
262248
}
263249

@@ -286,101 +272,6 @@ private static boolean storePlainJSInFile(BufferedSource body, File outputFile)
286272
return true;
287273
}
288274

289-
private synchronized boolean storeDeltaInFile(BufferedSource body, File outputFile) throws IOException {
290-
291-
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
292-
293-
jsonReader.beginObject();
294-
295-
int numChangedModules = 0;
296-
297-
while (jsonReader.hasNext()) {
298-
String name = jsonReader.nextName();
299-
if (name.equals("id")) {
300-
mDeltaId = jsonReader.nextString();
301-
} else if (name.equals("pre")) {
302-
numChangedModules += patchDelta(jsonReader, mPreModules);
303-
} else if (name.equals("post")) {
304-
numChangedModules += patchDelta(jsonReader, mPostModules);
305-
} else if (name.equals("delta")) {
306-
numChangedModules += patchDelta(jsonReader, mDeltaModules);
307-
} else {
308-
jsonReader.skipValue();
309-
}
310-
}
311-
312-
jsonReader.endObject();
313-
jsonReader.close();
314-
315-
if (numChangedModules == 0) {
316-
// If we receive an empty delta, we don't need to save the file again (it'll have the
317-
// same content).
318-
return false;
319-
}
320-
321-
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
322-
323-
try {
324-
for (byte[] code : mPreModules.values()) {
325-
fileOutputStream.write(code);
326-
fileOutputStream.write('\n');
327-
}
328-
329-
for (byte[] code : mDeltaModules.values()) {
330-
fileOutputStream.write(code);
331-
fileOutputStream.write('\n');
332-
}
333-
334-
for (byte[] code : mPostModules.values()) {
335-
fileOutputStream.write(code);
336-
fileOutputStream.write('\n');
337-
}
338-
} finally {
339-
fileOutputStream.flush();
340-
fileOutputStream.close();
341-
}
342-
343-
return true;
344-
}
345-
346-
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
347-
throws IOException {
348-
jsonReader.beginArray();
349-
350-
int numModules = 0;
351-
while (jsonReader.hasNext()) {
352-
jsonReader.beginArray();
353-
354-
int moduleId = jsonReader.nextInt();
355-
356-
if (jsonReader.peek() == JsonToken.NULL) {
357-
jsonReader.skipValue();
358-
map.remove(moduleId);
359-
} else {
360-
map.put(moduleId, jsonReader.nextString().getBytes());
361-
}
362-
363-
jsonReader.endArray();
364-
numModules++;
365-
}
366-
367-
jsonReader.endArray();
368-
369-
return numModules;
370-
}
371-
372-
private void resetDeltaCache() {
373-
mDeltaId = null;
374-
375-
mDeltaModules.clear();
376-
mPreModules.clear();
377-
mPostModules.clear();
378-
}
379-
380-
private static boolean isDeltaUrl(String bundleUrl) {
381-
return bundleUrl.indexOf(".delta?") != -1;
382-
}
383-
384275
private static void populateBundleInfo(String url, Headers headers, BundleInfo bundleInfo) {
385276
bundleInfo.mUrl = url;
386277

0 commit comments

Comments
 (0)