buffer.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package svg
  2. import (
  3. "github.com/tdewolff/parse/v2"
  4. "github.com/tdewolff/parse/v2/xml"
  5. minifyXML "github.com/tdewolff/minify/v2/xml"
  6. )
  7. // Token is a single token unit with an attribute value (if given) and hash of the data.
  8. type Token struct {
  9. xml.TokenType
  10. Hash Hash
  11. Data []byte
  12. Text []byte
  13. AttrVal []byte
  14. Offset int
  15. }
  16. // TokenBuffer is a buffer that allows for token look-ahead.
  17. type TokenBuffer struct {
  18. r *parse.Input
  19. l *xml.Lexer
  20. buf []Token
  21. pos int
  22. attrBuffer []*Token
  23. }
  24. // NewTokenBuffer returns a new TokenBuffer.
  25. func NewTokenBuffer(r *parse.Input, l *xml.Lexer) *TokenBuffer {
  26. return &TokenBuffer{
  27. r: r,
  28. l: l,
  29. buf: make([]Token, 0, 8),
  30. }
  31. }
  32. func (z *TokenBuffer) read(t *Token) {
  33. t.Offset = z.r.Offset()
  34. t.TokenType, t.Data = z.l.Next()
  35. t.Text = z.l.Text()
  36. if t.TokenType == xml.AttributeToken {
  37. t.Offset += 1 + len(t.Text) + 1
  38. t.AttrVal = z.l.AttrVal()
  39. if len(t.AttrVal) > 1 && (t.AttrVal[0] == '"' || t.AttrVal[0] == '\'') {
  40. t.Offset++
  41. t.AttrVal = t.AttrVal[1 : len(t.AttrVal)-1] // quotes will be readded in attribute loop if necessary
  42. t.AttrVal = parse.ReplaceMultipleWhitespaceAndEntities(t.AttrVal, minifyXML.EntitiesMap, nil)
  43. t.AttrVal = parse.TrimWhitespace(t.AttrVal)
  44. }
  45. t.Hash = ToHash(t.Text)
  46. } else if t.TokenType == xml.StartTagToken || t.TokenType == xml.EndTagToken {
  47. t.AttrVal = nil
  48. t.Hash = ToHash(t.Text)
  49. } else {
  50. t.AttrVal = nil
  51. t.Hash = 0
  52. }
  53. }
  54. // Peek returns the ith element and possibly does an allocation.
  55. // Peeking past an error will panic.
  56. func (z *TokenBuffer) Peek(pos int) *Token {
  57. pos += z.pos
  58. if pos >= len(z.buf) {
  59. if len(z.buf) > 0 && z.buf[len(z.buf)-1].TokenType == xml.ErrorToken {
  60. return &z.buf[len(z.buf)-1]
  61. }
  62. c := cap(z.buf)
  63. d := len(z.buf) - z.pos
  64. p := pos - z.pos + 1 // required peek length
  65. var buf []Token
  66. if 2*p > c {
  67. buf = make([]Token, 0, 2*c+p)
  68. } else {
  69. buf = z.buf
  70. }
  71. copy(buf[:d], z.buf[z.pos:])
  72. buf = buf[:p]
  73. pos -= z.pos
  74. for i := d; i < p; i++ {
  75. z.read(&buf[i])
  76. if buf[i].TokenType == xml.ErrorToken {
  77. buf = buf[:i+1]
  78. pos = i
  79. break
  80. }
  81. }
  82. z.pos, z.buf = 0, buf
  83. }
  84. return &z.buf[pos]
  85. }
  86. // Shift returns the first element and advances position.
  87. func (z *TokenBuffer) Shift() *Token {
  88. if z.pos >= len(z.buf) {
  89. t := &z.buf[:1][0]
  90. z.read(t)
  91. return t
  92. }
  93. t := &z.buf[z.pos]
  94. z.pos++
  95. return t
  96. }
  97. // Attributes extracts the gives attribute hashes from a tag.
  98. // It returns in the same order pointers to the requested token data or nil.
  99. func (z *TokenBuffer) Attributes(hashes ...Hash) []*Token {
  100. n := 0
  101. for {
  102. if t := z.Peek(n); t.TokenType != xml.AttributeToken {
  103. break
  104. }
  105. n++
  106. }
  107. if len(hashes) > cap(z.attrBuffer) {
  108. z.attrBuffer = make([]*Token, len(hashes))
  109. } else {
  110. z.attrBuffer = z.attrBuffer[:len(hashes)]
  111. for i := range z.attrBuffer {
  112. z.attrBuffer[i] = nil
  113. }
  114. }
  115. for i := z.pos; i < z.pos+n; i++ {
  116. attr := &z.buf[i]
  117. for j, hash := range hashes {
  118. if hash == attr.Hash {
  119. z.attrBuffer[j] = attr
  120. }
  121. }
  122. }
  123. return z.attrBuffer
  124. }