Jay aims to be the fastest production safe, serialization package written in Go as an alternative to JSON, Protocol Buffers, FlatBuffers, gob, MessagePack, Bebop, and mus-go with less setup required and no extra languages to learn.
Jay doesn't determine any types during runtime. Instead, the marshalling and unmarshalling functions are easily generated using the jay commandline tool.
- The fastest Go serialization and deserialization message format (some types use zero memory allocations).
- Encoded variables use less network overhead because no schema is added to the output when marshalling (unlike JSON).
- No custom language to learn. Jay uses Go's built in
astto find exported structs in your existing codebase. - No hassles between client and server. Generate the code once and share (via a Go module or copy-paste).
- Options to generate code optimized for:
- Processing more requests per second (higher CPU throughput) OR,
- Least network bandwidth used (10/100 networks).
- Doesn't introduce extra dependencies.
- Output could be compressed with
gzip,brotli,zstdor others. - Greater support for Go's built-in types.
- Need to regenerate methods when Go structs are modified using the jay commandline tool.
- Marshalled output is not human-readable.
- Only written for the Go language.
Unlike JSON, Jay's marshal and unmarshal functions are generated by a command line tool. This significantly increases execution speed during runtime by removing type reflection.
Most small structs with 10 fields can be serialized within 175 nanoseconds on old hardware (Intel T6400 @ 2.0 GHz with GM45 GPU).
Install the jay command line tool to generate marshal and unmarshal code.
go install github.com/speedyhoon/jay/generate/cmd/jay- Execute the command line tool and specify which
.gofile contains the exported structs. For example,type Car structis located withinmain.go.cd my_project_path jay main.go jaywill then generate methodsMarshalJ()andUnmarshalJ([]byte)injay.go(the default output file).- The new methods can then be used. For example,
package main
import "fmt"
type Car struct {
ID uint
Make, Model string
Auto, HasFuel bool
}
func main() {
car := Car{ID: 42, Make: "Ford", Model: "Escort", Auto: true, HasFuel: true}
var src []byte
src = car.MarshalJ()
fmt.Println(src)
var car2 Car
err := car2.UnmarshalJ(src)
fmt.Printf("err: %v, %+v", err, car2)
}Exported struct fields are concatenated together in binary format, delimited by each field's length.
High-throughput mode on a 64-bit system:
Auto & HasFuel, ID, Make, Model = 21 bytes
[192, 42,0,0,0,0,0,0,0, 4,F,o,r,d, 6,E,s,c,o,r,t]
Low-bandwidth mode: (unfinished)
Auto & HasFuel, ID, Make, Model = 15 bytes
[192, 1,42, 4,F,o,r,d, 6,E,s,c,o,r,t]
bool,[]boolbyte,[]bytecomplex64,complex128[]complex64,[]complex128float32,float64[]float32,[]float64int,int8,int16,int32,int64[]int,[]int8,[]int16,[]int32,[]int64rune,[]runestring(Currently limited to 255 byte lengths.)structand anonymous structstime.Time,time.Duration[]time.Time,[]time.Durationuint,uint8,uint16,uint32,uint64[]uint,[]uint8,[]uint16,[]uint32,[]uint64- Arrays for all the above types with lengths from 1 to 255, for example:
[5]string,[9]time.Durationand[33]time.Time(no fuzz testing yet)
Jay also supports imported types. If the exported type is in the same package or a subpackage then jay will automatically find it with:
cd my_project_directory
jay .To include an external type via the commandline, either:
- Specify the imported file:
jay myFile.go ../other_project/myImportedFile.go - Or include that directory:
jay myFile.go my_imported_directory
In order of priority:
- Expand fuzz testing and test coverage.
- Field tag options and documentation.
- Increase supported slice length from 255 items to 224 (16,777,215)
- Add tests for array types with more than 255 items.
- Performance benchmarks.
- Low-bandwidth mode.
- Aliased definition slice types like
[]floatwherefloatis defined astype float float32.
(Already supported:type float = float32,type floats = []float32&type floats []float32).
- Pointers (
*uint64)
R&D -- have a working prototype for*booland all integer types, undecided on*string, slices, arrays and struct slices. - Multi-dimensional arrays & slices? (
[][]string) - Maps? (
map[string]uint)
- Specify which struct types to process in a large project directory. E.g.:
-y Animal,settings.Config,engine.Specs - Aliased types like
type float = float32andtype floats = []float32. - Simple definition types for built-ins only like
type floats []float32. - Slices with lengths 0–255 (
[]string).
interface{}requires adding reflection and/or type switches at a significant performance and complexity cost. Either use a different package or write your own function to convert theinterface{}to one of the supported types (a[]bytemight be the fastest option).- Double nested pointers (
**string). - Generics (
any). - Functions.
Conceived in 2011 as an interesting way to improve network communication.
It wasn't until 2023 when I was using JSON (wasn't my choice by the way) to marshal and unmarshal data
between microcontrollers that it posed a significant bottleneck.
The aim was to process external messages within
a dozen microseconds to restore performance without upgrading the processor.
Clarification: The serialization format is Jay, whereas jay is the command line tool.
Jay (pronounced as just J) is a wordplay on JSON without the SON, since the schema information is chopped off 🪚 and it's not human-readable.
The name Jay also gives tribute to a 17-year-old netbook with a stuck j key on the keyboard. 🔁 😆 Every boot looks like:
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj