QueryTreeBuilder.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Dispatcher
  5. {
  6. using System.Runtime;
  7. class QueryTreeBuilder
  8. {
  9. Diverger diverger;
  10. Opcode lastOpcode;
  11. internal QueryTreeBuilder()
  12. {
  13. }
  14. internal Opcode LastOpcode
  15. {
  16. get
  17. {
  18. return this.lastOpcode;
  19. }
  20. }
  21. internal Opcode Build(Opcode tree, OpcodeBlock newBlock)
  22. {
  23. if (null == tree)
  24. {
  25. this.lastOpcode = newBlock.Last;
  26. return newBlock.First;
  27. }
  28. this.diverger = new Diverger(tree, newBlock.First);
  29. if (!this.diverger.Find())
  30. {
  31. // The opcodes in newBlock already have equivalents or identical opcodes
  32. // in the query tree that can do the job
  33. Fx.Assert(this.diverger.TreePath.Count > 0, "");
  34. this.lastOpcode = this.diverger.TreePath[this.diverger.TreePath.Count - 1];
  35. return tree;
  36. }
  37. Fx.Assert(this.diverger.TreePath.Count == this.diverger.InsertPath.Count, "");
  38. // We can reuse opcodes upto this.diverger.TreePath[this.diverger.TreePath.Count - 1]
  39. // The remainder of the code in newBlock must be executed as is...
  40. if (null == this.diverger.TreeOpcode)
  41. {
  42. // We reached a leaf in the query tree
  43. // Simply add the remainder of the inserted code to the end of the tree path..
  44. this.diverger.TreePath[this.diverger.TreePath.Count - 1].Attach(this.diverger.InsertOpcode);
  45. }
  46. else
  47. {
  48. // Merge in the remaider of the new code block into the query tree
  49. // The first diverging opcodes follow the last entry in each path
  50. this.diverger.TreeOpcode.Add(this.diverger.InsertOpcode);
  51. }
  52. this.lastOpcode = newBlock.Last;
  53. if (this.diverger.InsertOpcode.IsMultipleResult())
  54. {
  55. // The complete new block was merged in, except for the the actual result opcode, which never
  56. // automatically merges. This means that the new block found all of its opcodes in common with
  57. // the tree
  58. if (OpcodeID.Branch == this.diverger.TreeOpcode.ID)
  59. {
  60. OpcodeList branches = (((BranchOpcode) this.diverger.TreeOpcode).Branches);
  61. for (int i = 0, count = branches.Count; i < count; ++i)
  62. {
  63. if (branches[i].IsMultipleResult())
  64. {
  65. this.lastOpcode = branches[i];
  66. break;
  67. }
  68. }
  69. }
  70. else if (this.diverger.TreeOpcode.IsMultipleResult())
  71. {
  72. this.lastOpcode = this.diverger.TreeOpcode;
  73. }
  74. }
  75. // Since we'll be diverging, any jumps that preceeded and leapt past the divergence point
  76. // will have to be branched
  77. this.FixupJumps();
  78. return tree;
  79. }
  80. void FixupJumps()
  81. {
  82. QueryBuffer<Opcode> treePath = this.diverger.TreePath;
  83. QueryBuffer<Opcode> insertPath = this.diverger.InsertPath;
  84. for (int i = 0; i < insertPath.Count; ++i)
  85. {
  86. if (insertPath[i].TestFlag(OpcodeFlags.Jump))
  87. {
  88. Fx.Assert(treePath[i].ID == insertPath[i].ID, "");
  89. JumpOpcode insertJump = (JumpOpcode) insertPath[i];
  90. // Opcodes in 'insertPath' have equivalent opcodes in the query tree: i.e. the query tree contains an
  91. // an equivalent execution path (upto the point of divergence naturally) that will produce in an identical
  92. // result. The remainder of the query tree (anything that lies beyond the point of divergence) represents
  93. // a distinct execution path and is grafted onto the tree as a new branch. In fact, we simply break off
  94. // the remainder from the query being inserted and graft it onto the query tree.
  95. // If there are jumps on the insert path that jump to opcodes NOT in the insert path, then the jumps
  96. // will reach opcodes in the new branch we will add(see above). However, because the actual jump opcodes
  97. // are shared (used as is from the query tree), the actual jump must also be branched. One jump will
  98. // continue to jump to the original opcode and the second new one will jump to an opcode in the grafted branch.
  99. if (-1 == insertPath.IndexOf(insertJump.Jump, i + 1))
  100. {
  101. Fx.Assert(insertJump.Jump.ID == OpcodeID.BlockEnd, "");
  102. BlockEndOpcode jumpTo = (BlockEndOpcode) insertJump.Jump;
  103. // no longer jumping from insertJump to jumpTo
  104. insertJump.RemoveJump(jumpTo);
  105. // Instead, jumping from treePath[i] to jumpTo
  106. JumpOpcode treeJump = (JumpOpcode) treePath[i];
  107. treeJump.AddJump(jumpTo);
  108. }
  109. }
  110. }
  111. }
  112. // Can handle queries being merged into trees but not trees merged into trees.
  113. // In other words, no branch opcodes in the query being inserted
  114. internal struct Diverger
  115. {
  116. Opcode treeOpcode;
  117. QueryBuffer<Opcode> treePath;
  118. QueryBuffer<Opcode> insertPath;
  119. Opcode insertOpcode;
  120. internal Diverger(Opcode tree, Opcode insert)
  121. {
  122. this.treePath = new QueryBuffer<Opcode>(16);
  123. this.insertPath = new QueryBuffer<Opcode>(16);
  124. this.treeOpcode = tree;
  125. this.insertOpcode = insert;
  126. }
  127. internal Opcode InsertOpcode
  128. {
  129. get
  130. {
  131. return this.insertOpcode;
  132. }
  133. }
  134. internal QueryBuffer<Opcode> InsertPath
  135. {
  136. get
  137. {
  138. return this.insertPath;
  139. }
  140. }
  141. internal Opcode TreeOpcode
  142. {
  143. get
  144. {
  145. return this.treeOpcode;
  146. }
  147. }
  148. internal QueryBuffer<Opcode> TreePath
  149. {
  150. get
  151. {
  152. return this.treePath;
  153. }
  154. }
  155. // Stops at the last common node on each
  156. internal bool Find()
  157. {
  158. Opcode treeNext = null;
  159. while (true)
  160. {
  161. if (null == this.treeOpcode && null == this.insertOpcode)
  162. {
  163. return false; // no diverge. both ran out at the same time
  164. }
  165. if (null == this.insertOpcode)
  166. {
  167. return false; // Ran out before tree did. No divergence.
  168. }
  169. if (null == this.treeOpcode)
  170. {
  171. return true; // tree ran out before insert. Divergence
  172. }
  173. if (this.treeOpcode.TestFlag(OpcodeFlags.Branch))
  174. {
  175. treeNext = this.treeOpcode.Locate(this.insertOpcode);
  176. if (null == treeNext)
  177. {
  178. return true; // divergence
  179. }
  180. this.treeOpcode = treeNext;
  181. treeNext = treeNext.Next;
  182. }
  183. else
  184. {
  185. if (!this.treeOpcode.Equals(this.insertOpcode))
  186. {
  187. return true; // divergence, obviously
  188. }
  189. treeNext = this.treeOpcode.Next;
  190. }
  191. // No divergence. Add to paths
  192. this.treePath.Add(this.treeOpcode);
  193. this.insertPath.Add(this.insertOpcode);
  194. this.insertOpcode = this.insertOpcode.Next;
  195. this.treeOpcode = treeNext;
  196. }
  197. }
  198. }
  199. }
  200. }