Source: generator.js

'use strict';

/**
 * Generator functions.
 *
 * <p>They are actually a simple function which returns an {@link external:Iterator iterator} and can be implemented
 * in duck typing.</p>
 *
 * @example <caption>function-star notation</caption>
 * function* generator() {
 *   yield 1;
 *   yield 2;
 *   yield 3;
 * }
 *
 * @example <caption>Duck typing</caption>
 * function generator() {
 *   var i = 0;
 *   var content = [1, 2, 3];
 *   return {
 *     next : function () {
 *       var res;
 *       if (i < content.length) {
 *         res = { done : false, value : content[i] };
 *       } else {
 *         res = { done : true };
 *       }
 *       i++;
 *       return res;
 *     }
 *   };
 * }
 *
 * @class Generator
 * @external
 * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* Generators on Mozilla Developer Network}
 */

/**
 * An occurrence of a Generator.
 *
 * <p>This is the result of calling a generator function which does <code>yield</code> on each item it wants to
 * expose. It can actually be a hand made object as long as it respects the contract of this interface.</p>
 *
 * @example
 * function* generator() {
 *   yield 1;
 *   yield 2;
 *   yield 3;
 * }
 * var it = generator();
 *
 * @example
 * var it = (function () {
 *   var i = 0;
 *   var content = [1, 2, 3];
 *   return {
 *     next : function () {
 *       var res;
 *       if (i < content.length) {
 *         res = { done : false, value : content[i] };
 *       } else {
 *         res = { done : true };
 *       }
 *       i++;
 *       return res;
 *     }
 *   };
 * })();
 *
 * @class Iterator
 * @external
 * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol The Iterator protocol on Mozilla Developer Network}
 */
/**
 * Reads the next item on the iterator.
 *
 * <p>Can either return a value if it is not done, <code>{ done : false, value : 'some value' }</code>, or inform it
 * is done with <code>{ done : true }</code>.</p>
 *
 * @returns Object a done marker or a value container.
 * @function external:Iterator#next
 */

var skeleton = require('./skeleton');

function updaterFromGenerator(generator) {
  function updater(item, done) {
    var iterator = generator();
    var result = iterator.next();
    while(!result.done) {
      item(result.value);
      result = iterator.next();
    }
    done();
  }

  return updater;
}

function newSummarizer(generator, serialize, digest, selector) {
  return skeleton.newSummarizer(updaterFromGenerator(generator), serialize, digest, selector);
}

function newResolver(generator, remote, serialize, deserialize) {
  return skeleton.newResolver(updaterFromGenerator(generator), remote, serialize, deserialize);
}

/**
 * Generator/Iterator handling.
 *
 * @module mathsync/generator
 */
module.exports = {

  /**
   * Creates a new summarizer.
   *
   * @example <caption>Yields items from an array.</caption>
   * var items = [{ from: 1, to: 2 }, { from: 2, to: 5}];
   * function* generator() {
   *   var i, l = items.length;
   *   for (i = 0; i < l; i++) {
   *     yield items[i];
   *   }
   * }
   * function serialize(item) {
   *   return new Int32Array([item.from, item.to]).buffer;
   * }
   * var summarizer = require('mathsync/generator').newSummarizer(generator, serialize);
   *
   * @example <caption>Yields strings from a hash.</caption>
   * var data = { key1: "value", key2: "other" };
   * var summarizer = require('mathsync/generator').newSummarizer(function* () {
   *   for (var k in data) {
   *     if (data.hasOwnProperty(k)) {
   *       yield k + ':' + data[k];
   *     }
   *   }
   * }, require('mathsync/string').newSerializer());
   *
   * @name module:mathsync/generator.newSummarizer
   * @function
   * @param {external:Generator} generator - the generator yielding local items.
   * @param {Serial~Serialize} serialize - the item serializer.
   * @param {Digest~Digester} [digester] - the digester to use, defaults to SHA-1.
   * @param {BucketSelector~Selector} [selector] - how to place items in IBF buckets, uses 3 buckets by default.
   */
  newSummarizer : newSummarizer,

  /**
   * Creates a new resolver.
   *
   * @example <caption>Items from an array.</caption>
   * var remote = ...
   * var items = [{ from: 1, to: 2 }, { from: 2, to: 5}];
   * function* generator() {
   *   var i, l = items.length;
   *   for (i = 0; i < l; i++) {
   *     yield items[i];
   *   }
   * }
   * function serialize(item) {
   *   return new Int32Array([item.from, item.to]).buffer;
   * }
   * function deserialize(buffer) {
   *   var arr = new Int32Array(buffer);
   *   return { from: arr[0], to: arr[1] };
   * }
   * var resolver = require('mathsync/generator').newResolver(generator, remote, serialize, deserialize);
   *
   * @name module:mathsync/generator.newResolver
   * @function
   * @param {external:Generator} generator - the generator yielding local items.
   * @param {Summarizer} remote - summarizer producing summaires of the remote side.
   * @param {Serial~Serialize} serialize - the item serializer.
   * @param {Serial~Deserialize} deserialize - the item deserializer.
   */
  newResolver : newResolver
};