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.5KB

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