fatsify核心功能示例测试!!!

This commit is contained in:
2025-09-21 14:50:41 +08:00
commit 9145aea047
1958 changed files with 230098 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
'use strict'
const { spawn } = require('child_process')
const cliSelect = require('cli-select')
const simpleGit = require('simple-git')
const git = simpleGit(process.cwd())
const COMMAND = 'npm run bench'
const DEFAULT_BRANCH = 'master'
const PERCENT_THRESHOLD = 5
const greyColor = '\x1b[30m'
const redColor = '\x1b[31m'
const greenColor = '\x1b[32m'
const resetColor = '\x1b[0m'
async function selectBranchName (message, branches) {
console.log(message)
const result = await cliSelect({
type: 'list',
name: 'branch',
values: branches
})
console.log(result.value)
return result.value
}
async function executeCommandOnBranch (command, branch) {
console.log(`${greyColor}Checking out "${branch}"${resetColor}`)
await git.checkout(branch)
console.log(`${greyColor}Execute "${command}"${resetColor}`)
const childProcess = spawn(command, { stdio: 'pipe', shell: true })
let result = ''
childProcess.stdout.on('data', (data) => {
process.stdout.write(data.toString())
result += data.toString()
})
await new Promise(resolve => childProcess.on('close', resolve))
console.log()
return parseBenchmarksStdout(result)
}
function parseBenchmarksStdout (text) {
const results = []
const lines = text.split('\n')
for (const line of lines) {
const match = /^(.+?)(\.*) x (.+) ops\/sec .*$/.exec(line)
if (match !== null) {
results.push({
name: match[1],
alignedName: match[1] + match[2],
result: parseInt(match[3].split(',').join(''))
})
}
}
return results
}
function compareResults (featureBranch, mainBranch) {
for (const { name, alignedName, result: mainBranchResult } of mainBranch) {
const featureBranchBenchmark = featureBranch.find(result => result.name === name)
if (featureBranchBenchmark) {
const featureBranchResult = featureBranchBenchmark.result
const percent = (featureBranchResult - mainBranchResult) * 100 / mainBranchResult
const roundedPercent = Math.round(percent * 100) / 100
const percentString = roundedPercent > 0 ? `+${roundedPercent}%` : `${roundedPercent}%`
const message = alignedName + percentString.padStart(7, '.')
if (roundedPercent > PERCENT_THRESHOLD) {
console.log(`${greenColor}${message}${resetColor}`)
} else if (roundedPercent < -PERCENT_THRESHOLD) {
console.log(`${redColor}${message}${resetColor}`)
} else {
console.log(message)
}
}
}
}
(async function () {
const branches = await git.branch()
const currentBranch = branches.branches[branches.current]
let featureBranch = null
let mainBranch = null
if (process.argv[2] === '--ci') {
featureBranch = currentBranch.name
mainBranch = DEFAULT_BRANCH
} else {
featureBranch = await selectBranchName('Select the branch you want to compare (feature branch):', branches.all)
mainBranch = await selectBranchName('Select the branch you want to compare with (main branch):', branches.all)
}
try {
const featureBranchResult = await executeCommandOnBranch(COMMAND, featureBranch)
const mainBranchResult = await executeCommandOnBranch(COMMAND, mainBranch)
compareResults(featureBranchResult, mainBranchResult)
} catch (error) {
console.error('Switch to origin branch due to an error', error.message)
}
await git.checkout(currentBranch.commit)
await git.checkout(currentBranch.name)
console.log(`${greyColor}Back to ${currentBranch.name} ${currentBranch.commit}${resetColor}`)
})()

View File

@@ -0,0 +1,280 @@
'use strict'
const benchmark = require('benchmark')
const suite = new benchmark.Suite()
const STR_LEN = 1e4
const LARGE_ARRAY_SIZE = 2e4
const MULTI_ARRAY_LENGTH = 1e3
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
}
}
}
const schemaCJS = {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'number',
minimum: 0
}
}
}
const schemaAJVJTD = {
properties: {
firstName: {
type: 'string'
},
lastName: {
type: 'string',
nullable: true
},
age: {
type: 'uint8'
}
}
}
const arraySchema = {
title: 'array schema',
type: 'array',
items: schema
}
const arraySchemaCJS = {
title: 'array schema',
type: 'array',
items: schemaCJS
}
const arraySchemaAJVJTD = {
elements: schemaAJVJTD
}
const dateFormatSchema = {
description: 'Date of birth',
type: 'string',
format: 'date'
}
const dateFormatSchemaCJS = {
description: 'Date of birth',
type: 'string',
format: 'date'
}
const obj = {
firstName: 'Matteo',
lastName: 'Collina',
age: 32
}
const date = new Date()
const multiArray = new Array(MULTI_ARRAY_LENGTH)
const largeArray = new Array(LARGE_ARRAY_SIZE)
const CJS = require('compile-json-stringify')
const CJSStringify = CJS(schemaCJS)
const CJSStringifyArray = CJS(arraySchemaCJS)
const CJSStringifyDate = CJS(dateFormatSchemaCJS)
const CJSStringifyString = CJS({ type: 'string' })
const FJS = require('..')
const stringify = FJS(schema)
const stringifyArrayDefault = FJS(arraySchema)
const stringifyArrayJSONStringify = FJS(arraySchema, {
largeArrayMechanism: 'json-stringify'
})
const stringifyDate = FJS(dateFormatSchema)
const stringifyString = FJS({ type: 'string' })
let str = ''
const Ajv = require('ajv/dist/jtd')
const ajv = new Ajv()
const ajvSerialize = ajv.compileSerializer(schemaAJVJTD)
const ajvSerializeArray = ajv.compileSerializer(arraySchemaAJVJTD)
const ajvSerializeString = ajv.compileSerializer({ type: 'string' })
const getRandomString = (length) => {
if (!Number.isInteger(length)) {
throw new Error('Expected integer length')
}
const validCharacters = 'abcdefghijklmnopqrstuvwxyz'
const nValidCharacters = 26
let result = ''
for (let i = 0; i < length; ++i) {
result += validCharacters[Math.floor(Math.random() * nValidCharacters)]
}
return result[0].toUpperCase() + result.slice(1)
}
for (let i = 0; i < STR_LEN; i++) {
largeArray[i] = {
firstName: getRandomString(8),
lastName: getRandomString(6),
age: Math.ceil(Math.random() * 99)
}
str += i
if (i % 100 === 0) {
str += '"'
}
}
for (let i = STR_LEN; i < LARGE_ARRAY_SIZE; ++i) {
largeArray[i] = {
firstName: getRandomString(10),
lastName: getRandomString(4),
age: Math.ceil(Math.random() * 99)
}
}
Number(str)
for (let i = 0; i < MULTI_ARRAY_LENGTH; i++) {
multiArray[i] = obj
}
suite.add('FJS creation', function () {
FJS(schema)
})
suite.add('CJS creation', function () {
CJS(schemaCJS)
})
suite.add('AJV Serialize creation', function () {
ajv.compileSerializer(schemaAJVJTD)
})
suite.add('JSON.stringify array', function () {
JSON.stringify(multiArray)
})
suite.add('fast-json-stringify array default', function () {
stringifyArrayDefault(multiArray)
})
suite.add('fast-json-stringify array json-stringify', function () {
stringifyArrayJSONStringify(multiArray)
})
suite.add('compile-json-stringify array', function () {
CJSStringifyArray(multiArray)
})
suite.add('AJV Serialize array', function () {
ajvSerializeArray(multiArray)
})
suite.add('JSON.stringify large array', function () {
JSON.stringify(largeArray)
})
suite.add('fast-json-stringify large array default', function () {
stringifyArrayDefault(largeArray)
})
suite.add('fast-json-stringify large array json-stringify', function () {
stringifyArrayJSONStringify(largeArray)
})
suite.add('compile-json-stringify large array', function () {
CJSStringifyArray(largeArray)
})
suite.add('AJV Serialize large array', function () {
ajvSerializeArray(largeArray)
})
suite.add('JSON.stringify long string', function () {
JSON.stringify(str)
})
suite.add('fast-json-stringify long string', function () {
stringifyString(str)
})
suite.add('compile-json-stringify long string', function () {
CJSStringifyString(str)
})
suite.add('AJV Serialize long string', function () {
ajvSerializeString(str)
})
suite.add('JSON.stringify short string', function () {
JSON.stringify('hello world')
})
suite.add('fast-json-stringify short string', function () {
stringifyString('hello world')
})
suite.add('compile-json-stringify short string', function () {
CJSStringifyString('hello world')
})
suite.add('AJV Serialize short string', function () {
ajvSerializeString('hello world')
})
suite.add('JSON.stringify obj', function () {
JSON.stringify(obj)
})
suite.add('fast-json-stringify obj', function () {
stringify(obj)
})
suite.add('compile-json-stringify obj', function () {
CJSStringify(obj)
})
suite.add('AJV Serialize obj', function () {
ajvSerialize(obj)
})
suite.add('JSON stringify date', function () {
JSON.stringify(date)
})
suite.add('fast-json-stringify date format', function () {
stringifyDate(date)
})
suite.add('compile-json-stringify date format', function () {
CJSStringifyDate(date)
})
suite.on('cycle', cycle)
suite.run()
function cycle (e) {
console.log(e.target.toString())
}

View File

@@ -0,0 +1,21 @@
'use strict'
const { workerData: benchmark, parentPort } = require('worker_threads')
const Benchmark = require('benchmark')
Benchmark.options.minSamples = 100
const suite = Benchmark.Suite()
const FJS = require('..')
const stringify = FJS(benchmark.schema)
suite
.add(benchmark.name, () => {
stringify(benchmark.input)
})
.on('cycle', (event) => {
parentPort.postMessage(String(event.target))
})
.on('complete', () => {})
.run()

391
node_modules/fast-json-stringify/benchmark/bench.js generated vendored Normal file
View File

@@ -0,0 +1,391 @@
'use strict'
const path = require('path')
const { Worker } = require('worker_threads')
const BENCH_THREAD_PATH = path.join(__dirname, 'bench-thread.js')
const LONG_STRING_LENGTH = 1e4
const SHORT_ARRAY_SIZE = 1e3
const shortArrayOfNumbers = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfIntegers = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfShortStrings = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfLongStrings = new Array(SHORT_ARRAY_SIZE)
const shortArrayOfMultiObject = new Array(SHORT_ARRAY_SIZE)
function getRandomInt (max) {
return Math.floor(Math.random() * max)
}
let longSimpleString = ''
for (let i = 0; i < LONG_STRING_LENGTH; i++) {
longSimpleString += i
}
let longString = ''
for (let i = 0; i < LONG_STRING_LENGTH; i++) {
longString += i
if (i % 100 === 0) {
longString += '"'
}
}
for (let i = 0; i < SHORT_ARRAY_SIZE; i++) {
shortArrayOfNumbers[i] = getRandomInt(1000)
shortArrayOfIntegers[i] = getRandomInt(1000)
shortArrayOfShortStrings[i] = 'hello world'
shortArrayOfLongStrings[i] = longString
shortArrayOfMultiObject[i] = { s: 'hello world', n: 42, b: true }
}
const benchmarks = [
{
name: 'short string',
schema: {
type: 'string'
},
input: 'hello world'
},
{
name: 'unsafe short string',
schema: {
type: 'string',
format: 'unsafe'
},
input: 'hello world'
},
{
name: 'short string with double quote',
schema: {
type: 'string'
},
input: 'hello " world'
},
{
name: 'long string without double quotes',
schema: {
type: 'string'
},
input: longSimpleString
},
{
name: 'unsafe long string without double quotes',
schema: {
type: 'string',
format: 'unsafe'
},
input: longSimpleString
},
{
name: 'long string',
schema: {
type: 'string'
},
input: longString
},
{
name: 'unsafe long string',
schema: {
type: 'string',
format: 'unsafe'
},
input: longString
},
{
name: 'number',
schema: {
type: 'number'
},
input: 42
},
{
name: 'integer',
schema: {
type: 'integer'
},
input: 42
},
{
name: 'formatted date-time',
schema: {
type: 'string',
format: 'date-time'
},
input: new Date()
},
{
name: 'formatted date',
schema: {
type: 'string',
format: 'date'
},
input: new Date()
},
{
name: 'formatted time',
schema: {
type: 'string',
format: 'time'
},
input: new Date()
},
{
name: 'short array of numbers',
schema: {
type: 'array',
items: { type: 'number' }
},
input: shortArrayOfNumbers
},
{
name: 'short array of integers',
schema: {
type: 'array',
items: { type: 'integer' }
},
input: shortArrayOfIntegers
},
{
name: 'short array of short strings',
schema: {
type: 'array',
items: { type: 'string' }
},
input: shortArrayOfShortStrings
},
{
name: 'short array of long strings',
schema: {
type: 'array',
items: { type: 'string' }
},
input: shortArrayOfShortStrings
},
{
name: 'short array of objects with properties of different types',
schema: {
type: 'array',
items: {
type: 'object',
properties: {
s: { type: 'string' },
n: { type: 'number' },
b: { type: 'boolean' }
}
}
},
input: shortArrayOfMultiObject
},
{
name: 'object with number property',
schema: {
type: 'object',
properties: {
a: { type: 'number' }
}
},
input: { a: 42 }
},
{
name: 'object with integer property',
schema: {
type: 'object',
properties: {
a: { type: 'integer' }
}
},
input: { a: 42 }
},
{
name: 'object with short string property',
schema: {
type: 'object',
properties: {
a: { type: 'string' }
}
},
input: { a: 'hello world' }
},
{
name: 'object with long string property',
schema: {
type: 'object',
properties: {
a: { type: 'string' }
}
},
input: { a: longString }
},
{
name: 'object with properties of different types',
schema: {
type: 'object',
properties: {
s1: { type: 'string' },
n1: { type: 'number' },
b1: { type: 'boolean' },
s2: { type: 'string' },
n2: { type: 'number' },
b2: { type: 'boolean' },
s3: { type: 'string' },
n3: { type: 'number' },
b3: { type: 'boolean' },
s4: { type: 'string' },
n4: { type: 'number' },
b4: { type: 'boolean' },
s5: { type: 'string' },
n5: { type: 'number' },
b5: { type: 'boolean' }
}
},
input: {
s1: 'hello world',
n1: 42,
b1: true,
s2: 'hello world',
n2: 42,
b2: true,
s3: 'hello world',
n3: 42,
b3: true,
s4: 'hello world',
n4: 42,
b4: true,
s5: 'hello world',
n5: 42,
b5: true
}
},
{
name: 'simple object',
schema: {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
}
}
},
input: { firstName: 'Max', lastName: 'Power', age: 22 }
},
{
name: 'simple object with required fields',
schema: {
title: 'Example Schema',
type: 'object',
properties: {
firstName: {
type: 'string'
},
lastName: {
type: ['string', 'null']
},
age: {
description: 'Age in years',
type: 'integer',
minimum: 0
}
},
required: ['firstName', 'lastName', 'age']
},
input: { firstName: 'Max', lastName: 'Power', age: 22 }
},
{
name: 'object with const string property',
schema: {
type: 'object',
properties: {
a: { const: 'const string' }
}
},
input: { a: 'const string' }
},
{
name: 'object with const number property',
schema: {
type: 'object',
properties: {
a: { const: 1 }
}
},
input: { a: 1 }
},
{
name: 'object with const bool property',
schema: {
type: 'object',
properties: {
a: { const: true }
}
},
input: { a: true }
},
{
name: 'object with const object property',
schema: {
type: 'object',
properties: {
foo: { const: { bar: 'baz' } }
}
},
input: {
foo: { bar: 'baz' }
}
},
{
name: 'object with const null property',
schema: {
type: 'object',
properties: {
foo: { const: null }
}
},
input: {
foo: null
}
}
]
async function runBenchmark (benchmark) {
const worker = new Worker(BENCH_THREAD_PATH, { workerData: benchmark })
return new Promise((resolve, reject) => {
let result = null
worker.on('error', reject)
worker.on('message', (benchResult) => {
result = benchResult
})
worker.on('exit', (code) => {
if (code === 0) {
resolve(result)
} else {
reject(new Error(`Worker stopped with exit code ${code}`))
}
})
})
}
async function runBenchmarks () {
let maxNameLength = 0
for (const benchmark of benchmarks) {
maxNameLength = Math.max(benchmark.name.length, maxNameLength)
}
for (const benchmark of benchmarks) {
benchmark.name = benchmark.name.padEnd(maxNameLength, '.')
const resultMessage = await runBenchmark(benchmark)
console.log(resultMessage)
}
}
runBenchmarks()