You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser.go 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package gitgraph
  4. import (
  5. "bytes"
  6. "fmt"
  7. )
  8. // Parser represents a git graph parser. It is stateful containing the previous
  9. // glyphs, detected flows and color assignments.
  10. type Parser struct {
  11. glyphs []byte
  12. oldGlyphs []byte
  13. flows []int64
  14. oldFlows []int64
  15. maxFlow int64
  16. colors []int
  17. oldColors []int
  18. availableColors []int
  19. nextAvailable int
  20. firstInUse int
  21. firstAvailable int
  22. maxAllowedColors int
  23. }
  24. // Reset resets the internal parser state.
  25. func (parser *Parser) Reset() {
  26. parser.glyphs = parser.glyphs[0:0]
  27. parser.oldGlyphs = parser.oldGlyphs[0:0]
  28. parser.flows = parser.flows[0:0]
  29. parser.oldFlows = parser.oldFlows[0:0]
  30. parser.maxFlow = 0
  31. parser.colors = parser.colors[0:0]
  32. parser.oldColors = parser.oldColors[0:0]
  33. parser.availableColors = parser.availableColors[0:0]
  34. parser.availableColors = append(parser.availableColors, 1, 2)
  35. parser.nextAvailable = 0
  36. parser.firstInUse = -1
  37. parser.firstAvailable = 0
  38. parser.maxAllowedColors = 0
  39. }
  40. // AddLineToGraph adds the line as a row to the graph
  41. func (parser *Parser) AddLineToGraph(graph *Graph, row int, line []byte) error {
  42. idx := bytes.Index(line, []byte("DATA:"))
  43. if idx < 0 {
  44. parser.ParseGlyphs(line)
  45. } else {
  46. parser.ParseGlyphs(line[:idx])
  47. }
  48. var err error
  49. commitDone := false
  50. for column, glyph := range parser.glyphs {
  51. if glyph == ' ' {
  52. continue
  53. }
  54. flowID := parser.flows[column]
  55. graph.AddGlyph(row, column, flowID, parser.colors[column], glyph)
  56. if glyph == '*' {
  57. if commitDone {
  58. if err != nil {
  59. err = fmt.Errorf("double commit on line %d: %s. %w", row, string(line), err)
  60. } else {
  61. err = fmt.Errorf("double commit on line %d: %s", row, string(line))
  62. }
  63. }
  64. commitDone = true
  65. if idx < 0 {
  66. if err != nil {
  67. err = fmt.Errorf("missing data section on line %d with commit: %s. %w", row, string(line), err)
  68. } else {
  69. err = fmt.Errorf("missing data section on line %d with commit: %s", row, string(line))
  70. }
  71. continue
  72. }
  73. err2 := graph.AddCommit(row, column, flowID, line[idx+5:])
  74. if err != nil && err2 != nil {
  75. err = fmt.Errorf("%v %w", err2, err)
  76. continue
  77. } else if err2 != nil {
  78. err = err2
  79. continue
  80. }
  81. }
  82. }
  83. if !commitDone {
  84. graph.Commits = append(graph.Commits, RelationCommit)
  85. }
  86. return err
  87. }
  88. func (parser *Parser) releaseUnusedColors() {
  89. if parser.firstInUse > -1 {
  90. // Here we step through the old colors, searching for them in the
  91. // "in-use" section of availableColors (that is, the colors between
  92. // firstInUse and firstAvailable)
  93. // Ensure that the benchmarks are not worsened with proposed changes
  94. stepstaken := 0
  95. position := parser.firstInUse
  96. for _, color := range parser.oldColors {
  97. if color == 0 {
  98. continue
  99. }
  100. found := false
  101. i := position
  102. for j := stepstaken; i != parser.firstAvailable && j < len(parser.availableColors); j++ {
  103. colorToCheck := parser.availableColors[i]
  104. if colorToCheck == color {
  105. found = true
  106. break
  107. }
  108. i = (i + 1) % len(parser.availableColors)
  109. }
  110. if !found {
  111. // Duplicate color
  112. continue
  113. }
  114. // Swap them around
  115. parser.availableColors[position], parser.availableColors[i] = parser.availableColors[i], parser.availableColors[position]
  116. stepstaken++
  117. position = (parser.firstInUse + stepstaken) % len(parser.availableColors)
  118. if position == parser.firstAvailable || stepstaken == len(parser.availableColors) {
  119. break
  120. }
  121. }
  122. if stepstaken == len(parser.availableColors) {
  123. parser.firstAvailable = -1
  124. } else {
  125. parser.firstAvailable = position
  126. if parser.nextAvailable == -1 {
  127. parser.nextAvailable = parser.firstAvailable
  128. }
  129. }
  130. }
  131. }
  132. // ParseGlyphs parses the provided glyphs and sets the internal state
  133. func (parser *Parser) ParseGlyphs(glyphs []byte) {
  134. // Clean state for parsing this row
  135. parser.glyphs, parser.oldGlyphs = parser.oldGlyphs, parser.glyphs
  136. parser.glyphs = parser.glyphs[0:0]
  137. parser.flows, parser.oldFlows = parser.oldFlows, parser.flows
  138. parser.flows = parser.flows[0:0]
  139. parser.colors, parser.oldColors = parser.oldColors, parser.colors
  140. // Ensure we have enough flows and colors
  141. parser.colors = parser.colors[0:0]
  142. for range glyphs {
  143. parser.flows = append(parser.flows, 0)
  144. parser.colors = append(parser.colors, 0)
  145. }
  146. // Copy the provided glyphs in to state.glyphs for safekeeping
  147. parser.glyphs = append(parser.glyphs, glyphs...)
  148. // release unused colors
  149. parser.releaseUnusedColors()
  150. for i := len(glyphs) - 1; i >= 0; i-- {
  151. glyph := glyphs[i]
  152. switch glyph {
  153. case '|':
  154. fallthrough
  155. case '*':
  156. parser.setUpFlow(i)
  157. case '/':
  158. parser.setOutFlow(i)
  159. case '\\':
  160. parser.setInFlow(i)
  161. case '_':
  162. parser.setRightFlow(i)
  163. case '.':
  164. fallthrough
  165. case '-':
  166. parser.setLeftFlow(i)
  167. case ' ':
  168. // no-op
  169. default:
  170. parser.newFlow(i)
  171. }
  172. }
  173. }
  174. func (parser *Parser) takePreviousFlow(i, j int) {
  175. if j < len(parser.oldFlows) && parser.oldFlows[j] > 0 {
  176. parser.flows[i] = parser.oldFlows[j]
  177. parser.oldFlows[j] = 0
  178. parser.colors[i] = parser.oldColors[j]
  179. parser.oldColors[j] = 0
  180. } else {
  181. parser.newFlow(i)
  182. }
  183. }
  184. func (parser *Parser) takeCurrentFlow(i, j int) {
  185. if j < len(parser.flows) && parser.flows[j] > 0 {
  186. parser.flows[i] = parser.flows[j]
  187. parser.colors[i] = parser.colors[j]
  188. } else {
  189. parser.newFlow(i)
  190. }
  191. }
  192. func (parser *Parser) newFlow(i int) {
  193. parser.maxFlow++
  194. parser.flows[i] = parser.maxFlow
  195. // Now give this flow a color
  196. if parser.nextAvailable == -1 {
  197. next := len(parser.availableColors)
  198. if parser.maxAllowedColors < 1 || next < parser.maxAllowedColors {
  199. parser.nextAvailable = next
  200. parser.firstAvailable = next
  201. parser.availableColors = append(parser.availableColors, next+1)
  202. }
  203. }
  204. parser.colors[i] = parser.availableColors[parser.nextAvailable]
  205. if parser.firstInUse == -1 {
  206. parser.firstInUse = parser.nextAvailable
  207. }
  208. parser.availableColors[parser.firstAvailable], parser.availableColors[parser.nextAvailable] = parser.availableColors[parser.nextAvailable], parser.availableColors[parser.firstAvailable]
  209. parser.nextAvailable = (parser.nextAvailable + 1) % len(parser.availableColors)
  210. parser.firstAvailable = (parser.firstAvailable + 1) % len(parser.availableColors)
  211. if parser.nextAvailable == parser.firstInUse {
  212. parser.nextAvailable = parser.firstAvailable
  213. }
  214. if parser.nextAvailable == parser.firstInUse {
  215. parser.nextAvailable = -1
  216. parser.firstAvailable = -1
  217. }
  218. }
  219. // setUpFlow handles '|' or '*'
  220. func (parser *Parser) setUpFlow(i int) {
  221. // In preference order:
  222. //
  223. // Previous Row: '\? ' ' |' ' /'
  224. // Current Row: ' | ' ' |' ' | '
  225. if i > 0 && i-1 < len(parser.oldGlyphs) && parser.oldGlyphs[i-1] == '\\' {
  226. parser.takePreviousFlow(i, i-1)
  227. } else if i < len(parser.oldGlyphs) && (parser.oldGlyphs[i] == '|' || parser.oldGlyphs[i] == '*') {
  228. parser.takePreviousFlow(i, i)
  229. } else if i+1 < len(parser.oldGlyphs) && parser.oldGlyphs[i+1] == '/' {
  230. parser.takePreviousFlow(i, i+1)
  231. } else {
  232. parser.newFlow(i)
  233. }
  234. }
  235. // setOutFlow handles '/'
  236. func (parser *Parser) setOutFlow(i int) {
  237. // In preference order:
  238. //
  239. // Previous Row: ' |/' ' |_' ' |' ' /' ' _' '\'
  240. // Current Row: '/| ' '/| ' '/ ' '/ ' '/ ' '/'
  241. if i+2 < len(parser.oldGlyphs) &&
  242. (parser.oldGlyphs[i+1] == '|' || parser.oldGlyphs[i+1] == '*') &&
  243. (parser.oldGlyphs[i+2] == '/' || parser.oldGlyphs[i+2] == '_') &&
  244. i+1 < len(parser.glyphs) &&
  245. (parser.glyphs[i+1] == '|' || parser.glyphs[i+1] == '*') {
  246. parser.takePreviousFlow(i, i+2)
  247. } else if i+1 < len(parser.oldGlyphs) &&
  248. (parser.oldGlyphs[i+1] == '|' || parser.oldGlyphs[i+1] == '*' ||
  249. parser.oldGlyphs[i+1] == '/' || parser.oldGlyphs[i+1] == '_') {
  250. parser.takePreviousFlow(i, i+1)
  251. if parser.oldGlyphs[i+1] == '/' {
  252. parser.glyphs[i] = '|'
  253. }
  254. } else if i < len(parser.oldGlyphs) && parser.oldGlyphs[i] == '\\' {
  255. parser.takePreviousFlow(i, i)
  256. } else {
  257. parser.newFlow(i)
  258. }
  259. }
  260. // setInFlow handles '\'
  261. func (parser *Parser) setInFlow(i int) {
  262. // In preference order:
  263. //
  264. // Previous Row: '| ' '-. ' '| ' '\ ' '/' '---'
  265. // Current Row: '|\' ' \' ' \' ' \' '\' ' \ '
  266. if i > 0 && i-1 < len(parser.oldGlyphs) &&
  267. (parser.oldGlyphs[i-1] == '|' || parser.oldGlyphs[i-1] == '*') &&
  268. (parser.glyphs[i-1] == '|' || parser.glyphs[i-1] == '*') {
  269. parser.newFlow(i)
  270. } else if i > 0 && i-1 < len(parser.oldGlyphs) &&
  271. (parser.oldGlyphs[i-1] == '|' || parser.oldGlyphs[i-1] == '*' ||
  272. parser.oldGlyphs[i-1] == '.' || parser.oldGlyphs[i-1] == '\\') {
  273. parser.takePreviousFlow(i, i-1)
  274. if parser.oldGlyphs[i-1] == '\\' {
  275. parser.glyphs[i] = '|'
  276. }
  277. } else if i < len(parser.oldGlyphs) && parser.oldGlyphs[i] == '/' {
  278. parser.takePreviousFlow(i, i)
  279. } else {
  280. parser.newFlow(i)
  281. }
  282. }
  283. // setRightFlow handles '_'
  284. func (parser *Parser) setRightFlow(i int) {
  285. // In preference order:
  286. //
  287. // Current Row: '__' '_/' '_|_' '_|/'
  288. if i+1 < len(parser.glyphs) &&
  289. (parser.glyphs[i+1] == '_' || parser.glyphs[i+1] == '/') {
  290. parser.takeCurrentFlow(i, i+1)
  291. } else if i+2 < len(parser.glyphs) &&
  292. (parser.glyphs[i+1] == '|' || parser.glyphs[i+1] == '*') &&
  293. (parser.glyphs[i+2] == '_' || parser.glyphs[i+2] == '/') {
  294. parser.takeCurrentFlow(i, i+2)
  295. } else {
  296. parser.newFlow(i)
  297. }
  298. }
  299. // setLeftFlow handles '----.'
  300. func (parser *Parser) setLeftFlow(i int) {
  301. if parser.glyphs[i] == '.' {
  302. parser.newFlow(i)
  303. } else if i+1 < len(parser.glyphs) &&
  304. (parser.glyphs[i+1] == '-' || parser.glyphs[i+1] == '.') {
  305. parser.takeCurrentFlow(i, i+1)
  306. } else {
  307. parser.newFlow(i)
  308. }
  309. }