github.com/nyan233/littlerpc@v0.4.6-0.20230316182519-0c8d5c48abaf/core/protocol/message/serialization.go (about)

     1  package message
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/nyan233/littlerpc/core/container"
     8  	"github.com/nyan233/littlerpc/core/utils/convert"
     9  	"math"
    10  )
    11  
    12  // MarshaToMux 此API只会序列化Mux功能需要的数据
    13  func MarshaToMux(msg *Message, payloads *container.Slice[byte]) error {
    14  	if payloads.Cap() < msg.MinMux() {
    15  		*payloads = make([]byte, 0, msg.MinMux())
    16  	}
    17  	msg.GetAndSetLength()
    18  	*payloads = append(*payloads, msg.scope[:]...)
    19  	*payloads = (*payloads)[:msg.MinMux()]
    20  	binary.BigEndian.PutUint64((*payloads)[2:10], msg.msgId)
    21  	binary.BigEndian.PutUint32((*payloads)[10:14], msg.payloadLength)
    22  	return nil
    23  }
    24  
    25  // UnmarshalFromMux 此API之后反序列化Mux功能所需要的数据
    26  func UnmarshalFromMux(data container.Slice[byte], msg *Message) error {
    27  	if data.Len() < msg.MinMux() {
    28  		return errors.New("mux message is bad")
    29  	}
    30  	copy(msg.scope[:], data[:_ScopeLength])
    31  	msg.msgId = binary.BigEndian.Uint64(data[_ScopeLength:10])
    32  	msg.payloadLength = binary.BigEndian.Uint32(data[10:14])
    33  	return nil
    34  }
    35  
    36  // Unmarshal 从字节Slice中解码出Message,并返回载荷数据的起始地址
    37  func Unmarshal(p container.Slice[byte], msg *Message) error {
    38  	if p.Len() == 0 || msg == nil {
    39  		return errors.New("data or message is nil")
    40  	}
    41  	if p.Len() < msg.BaseLength() {
    42  		return errors.New("data Length < baseLen")
    43  	}
    44  	err := UnmarshalFromMux(p, msg)
    45  	if err != nil {
    46  		return err
    47  	}
    48  	p = p[msg.MinMux():]
    49  	// NameLayout
    50  	serviceNameLen := p[0]
    51  	p = p[_ServiceName:]
    52  	if p.Len() < int(serviceNameLen) {
    53  		return errors.New("service name Length greater than p")
    54  	}
    55  	msg.SetServiceName(string(p[:serviceNameLen]))
    56  	p = p[serviceNameLen:]
    57  	// 有多少个元数据
    58  	// 在可变长数据之后, 需要校验
    59  	if p.Len() < _Metadata {
    60  		return errors.New("p Length less than 1")
    61  	}
    62  	nMetaData := p[0]
    63  	p = p[_Metadata:]
    64  	for i := 0; i < int(nMetaData); i++ {
    65  		if p.Len() < 8 {
    66  			return errors.New("p Length less than 8 on nMetaData")
    67  		}
    68  		keySize := binary.BigEndian.Uint32(p[:4])
    69  		valueSize := binary.BigEndian.Uint32(p[4:8])
    70  		// 相加防止溢出, 所以需要检查溢出
    71  		if p.Len() < int(keySize+valueSize) || keySize > math.MaxUint32-valueSize {
    72  			return errors.New("key and value size overflow")
    73  		}
    74  		p = p[8:]
    75  		msg.MetaData.Store(string(p[:keySize]), string(p[keySize:keySize+valueSize]))
    76  		p = p[keySize+valueSize:]
    77  	}
    78  	// 在可变长数据之后, 需要校验
    79  	if p.Len() < _PayloadLayout {
    80  		return errors.New("p Length less than 4 on nArgs")
    81  	}
    82  	nArgs := p[0]
    83  	p = p[_PayloadLayout:]
    84  	// 为了保证更好的反序列化体验,如果不将layout置0的话
    85  	// 会导致与Marshal/Unmarshal的结果重叠
    86  	if msg.payloadLayout != nil {
    87  		msg.payloadLayout.Reset()
    88  	}
    89  	for i := 0; i < int(nArgs); i++ {
    90  		if p.Len() < 4 {
    91  			return errors.New("p Length less than 4 on argument layout")
    92  		}
    93  		argsSize := binary.BigEndian.Uint32(p[:4])
    94  		p = p[4:]
    95  		msg.payloadLayout = append(msg.payloadLayout, argsSize)
    96  	}
    97  	// 不根据参数布局计算所有参数的载荷数据长度, 因为参数载荷数据可能会被压缩
    98  	// 导致了长度不一致的情况
    99  	msg.payloads.Reset()
   100  	// 剩余的数据是载荷数据
   101  	msg.payloads.Append(p)
   102  	return nil
   103  }
   104  
   105  // Marshal 根据Msg Header编码出对应的字节Slice
   106  // *[]byte是为了提供更好的内存复用语义
   107  func Marshal(msg *Message, p *container.Slice[byte]) error {
   108  	if err := MarshaToMux(msg, p); err != nil {
   109  		return err
   110  	}
   111  	integerBuffer := make([]byte, 4)
   112  	if len(msg.serviceName) > math.MaxUint8 {
   113  		return errors.New(fmt.Sprintf("serviceName max Length = 255, but now Length = %d", len(msg.serviceName)))
   114  	}
   115  	p.AppendS(byte(len(msg.serviceName)))
   116  	*p = append(*p, msg.serviceName...)
   117  	// 序列化元数据
   118  	if msg.MetaData.Len() > math.MaxUint8 {
   119  		return errors.New(fmt.Sprintf("metaData max Length = 255, but now Length = %d", msg.MetaData.Len()))
   120  	}
   121  	p.AppendS(byte(msg.MetaData.Len()))
   122  	msg.MetaData.Range(func(k, v string) bool {
   123  		binary.BigEndian.PutUint32(integerBuffer, uint32(len(k)))
   124  		p.Append(integerBuffer)
   125  		binary.BigEndian.PutUint32(integerBuffer, uint32(len(v)))
   126  		p.Append(integerBuffer)
   127  		p.Append(convert.StringToBytes(k))
   128  		p.Append(convert.StringToBytes(v))
   129  		return true
   130  	})
   131  	// 序列化载荷数据描述信息
   132  	if msg.payloadLayout.Len() > math.MaxUint8 {
   133  		return errors.New(fmt.Sprintf("payloadLayout max Length = 255, but now Length = %d", msg.payloadLayout.Len()))
   134  	}
   135  	p.AppendS(byte(msg.payloadLayout.Len()))
   136  	for _, v := range msg.payloadLayout {
   137  		binary.BigEndian.PutUint32(integerBuffer, v)
   138  		p.Append(integerBuffer)
   139  	}
   140  	p.Append(msg.payloads)
   141  	return nil
   142  }
   143  
   144  // ResetMsg 指定策略的复用,对内存重用更加友好
   145  // resetOther指示是否释放|Scope|NameLayout|InstanceName|MethodName|MsgId|Timestamp
   146  // freeMetaData指示是否要释放存放元数据对应的map[string]sting
   147  // usePayload指示是否要复用载荷数据
   148  // useSize指示复用的slice类型长度的上限,即使指定了usePayload
   149  // payload数据超过这个长度还是会被释放
   150  func ResetMsg(msg *Message, resetOther, freeMetaData, usePayload bool, useSize int) {
   151  	if freeMetaData {
   152  		msg.MetaData.Reset()
   153  	}
   154  	if len(msg.payloadLayout) > useSize {
   155  		msg.payloadLayout = nil
   156  	} else {
   157  		msg.payloadLayout.Reset()
   158  	}
   159  	if !usePayload {
   160  		msg.payloads = nil
   161  	} else if usePayload && len(msg.payloads) > useSize {
   162  		msg.payloads = nil
   163  	} else {
   164  		msg.payloads.Reset()
   165  	}
   166  	if resetOther {
   167  		msg.scope = [...]uint8{MagicNumber, 0}
   168  		msg.serviceName = ""
   169  		msg.msgId = 0
   170  		msg.payloadLength = 0
   171  	}
   172  }