package openai import ( "encoding/json" "fmt" "sort" ) // topLevelJSONPatch is a request-local instruction for one top-level JSON // member. A nil value removes the member; a non-nil value replaces it or is // appended when the member is absent. type topLevelJSONPatch struct { name string value []byte } type topLevelJSONFieldSpan struct { name string memberFrom int memberTo int valueFrom int valueTo int } type jsonByteEdit struct { from int to int replacement []byte } type topLevelJSONPatchPlan struct { body []byte edits []jsonByteEdit outputSize int } func planTopLevelJSONPatches(body []byte, patches []topLevelJSONPatch) (topLevelJSONPatchPlan, error) { if len(patches) == 0 { return topLevelJSONPatchPlan{body: body, outputSize: len(body)}, nil } fields, objectEnd, err := scanTopLevelJSONObject(body) if err != nil { return topLevelJSONPatchPlan{}, err } patchByName := make(map[string]topLevelJSONPatch, len(patches)) for _, patch := range patches { if patch.name == "" { return topLevelJSONPatchPlan{}, fmt.Errorf("JSON patch field is required") } if patch.value != nil && !json.Valid(patch.value) { return topLevelJSONPatchPlan{}, fmt.Errorf("invalid JSON patch for %s", patch.name) } patchByName[patch.name] = patch } found := make(map[string]bool, len(patchByName)) edits := make([]jsonByteEdit, 0, len(patches)) for index, field := range fields { patch, ok := patchByName[field.name] if !ok { continue } found[field.name] = true if patch.value != nil { edits = append(edits, jsonByteEdit{from: field.valueFrom, to: field.valueTo, replacement: patch.value}) continue } // Removing a target member also removes exactly one adjacent comma. The // untouched members and all bytes outside this local span remain intact. from, to := field.memberFrom, field.memberTo if index+1 < len(fields) { to = fields[index+1].memberFrom } else if index > 0 { from = fields[index-1].memberTo } edits = append(edits, jsonByteEdit{from: from, to: to}) } var insertion []byte memberCount := len(fields) for _, patch := range patches { if found[patch.name] || patch.value == nil { continue } nameJSON, err := json.Marshal(patch.name) if err != nil { return topLevelJSONPatchPlan{}, err } if memberCount > 0 { insertion = append(insertion, ',') } insertion = append(insertion, nameJSON...) insertion = append(insertion, ':') insertion = append(insertion, patch.value...) memberCount++ } if len(insertion) > 0 { edits = append(edits, jsonByteEdit{from: objectEnd, to: objectEnd, replacement: insertion}) } sort.SliceStable(edits, func(i, j int) bool { return edits[i].from < edits[j].from }) outputSize := len(body) lastTo := 0 for _, edit := range edits { if edit.from < lastTo || edit.from < 0 || edit.to < edit.from || edit.to > len(body) { return topLevelJSONPatchPlan{}, fmt.Errorf("overlapping JSON field patches") } outputSize += len(edit.replacement) - (edit.to - edit.from) if outputSize < 0 { return topLevelJSONPatchPlan{}, fmt.Errorf("JSON patch output size overflow") } lastTo = edit.to } return topLevelJSONPatchPlan{body: body, edits: edits, outputSize: outputSize}, nil } func (p topLevelJSONPatchPlan) apply() []byte { if len(p.edits) == 0 { return p.body } out := make([]byte, 0, p.outputSize) last := 0 for _, edit := range p.edits { out = append(out, p.body[last:edit.from]...) out = append(out, edit.replacement...) last = edit.to } out = append(out, p.body[last:]...) return out } func scanTopLevelJSONObject(body []byte) ([]topLevelJSONFieldSpan, int, error) { if !json.Valid(body) { return nil, 0, fmt.Errorf("invalid JSON request") } i := skipJSONSpace(body, 0) if i >= len(body) || body[i] != '{' { return nil, 0, fmt.Errorf("JSON request must be an object") } i++ fields := make([]topLevelJSONFieldSpan, 0, 8) for { i = skipJSONSpace(body, i) if i >= len(body) { return nil, 0, fmt.Errorf("invalid JSON request") } if body[i] == '}' { return fields, i, nil } memberFrom := i keyTo, err := scanJSONString(body, i) if err != nil { return nil, 0, fmt.Errorf("invalid JSON request") } var name string if err := json.Unmarshal(body[i:keyTo], &name); err != nil { return nil, 0, fmt.Errorf("invalid JSON request") } i = skipJSONSpace(body, keyTo) if i >= len(body) || body[i] != ':' { return nil, 0, fmt.Errorf("invalid JSON request") } i = skipJSONSpace(body, i+1) valueFrom := i valueTo, err := scanJSONValue(body, i) if err != nil { return nil, 0, fmt.Errorf("invalid JSON request") } i = skipJSONSpace(body, valueTo) memberTo := i fields = append(fields, topLevelJSONFieldSpan{ name: name, memberFrom: memberFrom, memberTo: memberTo, valueFrom: valueFrom, valueTo: valueTo, }) if i >= len(body) { return nil, 0, fmt.Errorf("invalid JSON request") } switch body[i] { case ',': i++ case '}': return fields, i, nil default: return nil, 0, fmt.Errorf("invalid JSON request") } } } func scanJSONString(body []byte, start int) (int, error) { if start >= len(body) || body[start] != '"' { return 0, fmt.Errorf("JSON string expected") } escaped := false for i := start + 1; i < len(body); i++ { if escaped { escaped = false continue } switch body[i] { case '\\': escaped = true case '"': return i + 1, nil } } return 0, fmt.Errorf("unterminated JSON string") } func scanJSONValue(body []byte, start int) (int, error) { if start >= len(body) { return 0, fmt.Errorf("JSON value expected") } if body[start] == '"' { return scanJSONString(body, start) } if body[start] == '{' || body[start] == '[' { stack := []byte{body[start]} inString, escaped := false, false for i := start + 1; i < len(body); i++ { ch := body[i] if inString { if escaped { escaped = false } else if ch == '\\' { escaped = true } else if ch == '"' { inString = false } continue } switch ch { case '"': inString = true case '{', '[': stack = append(stack, ch) case '}', ']': open := stack[len(stack)-1] if (open == '{' && ch != '}') || (open == '[' && ch != ']') { return 0, fmt.Errorf("mismatched JSON container") } stack = stack[:len(stack)-1] if len(stack) == 0 { return i + 1, nil } } } return 0, fmt.Errorf("unterminated JSON container") } for i := start; i < len(body); i++ { switch body[i] { case ' ', '\t', '\r', '\n', ',', '}', ']': if i == start { return 0, fmt.Errorf("JSON value expected") } return i, nil } } return len(body), nil } func skipJSONSpace(body []byte, i int) int { for i < len(body) { switch body[i] { case ' ', '\t', '\r', '\n': i++ default: return i } } return i }