goencode

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2026 License: MIT Imports: 1 Imported by: 0

README

Build Status Go Report Card GoDoc

goencode

goencode

Generic encoding interfaces for Go with composable codecs.

Features

  • Generic Codec[S, T] and StreamCodec[S] function-type structs with compile-time type safety
  • Type-safe codec composition via PipeCodec (e.g., JSON → gzip, JSON → base64)
  • Standalone compression codecs (gzip, flate, snappy, zstd, brotli) as Codec[[]byte, []byte]
  • Stream support via io.Reader/io.Writer for memory-efficient pipelines
  • Atomic file I/O with temp file + rename
  • Zero dependencies in the core module

Installation

go get github.com/foomo/goencode

Core Types

// Function types
type Encoder[S, T any] func(s S) (T, error)
type Decoder[S, T any] func(t T, s *S) error

// Byte-oriented codec bundle
type Codec[S, T any] struct {
    Encode Encoder[S, T]
    Decode Decoder[S, T]
}

// Stream function types
type StreamEncoder[S any] func(w io.Writer, s S) error
type StreamDecoder[S any] func(r io.Reader, s *S) error

// Stream-oriented codec bundle
type StreamCodec[S any] struct {
    Encode StreamEncoder[S]
    Decode StreamDecoder[S]
}

// Composition
func PipeCodec[A, B, C any](first Codec[A, B], second Codec[B, C]) Codec[A, C]

Quick Start

// Basic JSON encode/decode
c := json.NewCodec[User]()           // Codec[User, []byte]
b, err := c.Encode(User{Name: "Alice", Age: 30})
var u User
err = c.Decode(b, &u)

// Compose with compression via PipeCodec
c := goencode.PipeCodec(json.NewCodec[User](), gzip.NewCodec())

// Chain multiple codecs: JSON → base64
c := goencode.PipeCodec(json.NewCodec[User](), base64.NewCodec())

// Add atomic file persistence
fc := file.NewCodec(goencode.PipeCodec(json.NewCodec[User](), gzip.NewCodec()))
err := fc.Encode("/tmp/user.json.gz", user)

Available Codecs

Category Packages
Serialization json/v1, xml, gob, asn1, csv, pem
Binary encoding base64, base32, hex, ascii85
Compression wrappers gzip, flate, snappy*, zstd*, brotli*
Utility file (atomic read/write)
Alternatives json/v2* (go-json-experiment), yaml/v2*, yaml/v3*, yaml/v4*, toml*, msgpack/tinylib*, msgpack/vmihailenco*

* Submodule with separate go.mod — requires its own go get.

All codecs are safe for concurrent use.

Benchmarks

Measured with go test -bench=. -benchmem on arm64 (darwin). Results vary by hardware — use these as relative comparisons between codecs.

Codec Encode (ns/op) Encode (B/op) Encode (allocs/op) Decode (ns/op) Decode (B/op) Decode (allocs/op)
ascii85 428.9 640 1 870.7 4272 4
asn1 1192 1080 18 563.4 552 4
base32 211.4 768 1 1173 1248 2
base64 201.8 640 1 172.0 480 1
brotli 121976 2165234 27 10356 65288 26
csv 420.2 4162 2 715.6 4840 22
file 174240 1578 12 49555 1760 14
flate 52005 816445 20 8301 42648 17
gob 965.1 2305 18 5836 8320 167
gzip 43324 816214 20 8392 43352 18
hex 308.3 1024 1 309.1 480 1
json/v1 359.3 576 1 467.7 552 3
json/v2 428.7 576 1 456.9 552 3
msgpack/tinylib 91.76 704 3 88.63 552 4
msgpack/vmihailenco 187.2 688 3 279.0 608 6
pem 634.6 3312 10 1448 580 4
snappy 734.2 1281 2 2196 1352 10
toml 2276 6012 47 5761 5400 56
xml 1527 5131 9 4868 4032 57
yaml/v2 7265 7200 42 8109 9944 137
yaml/v3 7507 9312 42 9013 12256 151
yaml/v4 7299 8640 41 9155 12528 151
zstd 350425 21432510 57 8075 25092 37

How to Contribute

Contributions are welcome! Please read the contributing guide.

Contributors

License

Distributed under MIT License, please see the license file within the code for more details.

Made with ♥ foomo by bestbytes

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Codec

type Codec[S, T any] struct {
	Encode Encoder[S, T]
	Decode Decoder[S, T]
}

Codec bundles an Encoder and Decoder for S ↔ T round-trips.

func PipeCodec added in v0.2.0

func PipeCodec[A, B, C any](first Codec[A, B], second Codec[B, C]) Codec[A, C]

PipeCodec chains two codecs: Codec[A,B] + Codec[B,C] → Codec[A,C].

Example
package main

import (
	"fmt"
	"strconv"

	goencode "github.com/foomo/goencode"
)

func main() {
	intStr := goencode.Codec[int, string]{
		Encode: func(i int) (string, error) {
			return strconv.Itoa(i), nil
		},
		Decode: func(s string, i *int) error {
			v, err := strconv.Atoi(s)
			if err != nil {
				return err
			}

			*i = v

			return nil
		},
	}
	strBytes := goencode.Codec[string, []byte]{
		Encode: func(s string) ([]byte, error) {
			return []byte(s), nil
		},
		Decode: func(b []byte, s *string) error {
			*s = string(b)
			return nil
		},
	}

	piped := goencode.PipeCodec(intStr, strBytes)

	encoded, err := piped.Encode(42)
	if err != nil {
		fmt.Printf("Encode failed: %v\n", err)
		return
	}

	var decoded int
	if err := piped.Decode(encoded, &decoded); err != nil {
		fmt.Printf("Decode failed: %v\n", err)
		return
	}

	fmt.Printf("Encoded: %s\n", string(encoded))
	fmt.Printf("Decoded: %d\n", decoded)
}
Output:
Encoded: 42
Decoded: 42

type Decoder

type Decoder[S, T any] func(t T, s *S) error

Decoder decodes target T back into source S.

func PipeDecoder added in v0.2.0

func PipeDecoder[A, B, C any](first Decoder[A, B], second Decoder[B, C]) Decoder[A, C]

PipeDecoder chains two decoders in reverse: decodes C → B via second, then B → A via first.

Example
package main

import (
	"fmt"
	"strconv"

	goencode "github.com/foomo/goencode"
)

func main() {
	strToInt := goencode.Decoder[int, string](func(s string, i *int) error {
		v, err := strconv.Atoi(s)
		if err != nil {
			return err
		}

		*i = v

		return nil
	})
	bytesToStr := goencode.Decoder[string, []byte](func(b []byte, s *string) error {
		*s = string(b)
		return nil
	})

	piped := goencode.PipeDecoder(strToInt, bytesToStr)

	var got int
	if err := piped([]byte("42"), &got); err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Result: %d\n", got)
}
Output:
Result: 42

type Encoder

type Encoder[S, T any] func(s S) (T, error)

Encoder encodes source S to target T.

func PipeEncoder added in v0.2.0

func PipeEncoder[A, B, C any](first Encoder[A, B], second Encoder[B, C]) Encoder[A, C]

PipeEncoder chains two encoders: A → B → C.

Example
package main

import (
	"fmt"
	"strconv"

	goencode "github.com/foomo/goencode"
)

func main() {
	intToStr := goencode.Encoder[int, string](func(i int) (string, error) {
		return strconv.Itoa(i), nil
	})
	strToBytes := goencode.Encoder[string, []byte](func(s string) ([]byte, error) {
		return []byte(s), nil
	})

	piped := goencode.PipeEncoder(intToStr, strToBytes)

	got, err := piped(42)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Result: %s\n", string(got))
}
Output:
Result: 42

type StreamCodec

type StreamCodec[S any] struct {
	Encode StreamEncoder[S]
	Decode StreamDecoder[S]
}

StreamCodec bundles streaming encode/decode for S.

type StreamDecoder

type StreamDecoder[S any] func(r io.Reader, s *S) error

StreamDecoder decodes S from an io.Reader.

type StreamEncoder

type StreamEncoder[S any] func(w io.Writer, s S) error

StreamEncoder encodes S into an io.Writer.

Directories

Path Synopsis
internal
json
v1

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL