file.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package burndown
  2. import (
  3. "fmt"
  4. "gopkg.in/src-d/hercules.v4/internal"
  5. "gopkg.in/src-d/hercules.v4/internal/rbtree"
  6. )
  7. // Status is the something we would like to keep track of in File.Update().
  8. type Status struct {
  9. data interface{}
  10. update func(interface{}, int, int, int)
  11. }
  12. // File encapsulates a balanced binary tree to store line intervals and
  13. // a cumulative mapping of values to the corresponding length counters. Users
  14. // are not supposed to create File-s directly; instead, they should call NewFile().
  15. // NewFileFromTree() is the special constructor which is useful in the tests.
  16. //
  17. // Len() returns the number of lines in File.
  18. //
  19. // Update() mutates File by introducing tree structural changes and updating the
  20. // length mapping.
  21. //
  22. // Dump() writes the tree to a string and Validate() checks the tree integrity.
  23. type File struct {
  24. tree *rbtree.RBTree
  25. statuses []Status
  26. }
  27. // NewStatus initializes a new instance of Status struct. It is needed to set the only two
  28. // private fields which are not supposed to be replaced during the whole lifetime.
  29. func NewStatus(data interface{}, update func(interface{}, int, int, int)) Status {
  30. return Status{data: data, update: update}
  31. }
  32. // TreeEnd denotes the value of the last leaf in the tree.
  33. const TreeEnd int = -1
  34. func (file *File) updateTime(currentTime int, previousTime int, delta int) {
  35. for _, status := range file.statuses {
  36. status.update(status.data, currentTime, previousTime, delta)
  37. }
  38. }
  39. // NewFile initializes a new instance of File struct.
  40. //
  41. // time is the starting value of the first node;
  42. //
  43. // length is the starting length of the tree (the key of the second and the
  44. // last node);
  45. //
  46. // statuses are the attached interval length mappings.
  47. func NewFile(time int, length int, statuses ...Status) *File {
  48. file := new(File)
  49. file.statuses = statuses
  50. file.tree = new(rbtree.RBTree)
  51. if length > 0 {
  52. file.updateTime(time, time, length)
  53. file.tree.Insert(rbtree.Item{Key: 0, Value: time})
  54. }
  55. file.tree.Insert(rbtree.Item{Key: length, Value: TreeEnd})
  56. return file
  57. }
  58. // NewFileFromTree is an alternative constructor for File which is used in tests.
  59. // The resulting tree is validated with Validate() to ensure the initial integrity.
  60. //
  61. // keys is a slice with the starting tree keys.
  62. //
  63. // vals is a slice with the starting tree values. Must match the size of keys.
  64. //
  65. // statuses are the attached interval length mappings.
  66. func NewFileFromTree(keys []int, vals []int, statuses ...Status) *File {
  67. file := new(File)
  68. file.statuses = statuses
  69. file.tree = new(rbtree.RBTree)
  70. if len(keys) != len(vals) {
  71. panic("keys and vals must be of equal length")
  72. }
  73. for i := 0; i < len(keys); i++ {
  74. file.tree.Insert(rbtree.Item{Key: keys[i], Value: vals[i]})
  75. }
  76. file.Validate()
  77. return file
  78. }
  79. // Len returns the File's size - that is, the maximum key in the tree of line
  80. // intervals.
  81. func (file *File) Len() int {
  82. return file.tree.Max().Item().Key
  83. }
  84. // Update modifies the underlying tree to adapt to the specified line changes.
  85. //
  86. // time is the time when the requested changes are made. Sets the values of the
  87. // inserted nodes.
  88. //
  89. // pos is the index of the line at which the changes are introduced.
  90. //
  91. // ins_length is the number of inserted lines after pos.
  92. //
  93. // del_length is the number of removed lines after pos. Deletions come before
  94. // the insertions.
  95. //
  96. // The code inside this function is probably the most important one throughout
  97. // the project. It is extensively covered with tests. If you find a bug, please
  98. // add the corresponding case in file_test.go.
  99. func (file *File) Update(time int, pos int, insLength int, delLength int) {
  100. if time < 0 {
  101. panic("time may not be negative")
  102. }
  103. if pos < 0 {
  104. panic("attempt to insert/delete at a negative position")
  105. }
  106. if insLength < 0 || delLength < 0 {
  107. panic("insLength and delLength must be non-negative")
  108. }
  109. if insLength|delLength == 0 {
  110. return
  111. }
  112. tree := file.tree
  113. if tree.Len() < 2 && tree.Min().Item().Key != 0 {
  114. panic("invalid tree state")
  115. }
  116. if pos > tree.Max().Item().Key {
  117. panic(fmt.Sprintf("attempt to insert after the end of the file: %d < %d",
  118. tree.Max().Item().Key, pos))
  119. }
  120. iter := tree.FindLE(pos)
  121. origin := *iter.Item()
  122. file.updateTime(time, time, insLength)
  123. if delLength == 0 {
  124. // simple case with insertions only
  125. if origin.Key < pos || (origin.Value == time && pos == 0) {
  126. iter = iter.Next()
  127. }
  128. for ; !iter.Limit(); iter = iter.Next() {
  129. iter.Item().Key += insLength
  130. }
  131. if origin.Value != time {
  132. tree.Insert(rbtree.Item{Key: pos, Value: time})
  133. if origin.Key < pos {
  134. tree.Insert(rbtree.Item{Key: pos + insLength, Value: origin.Value})
  135. }
  136. }
  137. return
  138. }
  139. // delete nodes
  140. for true {
  141. node := iter.Item()
  142. nextIter := iter.Next()
  143. if nextIter.Limit() {
  144. if pos+delLength > node.Key {
  145. panic("attempt to delete after the end of the file")
  146. }
  147. break
  148. }
  149. delta := internal.Min(nextIter.Item().Key, pos+delLength) - internal.Max(node.Key, pos)
  150. if delta <= 0 {
  151. break
  152. }
  153. file.updateTime(time, node.Value, -delta)
  154. if node.Key >= pos {
  155. origin = *node
  156. tree.DeleteWithIterator(iter)
  157. }
  158. iter = nextIter
  159. }
  160. // prepare for the keys update
  161. var previous *rbtree.Item
  162. if insLength > 0 && (origin.Value != time || origin.Key == pos) {
  163. // insert our new interval
  164. if iter.Item().Value == time {
  165. prev := iter.Prev()
  166. if prev.Item().Value != time {
  167. iter.Item().Key = pos
  168. } else {
  169. tree.DeleteWithIterator(iter)
  170. iter = prev
  171. }
  172. origin.Value = time // cancels the insertion after applying the delta
  173. } else {
  174. _, iter = tree.Insert(rbtree.Item{Key: pos, Value: time})
  175. }
  176. } else {
  177. // rollback 1 position back, see "for true" deletion cycle ^
  178. iter = iter.Prev()
  179. previous = iter.Item()
  180. }
  181. // update the keys of all subsequent nodes
  182. delta := insLength - delLength
  183. if delta != 0 {
  184. for iter = iter.Next(); !iter.Limit(); iter = iter.Next() {
  185. // we do not need to re-balance the tree
  186. iter.Item().Key += delta
  187. }
  188. // have to adjust origin in case insLength == 0
  189. if origin.Key > pos {
  190. origin.Key += delta
  191. }
  192. }
  193. if insLength > 0 {
  194. if origin.Value != time {
  195. tree.Insert(rbtree.Item{Key: pos + insLength, Value: origin.Value})
  196. } else if pos == 0 {
  197. // recover the beginning
  198. tree.Insert(rbtree.Item{Key: pos, Value: time})
  199. }
  200. } else if (pos > origin.Key && previous.Value != origin.Value) || pos == origin.Key || pos == 0 {
  201. // continue the original interval
  202. tree.Insert(rbtree.Item{Key: pos, Value: origin.Value})
  203. }
  204. }
  205. // Status returns the bound status object by the specified index.
  206. func (file *File) Status(index int) interface{} {
  207. if index < 0 || index >= len(file.statuses) {
  208. panic(fmt.Sprintf("status index %d is out of bounds [0, %d)",
  209. index, len(file.statuses)))
  210. }
  211. return file.statuses[index].data
  212. }
  213. // Dump formats the underlying line interval tree into a string.
  214. // Useful for error messages, panic()-s and debugging.
  215. func (file *File) Dump() string {
  216. buffer := ""
  217. for iter := file.tree.Min(); !iter.Limit(); iter = iter.Next() {
  218. node := iter.Item()
  219. buffer += fmt.Sprintf("%d %d\n", node.Key, node.Value)
  220. }
  221. return buffer
  222. }
  223. // Validate checks the underlying line interval tree integrity.
  224. // The checks are as follows:
  225. //
  226. // 1. The minimum key must be 0 because the first line index is always 0.
  227. //
  228. // 2. The last node must carry TreeEnd value. This is the maintained invariant
  229. // which marks the ending of the last line interval.
  230. //
  231. // 3. Node keys must monotonically increase and never duplicate.
  232. func (file *File) Validate() {
  233. if file.tree.Min().Item().Key != 0 {
  234. panic("the tree must start with key 0")
  235. }
  236. if file.tree.Max().Item().Value != TreeEnd {
  237. panic(fmt.Sprintf("the last value in the tree must be %d", TreeEnd))
  238. }
  239. prevKey := -1
  240. for iter := file.tree.Min(); !iter.Limit(); iter = iter.Next() {
  241. node := iter.Item()
  242. if node.Key == prevKey {
  243. panic(fmt.Sprintf("duplicate tree key: %d", node.Key))
  244. }
  245. prevKey = node.Key
  246. }
  247. }