ljpack

package module
v0.0.0-...-5021c84 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2022 License: BSD-2-Clause Imports: 17 Imported by: 0

README

LuaJIT string.buffer encoding for Golang

Build Status PkgGoDev

Resources

Notes

The encoding format for LuaJIT string.buffer is not a formalized structure, the format could change at any time. Thus use this project with your own risk.

Supported:

  • nil, false, true, userdata NULL
  • int (int32), double (float64)
  • Empty table, hash, 0-based array, 1-based array
  • FFI int64, uint64, complex
  • string, interned string

Work in Progress:

  • lightud32, lightud64
  • Mixed table, Metatable dict entry

Not supported

  • Non-string value as hash keys

Features

Installation

ljpack supports 2 last Go versions and requires support for Go modules. So make sure to initialize a Go module:

go mod init github.com/my/repo

And then install ljpack:

go get github.com/fffonion/ljpack

Quickstart

import "github.com/fffonion/ljpack"

func ExampleMarshal() {
    type Item struct {
        Foo string
    }

    b, err := ljpack.Marshal(&Item{Foo: "bar"})
    if err != nil {
        panic(err)
    }

    var item Item
    err = ljpack.Unmarshal(b, &item)
    if err != nil {
        panic(err)
    }
    fmt.Println(item.Foo)
    // Output: bar
}

Credits

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Marshal

func Marshal(v interface{}) ([]byte, error)

Marshal returns the MessagePack encoding of v.

Example
package main

import (
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	type Item struct {
		Foo string
	}

	b, err := ljpack.Marshal(&Item{Foo: "bar"})
	if err != nil {
		panic(err)
	}

	var item Item
	err = ljpack.Unmarshal(b, &item)
	if err != nil {
		panic(err)
	}
	fmt.Println(item.Foo)
}
Output:

bar
Example (AsArray)
package main

import (
	"bytes"
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	type Item struct {
		_ljpack struct{} `ljpack:",as_array"`
		Foo     string
		Bar     string
	}

	var buf bytes.Buffer
	enc := ljpack.NewEncoder(&buf)
	err := enc.Encode(&Item{Foo: "foo", Bar: "bar"})
	if err != nil {
		panic(err)
	}

	dec := ljpack.NewDecoder(&buf)
	v, err := dec.DecodeInterface()
	if err != nil {
		panic(err)
	}
	fmt.Println(v)
}
Output:

[foo bar]
Example (EscapedNames)
package main

import (
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	og := map[string]interface{}{
		"something:special": uint(123),
		"hello, world":      "hello!",
	}
	raw, err := ljpack.Marshal(og)
	if err != nil {
		panic(err)
	}

	type Item struct {
		SomethingSpecial uint   `ljpack:"'something:special'"`
		HelloWorld       string `ljpack:"'hello, world'"`
	}
	var item Item
	if err := ljpack.Unmarshal(raw, &item); err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", item)
}
Output:

ljpack_test.Item{SomethingSpecial:0x7b, HelloWorld:"hello!"}
Example (Ignore_simple_zero_structs_when_tagged_with_omitempty)
package main

import (
	"fmt"

	"github.com/fffonion/ljpack"
)

type NullInt struct {
	Valid bool
	Int   int
}

func (i *NullInt) Set(j int) {
	i.Int = j
	i.Valid = true
}

func (i NullInt) IsZero() bool {
	return !i.Valid
}

func (i NullInt) MarshalMsgpack() ([]byte, error) {
	return ljpack.Marshal(i.Int)
}

func (i *NullInt) UnmarshalMsgpack(b []byte) error {
	if err := ljpack.Unmarshal(b, &i.Int); err != nil {
		return err
	}
	i.Valid = true
	return nil
}

type Secretive struct {
	Visible bool
	hidden  bool
}

type T struct {
	I NullInt `ljpack:",omitempty"`
	J NullInt

	S Secretive `ljpack:",omitempty"`
}

func main() {
	var t1 T
	raw, err := ljpack.Marshal(t1)
	if err != nil {
		panic(err)
	}
	var t2 T
	if err = ljpack.Unmarshal(raw, &t2); err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", t2)

	t2.I.Set(42)
	t2.S.hidden = true // won't be included because it is a hidden field
	raw, err = ljpack.Marshal(t2)
	if err != nil {
		panic(err)
	}
	var t3 T
	if err = ljpack.Unmarshal(raw, &t3); err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", t3)
}
Output:

ljpack_test.T{I:ljpack_test.NullInt{Valid:false, Int:0}, J:ljpack_test.NullInt{Valid:true, Int:0}, S:ljpack_test.Secretive{Visible:false, hidden:false}}
ljpack_test.T{I:ljpack_test.NullInt{Valid:true, Int:42}, J:ljpack_test.NullInt{Valid:true, Int:0}, S:ljpack_test.Secretive{Visible:false, hidden:false}}
Example (MapStringInterface)
package main

import (
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	in := map[string]interface{}{"foo": 1, "hello": "world"}
	b, err := ljpack.Marshal(in)
	if err != nil {
		panic(err)
	}

	var out map[string]interface{}
	err = ljpack.Unmarshal(b, &out)
	if err != nil {
		panic(err)
	}

	fmt.Println("foo =", out["foo"])
	fmt.Println("hello =", out["hello"])

}
Output:

foo = 1
hello = world
Example (OmitEmpty)
package main

import (
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	type Item struct {
		Foo string
		Bar string
	}

	item := &Item{
		Foo: "hello",
	}
	b, err := ljpack.Marshal(item)
	if err != nil {
		panic(err)
	}
	fmt.Printf("item: %q\n", b)

	type ItemOmitEmpty struct {
		_ljpack struct{} `ljpack:",omitempty"`
		Foo     string
		Bar     string
	}

	itemOmitEmpty := &ItemOmitEmpty{
		Foo: "hello",
	}
	b, err = ljpack.Marshal(itemOmitEmpty)
	if err != nil {
		panic(err)
	}
	fmt.Printf("item2: %q\n", b)

}
Output:

item: "\x82\xa3Foo\xa5hello\xa3Bar\xa0"
item2: "\x81\xa3Foo\xa5hello"

func PutDecoder

func PutDecoder(dec *Decoder)

func PutEncoder

func PutEncoder(enc *Encoder)

func Register

func Register(value interface{}, enc encoderFunc, dec decoderFunc)

Register registers encoder and decoder functions for a value. This is low level API and in most cases you should prefer implementing CustomEncoder/CustomDecoder or Marshaler/Unmarshaler interfaces.

func Unmarshal

func Unmarshal(data []byte, v interface{}) error

Unmarshal decodes the LJPack-encoded data and stores the result in the value pointed to by v.

func Version

func Version() string

Version is the current release version.

Types

type CustomDecoder

type CustomDecoder interface {
	DecodeLJpack(*Decoder) error
}

type CustomEncoder

type CustomEncoder interface {
	EncodeLJpack(*Encoder) error
}
Example
package main

import (
	"fmt"

	"github.com/fffonion/ljpack"
)

type customStruct struct {
	S string
	N int
}

var _ ljpack.CustomEncoder = (*customStruct)(nil)
var _ ljpack.CustomDecoder = (*customStruct)(nil)

func (s *customStruct) EncodeLJpack(enc *ljpack.Encoder) error {
	return enc.EncodeMulti(s.S, s.N)
}

func (s *customStruct) DecodeLJpack(dec *ljpack.Decoder) error {
	return dec.DecodeMulti(&s.S, &s.N)
}

func main() {
	b, err := ljpack.Marshal(&customStruct{S: "hello", N: 42})
	if err != nil {
		panic(err)
	}

	var v customStruct
	err = ljpack.Unmarshal(b, &v)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v", v)
}
Output:

ljpack_test.customStruct{S:"hello", N:42}

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

A Decoder reads and decodes LJPack values from an input stream.

func GetDecoder

func GetDecoder() *Decoder

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder returns a new decoder that reads from r.

The decoder introduces its own buffering and may read data from r beyond the requested ljpack values. Buffering can be disabled by passing a reader that implements io.ByteScanner interface.

func (*Decoder) Buffered

func (d *Decoder) Buffered() io.Reader

Buffered returns a reader of the data remaining in the Decoder's buffer. The reader is valid until the next call to Decode.

func (*Decoder) Decode

func (d *Decoder) Decode(v interface{}) error

func (*Decoder) DecodeArrayLen

func (d *Decoder) DecodeArrayLen() (int, error)

DecodeArrayLen decodes array length. Length is -1 when array is nil.

func (*Decoder) DecodeBool

func (d *Decoder) DecodeBool() (bool, error)

func (*Decoder) DecodeBytes

func (d *Decoder) DecodeBytes() ([]byte, error)

func (*Decoder) DecodeBytesLen

func (d *Decoder) DecodeBytesLen() (int, error)

func (*Decoder) DecodeDouble

func (d *Decoder) DecodeDouble() (float64, error)

DecodeDouble decodes ljpack float32/64 into Go float64.

func (*Decoder) DecodeDuration

func (d *Decoder) DecodeDuration() (time.Duration, error)

func (*Decoder) DecodeFFIComplex

func (d *Decoder) DecodeFFIComplex() (complex128, error)

func (*Decoder) DecodeFFIInt64

func (d *Decoder) DecodeFFIInt64() (int64, error)

DecodeFFIInt64 decodes ljpack int8/16/32/64 and uint8/16/32/64 into Go int64.

func (*Decoder) DecodeFFIUint64

func (d *Decoder) DecodeFFIUint64() (uint64, error)

DecodeFFIUint64 decodes ljpack int8/16/32/64 and uint8/16/32/64 into Go uint64.

func (*Decoder) DecodeInt

func (d *Decoder) DecodeInt() (int, error)

func (*Decoder) DecodeInt32

func (d *Decoder) DecodeInt32() (int32, error)

func (*Decoder) DecodeInterface

func (d *Decoder) DecodeInterface() (interface{}, error)

DecodeInterface decodes value into interface. It returns following types:

  • nil,
  • bool,
  • int8, int16, int32, int64,
  • uint8, uint16, uint32, uint64,
  • float32 and float64,
  • string,
  • []byte,
  • slices of any of the above,
  • maps of any of the above.

DecodeInterface should be used only when you don't know the type of value you are decoding. For example, if you are decoding number it is better to use DecodeInt64 for negative numbers and DecodeUint64 for positive numbers.

func (*Decoder) DecodeInterfaceLoose

func (d *Decoder) DecodeInterfaceLoose() (interface{}, error)

DecodeInterfaceLoose is like DecodeInterface except that:

  • int8, int16, and int32 are converted to int64,
  • uint8, uint16, and uint32 are converted to uint64,
  • float32 is converted to float64.
  • []byte is converted to string.

func (*Decoder) DecodeMap

func (d *Decoder) DecodeMap() (map[string]interface{}, error)

func (*Decoder) DecodeMapLen

func (d *Decoder) DecodeMapLen() (int, error)

DecodeMapLen decodes map length. Length is -1 when map is nil.

func (*Decoder) DecodeMulti

func (d *Decoder) DecodeMulti(v ...interface{}) error

func (*Decoder) DecodeNull

func (d *Decoder) DecodeNull() error

func (*Decoder) DecodeRaw

func (d *Decoder) DecodeRaw() (RawMessage, error)

func (*Decoder) DecodeSlice

func (d *Decoder) DecodeSlice() ([]interface{}, error)

func (*Decoder) DecodeString

func (d *Decoder) DecodeString() (string, error)

func (*Decoder) DecodeTypedMap

func (d *Decoder) DecodeTypedMap() (interface{}, error)

DecodeTypedMap decodes a typed map. Typed map is a map that has a fixed type for keys and values. Key and value types may be different.

func (*Decoder) DecodeUint

func (d *Decoder) DecodeUint() (uint, error)

func (*Decoder) DecodeUint32

func (d *Decoder) DecodeUint32() (uint32, error)

func (*Decoder) DecodeUntypedMap

func (d *Decoder) DecodeUntypedMap() (map[interface{}]interface{}, error)

func (*Decoder) DecodeValue

func (d *Decoder) DecodeValue(v reflect.Value) error

func (*Decoder) DisallowUnknownFields

func (d *Decoder) DisallowUnknownFields(on bool)

DisallowUnknownFields causes the Decoder to return an error when the destination is a struct and the input contains object keys which do not match any non-ignored, exported fields in the destination.

func (*Decoder) PeekCode

func (d *Decoder) PeekCode() (byte, error)

PeekCode returns the next LJPack code without advancing the reader. Subpackage ljpack/codes defines the list of available ljpcode.

func (*Decoder) Query

func (d *Decoder) Query(query string) ([]interface{}, error)

Query extracts data specified by the query from the ljpack stream skipping any other data. Query consists of map keys and array indexes separated with dot, e.g. key1.0.key2.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	b, err := ljpack.Marshal([]map[string]interface{}{
		{"id": 1, "attrs": map[string]interface{}{"phone": 12345}},
		{"id": 2, "attrs": map[string]interface{}{"phone": 54321}},
	})
	if err != nil {
		panic(err)
	}

	dec := ljpack.NewDecoder(bytes.NewBuffer(b))
	values, err := dec.Query("*.attrs.phone")
	if err != nil {
		panic(err)
	}
	fmt.Println("phones are", values)

	dec.Reset(bytes.NewBuffer(b))
	values, err = dec.Query("1.attrs.phone")
	if err != nil {
		panic(err)
	}
	fmt.Println("2nd phone is", values[0])
}
Output:

phones are [12345 54321]
2nd phone is 54321

func (*Decoder) ReadFull

func (d *Decoder) ReadFull(buf []byte) error

ReadFull reads exactly len(buf) bytes into the buf.

func (*Decoder) Reset

func (d *Decoder) Reset(r io.Reader)

Reset discards any buffered data, resets all state, and switches the buffered reader to read from r.

func (*Decoder) ResetDict

func (d *Decoder) ResetDict(r io.Reader, dict []string)

ResetDict is like Reset, but also resets the dict.

func (*Decoder) SetCustomStructTag

func (d *Decoder) SetCustomStructTag(tag string)

SetCustomStructTag causes the decoder to use the supplied tag as a fallback option if there is no ljpack tag.

func (*Decoder) SetMapDecoder

func (d *Decoder) SetMapDecoder(fn func(*Decoder) (interface{}, error))
Example
package main

import (
	"bytes"
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	buf := new(bytes.Buffer)

	enc := ljpack.NewEncoder(buf)
	in := map[string]string{"hello": "world"}
	err := enc.Encode(in)
	if err != nil {
		panic(err)
	}

	dec := ljpack.NewDecoder(buf)

	// Causes decoder to produce map[string]string instead of map[string]interface{}.
	dec.SetMapDecoder(func(d *ljpack.Decoder) (interface{}, error) {
		n, err := d.DecodeMapLen()
		if err != nil {
			return nil, err
		}

		m := make(map[string]string, n)
		for i := 0; i < n; i++ {
			mk, err := d.DecodeString()
			if err != nil {
				return nil, err
			}

			mv, err := d.DecodeString()
			if err != nil {
				return nil, err
			}

			m[mk] = mv
		}
		return m, nil
	})

	out, err := dec.DecodeInterface()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v", out)
}
Output:

map[string]string{"hello":"world"}

func (*Decoder) Skip

func (d *Decoder) Skip() error

Skip skips next value.

func (*Decoder) UseInternedStrings

func (d *Decoder) UseInternedStrings(on bool)

UseInternedStrings enables support for decoding interned strings.

func (*Decoder) UseLooseInterfaceDecoding

func (d *Decoder) UseLooseInterfaceDecoding(on bool)

UseLooseInterfaceDecoding causes decoder to use DecodeInterfaceLoose to decode ljpack value into Go interface{}.

func (*Decoder) WithDict

func (d *Decoder) WithDict(dict []string, fn func(*Decoder) error) error

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

func GetEncoder

func GetEncoder() *Encoder

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder returns a new encoder that writes to w.

func (*Encoder) Encode

func (e *Encoder) Encode(vv ...interface{}) error

func (*Encoder) EncodeArrayLen

func (e *Encoder) EncodeArrayLen(l int) error

func (*Encoder) EncodeBool

func (e *Encoder) EncodeBool(value bool) error

func (*Encoder) EncodeBytes

func (e *Encoder) EncodeBytes(v []byte) error

func (*Encoder) EncodeBytesLen

func (e *Encoder) EncodeBytesLen(l int) error

func (*Encoder) EncodeDouble

func (e *Encoder) EncodeDouble(n float64) error

func (*Encoder) EncodeDuration

func (e *Encoder) EncodeDuration(d time.Duration) error

func (*Encoder) EncodeEmpty

func (e *Encoder) EncodeEmpty() error

func (*Encoder) EncodeFFIInt64

func (e *Encoder) EncodeFFIInt64(n int64) error

EncodeFFIInt64 encodes an int64 in 9 bytes preserving type of the number.

func (*Encoder) EncodeFFIUint

func (e *Encoder) EncodeFFIUint(n uint64) error

EncodeUnsignedNumber encodes an uint64 in 1, 2, 3, 5, or 9 bytes. Type of the number is lost during encoding.

func (*Encoder) EncodeFFIUint64

func (e *Encoder) EncodeFFIUint64(n uint64) error

EncodeFFIUint64 encodes an uint16 in 9 bytes preserving type of the number.

func (*Encoder) EncodeInt

func (e *Encoder) EncodeInt(n int64) error

EncodeNumber encodes an int64 in 1, 2, 3, 5, or 9 bytes. Type of the number is lost during encoding.

func (*Encoder) EncodeInt32

func (e *Encoder) EncodeInt32(n int32) error

EncodeInt32 encodes an int32 in 5 bytes preserving type of the number.

func (*Encoder) EncodeMap

func (e *Encoder) EncodeMap(m map[string]interface{}) error

func (*Encoder) EncodeMapLen

func (e *Encoder) EncodeMapLen(l int) error

func (*Encoder) EncodeMapSorted

func (e *Encoder) EncodeMapSorted(m map[string]interface{}) error

func (*Encoder) EncodeMulti

func (e *Encoder) EncodeMulti(v ...interface{}) error

func (*Encoder) EncodeNil

func (e *Encoder) EncodeNil() error

func (*Encoder) EncodeNoIntern

func (e *Encoder) EncodeNoIntern(v interface{}) error

func (*Encoder) EncodeNull

func (e *Encoder) EncodeNull() error

func (*Encoder) EncodeString

func (e *Encoder) EncodeString(v string) error

func (*Encoder) EncodeValue

func (e *Encoder) EncodeValue(v reflect.Value) error

func (*Encoder) EncodeValueNoIntern

func (e *Encoder) EncodeValueNoIntern(v reflect.Value) error

func (*Encoder) Reset

func (e *Encoder) Reset(w io.Writer)

Reset discards any buffered data, resets all state, and switches the writer to write to w.

func (*Encoder) ResetDict

func (e *Encoder) ResetDict(w io.Writer, dict map[string]int)

ResetDict is like Reset, but also resets the dict.

func (*Encoder) SetCustomStructTag

func (e *Encoder) SetCustomStructTag(tag string)

SetCustomStructTag causes the Encoder to use a custom struct tag as fallback option if there is no ljpack tag.

func (*Encoder) SetOmitEmpty

func (e *Encoder) SetOmitEmpty(on bool)

SetOmitEmpty causes the Encoder to omit empty values by default.

func (*Encoder) SetSortMapKeys

func (e *Encoder) SetSortMapKeys(on bool) *Encoder

SetSortMapKeys causes the Encoder to encode map keys in increasing order. Supported map types are:

  • map[string]string
  • map[string]interface{}

func (*Encoder) UseArrayEncodedStructs

func (e *Encoder) UseArrayEncodedStructs(on bool)

UseArrayEncodedStructs causes the Encoder to encode Go structs as ljpack arrays.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/fffonion/ljpack"
)

func main() {
	type Item struct {
		Foo string
		Bar string
	}

	var buf bytes.Buffer
	enc := ljpack.NewEncoder(&buf)
	enc.UseArrayEncodedStructs(true)

	err := enc.Encode(&Item{Foo: "foo", Bar: "bar"})
	if err != nil {
		panic(err)
	}

	dec := ljpack.NewDecoder(&buf)
	v, err := dec.DecodeInterface()
	if err != nil {
		panic(err)
	}
	fmt.Println(v)
}
Output:

[foo bar]

func (*Encoder) UseCompactFloats

func (e *Encoder) UseCompactFloats(on bool)

UseCompactFloats causes the Encoder to chose a compact integer encoding for floats that can be represented as integers.

func (*Encoder) UseCompactInts

func (e *Encoder) UseCompactInts(on bool)

UseCompactEncoding causes the Encoder to chose the most compact encoding. For example, it allows to encode small Go int64 as ljpack int8 saving 7 bytes.

func (*Encoder) UseInternedStrings

func (e *Encoder) UseInternedStrings(on bool)

UseInternedStrings causes the Encoder to intern strings.

func (*Encoder) WithDict

func (e *Encoder) WithDict(dict map[string]int, fn func(*Encoder) error) error

func (*Encoder) Writer

func (e *Encoder) Writer() io.Writer

Writer returns the Encoder's writer.

type Marshaler

type Marshaler interface {
	MarshalLJpack() ([]byte, error)
}

type RawMessage

type RawMessage []byte

func (*RawMessage) DecodeLJpack

func (m *RawMessage) DecodeLJpack(dec *Decoder) error

func (RawMessage) EncodeLJpack

func (m RawMessage) EncodeLJpack(enc *Encoder) error

type Unmarshaler

type Unmarshaler interface {
	UnmarshalLJpack([]byte) error
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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