Skip to content

Commit dd1f32d

Browse files
committed
perf(serdes): plain type for all raw values
+ move Object to single digits + bugfix, wasn't inflating arrays in deserializeData()
1 parent 56619f0 commit dd1f32d

File tree

3 files changed

+355
-375
lines changed

3 files changed

+355
-375
lines changed

packages/qwik/src/core/shared/shared-serialization.ts

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ import { SerializerSignalImpl } from '../reactive-primitives/impl/serializer-sig
6767
import { AsyncComputedSignalImpl } from '../reactive-primitives/impl/async-computed-signal-impl';
6868
import { isObject } from './utils/types';
6969

70+
/** Arrays/Objects are special-cased so their identifiers is a single digit. */
71+
const needsInflation = (typeId: TypeIds) =>
72+
typeId >= TypeIds.Error || typeId === TypeIds.Array || typeId === TypeIds.Object;
73+
7074
const deserializedProxyMap = new WeakMap<object, unknown[]>();
7175

7276
type DeserializerProxy<T extends object = object> = T & { [SERIALIZER_PROXY_UNWRAP]: object };
@@ -122,7 +126,7 @@ class DeserializationHandler implements ProxyHandler<object> {
122126
const idx = i * 2;
123127
const typeId = this.$data$[idx] as number;
124128
const value = this.$data$[idx + 1];
125-
if (typeId === undefined) {
129+
if (typeId === TypeIds.Plain) {
126130
// The value is already cached
127131
return value;
128132
}
@@ -131,14 +135,11 @@ class DeserializationHandler implements ProxyHandler<object> {
131135
const propValue = allocate(container, typeId, value);
132136

133137
Reflect.set(target, property, propValue);
134-
this.$data$[idx] = undefined;
138+
this.$data$[idx] = TypeIds.Plain;
135139
this.$data$[idx + 1] = propValue;
136140

137-
/**
138-
* We stored the reference, so now we can inflate, allowing cycles. Arrays are special-cased so
139-
* their identifiers is a single digit.
140-
*/
141-
if (typeId === TypeIds.Array || typeId >= TypeIds.Error) {
141+
/** We stored the reference, so now we can inflate, allowing cycles */
142+
if (needsInflation(typeId)) {
142143
inflate(container, propValue, typeId, value);
143144
}
144145

@@ -162,7 +163,7 @@ class DeserializationHandler implements ProxyHandler<object> {
162163
return out;
163164
}
164165
const idx = i * 2;
165-
this.$data$[idx] = undefined;
166+
this.$data$[idx] = TypeIds.Plain;
166167
this.$data$[idx + 1] = value;
167168
return true;
168169
}
@@ -191,7 +192,7 @@ const inflate = (
191192
typeId: TypeIds,
192193
data: unknown
193194
): void => {
194-
if (typeId === undefined) {
195+
if (typeId === TypeIds.Plain) {
195196
// Already processed
196197
return;
197198
}
@@ -458,7 +459,7 @@ const _constantNames = [
458459
] as const;
459460

460461
const allocate = (container: DeserializeContainer, typeId: number, value: unknown): any => {
461-
if (typeId === undefined) {
462+
if (typeId === TypeIds.Plain) {
462463
return value;
463464
}
464465
switch (typeId) {
@@ -478,8 +479,6 @@ const allocate = (container: DeserializeContainer, typeId: number, value: unknow
478479
return value;
479480
case TypeIds.Constant:
480481
return _constants[value as Constants];
481-
case TypeIds.Number:
482-
return value as number;
483482
case TypeIds.Array:
484483
// Wrap while inflating so we can handle cyclic references
485484
return wrapDeserializerProxy(container as any, value as any[]);
@@ -541,6 +540,8 @@ const allocate = (container: DeserializeContainer, typeId: number, value: unknow
541540
(value as any[])[0] as TypeIds,
542541
(value as any[])[1]
543542
);
543+
(value as any[])[0] = TypeIds.Plain;
544+
(value as any[])[1] = storeValue;
544545
return getOrCreateStore(storeValue, StoreFlags.NONE, container as DomContainer);
545546
case TypeIds.URLSearchParams:
546547
return new URLSearchParams(value as string);
@@ -554,8 +555,6 @@ const allocate = (container: DeserializeContainer, typeId: number, value: unknow
554555
return new Set();
555556
case TypeIds.Map:
556557
return new Map();
557-
case TypeIds.String:
558-
return value as string;
559558
case TypeIds.Promise:
560559
let resolve!: (value: any) => void;
561560
let reject!: (error: any) => void;
@@ -1096,7 +1095,7 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
10961095
} else if (value === Number.MIN_SAFE_INTEGER) {
10971096
output(TypeIds.Constant, Constants.MinSafeInt);
10981097
} else {
1099-
output(TypeIds.Number, value);
1098+
output(TypeIds.Plain, value);
11001099
}
11011100
} else if (typeof value === 'object') {
11021101
if (value === EMPTY_ARRAY) {
@@ -1118,7 +1117,7 @@ async function serialize(serializationContext: SerializationContext): Promise<vo
11181117
output(TypeIds.Constant, Constants.EmptyString);
11191118
} else {
11201119
if (!outputAsRootRef(value)) {
1121-
output(TypeIds.String, value);
1120+
output(TypeIds.Plain, value);
11221121
}
11231122
}
11241123
} else if (typeof value === 'undefined') {
@@ -1640,11 +1639,11 @@ export function _deserialize(rawStateData: string | null, element?: unknown): un
16401639
}
16411640

16421641
function deserializeData(container: DeserializeContainer, typeId: number, value: unknown) {
1643-
if (typeId === undefined) {
1642+
if (typeId === TypeIds.Plain) {
16441643
return value;
16451644
}
16461645
const propValue = allocate(container, typeId, value);
1647-
if (typeId >= TypeIds.Error) {
1646+
if (needsInflation(typeId)) {
16481647
inflate(container, propValue, typeId, value);
16491648
}
16501649
return propValue;
@@ -1925,24 +1924,24 @@ export const canSerialize = (value: any, seen: WeakSet<any> = new WeakSet()): bo
19251924
const QRL_RUNTIME_CHUNK = 'mock-chunk';
19261925

19271926
export const enum TypeIds {
1927+
Plain,
19281928
RootRef,
19291929
ForwardRef,
1930-
ForwardRefs,
19311930
/** Undefined, null, true, false, NaN, +Inf, -Inf, Slot, Fragment */
19321931
Constant,
1933-
Number,
1934-
String,
19351932
Array,
1933+
Object,
19361934
URL,
19371935
Date,
19381936
Regex,
19391937
VNode,
1938+
/// ^ single-digit types ^
19401939
RefVNode,
19411940
BigInt,
19421941
URLSearchParams,
1943-
/// All values below need inflation because they may have reference cycles
1942+
ForwardRefs,
1943+
/// All types below will be inflate()d
19441944
Error,
1945-
Object,
19461945
Promise,
19471946
Set,
19481947
Map,
@@ -1964,22 +1963,21 @@ export const enum TypeIds {
19641963
SubscriptionData,
19651964
}
19661965
export const _typeIdNames = [
1966+
'Plain',
19671967
'RootRef',
19681968
'ForwardRef',
1969-
'ForwardRefs',
19701969
'Constant',
1971-
'Number',
1972-
'String',
19731970
'Array',
1971+
'Object',
19741972
'URL',
19751973
'Date',
19761974
'Regex',
19771975
'VNode',
19781976
'RefVNode',
19791977
'BigInt',
19801978
'URLSearchParams',
1979+
'ForwardRefs',
19811980
'Error',
1982-
'Object',
19831981
'Promise',
19841982
'Set',
19851983
'Map',
@@ -2070,11 +2068,15 @@ export const dumpState = (
20702068
}
20712069
const key = state[i];
20722070
let value = state[++i];
2073-
if (key === undefined) {
2074-
hasRaw = true;
2075-
out.push(
2076-
`${RED}[raw${isObject(value) ? ` ${value.constructor.name}` : ''}]${RESET} ${printRaw(value, `${prefix} `)}`
2077-
);
2071+
if (key === TypeIds.Plain) {
2072+
const isRaw = typeof value !== 'number' && typeof value !== 'string';
2073+
if (isRaw) {
2074+
hasRaw = true;
2075+
}
2076+
const type = isRaw
2077+
? `[raw${isObject(value) ? ` ${value.constructor.name}` : ''}]`
2078+
: typeIdToName(key as TypeIds);
2079+
out.push(`${RED}${type}${RESET} ${printRaw(value, `${prefix} `)}`);
20782080
} else {
20792081
if (key === TypeIds.Constant) {
20802082
value = constantToName(value as Constants);

0 commit comments

Comments
 (0)