<commit_before>'use strict';
console.time('Took: ');
const fs = require('fs');

const taskName = 'sigkey';
const inputFileName = process.argv[2] || taskName + '.in',
  outputFileName = process.argv[3] || taskName + '.out';

const alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
const alphabetLength = alphabet.length;

fs.readFile(inputFileName, 'utf8', (err, data) => {
  if (err) throw err;
  const source = data.split('\n');
  const N = parseInt(source[0]);
  let i = N;
  let publics = [],
    privates = [];
  while (i--) {
    let key = source[i + 1];
    if (-1 === key.indexOf('a')) {
      privates.push(key);
    } else {
      publics.push(key);
    }
  }
  privates.sort();
  publics.sort();
  let totalPairs = 0,
    lpub = publics.length,
    ipub = 0;
  while (ipub < lpub && privates.length) {
    let pub = publics[ipub],
      ipriv = privates.length;
    while (ipriv--) {
      let priv = privates[ipriv];
      if (isKeyPair(priv, pub)) {
        ++totalPairs;
        privates.splice(ipriv, 1);
        break;
      }
    }
    // move on...
    ++ipub;
  }
  fs.writeFile(outputFileName, totalPairs, err => {
    if (err) throw err;
    console.log(`Total: ${totalPairs} pairs`);
    console.timeEnd('Took: ');
  });
});

function isKeyPair(first, second) {
  let result = true,
    lastIndexFirst = first.length - 1,
    lastIndexSecond = second.length - 1;
  let occurrences = Array(alphabetLength);
  // let keyMap = {};
  for (let i = 0; i < alphabetLength; i++) {
    let char = alphabet[i],
      index1 = first.indexOf(char),
      index2 = second.indexOf(char),
      exists = -1 === index1 ? 0 : 1;
    exists += -1 === index2 ? 0 : 1;
    if (2 === exists) {
      // duplicate
      return false;
    }
    occurrences[i] = exists;
  }
  return -1 === occurrences.indexOf(0)
    || occurrences.indexOf(0) > occurrences.lastIndexOf(1);
}
<commit_msg>port of the best python solution to javascript<commit_after>'use strict';
console.time('Took: ');
const fs = require('fs');

const taskName = 'sigkey';
const inputFileName = process.argv[2] || taskName + '.in',
  outputFileName = process.argv[3] || taskName + '.out';

fs.readFile(inputFileName, 'utf8', (err, data) => {
  if (err) throw err;
  const source = data.split('\n');
  const N = parseInt(source[0]);
  let keys = new Array(N)
  let i = N;
  let totalPairs = 0,
    pairs = new Set(),
    a = 'a'.charCodeAt(0);
  while (i--) {
    let mask = 0,
      key = source[i+1],
      l = key.length;
    while (l--) {
      mask |= 1 << (key.charCodeAt(l) - a);
    }
    keys[i] = mask
  }
  keys.sort((a,b) => b-a);
  // i = N;
  while (++i < N) {
    let key = keys[i];
    if (pairs.has(key)) {
      ++totalPairs;
      pairs.delete(key);
    } else {
      let l = key.toString(2).length, mask = 0;
      pairs.add(((1 << l) - 1) ^ key);
    }
  }
  fs.writeFile(outputFileName, totalPairs, err => {
    if (err) throw err;
    console.log(`Total: ${totalPairs} pairs`);
    console.timeEnd('Took: ');
  });
});

function getPair(key) {
  let pair = '',
    chars = key.split('').sort(),
    last = chars[chars.length - 1],
    len = alphabet.indexOf(last);
  for (let i = 0; i < len; i++) {
    let char = alphabet[i],
      index = key.indexOf(char);
    if (-1 === index){
      pair += char;
    }
  }
  return pair;
}
<|endoftext|>


<commit_before>'use strict';
const fs = require('fs');
const thrift = require('thrift');
const parquet_thrift = require('../gen-nodejs/parquet_types')
const parquet_shredder = require('./shred')
const parquet_util = require('./util')
const parquet_codec = require('./codec')
const parquet_compression = require('./compression')

/**
 * Parquet File Magic String
 */
const PARQUET_MAGIC = 'PAR1';

/**
 * Parquet File Format Version
 */
const PARQUET_VERSION = 1;

/**
 * Default Page and Row Group sizes
 */
const PARQUET_DEFAULT_PAGE_SIZE = 8192;
const PARQUET_DEFAULT_ROW_GROUP_SIZE = 4096;

/**
 * Repetition and Definition Level Encoding
 */
const PARQUET_RDLVL_TYPE = 'INT32';
const PARQUET_RDLVL_ENCODING = 'RLE';

/**
 * Write a parquet file to an output stream. The ParquetWriter will perform
 * buffering/batching for performance, so close() must be called after all rows
 * are written.
 */
class ParquetWriter {

  /**
   * Create a new buffered parquet writer that writes to the specified file
   */
  static async openFile(schema, path, opts) {
    if (!opts) {
      opts = {};
    }

    let envelopeWriter = await ParquetEnvelopeWriter.openFile(schema, path, opts);
    try {
      await envelopeWriter.writeHeader();
      return new ParquetWriter(schema, envelopeWriter, opts);
    } catch (err) {
      envelopeWriter.close();
      throw err;
    }
  }

  /**
   * Create a new buffered parquet writer for a given envelope writer
   */
  constructor(schema, envelopeWriter, opts) {
    this.schema = schema;
    this.envelopeWriter = envelopeWriter;
    this.rowBuffer = {};
    this.rowGroupSize = PARQUET_DEFAULT_ROW_GROUP_SIZE;
    this.closed = false;
  }

  /**
   * Append a single row to the parquet file. Rows are buffered in memory until
   * rowGroupSize rows are in the buffer or close() is called
   */
  async appendRow(row) {
    if (this.closed) {
      throw 'writer was closed';
    }

    parquet_shredder.shredRecord(this.schema, row, this.rowBuffer);

    if (this.rowBuffer.rowCount >= this.rowGroupSize) {
      await this.envelopeWriter.writeRowGroup(this.rowBuffer);
      this.rowBuffer = {};
    }
  }

  /**
   * Finish writing the parquet file and commit the footer to disk. This method
   * MUST be called after you are finished adding rows. You must not call this
   * method twice on the same object or add any rows after the close() method has
   * been called
   */
  async close() {
    if (this.closed) {
      throw 'writer was closed';
    }

    this.closed = true;

    if (this.rowBuffer.rowCount > 0 || this.rowBuffer.rowCount >= this.rowGroupSize) {
      await this.envelopeWriter.writeRowGroup(this.rowBuffer);
      this.rowBuffer = {};
    }

    await this.envelopeWriter.writeFooter();
    await this.envelopeWriter.close();
    this.envelopeWriter = null;
  }

  /**
   * Set the parquet row group size. This values controls the maximum number
   * of rows that are buffered in memory at any given time as well as the number
   * of rows that are co-located on disk. A higher value is generally better for
   * read-time I/O performance at the tradeoff of write-time memory usage.
   */
  setRowGroupSize(cnt) {
    this.rowGroupSize = cnt;
  }

  /**
   * Set the parquet data page size. The data page size controls the maximum
   * number of column values that are written to disk as a consecutive array
   */
  setPageSize(cnt) {
    this.writer.setPageSize(cnt);
  }

}

/**
 * Create a parquet file from a schema and a number of row groups. This class
 * performs direct, unbuffered writes to the underlying output stream and is
 * intendend for advanced and internal users; the writeXXX methods must be
 * called in the correct order to produce a valid file.
 */
class ParquetEnvelopeWriter {

  /**
   * Create a new parquet envelope writer that writes to the specified file
   */
  static async openFile(schema, path, opts) {
    let outputStream = await parquet_util.osopen(path, opts);
    let writeFn = parquet_util.oswrite.bind(undefined, outputStream);
    let closeFn = parquet_util.osclose.bind(undefined, outputStream);
    return new ParquetEnvelopeWriter(schema, writeFn, closeFn, 0, opts);
  }

  constructor(schema, writeFn, closeFn, fileOffset, opts) {
    this.schema = schema;
    this.write = writeFn,
    this.close = closeFn;
    this.offset = fileOffset;
    this.rowCount = 0;
    this.rowGroups = [];
    this.pageSize = PARQUET_DEFAULT_PAGE_SIZE;
    this.useDataPageV2 = ("useDataPageV2" in opts) ? opts.useDataPageV2 : true;
  }

  writeSection(buf) {
    this.offset += buf.length;
    return this.write(buf);
  }

  /**
   * Encode the parquet file header
   */
  writeHeader() {
    return this.writeSection(Buffer.from(PARQUET_MAGIC));
  }

  /**
   * Encode a parquet row group. The records object should be created using the
   * shredRecord method
   */
  writeRowGroup(records) {
    let rgroup = encodeRowGroup(
        this.schema,
        records,
        {
          baseOffset: this.offset,
          pageSize: this.pageSize,
          useDataPageV2: this.useDataPageV2
        });

    this.rowCount += records.rowCount;
    this.rowGroups.push(rgroup.metadata);
    return this.writeSection(rgroup.body);
  }

  /**
   * Write the parquet file footer
   */
  writeFooter() {
    if (this.rowCount === 0) {
      throw 'cannot write parquet file with zero rows';
    }

    if (this.schema.columns.length === 0) {
      throw 'cannot write parquet file with zero columns';
    }

    return this.writeSection(
        encodeFooter(this.schema, this.rowCount, this.rowGroups));
  };

  /**
   * Set the parquet data page size. The data page size controls the maximum
   * number of column values that are written to disk as a consecutive array
   */
  setPageSize(cnt) {
    this.pageSize = cnt;
  }

}

/**
 * Encode a consecutive array of data using one of the parquet encodings
 */
function encodeValues(type, encoding, values, opts) {
  if (!(encoding in parquet_codec)) {
    throw 'invalid encoding: ' + encoding;
  }

  return parquet_codec[encoding].encodeValues(type, values, opts);
}

/**
 * Encode a parquet data page
 */
function encodeDataPage(column, valueCount, values, rlevels, dlevels) {
  /* encode values */
  let valuesBuf = encodeValues(column.primitiveType, column.encoding, values);

  /* encode repetition and definition levels */
  let rLevelsBuf = Buffer.alloc(0);
  if (column.rLevelMax > 0) {
    rLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        rlevels,
        { bitWidth: parquet_util.getBitWidth(column.rLevelMax) });
  }

  let dLevelsBuf = Buffer.alloc(0);
  if (column.dLevelMax > 0) {
    dLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        dlevels,
        { bitWidth: parquet_util.getBitWidth(column.dLevelMax) });
  }

  /* build page header */
  let pageBody = Buffer.concat([rLevelsBuf, dLevelsBuf, valuesBuf]);
  let pageHeader = new parquet_thrift.PageHeader()
  pageHeader.type = parquet_util.setThriftEnum(parquet_thrift.PageType, 'DATA_PAGE');
  pageHeader.uncompressed_page_size = pageBody.length;
  pageHeader.compressed_page_size = pageBody.length;
  pageHeader.data_page_header = new parquet_thrift.DataPageHeader();
  pageHeader.data_page_header.num_values = valueCount;

  pageHeader.data_page_header.encoding =
      parquet_util.setThriftEnum(parquet_thrift.Encoding, column.encoding);

  pageHeader.data_page_header.definition_level_encoding =
    parquet_util.setThriftEnum(parquet_thrift.Encoding, PARQUET_RDLVL_ENCODING);

  pageHeader.data_page_header.repetition_level_encoding =
      parquet_util.setThriftEnum(parquet_thrift.Encoding, PARQUET_RDLVL_ENCODING);

  /* concat page header, repetition and definition levels and values */
  return Buffer.concat([parquet_util.serializeThrift(pageHeader), pageBody]);
}

/**
 * Encode a parquet data page (v2)
 */
function encodeDataPageV2(column, valueCount, rowCount, values, rlevels, dlevels) {
  /* encode values */
  let valuesBuf = encodeValues(column.primitiveType, column.encoding, values);
  let valuesBufCompressed = parquet_compression.deflate(
      column.compression,
      valuesBuf);

  /* encode repetition and definition levels */
  let rLevelsBuf = Buffer.alloc(0);
  if (column.rLevelMax > 0) {
    rLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        rlevels, {
          bitWidth: parquet_util.getBitWidth(column.rLevelMax),
          disableEnvelope: true
        });
  }

  let dLevelsBuf = Buffer.alloc(0);
  if (column.dLevelMax > 0) {
    dLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        dlevels, {
          bitWidth: parquet_util.getBitWidth(column.dLevelMax),
          disableEnvelope: true
        });
  }

  /* build page header */
  let pageHeader = new parquet_thrift.PageHeader();
  pageHeader.type = parquet_util.setThriftEnum(parquet_thrift.PageType, 'DATA_PAGE_V2');
  pageHeader.data_page_header_v2 = new parquet_thrift.DataPageHeaderV2();
  pageHeader.data_page_header_v2.num_values = valueCount;
  pageHeader.data_page_header_v2.num_nulls = values.length;
  pageHeader.data_page_header_v2.num_rows = rowCount;

  pageHeader.uncompressed_page_size =
      rLevelsBuf.length + dLevelsBuf.length + valuesBuf.length;

  pageHeader.compressed_page_size =
      rLevelsBuf.length + dLevelsBuf.length + valuesBufCompressed.length;

  pageHeader.data_page_header_v2.encoding =
      parquet_util.setThriftEnum(parquet_thrift.Encoding, column.encoding);

  pageHeader.data_page_header_v2.definition_levels_byte_length =
      parquet_util.setThriftWorkaround(dLevelsBuf.length);

  pageHeader.data_page_header_v2.repetition_levels_byte_length =
      parquet_util.setThriftWorkaround(rLevelsBuf.length);

  pageHeader.data_page_header_v2.is_compressed =
      column.compression !== 'UNCOMPRESSED';

  /* concat page header, repetition and definition levels and values */
  return Buffer.concat([
      parquet_util.serializeThrift(pageHeader),
      rLevelsBuf,
      dLevelsBuf,
      valuesBufCompressed]);
}

/**
 * Encode an array of values into a parquet column chunk
 */
function encodeColumnChunk(values, opts) {
  /* encode data page(s) */
  let pages = [];

  {
    let dataPage;
    if (opts.useDataPageV2) {
      dataPage = encodeDataPageV2(
            opts.column,
            values.count,
            opts.rowCount,
            values.values,
            values.rlevels,
            values.dlevels);
    } else {
      dataPage = encodeDataPage(
            opts.column,
            values.count,
            values.values,
            values.rlevels,
            values.dlevels);
    }

    pages.push(dataPage);
  }

  let pagesBuf = Buffer.concat(pages);

  /* prepare metadata header */
  let metadata = new parquet_thrift.ColumnMetaData();
  metadata.path_in_schema = opts.column.path;
  metadata.num_values = values.count;
  metadata.data_page_offset = opts.baseOffset;
  metadata.encodings = [];
  metadata.total_uncompressed_size = pagesBuf.length;
  metadata.total_compressed_size = pagesBuf.length;

  metadata.type = parquet_util.setThriftEnum(
      parquet_thrift.Type,
      opts.column.primitiveType);

  metadata.codec = parquet_util.setThriftEnum(
      parquet_thrift.CompressionCodec,
      opts.useDataPageV2 ? opts.column.compression : 'UNCOMPRESSED');

  /* list encodings */
  let encodingsSet = {};
  encodingsSet[PARQUET_RDLVL_ENCODING] = true;
  encodingsSet[opts.column.encoding] = true;
  for (let k in encodingsSet) {
    metadata.encodings.push(parquet_util.setThriftEnum(parquet_thrift.Encoding, k));
  }

  /* concat metadata header and data pages */
  let metadataOffset = opts.baseOffset + pagesBuf.length;
  let body = Buffer.concat([pagesBuf, parquet_util.serializeThrift(metadata)]);
  return { body, metadata, metadataOffset };
}

/**
 * Encode a list of column values into a parquet row group
 */
function encodeRowGroup(schema, data, opts) {
  let metadata = new parquet_thrift.RowGroup();
  metadata.num_rows = data.rowCount;
  metadata.columns = [];
  metadata.total_byte_size = 0;

  let body = Buffer.alloc(0);
  for (let colDef of schema.columns) {
    let cchunkData = encodeColumnChunk(
      data.columnData[colDef.name],
      {
        column: colDef,
        baseOffset: opts.baseOffset + body.length,
        pageSize: opts.pageSize,
        encoding: colDef.encoding,
        rowCount: data.rowCount,
        useDataPageV2: opts.useDataPageV2
      });

    let cchunk = new parquet_thrift.ColumnChunk();
    cchunk.file_offset = cchunkData.metadataOffset;
    cchunk.meta_data = cchunkData.metadata;
    metadata.columns.push(cchunk);
    metadata.total_byte_size += cchunkData.body.length;

    body = Buffer.concat([body, cchunkData.body]);
  }

  return { body, metadata };
}

/**
 * Encode a parquet file metadata footer
 */
function encodeFooter(schema, rowCount, rowGroups) {
  let metadata = new parquet_thrift.FileMetaData()
  metadata.version = PARQUET_VERSION;
  metadata.created_by = 'parquet.js';
  metadata.num_rows = rowCount;
  metadata.row_groups = rowGroups;
  metadata.schema = [];

  {
    let schemaRoot = new parquet_thrift.SchemaElement();
    schemaRoot.name = 'root';
    schemaRoot.num_children = schema.columns.length;
    metadata.schema.push(schemaRoot);
  }

  for (let colDef of schema.columns) {
    let schemaElem = new parquet_thrift.SchemaElement();
    schemaElem.name = colDef.name;
    schemaElem.type = parquet_util.setThriftEnum(
        parquet_thrift.Type,
        colDef.primitiveType);

    schemaElem.repetition_type = parquet_util.setThriftEnum(
        parquet_thrift.FieldRepetitionType,
        colDef.repetition_type);

    if (colDef.originalType) {
      schemaElem.converted_type = parquet_util.setThriftEnum(
          parquet_thrift.ConvertedType,
          colDef.originalType);
    }

    metadata.schema.push(schemaElem);
  }

  let metadataEncoded = parquet_util.serializeThrift(metadata);
  let footerEncoded = new Buffer(metadataEncoded.length + 8);
  metadataEncoded.copy(footerEncoded);
  footerEncoded.writeUInt32LE(metadataEncoded.length, metadataEncoded.length);
  footerEncoded.write(PARQUET_MAGIC, metadataEncoded.length + 4);
  return footerEncoded;
}

module.exports = {
  ParquetEnvelopeWriter,
  ParquetWriter,
};

<commit_msg>make row group size configurable via ctor options<commit_after>'use strict';
const fs = require('fs');
const thrift = require('thrift');
const parquet_thrift = require('../gen-nodejs/parquet_types')
const parquet_shredder = require('./shred')
const parquet_util = require('./util')
const parquet_codec = require('./codec')
const parquet_compression = require('./compression')

/**
 * Parquet File Magic String
 */
const PARQUET_MAGIC = 'PAR1';

/**
 * Parquet File Format Version
 */
const PARQUET_VERSION = 1;

/**
 * Default Page and Row Group sizes
 */
const PARQUET_DEFAULT_PAGE_SIZE = 8192;
const PARQUET_DEFAULT_ROW_GROUP_SIZE = 4096;

/**
 * Repetition and Definition Level Encoding
 */
const PARQUET_RDLVL_TYPE = 'INT32';
const PARQUET_RDLVL_ENCODING = 'RLE';

/**
 * Write a parquet file to an output stream. The ParquetWriter will perform
 * buffering/batching for performance, so close() must be called after all rows
 * are written.
 */
class ParquetWriter {

  /**
   * Create a new buffered parquet writer that writes to the specified file
   */
  static async openFile(schema, path, opts) {
    if (!opts) {
      opts = {};
    }

    let envelopeWriter = await ParquetEnvelopeWriter.openFile(schema, path, opts);
    try {
      await envelopeWriter.writeHeader();
      return new ParquetWriter(schema, envelopeWriter, opts);
    } catch (err) {
      envelopeWriter.close();
      throw err;
    }
  }

  /**
   * Create a new buffered parquet writer for a given envelope writer
   */
  constructor(schema, envelopeWriter, opts) {
    this.schema = schema;
    this.envelopeWriter = envelopeWriter;
    this.rowBuffer = {};
    this.rowGroupSize = opts.rowGroupSize || PARQUET_DEFAULT_ROW_GROUP_SIZE;
    this.closed = false;
  }

  /**
   * Append a single row to the parquet file. Rows are buffered in memory until
   * rowGroupSize rows are in the buffer or close() is called
   */
  async appendRow(row) {
    if (this.closed) {
      throw 'writer was closed';
    }

    parquet_shredder.shredRecord(this.schema, row, this.rowBuffer);

    if (this.rowBuffer.rowCount >= this.rowGroupSize) {
      await this.envelopeWriter.writeRowGroup(this.rowBuffer);
      this.rowBuffer = {};
    }
  }

  /**
   * Finish writing the parquet file and commit the footer to disk. This method
   * MUST be called after you are finished adding rows. You must not call this
   * method twice on the same object or add any rows after the close() method has
   * been called
   */
  async close() {
    if (this.closed) {
      throw 'writer was closed';
    }

    this.closed = true;

    if (this.rowBuffer.rowCount > 0 || this.rowBuffer.rowCount >= this.rowGroupSize) {
      await this.envelopeWriter.writeRowGroup(this.rowBuffer);
      this.rowBuffer = {};
    }

    await this.envelopeWriter.writeFooter();
    await this.envelopeWriter.close();
    this.envelopeWriter = null;
  }

  /**
   * Set the parquet row group size. This values controls the maximum number
   * of rows that are buffered in memory at any given time as well as the number
   * of rows that are co-located on disk. A higher value is generally better for
   * read-time I/O performance at the tradeoff of write-time memory usage.
   */
  setRowGroupSize(cnt) {
    this.rowGroupSize = cnt;
  }

  /**
   * Set the parquet data page size. The data page size controls the maximum
   * number of column values that are written to disk as a consecutive array
   */
  setPageSize(cnt) {
    this.writer.setPageSize(cnt);
  }

}

/**
 * Create a parquet file from a schema and a number of row groups. This class
 * performs direct, unbuffered writes to the underlying output stream and is
 * intendend for advanced and internal users; the writeXXX methods must be
 * called in the correct order to produce a valid file.
 */
class ParquetEnvelopeWriter {

  /**
   * Create a new parquet envelope writer that writes to the specified file
   */
  static async openFile(schema, path, opts) {
    let outputStream = await parquet_util.osopen(path, opts);
    let writeFn = parquet_util.oswrite.bind(undefined, outputStream);
    let closeFn = parquet_util.osclose.bind(undefined, outputStream);
    return new ParquetEnvelopeWriter(schema, writeFn, closeFn, 0, opts);
  }

  constructor(schema, writeFn, closeFn, fileOffset, opts) {
    this.schema = schema;
    this.write = writeFn,
    this.close = closeFn;
    this.offset = fileOffset;
    this.rowCount = 0;
    this.rowGroups = [];
    this.pageSize = PARQUET_DEFAULT_PAGE_SIZE;
    this.useDataPageV2 = ("useDataPageV2" in opts) ? opts.useDataPageV2 : true;
  }

  writeSection(buf) {
    this.offset += buf.length;
    return this.write(buf);
  }

  /**
   * Encode the parquet file header
   */
  writeHeader() {
    return this.writeSection(Buffer.from(PARQUET_MAGIC));
  }

  /**
   * Encode a parquet row group. The records object should be created using the
   * shredRecord method
   */
  writeRowGroup(records) {
    let rgroup = encodeRowGroup(
        this.schema,
        records,
        {
          baseOffset: this.offset,
          pageSize: this.pageSize,
          useDataPageV2: this.useDataPageV2
        });

    this.rowCount += records.rowCount;
    this.rowGroups.push(rgroup.metadata);
    return this.writeSection(rgroup.body);
  }

  /**
   * Write the parquet file footer
   */
  writeFooter() {
    if (this.rowCount === 0) {
      throw 'cannot write parquet file with zero rows';
    }

    if (this.schema.columns.length === 0) {
      throw 'cannot write parquet file with zero columns';
    }

    return this.writeSection(
        encodeFooter(this.schema, this.rowCount, this.rowGroups));
  };

  /**
   * Set the parquet data page size. The data page size controls the maximum
   * number of column values that are written to disk as a consecutive array
   */
  setPageSize(cnt) {
    this.pageSize = cnt;
  }

}

/**
 * Encode a consecutive array of data using one of the parquet encodings
 */
function encodeValues(type, encoding, values, opts) {
  if (!(encoding in parquet_codec)) {
    throw 'invalid encoding: ' + encoding;
  }

  return parquet_codec[encoding].encodeValues(type, values, opts);
}

/**
 * Encode a parquet data page
 */
function encodeDataPage(column, valueCount, values, rlevels, dlevels) {
  /* encode values */
  let valuesBuf = encodeValues(column.primitiveType, column.encoding, values);

  /* encode repetition and definition levels */
  let rLevelsBuf = Buffer.alloc(0);
  if (column.rLevelMax > 0) {
    rLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        rlevels,
        { bitWidth: parquet_util.getBitWidth(column.rLevelMax) });
  }

  let dLevelsBuf = Buffer.alloc(0);
  if (column.dLevelMax > 0) {
    dLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        dlevels,
        { bitWidth: parquet_util.getBitWidth(column.dLevelMax) });
  }

  /* build page header */
  let pageBody = Buffer.concat([rLevelsBuf, dLevelsBuf, valuesBuf]);
  let pageHeader = new parquet_thrift.PageHeader()
  pageHeader.type = parquet_util.setThriftEnum(parquet_thrift.PageType, 'DATA_PAGE');
  pageHeader.uncompressed_page_size = pageBody.length;
  pageHeader.compressed_page_size = pageBody.length;
  pageHeader.data_page_header = new parquet_thrift.DataPageHeader();
  pageHeader.data_page_header.num_values = valueCount;

  pageHeader.data_page_header.encoding =
      parquet_util.setThriftEnum(parquet_thrift.Encoding, column.encoding);

  pageHeader.data_page_header.definition_level_encoding =
    parquet_util.setThriftEnum(parquet_thrift.Encoding, PARQUET_RDLVL_ENCODING);

  pageHeader.data_page_header.repetition_level_encoding =
      parquet_util.setThriftEnum(parquet_thrift.Encoding, PARQUET_RDLVL_ENCODING);

  /* concat page header, repetition and definition levels and values */
  return Buffer.concat([parquet_util.serializeThrift(pageHeader), pageBody]);
}

/**
 * Encode a parquet data page (v2)
 */
function encodeDataPageV2(column, valueCount, rowCount, values, rlevels, dlevels) {
  /* encode values */
  let valuesBuf = encodeValues(column.primitiveType, column.encoding, values);
  let valuesBufCompressed = parquet_compression.deflate(
      column.compression,
      valuesBuf);

  /* encode repetition and definition levels */
  let rLevelsBuf = Buffer.alloc(0);
  if (column.rLevelMax > 0) {
    rLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        rlevels, {
          bitWidth: parquet_util.getBitWidth(column.rLevelMax),
          disableEnvelope: true
        });
  }

  let dLevelsBuf = Buffer.alloc(0);
  if (column.dLevelMax > 0) {
    dLevelsBuf = encodeValues(
        PARQUET_RDLVL_TYPE,
        PARQUET_RDLVL_ENCODING,
        dlevels, {
          bitWidth: parquet_util.getBitWidth(column.dLevelMax),
          disableEnvelope: true
        });
  }

  /* build page header */
  let pageHeader = new parquet_thrift.PageHeader();
  pageHeader.type = parquet_util.setThriftEnum(parquet_thrift.PageType, 'DATA_PAGE_V2');
  pageHeader.data_page_header_v2 = new parquet_thrift.DataPageHeaderV2();
  pageHeader.data_page_header_v2.num_values = valueCount;
  pageHeader.data_page_header_v2.num_nulls = values.length;
  pageHeader.data_page_header_v2.num_rows = rowCount;

  pageHeader.uncompressed_page_size =
      rLevelsBuf.length + dLevelsBuf.length + valuesBuf.length;

  pageHeader.compressed_page_size =
      rLevelsBuf.length + dLevelsBuf.length + valuesBufCompressed.length;

  pageHeader.data_page_header_v2.encoding =
      parquet_util.setThriftEnum(parquet_thrift.Encoding, column.encoding);

  pageHeader.data_page_header_v2.definition_levels_byte_length =
      parquet_util.setThriftWorkaround(dLevelsBuf.length);

  pageHeader.data_page_header_v2.repetition_levels_byte_length =
      parquet_util.setThriftWorkaround(rLevelsBuf.length);

  pageHeader.data_page_header_v2.is_compressed =
      column.compression !== 'UNCOMPRESSED';

  /* concat page header, repetition and definition levels and values */
  return Buffer.concat([
      parquet_util.serializeThrift(pageHeader),
      rLevelsBuf,
      dLevelsBuf,
      valuesBufCompressed]);
}

/**
 * Encode an array of values into a parquet column chunk
 */
function encodeColumnChunk(values, opts) {
  /* encode data page(s) */
  let pages = [];

  {
    let dataPage;
    if (opts.useDataPageV2) {
      dataPage = encodeDataPageV2(
            opts.column,
            values.count,
            opts.rowCount,
            values.values,
            values.rlevels,
            values.dlevels);
    } else {
      dataPage = encodeDataPage(
            opts.column,
            values.count,
            values.values,
            values.rlevels,
            values.dlevels);
    }

    pages.push(dataPage);
  }

  let pagesBuf = Buffer.concat(pages);

  /* prepare metadata header */
  let metadata = new parquet_thrift.ColumnMetaData();
  metadata.path_in_schema = opts.column.path;
  metadata.num_values = values.count;
  metadata.data_page_offset = opts.baseOffset;
  metadata.encodings = [];
  metadata.total_uncompressed_size = pagesBuf.length;
  metadata.total_compressed_size = pagesBuf.length;

  metadata.type = parquet_util.setThriftEnum(
      parquet_thrift.Type,
      opts.column.primitiveType);

  metadata.codec = parquet_util.setThriftEnum(
      parquet_thrift.CompressionCodec,
      opts.useDataPageV2 ? opts.column.compression : 'UNCOMPRESSED');

  /* list encodings */
  let encodingsSet = {};
  encodingsSet[PARQUET_RDLVL_ENCODING] = true;
  encodingsSet[opts.column.encoding] = true;
  for (let k in encodingsSet) {
    metadata.encodings.push(parquet_util.setThriftEnum(parquet_thrift.Encoding, k));
  }

  /* concat metadata header and data pages */
  let metadataOffset = opts.baseOffset + pagesBuf.length;
  let body = Buffer.concat([pagesBuf, parquet_util.serializeThrift(metadata)]);
  return { body, metadata, metadataOffset };
}

/**
 * Encode a list of column values into a parquet row group
 */
function encodeRowGroup(schema, data, opts) {
  let metadata = new parquet_thrift.RowGroup();
  metadata.num_rows = data.rowCount;
  metadata.columns = [];
  metadata.total_byte_size = 0;

  let body = Buffer.alloc(0);
  for (let colDef of schema.columns) {
    let cchunkData = encodeColumnChunk(
      data.columnData[colDef.name],
      {
        column: colDef,
        baseOffset: opts.baseOffset + body.length,
        pageSize: opts.pageSize,
        encoding: colDef.encoding,
        rowCount: data.rowCount,
        useDataPageV2: opts.useDataPageV2
      });

    let cchunk = new parquet_thrift.ColumnChunk();
    cchunk.file_offset = cchunkData.metadataOffset;
    cchunk.meta_data = cchunkData.metadata;
    metadata.columns.push(cchunk);
    metadata.total_byte_size += cchunkData.body.length;

    body = Buffer.concat([body, cchunkData.body]);
  }

  return { body, metadata };
}

/**
 * Encode a parquet file metadata footer
 */
function encodeFooter(schema, rowCount, rowGroups) {
  let metadata = new parquet_thrift.FileMetaData()
  metadata.version = PARQUET_VERSION;
  metadata.created_by = 'parquet.js';
  metadata.num_rows = rowCount;
  metadata.row_groups = rowGroups;
  metadata.schema = [];

  {
    let schemaRoot = new parquet_thrift.SchemaElement();
    schemaRoot.name = 'root';
    schemaRoot.num_children = schema.columns.length;
    metadata.schema.push(schemaRoot);
  }

  for (let colDef of schema.columns) {
    let schemaElem = new parquet_thrift.SchemaElement();
    schemaElem.name = colDef.name;
    schemaElem.type = parquet_util.setThriftEnum(
        parquet_thrift.Type,
        colDef.primitiveType);

    schemaElem.repetition_type = parquet_util.setThriftEnum(
        parquet_thrift.FieldRepetitionType,
        colDef.repetition_type);

    if (colDef.originalType) {
      schemaElem.converted_type = parquet_util.setThriftEnum(
          parquet_thrift.ConvertedType,
          colDef.originalType);
    }

    metadata.schema.push(schemaElem);
  }

  let metadataEncoded = parquet_util.serializeThrift(metadata);
  let footerEncoded = new Buffer(metadataEncoded.length + 8);
  metadataEncoded.copy(footerEncoded);
  footerEncoded.writeUInt32LE(metadataEncoded.length, metadataEncoded.length);
  footerEncoded.write(PARQUET_MAGIC, metadataEncoded.length + 4);
  return footerEncoded;
}

module.exports = {
  ParquetEnvelopeWriter,
  ParquetWriter,
};

<|endoftext|>


<commit_before>'use strict'

/* eslint-env mocha */

// ===================================================================

var hashy = require('./')

// -------------------------------------------------------------------

var expect = require('chai').expect
var Bluebird = require('bluebird')
Bluebird.longStackTraces()

// ===================================================================

var data = [
  {
    value: 'password',
    hash: '$2a$10$3F2S0bh8CO8aVzW/tqyjI.iVQnLNea1YIpNSpS8dmJwUVNXP3D4/y',
    info: {
      algo: 'bcrypt',
      id: '2a',
      options: {
        cost: 10
      }
    },
    needsRehash: true
  },
  {
    value: 'password',
    hash: '$2y$08$NVRpJ.42Kt3FM0SWq/.a1uk7U8stm6Ce7EMgooPjBpDZHMiugFVhu',
    info: {
      algo: 'bcrypt',
      id: '2y',
      options: {
        cost: 8
      }
    },
    needsRehash: true
  },
  {
    value: 'password',
    hash: '$2y$11$ddGrBWDagPYovj6dsoUy6OHkeh0wNfQWWhONtPdj7q8qbPX.LtvRW',
    info: {
      algo: 'bcrypt',
      id: '2y',
      options: {
        cost: 11
      }
    },
    needsRehash: false
  }
]

// ===================================================================

describe('hash()', function () {
  var hash = hashy.hash

  it('can return a promise', function () {
    return hash('test')
  })

  it('can work with callback', function (done) {
    hash('test', done)
  })

  it('does not creates the same hash twice', function () {
    return Bluebird.all([
      hash('test'),
      hash('test')
    ]).spread(function (hash1, hash2) {
      expect(hash1).to.not.equal(hash2)
    })
  })
})

describe('getInfo()', function () {
  var getInfo = hashy.getInfo

  it('returns the algorithm and options', function () {
    data.forEach(function (datum) {
      expect(getInfo(datum.hash)).to.deep.equal(datum.info)
    })
  })
})

describe('needsRehash()', function () {
  var needsRehash = hashy.needsRehash

  it('returns true if the algorithm or the options differs', function () {
    data.forEach(function (datum) {
      expect(needsRehash(datum.hash)).to.equal(datum.needsRehash)
    })
  })
})

describe('verify()', function () {
  var verify = hashy.verify

  it('returns whether the password matches the hash', function () {
    return Bluebird.map(data, function (datum) {
      return verify(datum.value, datum.hash).then(function (success) {
        expect(success).to.be.true
      })
    })
  })
})
<commit_msg>Speed up the tests.<commit_after>'use strict'

/* eslint-env mocha */

// ===================================================================

var hashy = require('./')

// -------------------------------------------------------------------

var expect = require('chai').expect
var Bluebird = require('bluebird')
Bluebird.longStackTraces()

// ===================================================================

var data = [
  {
    value: 'password',
    hash: '$2y$04$bCdlo4cUGt5.DpaorjzbN.XUX46/YNj4iKsdTvSQ3UE0pleNR2rjS',
    info: {
      algo: 'bcrypt',
      id: '2y',
      options: {
        cost: 4
      }
    },
    needsRehash: true
  },
  {
    value: 'password',
    hash: '$2y$05$P2ZY1eZ3oex3LZJ9bGuRnugsVeq6AXy2wlasiKmYamgDEl6w2dRMG',
    info: {
      algo: 'bcrypt',
      id: '2y',
      options: {
        cost: 5
      }
    },
    needsRehash: false
  }
]

// ===================================================================

// Sets a small cost for Bcrypt to speed up the tests.
hashy.options.bcrypt.cost = 5

describe('hash()', function () {
  var hash = hashy.hash

  it('can return a promise', function () {
    return hash('test')
  })

  it('can work with callback', function (done) {
    hash('test', done)
  })

  it('does not creates the same hash twice', function () {
    return Bluebird.all([
      hash('test'),
      hash('test')
    ]).spread(function (hash1, hash2) {
      expect(hash1).to.not.equal(hash2)
    })
  })
})

describe('getInfo()', function () {
  var getInfo = hashy.getInfo

  it('returns the algorithm and options', function () {
    data.forEach(function (datum) {
      expect(getInfo(datum.hash)).to.deep.equal(datum.info)
    })
  })
})

describe('needsRehash()', function () {
  var needsRehash = hashy.needsRehash

  it('returns true if the algorithm or the options differs', function () {
    data.forEach(function (datum) {
      expect(needsRehash(datum.hash)).to.equal(datum.needsRehash)
    })
  })
})

describe('verify()', function () {
  var verify = hashy.verify

  it('returns whether the password matches the hash', function () {
    return Bluebird.map(data, function (datum) {
      return verify(datum.value, datum.hash).then(function (success) {
        expect(success).to.be.true
      })
    })
  })
})
<|endoftext|>


<commit_before>import chai from 'chai';
import * as stack from './index';

let assert = chai.assert;

describe('Stack tests', () => {

    it('should be properly defined as an object', () => {
        assert.isObject(stack);
        assert.isFunction(stack.create);
    });

    it('shoult return a function when create is called', () => {
        let funcArr = [(data, original, next)=>{
                data.num ++;
                next(data);
            }
            , (data, original, next) => {
                data.num ++;
                next(data);
            }
            , (data, original) => {
                return new Promise(function (res, rej){
                    data.num ++;
                    res(data);
                });
            }];

        assert.isFunction(stack.create(funcArr));
    });

    it('should execute each of an array of functions in order', (done) => {
        let funcArr = [(data, original, next)=>{
                data.num ++;
                next(data);
            }
            , (data, original, next) => {
                data.num ++;
                next(data);
            }
            , (data, original) => {
                return new Promise(function (res, rej){
                    data.num ++;
                    res(data);
                });
            }]

            , run = stack.create(funcArr);

        run({num: 1}).then(function (data){
            assert.isTrue(data.num === 4);
            done();
        });
    });

    it('should pass through if no functions', (done) => {
        let funcArr

            , run = stack.create(funcArr);

        run({num: 1}).then(function (data){
            assert.isTrue(data.num === 1);
            done();
        });
    });
});
<commit_msg>tweaked the tests a bit<commit_after>import chai from 'chai';
import * as stack from './index';

let assert = chai.assert;

describe('Stack tests', () => {

    it('should be properly defined as an object', () => {
        assert.isObject(stack);
        assert.isFunction(stack.create);
    });

    it('shoult return a function when create is called', () => {
        let funcArr = [(data, original, next)=>{
                data.num ++;
                next(data);
            }
            , (data, original, next) => {
                data.num ++;
                next(data);
            }
            , (data, original) => {
                return new Promise(function (res, rej){
                    data.num ++;
                    res(data);
                });
            }];

        assert.isFunction(stack.create(funcArr));
    });

    it('should execute each of an array of functions in order', (done) => {
        let funcArr = [(data, original, next)=>{
                assert.isTrue(data.num === 0);
                data.num ++;
                assert.isTrue(data.num === 2);
                next(data);
            }
            , (data, original, next) => {
                data.num ++;
                assert.isTrue(data.num === 3);
                next(data);
            }
            , (data, original) => {
                return new Promise(function (res, rej){
                    data.num ++;
                    assert.isTrue(data.num === 4);
                    res(data);
                });
            }]

            , run = stack.create(funcArr);

        run({num: 1}).then(function (data){
            assert.isTrue(data.num === 4);
            done();
        }).catch(function (err){
            done();
        });
    });

    it('should pass through if no functions', (done) => {
        let funcArr

            , run = stack.create(funcArr);

        run({num: 1}).then(function (data){
            assert.isTrue(data.num === 1);
            done();
        });
    });
});
<|endoftext|>


<commit_before>module.exports = new users();

function users (){
    this.list = [];
}

users.prototype.add = function(usrObj){
    this.list.push(usrObj);
};

users.prototype.remove = function(usrObj){
    var index = _search(this,usrObj);
    if(index !== null){
        return this.list.splice(index, 1);
    }
    return null;
};

users.prototype.search = function (usrObj){
    var index = _search(this,usrObj);
    if(index !== null)
        return this.list[index];
    return null;
};

users.prototype.update = function (usrObj){
    if(!usrObj.uname){
        return null;
    }
    var index = _search(this,{'socket':usrObj.socket});
    var isExist = this.search({'uname':usrObj.uname});
    if(index !== null && isExist === null){
        this.list[index] = usrObj;
        return this.list[index];
    }
    return null;
};

function _search(self,usrObj){
    var i;
    if(!usrObj)
        return;
    if(usrObj.socket){
        for(i = 0 ; i < self.list.length ; i++){
            if(self.list[i].socket == usrObj.socket){
                return i;
            }
        }
    }

    if(usrObj.uname){
        for(i = 0 ; i < self.list.length ; i++){
            if(self.list[i].uname == usrObj.uname){
                return i;
            }
        }
    }
    return null;
}<commit_msg>adding user class<commit_after>module.exports = new users();

function users (){
    this.list = [];
}

users.prototype.add = function(usrObj){
    this.list.push(usrObj);
};

users.prototype.remove = function(usrObj){
    var index = _search(this,usrObj);
    if(index !== null){
        return this.list.splice(index, 1);
    }
    return null;
};

users.prototype.search = function (usrObj){
    var index = _search(this,usrObj);
    if(index !== null)
        return this.list[index];
    return null;
};

users.prototype.update = function (usrObj){
    if(!usrObj.uname){
        return null;
    }
    var index = _search(this,{'socket':usrObj.socket});
    var isExist = this.search({'uname':usrObj.uname});
    if(index !== null && isExist === null){
        this.list[index] = usrObj;
        return this.list[index];
    }
    return null;
};

users.prototype.getUsers = function (){

};

function _search(self,usrObj){
    var i;
    if(!usrObj)
        return;
    if(usrObj.socket){
        for(i = 0 ; i < self.list.length ; i++){
            if(self.list[i].socket == usrObj.socket){
                return i;
            }
        }
    }

    if(usrObj.uname){
        for(i = 0 ; i < self.list.length ; i++){
            if(self.list[i].uname == usrObj.uname){
                return i;
            }
        }
    }
    return null;
}

function user(params){
    if(params){
        for (var x in params){
            this[x] = params[x];
        }
    }
}<|endoftext|>


<commit_before>/**
 * This is where users are stored.
 *
 * New users are processed when joining new rooms and on receiving join
 * messages from the server. User chat data is processed here for use
 * in command permissions and automatic moderation.
 */

var Users = Object.create(null);
var users = Users.users = Object.create(null);
var getUser = Users.get = function (username) {
	var userid = toId(username);
	return users[userid];
};
var addUser = Users.add = function (username, room) {
	var user = getUser(username);
	if (!user) {
		user = new User(username, room);
		users[user.id] = user;
	}
	return user;
};

class User {
	constructor(username, roomid) {
		this.name = username.substr(1);
		this.id = toId(this.name);
		this.rooms = new Map();
		if (roomid) this.rooms.set(roomid, username.charAt(0));
	}

	isExcepted () {
		return Config.excepts.includes(this.id);
	}

	isWhitelisted () {
		return Config.whitelist.includes(this.id);
	}

	isRegexWhitelisted () {
		return Config.regexautobanwhitelist.includes(this.id);
	}

	hasRank (roomid, tarGroup) {
		if (this.isExcepted) return true;
		var group = this.rooms.get(roomid) || roomid; // PM messages use the roomid parameter as the user's group
		return Config.groups[group] >= Config.groups[tarGroup];
	}

	canUse (cmd, roomid) {
		var settings = Parse.settings[cmd];
		if (!settings || !settings[roomid]) {
			return this.hasRank(roomid, (cmd === 'autoban' || cmd === 'blacklist') ? '#' : Config.defaultrank);
		}

		var setting = settings[roomid];
		if (setting === true) return true;
		return this.hasRank(roomid, setting);
	}

	rename (username) {
		var oldid = this.id;
		delete users[oldid];
		this.id = toId(username);
		this.name = username.substr(1);
		users[this.id] = this;
		return this;
	}

	destroy () {
		this.rooms.forEach(function (group, roomid) {
			var room = Rooms.get(roomid);
			room.users.delete(this.id);
		});
		this.rooms.clear();
		delete users[this.id];
	}
}

Users.self = addUser(' ' + Config.nick);
module.exports = Users;
<commit_msg>Fix Users.self permission checks<commit_after>/**
 * This is where users are stored.
 *
 * New users are processed when joining new rooms and on receiving join
 * messages from the server. User chat data is processed here for use
 * in command permissions and automatic moderation.
 */

var Users = Object.create(null);
var users = Users.users = Object.create(null);
var getUser = Users.get = function (username) {
	var userid = toId(username);
	return users[userid];
};
var addUser = Users.add = function (username, room) {
	var user = getUser(username);
	if (!user) {
		user = new User(username, room);
		users[user.id] = user;
	}
	return user;
};

class User {
	constructor (username, roomid) {
		this.name = username.substr(1);
		this.id = toId(this.name);
		this.rooms = new Map();
		if (roomid) this.rooms.set(roomid, username.charAt(0));
	}

	isExcepted () {
		return Config.excepts.includes(this.id);
	}

	isWhitelisted () {
		return Config.whitelist.includes(this.id);
	}

	isRegexWhitelisted () {
		return Config.regexautobanwhitelist.includes(this.id);
	}

	hasRank (roomid, tarGroup) {
		if (this.isExcepted()) return true;
		var group = this.rooms.get(roomid) || roomid; // PM messages use the roomid parameter as the user's group
		return Config.groups[group] >= Config.groups[tarGroup];
	}

	canUse (cmd, roomid) {
		if (this.isExcepted()) return true;
		var settings = Parse.settings[cmd];
		if (!settings || !settings[roomid]) {
			return this.hasRank(roomid, (cmd === 'autoban' || cmd === 'blacklist') ? '#' : Config.defaultrank);
		}

		var setting = settings[roomid];
		if (setting === true) return true;
		return this.hasRank(roomid, setting);
	}

	rename (username) {
		var oldid = this.id;
		delete users[oldid];
		this.id = toId(username);
		this.name = username.substr(1);
		users[this.id] = this;
		return this;
	}

	destroy () {
		this.rooms.forEach(function (group, roomid) {
			var room = Rooms.get(roomid);
			room.users.delete(this.id);
		});
		this.rooms.clear();
		delete users[this.id];
	}
}

Users.self = addUser(' ' + Config.nick);
module.exports = Users;
<|endoftext|>


<commit_before>import {Keymap, Plugin} from "../edit"

// ;; Input rules are regular expressions describing a piece of text
// that, when typed, causes something to happen. This might be
// changing two dashes into an emdash, wrapping a paragraph starting
// with `"> "` into a blockquote, or something entirely different.
export class InputRule {
  // :: (RegExp, ?string, union<string, (pm: ProseMirror, match: [string], pos: number)>)
  // Create an input rule. The rule applies when the user typed
  // something and the text directly in front of the cursor matches
  // `match`, which should probably end with `$`. You can optionally
  // provide a filter, which should be a single character that always
  // appears at the end of the match, and will be used to only apply
  // the rule when there's an actual chance of it succeeding.
  //
  // The `handler` can be a string, in which case the matched text
  // will simply be replaced by that string, or a function, which will
  // be called with the match array produced by
  // [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
  // and should produce the effect of the rule.
  constructor(match, filter, handler) {
    this.filter = filter
    this.match = match
    this.handler = handler
  }
}

// ;; Manages the set of active input rules for an editor. Created
// with the `inputRules` plugin.
class InputRules {
  constructor(pm, options) {
    this.pm = pm
    this.rules = []
    this.cancelVersion = null

    pm.on("selectionChange", this.onSelChange = () => this.cancelVersion = null)
    pm.on("textInput", this.onTextInput = this.onTextInput.bind(this))
    pm.addKeymap(new Keymap({Backspace: pm => this.backspace(pm)}, {name: "inputRules"}), 20)

    options.rules.forEach(rule => this.addRule(rule))
  }

  detach() {
    this.pm.off("selectionChange", this.onSelChange)
    this.pm.off("textInput", this.onTextInput)
    this.pm.removeKeymap("inputRules")
  }

  // :: (InputRule)
  // Add the given input rule to the editor.
  addRule(rule) {
    this.rules.push(rule)
  }

  // :: (InputRule) → bool
  // Remove the given input rule from the editor. Returns false if the
  // rule wasn't found.
  removeRule(rule) {
    let found = this.rules.indexOf(rule)
    if (found > -1) {
      this.rules.splice(found, 1)
      return true
    }
    return false
  }

  onTextInput(text) {
    let pos = this.pm.selection.head
    if (!pos) return

    let textBefore, isCode, $pos
    let lastCh = text[text.length - 1]

    for (let i = 0; i < this.rules.length; i++) {
      let rule = this.rules[i], match
      if (rule.filter && rule.filter != lastCh) continue
      if (!$pos) {
        $pos = this.pm.doc.resolve(pos)
        ;({textBefore, isCode} = getContext($pos))
        if (isCode) return
      }
      if (match = rule.match.exec(textBefore)) {
        let startVersion = this.pm.history.getVersion()
        if (typeof rule.handler == "string") {
          let start = pos - (match[1] || match[0]).length
          let marks = this.pm.doc.marksAt(pos)
          this.pm.tr.delete(start, pos)
                    .insert(start, this.pm.schema.text(rule.handler, marks))
                    .apply()
        } else {
          rule.handler(this.pm, match, pos)
        }
        this.cancelVersion = startVersion
        return
      }
    }
  }

  backspace() {
    if (this.cancelVersion) {
      this.pm.history.backToVersion(this.cancelVersion)
      this.cancelVersion = null
    } else {
      return false
    }
  }
}

function getContext($pos) {
  let parent = $pos.parent, isCode = parent.type.isCode
  let textBefore = ""
  for (let i = 0, rem = $pos.parentOffset; rem > 0; i++) {
    let child = parent.child(i)
    if (child.isText) textBefore += child.text.slice(0, rem)
    else textBefore += "\ufffc"
    rem -= child.nodeSize
    if (rem <= 0 && child.marks.some(st => st.type.isCode)) isCode = true
  }
  return {textBefore, isCode}
}

// :: Plugin
// A plugin for adding input rules to an editor. A common pattern of
// use is to call `inputRules.ensure(editor).addRule(...)` to get an
// instance of the plugin state and add a rule to it.
//
// Takes a single option, `rules`, which may be an array of
// `InputRules` objects to add to initially add.
export const inputRules = new Plugin(InputRules, {
  rules: []
})
<commit_msg>Avoid a boatload of position resutions<commit_after>import {Keymap, Plugin} from "../edit"

// ;; Input rules are regular expressions describing a piece of text
// that, when typed, causes something to happen. This might be
// changing two dashes into an emdash, wrapping a paragraph starting
// with `"> "` into a blockquote, or something entirely different.
export class InputRule {
  // :: (RegExp, ?string, union<string, (pm: ProseMirror, match: [string], pos: number)>)
  // Create an input rule. The rule applies when the user typed
  // something and the text directly in front of the cursor matches
  // `match`, which should probably end with `$`. You can optionally
  // provide a filter, which should be a single character that always
  // appears at the end of the match, and will be used to only apply
  // the rule when there's an actual chance of it succeeding.
  //
  // The `handler` can be a string, in which case the matched text
  // will simply be replaced by that string, or a function, which will
  // be called with the match array produced by
  // [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
  // and should produce the effect of the rule.
  constructor(match, filter, handler) {
    this.filter = filter
    this.match = match
    this.handler = handler
  }
}

// ;; Manages the set of active input rules for an editor. Created
// with the `inputRules` plugin.
class InputRules {
  constructor(pm, options) {
    this.pm = pm
    this.rules = []
    this.cancelVersion = null

    pm.on("selectionChange", this.onSelChange = () => this.cancelVersion = null)
    pm.on("textInput", this.onTextInput = this.onTextInput.bind(this))
    pm.addKeymap(new Keymap({Backspace: pm => this.backspace(pm)}, {name: "inputRules"}), 20)

    options.rules.forEach(rule => this.addRule(rule))
  }

  detach() {
    this.pm.off("selectionChange", this.onSelChange)
    this.pm.off("textInput", this.onTextInput)
    this.pm.removeKeymap("inputRules")
  }

  // :: (InputRule)
  // Add the given input rule to the editor.
  addRule(rule) {
    this.rules.push(rule)
  }

  // :: (InputRule) → bool
  // Remove the given input rule from the editor. Returns false if the
  // rule wasn't found.
  removeRule(rule) {
    let found = this.rules.indexOf(rule)
    if (found > -1) {
      this.rules.splice(found, 1)
      return true
    }
    return false
  }

  onTextInput(text) {
    let $pos = this.pm.selection.$head
    if (!$pos) return

    let textBefore, isCode
    let lastCh = text[text.length - 1]

    for (let i = 0; i < this.rules.length; i++) {
      let rule = this.rules[i], match
      if (rule.filter && rule.filter != lastCh) continue
      if (textBefore == null) {
        ;({textBefore, isCode} = getContext($pos))
        if (isCode) return
      }
      if (match = rule.match.exec(textBefore)) {
        let startVersion = this.pm.history.getVersion()
        if (typeof rule.handler == "string") {
          let start = $pos.pos - (match[1] || match[0]).length
          let marks = this.pm.doc.marksAt($pos.pos)
          this.pm.tr.delete(start, $pos.pos)
                    .insert(start, this.pm.schema.text(rule.handler, marks))
                    .apply()
        } else {
          rule.handler(this.pm, match, $pos.pos)
        }
        this.cancelVersion = startVersion
        return
      }
    }
  }

  backspace() {
    if (this.cancelVersion) {
      this.pm.history.backToVersion(this.cancelVersion)
      this.cancelVersion = null
    } else {
      return false
    }
  }
}

function getContext($pos) {
  let parent = $pos.parent, isCode = parent.type.isCode
  let textBefore = ""
  for (let i = 0, rem = $pos.parentOffset; rem > 0; i++) {
    let child = parent.child(i)
    if (child.isText) textBefore += child.text.slice(0, rem)
    else textBefore += "\ufffc"
    rem -= child.nodeSize
    if (rem <= 0 && child.marks.some(st => st.type.isCode)) isCode = true
  }
  return {textBefore, isCode}
}

// :: Plugin
// A plugin for adding input rules to an editor. A common pattern of
// use is to call `inputRules.ensure(editor).addRule(...)` to get an
// instance of the plugin state and add a rule to it.
//
// Takes a single option, `rules`, which may be an array of
// `InputRules` objects to add to initially add.
export const inputRules = new Plugin(InputRules, {
  rules: []
})
<|endoftext|>


<commit_before>/**
 * Connections
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Abstraction layer for multi-process SockJS connections.
 *
 * @license MIT license
 */

var cluster = require('cluster');
var config = require('./config/config');

if (cluster.isMaster) {

	cluster.setupMaster({
		exec: 'sockets.js'
	});

	var workers = exports.workers = {};

	var spawnWorker = exports.spawnWorker = function() {
		var worker = cluster.fork();
		var id = worker.id;
		workers[id] = worker;
		worker.on('message', function(data) {
			// console.log('master received: '+data);
			switch (data.charAt(0)) {
			case '*': // *socketid, ip
				// connect
				var nlPos = data.indexOf('\n');
				Users.socketConnect(worker, id, data.substr(1, nlPos-1), data.substr(nlPos+1));
				break;

			case '!': // !socketid
				// disconnect
				Users.socketDisconnect(worker, id, data.substr(1));
				break;

			case '<': // <socketid, message
				// message
				var nlPos = data.indexOf('\n');
				Users.socketReceive(worker, id, data.substr(1, nlPos-1), data.substr(nlPos+1));
				break;
			}
		});
	};

	var workerCount = config.workers || 1;
	for (var i=0; i<workerCount; i++) {
		spawnWorker();
	}

	var killWorker = exports.killWorker = function(worker) {
		var idd = worker.id+'-';
		var count = 0;
		for (var connectionid in Users.connections) {
			if (connectionid.substr(idd.length) === idd) {
				var connection = Users.connections[connectionid];
				Users.socketDisconnect(worker, worker.id, connection.socketid);
				count++;
			}
		}
		worker.kill();
		delete workers[worker.id];
		return count;
	};

	var killPid = exports.killPid = function(pid) {
		pid = ''+pid;
		for (var id in workers) {
			var worker = workers[id];
			if (pid === ''+worker.process.pid) {
				return killWorker(worker);
			}
		}
		return false;
	};

	exports.socketSend = function(worker, socketid, message) {
		worker.send('>'+socketid+'\n'+message);
	};
	exports.socketDisconnect = function(worker, socketid) {
		worker.send('!'+socketid);
	};

	exports.channelBroadcast = function(channelid, message) {
		for (var workerid in workers) {
			workers[workerid].send('#'+channelid+'\n'+message);
		}
	};
	exports.channelSend = function(worker, channelid, message) {
		worker.send('#'+channelid+'\n'+message);
	};
	exports.channelAdd = function(worker, channelid, socketid) {
		worker.send('+'+channelid+'\n'+socketid);
	};
	exports.channelRemove = function(worker, channelid, socketid) {
		worker.send('-'+channelid+'\n'+socketid);
	};

} else {
	// is worker

	// Static HTTP server

	// This handles the custom CSS and custom avatar features, and also
	// redirects yourserver:8001 to yourserver-8001.psim.us

	// It's optional if you don't need these features.

	var Cidr = require('./cidr');

	var app = require('http').createServer();
	var appssl;
	if (config.ssl) {
		appssl = require('https').createServer(config.ssl.options);
	}
	try {
		(function() {
			var nodestatic = require('node-static');
			var cssserver = new nodestatic.Server('./config');
			var avatarserver = new nodestatic.Server('./config/avatars');
			var staticserver = new nodestatic.Server('./static');
			var staticRequestHandler = function(request, response) {
				request.resume();
				request.addListener('end', function() {
					if (config.customhttpresponse &&
							config.customhttpresponse(request, response)) {
						return;
					}
					var server;
					if (request.url === '/custom.css') {
						server = cssserver;
					} else if (request.url.substr(0, 9) === '/avatars/') {
						request.url = request.url.substr(8);
						server = avatarserver;
					} else {
						if (/^\/([A-Za-z0-9][A-Za-z0-9-]*)\/?$/.test(request.url)) {
							request.url = '/';
						}
						server = staticserver;
					}
					server.serve(request, response, function(e, res) {
						if (e && (e.status === 404)) {
							staticserver.serveFile('404.html', 404, {}, request, response);
						}
					});
				});
			};
			app.on('request', staticRequestHandler);
			if (appssl) {
				appssl.on('request', staticRequestHandler);
			}
		})();
	} catch (e) {
		console.log('Could not start node-static - try `npm install` if you want to use it');
	}

	// SockJS server

	// This is the main server that handles users connecting to our server
	// and doing things on our server.

	var sockjs = require('sockjs');

	var server = sockjs.createServer({
		sockjs_url: "//play.pokemonshowdown.com/js/lib/sockjs-0.3.min.js",
		log: function(severity, message) {
			if (severity === 'error') console.log('ERROR: '+message);
		},
		prefix: '/showdown',
		websocket: !config.disablewebsocket
	});

	// Make `app`, `appssl`, and `server` available to the console.
	global.App = app;
	global.AppSSL = appssl;
	global.Server = server;

	var sockets = {};
	var channels = {};

	process.on('message', function(data) {
		// console.log('worker received: '+data);
		var socket = null;
		var socketid = null;
		var channelid = null;
		switch (data.charAt(0)) {
		case '!': // !socketid
			// destroy
			socketid = data.substr(1);
			socket = sockets[socketid];
			if (!socket) return;
			socket.end();
			// After sending the FIN packet, we make sure the I/O is totally blocked for this socket
			socket.destroy();
			delete sockets[socketid];
			for (channelid in channels) {
				delete channels[channelid][socketid];
			}
			break;

		case '>': // >socketid, message
			// message
			var nlLoc = data.indexOf('\n');
			socket = sockets[data.substr(1, nlLoc-1)];
			if (!socket) return;
			socket.write(data.substr(nlLoc+1));
			break;

		case '#': // #channelid, message
			// message to channel
			var nlLoc = data.indexOf('\n');
			channel = channels[data.substr(1, nlLoc-1)];
			var message = data.substr(nlLoc+1);
			for (socketid in channel) {
				channel[socketid].write(message);
			}
			break;

		case '+': // +channelid, socketid
			// add to channel
			var nlLoc = data.indexOf('\n');
			socketid = data.substr(nlLoc+1);
			socket = sockets[socketid];
			if (!socket) return;
			channelid = data.substr(1, nlLoc-1);
			var channel = channels[channelid];
			if (!channel) channel = channels[channelid] = {};
			channel[socketid] = socket;
			break;

		case '-': // -channelid, socketid
			// remove from channel
			var nlLoc = data.indexOf('\n');
			var channel = channels[data.substr(1, nlLoc-1)];
			if (!channel) return;
			delete channel[data.substr(nlLoc+1)];
			break;
		}
	});

	// this is global so it can be hotpatched if necessary
	var isTrustedProxyIp = Cidr.checker(config.proxyip);
	var socketCounter = 0;
	server.on('connection', function(socket) {
		if (!socket) {
			// For reasons that are not entirely clear, SockJS sometimes triggers
			// this event with a null `socket` argument.
			return;
		} else if (!socket.remoteAddress) {
			// This condition occurs several times per day. It may be a SockJS bug.
			try {
				socket.end();
			} catch (e) {}
			return;
		}
		var socketid = socket.id = (++socketCounter);

		sockets[socket.id] = socket;

		if (isTrustedProxyIp(socket.remoteAddress)) {
			var ips = (socket.headers['x-forwarded-for'] || '').split(',');
			var ip;
			while (ip = ips.pop()) {
				ip = ip.trim();
				if (!isTrustedProxyIp(ip)) {
					socket.remoteAddress = ip;
					break;
				}
			}
		}

		process.send('*'+socketid+'\n'+socket.remoteAddress);

		// console.log('CONNECT: '+socket.remoteAddress+' ['+socket.id+']');
		var interval;
		if (config.herokuhack) {
			// see https://github.com/sockjs/sockjs-node/issues/57#issuecomment-5242187
			interval = setInterval(function() {
				try {
					socket._session.recv.didClose();
				} catch (e) {}
			}, 15000);
		}

		socket.on('data', function(message) {
			process.send('<'+socketid+'\n'+message);
		});

		socket.on('close', function() {
			if (interval) {
				clearInterval(interval);
			}
			process.send('!'+socketid);

			delete sockets[socketid];
			for (channelid in channels) {
				delete channels[channelid][socketid];
			}
		});
	});
	server.installHandlers(app, {});
	app.listen(config.port);
	console.log('Worker '+cluster.worker.id+' now listening on port ' + config.port);

	if (appssl) {
		server.installHandlers(appssl, {});
		appssl.listen(config.ssl.port);
		console.log('Worker '+cluster.worker.id+' now listening for SSL on port ' + config.ssl.port);
	}

	console.log('Test your server at http://localhost:' + config.port);

}
<commit_msg>Crashguard socket worker processes<commit_after>/**
 * Connections
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Abstraction layer for multi-process SockJS connections.
 *
 * @license MIT license
 */

var cluster = require('cluster');
var config = require('./config/config');

if (cluster.isMaster) {

	cluster.setupMaster({
		exec: 'sockets.js'
	});

	var workers = exports.workers = {};

	var spawnWorker = exports.spawnWorker = function() {
		var worker = cluster.fork();
		var id = worker.id;
		workers[id] = worker;
		worker.on('message', function(data) {
			// console.log('master received: '+data);
			switch (data.charAt(0)) {
			case '*': // *socketid, ip
				// connect
				var nlPos = data.indexOf('\n');
				Users.socketConnect(worker, id, data.substr(1, nlPos-1), data.substr(nlPos+1));
				break;

			case '!': // !socketid
				// disconnect
				Users.socketDisconnect(worker, id, data.substr(1));
				break;

			case '<': // <socketid, message
				// message
				var nlPos = data.indexOf('\n');
				Users.socketReceive(worker, id, data.substr(1, nlPos-1), data.substr(nlPos+1));
				break;
			}
		});
	};

	var workerCount = config.workers || 1;
	for (var i=0; i<workerCount; i++) {
		spawnWorker();
	}

	var killWorker = exports.killWorker = function(worker) {
		var idd = worker.id+'-';
		var count = 0;
		for (var connectionid in Users.connections) {
			if (connectionid.substr(idd.length) === idd) {
				var connection = Users.connections[connectionid];
				Users.socketDisconnect(worker, worker.id, connection.socketid);
				count++;
			}
		}
		worker.kill();
		delete workers[worker.id];
		return count;
	};

	var killPid = exports.killPid = function(pid) {
		pid = ''+pid;
		for (var id in workers) {
			var worker = workers[id];
			if (pid === ''+worker.process.pid) {
				return killWorker(worker);
			}
		}
		return false;
	};

	exports.socketSend = function(worker, socketid, message) {
		worker.send('>'+socketid+'\n'+message);
	};
	exports.socketDisconnect = function(worker, socketid) {
		worker.send('!'+socketid);
	};

	exports.channelBroadcast = function(channelid, message) {
		for (var workerid in workers) {
			workers[workerid].send('#'+channelid+'\n'+message);
		}
	};
	exports.channelSend = function(worker, channelid, message) {
		worker.send('#'+channelid+'\n'+message);
	};
	exports.channelAdd = function(worker, channelid, socketid) {
		worker.send('+'+channelid+'\n'+socketid);
	};
	exports.channelRemove = function(worker, channelid, socketid) {
		worker.send('-'+channelid+'\n'+socketid);
	};

} else {
	// is worker

	// Static HTTP server

	// This handles the custom CSS and custom avatar features, and also
	// redirects yourserver:8001 to yourserver-8001.psim.us

	// It's optional if you don't need these features.

	var Cidr = require('./cidr');

	if (config.crashguard) {
		// graceful crash
		process.on('uncaughtException', function(err) {
			require('./crashlogger.js')(err, 'Socket process '+cluster.worker.id+' ('+process.pid+')');
		});
	}

	var app = require('http').createServer();
	var appssl;
	if (config.ssl) {
		appssl = require('https').createServer(config.ssl.options);
	}
	try {
		(function() {
			var nodestatic = require('node-static');
			var cssserver = new nodestatic.Server('./config');
			var avatarserver = new nodestatic.Server('./config/avatars');
			var staticserver = new nodestatic.Server('./static');
			var staticRequestHandler = function(request, response) {
				request.resume();
				request.addListener('end', function() {
					if (config.customhttpresponse &&
							config.customhttpresponse(request, response)) {
						return;
					}
					var server;
					if (request.url === '/custom.css') {
						server = cssserver;
					} else if (request.url.substr(0, 9) === '/avatars/') {
						request.url = request.url.substr(8);
						server = avatarserver;
					} else {
						if (/^\/([A-Za-z0-9][A-Za-z0-9-]*)\/?$/.test(request.url)) {
							request.url = '/';
						}
						server = staticserver;
					}
					server.serve(request, response, function(e, res) {
						if (e && (e.status === 404)) {
							staticserver.serveFile('404.html', 404, {}, request, response);
						}
					});
				});
			};
			app.on('request', staticRequestHandler);
			if (appssl) {
				appssl.on('request', staticRequestHandler);
			}
		})();
	} catch (e) {
		console.log('Could not start node-static - try `npm install` if you want to use it');
	}

	// SockJS server

	// This is the main server that handles users connecting to our server
	// and doing things on our server.

	var sockjs = require('sockjs');

	var server = sockjs.createServer({
		sockjs_url: "//play.pokemonshowdown.com/js/lib/sockjs-0.3.min.js",
		log: function(severity, message) {
			if (severity === 'error') console.log('ERROR: '+message);
		},
		prefix: '/showdown',
		websocket: !config.disablewebsocket
	});

	// Make `app`, `appssl`, and `server` available to the console.
	global.App = app;
	global.AppSSL = appssl;
	global.Server = server;

	var sockets = {};
	var channels = {};

	process.on('message', function(data) {
		// console.log('worker received: '+data);
		var socket = null;
		var socketid = null;
		var channelid = null;
		switch (data.charAt(0)) {
		case '!': // !socketid
			// destroy
			socketid = data.substr(1);
			socket = sockets[socketid];
			if (!socket) return;
			socket.end();
			// After sending the FIN packet, we make sure the I/O is totally blocked for this socket
			socket.destroy();
			delete sockets[socketid];
			for (channelid in channels) {
				delete channels[channelid][socketid];
			}
			break;

		case '>': // >socketid, message
			// message
			var nlLoc = data.indexOf('\n');
			socket = sockets[data.substr(1, nlLoc-1)];
			if (!socket) return;
			socket.write(data.substr(nlLoc+1));
			break;

		case '#': // #channelid, message
			// message to channel
			var nlLoc = data.indexOf('\n');
			channel = channels[data.substr(1, nlLoc-1)];
			var message = data.substr(nlLoc+1);
			for (socketid in channel) {
				channel[socketid].write(message);
			}
			break;

		case '+': // +channelid, socketid
			// add to channel
			var nlLoc = data.indexOf('\n');
			socketid = data.substr(nlLoc+1);
			socket = sockets[socketid];
			if (!socket) return;
			channelid = data.substr(1, nlLoc-1);
			var channel = channels[channelid];
			if (!channel) channel = channels[channelid] = {};
			channel[socketid] = socket;
			break;

		case '-': // -channelid, socketid
			// remove from channel
			var nlLoc = data.indexOf('\n');
			var channel = channels[data.substr(1, nlLoc-1)];
			if (!channel) return;
			delete channel[data.substr(nlLoc+1)];
			break;
		}
	});

	// this is global so it can be hotpatched if necessary
	var isTrustedProxyIp = Cidr.checker(config.proxyip);
	var socketCounter = 0;
	server.on('connection', function(socket) {
		if (!socket) {
			// For reasons that are not entirely clear, SockJS sometimes triggers
			// this event with a null `socket` argument.
			return;
		} else if (!socket.remoteAddress) {
			// This condition occurs several times per day. It may be a SockJS bug.
			try {
				socket.end();
			} catch (e) {}
			return;
		}
		var socketid = socket.id = (++socketCounter);

		sockets[socket.id] = socket;

		if (isTrustedProxyIp(socket.remoteAddress)) {
			var ips = (socket.headers['x-forwarded-for'] || '').split(',');
			var ip;
			while (ip = ips.pop()) {
				ip = ip.trim();
				if (!isTrustedProxyIp(ip)) {
					socket.remoteAddress = ip;
					break;
				}
			}
		}

		process.send('*'+socketid+'\n'+socket.remoteAddress);

		// console.log('CONNECT: '+socket.remoteAddress+' ['+socket.id+']');
		var interval;
		if (config.herokuhack) {
			// see https://github.com/sockjs/sockjs-node/issues/57#issuecomment-5242187
			interval = setInterval(function() {
				try {
					socket._session.recv.didClose();
				} catch (e) {}
			}, 15000);
		}

		socket.on('data', function(message) {
			process.send('<'+socketid+'\n'+message);
		});

		socket.on('close', function() {
			if (interval) {
				clearInterval(interval);
			}
			process.send('!'+socketid);

			delete sockets[socketid];
			for (channelid in channels) {
				delete channels[channelid][socketid];
			}
		});
	});
	server.installHandlers(app, {});
	app.listen(config.port);
	console.log('Worker '+cluster.worker.id+' now listening on port ' + config.port);

	if (appssl) {
		server.installHandlers(appssl, {});
		appssl.listen(config.ssl.port);
		console.log('Worker '+cluster.worker.id+' now listening for SSL on port ' + config.ssl.port);
	}

	console.log('Test your server at http://localhost:' + config.port);

}
<|endoftext|>


<commit_before>const {createHash} = require('crypto');
const fs = require('fs');
const util = require('util');

// Utility functions

// Check that the list of parameters specified exist.
// If at least one is missing false is returned, otherwise true is returned.
//
// If a parameter is specified as as symbol only existence is tested.
// If it is specified as a string the parameter must also meet the condition
// of being a non empty string.
function checkParams (params, ...required) {
  for (let p of required) {
    if (params[p] && typeof params[p] == 'string') params[p] = params[p].trim();
    if (!params[p]) return false;
  }
  return true;
}

// Return the hex representation of an unguessable 160 bit random number.
async function getRand() {
  let p = new Promise((resolve, reject) => {
    fs.open('/dev/urandom', 'r', (err, fd) => {
      if (err) return reject(err);
      let b = new Buffer(20);
      fs.read(fd, b, 0, 20, 0, (err, byteRead, buffer) => {
        if (err) reject(err);
        resolve(buffer.toString('hex'));
      });
    });
  });
  return await p;
}

function hexdigest(str, algorithm = 'md5') {
  return createHash(algorithm).update(str).digest('hex');
}

// Return a Number representing the seconds elapsed since the UNIX epoch.
function numElapsed(t) {
  let ms = t && new Date(t).getTime();
  if (typeof ms != 'number' || isNaN(ms)) ms = Date.now();
  return parseInt(ms / 1000);
}

// Given an unix time in the past returns a string stating how much time
// has elapsed from the specified time, in the form "2 hours ago".
function strElapsed(t){
  let seconds = parseInt(numElapsed() - t);
  if (seconds <= 1) return 'now';

  let time_lengths = [[86400, "day"], [3600, "hour"], [60, "minute"], [1, "second"]];
  let [length, label] = time_lengths.filter((item) => {
    return seconds >= item[0];
  })[0];
  let units = parseInt(seconds / length);
  return `${units} ${label}${units > 1 ? 's' : ''} ago`;
}

// Combined Comparison / "Spaceship" Operator (<=>) in JavaScript
// See http://stackoverflow.com/questions/34852855/combined-comparison-spaceship-operator-in-javascript
function spaceship(a, b) {
  if ((a === null || b === null) || (typeof a != typeof b)) {
    return null;
  }
  if (typeof a === 'string') {
    return (a).localeCompare(b);
  } else {
    if (a > b) {
      return 1;
    } else if (a < b) {
      return -1;
    }
    return 0;
  }
}

module.exports = {
  checkParams: checkParams,
  getRand: getRand,
  hexdigest: hexdigest,
  numElapsed: numElapsed,
  strElapsed: strElapsed,
  spaceship: spaceship
}<commit_msg>Return directly the promise in getRand function, needn't await it<commit_after>const {createHash} = require('crypto');
const fs = require('fs');
const util = require('util');

// Utility functions

// Check that the list of parameters specified exist.
// If at least one is missing false is returned, otherwise true is returned.
//
// If a parameter is specified as as symbol only existence is tested.
// If it is specified as a string the parameter must also meet the condition
// of being a non empty string.
function checkParams (params, ...required) {
  for (let p of required) {
    if (params[p] && typeof params[p] == 'string') params[p] = params[p].trim();
    if (!params[p]) return false;
  }
  return true;
}

// Return a promise will resolve with the hex representation of an unguessable 160 bit random number.
function getRand() {
  return new Promise((resolve, reject) => {
    fs.open('/dev/urandom', 'r', (err, fd) => {
      if (err) return reject(err);
      let b = new Buffer(20);
      fs.read(fd, b, 0, 20, 0, (err, byteRead, buffer) => {
        if (err) reject(err);
        resolve(buffer.toString('hex'));
      });
    });
  });
}

function hexdigest(str, algorithm = 'md5') {
  return createHash(algorithm).update(str).digest('hex');
}

// Return a Number representing the seconds elapsed since the UNIX epoch.
function numElapsed(t) {
  let ms = t && new Date(t).getTime();
  if (typeof ms != 'number' || isNaN(ms)) ms = Date.now();
  return parseInt(ms / 1000);
}

// Given an unix time in the past returns a string stating how much time
// has elapsed from the specified time, in the form "2 hours ago".
function strElapsed(t){
  let seconds = parseInt(numElapsed() - t);
  if (seconds <= 1) return 'now';

  let time_lengths = [[86400, "day"], [3600, "hour"], [60, "minute"], [1, "second"]];
  let [length, label] = time_lengths.filter((item) => {
    return seconds >= item[0];
  })[0];
  let units = parseInt(seconds / length);
  return `${units} ${label}${units > 1 ? 's' : ''} ago`;
}

// Combined Comparison / "Spaceship" Operator (<=>) in JavaScript
// See http://stackoverflow.com/questions/34852855/combined-comparison-spaceship-operator-in-javascript
function spaceship(a, b) {
  if ((a === null || b === null) || (typeof a != typeof b)) {
    return null;
  }
  if (typeof a === 'string') {
    return (a).localeCompare(b);
  } else {
    if (a > b) {
      return 1;
    } else if (a < b) {
      return -1;
    }
    return 0;
  }
}

module.exports = {
  checkParams: checkParams,
  getRand: getRand,
  hexdigest: hexdigest,
  numElapsed: numElapsed,
  strElapsed: strElapsed,
  spaceship: spaceship
}<|endoftext|>


<commit_before>const mongoose = require('mongoose')

/* var createConnectionString = function (conf) {
  // 'mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]';
  var str = 'mongodb://';

  if (conf.user && conf.password)
    str = str+conf.user + ':' + conf.password + '@';

  conf.servers.forEach(function (v, i, a) {
    var host = v[0],
        port = v[1];

    str = str+host+':'+port;

    if (i < conf.servers.length-1) {
      str=str+',';
    }
  });

  if (!conf.database || typeof conf.database !== "string" || conf.database.length === 0) {
    console.error('=> Database name should be nonempty string');
    process.exit(1);
  }

  str = str + '/' + conf.database;

  return str;
};

*/

module.exports = {
  mongooseFind: model => {
    return Promise( (resolve, reject) => {
      model.find(function (err, person) {
        if (err) {
          reject(err)
        }
        resolve(person)
      })
    } )

  }

  // createConnectionString: conf => {
  //   var login = ''

  //   if (conf.user && conf.password) {
  //     login = conf.user + ':' + conf.password + '@'
  //   }

  //   if (!Array.isArray(conf.servers) || conf.servers.length < 1) {
  //     console.error('=> Must provide at least one Database server');
  //     process.exit(1);
  //   }

  //   if (typeof conf.database !== "string" || conf.database.length === 0) {
  //     console.error('=> Database name should be nonempty string');
  //     process.exit(1);
  //   }

  //   const
  //     database = conf.database,
  //     hosts = conf.servers.reduce((str, server, index) => {

  //       if (index === 1) {
  //         str = `${str}`

  //         if (!server) {
  //           return
  //         }
  //       }

  //       return `${str},${}`
  //     }, '')

  //   return `mongodb://${login}${hosts}/${database}`;
  // }
}
<commit_msg>Another small bug fix<commit_after>const mongoose = require('mongoose')

/* var createConnectionString = function (conf) {
  // 'mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]';
  var str = 'mongodb://';

  if (conf.user && conf.password)
    str = str+conf.user + ':' + conf.password + '@';

  conf.servers.forEach(function (v, i, a) {
    var host = v[0],
        port = v[1];

    str = str+host+':'+port;

    if (i < conf.servers.length-1) {
      str=str+',';
    }
  });

  if (!conf.database || typeof conf.database !== "string" || conf.database.length === 0) {
    console.error('=> Database name should be nonempty string');
    process.exit(1);
  }

  str = str + '/' + conf.database;

  return str;
};

*/

module.exports = {
  mongooseFind: model => {
    return new Promise( (resolve, reject) => {
      model.find(function (err, person) {
        if (err) {
          reject(err)
        }
        resolve(person)
      })
    } )

  }

  // createConnectionString: conf => {
  //   var login = ''

  //   if (conf.user && conf.password) {
  //     login = conf.user + ':' + conf.password + '@'
  //   }

  //   if (!Array.isArray(conf.servers) || conf.servers.length < 1) {
  //     console.error('=> Must provide at least one Database server');
  //     process.exit(1);
  //   }

  //   if (typeof conf.database !== "string" || conf.database.length === 0) {
  //     console.error('=> Database name should be nonempty string');
  //     process.exit(1);
  //   }

  //   const
  //     database = conf.database,
  //     hosts = conf.servers.reduce((str, server, index) => {

  //       if (index === 1) {
  //         str = `${str}`

  //         if (!server) {
  //           return
  //         }
  //       }

  //       return `${str},${}`
  //     }, '')

  //   return `mongodb://${login}${hosts}/${database}`;
  // }
}
<|endoftext|>


<commit_before>/*
 TaskOnFly allows you easy manage your tasks and task lists on the fly from your mobile or desktop device.
 Copyright (C) 2014  Valerii Zinchenko

 This file is part of TaskOnFly.

 TaskOnFly is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 TaskOnFly is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with TaskOnFly.  If not, see <http://www.gnu.org/licenses/>.

 All source files are available at: http://github.com/valerii-zinchenko/TaskOnFly
*/

/**
 * @file It contains some [utility functions]{@link utils}.
 *
 * @author Valerii Zinchenko
 *
 * @version 1.0.3
 */

'use strict';

/**
 * Utility functions.
 * @type {{deepCopy: deepCopy, deepExtend: deepExtend, date: date}}
 */
var utils = {
    /**
     * Make deep object copy.
     * If some object property is already exist in target object - it will be replaced
     * by the property from source object.
     *
     * @param {Object} target - Target object.
     * @param {Object} source - Source object.
     * @returns {Object} Target object.
     */
    deepCopy: function(target, source) {
        var key,
            value;

        for (key in source) {
            value = source[key];
            switch (Object.prototype.toString.call(value)) {
                case '[object Object]':
                    if (!target[key]) {
                        target[key] = {};
                    }
                    utils.deepCopy(target[key], value);
                    break;
                default :
                    target[key] = value
            }
        }

        return target;
    },

    /**
     * Extend object deeply.
     * Extend the target object with missed properties from source object.
     *
     * @param {Object} target - Target object.
     * @param {Object} source - Source object.
     * @returns {Object} Target object.
     */
    deepExtend: function(target, source) {
        var key,
            value;

        for (key in source) {
            value = source[key];
            if (target.hasOwnProperty(key)) {
                if (typeof target[key] === 'object') {
                    utils.deepExtend(target[key], value);
                }
                continue;
            }

            switch (Object.prototype.toString.call(value)) {
                case '[object Object]':
                    if (!target[key]) {
                        target[key] = {};
                    }
                    utils.deepExtend(target[key], value);
                    break;
                case '[object Array]':
                    target[key] = value.map(function(el) { return el; });
                    break;
                default :
                    target[key] = value;
            }
        }

        return target;
    },

    /**
     * Convert Date object into plain date string.
     * Conversion steps:
     * 1. Call 'toISOString()' which converts the date into following format: 'YYYY-MM-DDTHH:MM:SS.MMMZ'
     * 2. Make time and timezone offset invariant: 'YYYY-MM-DD'
     *
     * @param {Date} [dateObj] - Date object. If omitted 'new Date()' will be converted.
     * @returns {string} 'YYYY-MM-DD'
     */
    date: function(dateObj) {
        if (dateObj && dateObj.constructor !== Date) {
            throw new Error('Incorrect input argument type');
        }
        return (dateObj ? dateObj : new Date()).toISOString().slice(0,10);
    }
};

if (typeof module !== 'undefined' && module.exports) {
    module.exports = utils;
}<commit_msg>fix date() utils function<commit_after>/*
 TaskOnFly allows you easy manage your tasks and task lists on the fly from your mobile or desktop device.
 Copyright (C) 2014  Valerii Zinchenko

 This file is part of TaskOnFly.

 TaskOnFly is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 TaskOnFly is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with TaskOnFly.  If not, see <http://www.gnu.org/licenses/>.

 All source files are available at: http://github.com/valerii-zinchenko/TaskOnFly
*/

/**
 * @file It contains some [utility functions]{@link utils}.
 *
 * @author Valerii Zinchenko
 *
 * @version 1.0.3
 */

'use strict';

/**
 * Utility functions.
 * @type {{deepCopy: deepCopy, deepExtend: deepExtend, date: date}}
 */
var utils = {
    /**
     * Make deep object copy.
     * If some object property is already exist in target object - it will be replaced
     * by the property from source object.
     *
     * @param {Object} target - Target object.
     * @param {Object} source - Source object.
     * @returns {Object} Target object.
     */
    deepCopy: function(target, source) {
        var key,
            value;

        for (key in source) {
            value = source[key];
            switch (Object.prototype.toString.call(value)) {
                case '[object Object]':
                    if (!target[key]) {
                        target[key] = {};
                    }
                    utils.deepCopy(target[key], value);
                    break;
                default :
                    target[key] = value
            }
        }

        return target;
    },

    /**
     * Extend object deeply.
     * Extend the target object with missed properties from source object.
     *
     * @param {Object} target - Target object.
     * @param {Object} source - Source object.
     * @returns {Object} Target object.
     */
    deepExtend: function(target, source) {
        var key,
            value;

        for (key in source) {
            value = source[key];
            if (target.hasOwnProperty(key)) {
                if (typeof target[key] === 'object') {
                    utils.deepExtend(target[key], value);
                }
                continue;
            }

            switch (Object.prototype.toString.call(value)) {
                case '[object Object]':
                    if (!target[key]) {
                        target[key] = {};
                    }
                    utils.deepExtend(target[key], value);
                    break;
                case '[object Array]':
                    target[key] = value.map(function(el) { return el; });
                    break;
                default :
                    target[key] = value;
            }
        }

        return target;
    },

    /**
     * Convert Date object into plain date string.
     * Conversion steps:
     * 1. Call 'toISOString()' which converts the date into following format: 'YYYY-MM-DDTHH:MM:SS.MMMZ'
     * 2. Make time and timezone offset invariant: 'YYYY-MM-DD'
     *
     * @param {Date} [dateObj] - Date object. If omitted 'new Date()' will be converted.
     * @returns {string} 'YYYY-MM-DD'
     */
    date: function(dateObj) {
        if (!dateObj) {
            dateObj = new Date();
        } else if (dateObj.constructor !== Date) {
            throw new Error('Incorrect input argument type');
        }

        // Remove timezone influence by calling tiISOString().
        dateObj.setTime(dateObj.getTime() - dateObj.getTimezoneOffset()*60*1000);

        return dateObj.toISOString().slice(0,10);
    }
};

if (typeof module !== 'undefined' && module.exports) {
    module.exports = utils;
}<|endoftext|>