Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fork-context.js 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /**
  2. * @fileoverview A class to operate forking.
  3. *
  4. * This is state of forking.
  5. * This has a fork list and manages it.
  6. *
  7. * @author Toru Nagashima
  8. */
  9. "use strict";
  10. //------------------------------------------------------------------------------
  11. // Requirements
  12. //------------------------------------------------------------------------------
  13. const assert = require("assert"),
  14. CodePathSegment = require("./code-path-segment");
  15. //------------------------------------------------------------------------------
  16. // Helpers
  17. //------------------------------------------------------------------------------
  18. /**
  19. * Gets whether or not a given segment is reachable.
  20. * @param {CodePathSegment} segment A segment to get.
  21. * @returns {boolean} `true` if the segment is reachable.
  22. */
  23. function isReachable(segment) {
  24. return segment.reachable;
  25. }
  26. /**
  27. * Creates new segments from the specific range of `context.segmentsList`.
  28. *
  29. * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
  30. * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
  31. * This `h` is from `b`, `d`, and `f`.
  32. * @param {ForkContext} context An instance.
  33. * @param {number} begin The first index of the previous segments.
  34. * @param {number} end The last index of the previous segments.
  35. * @param {Function} create A factory function of new segments.
  36. * @returns {CodePathSegment[]} New segments.
  37. */
  38. function makeSegments(context, begin, end, create) {
  39. const list = context.segmentsList;
  40. const normalizedBegin = begin >= 0 ? begin : list.length + begin;
  41. const normalizedEnd = end >= 0 ? end : list.length + end;
  42. const segments = [];
  43. for (let i = 0; i < context.count; ++i) {
  44. const allPrevSegments = [];
  45. for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
  46. allPrevSegments.push(list[j][i]);
  47. }
  48. segments.push(create(context.idGenerator.next(), allPrevSegments));
  49. }
  50. return segments;
  51. }
  52. /**
  53. * `segments` becomes doubly in a `finally` block. Then if a code path exits by a
  54. * control statement (such as `break`, `continue`) from the `finally` block, the
  55. * destination's segments may be half of the source segments. In that case, this
  56. * merges segments.
  57. * @param {ForkContext} context An instance.
  58. * @param {CodePathSegment[]} segments Segments to merge.
  59. * @returns {CodePathSegment[]} The merged segments.
  60. */
  61. function mergeExtraSegments(context, segments) {
  62. let currentSegments = segments;
  63. while (currentSegments.length > context.count) {
  64. const merged = [];
  65. for (let i = 0, length = currentSegments.length / 2 | 0; i < length; ++i) {
  66. merged.push(CodePathSegment.newNext(
  67. context.idGenerator.next(),
  68. [currentSegments[i], currentSegments[i + length]]
  69. ));
  70. }
  71. currentSegments = merged;
  72. }
  73. return currentSegments;
  74. }
  75. //------------------------------------------------------------------------------
  76. // Public Interface
  77. //------------------------------------------------------------------------------
  78. /**
  79. * A class to manage forking.
  80. */
  81. class ForkContext {
  82. // eslint-disable-next-line jsdoc/require-description
  83. /**
  84. * @param {IdGenerator} idGenerator An identifier generator for segments.
  85. * @param {ForkContext|null} upper An upper fork context.
  86. * @param {number} count A number of parallel segments.
  87. */
  88. constructor(idGenerator, upper, count) {
  89. this.idGenerator = idGenerator;
  90. this.upper = upper;
  91. this.count = count;
  92. this.segmentsList = [];
  93. }
  94. /**
  95. * The head segments.
  96. * @type {CodePathSegment[]}
  97. */
  98. get head() {
  99. const list = this.segmentsList;
  100. return list.length === 0 ? [] : list[list.length - 1];
  101. }
  102. /**
  103. * A flag which shows empty.
  104. * @type {boolean}
  105. */
  106. get empty() {
  107. return this.segmentsList.length === 0;
  108. }
  109. /**
  110. * A flag which shows reachable.
  111. * @type {boolean}
  112. */
  113. get reachable() {
  114. const segments = this.head;
  115. return segments.length > 0 && segments.some(isReachable);
  116. }
  117. /**
  118. * Creates new segments from this context.
  119. * @param {number} begin The first index of previous segments.
  120. * @param {number} end The last index of previous segments.
  121. * @returns {CodePathSegment[]} New segments.
  122. */
  123. makeNext(begin, end) {
  124. return makeSegments(this, begin, end, CodePathSegment.newNext);
  125. }
  126. /**
  127. * Creates new segments from this context.
  128. * The new segments is always unreachable.
  129. * @param {number} begin The first index of previous segments.
  130. * @param {number} end The last index of previous segments.
  131. * @returns {CodePathSegment[]} New segments.
  132. */
  133. makeUnreachable(begin, end) {
  134. return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
  135. }
  136. /**
  137. * Creates new segments from this context.
  138. * The new segments don't have connections for previous segments.
  139. * But these inherit the reachable flag from this context.
  140. * @param {number} begin The first index of previous segments.
  141. * @param {number} end The last index of previous segments.
  142. * @returns {CodePathSegment[]} New segments.
  143. */
  144. makeDisconnected(begin, end) {
  145. return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
  146. }
  147. /**
  148. * Adds segments into this context.
  149. * The added segments become the head.
  150. * @param {CodePathSegment[]} segments Segments to add.
  151. * @returns {void}
  152. */
  153. add(segments) {
  154. assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
  155. this.segmentsList.push(mergeExtraSegments(this, segments));
  156. }
  157. /**
  158. * Replaces the head segments with given segments.
  159. * The current head segments are removed.
  160. * @param {CodePathSegment[]} segments Segments to add.
  161. * @returns {void}
  162. */
  163. replaceHead(segments) {
  164. assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
  165. this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
  166. }
  167. /**
  168. * Adds all segments of a given fork context into this context.
  169. * @param {ForkContext} context A fork context to add.
  170. * @returns {void}
  171. */
  172. addAll(context) {
  173. assert(context.count === this.count);
  174. const source = context.segmentsList;
  175. for (let i = 0; i < source.length; ++i) {
  176. this.segmentsList.push(source[i]);
  177. }
  178. }
  179. /**
  180. * Clears all segments in this context.
  181. * @returns {void}
  182. */
  183. clear() {
  184. this.segmentsList = [];
  185. }
  186. /**
  187. * Creates the root fork context.
  188. * @param {IdGenerator} idGenerator An identifier generator for segments.
  189. * @returns {ForkContext} New fork context.
  190. */
  191. static newRoot(idGenerator) {
  192. const context = new ForkContext(idGenerator, null, 1);
  193. context.add([CodePathSegment.newRoot(idGenerator.next())]);
  194. return context;
  195. }
  196. /**
  197. * Creates an empty fork context preceded by a given context.
  198. * @param {ForkContext} parentContext The parent fork context.
  199. * @param {boolean} forkLeavingPath A flag which shows inside of `finally` block.
  200. * @returns {ForkContext} New fork context.
  201. */
  202. static newEmpty(parentContext, forkLeavingPath) {
  203. return new ForkContext(
  204. parentContext.idGenerator,
  205. parentContext,
  206. (forkLeavingPath ? 2 : 1) * parentContext.count
  207. );
  208. }
  209. }
  210. module.exports = ForkContext;