npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

worst-json-stringify

v1.0.1

Published

Extremely fast JSON stringifying *almost* without iteration.

Downloads

9

Readme

worst-json-stringify

Extremely fast JSON stringifying almost without iteration.

Usage

pnpm i worst-json-stringify
const stringifyUser = makeStringifier({
	type: "struct",
	children: {
		firstName: { type: "string" },
		lastName: { type: "string", optional: true },
		age: { type: "number" },
		obj: {
			type: "object",
			types: [{ type: "number" }, { type: "boolean" }],
		},
	},
});

console.log("Function Source:", stringifyUser.toString());

console.log(
	"User String:",
	stringifyUser({
		firstName: "John\u1782",
		age: Infinity,
		obj: {
			one: true,
			two: NaN,
		},
	})
);

Benchmarks

Fastest Possible Options

cpu: Intel(R) Core(TM) i7-8086K CPU @ 4.00GHz
runtime: node v17.0.1 (x64-win32)

benchmark                 time (avg)             (min … max)       p75       p99      p995
------------------------------------------------------------ -----------------------------
• large string
------------------------------------------------------------ -----------------------------
native                 24.44 µs/iter    (20.8 µs … 610.8 µs)   22.4 µs   59.3 µs  102.2 µs
fast-json-stringify    23.89 µs/iter     (19.4 µs … 2.08 ms)   21.4 µs   61.5 µs    120 µs
slow-json-stringify     1.62 µs/iter     (1.38 µs … 3.01 µs)   1.68 µs   3.01 µs   3.01 µs
worst-json-stringify  702.44 ns/iter   (636.18 ns … 1.29 µs) 709.02 ns   1.29 µs   1.29 µs
protobuf               10.52 µs/iter      (4.2 µs … 4.76 ms)    8.5 µs   58.6 µs   81.5 µs

summary for large string
  worst-json-stringify
   2.31x faster than slow-json-stringify
   14.98x faster than protobuf
   34x faster than fast-json-stringify
   34.79x faster than native

Safest Possible Options

cpu: Intel(R) Core(TM) i7-8086K CPU @ 4.00GHz
runtime: node v17.0.1 (x64-win32)

benchmark                 time (avg)             (min … max)       p75       p99      p995
------------------------------------------------------------ -----------------------------
• large string
------------------------------------------------------------ -----------------------------
native                 23.12 µs/iter    (20.8 µs … 237.2 µs)   21.9 µs   41.6 µs   74.7 µs
fast-json-stringify    21.89 µs/iter      (19.3 µs … 766 µs)   20.6 µs   40.3 µs     81 µs
slow-json-stringify     7.39 µs/iter    (6.42 µs … 10.18 µs)   7.84 µs  10.18 µs  10.18 µs
worst-json-stringify    5.12 µs/iter      (4.8 µs … 6.73 µs)   5.06 µs   6.73 µs   6.73 µs
protobuf               10.17 µs/iter      (4.2 µs … 3.53 ms)    8.8 µs   54.2 µs   76.1 µs

summary for large string
  worst-json-stringify
   1.44x faster than slow-json-stringify
   1.99x faster than protobuf
   4.28x faster than fast-json-stringify
   4.52x faster than native

Explaination

Think of it like a macro. It takes in a schema and returns a function that is specifically made to stringify objects that match that schema. The function it generates--assuming it doesn't contain an object or array type--will stringify the object without iterating over any properties. That makes it incredibly fast at what it does.

Types

Currently the following types are supported:

  • [x] struct
  • [x] object
  • [x] tuple
  • [x] array
  • [x] string
  • [x] boolean
  • [x] number

struct, and tuple have children. The direct children of a tuple can't be optional while the children of a struct can.

object and array don't have children, they have a set of types that can be in them. If there are no types specified, it can be any type. The fewer types that are specified--excluding zero of course--the faster it is to stringify.

object and array are the only types that can't be stringified without iterating. This is because of the nature of them having unknown lengths. That's why the struct and tuple types exist.

Options

By default, the stringifier will escape all strings. This will reduce performance drastically, but it's much safer, especially if you're using large strings. If you are absolutely sure you don't need to escape strings, you can pass escape: false to the string type on the schema.

JSON does not support Infinity and NaN from IEEE 754, so by default the stringifier will convert Infinity and NaN to null. This will reduce performance slightly and lose data, but the result will be compatible with the specification and JSON.parse. To allow Infinity and NaN to be stringified "properly" you will need to use custom a replacer.

const stringifyUser = makeStringifier(
	{
		type: "tuple",
		children: [
			{ type: "number" },
			{ type: "number", ieee754: true },
			{ type: "bigint" },
			{ type: "regex" },
		],
	},
	{
		replacer: (accessor, type, path, iterator) => {
			// Add BigInt support.
			if (!iterator && type.type === "bigint") {
				return `${accessor}+"n"`;
			}
			// Add Infinity and NaN support.
			if (!iterator && type.type === "number" && type.ieee754) {
				return accessor;
			}
			// Add RegExp support.
			if (type.type === "regex") {
				if (iterator) {
					return `if(${accessor} instanceof RegExp)`;
				} else {
					return `${accessor}.toString()`;
				}
			}
		},
	}
);

console.log(stringifyUser.toString());
console.log(stringifyUser([Infinity, NaN, 2n, /a/g]));