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