Skip to content

Commit 16aadb5

Browse files
committed
🎉 feat: improve string operation
1 parent 4f06583 commit 16aadb5

File tree

5 files changed

+28
-24
lines changed

5 files changed

+28
-24
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 0.1.6 - 24 Apr 2025
2+
Improvement:
3+
- reduce instruction for string placement
4+
- inline regex test to each string
5+
16
# 0.1.5 - 22 Apr 2025
27
Improvement:
38
- add `schema.trusted` for string

benchmarks/utils.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { bench, run, barplot, summary, compact } from 'mitata'
1+
import { bench, run, barplot, summary, compact, do_not_optimize } from 'mitata'
22

33
import { createAccelerator } from '../src'
44
import { TypeCompiler } from '@sinclair/typebox/compiler'
@@ -28,7 +28,7 @@ export const benchmark = <T extends TAnySchema>(
2828
compact(() => {
2929
barplot(() => {
3030
summary(() => {
31-
bench('JSON Stingify', () => {
31+
bench('JSON Stringify', () => {
3232
return JSON.stringify(value)
3333
})
3434

@@ -40,13 +40,13 @@ export const benchmark = <T extends TAnySchema>(
4040
return encode(value)
4141
})
4242

43-
const validator = TypeCompiler.Compile(model)
43+
// const validator = TypeCompiler.Compile(model)
4444

45-
bench('JSON Accelerator w/ validation', () => {
46-
validator.Check(value)
45+
// bench('JSON Accelerator w/ validation', () => {
46+
// validator.Check(value)
4747

48-
return encode(value)
49-
})
48+
// return encode(value)
49+
// })
5050
})
5151
})
5252
})

bun.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"typescript": "^5.5.3",
1515
},
1616
"peerDependencies": {
17-
"@sinclair/typebox": "^0.34.31",
17+
"@sinclair/typebox": ">= 0.34.0",
1818
},
1919
"optionalPeers": [
2020
"@sinclair/typebox",

example/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ import { t } from 'elysia'
22
import { createAccelerator } from '../src/index'
33

44
const shape = t.Object({
5-
message: t.String({
6-
trusted: true
5+
name: t.String({
6+
// trusted: true
77
})
88
})
99

10+
const string = `hi awd`
11+
1012
const value = {
11-
message: 'a'
13+
name: string
1214
} satisfies typeof shape.static
1315

1416
const mirror = createAccelerator(shape)
1517

16-
console.log(mirror(value))
18+
console.log(JSON.parse(mirror(value)))

src/index.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ const isDateType = (schema: TAnySchema): boolean => {
156156
interface Instruction {
157157
array: number
158158
optional: number
159-
hasString: boolean
160159
properties: string[]
161160
/**
162161
* If unsafe character is found, how should the encoder handle it?
@@ -173,12 +172,15 @@ interface Instruction {
173172
definitions: Record<string, TAnySchema>
174173
}
175174

175+
// equivalent to /["\n\r\t\b\f\v]/
176+
const findEscapeSequence = /["\b\t\n\v\f\r\/]/
177+
176178
const SANITIZE = {
177179
auto: (property: string) =>
178-
`re.test(${property})?JSON.stringify(${property}):\`"$\{${property}}"\``,
180+
`${findEscapeSequence}.test(${property})?JSON.stringify(${property}).slice(1,-1):${property}`,
179181
manual: (property: string) => `${property}`,
180182
throw: (property: string) =>
181-
`re.test(${property})?(()=>{throw new Error("Property '${property}' contains invalid characters")})():${property}`
183+
`${findEscapeSequence}.test(${property})?(()=>{throw new Error("Property '${property}' contains invalid characters")})():${property}`
182184
} satisfies Record<Instruction['sanitize'], (v: string) => string>
183185

184186
const joinStringArray = (p: string) =>
@@ -260,8 +262,6 @@ const accelerate = (
260262

261263
switch (schema.type) {
262264
case 'string':
263-
if (!schema.const && !schema.trusted) instruction.hasString = true
264-
265265
// string operation would be repeated multiple time
266266
// it's fine to optimize it to the most optimized way
267267
if (
@@ -280,13 +280,13 @@ const accelerate = (
280280
sanitize = (v: string) =>
281281
`\`"$\{${SANITIZE['manual'](v)}}"\``
282282

283-
v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:${sanitize(property)}}`
283+
v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:\`"\${${sanitize(property)}}"\`}`
284284
} else {
285285
if (schema.const !== undefined)
286286
v = JSON.stringify(schema.const)
287287
else if (schema.trusted)
288288
v = `"\${${SANITIZE['manual'](property)}}"`
289-
else v = `\${${sanitize(property)}}`
289+
else v = `"\${${sanitize(property)}}"`
290290
}
291291
} else {
292292
// In this case quote is handle outside to improve performance
@@ -356,7 +356,8 @@ const accelerate = (
356356
const name = joinProperty(property, key)
357357
const hasShortName =
358358
schema.properties[key].type === 'object' &&
359-
!name.startsWith('ar')
359+
!name.startsWith('ar') &&
360+
Object.keys(schema.properties).length > 5
360361

361362
const i = instruction.properties.length
362363
if (hasShortName) instruction.properties.push(name)
@@ -487,9 +488,6 @@ const accelerate = (
487488

488489
let setup = ''
489490

490-
if (instruction.hasString)
491-
setup += `const re=/[\\b\\f\\n\\r\\t\\\\\\\\/"]/\n`
492-
493491
if (instruction.optional) {
494492
setup += 'let '
495493

@@ -528,7 +526,6 @@ export const createAccelerator = <T extends TAnySchema>(
528526
array: 0,
529527
optional: 0,
530528
properties: [],
531-
hasString: false,
532529
sanitize,
533530
definitions
534531
})

0 commit comments

Comments
 (0)