kamino

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 25, 2023 License: MIT Imports: 3 Imported by: 2

README

Kamino - deep copy library

Kamino is a programming library that provides faster way to deep copy data structures in Golang. It designed to be fast and avoid unnecessary allocations. See benchmarks. Also It uses generics to avoid type assertions in client's code. Although kamino designed to be fast, it handle circular pointers. By default kamino makes a shallow copy for unsupported kinds of values (chans and funcs) and unexported struct fields. Buit in can be changed via functional options.

Installation

go get github.com/LastPossum/kamino

Usage

To use the library, you need to import it into your project:

import "github.com/LastPossum/kamino"

Once you have imported the library, you can use the func Clone[T any](src T, opts ...funcOptions) (T, error) function to create a deep copy of your data structure.

Sample Program
package main

import (
	"fmt"
	"reflect"

	"github.com/LastPossum/kamino"
)

type Foo struct {
	A string
	B int
	C *float64
	D any
}

func main() {
	var f float64 = 1
	original := Foo{
		A: "a string",
		B: 1114,
		C: &f,
	}

    original.D = &original

	copy, err := kamino.Clone(original)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(original, original.D)
	fmt.Println(copy, copy.D)
	fmt.Println(reflect.DeepEqual(original, copy))
}
Sample Output

Note that the values are all the same, but the addresses are all different. Note also that circular references are handled.

{a string 1114 0xc00001c030 0xc0000161b0} &{a string 1114 0xc00001c030 0xc0000161b0}
{a string 1114 0xc00001c038 0xc0000162a0} &{a string 1114 0xc00001c038 0xc0000162a0}
true

Options

Kamino supports several following options:

  • WithExpectedPtrsCount - Use it to set the initial capacity for the pointers map used during the cloning process. If the amount of pointers in the source object is known and rather big, this setting could reduce allocations and slightly improve performance.
  • WithForceUnexported - when this setting is enabled, unexported fields will be cloned forcefully.
  • WithZeroUnexported - when this setting is enabled, unexported fields will be forcefully set to zero value of their kind.
  • WithErrOnUnsupported - when this setting is enabled, attempting to clone channels and functions, even if they are nested, will result in an error.

Benchmarks

Results as of April 25, 2023 with Go go1.20.3 on OSx Intel Core i5 (4 core 2,4 GHz)

BenchmarkCloneKaminoComplexStruct-8   	                                  277168	        3943 ns/op	    2013 B/op	      27 allocs/op
BenchmarkNestedStructKamino/kamino_for_7_fiels_nested_struct-8         	 4228910	       281.3 ns/op	      56 B/op	       4 allocs/op
BenchmarkCloneMap-8   	                                                  331911	        3482 ns/op	    1424 B/op	      38 allocs/op

Limitation

For now, Kamino does not support circular slices and maps. Additionally, if two slices in the source object point to the same underlying array, this feature will not be kept in the copy object. Therefore, these cases will cause a stack overflow:

func TestCircularSlice(t *testing.T) {
	a := []any{nil}
	a[0] = a
	cp, err := kamino.Clone(a)

	assert.NoError(t, err)
	assert.Equal(t, cp, a)
}

func TestCircularMap(t *testing.T) {
	a := map[string]any{"a": nil}
	a["a"] = a
	cp, err := kamino.Clone(a)

	assert.NoError(t, err)
	assert.Equal(t, cp, a)
}

License

This library is released under the MIT License. You are free to use, modify, and distribute it as you see fit. See the LICENSE file for more information.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Clone

func Clone[T any](src T, opts ...funcOptions) (T, error)

Clone returns a deep copy of passed argument. The cloned value will be identical to the source value. Cloned pointers will point to a new object with the same value as in the source. This also works for nested values, such as parts of collections (arrays, slices, and maps) or fields of passed structs. It additionally guarantied that if two or more pointers at the source object references to the same value, clones pointers will refers to same vallue. But if two or more slices at the source object refer to the same memory area (i.e. the same underlying array), the copy will refer to different arrays. By default, unexported struct fields will be shallow copied, but this can be changed with functional options. Unsupported types (channels and funcs) will be shallow copied by default, but this can also be changed with functional options. Unsafe pointers will be treated like ordinary values.

Example
package main

import (
	"fmt"

	"github.com/LastPossum/kamino"
)

func main() {
	tests := []any{
		200000,
		`units are ready, with a`,
		1000000,
		[]string{
			"more well",
			"on the way.",
		},
		map[string]any{
			"I don't like sand": "It's coarse and rough and irritating and it gets everywhere.",
		},
	}

	for _, expected := range tests {
		actual, err := kamino.Clone(expected, kamino.WithErrOnUnsupported())
		if err != nil {
			fmt.Println("got error:", err)
		}
		fmt.Println(actual)
	}
}
Output:

200000
units are ready, with a
1000000
[more well on the way.]
map[I don't like sand:It's coarse and rough and irritating and it gets everywhere.]

func WithErrOnUnsupported

func WithErrOnUnsupported() func(*config)

WithErrOnUnsupported is a functional option for the Clone function. When this setting is enabled, attempting to clone channels and functions, even if they are nested, will result in an error.

func WithExpectedPtrsCount

func WithExpectedPtrsCount(cnt int) func(*config)

WithExpectedPtrsCount is a functional option for the Clone function that sets the initial capacity for the pointers map used during the cloning process. If the amount of pointers in the source object is known and rather big, this setting could reduce allocations and improve performance.

func WithForceUnexported

func WithForceUnexported() func(*config)

WithForceUnexported is a functional option for the Clone function. When this setting is enabled, unexported fields will be cloned forcefully.

func WithZeroUnexported

func WithZeroUnexported() func(*config)

WithZeroUnexported is a functional option for the Clone function. When this setting is enabled, unexported fields will be forcefully set to zero value of their kind.

Types

This section is empty.

Jump to

Keyboard shortcuts

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