Giter VIP home page Giter VIP logo

ikeapack's Introduction

IkeaPack GoDoc Go Report Card codecov

Named IkeaPack because it compacts structs in a very compact and packed manner, and if you don't know how to reassemble it, it may just look like a random blob of parts. Just like ikea products!

(If anyone from ikea doesn't like me using their name, just reach out to me and I'll change it, no copyright/trademark infringement intended.)

This is a packed struct serializer that is mostly meant for a private project but was released as it may be useful to someone else.

Originally this package was made as an extension to binary.Read and binary.Write, but I soon found those functions didn't match my use case as they offered no support for strings nor compression.

Features

  • Caches types for faster calls to the same type
  • Compression support
  • Tread safe (the calls are, reading to the value is not)
  • Easy to implement in other languages
  • Supported types:
    • uint8 (and byte) up to uint64
    • int8 up to int64
    • float32 and float64
    • string
    • anything implementing the Packer/Unpacker interfaces
    • slices
    • structs

Format

  • All primitives are stored in big endian format
  • All slices are stored with a uint32 prefix indicating their length
  • Strings are stored with a uint32 prefix indicating their length
  • Compression blocks are stored using deflate (level 9) with a uint32 prefixing the size of the compressed data blob

Note about int/uint

The types int and uint are not supported because their actual sizes depend on the compiler architecture.
Instead, be explicit and use int32/int64/uint32/uint64.

Note about nil values

This lib will initialize nil values when Unpacking/Unmarshalling, however it will panic if a nil value is attempted to be Packed/Serialized. This is due to the fact that there is no safe way to distinguish nil pointers from zero values.
Be safe, don't try to serialize nil values.

Include in your project

import "github.com/ikkerens/ikeapack"

Usage

package main

import (
	"bytes"
	"log"

	"github.com/ikkerens/ikeapack"
)

type myBlob struct {
	A uint64  // all fields have to be exported
	B []byte  `ikea:"compress:9"` // this field will be packed and compressed, with flate level 5
	C subBlob // If you omit the level `ikea:"compress"`, level 9 will be assumed.
	D int32
}

type subBlob struct {
	D string
}

func main() {
	b := new(bytes.Buffer)
	blob := &myBlob{A: 1, B: []byte{1, 2, 3, 4}, C: subBlob{D: "test message"}}

	// Pack
	if err := ikea.Pack(b, blob); err != nil { // Write does not need a pointer, but it is recommended
		log.Fatalln(err)
	}

	// Unpack
	newBlob := new(myBlob)
	if err := ikea.Unpack(b, newBlob); err != nil { // Read *needs* a pointer, or it will panic
		log.Fatalln(err)
	}

	log.Printf("Successfully unpacked: %+v", newBlob)
}

Benchmarks

These benchmarks can be found in alecthomas's go serialization benchmarks. While not all benchmarks are included since not all dependencies could resolve, these give a good overview of the performance of this lib vs the others.
Note that this library does not have a focus on being the fastest in any way, as this was made to cover a specific use-case. But it does strive to be as fast as it can be.

These benchmarks were executed on a Dell laptop with an i7-8550U cpu and 16GB of ram.

BenchmarkIkeaMarshal-8                           3000000               505 ns/op              72 B/op          8 allocs/op
BenchmarkIkeaUnmarshal-8                         2000000               670 ns/op             160 B/op         11 allocs/op
BenchmarkJsonMarshal-8                            500000              3785 ns/op            1224 B/op          9 allocs/op
BenchmarkJsonUnmarshal-8                          300000              4412 ns/op             464 B/op          7 allocs/op
BenchmarkEasyJsonMarshal-8                       1000000              1559 ns/op             784 B/op          5 allocs/op
BenchmarkEasyJsonUnmarshal-8                     1000000              1363 ns/op             160 B/op          4 allocs/op
BenchmarkBsonMarshal-8                           1000000              1433 ns/op             392 B/op         10 allocs/op
BenchmarkBsonUnmarshal-8                         1000000              1928 ns/op             244 B/op         19 allocs/op
BenchmarkGobMarshal-8                            2000000               930 ns/op              48 B/op          2 allocs/op
BenchmarkGobUnmarshal-8                          2000000               943 ns/op             112 B/op          3 allocs/op
BenchmarkXdrMarshal-8                            1000000              1740 ns/op             456 B/op         21 allocs/op
BenchmarkXdrUnmarshal-8                          1000000              1449 ns/op             240 B/op         11 allocs/op
BenchmarkUgorjiCodecMsgpackMarshal-8             1000000              1141 ns/op             561 B/op          6 allocs/op
BenchmarkUgorjiCodecMsgpackUnmarshal-8           1000000              1349 ns/op             449 B/op          6 allocs/op
BenchmarkSerealMarshal-8                          500000              2680 ns/op             912 B/op         21 allocs/op
BenchmarkSerealUnmarshal-8                        500000              2943 ns/op            1008 B/op         34 allocs/op
BenchmarkBinaryMarshal-8                         1000000              1427 ns/op             334 B/op         20 allocs/op
BenchmarkBinaryUnmarshal-8                       1000000              1554 ns/op             336 B/op         22 allocs/op
BenchmarkHproseMarshal-8                         2000000               971 ns/op             479 B/op          8 allocs/op
BenchmarkHproseUnmarshal-8                       1000000              1140 ns/op             320 B/op         10 allocs/op
BenchmarkGoAvroMarshal-8                          500000              2561 ns/op            1030 B/op         31 allocs/op
BenchmarkGoAvroUnmarshal-8                        200000              6346 ns/op            3437 B/op         87 allocs/op
BenchmarkGoAvro2TextMarshal-8                     500000              2875 ns/op            1326 B/op         20 allocs/op
BenchmarkGoAvro2TextUnmarshal-8                   500000              2690 ns/op             807 B/op         34 allocs/op
BenchmarkGoAvro2BinaryMarshal-8                  2000000               916 ns/op             510 B/op         11 allocs/op
BenchmarkGoAvro2BinaryUnmarshal-8                2000000               979 ns/op             576 B/op         13 allocs/op
BenchmarkProtobufMarshal-8                       2000000               984 ns/op             200 B/op          7 allocs/op
BenchmarkProtobufUnmarshal-8                     2000000               827 ns/op             192 B/op         10 allocs/op

Below you will find some of the benchmarks that do not rely on Go's reflection at runtime, which creates a significant performance boost, but for diligence it's worth mentioning here regardless.

BenchmarkMsgpMarshal-8                          10000000               178 ns/op             128 B/op          1 allocs/op
BenchmarkMsgpUnmarshal-8                         5000000               340 ns/op             112 B/op          3 allocs/op
BenchmarkFlatBuffersMarshal-8                    5000000               341 ns/op               0 B/op          0 allocs/op
BenchmarkFlatBuffersUnmarshal-8                  5000000               249 ns/op             112 B/op          3 allocs/op
BenchmarkCapNProtoMarshal-8                      3000000               483 ns/op              56 B/op          2 allocs/op
BenchmarkCapNProtoUnmarshal-8                    3000000               438 ns/op             200 B/op          6 allocs/op
BenchmarkCapNProto2Marshal-8                     2000000               723 ns/op             244 B/op          3 allocs/op
BenchmarkCapNProto2Unmarshal-8                   1000000              1019 ns/op             320 B/op          6 allocs/op
BenchmarkGoprotobufMarshal-8                     3000000               396 ns/op              96 B/op          2 allocs/op
BenchmarkGoprotobufUnmarshal-8                   2000000               614 ns/op             200 B/op         10 allocs/op
BenchmarkGogoprotobufMarshal-8                  10000000               162 ns/op              64 B/op          1 allocs/op
BenchmarkGogoprotobufUnmarshal-8                10000000               223 ns/op              96 B/op          3 allocs/op
BenchmarkColferMarshal-8                        10000000               133 ns/op              64 B/op          1 allocs/op
BenchmarkColferUnmarshal-8                      10000000               187 ns/op             112 B/op          3 allocs/op
BenchmarkGencodeMarshal-8                       10000000               179 ns/op              80 B/op          2 allocs/op
BenchmarkGencodeUnmarshal-8                     10000000               202 ns/op             112 B/op          3 allocs/op
BenchmarkGencodeUnsafeMarshal-8                 20000000               102 ns/op              48 B/op          1 allocs/op
BenchmarkGencodeUnsafeUnmarshal-8               10000000               146 ns/op              96 B/op          3 allocs/op
BenchmarkXDR2Marshal-8                          10000000               162 ns/op              64 B/op          1 allocs/op
BenchmarkXDR2Unmarshal-8                        10000000               136 ns/op              32 B/op          2 allocs/op

ikeapack's People

Contributors

ikkerens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ikeapack's Issues

add ignore option

When saving a nil slice, it would be nice to have an ignore flag such that ikeapack initializes the empty slice so that I do not have to, instead of getting a nil panic. If that is not the current case.

better errors

Currently the error can be seen as:

panic: Attempting to marshal nil value

goroutine 1 [running]:
github.com/ikkerens/ikeapack.(*pointerWrapper).writeVariable(0xc00000e6a0, 0x67f320, 0xc0000929c0, 0x5e0060, 0xc000452100, 0x196, 0x608ea0, 0x5d2913)
        /home/anders/go/pkg/mod/github.com/ikkerens/[email protected]/pointer.go:41 +0x1ef
github.com/ikkerens/ikeapack.handleVariableWriter(0x67f320, 0xc0000929c0, 0x67f480, 0xc00000e6a0, 0x5e0060, 0xc000452100, 0x196, 0x5ff2a0, 0xc0000bf530)
        /home/anders/go/pkg/mod/github.com/ikkerens/[email protected]/variablehandlers.go:38 +0x1ea
github.com/ikkerens/ikeapack.(*variableStructReadWriter).writeVariable(0xc00000e7e0, 0x67f320, 0xc0000929c0, 0x6263e0, 0xc000452100, 0x199, 0x6786b4, 0xf9)
        /home/anders/go/pkg/mod/github.com/ikkerens/[email protected]/struct.go:194 +0xe1
github.com/ikkerens/ikeapack.handleVariableWriter(0x67f320, 0xc0000929c0, 0x67f520, 0xc00000e7e0, 0x6263e0, 0xc000452100, 0x199, 0x4b492d, 0xc0000202a0)
        /home/anders/go/pkg/mod/github.com/ikkerens/[email protected]/variablehandlers.go:38 +0x1ea
github.com/ikkerens/ikeapack.Pack(0x67f320, 0xc0000929c0, 0x5ff2a0, 0xc000452100, 0x67f720, 0xc0000929f0)
        /home/anders/go/pkg/mod/github.com/ikkerens/[email protected]/public.go:27 +0x121
main.saveToFile(0xc000452100, 0xc0000929c0)
        /home/anders/dev/onitamago/cmd/abacus/work.go:162 +0x2c3
main.startWork(0x1, 0xa, 0xc0000bf8b8, 0x1, 0x1)
        /home/anders/dev/onitamago/cmd/abacus/work.go:84 +0x50a
main.glob..func3(0xc0000a0420, 0x0, 0xc0000a0420)
        /home/anders/dev/onitamago/cmd/abacus/work.go:69 +0x33b
github.com/urfave/cli.HandleAction(0x5f4ac0, 0x648188, 0xc0000a0420, 0xc000072300, 0x0)
        /home/anders/go/pkg/mod/github.com/urfave/[email protected]/app.go:490 +0xc8
github.com/urfave/cli.Command.Run(0x63b609, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64645f, 0x36, 0x0, ...)
        /home/anders/go/pkg/mod/github.com/urfave/[email protected]/command.go:210 +0x996
github.com/urfave/cli.(*App).Run(0xc00009c680, 0xc000012100, 0x4, 0x4, 0x0, 0x0)
        /home/anders/go/pkg/mod/github.com/urfave/[email protected]/app.go:255 +0x6af
main.main()
        /home/anders/dev/onitamago/cmd/abacus/app.go:39 +0x3bd

Which yields no information about what is actually wrong here. Does the reflect package allow to print the field name or type?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.