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

feat: cli option to not encode/decode default values #48

Closed
wants to merge 1 commit into from
Closed
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
10 changes: 5 additions & 5 deletions packages/protons-benchmark/src/protons/bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export namespace Foo {
export const codec = (): Codec<Foo> => {
return message<Foo>({
1: { name: 'baz', codec: uint32 }
})
}, false)
}

export const encode = (obj: Foo): Uint8ArrayList => {
Expand All @@ -33,7 +33,7 @@ export namespace Bar {
export const codec = (): Codec<Bar> => {
return message<Bar>({
1: { name: 'tmp', codec: Foo.codec() }
})
}, false)
}

export const encode = (obj: Bar): Uint8ArrayList => {
Expand Down Expand Up @@ -68,7 +68,7 @@ export namespace Yo {
export const codec = (): Codec<Yo> => {
return message<Yo>({
1: { name: 'lol', codec: FOO.codec(), repeats: true }
})
}, false)
}

export const encode = (obj: Yo): Uint8ArrayList => {
Expand All @@ -90,7 +90,7 @@ export namespace Lol {
return message<Lol>({
1: { name: 'lol', codec: string },
2: { name: 'b', codec: Bar.codec() }
})
}, false)
}

export const encode = (obj: Lol): Uint8ArrayList => {
Expand All @@ -116,7 +116,7 @@ export namespace Test {
3: { name: 'hello', codec: uint32 },
1: { name: 'foo', codec: string },
7: { name: 'payload', codec: bytes }
})
}, false)
}

export const encode = (obj: Test): Uint8ArrayList => {
Expand Down
16 changes: 14 additions & 2 deletions packages/protons-runtime/src/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,32 @@ export interface EncodingLengthFunction<T> {
(value: T): number
}

export interface DefaultValueFunction<T> {
(): T
}

export interface IsDefaultValueFunction<T> {
(value: T): boolean
}

export interface Codec<T> {
name: string
type: CODEC_TYPES
encode: EncodeFunction<T>
decode: DecodeFunction<T>
encodingLength: EncodingLengthFunction<T>
defaultValue: DefaultValueFunction<T>
isDefaultValue: IsDefaultValueFunction<T>
}

export function createCodec <T> (name: string, type: CODEC_TYPES, encode: EncodeFunction<T>, decode: DecodeFunction<T>, encodingLength: EncodingLengthFunction<T>): Codec<T> {
export function createCodec<T> (name: string, type: CODEC_TYPES, encode: EncodeFunction<T>, decode: DecodeFunction<T>, encodingLength: EncodingLengthFunction<T>, defaultValue: DefaultValueFunction<T>, isDefaultValue: IsDefaultValueFunction<T>): Codec<T> {
return {
name,
type,
encode,
decode,
encodingLength
encodingLength,
defaultValue,
isDefaultValue
}
}
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/bool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<boolean> = function boolEncodingLength () {
Expand All @@ -13,4 +13,8 @@ const decode: DecodeFunction<boolean> = function boolDecode (buffer, offset) {
return buffer.get(offset) > 0
}

export const bool = createCodec('bool', CODEC_TYPES.VARINT, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<boolean> = () => false

const isDefaultValue: IsDefaultValueFunction<boolean> = (value: boolean) => !value

export const bool = createCodec('bool', CODEC_TYPES.VARINT, encode, decode, encodingLength, defaultValue, isDefaultValue)
9 changes: 7 additions & 2 deletions packages/protons-runtime/src/codecs/bytes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@

import { Uint8ArrayList } from 'uint8arraylist'
import { unsigned } from 'uint8-varint'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'
import { equals } from 'uint8arrays/equals'

const encodingLength: EncodingLengthFunction<Uint8Array> = function bytesEncodingLength (val) {
const len = val.byteLength
Expand All @@ -23,4 +24,8 @@ const decode: DecodeFunction<Uint8Array> = function bytesDecode (buf, offset) {
return buf.subarray(offset, offset + byteLength)
}

export const bytes = createCodec('bytes', CODEC_TYPES.LENGTH_DELIMITED, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<Uint8Array> = () => new Uint8Array()

const isDefaultValue: IsDefaultValueFunction<Uint8Array> = (value: Uint8Array) => equals(value, defaultValue())

export const bytes = createCodec('bytes', CODEC_TYPES.LENGTH_DELIMITED, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/double.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uint8ArrayList } from 'uint8arraylist'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<number> = function doubleEncodingLength () {
Expand All @@ -17,4 +17,8 @@ const decode: DecodeFunction<number> = function doubleDecode (buf, offset) {
return buf.getFloat64(offset, true)
}

export const double = createCodec('double', CODEC_TYPES.BIT64, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<number> = () => 0

const isDefaultValue: IsDefaultValueFunction<number> = (value) => value === defaultValue()

export const double = createCodec('double', CODEC_TYPES.BIT64, encode, decode, encodingLength, defaultValue, isDefaultValue)
12 changes: 10 additions & 2 deletions packages/protons-runtime/src/codecs/enum.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import { unsigned } from 'uint8-varint'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction, Codec } from '../codec.js'
import { allocUnsafe } from '../utils/alloc.js'

Expand Down Expand Up @@ -43,6 +43,14 @@ export function enumeration <T> (v: any): Codec<T> {
return v[strValue]
}

const defaultValue: DefaultValueFunction<number | string> = function defaultValue () {
return Object.values(v)[0] as (string | number)
}

const isDefaultValue: IsDefaultValueFunction<number| string> = function isDefaultValue (val) {
return val === defaultValue()
}

// @ts-expect-error yeah yeah
return createCodec('enum', CODEC_TYPES.VARINT, encode, decode, encodingLength)
return createCodec('enum', CODEC_TYPES.VARINT, encode, decode, encodingLength, defaultValue, isDefaultValue)
}
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/fixed32.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uint8ArrayList } from 'uint8arraylist'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<number> = function fixed32EncodingLength () {
Expand All @@ -17,4 +17,8 @@ const decode: DecodeFunction<number> = function fixed32Decode (buf, offset) {
return buf.getInt32(offset, true)
}

export const fixed32 = createCodec('fixed32', CODEC_TYPES.BIT32, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<number> = () => 0

const isDefaultValue: IsDefaultValueFunction<number> = (val) => val === defaultValue()

export const fixed32 = createCodec('fixed32', CODEC_TYPES.BIT32, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/fixed64.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uint8ArrayList } from 'uint8arraylist'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<bigint> = function int64EncodingLength (val) {
Expand All @@ -17,4 +17,8 @@ const decode: DecodeFunction<bigint> = function int64Decode (buf, offset) {
return buf.getBigInt64(offset, true)
}

export const fixed64 = createCodec('fixed64', CODEC_TYPES.BIT64, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<bigint> = () => 0n

const isDefaultValue: IsDefaultValueFunction<bigint> = (val) => val === defaultValue()

export const fixed64 = createCodec('fixed64', CODEC_TYPES.BIT64, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/float.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uint8ArrayList } from 'uint8arraylist'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<number> = function floatEncodingLength () {
Expand All @@ -17,4 +17,8 @@ const decode: DecodeFunction<number> = function floatDecode (buf, offset) {
return buf.getFloat32(offset, true)
}

export const float = createCodec('float', CODEC_TYPES.BIT32, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<number> = () => 0

const isDefaultValue: IsDefaultValueFunction<number> = (val) => val === defaultValue()

export const float = createCodec('float', CODEC_TYPES.BIT32, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/int32.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { signed } from 'uint8-varint'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<number> = function int32EncodingLength (val) {
Expand All @@ -20,4 +20,8 @@ const decode: DecodeFunction<number> = function int32Decode (buf, offset) {
return signed.decode(buf, offset) | 0
}

export const int32 = createCodec('int32', CODEC_TYPES.VARINT, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<number> = () => 0

const isDefaultValue: IsDefaultValueFunction<number> = (val) => val === defaultValue()

export const int32 = createCodec('int32', CODEC_TYPES.VARINT, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/int64.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { signed } from 'uint8-varint/big'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<bigint> = function int64EncodingLength (val) {
Expand All @@ -20,4 +20,8 @@ const decode: DecodeFunction<bigint> = function int64Decode (buf, offset) {
return signed.decode(buf, offset) | 0n
}

export const int64 = createCodec('int64', CODEC_TYPES.VARINT, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<bigint> = () => 0n

const isDefaultValue: IsDefaultValueFunction<bigint> = (val) => val === defaultValue()

export const int64 = createCodec('int64', CODEC_TYPES.VARINT, encode, decode, encodingLength, defaultValue, isDefaultValue)
55 changes: 48 additions & 7 deletions packages/protons-runtime/src/codecs/message.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import { unsigned } from 'uint8-varint'
import { createCodec, CODEC_TYPES } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction, Codec } from '../codec.js'
import type {
Codec,
DecodeFunction,
DefaultValueFunction,
EncodeFunction,
EncodingLengthFunction,
IsDefaultValueFunction
} from '../codec.js'
import { CODEC_TYPES, createCodec } from '../codec.js'
import { Uint8ArrayList } from 'uint8arraylist'
import type { FieldDefs, FieldDef } from '../index.js'
import type { FieldDef, FieldDefs } from '../index.js'
import { allocUnsafe } from '../utils/alloc.js'

export interface Factory<A, T> {
new (obj: A): T
}

export function message <T> (fieldDefs: FieldDefs): Codec<T> {
export function message <T> (fieldDefs: FieldDefs, noDefaultOnWire: boolean): Codec<T> {
const encodingLength: EncodingLengthFunction<T> = function messageEncodingLength (val: Record<string, any>) {
let length = 0

for (const fieldDef of Object.values(fieldDefs)) {
length += fieldDef.codec.encodingLength(val[fieldDef.name])
if (!noDefaultOnWire || !fieldDef.codec.isDefaultValue(val[fieldDef.name])) {
length += fieldDef.codec.encodingLength(val[fieldDef.name])
}
}

return unsigned.encodingLength(length) + length
Expand All @@ -32,6 +41,10 @@ export function message <T> (fieldDefs: FieldDefs): Codec<T> {
throw new Error(`Non optional field "${fieldDef.name}" was ${value === null ? 'null' : 'undefined'}`)
}

if (noDefaultOnWire && fieldDef.codec.isDefaultValue(value)) {
return
}

const key = (fieldNumber << 3) | fieldDef.codec.type
const prefix = allocUnsafe(unsigned.encodingLength(key))
unsigned.encode(key, prefix)
Expand Down Expand Up @@ -116,15 +129,43 @@ export function message <T> (fieldDefs: FieldDefs): Codec<T> {
offset += fieldLength
}

// make sure repeated fields have an array if not set
for (const fieldDef of Object.values(fieldDefs)) {
// make sure repeated fields have an array if not set
if (fieldDef.repeats === true && fields[fieldDef.name] == null) {
fields[fieldDef.name] = []
} else if (noDefaultOnWire && fields[fieldDef.name] == null) {
// apply default values if not set
fields[fieldDef.name] = fieldDef.codec.defaultValue()
}
}

return fields
}

return createCodec('message', CODEC_TYPES.LENGTH_DELIMITED, encode, decode, encodingLength)
// Need to initialized sub messages as default value.
const defaultValue: DefaultValueFunction<T> = function defaultValue () {
const defaultValue: any = {}

for (const fieldDef of Object.values(fieldDefs)) {
if (fieldDef.codec.type === CODEC_TYPES.LENGTH_DELIMITED) {
defaultValue[fieldDef.name] = fieldDef.codec.defaultValue()
}
}

return defaultValue
}

const isDefaultValue: IsDefaultValueFunction<T> = function isDefaultValue (val) {
for (const fieldDef of Object.values(fieldDefs)) {
// @ts-expect-error
const fieldValue = val[fieldDef.name]

if (!fieldDef.codec.isDefaultValue(fieldValue)) {
return false
}
}
return true
}

return createCodec('message', CODEC_TYPES.LENGTH_DELIMITED, encode, decode, encodingLength, defaultValue, isDefaultValue)
}
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/sfixed32.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uint8ArrayList } from 'uint8arraylist'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<number> = function sfixed32EncodingLength () {
Expand All @@ -17,4 +17,8 @@ const decode: DecodeFunction<number> = function sfixed32Decode (buf, offset) {
return buf.getInt32(offset, true)
}

export const sfixed32 = createCodec('sfixed32', CODEC_TYPES.BIT32, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<number> = () => 0

const isDefaultValue: IsDefaultValueFunction<number> = (val) => val === defaultValue()

export const sfixed32 = createCodec('sfixed32', CODEC_TYPES.BIT32, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/sfixed64.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Uint8ArrayList } from 'uint8arraylist'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<bigint> = function sfixed64EncodingLength () {
Expand All @@ -17,4 +17,8 @@ const decode: DecodeFunction<bigint> = function sfixed64Decode (buf, offset) {
return buf.getBigInt64(offset, true)
}

export const sfixed64 = createCodec('sfixed64', CODEC_TYPES.BIT64, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<bigint> = () => 0n

const isDefaultValue: IsDefaultValueFunction<bigint> = (val) => val === defaultValue()

export const sfixed64 = createCodec('sfixed64', CODEC_TYPES.BIT64, encode, decode, encodingLength, defaultValue, isDefaultValue)
8 changes: 6 additions & 2 deletions packages/protons-runtime/src/codecs/sint32.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { zigzag } from 'uint8-varint'
import { createCodec, CODEC_TYPES } from '../codec.js'
import { createCodec, CODEC_TYPES, DefaultValueFunction, IsDefaultValueFunction } from '../codec.js'
import type { DecodeFunction, EncodeFunction, EncodingLengthFunction } from '../codec.js'

const encodingLength: EncodingLengthFunction<number> = function sint32EncodingLength (val) {
Expand All @@ -14,4 +14,8 @@ const decode: DecodeFunction<number> = function svarintDecode (buf, offset) {
return zigzag.decode(buf, offset)
}

export const sint32 = createCodec('sint32', CODEC_TYPES.VARINT, encode, decode, encodingLength)
const defaultValue: DefaultValueFunction<number> = () => 0

const isDefaultValue: IsDefaultValueFunction<number> = (val) => val === defaultValue()

export const sint32 = createCodec('sint32', CODEC_TYPES.VARINT, encode, decode, encodingLength, defaultValue, isDefaultValue)
Loading