1package vt
2
3import (
4 "encoding/binary"
5 "math"
6 "unicode/utf8"
7
8 renderer "github.com/bnema/vev-vt/core"
9)
10
11func appendHistoryStyle(dst []byte, style renderer.Style) []byte {
12 style = style.Canonical()
13 flags := byte(0)
14 for i, set := range [...]bool{style.Bold, style.Italic, style.Inverse, style.HasForegroundRGB, style.HasBackgroundRGB, style.HasUnderlineColor, style.HasUnderlineColorRGB} {
15 if set {
16 flags |= 1 << i
17 }
18 }
19 dst = append(dst, flags)
20 dst = binary.BigEndian.AppendUint16(dst, uint16(style.Attrs))
21 dst = binary.BigEndian.AppendUint64(dst, uint64(int64(style.Foreground)))
22 dst = binary.BigEndian.AppendUint64(dst, uint64(int64(style.Background)))
23 dst = append(dst, style.ForegroundRGB.R, style.ForegroundRGB.G, style.ForegroundRGB.B)
24 dst = append(dst, style.BackgroundRGB.R, style.BackgroundRGB.G, style.BackgroundRGB.B)
25 dst = append(dst, byte(style.UnderlineStyle))
26 dst = binary.BigEndian.AppendUint64(dst, uint64(int64(style.UnderlineColor)))
27 return append(dst, style.UnderlineColorRGB.R, style.UnderlineColorRGB.G, style.UnderlineColorRGB.B)
28}
29
30func appendHistoryBound(dst []byte, bound LineBound) []byte {
31 dst = binary.BigEndian.AppendUint32(dst, uint32(bound.End))
32 if bound.Soft {
33 return append(dst, 1)
34 }
35 return append(dst, 0)
36}
37
38type historyParser struct{ data []byte }
39
40func (p *historyParser) uint32() (uint32, bool) {
41 if len(p.data) < 4 {
42 return 0, false
43 }
44 value := binary.BigEndian.Uint32(p.data)
45 p.data = p.data[4:]
46 return value, true
47}
48
49func (p *historyParser) uint64() (uint64, bool) {
50 if len(p.data) < 8 {
51 return 0, false
52 }
53 value := binary.BigEndian.Uint64(p.data)
54 p.data = p.data[8:]
55 return value, true
56}
57
58func (p *historyParser) style() (renderer.Style, bool) {
59 if len(p.data) < historyStyleBytes {
60 return renderer.Style{}, false
61 }
62 b := p.data[:historyStyleBytes]
63 p.data = p.data[historyStyleBytes:]
64 flags := b[0]
65 if flags&0x80 != 0 {
66 return renderer.Style{}, false
67 }
68 foreground, fok := historyInt(binary.BigEndian.Uint64(b[3:11]))
69 background, bok := historyInt(binary.BigEndian.Uint64(b[11:19]))
70 underline, uok := historyInt(binary.BigEndian.Uint64(b[26:34]))
71 if !fok || !bok || !uok {
72 return renderer.Style{}, false
73 }
74 return renderer.Style{
75 Bold: flags&1 != 0,
76 Italic: flags&2 != 0,
77 Inverse: flags&4 != 0,
78 Attrs: renderer.StyleAttrs(binary.BigEndian.Uint16(b[1:3])),
79 Foreground: foreground,
80 Background: background,
81 HasForegroundRGB: flags&8 != 0,
82 ForegroundRGB: renderer.RGB{R: b[19], G: b[20], B: b[21]},
83 HasBackgroundRGB: flags&16 != 0,
84 BackgroundRGB: renderer.RGB{R: b[22], G: b[23], B: b[24]},
85 UnderlineStyle: renderer.UnderlineStyle(b[25]),
86 HasUnderlineColor: flags&32 != 0,
87 UnderlineColor: underline,
88 HasUnderlineColorRGB: flags&64 != 0,
89 UnderlineColorRGB: renderer.RGB{R: b[34], G: b[35], B: b[36]},
90 }, true
91}
92
93func (p *historyParser) bound() (LineBound, bool) {
94 if len(p.data) < historyBoundBytes {
95 return LineBound{}, false
96 }
97 end := binary.BigEndian.Uint32(p.data[:4])
98 flag := p.data[4]
99 p.data = p.data[historyBoundBytes:]
100 if flag > 1 || uint64(end) > uint64(math.MaxInt) {
101 return LineBound{}, false
102 }
103 return LineBound{End: int(end), Soft: flag == 1}, true
104}
105
106func (p *historyParser) storedCell(styles, payloads uint32) (rune, uint32, uint32, bool, bool) {
107 if len(p.data) < historyStoredCellBytes {
108 return 0, 0, 0, false, false
109 }
110 b := p.data[:historyStoredCellBytes]
111 p.data = p.data[historyStoredCellBytes:]
112 r := rune(binary.BigEndian.Uint32(b[:4]))
113 id := binary.BigEndian.Uint32(b[4:8])
114 if !utf8.ValidRune(r) || id >= styles || b[8] > 3 {
115 return 0, 0, 0, false, false
116 }
117 var payloadID uint32
118 if b[8]&2 != 0 {
119 var ok bool
120 payloadID, ok = p.uint32()
121 if !ok || payloadID == 0 || payloadID > payloads {
122 return 0, 0, 0, false, false
123 }
124 }
125 return r, id, payloadID, b[8]&1 != 0, true
126}
127
128func historyInt(raw uint64) (int, bool) {
129 value := int64(raw)
130 return int(value), int64(int(value)) == value
131}
132
133func validHistoryCell(cell renderer.Cell) bool {
134 return utf8.ValidRune(cell.Rune) && cell.Style.UnderlineStyle <= renderer.UnderlineDashed
135}