Skip to content

Commit bb71759

Browse files
committed
Speed up string<->buffer conversion everywhere
This reduces conversion for very large buffers (~25MB) from about 600ms to 100ms or less, which makes a big difference for the resulting UX! Most important when editing a very large breakpointed body.
1 parent a679b07 commit bb71759

File tree

11 files changed

+76
-35
lines changed

11 files changed

+76
-35
lines changed

src/components/editor/content-viewer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as portals from 'react-reverse-portal';
88
import { css, styled } from '../../styles';
99
import { ObservablePromise, isObservablePromise } from '../../util/observable';
1010
import { asError } from '../../util/error';
11+
import { stringToBuffer } from '../../util';
1112

1213
import { ViewableContentType } from '../../model/events/content-types';
1314
import { Formatters, isEditorFormatter } from '../../model/events/body-formatting';
@@ -70,7 +71,7 @@ export class ContentViewer extends React.Component<ContentViewerProps> {
7071
@computed
7172
private get contentBuffer() {
7273
return _.isString(this.props.children)
73-
? Buffer.from(this.props.children)
74+
? stringToBuffer(this.props.children)
7475
: this.props.children;
7576
}
7677

src/components/intercept/config/android-device-config.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from 'mockttp';
1010

1111
import { styled } from '../../../styles';
12+
import { stringToBuffer } from '../../../util';
1213

1314
import { Interceptor } from '../../../model/interception/interceptors';
1415
import { ProxyStore } from '../../../model/proxy-store';
@@ -61,7 +62,8 @@ const Spacer = styled.div`
6162
`;
6263

6364
function urlSafeBase64(content: string) {
64-
return Buffer.from(content, 'utf8').toString('base64')
65+
return stringToBuffer(content)
66+
.toString('base64')
6567
.replace(/\+/g, '-')
6668
.replace(/\//g, '_');
6769
}

src/components/mock/handler-config.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ import { css, styled } from '../../styles';
99
import { WarningIcon } from '../../icons';
1010
import { uploadFile } from '../../util/ui';
1111
import { asError, isErrorLike, UnreachableCheck } from '../../util/error';
12-
import { byteLength, asBuffer, isProbablyUtf8 } from '../../util';
12+
import {
13+
byteLength,
14+
asBuffer,
15+
isProbablyUtf8,
16+
stringToBuffer,
17+
bufferToString
18+
} from '../../util';
1319

1420
import {
1521
Handler,
@@ -446,7 +452,7 @@ class StaticResponseHandlerConfig extends HandlerConfig<StaticResponseHandler |
446452

447453
@action.bound
448454
setBody(body: string) {
449-
this.body = Buffer.from(body, this.textEncoding);
455+
this.body = stringToBuffer(body, this.textEncoding);
450456
}
451457

452458
updateHandler() {
@@ -1158,7 +1164,7 @@ const RawBodyTransfomConfig = (props: {
11581164
<ThemedSelfSizedEditor
11591165
contentId={null}
11601166
language={contentType}
1161-
value={props.body.toString('utf8')}
1167+
value={bufferToString(props.body)}
11621168
onChange={props.updateBody}
11631169
/>
11641170
</BodyContainer>
@@ -1914,7 +1920,7 @@ class IpfsCatTextHandlerConfig extends HandlerConfig<IpfsCatTextHandler> {
19141920

19151921
@action.bound
19161922
setBody(body: string) {
1917-
this.body = Buffer.from(body, this.textEncoding);
1923+
this.body = stringToBuffer(body, this.textEncoding);
19181924
this.props.onChange(
19191925
new IpfsCatTextHandler(this.body)
19201926
);
@@ -2415,7 +2421,7 @@ class RTCSendMessageStepConfig extends HandlerConfig<SendStepDefinition> {
24152421

24162422
@action.bound
24172423
setMessage(message: string) {
2418-
this.message = Buffer.from(message, this.textEncoding);
2424+
this.message = stringToBuffer(message, this.textEncoding);
24192425
this.updateHandler();
24202426
}
24212427

src/components/view/http/http-breakpoint-body-card.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import { observer, disposeOnUnmount } from 'mobx-react';
55
import * as portals from 'react-reverse-portal';
66

77
import { Headers } from '../../../types';
8-
import { lastHeader, isProbablyUtf8 } from '../../../util';
8+
import {
9+
lastHeader,
10+
isProbablyUtf8,
11+
bufferToString,
12+
stringToBuffer
13+
} from '../../../util';
914
import {
1015
EditableContentType,
1116
EditableContentTypes,
@@ -75,7 +80,7 @@ export class HttpBreakpointBodyCard extends React.Component<{
7580
onExpandToggled
7681
} = this.props;
7782

78-
const bodyString = body.toString(this.textEncoding);
83+
const bodyString = bufferToString(body, this.textEncoding);
7984

8085
return <CollapsibleCard
8186
direction={direction}
@@ -121,7 +126,7 @@ export class HttpBreakpointBodyCard extends React.Component<{
121126
}
122127

123128
private onBodyChange = (body: string) => {
124-
this.props.onChange(Buffer.from(body, this.textEncoding));
129+
this.props.onChange(stringToBuffer(body, this.textEncoding));
125130
}
126131

127132
}

src/components/view/stream-message-rows.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { disposeOnUnmount, observer } from 'mobx-react';
55
import * as portals from 'react-reverse-portal';
66

77
import { styled, warningColor } from '../../styles';
8-
import { asBuffer } from '../../util';
8+
import { asBuffer, bufferToString } from '../../util';
99
import { ArrowIcon } from '../../icons';
1010

1111
import {
@@ -57,12 +57,12 @@ export const StreamMessageCollapsedRow = React.memo((p: {
5757
<MessageArrow selected={false} messageDirection={visualDirection(p.message)} />
5858
<CollapsedStreamContent>
5959
{
60-
p.message.content
61-
// Limit the length - no point showing huge messages here. On a typical UI, we show about
62-
// 100 chars here, so this should give us a good bit of buffer
63-
.slice(0, 200)
64-
// Show everything as UTF-8 - binary data can be viewed up close instead.
65-
.toString('utf8')
60+
bufferToString( // Show everything as UTF-8 - binary data can be viewed up close instead.
61+
p.message.content
62+
// Limit the length - no point showing huge messages here. On a typical UI, we show about
63+
// 100 chars here, so this should give us a good bit of buffer
64+
.slice(0, 200)
65+
)
6666
}
6767
</CollapsedStreamContent>
6868
{

src/model/events/body-formatting.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { styled } from '../../styles';
22

33
import { ViewableContentType } from '../events/content-types';
44
import { ObservablePromise, observablePromise } from '../../util/observable';
5+
import { bufferToString } from '../../util';
56

67
import type { WorkerFormatterKey } from '../../services/ui-worker-formatters';
78
import { formatBufferAsync } from '../../services/ui-worker-api';
@@ -64,7 +65,7 @@ export const Formatters: { [key in ViewableContentType]: Formatter } = {
6465
language: 'text',
6566
cacheKey: Symbol('text'),
6667
render: (input: Buffer) => {
67-
return input.toString('utf8');
68+
return bufferToString(input);
6869
}
6970
},
7071
base64: {
@@ -96,8 +97,8 @@ export const Formatters: { [key in ViewableContentType]: Formatter } = {
9697
language: 'json',
9798
cacheKey: Symbol('json'),
9899
render: (input: Buffer) => {
99-
if (input.byteLength < 2000) {
100-
const inputAsString = input.toString('utf8')
100+
if (input.byteLength < 10000) {
101+
const inputAsString = bufferToString(input);
101102

102103
try {
103104
// For short-ish inputs, we return synchronously - conveniently this avoids
@@ -130,7 +131,7 @@ export const Formatters: { [key in ViewableContentType]: Formatter } = {
130131
'url-encoded': {
131132
scrollable: true,
132133
Component: styled(ReadOnlyParams).attrs((p: FormatComponentProps) => ({
133-
content: p.content.toString('utf8')
134+
content: bufferToString(p.content)
134135
}))`
135136
padding: 5px;
136137
` as FormatComponent

src/model/filters/search-filters.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as _ from 'lodash';
22

33
import { CollectedEvent } from '../../types';
44
import { joinAnd } from '../../util/text';
5+
import { stringToBuffer } from '../../util';
56

67
import { getStatusDocs } from '../http/http-docs';
78
import { getReadableSize } from '../events/bodies';
@@ -1214,7 +1215,7 @@ class BodyFilter extends Filter {
12141215
const [, op, expectedBody] = parseFilter(BodyFilter, filter);
12151216
this.op = op;
12161217

1217-
this.expectedBody = Buffer.from(expectedBody);
1218+
this.expectedBody = stringToBuffer(expectedBody);
12181219
this.predicate = bufferOperations[this.op];
12191220
}
12201221

src/model/http/exchange-breakpoint.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
BreakpointBody,
1313
MockttpBreakpointResponseResult,
1414
} from "../../types";
15-
import { asHeaderArray } from "../../util";
15+
import { asHeaderArray, stringToBuffer } from "../../util";
1616
import { getDeferred, Deferred } from "../../util/promise";
1717
import { reportError } from "../../errors";
1818

@@ -36,8 +36,8 @@ function getBody(message: MockttpBreakpointedRequest | MockttpBreakpointedRespon
3636
check content-encoding
3737
`;
3838
return {
39-
encoded: Buffer.from(error),
40-
decoded: Buffer.from(error)
39+
encoded: stringToBuffer(error),
40+
decoded: stringToBuffer(error)
4141
};
4242
});
4343
}
@@ -68,8 +68,8 @@ export function getDummyResponseBreakpoint(httpVersion: 1 | 2) {
6868
statusMessage: undefined,
6969
headers: httpVersion === 2 ? { ':status': '200' } : {},
7070
},
71-
Buffer.from(''),
72-
Buffer.from('')
71+
stringToBuffer(''),
72+
stringToBuffer('')
7373
);
7474

7575
return breakpoint;

src/model/http/exchange.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
FakeBuffer,
2222
asHeaderArray,
2323
lastHeader,
24+
stringToBuffer,
2425
} from '../../util';
2526
import { UnreachableCheck } from '../../util/error';
2627
import { lazyObservablePromise, ObservablePromise, observablePromise } from "../../util/observable";
@@ -108,7 +109,7 @@ export class HttpBody implements MessageBody {
108109
headers: Headers
109110
) {
110111
if (!('body' in message) || !message.body) {
111-
this._encoded = Buffer.from("");
112+
this._encoded = stringToBuffer("");
112113
} else if ('buffer' in message.body) {
113114
this._encoded = message.body.buffer;
114115
} else {

src/model/http/har.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as HarFormat from 'har-format';
44
import * as HarValidator from 'har-validator';
55
import * as querystring from 'querystring';
66

7-
import { lastHeader } from '../../util';
7+
import { lastHeader, stringToBuffer } from '../../util';
88
import { ObservablePromise } from '../../util/observable';
99
import {
1010
Headers,
@@ -615,10 +615,10 @@ function parseHarRequestContents(data: RequestContentData): Buffer {
615615
function parseHarPostData(data: HarFormat.PostData | undefined): Buffer {
616616
if (data?.text) {
617617
// Prefer raw exported 'text' data, when available
618-
return Buffer.from(data.text, 'utf8');
618+
return stringToBuffer(data.text, 'utf8');
619619
} else if (data?.params) {
620620
// If only 'params' is available, stringify and use that.
621-
return Buffer.from(
621+
return stringToBuffer(
622622
// Go from array of key-value objects to object of key -> value array:
623623
querystring.stringify(_(data.params)
624624
.groupBy(({ name }) => name)
@@ -627,7 +627,7 @@ function parseHarPostData(data: HarFormat.PostData | undefined): Buffer {
627627
)
628628
);
629629
} else {
630-
return Buffer.from('');
630+
return stringToBuffer('');
631631
}
632632
}
633633

0 commit comments

Comments
 (0)