1. my class MixHash does Mixy {
  2. #--- interface methods
  3. method total() { Rakudo::QuantHash.MIX-TOTAL($!elems) }
  4. method !total-positive() { Rakudo::QuantHash.MIX-TOTAL-POSITIVE($!elems) }
  5. method STORE(*@pairs --> MixHash:D) {
  6. nqp::if(
  7. (my $iterator := @pairs.iterator).is-lazy,
  8. Failure.new(X::Cannot::Lazy.new(:action<initialize>,:what(self.^name))),
  9. self.SET-SELF(
  10. Rakudo::QuantHash.ADD-PAIRS-TO-MIX(
  11. nqp::create(Rakudo::Internals::IterationSet), $iterator
  12. )
  13. )
  14. )
  15. }
  16. multi method AT-KEY(MixHash:D: \k) is raw {
  17. Proxy.new(
  18. FETCH => {
  19. nqp::if(
  20. $!elems && nqp::existskey($!elems,(my $which := k.WHICH)),
  21. nqp::getattr(nqp::atkey($!elems,$which),Pair,'$!value'),
  22. 0
  23. )
  24. },
  25. STORE => -> $, Real() $value {
  26. nqp::if(
  27. nqp::istype($value,Failure), # RT 128927
  28. $value.throw,
  29. nqp::if(
  30. $!elems,
  31. nqp::if( # allocated hash
  32. nqp::existskey($!elems,(my $which := k.WHICH)),
  33. nqp::if( # existing element
  34. $value == 0,
  35. nqp::stmts(
  36. nqp::deletekey($!elems,$which),
  37. 0
  38. ),
  39. nqp::bindattr(
  40. nqp::atkey($!elems,$which),
  41. Pair,
  42. '$!value',
  43. nqp::decont($value)
  44. ),
  45. ),
  46. nqp::unless(
  47. $value == 0,
  48. nqp::bindkey($!elems,$which,Pair.new(k,nqp::decont($value)))
  49. )
  50. ),
  51. nqp::unless( # no hash allocated yet
  52. $value == 0,
  53. nqp::bindkey(
  54. nqp::bindattr(self,::?CLASS,'$!elems',
  55. nqp::create(Rakudo::Internals::IterationSet)),
  56. k.WHICH,
  57. Pair.new(k,nqp::decont($value))
  58. )
  59. )
  60. )
  61. )
  62. }
  63. )
  64. }
  65. #--- object creation methods
  66. multi method new(MixHash:_:) { nqp::create(self) }
  67. #--- coercion methods
  68. multi method Mix(MixHash:D: :$view) {
  69. nqp::if(
  70. $!elems && nqp::elems($!elems),
  71. nqp::p6bindattrinvres(
  72. nqp::create(Mix),Mix,'$!elems',
  73. nqp::if($view,$!elems,$!elems.clone)
  74. ),
  75. mix()
  76. )
  77. }
  78. multi method MixHash(MixHash:D:) { self }
  79. method clone() {
  80. nqp::if(
  81. $!elems && nqp::elems($!elems),
  82. nqp::create(MixHash).SET-SELF(Rakudo::QuantHash.BAGGY-CLONE($!elems)),
  83. nqp::create(MixHash)
  84. )
  85. }
  86. #--- iterator methods
  87. sub proxy(Mu \iter,Mu \storage) is raw {
  88. # We are only sure that the key exists when the Proxy
  89. # is made, but we cannot be sure of its existence when
  90. # either the FETCH or STORE block is executed. So we
  91. # still need to check for existence, and handle the case
  92. # where we need to (re-create) the key and value. The
  93. # logic is therefore basically the same as in AT-KEY,
  94. # except for tests for allocated storage and .WHICH
  95. # processing.
  96. nqp::stmts(
  97. (my $which := nqp::iterkey_s(iter)),
  98. # save for possible object recreation
  99. (my $object := nqp::getattr(nqp::iterval(iter),Pair,'$!key')),
  100. Proxy.new(
  101. FETCH => {
  102. nqp::if(
  103. nqp::existskey(storage,$which),
  104. nqp::getattr(nqp::atkey(storage,$which),Pair,'$!value'),
  105. 0
  106. )
  107. },
  108. STORE => -> $, Real() $value {
  109. nqp::if(
  110. nqp::istype($value,Failure), # RT 128927
  111. $value.throw,
  112. nqp::if(
  113. nqp::existskey(storage,$which),
  114. nqp::if( # existing element
  115. $value == 0,
  116. nqp::stmts( # goodbye!
  117. nqp::deletekey(storage,$which),
  118. 0
  119. ),
  120. nqp::bindattr( # value ok
  121. nqp::atkey(storage,$which),
  122. Pair,
  123. '$!value',
  124. nqp::decont($value)
  125. )
  126. ),
  127. nqp::unless( # where did it go?
  128. $value == 0,
  129. nqp::bindkey(
  130. storage,
  131. $which,
  132. Pair.new($object,nqp::decont($value))
  133. )
  134. )
  135. )
  136. )
  137. }
  138. )
  139. )
  140. }
  141. multi method iterator(MixHash:D:) { # also .pairs
  142. class :: does Rakudo::Iterator::Mappy {
  143. method pull-one() is raw {
  144. nqp::if(
  145. $!iter,
  146. nqp::p6bindattrinvres(
  147. nqp::clone(nqp::iterval(nqp::shift($!iter))),
  148. Pair,
  149. '$!value',
  150. proxy($!iter,$!hash)
  151. ),
  152. IterationEnd
  153. )
  154. }
  155. method push-all($target --> IterationEnd) {
  156. nqp::while( # doesn't sink
  157. $!iter,
  158. $target.push(nqp::iterval(nqp::shift($!iter)))
  159. )
  160. }
  161. }.new($!elems)
  162. }
  163. multi method values(MixHash:D:) {
  164. Seq.new(class :: does Rakudo::Iterator::Mappy {
  165. method pull-one() is raw {
  166. nqp::if(
  167. $!iter,
  168. proxy(nqp::shift($!iter),$!hash),
  169. IterationEnd
  170. )
  171. }
  172. # same as Baggy.values
  173. method push-all($target --> IterationEnd) {
  174. nqp::while( # doesn't sink
  175. $!iter,
  176. $target.push(nqp::getattr(
  177. nqp::iterval(nqp::shift($!iter)),Pair,'$!value'))
  178. )
  179. }
  180. }.new($!elems))
  181. }
  182. multi method kv(MixHash:D:) {
  183. Seq.new(class :: does Rakudo::Iterator::Mappy-kv-from-pairs {
  184. method pull-one() is raw {
  185. nqp::if(
  186. $!on,
  187. nqp::stmts(
  188. ($!on = 0),
  189. proxy($!iter,$!hash)
  190. ),
  191. nqp::if(
  192. $!iter,
  193. nqp::stmts(
  194. ($!on = 1),
  195. nqp::getattr(
  196. nqp::iterval(nqp::shift($!iter)),Pair,'$!key')
  197. ),
  198. IterationEnd
  199. )
  200. )
  201. }
  202. }.new($!elems))
  203. }
  204. }