package gtp import ( "errors" "fmt" "git.noahlan.cn/noahlan/ntool/nbyte" "git.noahlan.cn/noahlan/ntool/nstr" "regexp" "strings" ) var ( TabRegex = regexp.MustCompile(`\t`) IdRegex = regexp.MustCompile(`\s`) ) type GTPSerializer struct { contentBuf strings.Builder firstLine bool // lastResp *GTPResponse } func NewGTPSerializer() *GTPSerializer { ret := >PSerializer{ contentBuf: strings.Builder{}, } ret.reset() return ret } func (s *GTPSerializer) Marshal(v any) ([]byte, error) { ret, ok := v.(*GTPCommand) if !ok { return nil, errors.New(fmt.Sprintf("参数类型必须为 %T", GTPCommand{})) } // ret arg0 arg1 arg2 ... // Cmd arg0 arg1 arg2 ... sb := strings.Builder{} if ret.ID != "" { sb.WriteString(ret.ID) sb.WriteString(" ") } sb.WriteString(ret.Cmd) sb.WriteString(" ") sb.WriteString(strings.Join(ret.Args, " ")) return []byte(sb.String()), nil } func (s *GTPSerializer) reset() { s.contentBuf.Reset() s.firstLine = true s.lastResp = nil } func (s *GTPSerializer) Unmarshal(line string) (*GTPResponse, bool) { // replace \t and trimSpace line = strings.TrimSpace(TabRegex.ReplaceAllString(line, " ")) // 是否第一行 s.firstLine = nstr.ContainsOne(line, []string{"=", "?"}) if len(line) == 0 { defer s.reset() if s.firstLine { s.lastResp = >PResponse{ ID: "", Content: s.contentBuf.String(), Err: errors.New("unknown"), } return s.lastResp, true } else { // end line content := s.contentBuf.String() // remove last '\n' cLen := len(content) if cLen > 0 && content[cLen-1] == '\n' { content = content[:cLen-1] } if s.lastResp.Err == nil { s.lastResp.Content = content } else { s.lastResp.Err = errors.New(content) } return s.lastResp, true } } if s.firstLine { s.lastResp = >PResponse{} // 第一行,处理 err, id 等内容 hasErr := line[0] == '?' hasId := len(line) >= 2 && nbyte.IsDigit(line[1]) line = line[1:] if hasId { s.lastResp.ID = IdRegex.Split(line, -1)[0] line = line[len(s.lastResp.ID):] } if hasErr { s.lastResp.Err = errors.New("接收GTP错误消息") } } // 填充内容直至末尾行 s.contentBuf.WriteString(strings.TrimSpace(line)) s.contentBuf.WriteByte('\n') return nil, false }