File: buffer.go

package info (click to toggle)
golang-github-tdewolff-minify 2.20.37-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 39,388 kB
  • sloc: javascript: 394,644; xml: 25,649; ansic: 253; makefile: 108; python: 108; sh: 47
file content (136 lines) | stat: -rw-r--r-- 3,068 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package svg

import (
	"github.com/tdewolff/parse/v2"
	"github.com/tdewolff/parse/v2/xml"
	minifyXML "github.com/tdewolff/minify/v2/xml"
)

// Token is a single token unit with an attribute value (if given) and hash of the data.
type Token struct {
	xml.TokenType
	Hash    Hash
	Data    []byte
	Text    []byte
	AttrVal []byte
	Offset  int
}

// TokenBuffer is a buffer that allows for token look-ahead.
type TokenBuffer struct {
	r *parse.Input
	l *xml.Lexer

	buf []Token
	pos int

	attrBuffer []*Token
}

// NewTokenBuffer returns a new TokenBuffer.
func NewTokenBuffer(r *parse.Input, l *xml.Lexer) *TokenBuffer {
	return &TokenBuffer{
		r:   r,
		l:   l,
		buf: make([]Token, 0, 8),
	}
}

func (z *TokenBuffer) read(t *Token) {
	t.Offset = z.r.Offset()
	t.TokenType, t.Data = z.l.Next()
	t.Text = z.l.Text()
	if t.TokenType == xml.AttributeToken {
		t.Offset += 1 + len(t.Text) + 1
		t.AttrVal = z.l.AttrVal()
		if len(t.AttrVal) > 1 && (t.AttrVal[0] == '"' || t.AttrVal[0] == '\'') {
			t.Offset++
			t.AttrVal = t.AttrVal[1 : len(t.AttrVal)-1] // quotes will be readded in attribute loop if necessary
			t.AttrVal = parse.ReplaceMultipleWhitespaceAndEntities(t.AttrVal, minifyXML.EntitiesMap, nil)
			t.AttrVal = parse.TrimWhitespace(t.AttrVal)
		}
		t.Hash = ToHash(t.Text)
	} else if t.TokenType == xml.StartTagToken || t.TokenType == xml.EndTagToken {
		t.AttrVal = nil
		t.Hash = ToHash(t.Text)
	} else {
		t.AttrVal = nil
		t.Hash = 0
	}
}

// Peek returns the ith element and possibly does an allocation.
// Peeking past an error will panic.
func (z *TokenBuffer) Peek(pos int) *Token {
	pos += z.pos
	if pos >= len(z.buf) {
		if len(z.buf) > 0 && z.buf[len(z.buf)-1].TokenType == xml.ErrorToken {
			return &z.buf[len(z.buf)-1]
		}

		c := cap(z.buf)
		d := len(z.buf) - z.pos
		p := pos - z.pos + 1 // required peek length
		var buf []Token
		if 2*p > c {
			buf = make([]Token, 0, 2*c+p)
		} else {
			buf = z.buf
		}
		copy(buf[:d], z.buf[z.pos:])

		buf = buf[:p]
		pos -= z.pos
		for i := d; i < p; i++ {
			z.read(&buf[i])
			if buf[i].TokenType == xml.ErrorToken {
				buf = buf[:i+1]
				pos = i
				break
			}
		}
		z.pos, z.buf = 0, buf
	}
	return &z.buf[pos]
}

// Shift returns the first element and advances position.
func (z *TokenBuffer) Shift() *Token {
	if z.pos >= len(z.buf) {
		t := &z.buf[:1][0]
		z.read(t)
		return t
	}
	t := &z.buf[z.pos]
	z.pos++
	return t
}

// Attributes extracts the gives attribute hashes from a tag.
// It returns in the same order pointers to the requested token data or nil.
func (z *TokenBuffer) Attributes(hashes ...Hash) []*Token {
	n := 0
	for {
		if t := z.Peek(n); t.TokenType != xml.AttributeToken {
			break
		}
		n++
	}
	if len(hashes) > cap(z.attrBuffer) {
		z.attrBuffer = make([]*Token, len(hashes))
	} else {
		z.attrBuffer = z.attrBuffer[:len(hashes)]
		for i := range z.attrBuffer {
			z.attrBuffer[i] = nil
		}
	}
	for i := z.pos; i < z.pos+n; i++ {
		attr := &z.buf[i]
		for j, hash := range hashes {
			if hash == attr.Hash {
				z.attrBuffer[j] = attr
			}
		}
	}
	return z.attrBuffer
}