From 696e263e4dc71f8eb1c5a710ebb28f163a07402b Mon Sep 17 00:00:00 2001
From: Alex Vanin <alexey@nspcc.ru>
Date: Wed, 12 Aug 2020 13:34:15 +0300
Subject: [PATCH] Add stable marshal for placement rule

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
---
 netmap/v2/marshal.go | 362 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 362 insertions(+)
 create mode 100644 netmap/v2/marshal.go

diff --git a/netmap/v2/marshal.go b/netmap/v2/marshal.go
new file mode 100644
index 0000000..11c6e50
--- /dev/null
+++ b/netmap/v2/marshal.go
@@ -0,0 +1,362 @@
+package v2
+
+import (
+	"encoding/binary"
+	"math/bits"
+
+	"github.com/pkg/errors"
+)
+
+func (m *PlacementRule) StableMarshal(buf []byte) ([]byte, error) {
+	if m == nil {
+		return []byte{}, nil
+	}
+
+	if buf == nil {
+		buf = make([]byte, m.StableSize())
+	}
+
+	var (
+		i, n, offset int
+	)
+
+	// Write replication factor field.
+
+	buf[i] = 0x08 // id:0x1 << 3 | wiretype:0x0
+	offset = binary.PutUvarint(buf[i+1:], uint64(m.ReplFactor))
+	i += 1 + offset
+
+	// write select/filter groups field
+	for j := range m.SfGroups {
+		buf[i] = 0x12 // id:0x2 << 3 | wiretype:0x2
+
+		n, _ = m.SfGroups[j].stableSizeWithExclude()
+		offset = binary.PutUvarint(buf[i+1:], uint64(n))
+
+		_, err := m.SfGroups[j].StableMarshal(buf[i+1+offset:])
+		if err != nil {
+			return nil, errors.Wrapf(err, "can't marshal SFGroup id:%d", j)
+		}
+
+		i += 1 + offset + n
+	}
+
+	return buf, nil
+}
+
+func (m *PlacementRule) StableSize() int {
+	if m == nil {
+		return 0
+	}
+
+	var (
+		ln, size int
+	)
+	_ = ln
+
+	// size of replication factor field
+	size += 1 + uvarIntSize(uint64(m.ReplFactor)) // wiretype + varint
+
+	for i := range m.SfGroups {
+		ln, _ = m.SfGroups[i].stableSizeWithExclude()
+		size += 1 + uvarIntSize(uint64(ln)) + ln // wiretype + size of struct + struct
+	}
+
+	return size
+}
+
+func (m *PlacementRule_SFGroup) StableMarshal(buf []byte) ([]byte, error) {
+	if m == nil {
+		return []byte{}, nil
+	}
+
+	size, excludeSize := m.stableSizeWithExclude()
+	if buf == nil {
+		buf = make([]byte, size)
+	}
+
+	var (
+		i, n, offset int
+	)
+
+	// write filters field
+	for j := range m.Filters {
+		buf[i] = 0x0A // id:0x1 << 3 | wiretype:0x2
+		n = m.Filters[j].stableSize()
+		offset = binary.PutUvarint(buf[i+1:], uint64(n))
+		_, err := m.Filters[j].StableMarshal(buf[i+1+offset:])
+		if err != nil {
+			return nil, errors.Wrapf(err, "can't marshal Filter id:%d", j)
+		}
+		i += 1 + offset + n
+	}
+
+	// write selectors field
+	for j := range m.Selectors {
+		buf[i] = 0x12 // id:0x2 << 3 | wiretype:0x2
+		n = m.Selectors[j].stableSize()
+		offset = binary.PutUvarint(buf[i+1:], uint64(n))
+		_, err := m.Selectors[j].StableMarshal(buf[i+1+offset:])
+		if err != nil {
+			return nil, errors.Wrapf(err, "can't marshal Selector id:%d", j)
+		}
+		i += 1 + offset + n
+	}
+
+	// write excluded field in packed format
+	buf[i] = 0x1A // id:0x3 << 3 | wiretype:0x2
+	offset = binary.PutUvarint(buf[i+1:], uint64(excludeSize))
+	i += 1 + offset
+	for j := range m.Exclude {
+		offset = binary.PutUvarint(buf[i:], uint64(m.Exclude[j]))
+		i += offset
+	}
+
+	return buf, nil
+}
+
+func (m *PlacementRule_SFGroup) stableSizeWithExclude() (int, int) {
+	if m == nil {
+		return 0, 0
+	}
+
+	var (
+		ln, size int
+	)
+
+	// size of filters field
+	for i := range m.Filters {
+		ln = m.Filters[i].stableSize()
+		size += 1 + uvarIntSize(uint64(ln)) + ln // wiretype + size of struct + struct
+	}
+
+	// size of selectors field
+	for i := range m.Selectors {
+		ln = m.Selectors[i].stableSize()
+		size += 1 + uvarIntSize(uint64(ln)) + ln // wiretype + size of struct + struct
+	}
+
+	// size of exclude field
+	ln = 0
+	for i := range m.Exclude {
+		ln += uvarIntSize(uint64(m.Exclude[i]))
+	}
+	size += 1 + uvarIntSize(uint64(ln)) + ln // wiretype + packed varints size + packed varints
+
+	return size, ln
+}
+
+func (m *PlacementRule_SFGroup_Selector) StableMarshal(buf []byte) ([]byte, error) {
+	if m == nil {
+		return []byte{}, nil
+	}
+
+	if buf == nil {
+		buf = make([]byte, m.stableSize())
+	}
+
+	var (
+		i, offset int
+	)
+
+	// write count field
+	buf[i] = 0x8 // id:0x1 << 3 | wiretype:0x0
+	offset = binary.PutUvarint(buf[i+1:], uint64(m.Count))
+	i += 1 + offset
+
+	// write key field
+	buf[i] = 0x12 // id:0x2 << 3 | wiretype:0x2
+	offset = binary.PutUvarint(buf[i+1:], uint64(len(m.Key)))
+	copy(buf[i+1+offset:], m.Key)
+
+	return buf, nil
+}
+
+func (m *PlacementRule_SFGroup_Selector) stableSize() int {
+	if m == nil {
+		return 0
+	}
+
+	var (
+		ln, size int
+	)
+
+	// size of count field
+	size += 1 + uvarIntSize(uint64(m.Count))
+
+	// size of key field
+	ln = len(m.Key)
+	size += 1 + uvarIntSize(uint64(ln)) + ln
+
+	return size
+}
+
+func (m *PlacementRule_SFGroup_Filter) StableMarshal(buf []byte) ([]byte, error) {
+	if m == nil {
+		return []byte{}, nil
+	}
+
+	if buf == nil {
+		buf = make([]byte, m.stableSize())
+	}
+
+	var (
+		i, n, offset int
+	)
+
+	// write key field
+	buf[i] = 0x0A // id:0x1 << 3 | wiretype:0x2
+	offset = binary.PutUvarint(buf[i+1:], uint64(len(m.Key)))
+	n = copy(buf[i+1+offset:], m.Key)
+	i += 1 + offset + n
+
+	// write simple filter field
+	if m.F != nil {
+		buf[i] = 0x12 // id:0x2 << 3 | wiretype:0x2
+		n = m.F.stableSize()
+		offset = binary.PutUvarint(buf[i+1:], uint64(n))
+		_, err := m.F.StableMarshal(buf[i+1+offset:])
+		if err != nil {
+			return nil, errors.Wrap(err, "can't marshal netmap filter")
+		}
+	}
+
+	return buf, nil
+}
+
+func (m *PlacementRule_SFGroup_Filter) stableSize() int {
+	if m == nil {
+		return 0
+	}
+
+	var (
+		ln, size int
+	)
+
+	// size of key field
+	ln = len(m.Key)
+	size += 1 + uvarIntSize(uint64(ln)) + ln
+
+	// size of simple filter
+	if m.F != nil {
+		ln = m.F.stableSize()
+		size += 1 + uvarIntSize(uint64(ln)) + ln
+	}
+
+	return size
+}
+
+func (m *PlacementRule_SFGroup_Filter_SimpleFilter) StableMarshal(buf []byte) ([]byte, error) {
+	if m == nil {
+		return []byte{}, nil
+	}
+
+	if buf == nil {
+		buf = make([]byte, m.stableSize())
+	}
+
+	var (
+		i, n, offset int
+	)
+
+	// write key field
+	buf[i] = 0x08 // id:0x1 << 3 | wiretype:0x0
+	offset = binary.PutUvarint(buf[i+1:], uint64(m.Op))
+	i += 1 + offset
+
+	// write value if present
+	if val, ok := m.Args.(*PlacementRule_SFGroup_Filter_SimpleFilter_Value); ok {
+		buf[i] = 0x12 // id:0x2 << 3 | wiretype:0x2
+		offset = binary.PutUvarint(buf[i+1:], uint64(len(val.Value)))
+		copy(buf[i+1+offset:], val.Value)
+	} else if filters, ok := m.Args.(*PlacementRule_SFGroup_Filter_SimpleFilter_FArgs); ok {
+		if filters.FArgs != nil {
+			buf[i] = 0x1A // id:0x3 << 3 | wiretype:0x2
+			n = filters.FArgs.stableSize()
+			offset = binary.PutUvarint(buf[i+1:], uint64(n))
+			_, err := filters.FArgs.StableMarshal(buf[i+1+offset:])
+			if err != nil {
+				return nil, errors.Wrap(err, "can't marshal simple filters")
+			}
+		}
+	}
+
+	return buf, nil
+}
+
+func (m *PlacementRule_SFGroup_Filter_SimpleFilter) stableSize() int {
+	if m == nil {
+		return 0
+	}
+
+	var (
+		ln, size int
+	)
+
+	// size of key field
+	size += 1 + uvarIntSize(uint64(m.Op))
+
+	if val, ok := m.Args.(*PlacementRule_SFGroup_Filter_SimpleFilter_Value); ok {
+		// size of value if present
+		ln = len(val.Value)
+		size += 1 + uvarIntSize(uint64(ln)) + ln
+	} else if filters, ok := m.Args.(*PlacementRule_SFGroup_Filter_SimpleFilter_FArgs); ok {
+		// size of simple filters if present
+		if filters.FArgs != nil {
+			ln = filters.FArgs.stableSize()
+			size += 1 + uvarIntSize(uint64(ln)) + ln
+		}
+	}
+
+	return size
+}
+
+func (m *PlacementRule_SFGroup_Filter_SimpleFilters) StableMarshal(buf []byte) ([]byte, error) {
+	if m == nil {
+		return []byte{}, nil
+	}
+
+	if buf == nil {
+		buf = make([]byte, m.stableSize())
+	}
+
+	var (
+		i, n, offset int
+	)
+
+	// write filters field
+	for j := range m.Filters {
+		buf[i] = 0x0A // id:0x1 << 3 | wiretype:0x2
+		n = m.Filters[j].stableSize()
+		offset = binary.PutUvarint(buf[i+1:], uint64(n))
+		_, err := m.Filters[j].StableMarshal(buf[i+1+offset:])
+		if err != nil {
+			return nil, errors.Wrapf(err, "can't marshal simple filter id:%d", j)
+		}
+		i += 1 + offset + n
+	}
+
+	return buf, nil
+}
+
+func (m *PlacementRule_SFGroup_Filter_SimpleFilters) stableSize() int {
+	if m == nil {
+		return 0
+	}
+	var (
+		ln, size int
+	)
+
+	// size of key field
+	for i := range m.Filters {
+		ln = m.Filters[i].stableSize()
+		size += 1 + uvarIntSize(uint64(ln)) + ln
+	}
+
+	return size
+}
+
+// uvarIntSize returns length of varint byte sequence for uint64 value 'x'.
+func uvarIntSize(x uint64) int {
+	return (bits.Len64(x|1) + 6) / 7
+}