返回 DeepSeek-Reasonix
utf8.go
根目录 / internal / shellrun / utf8.go
1 package shellrun
2
3 import (
4 "strings"
5 "unicode/utf8"
6 )
7
8 // completePrefix drops only a final partial rune. Interior invalid bytes retain
9 // their existing behavior; transport must not manufacture invalid boundary bytes.
10 func completePrefix(p []byte) []byte {
11 start := len(p) - 1
12 for start >= 0 && !utf8.RuneStart(p[start]) {
13 start--
14 }
15 if start >= 0 && !utf8.FullRune(p[start:]) {
16 return p[:start]
17 }
18 return p
19 }
20
21 func completeTail(p []byte) []byte {
22 for len(p) > 0 && !utf8.RuneStart(p[0]) {
23 p = p[1:]
24 }
25 return completePrefix(p)
26 }
27
28 // Flush finishes a progress stream after the owner has drained the child,
29 // including cancellation. An unfinished rune is represented once, not silently
30 // lost or emitted as several invalid JSON strings.
31 func (w *progressWriter) Flush() {
32 w.mu.Lock()
33 defer w.mu.Unlock()
34 if w.emit == nil || w.truncated {
35 return
36 }
37 w.writeUTF8(nil, true)
38 }
39
40 func (w *progressWriter) writeUTF8(p []byte, final bool) {
41 // At most one additional rune is needed to detect truncation. Do not copy
42 // an arbitrarily large Write when joining it to a pending character.
43 p = p[:min(len(p), max(0, w.limit-w.forwarded)+utf8.UTFMax)]
44 if len(w.pending) > 0 {
45 p = append(w.pending, p...)
46 w.pending = nil
47 }
48 var out strings.Builder
49 truncated := false
50 for len(p) > 0 {
51 if !utf8.FullRune(p) {
52 if !final {
53 w.pending = append([]byte(nil), p...)
54 break
55 }
56 p = []byte(string(utf8.RuneError))
57 }
58 r, size := utf8.DecodeRune(p)
59 encodedSize := size
60 if r == utf8.RuneError && size == 1 {
61 encodedSize = 3
62 }
63 if w.forwarded+out.Len()+encodedSize > w.limit {
64 truncated = true
65 break
66 }
67 out.WriteRune(r)
68 p = p[size:]
69 }
70 if out.Len() > 0 {
71 w.forwarded += out.Len()
72 w.emit(out.String())
73 }
74 if truncated {
75 w.truncated = true
76 w.pending = nil
77 if w.marker != "" {
78 w.emit(w.marker)
79 }
80 }
81 }
82
82 lines GO