Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flight] Optimize Large Strings by Not Escaping Them #26932

Merged
merged 2 commits into from
Jun 13, 2023

Conversation

sebmarkbage
Copy link
Collaborator

This introduces a Text row (T) which is essentially a string blob and refactors the parsing to now happen at the binary level.

RowID + ":" + "T" + ByteLengthInHex + "," + Text

Today, we encode all row data in JSON, which conveniently never has newline characters and so we use newline as the line terminator. We can't do that if we pass arbitrary unicode without escaping it. Instead, we pass the byte length (in hexadecimal) in the leading header for this row tag followed by a comma.

We could be clever and use fixed or variable-length binary integers for the row id and length but it's not worth the more difficult debuggability so we keep these human readable in text.

Before this PR, we used to decode the binary stream into UTF-8 strings before parsing them. This is inefficient because sometimes the slices end up having to be copied so it's better to decode it directly into the format. The follow up to this is also to add support for binary data and then we can't assume the entire payload is UTF-8 anyway. So this refactors the parser to parse the rows in binary and then decode the result into UTF-8. It does add some overhead to decoding on a per row basis though.

Since we do this, we need to encode the byte length that we want decode - not the string length. Therefore, this requires clients to receive binary data and why I had to delete the string option.

It also means that I had to add a way to get the byteLength from a chunk since they're not always binary. For Web streams it's easy since they're always typed arrays. For Node streams it's trickier so we use the byteLength helper which may not be very efficient. Might be worth eagerly encoding them to UTF8 - perhaps only for this case.

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jun 12, 2023
@@ -66,6 +66,10 @@ export function clonePrecomputedChunk(
return chunk;
}

export function byteLengthOfChunk(chunk: Chunk | PrecomputedChunk): number {
throw new Error('Not implemented.');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jarred-Sumner Not sure what you want to do here. Either you can expose a JSC fast path for getting the byteLength of a string in UTF-8 or we can switch to always encoding strings eagerly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Buffer.byteLength(string, "utf8") is fast in Bun. Uses SIMD, handles latin1 vs utf16, etc

@@ -960,7 +975,12 @@ function resolveModelToJSON(
return serializeDateFromDateJSON(value);
}
}

if (value.length > 1024) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took some large looking strings that looked clowny and measured them but this is heuristic is pretty much taken out of thin air.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: wouldn't >= 1024 be a rounder number?

@react-sizebot
Copy link

react-sizebot commented Jun 12, 2023

Comparing: ce6842d...68c3e44

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 164.23 kB 164.23 kB = 51.73 kB 51.72 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 171.67 kB 171.67 kB = 53.97 kB 53.97 kB
facebook-www/ReactDOM-prod.classic.js = 570.12 kB 570.12 kB = 100.58 kB 100.58 kB
facebook-www/ReactDOM-prod.modern.js = 553.90 kB 553.90 kB = 97.75 kB 97.75 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +10.17% 34.97 kB 38.53 kB +9.06% 8.90 kB 9.70 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +10.17% 34.97 kB 38.53 kB +9.06% 8.90 kB 9.70 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +10.17% 34.97 kB 38.53 kB +9.06% 8.90 kB 9.70 kB
oss-experimental/react-client/cjs/react-client-flight.development.js +8.15% 43.47 kB 47.02 kB +7.63% 11.03 kB 11.87 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js +8.15% 43.47 kB 47.02 kB +7.63% 11.03 kB 11.87 kB
oss-stable/react-client/cjs/react-client-flight.development.js +8.15% 43.47 kB 47.02 kB +7.63% 11.03 kB 11.87 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +7.67% 46.15 kB 49.69 kB +7.34% 11.50 kB 12.34 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +7.67% 46.15 kB 49.69 kB +7.34% 11.50 kB 12.34 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +7.67% 46.15 kB 49.69 kB +7.34% 11.50 kB 12.34 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +7.53% 47.05 kB 50.59 kB +7.18% 11.72 kB 12.56 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +7.53% 47.05 kB 50.59 kB +7.18% 11.72 kB 12.56 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +7.53% 47.05 kB 50.59 kB +7.18% 11.72 kB 12.56 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +7.52% 47.10 kB 50.64 kB +7.30% 11.60 kB 12.45 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +7.52% 47.10 kB 50.64 kB +7.30% 11.60 kB 12.45 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +7.52% 47.10 kB 50.64 kB +7.30% 11.60 kB 12.45 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +7.49% 47.26 kB 50.80 kB +7.26% 11.66 kB 12.50 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +7.49% 47.26 kB 50.80 kB +7.26% 11.66 kB 12.50 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +7.49% 47.26 kB 50.80 kB +7.26% 11.66 kB 12.50 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +7.29% 48.58 kB 52.13 kB +6.94% 12.17 kB 13.01 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +7.29% 48.58 kB 52.13 kB +6.94% 12.17 kB 13.01 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +7.29% 48.58 kB 52.13 kB +6.94% 12.17 kB 13.01 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +7.18% 49.31 kB 52.85 kB +6.87% 12.32 kB 13.16 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +7.18% 49.31 kB 52.85 kB +6.87% 12.32 kB 13.16 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +7.18% 49.31 kB 52.85 kB +6.87% 12.32 kB 13.16 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +7.18% 52.80 kB 56.59 kB +6.72% 12.55 kB 13.39 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +7.18% 52.80 kB 56.59 kB +6.72% 12.55 kB 13.39 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +7.18% 52.80 kB 56.59 kB +6.72% 12.55 kB 13.39 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +7.13% 49.67 kB 53.21 kB +6.84% 12.33 kB 13.18 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +7.13% 49.67 kB 53.21 kB +6.84% 12.33 kB 13.18 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +7.13% 49.67 kB 53.21 kB +6.84% 12.33 kB 13.18 kB
oss-experimental/react-client/cjs/react-client-flight.production.min.js +6.58% 8.22 kB 8.76 kB +7.60% 3.29 kB 3.54 kB
oss-stable-semver/react-client/cjs/react-client-flight.production.min.js +6.58% 8.22 kB 8.76 kB +7.60% 3.29 kB 3.54 kB
oss-stable/react-client/cjs/react-client-flight.production.min.js +6.58% 8.22 kB 8.76 kB +7.60% 3.29 kB 3.54 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +5.41% 9.20 kB 9.70 kB +6.66% 3.65 kB 3.89 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +5.41% 9.20 kB 9.70 kB +6.66% 3.65 kB 3.89 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +5.41% 9.20 kB 9.70 kB +6.66% 3.65 kB 3.89 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +5.35% 9.49 kB 10.00 kB +6.51% 3.72 kB 3.96 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +5.35% 9.49 kB 10.00 kB +6.51% 3.72 kB 3.96 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +5.35% 9.49 kB 10.00 kB +6.51% 3.72 kB 3.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +5.23% 9.52 kB 10.02 kB +6.34% 3.78 kB 4.02 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +5.23% 9.52 kB 10.02 kB +6.34% 3.78 kB 4.02 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +5.23% 9.52 kB 10.02 kB +6.34% 3.78 kB 4.02 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +5.03% 9.90 kB 10.40 kB +6.10% 3.97 kB 4.21 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +5.03% 9.90 kB 10.40 kB +6.10% 3.97 kB 4.21 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +5.03% 9.90 kB 10.40 kB +6.10% 3.97 kB 4.21 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +5.00% 10.15 kB 10.65 kB +5.99% 4.04 kB 4.28 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +5.00% 10.15 kB 10.65 kB +5.99% 4.04 kB 4.28 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +5.00% 10.15 kB 10.65 kB +5.99% 4.04 kB 4.28 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +4.99% 10.17 kB 10.67 kB +6.23% 4.01 kB 4.26 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +4.99% 10.17 kB 10.67 kB +6.23% 4.01 kB 4.26 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +4.99% 10.17 kB 10.67 kB +6.23% 4.01 kB 4.26 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +4.87% 10.43 kB 10.94 kB +6.06% 4.11 kB 4.36 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +4.87% 10.43 kB 10.94 kB +6.06% 4.11 kB 4.36 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +4.87% 10.43 kB 10.94 kB +6.06% 4.11 kB 4.36 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +10.17% 34.97 kB 38.53 kB +9.06% 8.90 kB 9.70 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +10.17% 34.97 kB 38.53 kB +9.06% 8.90 kB 9.70 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +10.17% 34.97 kB 38.53 kB +9.06% 8.90 kB 9.70 kB
oss-experimental/react-client/cjs/react-client-flight.development.js +8.15% 43.47 kB 47.02 kB +7.63% 11.03 kB 11.87 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js +8.15% 43.47 kB 47.02 kB +7.63% 11.03 kB 11.87 kB
oss-stable/react-client/cjs/react-client-flight.development.js +8.15% 43.47 kB 47.02 kB +7.63% 11.03 kB 11.87 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +7.67% 46.15 kB 49.69 kB +7.34% 11.50 kB 12.34 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +7.67% 46.15 kB 49.69 kB +7.34% 11.50 kB 12.34 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +7.67% 46.15 kB 49.69 kB +7.34% 11.50 kB 12.34 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +7.53% 47.05 kB 50.59 kB +7.18% 11.72 kB 12.56 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +7.53% 47.05 kB 50.59 kB +7.18% 11.72 kB 12.56 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +7.53% 47.05 kB 50.59 kB +7.18% 11.72 kB 12.56 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +7.52% 47.10 kB 50.64 kB +7.30% 11.60 kB 12.45 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +7.52% 47.10 kB 50.64 kB +7.30% 11.60 kB 12.45 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +7.52% 47.10 kB 50.64 kB +7.30% 11.60 kB 12.45 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +7.49% 47.26 kB 50.80 kB +7.26% 11.66 kB 12.50 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +7.49% 47.26 kB 50.80 kB +7.26% 11.66 kB 12.50 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +7.49% 47.26 kB 50.80 kB +7.26% 11.66 kB 12.50 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +7.29% 48.58 kB 52.13 kB +6.94% 12.17 kB 13.01 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +7.29% 48.58 kB 52.13 kB +6.94% 12.17 kB 13.01 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +7.29% 48.58 kB 52.13 kB +6.94% 12.17 kB 13.01 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +7.18% 49.31 kB 52.85 kB +6.87% 12.32 kB 13.16 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +7.18% 49.31 kB 52.85 kB +6.87% 12.32 kB 13.16 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +7.18% 49.31 kB 52.85 kB +6.87% 12.32 kB 13.16 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +7.18% 52.80 kB 56.59 kB +6.72% 12.55 kB 13.39 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +7.18% 52.80 kB 56.59 kB +6.72% 12.55 kB 13.39 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +7.18% 52.80 kB 56.59 kB +6.72% 12.55 kB 13.39 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +7.13% 49.67 kB 53.21 kB +6.84% 12.33 kB 13.18 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +7.13% 49.67 kB 53.21 kB +6.84% 12.33 kB 13.18 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +7.13% 49.67 kB 53.21 kB +6.84% 12.33 kB 13.18 kB
oss-experimental/react-client/cjs/react-client-flight.production.min.js +6.58% 8.22 kB 8.76 kB +7.60% 3.29 kB 3.54 kB
oss-stable-semver/react-client/cjs/react-client-flight.production.min.js +6.58% 8.22 kB 8.76 kB +7.60% 3.29 kB 3.54 kB
oss-stable/react-client/cjs/react-client-flight.production.min.js +6.58% 8.22 kB 8.76 kB +7.60% 3.29 kB 3.54 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +5.41% 9.20 kB 9.70 kB +6.66% 3.65 kB 3.89 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +5.41% 9.20 kB 9.70 kB +6.66% 3.65 kB 3.89 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +5.41% 9.20 kB 9.70 kB +6.66% 3.65 kB 3.89 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +5.35% 9.49 kB 10.00 kB +6.51% 3.72 kB 3.96 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +5.35% 9.49 kB 10.00 kB +6.51% 3.72 kB 3.96 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +5.35% 9.49 kB 10.00 kB +6.51% 3.72 kB 3.96 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +5.23% 9.52 kB 10.02 kB +6.34% 3.78 kB 4.02 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +5.23% 9.52 kB 10.02 kB +6.34% 3.78 kB 4.02 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +5.23% 9.52 kB 10.02 kB +6.34% 3.78 kB 4.02 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +5.03% 9.90 kB 10.40 kB +6.10% 3.97 kB 4.21 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +5.03% 9.90 kB 10.40 kB +6.10% 3.97 kB 4.21 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +5.03% 9.90 kB 10.40 kB +6.10% 3.97 kB 4.21 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +5.00% 10.15 kB 10.65 kB +5.99% 4.04 kB 4.28 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +5.00% 10.15 kB 10.65 kB +5.99% 4.04 kB 4.28 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +5.00% 10.15 kB 10.65 kB +5.99% 4.04 kB 4.28 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +4.99% 10.17 kB 10.67 kB +6.23% 4.01 kB 4.26 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +4.99% 10.17 kB 10.67 kB +6.23% 4.01 kB 4.26 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +4.99% 10.17 kB 10.67 kB +6.23% 4.01 kB 4.26 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +4.87% 10.43 kB 10.94 kB +6.06% 4.11 kB 4.36 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +4.87% 10.43 kB 10.94 kB +6.06% 4.11 kB 4.36 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +4.87% 10.43 kB 10.94 kB +6.06% 4.11 kB 4.36 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +1.42% 65.58 kB 66.51 kB +1.53% 15.78 kB 16.03 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js +1.42% 65.58 kB 66.51 kB +1.53% 15.78 kB 16.03 kB
oss-stable/react-server/cjs/react-server-flight.development.js +1.42% 65.58 kB 66.51 kB +1.53% 15.78 kB 16.03 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +1.08% 92.75 kB 93.75 kB +1.23% 22.11 kB 22.39 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +1.08% 92.75 kB 93.75 kB +1.23% 22.11 kB 22.39 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +1.08% 92.75 kB 93.75 kB +1.23% 22.11 kB 22.39 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +1.07% 93.65 kB 94.65 kB +1.21% 22.35 kB 22.62 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +1.07% 93.65 kB 94.65 kB +1.21% 22.35 kB 22.62 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +1.07% 93.65 kB 94.65 kB +1.21% 22.35 kB 22.62 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.05% 95.96 kB 96.97 kB +1.18% 23.00 kB 23.27 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.05% 95.96 kB 96.97 kB +1.18% 23.00 kB 23.27 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +1.05% 95.96 kB 96.97 kB +1.18% 23.00 kB 23.27 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +1.04% 90.33 kB 91.27 kB +1.18% 21.84 kB 22.10 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +1.04% 90.33 kB 91.27 kB +1.18% 21.84 kB 22.10 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +1.04% 90.33 kB 91.27 kB +1.18% 21.84 kB 22.10 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +1.04% 90.74 kB 91.68 kB +1.13% 21.96 kB 22.21 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +1.04% 90.74 kB 91.68 kB +1.13% 21.96 kB 22.21 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +1.04% 90.74 kB 91.68 kB +1.13% 21.96 kB 22.21 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js +1.03% 95.53 kB 96.51 kB +1.26% 22.15 kB 22.43 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js +1.03% 95.53 kB 96.51 kB +1.26% 22.15 kB 22.43 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js +1.03% 95.53 kB 96.51 kB +1.26% 22.15 kB 22.43 kB
oss-experimental/react-server/cjs/react-server-flight.production.min.js +0.99% 16.04 kB 16.20 kB +1.21% 5.77 kB 5.84 kB
oss-stable-semver/react-server/cjs/react-server-flight.production.min.js +0.99% 16.04 kB 16.20 kB +1.21% 5.77 kB 5.84 kB
oss-stable/react-server/cjs/react-server-flight.production.min.js +0.99% 16.04 kB 16.20 kB +1.21% 5.77 kB 5.84 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +0.74% 22.89 kB 23.06 kB +0.82% 8.03 kB 8.10 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +0.74% 22.89 kB 23.06 kB +0.82% 8.03 kB 8.10 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +0.74% 22.89 kB 23.06 kB +0.82% 8.03 kB 8.10 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +0.73% 23.00 kB 23.17 kB +0.79% 8.11 kB 8.17 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +0.73% 23.00 kB 23.17 kB +0.79% 8.11 kB 8.17 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +0.73% 23.00 kB 23.17 kB +0.79% 8.11 kB 8.17 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js +0.72% 23.56 kB 23.73 kB +0.83% 8.29 kB 8.36 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js +0.72% 23.56 kB 23.73 kB +0.83% 8.29 kB 8.36 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js +0.72% 23.56 kB 23.73 kB +0.83% 8.29 kB 8.36 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js +0.68% 21.80 kB 21.94 kB +0.77% 7.71 kB 7.77 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js +0.68% 21.80 kB 21.94 kB +0.77% 7.71 kB 7.77 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js +0.68% 21.80 kB 21.94 kB +0.77% 7.71 kB 7.77 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js +0.68% 22.00 kB 22.15 kB +0.83% 7.81 kB 7.87 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js +0.68% 22.00 kB 22.15 kB +0.83% 7.81 kB 7.87 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js +0.68% 22.00 kB 22.15 kB +0.83% 7.81 kB 7.87 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.min.js +0.67% 22.10 kB 22.25 kB +0.70% 7.82 kB 7.87 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.min.js +0.67% 22.10 kB 22.25 kB +0.70% 7.82 kB 7.87 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.min.js +0.67% 22.10 kB 22.25 kB +0.70% 7.82 kB 7.87 kB

Generated by 🚫 dangerJS against 68c3e44

@sebmarkbage sebmarkbage force-pushed the flighttext branch 2 times, most recently from d686497 to df81cb6 Compare June 12, 2023 20:21
@sebmarkbage
Copy link
Collaborator Author

This puts a lot of burden on a runtime to support UTF-8 encoding exposed in the runtime. E.g. the FB one doesn't atm. However, I'm not aware of any current supported environment that wouldn't be able to. The follow up to support binary data also puts more requirements on the client (and SSR).

Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you know that TextEncoder doesn't let you specify an encoding? I learned this last week when thinking about your question

_rowID: number, // parts of a row ID parsed so far
_rowTag: number, // 0 indicates that we're currently parsing the row ID
_rowLength: number, // remaining bytes in the row. 0 indicates that we're looking for a newline.
_buffer: Array<string | Uint8Array>, // chunks received so far as part of this row
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when is this a string?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not anymore but really ideally it should be able to accept string chunks at least it’s known not to be binary content but then it gets tricky to count the bytes again, especially for the last chunk, so maybe we don’t support it.

When it’s coming through a html encoded stream we’d encode the chunking in html so there might be that the last chunk resolved is a string but not the buffer.

}
if (lastIdx > -1) {
// We found the last chunk of the row
const lastChunk = chunk.slice(i, lastIdx);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DataView be more efficient here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubt it. It generally isn’t but also we don’t really control the source and this will also be used for binary streams where the protocol is uint8arrays too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you mean to avoid the copy? I actually don’t know why I did a copy.

In general we expect these to be immutable representations so I could probably do a new array from the same underlying buffer.

It’ll need to be copied for serialized typed arrays in some cases anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, in JSC (don't know about V8), calling .subarray or .buffer on an ArrayBufferView will actually clone it on the first access because by default the lifetime of the buffer is the lifetime of the ArrayBufferView ("WastefulTypedArray" is the name for this in the code iirc)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate. Given that TextDecoder requires a view, is there any workaround that doesn't?

@@ -960,7 +975,12 @@ function resolveModelToJSON(
return serializeDateFromDateJSON(value);
}
}

if (value.length > 1024) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: wouldn't >= 1024 be a rounder number?

packages/react-client/src/ReactFlightClient.js Outdated Show resolved Hide resolved
packages/react-server/src/ReactFlightServer.js Outdated Show resolved Hide resolved
@sebmarkbage
Copy link
Collaborator Author

did you know that TextEncoder doesn't let you specify an encoding?

Yea, that was the part that made any clever hacks with encoding in HTML impossible. I guess it makes sense that browsers don't really have to know how to encode other encodings - only decoding.

@sebmarkbage sebmarkbage merged commit db50164 into facebook:main Jun 13, 2023
sebmarkbage added a commit that referenced this pull request Jun 14, 2023
…ter (#26945)

Follow up to #26932

For regular rows, we're increasing the index by one to skip past the
last trailing newline character which acts a boundary. For length
encoded rows we shouldn't skip an extra byte because it'll leave us
missing one.

This only accidentally worked because this was also the end of the
current chunk which tests don't account for since we're just passing
through the chunks. So I added some noise by splitting and joining the
chunks so that this gets tested.
sebmarkbage added a commit that referenced this pull request Jun 29, 2023
…26954)

This uses the same mechanism as [large
strings](#26932) to encode chunks
of length based binary data in the RSC payload behind a flag.

I introduce a new BinaryChunk type that's specific to each stream and
ways to convert into it. That's because we sometimes need all chunks to
be Uint8Array for the output, even if the source is another array buffer
view, and sometimes we need to clone it before transferring.

Each type of typed array is its own row tag. This lets us ensure that
the instance is directly in the right format in the cached entry instead
of creating a wrapper at each reference. Ideally this is also how
Map/Set should work but those are lazy which complicates that approach a
bit.

We assume both server and client use little-endian for now. If we want
to support other modes, we'd convert it to/from little-endian so that
the transfer protocol is always little-endian. That way the common
clients can be the fastest possible.

So far this only implements Server to Client. Still need to implement
Client to Server for parity.

NOTE: This is the first time we make RSC effectively a binary format.
This is not compatible with existing SSR techniques which serialize the
stream as unicode in the HTML. To be compatible, those implementations
would have to use base64 or something like that. Which is what we'll do
when we move this technique to be built-in to Fizz.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
This introduces a Text row (T) which is essentially a string blob and
refactors the parsing to now happen at the binary level.

```
RowID + ":" + "T" + ByteLengthInHex + "," + Text
```

Today, we encode all row data in JSON, which conveniently never has
newline characters and so we use newline as the line terminator. We
can't do that if we pass arbitrary unicode without escaping it. Instead,
we pass the byte length (in hexadecimal) in the leading header for this
row tag followed by a comma.

We could be clever and use fixed or variable-length binary integers for
the row id and length but it's not worth the more difficult
debuggability so we keep these human readable in text.

Before this PR, we used to decode the binary stream into UTF-8 strings
before parsing them. This is inefficient because sometimes the slices
end up having to be copied so it's better to decode it directly into the
format. The follow up to this is also to add support for binary data and
then we can't assume the entire payload is UTF-8 anyway. So this
refactors the parser to parse the rows in binary and then decode the
result into UTF-8. It does add some overhead to decoding on a per row
basis though.

Since we do this, we need to encode the byte length that we want decode
- not the string length. Therefore, this requires clients to receive
binary data and why I had to delete the string option.

It also means that I had to add a way to get the byteLength from a chunk
since they're not always binary. For Web streams it's easy since they're
always typed arrays. For Node streams it's trickier so we use the
byteLength helper which may not be very efficient. Might be worth
eagerly encoding them to UTF8 - perhaps only for this case.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…ter (facebook#26945)

Follow up to facebook#26932

For regular rows, we're increasing the index by one to skip past the
last trailing newline character which acts a boundary. For length
encoded rows we shouldn't skip an extra byte because it'll leave us
missing one.

This only accidentally worked because this was also the end of the
current chunk which tests don't account for since we're just passing
through the chunks. So I added some noise by splitting and joining the
chunks so that this gets tested.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…acebook#26954)

This uses the same mechanism as [large
strings](facebook#26932) to encode chunks
of length based binary data in the RSC payload behind a flag.

I introduce a new BinaryChunk type that's specific to each stream and
ways to convert into it. That's because we sometimes need all chunks to
be Uint8Array for the output, even if the source is another array buffer
view, and sometimes we need to clone it before transferring.

Each type of typed array is its own row tag. This lets us ensure that
the instance is directly in the right format in the cached entry instead
of creating a wrapper at each reference. Ideally this is also how
Map/Set should work but those are lazy which complicates that approach a
bit.

We assume both server and client use little-endian for now. If we want
to support other modes, we'd convert it to/from little-endian so that
the transfer protocol is always little-endian. That way the common
clients can be the fastest possible.

So far this only implements Server to Client. Still need to implement
Client to Server for parity.

NOTE: This is the first time we make RSC effectively a binary format.
This is not compatible with existing SSR techniques which serialize the
stream as unicode in the HTML. To be compatible, those implementations
would have to use base64 or something like that. Which is what we'll do
when we move this technique to be built-in to Fizz.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
This introduces a Text row (T) which is essentially a string blob and
refactors the parsing to now happen at the binary level.

```
RowID + ":" + "T" + ByteLengthInHex + "," + Text
```

Today, we encode all row data in JSON, which conveniently never has
newline characters and so we use newline as the line terminator. We
can't do that if we pass arbitrary unicode without escaping it. Instead,
we pass the byte length (in hexadecimal) in the leading header for this
row tag followed by a comma.

We could be clever and use fixed or variable-length binary integers for
the row id and length but it's not worth the more difficult
debuggability so we keep these human readable in text.

Before this PR, we used to decode the binary stream into UTF-8 strings
before parsing them. This is inefficient because sometimes the slices
end up having to be copied so it's better to decode it directly into the
format. The follow up to this is also to add support for binary data and
then we can't assume the entire payload is UTF-8 anyway. So this
refactors the parser to parse the rows in binary and then decode the
result into UTF-8. It does add some overhead to decoding on a per row
basis though.

Since we do this, we need to encode the byte length that we want decode
- not the string length. Therefore, this requires clients to receive
binary data and why I had to delete the string option.

It also means that I had to add a way to get the byteLength from a chunk
since they're not always binary. For Web streams it's easy since they're
always typed arrays. For Node streams it's trickier so we use the
byteLength helper which may not be very efficient. Might be worth
eagerly encoding them to UTF8 - perhaps only for this case.

DiffTrain build for commit db50164.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…ter (#26945)

Follow up to #26932

For regular rows, we're increasing the index by one to skip past the
last trailing newline character which acts a boundary. For length
encoded rows we shouldn't skip an extra byte because it'll leave us
missing one.

This only accidentally worked because this was also the end of the
current chunk which tests don't account for since we're just passing
through the chunks. So I added some noise by splitting and joining the
chunks so that this gets tested.

DiffTrain build for commit a1723e1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants