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

Introduce type ScalarValue and rename function scalarDefaultValue #711

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/protobuf-bench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ server would usually do.

| code generator | bundle size | minified | compressed |
|---------------------|------------------------:|-----------------------:|-------------------:|
| protobuf-es | 97,101 b | 41,461 b | 10,746 b |
| protobuf-es | 96,999 b | 41,438 b | 10,763 b |
| protobuf-javascript | 394,384 b | 288,654 b | 45,122 b |
19 changes: 14 additions & 5 deletions packages/protobuf/src/codegen-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
safeObjectProperty,
} from "./private/names.js";
import { getUnwrappedFieldType } from "./private/field-wrapper.js";
import { scalarDefaultValue } from "./private/scalars.js";
import { scalarZeroValue } from "./private/scalars.js";
import { reifyWkt } from "./private/reify-wkt.js";
import type {
DescEnum,
Expand All @@ -30,7 +30,8 @@ import type {
DescOneof,
DescService,
} from "./descriptor-set.js";
import { LongType, ScalarType } from "./field.js";
import type { ScalarValue } from "./scalar.js";
import { LongType, ScalarType } from "./scalar.js";

interface CodegenInfo {
/**
Expand All @@ -53,7 +54,14 @@ interface CodegenInfo {
field: DescField | DescExtension,
) => ScalarType | undefined;
readonly wktSourceFiles: readonly string[];
/**
* @deprecated please use scalarZeroValue instead
*/
readonly scalarDefaultValue: (type: ScalarType, longType: LongType) => any; // eslint-disable-line @typescript-eslint/no-explicit-any
readonly scalarZeroValue: <T extends ScalarType, L extends LongType>(
type: T,
longType: L,
) => ScalarValue<T, L>;
/**
* @deprecated please use reifyWkt from @bufbuild/protoplugin/ecmascript instead
*/
Expand Down Expand Up @@ -98,7 +106,8 @@ export const codegenInfo: CodegenInfo = {
localName,
reifyWkt,
getUnwrappedFieldType,
scalarDefaultValue,
scalarDefaultValue: scalarZeroValue,
scalarZeroValue,
safeIdentifier,
safeObjectProperty,
// prettier-ignore
Expand All @@ -119,8 +128,8 @@ export const codegenInfo: CodegenInfo = {
JsonObject: {typeOnly: true, privateImportPath: "./json-format.js", publicImportPath: packageName},
protoDouble: {typeOnly: false, privateImportPath: "./proto-double.js", publicImportPath: packageName},
protoInt64: {typeOnly: false, privateImportPath: "./proto-int64.js", publicImportPath: packageName},
ScalarType: {typeOnly: false, privateImportPath: "./field.js", publicImportPath: packageName},
LongType: {typeOnly: false, privateImportPath: "./field.js", publicImportPath: packageName},
ScalarType: {typeOnly: false, privateImportPath: "./scalar.js", publicImportPath: packageName},
LongType: {typeOnly: false, privateImportPath: "./scalar.js", publicImportPath: packageName},
MethodKind: {typeOnly: false, privateImportPath: "./service-type.js", publicImportPath: packageName},
MethodIdempotency: {typeOnly: false, privateImportPath: "./service-type.js", publicImportPath: packageName},
IMessageTypeRegistry: {typeOnly: true, privateImportPath: "./type-registry.js", publicImportPath: packageName},
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf/src/create-descriptor-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import type {
DescriptorSet,
DescService,
} from "./descriptor-set.js";
import { LongType, ScalarType } from "./field.js";
import { MethodIdempotency, MethodKind } from "./service-type.js";
import { fieldJsonName, findEnumSharedPrefix } from "./private/names.js";
import {
Expand All @@ -55,6 +54,7 @@ import {
import type { BinaryReadOptions, BinaryWriteOptions } from "./binary-format.js";
import type { FeatureResolverFn } from "./private/feature-set.js";
import { createFeatureResolver } from "./private/feature-set.js";
import { LongType, ScalarType } from "./scalar.js";

/**
* Create a DescriptorSet, a convenient interface for working with a set of
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf/src/create-registry-from-desc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { MessageType } from "./message-type.js";
import { proto3 } from "./proto3.js";
import { proto2 } from "./proto2.js";
import type { FieldInfo } from "./field.js";
import { ScalarType } from "./field.js";
import { ScalarType } from "./scalar.js";
import type { EnumType, EnumValueInfo } from "./enum.js";
import type {
IEnumTypeRegistry,
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf/src/descriptor-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type {
OneofDescriptorProto,
ServiceDescriptorProto,
} from "./google/protobuf/descriptor_pb.js";
import type { LongType, ScalarType } from "./field.js";
import { LongType, ScalarType } from "./scalar.js";
import type { MethodIdempotency, MethodKind } from "./service-type.js";
import type { MergedFeatureSet } from "./private/feature-set.js";

Expand Down
68 changes: 1 addition & 67 deletions packages/protobuf/src/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import type { EnumType } from "./enum.js";
import type { MessageType } from "./message-type.js";
import type { LongType, ScalarType } from "./scalar.js";

/* eslint-disable @typescript-eslint/no-explicit-any */

Expand Down Expand Up @@ -322,70 +323,3 @@ type fiPartialRules<T extends fiScalar|fiMap|fiEnum|fiMessage> = Omit<T, "jsonNa
| { readonly jsonName?: string; readonly repeated?: false; readonly packed?: false; readonly opt: true; readonly req?: false; readonly oneof?: undefined; default?: T["default"]; L?: LongType; delimited?: boolean; }
| { readonly jsonName?: string; readonly repeated?: boolean; readonly packed?: boolean; readonly opt?: false; readonly req?: boolean; readonly oneof?: undefined; default?: T["default"]; L?: LongType; delimited?: boolean; }
| { readonly jsonName?: string; readonly repeated?: false; readonly packed?: false; readonly opt?: false; readonly req?: false; readonly oneof: string; default?: T["default"]; L?: LongType; delimited?: boolean; });

/**
* Scalar value types. This is a subset of field types declared by protobuf
* enum google.protobuf.FieldDescriptorProto.Type The types GROUP and MESSAGE
* are omitted, but the numerical values are identical.
*/
export enum ScalarType {
// 0 is reserved for errors.
// Order is weird for historical reasons.
DOUBLE = 1,
FLOAT = 2,
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
// negative values are likely.
INT64 = 3,
UINT64 = 4,
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
// negative values are likely.
INT32 = 5,
FIXED64 = 6,
FIXED32 = 7,
BOOL = 8,
STRING = 9,
// Tag-delimited aggregate.
// Group type is deprecated and not supported in proto3. However, Proto3
// implementations should still be able to parse the group wire format and
// treat group fields as unknown fields.
// TYPE_GROUP = 10,
// TYPE_MESSAGE = 11, // Length-delimited aggregate.

// New in version 2.
BYTES = 12,
UINT32 = 13,
// TYPE_ENUM = 14,
SFIXED32 = 15,
SFIXED64 = 16,
SINT32 = 17, // Uses ZigZag encoding.
SINT64 = 18, // Uses ZigZag encoding.
}

/**
* JavaScript representation of fields with 64 bit integral types (int64, uint64,
* sint64, fixed64, sfixed64).
*
* This is a subset of google.protobuf.FieldOptions.JSType, which defines JS_NORMAL,
* JS_STRING, and JS_NUMBER. Protobuf-ES uses BigInt by default, but will use
* String if `[jstype = JS_STRING]` is specified.
*
* ```protobuf
* uint64 field_a = 1; // BigInt
* uint64 field_b = 2 [jstype = JS_NORMAL]; // BigInt
* uint64 field_b = 2 [jstype = JS_NUMBER]; // BigInt
* uint64 field_b = 2 [jstype = JS_STRING]; // String
* ```
*/
export enum LongType {
/**
* Use JavaScript BigInt.
*/
BIGINT = 0,

/**
* Use JavaScript String.
*
* Field option `[jstype = JS_STRING]`.
*/
STRING = 1,
}
2 changes: 1 addition & 1 deletion packages/protobuf/src/google/protobuf/wrappers_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/protobuf/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export type { AnyMessage, PartialMessage, PlainMessage } from "./message.js";

export type { FieldInfo } from "./field.js";
export type { FieldList } from "./field-list.js";
export { ScalarType, LongType } from "./field.js";
export { LongType, ScalarType } from "./scalar.js";
export type { ScalarValue } from "./scalar.js";

export type { MessageType } from "./message-type.js";
export type { EnumType, EnumValueInfo } from "./enum.js";
Expand Down
3 changes: 1 addition & 2 deletions packages/protobuf/src/json-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@

import type { Message } from "./message.js";
import type { MessageType } from "./message-type.js";
import type { ScalarType } from "./field.js";
import { LongType } from "./field.js";
import type { ScalarType, LongType } from "./scalar.js";
import type {
IExtensionRegistry,
IMessageTypeRegistry,
Expand Down
54 changes: 48 additions & 6 deletions packages/protobuf/src/private/binary-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import type {
} from "../binary-format.js";
import { type AnyMessage, Message } from "../message.js";
import type { FieldInfo } from "../field.js";
import { LongType, ScalarType } from "../field.js";
import { wrapField } from "./field-wrapper.js";
import { scalarDefaultValue, scalarTypeInfo } from "./scalars.js";
import type { ScalarValue } from "./scalars.js";
import { scalarZeroValue } from "./scalars.js";
import { assert } from "./assert.js";
import { isFieldSet } from "./reflect.js";
import type { ScalarValue } from "../scalar.js";
import { LongType, ScalarType } from "../scalar.js";

/* eslint-disable prefer-const,no-case-declarations,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return */

Expand Down Expand Up @@ -286,15 +286,15 @@ function readMapEntry(
}
}
if (key === undefined) {
key = scalarDefaultValue(field.K, LongType.BIGINT) as ScalarValue;
key = scalarZeroValue(field.K, LongType.BIGINT);
}
if (typeof key != "string" && typeof key != "number") {
key = key.toString();
}
if (val === undefined) {
switch (field.V.kind) {
case "scalar":
val = scalarDefaultValue(field.V.T, LongType.BIGINT) as ScalarValue;
val = scalarZeroValue(field.V.T, LongType.BIGINT);
break;
case "enum":
val = field.V.T.values[0].no;
Expand Down Expand Up @@ -472,7 +472,7 @@ function writeScalar(
value: unknown,
): void {
assert(value !== undefined);
let [wireType, method] = scalarTypeInfo(type, value);
let [wireType, method] = scalarTypeInfo(type);
(writer.tag(fieldNo, wireType)[method] as any)(value);
}

Expand All @@ -492,3 +492,45 @@ function writePacked(
}
writer.join();
}

/**
* Get information for writing a scalar value.
*
* Returns tuple:
* [0]: appropriate WireType
* [1]: name of the appropriate method of IBinaryWriter
* [2]: whether the given value is a default value for proto3 semantics
*
* If argument `value` is omitted, [2] is always false.
*/
// TODO replace call-sites writeScalar() and writePacked(), then remove
function scalarTypeInfo(
type: ScalarType,
): [
WireType,
Exclude<keyof IBinaryWriter, "tag" | "raw" | "fork" | "join" | "finish">,
] {
let wireType = WireType.Varint;
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- INT32, UINT32, SINT32 are covered by the defaults
switch (type) {
case ScalarType.BYTES:
case ScalarType.STRING:
wireType = WireType.LengthDelimited;
break;
case ScalarType.DOUBLE:
case ScalarType.FIXED64:
case ScalarType.SFIXED64:
wireType = WireType.Bit64;
break;
case ScalarType.FIXED32:
case ScalarType.SFIXED32:
case ScalarType.FLOAT:
wireType = WireType.Bit32;
break;
}
const method = ScalarType[type].toLowerCase() as Exclude<
keyof IBinaryWriter,
"tag" | "raw" | "fork" | "join" | "finish"
>;
return [wireType, method];
}
4 changes: 2 additions & 2 deletions packages/protobuf/src/private/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { scalarDefaultValue } from "./scalars.js";
import { scalarZeroValue } from "./scalars.js";
import type { Extension } from "../extension.js";
import type { AnyMessage, Message } from "../message.js";
import type { FieldInfo, OneofInfo, PartialFieldInfo } from "../field.js";
Expand Down Expand Up @@ -88,7 +88,7 @@ function initExtensionField(ext: Extension): unknown {
case "enum":
return field.T.values[0].no;
case "scalar":
return scalarDefaultValue(field.T, field.L);
return scalarZeroValue(field.T, field.L);
case "message":
// eslint-disable-next-line no-case-declarations
const T = field.T,
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf/src/private/field-normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

import type { FieldListSource } from "./field-list.js";
import type { FieldInfo } from "../field.js";
import { LongType, ScalarType } from "../field.js";
import { InternalOneofInfo } from "./field.js";
import { fieldJsonName, localFieldName } from "./names.js";
import { LongType, ScalarType } from "../scalar.js";

/**
* Convert a collection of field info to an array of normalized FieldInfo.
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf/src/private/field-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import { Message } from "../message.js";
import type { MessageType } from "../message-type.js";
import type { DescExtension, DescField } from "../descriptor-set.js";
import { ScalarType } from "../field.js";
import { ScalarType } from "../scalar.js";

/* eslint-disable @typescript-eslint/no-explicit-any -- unknown fields are represented with any */

Expand Down
9 changes: 5 additions & 4 deletions packages/protobuf/src/private/json-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type { AnyMessage } from "../message.js";
import { Message } from "../message.js";
import type { MessageType } from "../message-type.js";
import type { FieldInfo, OneofInfo } from "../field.js";
import { LongType, ScalarType } from "../field.js";
import { assert, assertFloat32, assertInt32, assertUInt32 } from "./assert.js";
import { protoInt64 } from "../proto-int64.js";
import { protoBase64 } from "../proto-base64.js";
Expand All @@ -42,8 +41,10 @@ import type {
} from "../binary-format.js";
import { clearField, isFieldSet } from "./reflect.js";
import { wrapField } from "./field-wrapper.js";
import type { ScalarValue } from "./scalars.js";
import { scalarDefaultValue, isScalarZeroValue } from "./scalars.js";
import { scalarZeroValue } from "./scalars.js";
import { isScalarZeroValue } from "./scalars.js";
import type { ScalarValue } from "../scalar.js";
import { LongType, ScalarType } from "../scalar.js";

/* eslint-disable no-case-declarations,@typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call */

Expand Down Expand Up @@ -475,7 +476,7 @@ function readScalar(
): ScalarValue | typeof tokenNull {
if (json === null) {
if (nullAsZeroValue) {
return scalarDefaultValue(type, longType) as ScalarValue;
return scalarZeroValue(type, longType);
}
return tokenNull;
}
Expand Down
5 changes: 2 additions & 3 deletions packages/protobuf/src/private/reflect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

import type { FieldInfo } from "../field.js";
import { isScalarZeroValue, scalarDefaultValue } from "./scalars.js";
import { isScalarZeroValue, scalarZeroValue } from "./scalars.js";

/**
* Returns true if the field is set.
Expand Down Expand Up @@ -70,9 +70,8 @@ export function clearField(
target[localName] = implicitPresence ? field.T.values[0].no : undefined;
break;
case "scalar":
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
target[localName] = implicitPresence
? scalarDefaultValue(field.T, field.L)
? scalarZeroValue(field.T, field.L)
: undefined;
break;
case "message":
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf/src/private/reify-wkt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

import type { DescField, DescMessage, DescOneof } from "../descriptor-set.js";
import { ScalarType } from "../field.js";
import { ScalarType } from "../scalar.js";

type DescWkt =
| {
Expand Down
Loading
Loading