WeakTupleMap.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const isWeakKey = thing => typeof thing === "object" && thing !== null;
  7. /**
  8. * @template {any[]} T
  9. * @template V
  10. */
  11. class WeakTupleMap {
  12. constructor() {
  13. /** @private */
  14. this.f = 0;
  15. /** @private @type {any} */
  16. this.v = undefined;
  17. /** @private @type {Map<object, WeakTupleMap<T, V>> | undefined} */
  18. this.m = undefined;
  19. /** @private @type {WeakMap<object, WeakTupleMap<T, V>> | undefined} */
  20. this.w = undefined;
  21. }
  22. /**
  23. * @param {[...T, V]} args tuple
  24. * @returns {void}
  25. */
  26. set(...args) {
  27. /** @type {WeakTupleMap<T, V>} */
  28. let node = this;
  29. for (let i = 0; i < args.length - 1; i++) {
  30. node = node._get(args[i]);
  31. }
  32. node._setValue(args[args.length - 1]);
  33. }
  34. /**
  35. * @param {T} args tuple
  36. * @returns {boolean} true, if the tuple is in the Set
  37. */
  38. has(...args) {
  39. /** @type {WeakTupleMap<T, V>} */
  40. let node = this;
  41. for (let i = 0; i < args.length; i++) {
  42. node = node._peek(args[i]);
  43. if (node === undefined) return false;
  44. }
  45. return node._hasValue();
  46. }
  47. /**
  48. * @param {T} args tuple
  49. * @returns {V} the value
  50. */
  51. get(...args) {
  52. /** @type {WeakTupleMap<T, V>} */
  53. let node = this;
  54. for (let i = 0; i < args.length; i++) {
  55. node = node._peek(args[i]);
  56. if (node === undefined) return undefined;
  57. }
  58. return node._getValue();
  59. }
  60. /**
  61. * @param {[...T, function(): V]} args tuple
  62. * @returns {V} the value
  63. */
  64. provide(...args) {
  65. /** @type {WeakTupleMap<T, V>} */
  66. let node = this;
  67. for (let i = 0; i < args.length - 1; i++) {
  68. node = node._get(args[i]);
  69. }
  70. if (node._hasValue()) return node._getValue();
  71. const fn = args[args.length - 1];
  72. const newValue = fn(...args.slice(0, -1));
  73. node._setValue(newValue);
  74. return newValue;
  75. }
  76. /**
  77. * @param {T} args tuple
  78. * @returns {void}
  79. */
  80. delete(...args) {
  81. /** @type {WeakTupleMap<T, V>} */
  82. let node = this;
  83. for (let i = 0; i < args.length; i++) {
  84. node = node._peek(args[i]);
  85. if (node === undefined) return;
  86. }
  87. node._deleteValue();
  88. }
  89. /**
  90. * @returns {void}
  91. */
  92. clear() {
  93. this.f = 0;
  94. this.v = undefined;
  95. this.w = undefined;
  96. this.m = undefined;
  97. }
  98. _getValue() {
  99. return this.v;
  100. }
  101. _hasValue() {
  102. return (this.f & 1) === 1;
  103. }
  104. _setValue(v) {
  105. this.f |= 1;
  106. this.v = v;
  107. }
  108. _deleteValue() {
  109. this.f &= 6;
  110. this.v = undefined;
  111. }
  112. _peek(thing) {
  113. if (isWeakKey(thing)) {
  114. if ((this.f & 4) !== 4) return undefined;
  115. return this.w.get(thing);
  116. } else {
  117. if ((this.f & 2) !== 2) return undefined;
  118. return this.m.get(thing);
  119. }
  120. }
  121. _get(thing) {
  122. if (isWeakKey(thing)) {
  123. if ((this.f & 4) !== 4) {
  124. const newMap = new WeakMap();
  125. this.f |= 4;
  126. const newNode = new WeakTupleMap();
  127. (this.w = newMap).set(thing, newNode);
  128. return newNode;
  129. }
  130. const entry = this.w.get(thing);
  131. if (entry !== undefined) {
  132. return entry;
  133. }
  134. const newNode = new WeakTupleMap();
  135. this.w.set(thing, newNode);
  136. return newNode;
  137. } else {
  138. if ((this.f & 2) !== 2) {
  139. const newMap = new Map();
  140. this.f |= 2;
  141. const newNode = new WeakTupleMap();
  142. (this.m = newMap).set(thing, newNode);
  143. return newNode;
  144. }
  145. const entry = this.m.get(thing);
  146. if (entry !== undefined) {
  147. return entry;
  148. }
  149. const newNode = new WeakTupleMap();
  150. this.m.set(thing, newNode);
  151. return newNode;
  152. }
  153. }
  154. }
  155. module.exports = WeakTupleMap;