付智勇

no message

要显示太多修改。

为保证性能只显示 38 of 38+ 个文件。

@@ -3,6 +3,8 @@ var saitMd5 = require('../util/saltMD5') @@ -3,6 +3,8 @@ var saitMd5 = require('../util/saltMD5')
3 var status = require('../util/resTemplate') 3 var status = require('../util/resTemplate')
4 const userService = require('../services/userService') 4 const userService = require('../services/userService')
5 const uuid = require('../util/UuidUtil') 5 const uuid = require('../util/UuidUtil')
  6 +const redis = require('../util/redis')
  7 +
6 8
7 9
8 var userController =function (){ 10 var userController =function (){
@@ -19,14 +21,30 @@ var userController =function (){ @@ -19,14 +21,30 @@ var userController =function (){
19 userController.prototype.addUser = async(ctx, next) =>{ 21 userController.prototype.addUser = async(ctx, next) =>{
20 try{ 22 try{
21 var params = ctx.request.body; 23 var params = ctx.request.body;
22 - const pw = saitMd5.md5AddSalt(params.password) 24 + var pw = saitMd5.md5AddSalt(params.password)
23 25
24 if(!params.loginName){ 26 if(!params.loginName){
25 return status.paramError('loginName'); 27 return status.paramError('loginName');
26 - }else if(!params.password){  
27 - return status.paramError('password','不能为空');  
28 - }else if(params.password.length < 6){  
29 - return status.paramError('password','不得小于6位'); 28 + }else if(!params.userEmail){
  29 + return status.paramError('userEmail');
  30 + }
  31 +
  32 + if(params.userRole == 2 || params.userRole == 3){
  33 + if(!params.IDcard){
  34 + return status.paramError('IDcard','不能为空');
  35 + }else if(!params.userName){
  36 + return status.paramError('userName','不能为空');
  37 + }
  38 + pw = saitMd5.md5AddSalt('123456')
  39 + params.loginName = Math.random().toString(24).substr(9);
  40 + }else if(params.userRole == 4){
  41 + if(!params.userMobile){
  42 + return status.paramError('userMobile','不能为空');
  43 + }else if(!params.password){
  44 + return status.paramError('password','不能为空');
  45 + }else if(params.password.length < 6){
  46 + return status.paramError('password','不得小于6位');
  47 + }
30 } 48 }
31 49
32 var user = { 50 var user = {
@@ -34,6 +52,7 @@ userController.prototype.addUser = async(ctx, next) =>{ @@ -34,6 +52,7 @@ userController.prototype.addUser = async(ctx, next) =>{
34 password:pw.md5Pass, 52 password:pw.md5Pass,
35 companyName:params.companyName, 53 companyName:params.companyName,
36 userName:params.userName, 54 userName:params.userName,
  55 + IDcard:params.IDcard,
37 salt:pw.salt, 56 salt:pw.salt,
38 userType:params.userType, 57 userType:params.userType,
39 userRole:params.userRole, 58 userRole:params.userRole,
@@ -54,16 +73,19 @@ userController.prototype.addUser = async(ctx, next) =>{ @@ -54,16 +73,19 @@ userController.prototype.addUser = async(ctx, next) =>{
54 * 用户登录 73 * 用户登录
55 */ 74 */
56 userController.prototype.login = async(ctx, next) =>{ 75 userController.prototype.login = async(ctx, next) =>{
  76 + try{
57 const body = ctx.request.body; 77 const body = ctx.request.body;
58 -  
59 - if(!body.name){  
60 - return status.paramError('name'); 78 + await redis.setToken('qwe123','qwe123');
  79 + let redisCode = await redis.getToken('qwe123')
  80 + console.log(redisCode)
  81 + if(redisCode != body.code ){
  82 + }else if(!body.loginName&&!body.userEmail){
  83 + return status.paramError('userEmail loginName');
61 }else if(!body.password){ 84 }else if(!body.password){
62 return status.paramError('password'); 85 return status.paramError('password');
63 } 86 }
64 87
65 - try{  
66 - let userData = await userService.login(body.name ,body.password); 88 + let userData = await userService.login(body.loginName ,body.password,body.userEmail);
67 var userBack = { 89 var userBack = {
68 id:userData.id, 90 id:userData.id,
69 loginName:userData.loginName, 91 loginName:userData.loginName,
@@ -97,6 +119,7 @@ userController.prototype.getStu = async(ctx, next) =>{ @@ -97,6 +119,7 @@ userController.prototype.getStu = async(ctx, next) =>{
97 throw new Error(error) 119 throw new Error(error)
98 } 120 }
99 } 121 }
  122 +
100 userController.prototype.updateUserByUserId = async(ctx, next) =>{ 123 userController.prototype.updateUserByUserId = async(ctx, next) =>{
101 const userId = ctx.params.userId; 124 const userId = ctx.params.userId;
102 const updateData = ctx.request.body; 125 const updateData = ctx.request.body;
@@ -147,4 +170,21 @@ userController.prototype.resetPasswordByUserId = async(ctx,next) =>{ @@ -147,4 +170,21 @@ userController.prototype.resetPasswordByUserId = async(ctx,next) =>{
147 } 170 }
148 } 171 }
149 172
  173 +userController.prototype.updatePwByTelphone = async(ctx, next) =>{
  174 + const code = ctx.request.body.code;
  175 + const telephone = ctx.request.body.telephone;
  176 + const password = ctx.request.body.password;
  177 + const pw = saitMd5.md5AddSalt(password);
  178 +
  179 + let setCode = await redis.setToken(code,code)
  180 + let redisCode = await redis.getToken(code);
  181 + if(code != redisCode){
  182 + return {code:500,msg:'验证码错误'};
  183 + }else if(!password){
  184 + return {code:501,msg:'密码不能为空'};
  185 + }
  186 + let updatePW = await userService.updatePwByTelphone(telephone,code,pw)
  187 + return {code:redisCode}
  188 +}
  189 +
150 module.exports = new userController(); 190 module.exports = new userController();
@@ -14,8 +14,10 @@ @@ -14,8 +14,10 @@
14 名称|类型|描述 14 名称|类型|描述
15 :--|:--|:-- 15 :--|:--|:--
16 |loginName |string |必须, 登录名 | 16 |loginName |string |必须, 登录名 |
17 -|password |string |必须,密码 |  
18 -|type | int |用户类型:0管理员 1监课 2老师 4学生 | 17 +|password |string |必须,密码 |
  18 +|userEmail |string |必须,邮箱 |
  19 +|userMobile |string |必须,用户手机号 |
  20 +
19 21
20 请求示例: 22 请求示例:
21 23
@@ -23,10 +25,15 @@ @@ -23,10 +25,15 @@
23 25
24 body 26 body
25 { 27 {
26 - "loginName": "admin",  
27 - "telephone": 11112112, 28 + "loginName": "尼安德特",
28 "password": "123456", 29 "password": "123456",
29 - "type":1 30 + "companyName":"bat",
  31 + "userName":"尼安德特人",
  32 + "userRole":0,
  33 + "userEmail":"kengni@buyouyu.com",
  34 + "userMobile":123124,
  35 + "content":"2017-09-01",
  36 + "groupId":1
30 } 37 }
31 返回参数: 38 返回参数:
32 39
不能预览此文件类型
  1 +var sequelize = require('../config');
  2 +var Sequelize = require('sequelize');
  3 +const uuid = require('../util/UuidUtil')
  4 +
  5 +var user = sequelize.define('3m_student_meeting', {
  6 + id: {
  7 + type: Sequelize.STRING(32),
  8 + defaultValue:uuid.db32(),
  9 + allowNull: false,
  10 + unique: true,
  11 + primaryKey: true,
  12 + field: "id"
  13 + },
  14 + studentId: {
  15 + allowNull: false,
  16 + type:Sequelize.STRING(32),
  17 + field: "student_id"
  18 + },
  19 + meetingId: {
  20 + allowNull: false,
  21 + type:Sequelize.STRING(32),
  22 + field: "meeting_id"
  23 + },
  24 +});
@@ -17,6 +17,11 @@ var user = sequelize.define('3m_user', { @@ -17,6 +17,11 @@ var user = sequelize.define('3m_user', {
17 type:Sequelize.STRING(100), 17 type:Sequelize.STRING(100),
18 field: "login_name" 18 field: "login_name"
19 }, 19 },
  20 + IDcard: {
  21 + allowNull: false,
  22 + type:Sequelize.STRING(50),
  23 + field: "IDcard"
  24 + },
20 password: { 25 password: {
21 allowNull: false, 26 allowNull: false,
22 type:Sequelize.STRING(100), 27 type:Sequelize.STRING(100),
  1 +node_modules/*
  2 +todo.txt
  3 +npm-debug.log
  4 +test/*
  5 +benchmark/*
  6 +browser/*
  7 +src/*
  8 +async
  9 +sync
  10 +mixed
  11 +bench.json
  12 +js/browser
  13 +js/browser/*
  14 +js/debug
  15 +js/debug/*
  16 +reader.js
  17 +read.txt
  18 +bench
  19 +.editorconfig
  20 +.jshintrc
  21 +ast_passes.js
  22 +mocharun.js
  23 +throwaway.js
  24 +throwaway.html
  25 +deque.sublime-workspace
  26 +deque.sublime-project
  27 +changelog.js
  28 +.travis.yml
  29 +sauce_connect.log
  30 +nodex64.exe
  31 +bump.js
  1 +"use strict";
  2 +Error.stackTraceLimit = 100;
  3 +var astPasses = require("./ast_passes.js");
  4 +
  5 +module.exports = function( grunt ) {
  6 + var isCI = !!grunt.option("ci");
  7 +
  8 + var license;
  9 + function getLicense() {
  10 + if( !license ) {
  11 + var fs = require("fs");
  12 + var text = fs.readFileSync("LICENSE", "utf8");
  13 + text = text.split("\n").map(function(line, index){
  14 + return " * " + line;
  15 + }).join("\n")
  16 + license = "/**\n" + text + "\n */\n";
  17 + }
  18 + return license
  19 + }
  20 +
  21 + function writeFile( dest, content ) {
  22 + grunt.file.write( dest, content );
  23 + grunt.log.writeln('File "' + dest + '" created.');
  24 + }
  25 +
  26 + var gruntConfig = {};
  27 +
  28 + var getGlobals = function() {
  29 + var fs = require("fs");
  30 + var file = "./src/constants.js";
  31 + var contents = fs.readFileSync(file, "utf8");
  32 + var rconstantname = /CONSTANT\(\s*([^,]+)/g;
  33 + var m;
  34 + var globals = {
  35 + "console": false,
  36 + "require": false,
  37 + "module": false,
  38 + "define": false
  39 + };
  40 + while( ( m = rconstantname.exec( contents ) ) ) {
  41 + globals[m[1]] = false;
  42 + }
  43 + return globals;
  44 + }
  45 +
  46 + gruntConfig.pkg = grunt.file.readJSON("package.json");
  47 +
  48 + gruntConfig.jshint = {
  49 + all: {
  50 + options: {
  51 + globals: getGlobals(),
  52 +
  53 + "bitwise": false,
  54 + "camelcase": true,
  55 + "curly": true,
  56 + "eqeqeq": true,
  57 + "es3": true,
  58 + "forin": true,
  59 + "immed": true,
  60 + "latedef": false,
  61 + "newcap": true,
  62 + "noarg": true,
  63 + "noempty": true,
  64 + "nonew": true,
  65 + "plusplus": false,
  66 + "quotmark": "double",
  67 + "undef": true,
  68 + "unused": true,
  69 + "strict": false,
  70 + "trailing": true,
  71 + "maxparams": 7,
  72 + "maxlen": 80,
  73 +
  74 + "asi": false,
  75 + "boss": true,
  76 + "eqnull": true,
  77 + "evil": true,
  78 + "expr": false,
  79 + "funcscope": false,
  80 + "globalstrict": false,
  81 + "lastsemic": false,
  82 + "laxcomma": false,
  83 + "laxbreak": false,
  84 + "loopfunc": true,
  85 + "multistr": true,
  86 + "proto": false,
  87 + "scripturl": true,
  88 + "smarttabs": false,
  89 + "shadow": true,
  90 + "sub": true,
  91 + "supernew": false,
  92 + "validthis": true,
  93 +
  94 + "browser": true,
  95 + "jquery": true,
  96 + "devel": true,
  97 +
  98 +
  99 + '-W014': true,
  100 + '-W116': true,
  101 + '-W106': true,
  102 + '-W064': true,
  103 + '-W097': true
  104 + },
  105 +
  106 + files: {
  107 + src: [
  108 + "./src/deque.js"
  109 + ]
  110 + }
  111 + }
  112 + };
  113 +
  114 + if( !isCI ) {
  115 + gruntConfig.jshint.all.options.reporter = require("jshint-stylish");
  116 + }
  117 +
  118 + gruntConfig.bump = {
  119 + options: {
  120 + files: ['package.json'],
  121 + updateConfigs: [],
  122 + commit: true,
  123 + commitMessage: 'Release v%VERSION%',
  124 + commitFiles: ['-a'],
  125 + createTag: true,
  126 + tagName: 'v%VERSION%',
  127 + tagMessage: 'Version %VERSION%',
  128 + false: true,
  129 + pushTo: 'master',
  130 + gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' // options to use with '$ git describe'
  131 + }
  132 + };
  133 +
  134 + grunt.initConfig(gruntConfig);
  135 + grunt.loadNpmTasks('grunt-contrib-jshint');
  136 + grunt.loadNpmTasks('grunt-bump');
  137 +
  138 +
  139 + grunt.registerTask( "build", function() {
  140 + var fs = require("fs");
  141 + var CONSTANTS_FILE = "./src/constants.js";
  142 +
  143 + astPasses.readConstants(fs.readFileSync(CONSTANTS_FILE, "utf8"), CONSTANTS_FILE);
  144 + var fileNames = ["deque.js"];
  145 + fileNames.forEach(function(fileName){
  146 + var src = fs.readFileSync("./src/" + fileName, "utf8");
  147 + src = astPasses.removeComments(src, fileName);
  148 + src = astPasses.expandConstants(src, fileName);
  149 + src = getLicense() + src;
  150 + writeFile("./js/" + fileName, src);
  151 + });
  152 + });
  153 +
  154 + grunt.registerTask( "testrun", function() {
  155 + var fs = require("fs");
  156 + var done = this.async();
  157 + var Mocha = require("mocha");
  158 +
  159 + var mochaOpts = {
  160 + reporter: "spec",
  161 + timeout: 500,
  162 + slow: Infinity
  163 + };
  164 +
  165 + var mocha = new Mocha(mochaOpts);
  166 +
  167 + fs.readdirSync("./test").forEach(function(fileName) {
  168 + mocha.addFile("./test/" + fileName);
  169 + });
  170 +
  171 + mocha.run(function(err){
  172 + if( err ) {
  173 + process.stderr.write(test.title + "\n" + err.stack + "\n");
  174 + done(err);
  175 + }
  176 + else {
  177 + done();
  178 + }
  179 + }).on( "fail", function( test, err ) {
  180 + process.stderr.write(test.title + "\n" + err.stack + "\n");
  181 + done(err);
  182 + });
  183 + });
  184 +
  185 + grunt.registerTask( "test", ["jshint", "build", "testrun"] );
  186 + grunt.registerTask( "default", ["jshint", "build"] );
  187 +
  188 +};
  1 +Copyright (c) 2013 Petka Antonov
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining a copy
  4 +of this software and associated documentation files (the "Software"), to deal
  5 +in the Software without restriction, including without limitation the rights
  6 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7 +copies of the Software, and to permit persons to whom the Software is
  8 +furnished to do so, subject to the following conditions:</p>
  9 +
  10 +The above copyright notice and this permission notice shall be included in
  11 +all copies or substantial portions of the Software.
  12 +
  13 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19 +THE SOFTWARE.
  1 +#Introduction
  2 +
  3 +Extremely fast [double-ended queue](http://en.wikipedia.org/wiki/Double-ended_queue) implementation. Double-ended queue can also be used as a:
  4 +
  5 +- [Stack](http://en.wikipedia.org/wiki/Stack_\(abstract_data_type\))
  6 +- [Queue](http://en.wikipedia.org/wiki/Queue_\(data_structure\))
  7 +
  8 +The implementation is GC and CPU cache friendly [circular buffer](http://en.wikipedia.org/wiki/Circular_buffer). [It will run circles around any "linked list" implementation](#performance).
  9 +
  10 +Every queue operation is done in constant `O(1)` - including random access from `.get()`.
  11 +
  12 +#Topics
  13 +
  14 +- [Quick start](#quick-start)
  15 +- [Why not use an Array?](#why-not-use-an-array)
  16 +- [Using double-ended queue as a normal queue](#using-double-ended-queue-as-a-normal-queue)
  17 +- [API reference and examples](#api)
  18 +- [Performance](#performance)
  19 +
  20 +#Quick start
  21 +
  22 + npm install double-ended-queue
  23 +
  24 +```js
  25 +var Deque = require("double-ended-queue");
  26 +
  27 +var deque = new Deque([1,2,3,4]);
  28 +deque.shift(); //1
  29 +deque.pop(); //4
  30 +```
  31 +
  32 +#Why not use an Array?
  33 +
  34 +Arrays take linear `O(N)` time to do `shift` and `unshift` operations. That means in theory that an array with 1000 items is 1000x slower to do those operations than a deque with 1000 items. 10000x slower with 10000 items and so on.
  35 +
  36 +V8 implements [a trick for small arrays](https://code.google.com/p/v8/issues/detail?id=3059) where these operations are done in constant time, however even with this trick deque is still 4x faster.
  37 +
  38 +But arrays use "native" methods, they must be faster!
  39 +
  40 +In V8, there is almost no advantage for a method to be a built-in. In fact many times built-ins are at a severe disadvantage of having to implement far more complex semantics than is actually needed in practice. For example, sparse array handling punishes almost every built-in array method even though nobody uses sparse arrays as is evidenced by the popularity of the underscore library which doesn't handle sparse arrays in the same way across different browsers.
  41 +
  42 +#Using double-ended queue as a normal queue
  43 +
  44 +Queue is a more commonly needed data structure however a separate implementation does not provide any advantage in terms of performance. Aliases are provided specifically for the queue use-case. You may use `.enqueue(items...)` to enqueue item(s) and `.dequeue()` to dequeue an item.
  45 +
  46 +#API
  47 +
  48 +- [`new Deque()`](#new-deque---deque)
  49 +- [`new Deque(Array items)`](#new-dequearray-items---deque)
  50 +- [`new Deque(int capacity)`](#new-dequeint-capacity---deque)
  51 +- [`push(dynamic items...)`](#pushdynamic-items---int)
  52 +- [`unshift(dynamic items...)`](#unshiftdynamic-items---int)
  53 +- [`pop()`](#pop---dynamic)
  54 +- [`shift()`](#shift---dynamic)
  55 +- [`toArray()`](#toarray---array)
  56 +- [`peekBack()`](#peekback---dynamic)
  57 +- [`peekFront()`](#peekfront---dynamic)
  58 +- [`get(int index)`](#getint-index---dynamic)
  59 +- [`isEmpty()`](#isempty---boolean)
  60 +- [`clear()`](#clear---void)
  61 +
  62 +#####`new Deque()` -> `Deque`
  63 +
  64 +Creates an empty double-ended queue with initial capacity of 16. If you know the optimal size before-hand, use [`new Deque(int capacity)`](#new-dequeint-capacity---deque).
  65 +
  66 +```js
  67 +var deque = new Deque();
  68 +deque.push(1, 2, 3);
  69 +deque.shift(); //1
  70 +deque.pop(); //3
  71 +```
  72 +
  73 +<hr>
  74 +
  75 +#####`new Deque(Array items)` -> `Deque`
  76 +
  77 +Creates a double-ended queue from `items`.
  78 +
  79 +```js
  80 +var deque = new Deque([1,2,3,4]);
  81 +deque.shift(); //1
  82 +deque.pop(); //4
  83 +```
  84 +
  85 +<hr>
  86 +
  87 +#####`new Deque(int capacity)` -> `Deque`
  88 +
  89 +Creates an empty double-ended queue with the given `capacity`. `Capacity` should be the maximum amount of items the queue will hold at a given time.
  90 +
  91 +The reason to give an initial capacity is to avoid potentially expensive resizing operations at runtime.
  92 +
  93 +```js
  94 +var deque = new Deque(100);
  95 +deque.push(1, 2, 3);
  96 +deque.shift(); //1
  97 +deque.pop(); //3
  98 +```
  99 +
  100 +<hr>
  101 +
  102 +#####`push(dynamic items...)` -> `int`
  103 +
  104 +Push items to the back of this queue. Returns the amount of items currently in the queue after the operation.
  105 +
  106 +```js
  107 +var deque = new Deque();
  108 +deque.push(1);
  109 +deque.pop(); //1
  110 +deque.push(1, 2, 3);
  111 +deque.shift(); //1
  112 +deque.shift(); //2
  113 +deque.shift(); //3
  114 +```
  115 +
  116 +**Aliases:** `enqueue`, `insertBack`
  117 +
  118 +<hr>
  119 +
  120 +#####`unshift(dynamic items...)` -> `int`
  121 +
  122 +Unshift items to the front of this queue. Returns the amount of items currently in the queue after the operation.
  123 +
  124 +```js
  125 +var deque = new Deque([2,3]);
  126 +deque.unshift(1);
  127 +deque.toString(); //"1,2,3"
  128 +deque.unshift(-2, -1, 0);
  129 +deque.toString(); //"-2,-1,0,1,2,3"
  130 +```
  131 +
  132 +**Aliases:** `insertFront`
  133 +
  134 +<hr>
  135 +
  136 +
  137 +#####`pop()` -> `dynamic`
  138 +
  139 +Pop off the item at the back of this queue.
  140 +
  141 +Note: The item will be removed from the queue. If you simply want to see what's at the back of the queue use [`peekBack()`](#peekback---dynamic) or [`.get(-1)`](#getint-index---dynamic).
  142 +
  143 +If the queue is empty, `undefined` is returned. If you need to differentiate between `undefined` values in the queue and `pop()` return value -
  144 +check the queue `.length` before popping.
  145 +
  146 +```js
  147 +var deque = new Deque([1,2,3]);
  148 +deque.pop(); //3
  149 +deque.pop(); //2
  150 +deque.pop(); //1
  151 +deque.pop(); //undefined
  152 +```
  153 +
  154 +**Aliases:** `removeBack`
  155 +
  156 +<hr>
  157 +
  158 +#####`shift()` -> `dynamic`
  159 +
  160 +Shifts off the item at the front of this queue.
  161 +
  162 +Note: The item will be removed from the queue. If you simply want to see what's at the front of the queue use [`peekFront()`](#peekfront---dynamic) or [`.get(0)`](#getint-index---dynamic).
  163 +
  164 +If the queue is empty, `undefined` is returned. If you need to differentiate between `undefined` values in the queue and `shift()` return value -
  165 +check the queue `.length` before shifting.
  166 +
  167 +```js
  168 +var deque = new Deque([1,2,3]);
  169 +deque.shift(); //1
  170 +deque.shift(); //2
  171 +deque.shift(); //3
  172 +deque.shift(); //undefined
  173 +```
  174 +
  175 +**Aliases:** `removeFront`, `dequeue`
  176 +
  177 +<hr>
  178 +
  179 +#####`toArray()` -> `Array`
  180 +
  181 +Returns the items in the queue as an array. Starting from the item in the front of the queue and ending to the item at the back of the queue.
  182 +
  183 +```js
  184 +var deque = new Deque([1,2,3]);
  185 +deque.push(4);
  186 +deque.unshift(0);
  187 +deque.toArray(); //[0,1,2,3,4]
  188 +```
  189 +
  190 +**Aliases:** `toJSON`
  191 +
  192 +<hr>
  193 +
  194 +#####`peekBack()` -> `dynamic`
  195 +
  196 +Returns the item that is at the back of this queue without removing it.
  197 +
  198 +If the queue is empty, `undefined` is returned.
  199 +
  200 +```js
  201 +var deque = new Deque([1,2,3]);
  202 +deque.push(4);
  203 +deque.peekBack(); //4
  204 +```
  205 +
  206 +<hr>
  207 +
  208 +#####`peekFront()` -> `dynamic`
  209 +
  210 +Returns the item that is at the front of this queue without removing it.
  211 +
  212 +If the queue is empty, `undefined` is returned.
  213 +
  214 +```js
  215 +var deque = new Deque([1,2,3]);
  216 +deque.push(4);
  217 +deque.peekFront(); //1
  218 +```
  219 +
  220 +<hr>
  221 +
  222 +#####`get(int index)` -> `dynamic`
  223 +
  224 +Returns the item that is at the given `index` of this queue without removing it.
  225 +
  226 +The index is zero-based, so `.get(0)` will return the item that is at the front, `.get(1)` will return
  227 +the item that comes after and so on.
  228 +
  229 +The index can be negative to read items at the back of the queue. `.get(-1)` returns the item that is at the back of the queue,
  230 +`.get(-2)` will return the item that comes before and so on.
  231 +
  232 +Returns `undefined` if `index` is not a valid index into the queue.
  233 +
  234 +```js
  235 +var deque = new Deque([1,2,3]);
  236 +deque.get(0); //1
  237 +deque.get(1); //2
  238 +deque.get(2); //3
  239 +
  240 +deque.get(-1); //3
  241 +deque.get(-2); //2
  242 +deque.get(-3); //1
  243 +```
  244 +
  245 +**Note**: Even though indexed accessor (e.g. `queue[0]`) could *appear* to return a correct value *sometimes*, this is completely unreliable. The numeric slots
  246 +of the deque object are internally used as an optimization and have no meaningful order or meaning to outside. Always use `.get()`.
  247 +
  248 +**Note**: The implementation has O(1) random access using `.get()`.
  249 +
  250 +<hr>
  251 +
  252 +#####`isEmpty()` -> `boolean`
  253 +
  254 +Return `true` if this queue is empty, `false` otherwise.
  255 +
  256 +```js
  257 +var deque = new Deque();
  258 +deque.isEmpty(); //true
  259 +deque.push(1);
  260 +deque.isEmpty(); //false
  261 +```
  262 +
  263 +<hr>
  264 +
  265 +#####`clear()` -> `void`
  266 +
  267 +Remove all items from this queue. Does not change the queue's capacity.
  268 +
  269 +```js
  270 +var deque = new Deque([1,2,3]);
  271 +deque.toString(); //"1,2,3"
  272 +deque.clear();
  273 +deque.toString(); //""
  274 +```
  275 +<hr>
  276 +
  277 +#Performance
  278 +
  279 +Clone the repo and `npm install`. Then run the `bench` script.
  280 +
  281 +##1000 items in the queue
  282 +
  283 + double-ended-queue x 15,532,714 ops/sec ±0.19% (96 runs sampled)
  284 + built-in array x 6,501,398 ops/sec ±0.87% (95 runs sampled)
  285 + node-deque x 2,938,068 ops/sec ±3.50% (68 runs sampled)
  286 +
  287 +##2 million items in the queue
  288 +
  289 + double-ended-queue x 14,425,547 ops/sec ±0.17% (94 runs sampled)
  290 + node-deque x 2,815,628 ops/sec ±10.56% (76 runs sampled)
  291 + built-in array x 19.23 ops/sec ±0.35% (51 runs sampled)
  292 +
  293 +Noteworthy is just how bad the degradation can be for built-in array when V8 cannot use the trick.
  1 +/**
  2 + * Copyright (c) 2013 Petka Antonov
  3 + *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  5 + * of this software and associated documentation files (the "Software"), to deal
  6 + * in the Software without restriction, including without limitation the rights
  7 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8 + * copies of the Software, and to permit persons to whom the Software is
  9 + * furnished to do so, subject to the following conditions:</p>
  10 + *
  11 + * The above copyright notice and this permission notice shall be included in
  12 + * all copies or substantial portions of the Software.
  13 + *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20 + * THE SOFTWARE.
  21 + */
  22 +"use strict";
  23 +function Deque(capacity) {
  24 + this._capacity = getCapacity(capacity);
  25 + this._length = 0;
  26 + this._front = 0;
  27 + if (isArray(capacity)) {
  28 + var len = capacity.length;
  29 + for (var i = 0; i < len; ++i) {
  30 + this[i] = capacity[i];
  31 + }
  32 + this._length = len;
  33 + }
  34 +}
  35 +
  36 +Deque.prototype.toArray = function Deque$toArray() {
  37 + var len = this._length;
  38 + var ret = new Array(len);
  39 + var front = this._front;
  40 + var capacity = this._capacity;
  41 + for (var j = 0; j < len; ++j) {
  42 + ret[j] = this[(front + j) & (capacity - 1)];
  43 + }
  44 + return ret;
  45 +};
  46 +
  47 +Deque.prototype.push = function Deque$push(item) {
  48 + var argsLength = arguments.length;
  49 + var length = this._length;
  50 + if (argsLength > 1) {
  51 + var capacity = this._capacity;
  52 + if (length + argsLength > capacity) {
  53 + for (var i = 0; i < argsLength; ++i) {
  54 + this._checkCapacity(length + 1);
  55 + var j = (this._front + length) & (this._capacity - 1);
  56 + this[j] = arguments[i];
  57 + length++;
  58 + this._length = length;
  59 + }
  60 + return length;
  61 + }
  62 + else {
  63 + var j = this._front;
  64 + for (var i = 0; i < argsLength; ++i) {
  65 + this[(j + length) & (capacity - 1)] = arguments[i];
  66 + j++;
  67 + }
  68 + this._length = length + argsLength;
  69 + return length + argsLength;
  70 + }
  71 +
  72 + }
  73 +
  74 + if (argsLength === 0) return length;
  75 +
  76 + this._checkCapacity(length + 1);
  77 + var i = (this._front + length) & (this._capacity - 1);
  78 + this[i] = item;
  79 + this._length = length + 1;
  80 + return length + 1;
  81 +};
  82 +
  83 +Deque.prototype.pop = function Deque$pop() {
  84 + var length = this._length;
  85 + if (length === 0) {
  86 + return void 0;
  87 + }
  88 + var i = (this._front + length - 1) & (this._capacity - 1);
  89 + var ret = this[i];
  90 + this[i] = void 0;
  91 + this._length = length - 1;
  92 + return ret;
  93 +};
  94 +
  95 +Deque.prototype.shift = function Deque$shift() {
  96 + var length = this._length;
  97 + if (length === 0) {
  98 + return void 0;
  99 + }
  100 + var front = this._front;
  101 + var ret = this[front];
  102 + this[front] = void 0;
  103 + this._front = (front + 1) & (this._capacity - 1);
  104 + this._length = length - 1;
  105 + return ret;
  106 +};
  107 +
  108 +Deque.prototype.unshift = function Deque$unshift(item) {
  109 + var length = this._length;
  110 + var argsLength = arguments.length;
  111 +
  112 +
  113 + if (argsLength > 1) {
  114 + var capacity = this._capacity;
  115 + if (length + argsLength > capacity) {
  116 + for (var i = argsLength - 1; i >= 0; i--) {
  117 + this._checkCapacity(length + 1);
  118 + var capacity = this._capacity;
  119 + var j = (((( this._front - 1 ) &
  120 + ( capacity - 1) ) ^ capacity ) - capacity );
  121 + this[j] = arguments[i];
  122 + length++;
  123 + this._length = length;
  124 + this._front = j;
  125 + }
  126 + return length;
  127 + }
  128 + else {
  129 + var front = this._front;
  130 + for (var i = argsLength - 1; i >= 0; i--) {
  131 + var j = (((( front - 1 ) &
  132 + ( capacity - 1) ) ^ capacity ) - capacity );
  133 + this[j] = arguments[i];
  134 + front = j;
  135 + }
  136 + this._front = front;
  137 + this._length = length + argsLength;
  138 + return length + argsLength;
  139 + }
  140 + }
  141 +
  142 + if (argsLength === 0) return length;
  143 +
  144 + this._checkCapacity(length + 1);
  145 + var capacity = this._capacity;
  146 + var i = (((( this._front - 1 ) &
  147 + ( capacity - 1) ) ^ capacity ) - capacity );
  148 + this[i] = item;
  149 + this._length = length + 1;
  150 + this._front = i;
  151 + return length + 1;
  152 +};
  153 +
  154 +Deque.prototype.peekBack = function Deque$peekBack() {
  155 + var length = this._length;
  156 + if (length === 0) {
  157 + return void 0;
  158 + }
  159 + var index = (this._front + length - 1) & (this._capacity - 1);
  160 + return this[index];
  161 +};
  162 +
  163 +Deque.prototype.peekFront = function Deque$peekFront() {
  164 + if (this._length === 0) {
  165 + return void 0;
  166 + }
  167 + return this[this._front];
  168 +};
  169 +
  170 +Deque.prototype.get = function Deque$get(index) {
  171 + var i = index;
  172 + if ((i !== (i | 0))) {
  173 + return void 0;
  174 + }
  175 + var len = this._length;
  176 + if (i < 0) {
  177 + i = i + len;
  178 + }
  179 + if (i < 0 || i >= len) {
  180 + return void 0;
  181 + }
  182 + return this[(this._front + i) & (this._capacity - 1)];
  183 +};
  184 +
  185 +Deque.prototype.isEmpty = function Deque$isEmpty() {
  186 + return this._length === 0;
  187 +};
  188 +
  189 +Deque.prototype.clear = function Deque$clear() {
  190 + var len = this._length;
  191 + var front = this._front;
  192 + var capacity = this._capacity;
  193 + for (var j = 0; j < len; ++j) {
  194 + this[(front + j) & (capacity - 1)] = void 0;
  195 + }
  196 + this._length = 0;
  197 + this._front = 0;
  198 +};
  199 +
  200 +Deque.prototype.toString = function Deque$toString() {
  201 + return this.toArray().toString();
  202 +};
  203 +
  204 +Deque.prototype.valueOf = Deque.prototype.toString;
  205 +Deque.prototype.removeFront = Deque.prototype.shift;
  206 +Deque.prototype.removeBack = Deque.prototype.pop;
  207 +Deque.prototype.insertFront = Deque.prototype.unshift;
  208 +Deque.prototype.insertBack = Deque.prototype.push;
  209 +Deque.prototype.enqueue = Deque.prototype.push;
  210 +Deque.prototype.dequeue = Deque.prototype.shift;
  211 +Deque.prototype.toJSON = Deque.prototype.toArray;
  212 +
  213 +Object.defineProperty(Deque.prototype, "length", {
  214 + get: function() {
  215 + return this._length;
  216 + },
  217 + set: function() {
  218 + throw new RangeError("");
  219 + }
  220 +});
  221 +
  222 +Deque.prototype._checkCapacity = function Deque$_checkCapacity(size) {
  223 + if (this._capacity < size) {
  224 + this._resizeTo(getCapacity(this._capacity * 1.5 + 16));
  225 + }
  226 +};
  227 +
  228 +Deque.prototype._resizeTo = function Deque$_resizeTo(capacity) {
  229 + var oldCapacity = this._capacity;
  230 + this._capacity = capacity;
  231 + var front = this._front;
  232 + var length = this._length;
  233 + if (front + length > oldCapacity) {
  234 + var moveItemsCount = (front + length) & (oldCapacity - 1);
  235 + arrayMove(this, 0, this, oldCapacity, moveItemsCount);
  236 + }
  237 +};
  238 +
  239 +
  240 +var isArray = Array.isArray;
  241 +
  242 +function arrayMove(src, srcIndex, dst, dstIndex, len) {
  243 + for (var j = 0; j < len; ++j) {
  244 + dst[j + dstIndex] = src[j + srcIndex];
  245 + src[j + srcIndex] = void 0;
  246 + }
  247 +}
  248 +
  249 +function pow2AtLeast(n) {
  250 + n = n >>> 0;
  251 + n = n - 1;
  252 + n = n | (n >> 1);
  253 + n = n | (n >> 2);
  254 + n = n | (n >> 4);
  255 + n = n | (n >> 8);
  256 + n = n | (n >> 16);
  257 + return n + 1;
  258 +}
  259 +
  260 +function getCapacity(capacity) {
  261 + if (typeof capacity !== "number") {
  262 + if (isArray(capacity)) {
  263 + capacity = capacity.length;
  264 + }
  265 + else {
  266 + return 16;
  267 + }
  268 + }
  269 + return pow2AtLeast(
  270 + Math.min(
  271 + Math.max(16, capacity), 1073741824)
  272 + );
  273 +}
  274 +
  275 +module.exports = Deque;
  1 +{
  2 + "_args": [
  3 + [
  4 + {
  5 + "raw": "double-ended-queue@^2.1.0-0",
  6 + "scope": null,
  7 + "escapedName": "double-ended-queue",
  8 + "name": "double-ended-queue",
  9 + "rawSpec": "^2.1.0-0",
  10 + "spec": ">=2.1.0-0 <3.0.0",
  11 + "type": "range"
  12 + },
  13 + "/Users/fzy/project/koa2_Sequelize_project/node_modules/redis"
  14 + ]
  15 + ],
  16 + "_from": "double-ended-queue@>=2.1.0-0 <3.0.0",
  17 + "_id": "double-ended-queue@2.1.0-0",
  18 + "_inCache": true,
  19 + "_location": "/double-ended-queue",
  20 + "_nodeVersion": "0.10.34",
  21 + "_npmUser": {
  22 + "name": "esailija",
  23 + "email": "petka_antonov@hotmail.com"
  24 + },
  25 + "_npmVersion": "2.1.12",
  26 + "_phantomChildren": {},
  27 + "_requested": {
  28 + "raw": "double-ended-queue@^2.1.0-0",
  29 + "scope": null,
  30 + "escapedName": "double-ended-queue",
  31 + "name": "double-ended-queue",
  32 + "rawSpec": "^2.1.0-0",
  33 + "spec": ">=2.1.0-0 <3.0.0",
  34 + "type": "range"
  35 + },
  36 + "_requiredBy": [
  37 + "/redis"
  38 + ],
  39 + "_resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
  40 + "_shasum": "103d3527fd31528f40188130c841efdd78264e5c",
  41 + "_shrinkwrap": null,
  42 + "_spec": "double-ended-queue@^2.1.0-0",
  43 + "_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/redis",
  44 + "author": {
  45 + "name": "Petka Antonov",
  46 + "email": "petka_antonov@hotmail.com",
  47 + "url": "http://github.com/petkaantonov/"
  48 + },
  49 + "bugs": {
  50 + "url": "http://github.com/petkaantonov/deque/issues"
  51 + },
  52 + "dependencies": {},
  53 + "description": "Extremely fast double-ended queue implementation",
  54 + "devDependencies": {
  55 + "acorn": "~0.3.1",
  56 + "benchmark": "~1.0.0",
  57 + "bluebird": "~0.11",
  58 + "deque": "0.0.4",
  59 + "grunt": "~0.4.1",
  60 + "grunt-cli": "~0.1.9",
  61 + "grunt-contrib-jshint": "~0.6.4",
  62 + "jshint-stylish": "latest",
  63 + "mocha": "~1.12.1",
  64 + "q": "~0.9.7",
  65 + "semver-utils": "~1.1.0"
  66 + },
  67 + "directories": {},
  68 + "dist": {
  69 + "shasum": "103d3527fd31528f40188130c841efdd78264e5c",
  70 + "tarball": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz"
  71 + },
  72 + "gitHead": "51eada75cea686f1eb0c8bb5be486ac630e9b7ee",
  73 + "homepage": "https://github.com/petkaantonov/deque",
  74 + "keywords": [
  75 + "data-structure",
  76 + "data-structures",
  77 + "queue",
  78 + "deque",
  79 + "double-ended-queue"
  80 + ],
  81 + "license": "MIT",
  82 + "main": "./js/deque.js",
  83 + "maintainers": [
  84 + {
  85 + "name": "esailija",
  86 + "email": "petka_antonov@hotmail.com"
  87 + }
  88 + ],
  89 + "name": "double-ended-queue",
  90 + "optionalDependencies": {},
  91 + "readme": "#Introduction\n\nExtremely fast [double-ended queue](http://en.wikipedia.org/wiki/Double-ended_queue) implementation. Double-ended queue can also be used as a:\n\n- [Stack](http://en.wikipedia.org/wiki/Stack_\\(abstract_data_type\\))\n- [Queue](http://en.wikipedia.org/wiki/Queue_\\(data_structure\\))\n\nThe implementation is GC and CPU cache friendly [circular buffer](http://en.wikipedia.org/wiki/Circular_buffer). [It will run circles around any \"linked list\" implementation](#performance).\n\nEvery queue operation is done in constant `O(1)` - including random access from `.get()`.\n\n#Topics\n\n- [Quick start](#quick-start)\n- [Why not use an Array?](#why-not-use-an-array)\n- [Using double-ended queue as a normal queue](#using-double-ended-queue-as-a-normal-queue)\n- [API reference and examples](#api)\n- [Performance](#performance)\n\n#Quick start\n\n npm install double-ended-queue\n\n```js\nvar Deque = require(\"double-ended-queue\");\n\nvar deque = new Deque([1,2,3,4]);\ndeque.shift(); //1\ndeque.pop(); //4\n```\n\n#Why not use an Array?\n\nArrays take linear `O(N)` time to do `shift` and `unshift` operations. That means in theory that an array with 1000 items is 1000x slower to do those operations than a deque with 1000 items. 10000x slower with 10000 items and so on.\n\nV8 implements [a trick for small arrays](https://code.google.com/p/v8/issues/detail?id=3059) where these operations are done in constant time, however even with this trick deque is still 4x faster.\n\nBut arrays use \"native\" methods, they must be faster!\n\nIn V8, there is almost no advantage for a method to be a built-in. In fact many times built-ins are at a severe disadvantage of having to implement far more complex semantics than is actually needed in practice. For example, sparse array handling punishes almost every built-in array method even though nobody uses sparse arrays as is evidenced by the popularity of the underscore library which doesn't handle sparse arrays in the same way across different browsers.\n\n#Using double-ended queue as a normal queue\n\nQueue is a more commonly needed data structure however a separate implementation does not provide any advantage in terms of performance. Aliases are provided specifically for the queue use-case. You may use `.enqueue(items...)` to enqueue item(s) and `.dequeue()` to dequeue an item.\n\n#API\n\n- [`new Deque()`](#new-deque---deque)\n- [`new Deque(Array items)`](#new-dequearray-items---deque)\n- [`new Deque(int capacity)`](#new-dequeint-capacity---deque)\n- [`push(dynamic items...)`](#pushdynamic-items---int)\n- [`unshift(dynamic items...)`](#unshiftdynamic-items---int)\n- [`pop()`](#pop---dynamic)\n- [`shift()`](#shift---dynamic)\n- [`toArray()`](#toarray---array)\n- [`peekBack()`](#peekback---dynamic)\n- [`peekFront()`](#peekfront---dynamic)\n- [`get(int index)`](#getint-index---dynamic)\n- [`isEmpty()`](#isempty---boolean)\n- [`clear()`](#clear---void)\n\n#####`new Deque()` -> `Deque`\n\nCreates an empty double-ended queue with initial capacity of 16. If you know the optimal size before-hand, use [`new Deque(int capacity)`](#new-dequeint-capacity---deque).\n\n```js\nvar deque = new Deque();\ndeque.push(1, 2, 3);\ndeque.shift(); //1\ndeque.pop(); //3\n```\n\n<hr>\n\n#####`new Deque(Array items)` -> `Deque`\n\nCreates a double-ended queue from `items`.\n\n```js\nvar deque = new Deque([1,2,3,4]);\ndeque.shift(); //1\ndeque.pop(); //4\n```\n\n<hr>\n\n#####`new Deque(int capacity)` -> `Deque`\n\nCreates an empty double-ended queue with the given `capacity`. `Capacity` should be the maximum amount of items the queue will hold at a given time.\n\nThe reason to give an initial capacity is to avoid potentially expensive resizing operations at runtime.\n\n```js\nvar deque = new Deque(100);\ndeque.push(1, 2, 3);\ndeque.shift(); //1\ndeque.pop(); //3\n```\n\n<hr>\n\n#####`push(dynamic items...)` -> `int`\n\nPush items to the back of this queue. Returns the amount of items currently in the queue after the operation.\n\n```js\nvar deque = new Deque();\ndeque.push(1);\ndeque.pop(); //1\ndeque.push(1, 2, 3);\ndeque.shift(); //1\ndeque.shift(); //2\ndeque.shift(); //3\n```\n\n**Aliases:** `enqueue`, `insertBack`\n\n<hr>\n\n#####`unshift(dynamic items...)` -> `int`\n\nUnshift items to the front of this queue. Returns the amount of items currently in the queue after the operation.\n\n```js\nvar deque = new Deque([2,3]);\ndeque.unshift(1);\ndeque.toString(); //\"1,2,3\"\ndeque.unshift(-2, -1, 0);\ndeque.toString(); //\"-2,-1,0,1,2,3\"\n```\n\n**Aliases:** `insertFront`\n\n<hr>\n\n\n#####`pop()` -> `dynamic`\n\nPop off the item at the back of this queue.\n\nNote: The item will be removed from the queue. If you simply want to see what's at the back of the queue use [`peekBack()`](#peekback---dynamic) or [`.get(-1)`](#getint-index---dynamic).\n\nIf the queue is empty, `undefined` is returned. If you need to differentiate between `undefined` values in the queue and `pop()` return value -\ncheck the queue `.length` before popping.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.pop(); //3\ndeque.pop(); //2\ndeque.pop(); //1\ndeque.pop(); //undefined\n```\n\n**Aliases:** `removeBack`\n\n<hr>\n\n#####`shift()` -> `dynamic`\n\nShifts off the item at the front of this queue.\n\nNote: The item will be removed from the queue. If you simply want to see what's at the front of the queue use [`peekFront()`](#peekfront---dynamic) or [`.get(0)`](#getint-index---dynamic).\n\nIf the queue is empty, `undefined` is returned. If you need to differentiate between `undefined` values in the queue and `shift()` return value -\ncheck the queue `.length` before shifting.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.shift(); //1\ndeque.shift(); //2\ndeque.shift(); //3\ndeque.shift(); //undefined\n```\n\n**Aliases:** `removeFront`, `dequeue`\n\n<hr>\n\n#####`toArray()` -> `Array`\n\nReturns the items in the queue as an array. Starting from the item in the front of the queue and ending to the item at the back of the queue.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.push(4);\ndeque.unshift(0);\ndeque.toArray(); //[0,1,2,3,4]\n```\n\n**Aliases:** `toJSON`\n\n<hr>\n\n#####`peekBack()` -> `dynamic`\n\nReturns the item that is at the back of this queue without removing it.\n\nIf the queue is empty, `undefined` is returned.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.push(4);\ndeque.peekBack(); //4\n```\n\n<hr>\n\n#####`peekFront()` -> `dynamic`\n\nReturns the item that is at the front of this queue without removing it.\n\nIf the queue is empty, `undefined` is returned.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.push(4);\ndeque.peekFront(); //1\n```\n\n<hr>\n\n#####`get(int index)` -> `dynamic`\n\nReturns the item that is at the given `index` of this queue without removing it.\n\nThe index is zero-based, so `.get(0)` will return the item that is at the front, `.get(1)` will return\nthe item that comes after and so on.\n\nThe index can be negative to read items at the back of the queue. `.get(-1)` returns the item that is at the back of the queue,\n`.get(-2)` will return the item that comes before and so on.\n\nReturns `undefined` if `index` is not a valid index into the queue.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.get(0); //1\ndeque.get(1); //2\ndeque.get(2); //3\n\ndeque.get(-1); //3\ndeque.get(-2); //2\ndeque.get(-3); //1\n```\n\n**Note**: Even though indexed accessor (e.g. `queue[0]`) could *appear* to return a correct value *sometimes*, this is completely unreliable. The numeric slots\nof the deque object are internally used as an optimization and have no meaningful order or meaning to outside. Always use `.get()`.\n\n**Note**: The implementation has O(1) random access using `.get()`.\n\n<hr>\n\n#####`isEmpty()` -> `boolean`\n\nReturn `true` if this queue is empty, `false` otherwise.\n\n```js\nvar deque = new Deque();\ndeque.isEmpty(); //true\ndeque.push(1);\ndeque.isEmpty(); //false\n```\n\n<hr>\n\n#####`clear()` -> `void`\n\nRemove all items from this queue. Does not change the queue's capacity.\n\n```js\nvar deque = new Deque([1,2,3]);\ndeque.toString(); //\"1,2,3\"\ndeque.clear();\ndeque.toString(); //\"\"\n```\n<hr>\n\n#Performance\n\nClone the repo and `npm install`. Then run the `bench` script.\n\n##1000 items in the queue\n\n double-ended-queue x 15,532,714 ops/sec ±0.19% (96 runs sampled)\n built-in array x 6,501,398 ops/sec ±0.87% (95 runs sampled)\n node-deque x 2,938,068 ops/sec ±3.50% (68 runs sampled)\n\n##2 million items in the queue\n\n double-ended-queue x 14,425,547 ops/sec ±0.17% (94 runs sampled)\n node-deque x 2,815,628 ops/sec ±10.56% (76 runs sampled)\n built-in array x 19.23 ops/sec ±0.35% (51 runs sampled)\n\nNoteworthy is just how bad the degradation can be for built-in array when V8 cannot use the trick.\n",
  92 + "readmeFilename": "README.md",
  93 + "repository": {
  94 + "type": "git",
  95 + "url": "git://github.com/petkaantonov/deque.git"
  96 + },
  97 + "scripts": {
  98 + "test": "grunt test"
  99 + },
  100 + "version": "2.1.0-0"
  101 +}
  1 +# Logs
  2 +logs
  3 +*.log
  4 +
  5 +# Runtime data
  6 +pids
  7 +*.pid
  8 +*.seed
  9 +*.rdb
  10 +
  11 +# Directory for instrumented libs generated by jscoverage/JSCover
  12 +lib-cov
  13 +
  14 +# Coverage directory used by tools like istanbul
  15 +coverage
  16 +
  17 +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
  18 +.grunt
  19 +
  20 +# Compiled binary addons (http://nodejs.org/api/addons.html)
  21 +build/Release
  22 +
  23 +# Dependency directory
  24 +# Commenting this out is preferred by some people, see
  25 +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
  26 +node_modules
  27 +
  28 +# Users Environment Variables
  29 +.lock-wscript
  1 +language: node_js
  2 +sudo: false
  3 +node_js:
  4 + - "0.10"
  5 + - "0.12"
  6 + - "4"
  7 + - "5"
  8 +after_success:
  9 + - CODECLIMATE_REPO_TOKEN=b57723fafcf0516f275d6b380cd506fd082ea88d86507eb82c8abd489b9b9a09 node ./node_modules/.bin/codeclimate-test-reporter < coverage/lcov.info
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) 2015 NodeRedis
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  22 +
  1 +# Redis Commands
  2 +
  3 +[![Build Status](https://travis-ci.org/NodeRedis/redis-commands.png?branch=master)](https://travis-ci.org/NodeRedis/redis-commands)
  4 +[![Code Climate](https://codeclimate.com/github/NodeRedis/redis-commands/badges/gpa.svg)](https://codeclimate.com/github/NodeRedis/redis-commands)
  5 +[![Test Coverage](https://codeclimate.com/github/NodeRedis/redis-commands/badges/coverage.svg)](https://codeclimate.com/github/NodeRedis/redis-commands/coverage)
  6 +
  7 +This module exports all the commands that Redis supports.
  8 +
  9 +## Install
  10 +
  11 +```shell
  12 +$ npm install redis-commands
  13 +```
  14 +
  15 +## Usage
  16 +
  17 +```javascript
  18 +var commands = require('redis-commands');
  19 +```
  20 +
  21 +`.list` is an array contains all the lowercased commands:
  22 +
  23 +```javascript
  24 +commands.list.forEach(function (command) {
  25 + console.log(command);
  26 +});
  27 +```
  28 +
  29 +`.exists()` is used to check if the command exists:
  30 +
  31 +```javascript
  32 +commands.exists('set') // true
  33 +commands.exists('other-command') // false
  34 +```
  35 +
  36 +`.hasFlag()` is used to check if the command has the flag:
  37 +
  38 +```javascript
  39 +commands.hasFlag('set', 'readonly') // false
  40 +```
  41 +
  42 +`.getKeyIndexes()` is used to get the indexes of keys in the command arguments:
  43 +
  44 +```javascript
  45 +commands.getKeyIndexes('set', ['key', 'value']) // [0]
  46 +commands.getKeyIndexes('mget', ['key1', 'key2']) // [0, 1]
  47 +```
  48 +
  49 +## Acknowledgment
  50 +
  51 +Thank [@Yuan Chuan](https://github.com/yuanchuan) for the package name. The original redis-commands is renamed to [@yuanchuan/redis-commands](https://www.npmjs.com/package/@yuanchuan/redis-commands).
  1 +## v.1.3.1 - 25 Jan, 2017
  2 +
  3 +Bugfix
  4 +
  5 +- Fix require for for webpack
  6 +
  7 +## v.1.3.0 - 20 Oct, 2016
  8 +
  9 +Features
  10 +
  11 +- Rebuild the commands with the newest Redis unstable release
  12 +
  13 +## v.1.2.0 - 21 Apr, 2016
  14 +
  15 +Features
  16 +
  17 +- Added support for `MIGRATE [...] KEYS key1, key2` (Redis >= v.3.0.6)
  18 +- Added build sanity check for unhandled commands with moveable keys
  19 +- Rebuild the commands with the newest unstable release
  20 +- Improved performance of .getKeyIndexes()
  21 +
  22 +Bugfix
  23 +
  24 +- Fixed command command returning the wrong arity due to a Redis bug
  25 +- Fixed brpop command returning the wrong keystop due to a Redis bug
  26 +
  27 +## v.1.1.0 - 09 Feb, 2016
  28 +
  29 +Features
  30 +
  31 +- Added .exists() to check for command existence
  32 +- Improved performance of .hasFlag()
  1 +{
  2 + "append": {
  3 + "arity": 3,
  4 + "flags": [
  5 + "write",
  6 + "denyoom"
  7 + ],
  8 + "keyStart": 1,
  9 + "keyStop": 1,
  10 + "step": 1
  11 + },
  12 + "asking": {
  13 + "arity": 1,
  14 + "flags": [
  15 + "fast"
  16 + ],
  17 + "keyStart": 0,
  18 + "keyStop": 0,
  19 + "step": 0
  20 + },
  21 + "auth": {
  22 + "arity": 2,
  23 + "flags": [
  24 + "noscript",
  25 + "loading",
  26 + "stale",
  27 + "fast"
  28 + ],
  29 + "keyStart": 0,
  30 + "keyStop": 0,
  31 + "step": 0
  32 + },
  33 + "bgrewriteaof": {
  34 + "arity": 1,
  35 + "flags": [
  36 + "admin"
  37 + ],
  38 + "keyStart": 0,
  39 + "keyStop": 0,
  40 + "step": 0
  41 + },
  42 + "bgsave": {
  43 + "arity": -1,
  44 + "flags": [
  45 + "admin"
  46 + ],
  47 + "keyStart": 0,
  48 + "keyStop": 0,
  49 + "step": 0
  50 + },
  51 + "bitcount": {
  52 + "arity": -2,
  53 + "flags": [
  54 + "readonly"
  55 + ],
  56 + "keyStart": 1,
  57 + "keyStop": 1,
  58 + "step": 1
  59 + },
  60 + "bitfield": {
  61 + "arity": -2,
  62 + "flags": [
  63 + "write",
  64 + "denyoom"
  65 + ],
  66 + "keyStart": 1,
  67 + "keyStop": 1,
  68 + "step": 1
  69 + },
  70 + "bitop": {
  71 + "arity": -4,
  72 + "flags": [
  73 + "write",
  74 + "denyoom"
  75 + ],
  76 + "keyStart": 2,
  77 + "keyStop": -1,
  78 + "step": 1
  79 + },
  80 + "bitpos": {
  81 + "arity": -3,
  82 + "flags": [
  83 + "readonly"
  84 + ],
  85 + "keyStart": 1,
  86 + "keyStop": 1,
  87 + "step": 1
  88 + },
  89 + "blpop": {
  90 + "arity": -3,
  91 + "flags": [
  92 + "write",
  93 + "noscript"
  94 + ],
  95 + "keyStart": 1,
  96 + "keyStop": -2,
  97 + "step": 1
  98 + },
  99 + "brpop": {
  100 + "arity": -3,
  101 + "flags": [
  102 + "write",
  103 + "noscript"
  104 + ],
  105 + "keyStart": 1,
  106 + "keyStop": -2,
  107 + "step": 1
  108 + },
  109 + "brpoplpush": {
  110 + "arity": 4,
  111 + "flags": [
  112 + "write",
  113 + "denyoom",
  114 + "noscript"
  115 + ],
  116 + "keyStart": 1,
  117 + "keyStop": 2,
  118 + "step": 1
  119 + },
  120 + "client": {
  121 + "arity": -2,
  122 + "flags": [
  123 + "admin",
  124 + "noscript"
  125 + ],
  126 + "keyStart": 0,
  127 + "keyStop": 0,
  128 + "step": 0
  129 + },
  130 + "cluster": {
  131 + "arity": -2,
  132 + "flags": [
  133 + "admin"
  134 + ],
  135 + "keyStart": 0,
  136 + "keyStop": 0,
  137 + "step": 0
  138 + },
  139 + "command": {
  140 + "arity": 1,
  141 + "flags": [
  142 + "loading",
  143 + "stale"
  144 + ],
  145 + "keyStart": 0,
  146 + "keyStop": 0,
  147 + "step": 0
  148 + },
  149 + "config": {
  150 + "arity": -2,
  151 + "flags": [
  152 + "admin",
  153 + "loading",
  154 + "stale"
  155 + ],
  156 + "keyStart": 0,
  157 + "keyStop": 0,
  158 + "step": 0
  159 + },
  160 + "dbsize": {
  161 + "arity": 1,
  162 + "flags": [
  163 + "readonly",
  164 + "fast"
  165 + ],
  166 + "keyStart": 0,
  167 + "keyStop": 0,
  168 + "step": 0
  169 + },
  170 + "debug": {
  171 + "arity": -1,
  172 + "flags": [
  173 + "admin",
  174 + "noscript"
  175 + ],
  176 + "keyStart": 0,
  177 + "keyStop": 0,
  178 + "step": 0
  179 + },
  180 + "decr": {
  181 + "arity": 2,
  182 + "flags": [
  183 + "write",
  184 + "denyoom",
  185 + "fast"
  186 + ],
  187 + "keyStart": 1,
  188 + "keyStop": 1,
  189 + "step": 1
  190 + },
  191 + "decrby": {
  192 + "arity": 3,
  193 + "flags": [
  194 + "write",
  195 + "denyoom",
  196 + "fast"
  197 + ],
  198 + "keyStart": 1,
  199 + "keyStop": 1,
  200 + "step": 1
  201 + },
  202 + "del": {
  203 + "arity": -2,
  204 + "flags": [
  205 + "write"
  206 + ],
  207 + "keyStart": 1,
  208 + "keyStop": -1,
  209 + "step": 1
  210 + },
  211 + "discard": {
  212 + "arity": 1,
  213 + "flags": [
  214 + "noscript",
  215 + "fast"
  216 + ],
  217 + "keyStart": 0,
  218 + "keyStop": 0,
  219 + "step": 0
  220 + },
  221 + "dump": {
  222 + "arity": 2,
  223 + "flags": [
  224 + "readonly"
  225 + ],
  226 + "keyStart": 1,
  227 + "keyStop": 1,
  228 + "step": 1
  229 + },
  230 + "echo": {
  231 + "arity": 2,
  232 + "flags": [
  233 + "fast"
  234 + ],
  235 + "keyStart": 0,
  236 + "keyStop": 0,
  237 + "step": 0
  238 + },
  239 + "eval": {
  240 + "arity": -3,
  241 + "flags": [
  242 + "noscript",
  243 + "movablekeys"
  244 + ],
  245 + "keyStart": 0,
  246 + "keyStop": 0,
  247 + "step": 0
  248 + },
  249 + "evalsha": {
  250 + "arity": -3,
  251 + "flags": [
  252 + "noscript",
  253 + "movablekeys"
  254 + ],
  255 + "keyStart": 0,
  256 + "keyStop": 0,
  257 + "step": 0
  258 + },
  259 + "exec": {
  260 + "arity": 1,
  261 + "flags": [
  262 + "noscript",
  263 + "skip_monitor"
  264 + ],
  265 + "keyStart": 0,
  266 + "keyStop": 0,
  267 + "step": 0
  268 + },
  269 + "exists": {
  270 + "arity": -2,
  271 + "flags": [
  272 + "readonly",
  273 + "fast"
  274 + ],
  275 + "keyStart": 1,
  276 + "keyStop": -1,
  277 + "step": 1
  278 + },
  279 + "expire": {
  280 + "arity": 3,
  281 + "flags": [
  282 + "write",
  283 + "fast"
  284 + ],
  285 + "keyStart": 1,
  286 + "keyStop": 1,
  287 + "step": 1
  288 + },
  289 + "expireat": {
  290 + "arity": 3,
  291 + "flags": [
  292 + "write",
  293 + "fast"
  294 + ],
  295 + "keyStart": 1,
  296 + "keyStop": 1,
  297 + "step": 1
  298 + },
  299 + "flushall": {
  300 + "arity": -1,
  301 + "flags": [
  302 + "write"
  303 + ],
  304 + "keyStart": 0,
  305 + "keyStop": 0,
  306 + "step": 0
  307 + },
  308 + "flushdb": {
  309 + "arity": -1,
  310 + "flags": [
  311 + "write"
  312 + ],
  313 + "keyStart": 0,
  314 + "keyStop": 0,
  315 + "step": 0
  316 + },
  317 + "geoadd": {
  318 + "arity": -5,
  319 + "flags": [
  320 + "write",
  321 + "denyoom"
  322 + ],
  323 + "keyStart": 1,
  324 + "keyStop": 1,
  325 + "step": 1
  326 + },
  327 + "geodist": {
  328 + "arity": -4,
  329 + "flags": [
  330 + "readonly"
  331 + ],
  332 + "keyStart": 1,
  333 + "keyStop": 1,
  334 + "step": 1
  335 + },
  336 + "geohash": {
  337 + "arity": -2,
  338 + "flags": [
  339 + "readonly"
  340 + ],
  341 + "keyStart": 1,
  342 + "keyStop": 1,
  343 + "step": 1
  344 + },
  345 + "geopos": {
  346 + "arity": -2,
  347 + "flags": [
  348 + "readonly"
  349 + ],
  350 + "keyStart": 1,
  351 + "keyStop": 1,
  352 + "step": 1
  353 + },
  354 + "georadius": {
  355 + "arity": -6,
  356 + "flags": [
  357 + "write"
  358 + ],
  359 + "keyStart": 1,
  360 + "keyStop": 1,
  361 + "step": 1
  362 + },
  363 + "georadiusbymember": {
  364 + "arity": -5,
  365 + "flags": [
  366 + "write"
  367 + ],
  368 + "keyStart": 1,
  369 + "keyStop": 1,
  370 + "step": 1
  371 + },
  372 + "get": {
  373 + "arity": 2,
  374 + "flags": [
  375 + "readonly",
  376 + "fast"
  377 + ],
  378 + "keyStart": 1,
  379 + "keyStop": 1,
  380 + "step": 1
  381 + },
  382 + "getbit": {
  383 + "arity": 3,
  384 + "flags": [
  385 + "readonly",
  386 + "fast"
  387 + ],
  388 + "keyStart": 1,
  389 + "keyStop": 1,
  390 + "step": 1
  391 + },
  392 + "getrange": {
  393 + "arity": 4,
  394 + "flags": [
  395 + "readonly"
  396 + ],
  397 + "keyStart": 1,
  398 + "keyStop": 1,
  399 + "step": 1
  400 + },
  401 + "getset": {
  402 + "arity": 3,
  403 + "flags": [
  404 + "write",
  405 + "denyoom"
  406 + ],
  407 + "keyStart": 1,
  408 + "keyStop": 1,
  409 + "step": 1
  410 + },
  411 + "hdel": {
  412 + "arity": -3,
  413 + "flags": [
  414 + "write",
  415 + "fast"
  416 + ],
  417 + "keyStart": 1,
  418 + "keyStop": 1,
  419 + "step": 1
  420 + },
  421 + "hexists": {
  422 + "arity": 3,
  423 + "flags": [
  424 + "readonly",
  425 + "fast"
  426 + ],
  427 + "keyStart": 1,
  428 + "keyStop": 1,
  429 + "step": 1
  430 + },
  431 + "hget": {
  432 + "arity": 3,
  433 + "flags": [
  434 + "readonly",
  435 + "fast"
  436 + ],
  437 + "keyStart": 1,
  438 + "keyStop": 1,
  439 + "step": 1
  440 + },
  441 + "hgetall": {
  442 + "arity": 2,
  443 + "flags": [
  444 + "readonly"
  445 + ],
  446 + "keyStart": 1,
  447 + "keyStop": 1,
  448 + "step": 1
  449 + },
  450 + "hincrby": {
  451 + "arity": 4,
  452 + "flags": [
  453 + "write",
  454 + "denyoom",
  455 + "fast"
  456 + ],
  457 + "keyStart": 1,
  458 + "keyStop": 1,
  459 + "step": 1
  460 + },
  461 + "hincrbyfloat": {
  462 + "arity": 4,
  463 + "flags": [
  464 + "write",
  465 + "denyoom",
  466 + "fast"
  467 + ],
  468 + "keyStart": 1,
  469 + "keyStop": 1,
  470 + "step": 1
  471 + },
  472 + "hkeys": {
  473 + "arity": 2,
  474 + "flags": [
  475 + "readonly",
  476 + "sort_for_script"
  477 + ],
  478 + "keyStart": 1,
  479 + "keyStop": 1,
  480 + "step": 1
  481 + },
  482 + "hlen": {
  483 + "arity": 2,
  484 + "flags": [
  485 + "readonly",
  486 + "fast"
  487 + ],
  488 + "keyStart": 1,
  489 + "keyStop": 1,
  490 + "step": 1
  491 + },
  492 + "hmget": {
  493 + "arity": -3,
  494 + "flags": [
  495 + "readonly"
  496 + ],
  497 + "keyStart": 1,
  498 + "keyStop": 1,
  499 + "step": 1
  500 + },
  501 + "hmset": {
  502 + "arity": -4,
  503 + "flags": [
  504 + "write",
  505 + "denyoom"
  506 + ],
  507 + "keyStart": 1,
  508 + "keyStop": 1,
  509 + "step": 1
  510 + },
  511 + "host:": {
  512 + "arity": -1,
  513 + "flags": [
  514 + "loading",
  515 + "stale"
  516 + ],
  517 + "keyStart": 0,
  518 + "keyStop": 0,
  519 + "step": 0
  520 + },
  521 + "hscan": {
  522 + "arity": -3,
  523 + "flags": [
  524 + "readonly",
  525 + "random"
  526 + ],
  527 + "keyStart": 1,
  528 + "keyStop": 1,
  529 + "step": 1
  530 + },
  531 + "hset": {
  532 + "arity": 4,
  533 + "flags": [
  534 + "write",
  535 + "denyoom",
  536 + "fast"
  537 + ],
  538 + "keyStart": 1,
  539 + "keyStop": 1,
  540 + "step": 1
  541 + },
  542 + "hsetnx": {
  543 + "arity": 4,
  544 + "flags": [
  545 + "write",
  546 + "denyoom",
  547 + "fast"
  548 + ],
  549 + "keyStart": 1,
  550 + "keyStop": 1,
  551 + "step": 1
  552 + },
  553 + "hstrlen": {
  554 + "arity": 3,
  555 + "flags": [
  556 + "readonly",
  557 + "fast"
  558 + ],
  559 + "keyStart": 1,
  560 + "keyStop": 1,
  561 + "step": 1
  562 + },
  563 + "hvals": {
  564 + "arity": 2,
  565 + "flags": [
  566 + "readonly",
  567 + "sort_for_script"
  568 + ],
  569 + "keyStart": 1,
  570 + "keyStop": 1,
  571 + "step": 1
  572 + },
  573 + "incr": {
  574 + "arity": 2,
  575 + "flags": [
  576 + "write",
  577 + "denyoom",
  578 + "fast"
  579 + ],
  580 + "keyStart": 1,
  581 + "keyStop": 1,
  582 + "step": 1
  583 + },
  584 + "incrby": {
  585 + "arity": 3,
  586 + "flags": [
  587 + "write",
  588 + "denyoom",
  589 + "fast"
  590 + ],
  591 + "keyStart": 1,
  592 + "keyStop": 1,
  593 + "step": 1
  594 + },
  595 + "incrbyfloat": {
  596 + "arity": 3,
  597 + "flags": [
  598 + "write",
  599 + "denyoom",
  600 + "fast"
  601 + ],
  602 + "keyStart": 1,
  603 + "keyStop": 1,
  604 + "step": 1
  605 + },
  606 + "info": {
  607 + "arity": -1,
  608 + "flags": [
  609 + "loading",
  610 + "stale"
  611 + ],
  612 + "keyStart": 0,
  613 + "keyStop": 0,
  614 + "step": 0
  615 + },
  616 + "keys": {
  617 + "arity": 2,
  618 + "flags": [
  619 + "readonly",
  620 + "sort_for_script"
  621 + ],
  622 + "keyStart": 0,
  623 + "keyStop": 0,
  624 + "step": 0
  625 + },
  626 + "lastsave": {
  627 + "arity": 1,
  628 + "flags": [
  629 + "random",
  630 + "fast"
  631 + ],
  632 + "keyStart": 0,
  633 + "keyStop": 0,
  634 + "step": 0
  635 + },
  636 + "latency": {
  637 + "arity": -2,
  638 + "flags": [
  639 + "admin",
  640 + "noscript",
  641 + "loading",
  642 + "stale"
  643 + ],
  644 + "keyStart": 0,
  645 + "keyStop": 0,
  646 + "step": 0
  647 + },
  648 + "lindex": {
  649 + "arity": 3,
  650 + "flags": [
  651 + "readonly"
  652 + ],
  653 + "keyStart": 1,
  654 + "keyStop": 1,
  655 + "step": 1
  656 + },
  657 + "linsert": {
  658 + "arity": 5,
  659 + "flags": [
  660 + "write",
  661 + "denyoom"
  662 + ],
  663 + "keyStart": 1,
  664 + "keyStop": 1,
  665 + "step": 1
  666 + },
  667 + "llen": {
  668 + "arity": 2,
  669 + "flags": [
  670 + "readonly",
  671 + "fast"
  672 + ],
  673 + "keyStart": 1,
  674 + "keyStop": 1,
  675 + "step": 1
  676 + },
  677 + "lpop": {
  678 + "arity": 2,
  679 + "flags": [
  680 + "write",
  681 + "fast"
  682 + ],
  683 + "keyStart": 1,
  684 + "keyStop": 1,
  685 + "step": 1
  686 + },
  687 + "lpush": {
  688 + "arity": -3,
  689 + "flags": [
  690 + "write",
  691 + "denyoom",
  692 + "fast"
  693 + ],
  694 + "keyStart": 1,
  695 + "keyStop": 1,
  696 + "step": 1
  697 + },
  698 + "lpushx": {
  699 + "arity": -3,
  700 + "flags": [
  701 + "write",
  702 + "denyoom",
  703 + "fast"
  704 + ],
  705 + "keyStart": 1,
  706 + "keyStop": 1,
  707 + "step": 1
  708 + },
  709 + "lrange": {
  710 + "arity": 4,
  711 + "flags": [
  712 + "readonly"
  713 + ],
  714 + "keyStart": 1,
  715 + "keyStop": 1,
  716 + "step": 1
  717 + },
  718 + "lrem": {
  719 + "arity": 4,
  720 + "flags": [
  721 + "write"
  722 + ],
  723 + "keyStart": 1,
  724 + "keyStop": 1,
  725 + "step": 1
  726 + },
  727 + "lset": {
  728 + "arity": 4,
  729 + "flags": [
  730 + "write",
  731 + "denyoom"
  732 + ],
  733 + "keyStart": 1,
  734 + "keyStop": 1,
  735 + "step": 1
  736 + },
  737 + "ltrim": {
  738 + "arity": 4,
  739 + "flags": [
  740 + "write"
  741 + ],
  742 + "keyStart": 1,
  743 + "keyStop": 1,
  744 + "step": 1
  745 + },
  746 + "memory": {
  747 + "arity": -2,
  748 + "flags": [
  749 + "readonly"
  750 + ],
  751 + "keyStart": 0,
  752 + "keyStop": 0,
  753 + "step": 0
  754 + },
  755 + "mget": {
  756 + "arity": -2,
  757 + "flags": [
  758 + "readonly"
  759 + ],
  760 + "keyStart": 1,
  761 + "keyStop": -1,
  762 + "step": 1
  763 + },
  764 + "migrate": {
  765 + "arity": -6,
  766 + "flags": [
  767 + "write",
  768 + "movablekeys"
  769 + ],
  770 + "keyStart": 0,
  771 + "keyStop": 0,
  772 + "step": 0
  773 + },
  774 + "module": {
  775 + "arity": -2,
  776 + "flags": [
  777 + "admin",
  778 + "noscript"
  779 + ],
  780 + "keyStart": 1,
  781 + "keyStop": 1,
  782 + "step": 1
  783 + },
  784 + "monitor": {
  785 + "arity": 1,
  786 + "flags": [
  787 + "admin",
  788 + "noscript"
  789 + ],
  790 + "keyStart": 0,
  791 + "keyStop": 0,
  792 + "step": 0
  793 + },
  794 + "move": {
  795 + "arity": 3,
  796 + "flags": [
  797 + "write",
  798 + "fast"
  799 + ],
  800 + "keyStart": 1,
  801 + "keyStop": 1,
  802 + "step": 1
  803 + },
  804 + "mset": {
  805 + "arity": -3,
  806 + "flags": [
  807 + "write",
  808 + "denyoom"
  809 + ],
  810 + "keyStart": 1,
  811 + "keyStop": -1,
  812 + "step": 2
  813 + },
  814 + "msetnx": {
  815 + "arity": -3,
  816 + "flags": [
  817 + "write",
  818 + "denyoom"
  819 + ],
  820 + "keyStart": 1,
  821 + "keyStop": -1,
  822 + "step": 2
  823 + },
  824 + "multi": {
  825 + "arity": 1,
  826 + "flags": [
  827 + "noscript",
  828 + "fast"
  829 + ],
  830 + "keyStart": 0,
  831 + "keyStop": 0,
  832 + "step": 0
  833 + },
  834 + "object": {
  835 + "arity": 3,
  836 + "flags": [
  837 + "readonly"
  838 + ],
  839 + "keyStart": 2,
  840 + "keyStop": 2,
  841 + "step": 2
  842 + },
  843 + "persist": {
  844 + "arity": 2,
  845 + "flags": [
  846 + "write",
  847 + "fast"
  848 + ],
  849 + "keyStart": 1,
  850 + "keyStop": 1,
  851 + "step": 1
  852 + },
  853 + "pexpire": {
  854 + "arity": 3,
  855 + "flags": [
  856 + "write",
  857 + "fast"
  858 + ],
  859 + "keyStart": 1,
  860 + "keyStop": 1,
  861 + "step": 1
  862 + },
  863 + "pexpireat": {
  864 + "arity": 3,
  865 + "flags": [
  866 + "write",
  867 + "fast"
  868 + ],
  869 + "keyStart": 1,
  870 + "keyStop": 1,
  871 + "step": 1
  872 + },
  873 + "pfadd": {
  874 + "arity": -2,
  875 + "flags": [
  876 + "write",
  877 + "denyoom",
  878 + "fast"
  879 + ],
  880 + "keyStart": 1,
  881 + "keyStop": 1,
  882 + "step": 1
  883 + },
  884 + "pfcount": {
  885 + "arity": -2,
  886 + "flags": [
  887 + "readonly"
  888 + ],
  889 + "keyStart": 1,
  890 + "keyStop": -1,
  891 + "step": 1
  892 + },
  893 + "pfdebug": {
  894 + "arity": -3,
  895 + "flags": [
  896 + "write"
  897 + ],
  898 + "keyStart": 0,
  899 + "keyStop": 0,
  900 + "step": 0
  901 + },
  902 + "pfmerge": {
  903 + "arity": -2,
  904 + "flags": [
  905 + "write",
  906 + "denyoom"
  907 + ],
  908 + "keyStart": 1,
  909 + "keyStop": -1,
  910 + "step": 1
  911 + },
  912 + "pfselftest": {
  913 + "arity": 1,
  914 + "flags": [
  915 + "admin"
  916 + ],
  917 + "keyStart": 0,
  918 + "keyStop": 0,
  919 + "step": 0
  920 + },
  921 + "ping": {
  922 + "arity": -1,
  923 + "flags": [
  924 + "stale",
  925 + "fast"
  926 + ],
  927 + "keyStart": 0,
  928 + "keyStop": 0,
  929 + "step": 0
  930 + },
  931 + "post": {
  932 + "arity": -1,
  933 + "flags": [
  934 + "loading",
  935 + "stale"
  936 + ],
  937 + "keyStart": 0,
  938 + "keyStop": 0,
  939 + "step": 0
  940 + },
  941 + "psetex": {
  942 + "arity": 4,
  943 + "flags": [
  944 + "write",
  945 + "denyoom"
  946 + ],
  947 + "keyStart": 1,
  948 + "keyStop": 1,
  949 + "step": 1
  950 + },
  951 + "psubscribe": {
  952 + "arity": -2,
  953 + "flags": [
  954 + "pubsub",
  955 + "noscript",
  956 + "loading",
  957 + "stale"
  958 + ],
  959 + "keyStart": 0,
  960 + "keyStop": 0,
  961 + "step": 0
  962 + },
  963 + "psync": {
  964 + "arity": 3,
  965 + "flags": [
  966 + "readonly",
  967 + "admin",
  968 + "noscript"
  969 + ],
  970 + "keyStart": 0,
  971 + "keyStop": 0,
  972 + "step": 0
  973 + },
  974 + "pttl": {
  975 + "arity": 2,
  976 + "flags": [
  977 + "readonly",
  978 + "fast"
  979 + ],
  980 + "keyStart": 1,
  981 + "keyStop": 1,
  982 + "step": 1
  983 + },
  984 + "publish": {
  985 + "arity": 3,
  986 + "flags": [
  987 + "pubsub",
  988 + "loading",
  989 + "stale",
  990 + "fast"
  991 + ],
  992 + "keyStart": 0,
  993 + "keyStop": 0,
  994 + "step": 0
  995 + },
  996 + "pubsub": {
  997 + "arity": -2,
  998 + "flags": [
  999 + "pubsub",
  1000 + "random",
  1001 + "loading",
  1002 + "stale"
  1003 + ],
  1004 + "keyStart": 0,
  1005 + "keyStop": 0,
  1006 + "step": 0
  1007 + },
  1008 + "punsubscribe": {
  1009 + "arity": -1,
  1010 + "flags": [
  1011 + "pubsub",
  1012 + "noscript",
  1013 + "loading",
  1014 + "stale"
  1015 + ],
  1016 + "keyStart": 0,
  1017 + "keyStop": 0,
  1018 + "step": 0
  1019 + },
  1020 + "quit": {
  1021 + "arity": 1,
  1022 + "flags": [
  1023 + "loading",
  1024 + "stale",
  1025 + "readonly"
  1026 + ],
  1027 + "keyStart": 0,
  1028 + "keyStop": 0,
  1029 + "step": 0
  1030 + },
  1031 + "randomkey": {
  1032 + "arity": 1,
  1033 + "flags": [
  1034 + "readonly",
  1035 + "random"
  1036 + ],
  1037 + "keyStart": 0,
  1038 + "keyStop": 0,
  1039 + "step": 0
  1040 + },
  1041 + "readonly": {
  1042 + "arity": 1,
  1043 + "flags": [
  1044 + "fast"
  1045 + ],
  1046 + "keyStart": 0,
  1047 + "keyStop": 0,
  1048 + "step": 0
  1049 + },
  1050 + "readwrite": {
  1051 + "arity": 1,
  1052 + "flags": [
  1053 + "fast"
  1054 + ],
  1055 + "keyStart": 0,
  1056 + "keyStop": 0,
  1057 + "step": 0
  1058 + },
  1059 + "rename": {
  1060 + "arity": 3,
  1061 + "flags": [
  1062 + "write"
  1063 + ],
  1064 + "keyStart": 1,
  1065 + "keyStop": 2,
  1066 + "step": 1
  1067 + },
  1068 + "renamenx": {
  1069 + "arity": 3,
  1070 + "flags": [
  1071 + "write",
  1072 + "fast"
  1073 + ],
  1074 + "keyStart": 1,
  1075 + "keyStop": 2,
  1076 + "step": 1
  1077 + },
  1078 + "replconf": {
  1079 + "arity": -1,
  1080 + "flags": [
  1081 + "admin",
  1082 + "noscript",
  1083 + "loading",
  1084 + "stale"
  1085 + ],
  1086 + "keyStart": 0,
  1087 + "keyStop": 0,
  1088 + "step": 0
  1089 + },
  1090 + "restore": {
  1091 + "arity": -4,
  1092 + "flags": [
  1093 + "write",
  1094 + "denyoom"
  1095 + ],
  1096 + "keyStart": 1,
  1097 + "keyStop": 1,
  1098 + "step": 1
  1099 + },
  1100 + "restore-asking": {
  1101 + "arity": -4,
  1102 + "flags": [
  1103 + "write",
  1104 + "denyoom",
  1105 + "asking"
  1106 + ],
  1107 + "keyStart": 1,
  1108 + "keyStop": 1,
  1109 + "step": 1
  1110 + },
  1111 + "role": {
  1112 + "arity": 1,
  1113 + "flags": [
  1114 + "noscript",
  1115 + "loading",
  1116 + "stale"
  1117 + ],
  1118 + "keyStart": 0,
  1119 + "keyStop": 0,
  1120 + "step": 0
  1121 + },
  1122 + "rpop": {
  1123 + "arity": 2,
  1124 + "flags": [
  1125 + "write",
  1126 + "fast"
  1127 + ],
  1128 + "keyStart": 1,
  1129 + "keyStop": 1,
  1130 + "step": 1
  1131 + },
  1132 + "rpoplpush": {
  1133 + "arity": 3,
  1134 + "flags": [
  1135 + "write",
  1136 + "denyoom"
  1137 + ],
  1138 + "keyStart": 1,
  1139 + "keyStop": 2,
  1140 + "step": 1
  1141 + },
  1142 + "rpush": {
  1143 + "arity": -3,
  1144 + "flags": [
  1145 + "write",
  1146 + "denyoom",
  1147 + "fast"
  1148 + ],
  1149 + "keyStart": 1,
  1150 + "keyStop": 1,
  1151 + "step": 1
  1152 + },
  1153 + "rpushx": {
  1154 + "arity": -3,
  1155 + "flags": [
  1156 + "write",
  1157 + "denyoom",
  1158 + "fast"
  1159 + ],
  1160 + "keyStart": 1,
  1161 + "keyStop": 1,
  1162 + "step": 1
  1163 + },
  1164 + "sadd": {
  1165 + "arity": -3,
  1166 + "flags": [
  1167 + "write",
  1168 + "denyoom",
  1169 + "fast"
  1170 + ],
  1171 + "keyStart": 1,
  1172 + "keyStop": 1,
  1173 + "step": 1
  1174 + },
  1175 + "save": {
  1176 + "arity": 1,
  1177 + "flags": [
  1178 + "admin",
  1179 + "noscript"
  1180 + ],
  1181 + "keyStart": 0,
  1182 + "keyStop": 0,
  1183 + "step": 0
  1184 + },
  1185 + "scan": {
  1186 + "arity": -2,
  1187 + "flags": [
  1188 + "readonly",
  1189 + "random"
  1190 + ],
  1191 + "keyStart": 0,
  1192 + "keyStop": 0,
  1193 + "step": 0
  1194 + },
  1195 + "scard": {
  1196 + "arity": 2,
  1197 + "flags": [
  1198 + "readonly",
  1199 + "fast"
  1200 + ],
  1201 + "keyStart": 1,
  1202 + "keyStop": 1,
  1203 + "step": 1
  1204 + },
  1205 + "script": {
  1206 + "arity": -2,
  1207 + "flags": [
  1208 + "noscript"
  1209 + ],
  1210 + "keyStart": 0,
  1211 + "keyStop": 0,
  1212 + "step": 0
  1213 + },
  1214 + "sdiff": {
  1215 + "arity": -2,
  1216 + "flags": [
  1217 + "readonly",
  1218 + "sort_for_script"
  1219 + ],
  1220 + "keyStart": 1,
  1221 + "keyStop": -1,
  1222 + "step": 1
  1223 + },
  1224 + "sdiffstore": {
  1225 + "arity": -3,
  1226 + "flags": [
  1227 + "write",
  1228 + "denyoom"
  1229 + ],
  1230 + "keyStart": 1,
  1231 + "keyStop": -1,
  1232 + "step": 1
  1233 + },
  1234 + "select": {
  1235 + "arity": 2,
  1236 + "flags": [
  1237 + "loading",
  1238 + "fast"
  1239 + ],
  1240 + "keyStart": 0,
  1241 + "keyStop": 0,
  1242 + "step": 0
  1243 + },
  1244 + "set": {
  1245 + "arity": -3,
  1246 + "flags": [
  1247 + "write",
  1248 + "denyoom"
  1249 + ],
  1250 + "keyStart": 1,
  1251 + "keyStop": 1,
  1252 + "step": 1
  1253 + },
  1254 + "setbit": {
  1255 + "arity": 4,
  1256 + "flags": [
  1257 + "write",
  1258 + "denyoom"
  1259 + ],
  1260 + "keyStart": 1,
  1261 + "keyStop": 1,
  1262 + "step": 1
  1263 + },
  1264 + "setex": {
  1265 + "arity": 4,
  1266 + "flags": [
  1267 + "write",
  1268 + "denyoom"
  1269 + ],
  1270 + "keyStart": 1,
  1271 + "keyStop": 1,
  1272 + "step": 1
  1273 + },
  1274 + "setnx": {
  1275 + "arity": 3,
  1276 + "flags": [
  1277 + "write",
  1278 + "denyoom",
  1279 + "fast"
  1280 + ],
  1281 + "keyStart": 1,
  1282 + "keyStop": 1,
  1283 + "step": 1
  1284 + },
  1285 + "setrange": {
  1286 + "arity": 4,
  1287 + "flags": [
  1288 + "write",
  1289 + "denyoom"
  1290 + ],
  1291 + "keyStart": 1,
  1292 + "keyStop": 1,
  1293 + "step": 1
  1294 + },
  1295 + "shutdown": {
  1296 + "arity": -1,
  1297 + "flags": [
  1298 + "admin",
  1299 + "loading",
  1300 + "stale"
  1301 + ],
  1302 + "keyStart": 0,
  1303 + "keyStop": 0,
  1304 + "step": 0
  1305 + },
  1306 + "sinter": {
  1307 + "arity": -2,
  1308 + "flags": [
  1309 + "readonly",
  1310 + "sort_for_script"
  1311 + ],
  1312 + "keyStart": 1,
  1313 + "keyStop": -1,
  1314 + "step": 1
  1315 + },
  1316 + "sinterstore": {
  1317 + "arity": -3,
  1318 + "flags": [
  1319 + "write",
  1320 + "denyoom"
  1321 + ],
  1322 + "keyStart": 1,
  1323 + "keyStop": -1,
  1324 + "step": 1
  1325 + },
  1326 + "sismember": {
  1327 + "arity": 3,
  1328 + "flags": [
  1329 + "readonly",
  1330 + "fast"
  1331 + ],
  1332 + "keyStart": 1,
  1333 + "keyStop": 1,
  1334 + "step": 1
  1335 + },
  1336 + "slaveof": {
  1337 + "arity": 3,
  1338 + "flags": [
  1339 + "admin",
  1340 + "noscript",
  1341 + "stale"
  1342 + ],
  1343 + "keyStart": 0,
  1344 + "keyStop": 0,
  1345 + "step": 0
  1346 + },
  1347 + "slowlog": {
  1348 + "arity": -2,
  1349 + "flags": [
  1350 + "admin"
  1351 + ],
  1352 + "keyStart": 0,
  1353 + "keyStop": 0,
  1354 + "step": 0
  1355 + },
  1356 + "smembers": {
  1357 + "arity": 2,
  1358 + "flags": [
  1359 + "readonly",
  1360 + "sort_for_script"
  1361 + ],
  1362 + "keyStart": 1,
  1363 + "keyStop": 1,
  1364 + "step": 1
  1365 + },
  1366 + "smove": {
  1367 + "arity": 4,
  1368 + "flags": [
  1369 + "write",
  1370 + "fast"
  1371 + ],
  1372 + "keyStart": 1,
  1373 + "keyStop": 2,
  1374 + "step": 1
  1375 + },
  1376 + "sort": {
  1377 + "arity": -2,
  1378 + "flags": [
  1379 + "write",
  1380 + "denyoom",
  1381 + "movablekeys"
  1382 + ],
  1383 + "keyStart": 1,
  1384 + "keyStop": 1,
  1385 + "step": 1
  1386 + },
  1387 + "spop": {
  1388 + "arity": -2,
  1389 + "flags": [
  1390 + "write",
  1391 + "random",
  1392 + "fast"
  1393 + ],
  1394 + "keyStart": 1,
  1395 + "keyStop": 1,
  1396 + "step": 1
  1397 + },
  1398 + "srandmember": {
  1399 + "arity": -2,
  1400 + "flags": [
  1401 + "readonly",
  1402 + "random"
  1403 + ],
  1404 + "keyStart": 1,
  1405 + "keyStop": 1,
  1406 + "step": 1
  1407 + },
  1408 + "srem": {
  1409 + "arity": -3,
  1410 + "flags": [
  1411 + "write",
  1412 + "fast"
  1413 + ],
  1414 + "keyStart": 1,
  1415 + "keyStop": 1,
  1416 + "step": 1
  1417 + },
  1418 + "sscan": {
  1419 + "arity": -3,
  1420 + "flags": [
  1421 + "readonly",
  1422 + "random"
  1423 + ],
  1424 + "keyStart": 1,
  1425 + "keyStop": 1,
  1426 + "step": 1
  1427 + },
  1428 + "strlen": {
  1429 + "arity": 2,
  1430 + "flags": [
  1431 + "readonly",
  1432 + "fast"
  1433 + ],
  1434 + "keyStart": 1,
  1435 + "keyStop": 1,
  1436 + "step": 1
  1437 + },
  1438 + "subscribe": {
  1439 + "arity": -2,
  1440 + "flags": [
  1441 + "pubsub",
  1442 + "noscript",
  1443 + "loading",
  1444 + "stale"
  1445 + ],
  1446 + "keyStart": 0,
  1447 + "keyStop": 0,
  1448 + "step": 0
  1449 + },
  1450 + "substr": {
  1451 + "arity": 4,
  1452 + "flags": [
  1453 + "readonly"
  1454 + ],
  1455 + "keyStart": 1,
  1456 + "keyStop": 1,
  1457 + "step": 1
  1458 + },
  1459 + "sunion": {
  1460 + "arity": -2,
  1461 + "flags": [
  1462 + "readonly",
  1463 + "sort_for_script"
  1464 + ],
  1465 + "keyStart": 1,
  1466 + "keyStop": -1,
  1467 + "step": 1
  1468 + },
  1469 + "sunionstore": {
  1470 + "arity": -3,
  1471 + "flags": [
  1472 + "write",
  1473 + "denyoom"
  1474 + ],
  1475 + "keyStart": 1,
  1476 + "keyStop": -1,
  1477 + "step": 1
  1478 + },
  1479 + "swapdb": {
  1480 + "arity": 3,
  1481 + "flags": [
  1482 + "write",
  1483 + "fast"
  1484 + ],
  1485 + "keyStart": 0,
  1486 + "keyStop": 0,
  1487 + "step": 0
  1488 + },
  1489 + "sync": {
  1490 + "arity": 1,
  1491 + "flags": [
  1492 + "readonly",
  1493 + "admin",
  1494 + "noscript"
  1495 + ],
  1496 + "keyStart": 0,
  1497 + "keyStop": 0,
  1498 + "step": 0
  1499 + },
  1500 + "time": {
  1501 + "arity": 1,
  1502 + "flags": [
  1503 + "random",
  1504 + "fast"
  1505 + ],
  1506 + "keyStart": 0,
  1507 + "keyStop": 0,
  1508 + "step": 0
  1509 + },
  1510 + "touch": {
  1511 + "arity": -2,
  1512 + "flags": [
  1513 + "readonly",
  1514 + "fast"
  1515 + ],
  1516 + "keyStart": 1,
  1517 + "keyStop": 1,
  1518 + "step": 1
  1519 + },
  1520 + "ttl": {
  1521 + "arity": 2,
  1522 + "flags": [
  1523 + "readonly",
  1524 + "fast"
  1525 + ],
  1526 + "keyStart": 1,
  1527 + "keyStop": 1,
  1528 + "step": 1
  1529 + },
  1530 + "type": {
  1531 + "arity": 2,
  1532 + "flags": [
  1533 + "readonly",
  1534 + "fast"
  1535 + ],
  1536 + "keyStart": 1,
  1537 + "keyStop": 1,
  1538 + "step": 1
  1539 + },
  1540 + "unlink": {
  1541 + "arity": -2,
  1542 + "flags": [
  1543 + "write",
  1544 + "fast"
  1545 + ],
  1546 + "keyStart": 1,
  1547 + "keyStop": -1,
  1548 + "step": 1
  1549 + },
  1550 + "unsubscribe": {
  1551 + "arity": -1,
  1552 + "flags": [
  1553 + "pubsub",
  1554 + "noscript",
  1555 + "loading",
  1556 + "stale"
  1557 + ],
  1558 + "keyStart": 0,
  1559 + "keyStop": 0,
  1560 + "step": 0
  1561 + },
  1562 + "unwatch": {
  1563 + "arity": 1,
  1564 + "flags": [
  1565 + "noscript",
  1566 + "fast"
  1567 + ],
  1568 + "keyStart": 0,
  1569 + "keyStop": 0,
  1570 + "step": 0
  1571 + },
  1572 + "wait": {
  1573 + "arity": 3,
  1574 + "flags": [
  1575 + "noscript"
  1576 + ],
  1577 + "keyStart": 0,
  1578 + "keyStop": 0,
  1579 + "step": 0
  1580 + },
  1581 + "watch": {
  1582 + "arity": -2,
  1583 + "flags": [
  1584 + "noscript",
  1585 + "fast"
  1586 + ],
  1587 + "keyStart": 1,
  1588 + "keyStop": -1,
  1589 + "step": 1
  1590 + },
  1591 + "zadd": {
  1592 + "arity": -4,
  1593 + "flags": [
  1594 + "write",
  1595 + "denyoom",
  1596 + "fast"
  1597 + ],
  1598 + "keyStart": 1,
  1599 + "keyStop": 1,
  1600 + "step": 1
  1601 + },
  1602 + "zcard": {
  1603 + "arity": 2,
  1604 + "flags": [
  1605 + "readonly",
  1606 + "fast"
  1607 + ],
  1608 + "keyStart": 1,
  1609 + "keyStop": 1,
  1610 + "step": 1
  1611 + },
  1612 + "zcount": {
  1613 + "arity": 4,
  1614 + "flags": [
  1615 + "readonly",
  1616 + "fast"
  1617 + ],
  1618 + "keyStart": 1,
  1619 + "keyStop": 1,
  1620 + "step": 1
  1621 + },
  1622 + "zincrby": {
  1623 + "arity": 4,
  1624 + "flags": [
  1625 + "write",
  1626 + "denyoom",
  1627 + "fast"
  1628 + ],
  1629 + "keyStart": 1,
  1630 + "keyStop": 1,
  1631 + "step": 1
  1632 + },
  1633 + "zinterstore": {
  1634 + "arity": -4,
  1635 + "flags": [
  1636 + "write",
  1637 + "denyoom",
  1638 + "movablekeys"
  1639 + ],
  1640 + "keyStart": 0,
  1641 + "keyStop": 0,
  1642 + "step": 0
  1643 + },
  1644 + "zlexcount": {
  1645 + "arity": 4,
  1646 + "flags": [
  1647 + "readonly",
  1648 + "fast"
  1649 + ],
  1650 + "keyStart": 1,
  1651 + "keyStop": 1,
  1652 + "step": 1
  1653 + },
  1654 + "zrange": {
  1655 + "arity": -4,
  1656 + "flags": [
  1657 + "readonly"
  1658 + ],
  1659 + "keyStart": 1,
  1660 + "keyStop": 1,
  1661 + "step": 1
  1662 + },
  1663 + "zrangebylex": {
  1664 + "arity": -4,
  1665 + "flags": [
  1666 + "readonly"
  1667 + ],
  1668 + "keyStart": 1,
  1669 + "keyStop": 1,
  1670 + "step": 1
  1671 + },
  1672 + "zrangebyscore": {
  1673 + "arity": -4,
  1674 + "flags": [
  1675 + "readonly"
  1676 + ],
  1677 + "keyStart": 1,
  1678 + "keyStop": 1,
  1679 + "step": 1
  1680 + },
  1681 + "zrank": {
  1682 + "arity": 3,
  1683 + "flags": [
  1684 + "readonly",
  1685 + "fast"
  1686 + ],
  1687 + "keyStart": 1,
  1688 + "keyStop": 1,
  1689 + "step": 1
  1690 + },
  1691 + "zrem": {
  1692 + "arity": -3,
  1693 + "flags": [
  1694 + "write",
  1695 + "fast"
  1696 + ],
  1697 + "keyStart": 1,
  1698 + "keyStop": 1,
  1699 + "step": 1
  1700 + },
  1701 + "zremrangebylex": {
  1702 + "arity": 4,
  1703 + "flags": [
  1704 + "write"
  1705 + ],
  1706 + "keyStart": 1,
  1707 + "keyStop": 1,
  1708 + "step": 1
  1709 + },
  1710 + "zremrangebyrank": {
  1711 + "arity": 4,
  1712 + "flags": [
  1713 + "write"
  1714 + ],
  1715 + "keyStart": 1,
  1716 + "keyStop": 1,
  1717 + "step": 1
  1718 + },
  1719 + "zremrangebyscore": {
  1720 + "arity": 4,
  1721 + "flags": [
  1722 + "write"
  1723 + ],
  1724 + "keyStart": 1,
  1725 + "keyStop": 1,
  1726 + "step": 1
  1727 + },
  1728 + "zrevrange": {
  1729 + "arity": -4,
  1730 + "flags": [
  1731 + "readonly"
  1732 + ],
  1733 + "keyStart": 1,
  1734 + "keyStop": 1,
  1735 + "step": 1
  1736 + },
  1737 + "zrevrangebylex": {
  1738 + "arity": -4,
  1739 + "flags": [
  1740 + "readonly"
  1741 + ],
  1742 + "keyStart": 1,
  1743 + "keyStop": 1,
  1744 + "step": 1
  1745 + },
  1746 + "zrevrangebyscore": {
  1747 + "arity": -4,
  1748 + "flags": [
  1749 + "readonly"
  1750 + ],
  1751 + "keyStart": 1,
  1752 + "keyStop": 1,
  1753 + "step": 1
  1754 + },
  1755 + "zrevrank": {
  1756 + "arity": 3,
  1757 + "flags": [
  1758 + "readonly",
  1759 + "fast"
  1760 + ],
  1761 + "keyStart": 1,
  1762 + "keyStop": 1,
  1763 + "step": 1
  1764 + },
  1765 + "zscan": {
  1766 + "arity": -3,
  1767 + "flags": [
  1768 + "readonly",
  1769 + "random"
  1770 + ],
  1771 + "keyStart": 1,
  1772 + "keyStop": 1,
  1773 + "step": 1
  1774 + },
  1775 + "zscore": {
  1776 + "arity": 3,
  1777 + "flags": [
  1778 + "readonly",
  1779 + "fast"
  1780 + ],
  1781 + "keyStart": 1,
  1782 + "keyStop": 1,
  1783 + "step": 1
  1784 + },
  1785 + "zunionstore": {
  1786 + "arity": -4,
  1787 + "flags": [
  1788 + "write",
  1789 + "denyoom",
  1790 + "movablekeys"
  1791 + ],
  1792 + "keyStart": 0,
  1793 + "keyStop": 0,
  1794 + "step": 0
  1795 + }
  1796 +}
  1 +'use strict'
  2 +
  3 +var commands = require('./commands.json')
  4 +
  5 +/**
  6 + * Redis command list
  7 + *
  8 + * All commands are lowercased.
  9 + *
  10 + * @var {string[]}
  11 + * @public
  12 + */
  13 +exports.list = Object.keys(commands)
  14 +
  15 +var flags = {}
  16 +exports.list.forEach(function (commandName) {
  17 + flags[commandName] = commands[commandName].flags.reduce(function (flags, flag) {
  18 + flags[flag] = true
  19 + return flags
  20 + }, {})
  21 +})
  22 +/**
  23 + * Check if the command exists
  24 + *
  25 + * @param {string} commandName - the command name
  26 + * @return {boolean} result
  27 + * @public
  28 + */
  29 +exports.exists = function (commandName) {
  30 + return Boolean(commands[commandName])
  31 +}
  32 +
  33 +/**
  34 + * Check if the command has the flag
  35 + *
  36 + * Some of possible flags: readonly, noscript, loading
  37 + * @param {string} commandName - the command name
  38 + * @param {string} flag - the flag to check
  39 + * @return {boolean} result
  40 + * @public
  41 + */
  42 +exports.hasFlag = function (commandName, flag) {
  43 + if (!flags[commandName]) {
  44 + throw new Error('Unknown command ' + commandName)
  45 + }
  46 +
  47 + return Boolean(flags[commandName][flag])
  48 +}
  49 +
  50 +/**
  51 + * Get indexes of keys in the command arguments
  52 + *
  53 + * @param {string} commandName - the command name
  54 + * @param {string[]} args - the arguments of the command
  55 + * @param {object} [options] - options
  56 + * @param {boolean} [options.parseExternalKey] - parse external keys
  57 + * @return {number[]} - the list of the index
  58 + * @public
  59 + *
  60 + * @example
  61 + * ```javascript
  62 + * getKeyIndexes('set', ['key', 'value']) // [0]
  63 + * getKeyIndexes('mget', ['key1', 'key2']) // [0, 1]
  64 + * ```
  65 + */
  66 +exports.getKeyIndexes = function (commandName, args, options) {
  67 + var command = commands[commandName]
  68 + if (!command) {
  69 + throw new Error('Unknown command ' + commandName)
  70 + }
  71 +
  72 + if (!Array.isArray(args)) {
  73 + throw new Error('Expect args to be an array')
  74 + }
  75 +
  76 + var keys = []
  77 + var i, keyStart, keyStop, parseExternalKey
  78 + switch (commandName) {
  79 + case 'zunionstore':
  80 + case 'zinterstore':
  81 + keys.push(0)
  82 + // fall through
  83 + case 'eval':
  84 + case 'evalsha':
  85 + keyStop = Number(args[1]) + 2
  86 + for (i = 2; i < keyStop; i++) {
  87 + keys.push(i)
  88 + }
  89 + break
  90 + case 'sort':
  91 + parseExternalKey = options && options.parseExternalKey
  92 + keys.push(0)
  93 + for (i = 1; i < args.length - 1; i++) {
  94 + if (typeof args[i] !== 'string') {
  95 + continue
  96 + }
  97 + var directive = args[i].toUpperCase()
  98 + if (directive === 'GET') {
  99 + i += 1
  100 + if (args[i] !== '#') {
  101 + if (parseExternalKey) {
  102 + keys.push([i, getExternalKeyNameLength(args[i])])
  103 + } else {
  104 + keys.push(i)
  105 + }
  106 + }
  107 + } else if (directive === 'BY') {
  108 + i += 1
  109 + if (parseExternalKey) {
  110 + keys.push([i, getExternalKeyNameLength(args[i])])
  111 + } else {
  112 + keys.push(i)
  113 + }
  114 + } else if (directive === 'STORE') {
  115 + i += 1
  116 + keys.push(i)
  117 + }
  118 + }
  119 + break
  120 + case 'migrate':
  121 + if (args[2] === '') {
  122 + for (i = 5; i < args.length - 1; i++) {
  123 + if (args[i].toUpperCase() === 'KEYS') {
  124 + for (var j = i + 1; j < args.length; j++) {
  125 + keys.push(j)
  126 + }
  127 + break
  128 + }
  129 + }
  130 + } else {
  131 + keys.push(2)
  132 + }
  133 + break
  134 + default:
  135 + // step has to be at least one in this case, otherwise the command does not contain a key
  136 + if (command.step > 0) {
  137 + keyStart = command.keyStart - 1
  138 + keyStop = command.keyStop > 0 ? command.keyStop : args.length + command.keyStop + 1
  139 + for (i = keyStart; i < keyStop; i += command.step) {
  140 + keys.push(i)
  141 + }
  142 + }
  143 + break
  144 + }
  145 +
  146 + return keys
  147 +}
  148 +
  149 +function getExternalKeyNameLength (key) {
  150 + if (typeof key !== 'string') {
  151 + key = String(key)
  152 + }
  153 + var hashPos = key.indexOf('->')
  154 + return hashPos === -1 ? key.length : hashPos
  155 +}
  1 +{
  2 + "_args": [
  3 + [
  4 + {
  5 + "raw": "redis-commands@^1.2.0",
  6 + "scope": null,
  7 + "escapedName": "redis-commands",
  8 + "name": "redis-commands",
  9 + "rawSpec": "^1.2.0",
  10 + "spec": ">=1.2.0 <2.0.0",
  11 + "type": "range"
  12 + },
  13 + "/Users/fzy/project/koa2_Sequelize_project/node_modules/redis"
  14 + ]
  15 + ],
  16 + "_from": "redis-commands@>=1.2.0 <2.0.0",
  17 + "_id": "redis-commands@1.3.1",
  18 + "_inCache": true,
  19 + "_location": "/redis-commands",
  20 + "_nodeVersion": "7.4.0",
  21 + "_npmOperationalInternal": {
  22 + "host": "packages-12-west.internal.npmjs.com",
  23 + "tmp": "tmp/redis-commands-1.3.1.tgz_1485363355063_0.7909097610972822"
  24 + },
  25 + "_npmUser": {
  26 + "name": "bridgear",
  27 + "email": "ruben@bridgewater.de"
  28 + },
  29 + "_npmVersion": "4.0.5",
  30 + "_phantomChildren": {},
  31 + "_requested": {
  32 + "raw": "redis-commands@^1.2.0",
  33 + "scope": null,
  34 + "escapedName": "redis-commands",
  35 + "name": "redis-commands",
  36 + "rawSpec": "^1.2.0",
  37 + "spec": ">=1.2.0 <2.0.0",
  38 + "type": "range"
  39 + },
  40 + "_requiredBy": [
  41 + "/redis"
  42 + ],
  43 + "_resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz",
  44 + "_shasum": "81d826f45fa9c8b2011f4cd7a0fe597d241d442b",
  45 + "_shrinkwrap": null,
  46 + "_spec": "redis-commands@^1.2.0",
  47 + "_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/redis",
  48 + "author": {
  49 + "name": "luin",
  50 + "email": "i@zihua.li",
  51 + "url": "http://zihua.li"
  52 + },
  53 + "bugs": {
  54 + "url": "https://github.com/NodeRedis/redis-commonds/issues"
  55 + },
  56 + "dependencies": {},
  57 + "description": "Redis commands",
  58 + "devDependencies": {
  59 + "chai": "^3.4.0",
  60 + "codeclimate-test-reporter": "^0.4.0",
  61 + "ioredis": "^2.0.0",
  62 + "istanbul": "^0.4.3",
  63 + "json-stable-stringify": "^1.0.0",
  64 + "mocha": "^3.0.0",
  65 + "snazzy": "^6.0.0",
  66 + "standard": "^8.0.0"
  67 + },
  68 + "directories": {},
  69 + "dist": {
  70 + "shasum": "81d826f45fa9c8b2011f4cd7a0fe597d241d442b",
  71 + "tarball": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz"
  72 + },
  73 + "gitHead": "3c87f94d94fcf66ca0c77101a6d36fad32134326",
  74 + "homepage": "https://github.com/NodeRedis/redis-commonds",
  75 + "keywords": [
  76 + "redis",
  77 + "commands",
  78 + "prefix"
  79 + ],
  80 + "license": "MIT",
  81 + "main": "index.js",
  82 + "maintainers": [
  83 + {
  84 + "name": "bridgear",
  85 + "email": "ruben@bridgewater.de"
  86 + },
  87 + {
  88 + "name": "luin",
  89 + "email": "i@zihua.li"
  90 + }
  91 + ],
  92 + "name": "redis-commands",
  93 + "optionalDependencies": {},
  94 + "readme": "# Redis Commands\n\n[![Build Status](https://travis-ci.org/NodeRedis/redis-commands.png?branch=master)](https://travis-ci.org/NodeRedis/redis-commands)\n[![Code Climate](https://codeclimate.com/github/NodeRedis/redis-commands/badges/gpa.svg)](https://codeclimate.com/github/NodeRedis/redis-commands)\n[![Test Coverage](https://codeclimate.com/github/NodeRedis/redis-commands/badges/coverage.svg)](https://codeclimate.com/github/NodeRedis/redis-commands/coverage)\n\nThis module exports all the commands that Redis supports.\n\n## Install\n\n```shell\n$ npm install redis-commands\n```\n\n## Usage\n\n```javascript\nvar commands = require('redis-commands');\n```\n\n`.list` is an array contains all the lowercased commands:\n\n```javascript\ncommands.list.forEach(function (command) {\n console.log(command);\n});\n```\n\n`.exists()` is used to check if the command exists:\n\n```javascript\ncommands.exists('set') // true\ncommands.exists('other-command') // false\n```\n\n`.hasFlag()` is used to check if the command has the flag:\n\n```javascript\ncommands.hasFlag('set', 'readonly') // false\n```\n\n`.getKeyIndexes()` is used to get the indexes of keys in the command arguments:\n\n```javascript\ncommands.getKeyIndexes('set', ['key', 'value']) // [0]\ncommands.getKeyIndexes('mget', ['key1', 'key2']) // [0, 1]\n```\n\n## Acknowledgment\n\nThank [@Yuan Chuan](https://github.com/yuanchuan) for the package name. The original redis-commands is renamed to [@yuanchuan/redis-commands](https://www.npmjs.com/package/@yuanchuan/redis-commands).\n",
  95 + "readmeFilename": "README.md",
  96 + "repository": {
  97 + "type": "git",
  98 + "url": "git+https://github.com/NodeRedis/redis-commands.git"
  99 + },
  100 + "scripts": {
  101 + "build": "node tools/build",
  102 + "coverage": "node ./node_modules/istanbul/lib/cli.js cover --preserve-comments ./node_modules/mocha/bin/_mocha -- -R spec",
  103 + "coverage:check": "node ./node_modules/istanbul/lib/cli.js check-coverage --branch 100 --statement 100",
  104 + "lint": "standard --fix --verbose | snazzy",
  105 + "posttest": "npm run coverage && npm run coverage:check",
  106 + "pretest": "npm run lint",
  107 + "test": "mocha"
  108 + },
  109 + "version": "1.3.1"
  110 +}
  1 +'use strict'
  2 +
  3 +/* global describe, it */
  4 +
  5 +var commands = require('..')
  6 +var expect = require('chai').expect
  7 +
  8 +describe('redis-commands', function () {
  9 + describe('.list', function () {
  10 + it('should be an array', function () {
  11 + expect(commands.list).to.be.instanceof(Array)
  12 + })
  13 +
  14 + it('should ensure every command is lowercase', function () {
  15 + commands.list.forEach(function (command) {
  16 + expect(command.toLowerCase()).to.eql(command)
  17 + })
  18 + })
  19 +
  20 + it('should ensure quit command is added to the commands list', function () {
  21 + expect(commands.list.indexOf('quit')).not.to.eql(-1)
  22 + })
  23 +
  24 + it('should not contain multi-word commands', function () {
  25 + commands.list.forEach(function (command) {
  26 + expect(command.indexOf(' ')).to.eql(-1)
  27 + })
  28 + })
  29 + })
  30 +
  31 + describe('.exists()', function () {
  32 + it('should return true for existing commands', function () {
  33 + expect(commands.exists('set')).to.eql(true)
  34 + expect(commands.exists('get')).to.eql(true)
  35 + expect(commands.exists('cluster')).to.eql(true)
  36 + expect(commands.exists('quit')).to.eql(true)
  37 + expect(commands.exists('config')).to.eql(true)
  38 + })
  39 +
  40 + it('should return false for non-existing commands', function () {
  41 + expect(commands.exists('SET')).to.eql(false)
  42 + expect(commands.exists('set get')).to.eql(false)
  43 + expect(commands.exists('other-command')).to.eql(false)
  44 + })
  45 + })
  46 +
  47 + describe('.hasFlag()', function () {
  48 + it('should return true if the command has the flag', function () {
  49 + expect(commands.hasFlag('set', 'write')).to.eql(true)
  50 + expect(commands.hasFlag('set', 'denyoom')).to.eql(true)
  51 + expect(commands.hasFlag('select', 'fast')).to.eql(true)
  52 + })
  53 +
  54 + it('should return false otherwise', function () {
  55 + expect(commands.hasFlag('set', 'fast')).to.eql(false)
  56 + expect(commands.hasFlag('set', 'readonly')).to.eql(false)
  57 + expect(commands.hasFlag('select', 'denyoom')).to.eql(false)
  58 + expect(commands.hasFlag('quit', 'denyoom')).to.eql(false)
  59 + })
  60 +
  61 + it('should throw on unknown commands', function () {
  62 + expect(function () { commands.hasFlag('UNKNOWN') }).to.throw(Error)
  63 + })
  64 + })
  65 +
  66 + describe('.getKeyIndexes()', function () {
  67 + var index = commands.getKeyIndexes
  68 +
  69 + it('should throw on unknown commands', function () {
  70 + expect(function () { index('UNKNOWN') }).to.throw(Error)
  71 + })
  72 +
  73 + it('should throw on faulty args', function () {
  74 + expect(function () { index('get', 'foo') }).to.throw(Error)
  75 + })
  76 +
  77 + it('should return an empty array if no keys exist', function () {
  78 + expect(index('auth', [])).to.eql([])
  79 + })
  80 +
  81 + it('should return key indexes', function () {
  82 + expect(index('set', ['foo', 'bar'])).to.eql([0])
  83 + expect(index('del', ['foo'])).to.eql([0])
  84 + expect(index('get', ['foo'])).to.eql([0])
  85 + expect(index('mget', ['foo', 'bar'])).to.eql([0, 1])
  86 + expect(index('mset', ['foo', 'v1', 'bar', 'v2'])).to.eql([0, 2])
  87 + expect(index('hmset', ['key', 'foo', 'v1', 'bar', 'v2'])).to.eql([0])
  88 + expect(index('blpop', ['key1', 'key2', '17'])).to.eql([0, 1])
  89 + expect(index('evalsha', ['23123', '2', 'foo', 'bar', 'zoo'])).to.eql([2, 3])
  90 + expect(index('sort', ['key'])).to.eql([0])
  91 + expect(index('zunionstore', ['out', '2', 'zset1', 'zset2', 'WEIGHTS', '2', '3'])).to.eql([0, 2, 3])
  92 + expect(index('migrate', ['127.0.0.1', 6379, 'foo', 0, 0, 'COPY'])).to.eql([2])
  93 + expect(index('migrate', ['127.0.0.1', 6379, '', 0, 0, 'REPLACE', 'KEYS', 'foo', 'bar'])).to.eql([7, 8])
  94 + expect(index('migrate', ['127.0.0.1', 6379, '', 0, 0, 'KEYS', 'foo', 'bar'])).to.eql([6, 7])
  95 + })
  96 +
  97 + it('should support numeric argument', function () {
  98 + expect(index('evalsha', ['23123', 2, 'foo', 'bar', 'zoo'])).to.eql([2, 3])
  99 + expect(index('zinterstore', ['out', 2, 'zset1', 'zset2', 'WEIGHTS', 2, 3])).to.eql([0, 2, 3])
  100 + })
  101 +
  102 + describe('disable parseExternalKey', function () {
  103 + it('should not parse external keys', function () {
  104 + expect(index('sort', ['key', 'BY', 'hash:*->field'])).to.eql([0, 2])
  105 + expect(index('sort', ['key', 'BY', 'hash:*->field', 'LIMIT', 2, 3, 'GET', 'gk', 'GET', '#', 'Get', 'gh->f*', 'DESC', 'ALPHA', 'STORE', 'store'])).to.eql([0, 2, 7, 11, 15])
  106 + })
  107 + })
  108 +
  109 + describe('enable parseExternalKey', function () {
  110 + it('should parse external keys', function () {
  111 + expect(index('sort', ['key', 'BY', 'hash:*->field'], {
  112 + parseExternalKey: true
  113 + })).to.eql([0, [2, 6]])
  114 + expect(index('sort', ['key', 'BY', 'hash:*->field', 'LIMIT', 2, 3, 'GET', new Buffer('gk'), 'GET', '#', 'Get', 'gh->f*', 'DESC', 'ALPHA', 'STORE', 'store'], {
  115 + parseExternalKey: true
  116 + })).to.eql([0, [2, 6], [7, 2], [11, 2], 15])
  117 + })
  118 + })
  119 + })
  120 +})
  1 +var fs = require('fs')
  2 +var path = require('path')
  3 +var stringify = require('json-stable-stringify')
  4 +var commandPath = path.join(__dirname, '..', 'commands.json')
  5 +var redisCommands = require('../')
  6 +
  7 +var Redis = require('ioredis')
  8 +var redis = new Redis(process.env.REDIS_URI)
  9 +
  10 +redis.command().then(function (res) {
  11 + redis.disconnect()
  12 +
  13 + // Find all special handled cases
  14 + var movableKeys = String(redisCommands.getKeyIndexes).match(/case '[a-z-]+':/g).map(function (entry) {
  15 + return entry.replace(/^case '|':$/g, '')
  16 + })
  17 +
  18 + var commands = res.reduce(function (prev, current) {
  19 + var currentCommandPos = movableKeys.indexOf(current[0])
  20 + if (currentCommandPos !== -1 && current[2].indexOf('movablekeys') !== -1) {
  21 + movableKeys.splice(currentCommandPos, 1)
  22 + }
  23 + // https://github.com/antirez/redis/issues/2598
  24 + if (current[0] === 'brpop' && current[4] === 1) {
  25 + current[4] = -2
  26 + }
  27 + prev[current[0]] = {
  28 + arity: current[1] || 1, // https://github.com/antirez/redis/pull/2986
  29 + flags: current[2],
  30 + keyStart: current[3],
  31 + keyStop: current[4],
  32 + step: current[5]
  33 + }
  34 + return prev
  35 + }, {})
  36 +
  37 + // Future proof. Redis might implement this at some point
  38 + // https://github.com/antirez/redis/pull/2982
  39 + if (!commands.quit) {
  40 + commands.quit = {
  41 + arity: 1,
  42 + flags: [
  43 + 'loading',
  44 + 'stale',
  45 + 'readonly'
  46 + ],
  47 + keyStart: 0,
  48 + keyStop: 0,
  49 + step: 0
  50 + }
  51 + }
  52 +
  53 + if (movableKeys.length !== 0) {
  54 + throw new Error('Not all commands (\'' + movableKeys.join('\', \'') + '\') with the "movablekeys" flag are handled in the code')
  55 + }
  56 +
  57 + // Use json-stable-stringify instead fo JSON.stringify
  58 + // for easier diffing
  59 + var content = stringify(commands, { space: ' ' })
  60 +
  61 + fs.writeFile(commandPath, content)
  62 +})
  1 +# IntelliJ project files
  2 +.idea
  3 +*.iml
  4 +out
  5 +gen
  6 +
  7 +# Unrelevant files and folders
  8 +benchmark
  9 +coverage
  10 +test
  11 +.travis.yml
  12 +.gitignore
  13 +*.log
  14 +.vscode
  15 +.codeclimate.yml
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) 2015 NodeRedis
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  22 +
  1 +[![Build Status](https://travis-ci.org/NodeRedis/node-redis-parser.png?branch=master)](https://travis-ci.org/NodeRedis/node-redis-parser)
  2 +[![Test Coverage](https://codeclimate.com/github/NodeRedis/node-redis-parser/badges/coverage.svg)](https://codeclimate.com/github/NodeRedis/node-redis-parser/coverage)
  3 +[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
  4 +
  5 +# redis-parser
  6 +
  7 +A high performance javascript redis parser built for [node_redis](https://github.com/NodeRedis/node_redis) and [ioredis](https://github.com/luin/ioredis). Parses all [RESP](http://redis.io/topics/protocol) data.
  8 +
  9 +## Install
  10 +
  11 +Install with [NPM](https://npmjs.org/):
  12 +
  13 + npm install redis-parser
  14 +
  15 +## Usage
  16 +
  17 +```js
  18 +var Parser = require('redis-parser');
  19 +
  20 +var myParser = new Parser(options);
  21 +```
  22 +
  23 +### Options
  24 +
  25 +* `returnReply`: *function*; mandatory
  26 +* `returnError`: *function*; mandatory
  27 +* `returnFatalError`: *function*; optional, defaults to the returnError function
  28 +* `returnBuffers`: *boolean*; optional, defaults to false
  29 +* `stringNumbers`: *boolean*; optional, defaults to false
  30 +
  31 +### Functions
  32 +
  33 +* `reset()`: reset the parser to it's initial state
  34 +* `setReturnBuffers(boolean)`: (JSParser only) set the returnBuffers option on/off without resetting the parser
  35 +* `setStringNumbers(boolean)`: (JSParser only) set the stringNumbers option on/off without resetting the parser
  36 +
  37 +### Error classes
  38 +
  39 +* `RedisError` sub class of Error
  40 +* `ReplyError` sub class of RedisError
  41 +* `ParserError` sub class of RedisError
  42 +
  43 +All Redis errors will be returned as `ReplyErrors` while a parser error is returned as `ParserError`.
  44 +All error classes are exported by the parser.
  45 +
  46 +### Example
  47 +
  48 +```js
  49 +var Parser = require("redis-parser");
  50 +
  51 +function Library () {}
  52 +
  53 +Library.prototype.returnReply = function (reply) { ... }
  54 +Library.prototype.returnError = function (err) { ... }
  55 +Library.prototype.returnFatalError = function (err) { ... }
  56 +
  57 +var lib = new Library();
  58 +
  59 +var parser = new Parser({
  60 + returnReply: function(reply) {
  61 + lib.returnReply(reply);
  62 + },
  63 + returnError: function(err) {
  64 + lib.returnError(err);
  65 + },
  66 + returnFatalError: function (err) {
  67 + lib.returnFatalError(err);
  68 + }
  69 +});
  70 +
  71 +Library.prototype.streamHandler = function () {
  72 + this.stream.on('data', function (buffer) {
  73 + // Here the data (e.g. `new Buffer('$5\r\nHello\r\n'`)) is passed to the parser and the result is passed to either function depending on the provided data.
  74 + parser.execute(buffer);
  75 + });
  76 +};
  77 +```
  78 +You do not have to use the returnFatalError function. Fatal errors will be returned in the normal error function in that case.
  79 +
  80 +And if you want to return buffers instead of strings, you can do this by adding the `returnBuffers` option.
  81 +
  82 +If you handle with big numbers that are to large for JS (Number.MAX_SAFE_INTEGER === 2^53 - 16) please use the `stringNumbers` option. That way all numbers are going to be returned as String and you can handle them safely.
  83 +
  84 +```js
  85 +// Same functions as in the first example
  86 +
  87 +var parser = new Parser({
  88 + returnReply: function(reply) {
  89 + lib.returnReply(reply);
  90 + },
  91 + returnError: function(err) {
  92 + lib.returnError(err);
  93 + },
  94 + returnBuffers: true, // All strings are returned as Buffer e.g. <Buffer 48 65 6c 6c 6f>
  95 + stringNumbers: true // All numbers are returned as String
  96 +});
  97 +
  98 +// The streamHandler as above
  99 +```
  100 +
  101 +## Protocol errors
  102 +
  103 +To handle protocol errors (this is very unlikely to happen) gracefully you should add the returnFatalError option, reject any still running command (they might have been processed properly but the reply is just wrong), destroy the socket and reconnect. Note that while doing this no new command may be added, so all new commands have to be buffered in the meantime, otherwise a chunk might still contain partial data of a following command that was already processed properly but answered in the same chunk as the command that resulted in the protocol error.
  104 +
  105 +## Contribute
  106 +
  107 +The parser is highly optimized but there may still be further optimizations possible.
  108 +
  109 + npm install
  110 + npm test
  111 + npm run benchmark
  112 +
  113 +Currently the benchmark compares the performance against the hiredis parser:
  114 +
  115 + HIREDIS: $ multiple chunks in a bulk string x 859,880 ops/sec ±1.22% (82 runs sampled)
  116 + HIREDIS BUF: $ multiple chunks in a bulk string x 608,869 ops/sec ±1.72% (85 runs sampled)
  117 + JS PARSER: $ multiple chunks in a bulk string x 910,590 ops/sec ±0.87% (89 runs sampled)
  118 + JS PARSER BUF: $ multiple chunks in a bulk string x 1,299,507 ops/sec ±2.18% (84 runs sampled)
  119 +
  120 + HIREDIS: + multiple chunks in a string x 1,787,203 ops/sec ±0.58% (96 runs sampled)
  121 + HIREDIS BUF: + multiple chunks in a string x 943,584 ops/sec ±1.62% (87 runs sampled)
  122 + JS PARSER: + multiple chunks in a string x 2,008,264 ops/sec ±1.01% (91 runs sampled)
  123 + JS PARSER BUF: + multiple chunks in a string x 2,045,546 ops/sec ±0.78% (91 runs sampled)
  124 +
  125 + HIREDIS: $ 4mb bulk string x 310 ops/sec ±1.58% (75 runs sampled)
  126 + HIREDIS BUF: $ 4mb bulk string x 471 ops/sec ±2.28% (78 runs sampled)
  127 + JS PARSER: $ 4mb bulk string x 747 ops/sec ±2.43% (85 runs sampled)
  128 + JS PARSER BUF: $ 4mb bulk string x 846 ops/sec ±5.52% (72 runs sampled)
  129 +
  130 + HIREDIS: + simple string x 2,324,866 ops/sec ±1.61% (90 runs sampled)
  131 + HIREDIS BUF: + simple string x 1,085,823 ops/sec ±2.47% (82 runs sampled)
  132 + JS PARSER: + simple string x 4,567,358 ops/sec ±1.97% (81 runs sampled)
  133 + JS PARSER BUF: + simple string x 5,433,901 ops/sec ±0.66% (93 runs sampled)
  134 +
  135 + HIREDIS: : integer x 2,332,946 ops/sec ±0.47% (93 runs sampled)
  136 + JS PARSER: : integer x 17,730,449 ops/sec ±0.73% (91 runs sampled)
  137 + JS PARSER STR: : integer x 12,942,037 ops/sec ±0.51% (92 runs sampled)
  138 +
  139 + HIREDIS: : big integer x 2,012,572 ops/sec ±0.33% (93 runs sampled)
  140 + JS PARSER: : big integer x 10,210,923 ops/sec ±0.94% (94 runs sampled)
  141 + JS PARSER STR: : big integer x 4,453,320 ops/sec ±0.52% (94 runs sampled)
  142 +
  143 + HIREDIS: * array x 44,479 ops/sec ±0.55% (94 runs sampled)
  144 + HIREDIS BUF: * array x 14,391 ops/sec ±1.04% (86 runs sampled)
  145 + JS PARSER: * array x 53,796 ops/sec ±2.08% (79 runs sampled)
  146 + JS PARSER BUF: * array x 72,428 ops/sec ±0.72% (93 runs sampled)
  147 +
  148 + HIREDIS: * big nested array x 217 ops/sec ±0.97% (83 runs sampled)
  149 + HIREDIS BUF: * big nested array x 255 ops/sec ±2.28% (77 runs sampled)
  150 + JS PARSER: * big nested array x 242 ops/sec ±1.10% (85 runs sampled)
  151 + JS PARSER BUF: * big nested array x 375 ops/sec ±1.21% (88 runs sampled)
  152 +
  153 + HIREDIS: - error x 78,821 ops/sec ±0.80% (93 runs sampled)
  154 + JS PARSER: - error x 143,382 ops/sec ±0.75% (92 runs sampled)
  155 +
  156 + Platform info:
  157 + Ubuntu 16.10
  158 + Node.js 7.4.0
  159 + Intel(R) Core(TM) i7-5600U CPU
  160 +
  161 +## License
  162 +
  163 +[MIT](./LICENSE)
  1 +## v.2.6.0 - 03 Apr, 2017
  2 +
  3 +Internals
  4 +
  5 +- Use Buffer.allocUnsafe instead of new Buffer() with modern Node.js versions
  6 +
  7 +## v.2.5.0 - 11 Mar, 2017
  8 +
  9 +Features
  10 +
  11 +- Added a `ParserError` class to differentiate them to ReplyErrors. The class is also exported
  12 +
  13 +Bugfixes
  14 +
  15 +- All errors now show their error message again next to the error name in the stack trace
  16 +- ParserErrors now show the offset and buffer attributes while being logged
  17 +
  18 +## v.2.4.1 - 05 Feb, 2017
  19 +
  20 +Bugfixes
  21 +
  22 +- Fixed minimal memory consumption overhead for chunked buffers
  23 +
  24 +## v.2.4.0 - 25 Jan, 2017
  25 +
  26 +Features
  27 +
  28 +- Added `reset` function to reset the parser to it's initial values
  29 +- Added `setReturnBuffers` function to reset the returnBuffers option (Only for the JSParser)
  30 +- Added `setStringNumbers` function to reset the stringNumbers option (Only for the JSParser)
  31 +- All Errors are now of sub classes of the new `RedisError` class. It is also exported.
  32 +- Improved bulk string chunked data handling performance
  33 +
  34 +Bugfixes
  35 +
  36 +- Parsing time for big nested arrays is now linear
  37 +
  38 +## v.2.3.0 - 25 Nov, 2016
  39 +
  40 +Features
  41 +
  42 +- Parsing time for big arrays (e.g. 4mb+) is now linear and works well for arbitrary array sizes
  43 +
  44 +This case is a magnitude faster than before
  45 +
  46 + OLD STR: * big array x 1.09 ops/sec ±2.15% (7 runs sampled)
  47 + OLD BUF: * big array x 1.23 ops/sec ±2.67% (8 runs sampled)
  48 +
  49 + NEW STR: * big array x 273 ops/sec ±2.09% (85 runs sampled)
  50 + NEW BUF: * big array x 259 ops/sec ±1.32% (85 runs sampled)
  51 + (~10mb array with 1000 entries)
  52 +
  53 +## v.2.2.0 - 18 Nov, 2016
  54 +
  55 +Features
  56 +
  57 +- Improve `stringNumbers` parsing performance by up to 100%
  58 +
  59 +Bugfixes
  60 +
  61 +- Do not unref the interval anymore due to issues with NodeJS
  62 +
  63 +## v.2.1.1 - 31 Oct, 2016
  64 +
  65 +Bugfixes
  66 +
  67 +- Remove erroneously added const to support Node.js 0.10
  68 +
  69 +## v.2.1.0 - 30 Oct, 2016
  70 +
  71 +Features
  72 +
  73 +- Improve parser errors by adding more detailed information to them
  74 +- Accept manipulated Object.prototypes
  75 +- Unref the interval if used
  76 +
  77 +## v.2.0.4 - 21 Jul, 2016
  78 +
  79 +Bugfixes
  80 +
  81 +- Fixed multi byte characters getting corrupted
  82 +
  83 +## v.2.0.3 - 17 Jun, 2016
  84 +
  85 +Bugfixes
  86 +
  87 +- Fixed parser not working with huge buffers (e.g. 300 MB)
  88 +
  89 +## v.2.0.2 - 08 Jun, 2016
  90 +
  91 +Bugfixes
  92 +
  93 +- Fixed parser with returnBuffers option returning corrupted data
  94 +
  95 +## v.2.0.1 - 04 Jun, 2016
  96 +
  97 +Bugfixes
  98 +
  99 +- Fixed multiple parsers working concurrently resulting in faulty data in some cases
  100 +
  101 +## v.2.0.0 - 29 May, 2016
  102 +
  103 +The javascript parser got completely rewritten by [Michael Diarmid](https://github.com/Salakar) and [Ruben Bridgewater](https://github.com/BridgeAR) and is now a lot faster than the hiredis parser.
  104 +Therefore the hiredis parser was deprecated and should only be used for testing purposes and benchmarking comparison.
  105 +
  106 +All Errors returned by the parser are from now on of class ReplyError
  107 +
  108 +Features
  109 +
  110 +- Improved performance by up to 15x as fast as before
  111 +- Improved options validation
  112 +- Added ReplyError Class
  113 +- Added parser benchmark
  114 +- Switched default parser from hiredis to JS, no matter if hiredis is installed or not
  115 +
  116 +Removed
  117 +
  118 +- Deprecated hiredis support
  119 +
  120 +## v.1.3.0 - 27 Mar, 2016
  121 +
  122 +Features
  123 +
  124 +- Added `auto` as parser name option to check what parser is available
  125 +- Non existing requested parsers falls back into auto mode instead of always choosing the JS parser
  126 +
  127 +## v.1.2.0 - 27 Mar, 2016
  128 +
  129 +Features
  130 +
  131 +- Added `stringNumbers` option to make sure all numbers are returned as string instead of a js number for precision
  132 +- The parser is from now on going to print warnings if a parser is explicitly requested that does not exist and gracefully chooses the JS parser
  133 +
  134 +## v.1.1.0 - 26 Jan, 2016
  135 +
  136 +Features
  137 +
  138 +- The parser is from now on going to reset itself on protocol errors
  1 +'use strict'
  2 +
  3 +module.exports = require('./lib/parser')
  4 +module.exports.ReplyError = require('./lib/replyError')
  5 +module.exports.RedisError = require('./lib/redisError')
  6 +module.exports.ParserError = require('./lib/redisError')
  1 +'use strict'
  2 +
  3 +var hiredis = require('hiredis')
  4 +var ReplyError = require('../lib/replyError')
  5 +var ParserError = require('../lib/parserError')
  6 +
  7 +/**
  8 + * Parse data
  9 + * @param parser
  10 + * @returns {*}
  11 + */
  12 +function parseData (parser, data) {
  13 + try {
  14 + return parser.reader.get()
  15 + } catch (err) {
  16 + // Protocol errors land here
  17 + // Reset the parser. Otherwise new commands can't be processed properly
  18 + parser.reader = new hiredis.Reader(parser.options)
  19 + parser.returnFatalError(new ParserError(err.message, JSON.stringify(data), -1))
  20 + }
  21 +}
  22 +
  23 +/**
  24 + * Hiredis Parser
  25 + * @param options
  26 + * @constructor
  27 + */
  28 +function HiredisReplyParser (options) {
  29 + this.returnError = options.returnError
  30 + this.returnFatalError = options.returnFatalError || options.returnError
  31 + this.returnReply = options.returnReply
  32 + this.name = 'hiredis'
  33 + this.options = {
  34 + return_buffers: !!options.returnBuffers
  35 + }
  36 + this.reader = new hiredis.Reader(this.options)
  37 +}
  38 +
  39 +HiredisReplyParser.prototype.execute = function (data) {
  40 + this.reader.feed(data)
  41 + var reply = parseData(this, data)
  42 +
  43 + while (reply !== undefined) {
  44 + if (reply && reply.name === 'Error') {
  45 + this.returnError(new ReplyError(reply.message))
  46 + } else {
  47 + this.returnReply(reply)
  48 + }
  49 + reply = parseData(this, data)
  50 + }
  51 +}
  52 +
  53 +/**
  54 + * Reset the parser values to the initial state
  55 + *
  56 + * @returns {undefined}
  57 + */
  58 +HiredisReplyParser.prototype.reset = function () {
  59 + this.reader = new hiredis.Reader(this.options)
  60 +}
  61 +
  62 +module.exports = HiredisReplyParser
  1 +'use strict'
  2 +
  3 +var StringDecoder = require('string_decoder').StringDecoder
  4 +var decoder = new StringDecoder()
  5 +var ReplyError = require('./replyError')
  6 +var ParserError = require('./parserError')
  7 +var bufferPool = bufferAlloc(32 * 1024)
  8 +var bufferOffset = 0
  9 +var interval = null
  10 +var counter = 0
  11 +var notDecreased = 0
  12 +var isModern = typeof Buffer.allocUnsafe === 'function'
  13 +
  14 +/**
  15 + * For backwards compatibility
  16 + * @param len
  17 + * @returns {Buffer}
  18 + */
  19 +
  20 +function bufferAlloc (len) {
  21 + return isModern ? Buffer.allocUnsafe(len) : new Buffer(len)
  22 +}
  23 +
  24 +/**
  25 + * Used for lengths and numbers only, faster perf on arrays / bulks
  26 + * @param parser
  27 + * @returns {*}
  28 + */
  29 +function parseSimpleNumbers (parser) {
  30 + var offset = parser.offset
  31 + var length = parser.buffer.length - 1
  32 + var number = 0
  33 + var sign = 1
  34 +
  35 + if (parser.buffer[offset] === 45) {
  36 + sign = -1
  37 + offset++
  38 + }
  39 +
  40 + while (offset < length) {
  41 + var c1 = parser.buffer[offset++]
  42 + if (c1 === 13) { // \r\n
  43 + parser.offset = offset + 1
  44 + return sign * number
  45 + }
  46 + number = (number * 10) + (c1 - 48)
  47 + }
  48 +}
  49 +
  50 +/**
  51 + * Used for integer numbers in case of the returnNumbers option
  52 + *
  53 + * The maximimum possible integer to use is: Math.floor(Number.MAX_SAFE_INTEGER / 10)
  54 + * Staying in a SMI Math.floor((Math.pow(2, 32) / 10) - 1) is even more efficient though
  55 + *
  56 + * @param parser
  57 + * @returns {*}
  58 + */
  59 +function parseStringNumbers (parser) {
  60 + var offset = parser.offset
  61 + var length = parser.buffer.length - 1
  62 + var number = 0
  63 + var res = ''
  64 +
  65 + if (parser.buffer[offset] === 45) {
  66 + res += '-'
  67 + offset++
  68 + }
  69 +
  70 + while (offset < length) {
  71 + var c1 = parser.buffer[offset++]
  72 + if (c1 === 13) { // \r\n
  73 + parser.offset = offset + 1
  74 + if (number !== 0) {
  75 + res += number
  76 + }
  77 + return res
  78 + } else if (number > 429496728) {
  79 + res += (number * 10) + (c1 - 48)
  80 + number = 0
  81 + } else if (c1 === 48 && number === 0) {
  82 + res += 0
  83 + } else {
  84 + number = (number * 10) + (c1 - 48)
  85 + }
  86 + }
  87 +}
  88 +
  89 +/**
  90 + * Returns a string or buffer of the provided offset start and
  91 + * end ranges. Checks `optionReturnBuffers`.
  92 + *
  93 + * If returnBuffers is active, all return values are returned as buffers besides numbers and errors
  94 + *
  95 + * @param parser
  96 + * @param start
  97 + * @param end
  98 + * @returns {*}
  99 + */
  100 +function convertBufferRange (parser, start, end) {
  101 + parser.offset = end + 2
  102 + if (parser.optionReturnBuffers === true) {
  103 + return parser.buffer.slice(start, end)
  104 + }
  105 +
  106 + return parser.buffer.toString('utf-8', start, end)
  107 +}
  108 +
  109 +/**
  110 + * Parse a '+' redis simple string response but forward the offsets
  111 + * onto convertBufferRange to generate a string.
  112 + * @param parser
  113 + * @returns {*}
  114 + */
  115 +function parseSimpleString (parser) {
  116 + var start = parser.offset
  117 + var offset = start
  118 + var buffer = parser.buffer
  119 + var length = buffer.length - 1
  120 +
  121 + while (offset < length) {
  122 + if (buffer[offset++] === 13) { // \r\n
  123 + return convertBufferRange(parser, start, offset - 1)
  124 + }
  125 + }
  126 +}
  127 +
  128 +/**
  129 + * Returns the string length via parseSimpleNumbers
  130 + * @param parser
  131 + * @returns {*}
  132 + */
  133 +function parseLength (parser) {
  134 + var string = parseSimpleNumbers(parser)
  135 + if (string !== undefined) {
  136 + return string
  137 + }
  138 +}
  139 +
  140 +/**
  141 + * Parse a ':' redis integer response
  142 + *
  143 + * If stringNumbers is activated the parser always returns numbers as string
  144 + * This is important for big numbers (number > Math.pow(2, 53)) as js numbers
  145 + * are 64bit floating point numbers with reduced precision
  146 + *
  147 + * @param parser
  148 + * @returns {*}
  149 + */
  150 +function parseInteger (parser) {
  151 + if (parser.optionStringNumbers) {
  152 + return parseStringNumbers(parser)
  153 + }
  154 + return parseSimpleNumbers(parser)
  155 +}
  156 +
  157 +/**
  158 + * Parse a '$' redis bulk string response
  159 + * @param parser
  160 + * @returns {*}
  161 + */
  162 +function parseBulkString (parser) {
  163 + var length = parseLength(parser)
  164 + if (length === undefined) {
  165 + return
  166 + }
  167 + if (length === -1) {
  168 + return null
  169 + }
  170 + var offsetEnd = parser.offset + length
  171 + if (offsetEnd + 2 > parser.buffer.length) {
  172 + parser.bigStrSize = offsetEnd + 2
  173 + parser.bigOffset = parser.offset
  174 + parser.totalChunkSize = parser.buffer.length
  175 + parser.bufferCache.push(parser.buffer)
  176 + return
  177 + }
  178 +
  179 + return convertBufferRange(parser, parser.offset, offsetEnd)
  180 +}
  181 +
  182 +/**
  183 + * Parse a '-' redis error response
  184 + * @param parser
  185 + * @returns {Error}
  186 + */
  187 +function parseError (parser) {
  188 + var string = parseSimpleString(parser)
  189 + if (string !== undefined) {
  190 + if (parser.optionReturnBuffers === true) {
  191 + string = string.toString()
  192 + }
  193 + return new ReplyError(string)
  194 + }
  195 +}
  196 +
  197 +/**
  198 + * Parsing error handler, resets parser buffer
  199 + * @param parser
  200 + * @param error
  201 + */
  202 +function handleError (parser, error) {
  203 + parser.buffer = null
  204 + parser.returnFatalError(error)
  205 +}
  206 +
  207 +/**
  208 + * Parse a '*' redis array response
  209 + * @param parser
  210 + * @returns {*}
  211 + */
  212 +function parseArray (parser) {
  213 + var length = parseLength(parser)
  214 + if (length === undefined) {
  215 + return
  216 + }
  217 + if (length === -1) {
  218 + return null
  219 + }
  220 + var responses = new Array(length)
  221 + return parseArrayElements(parser, responses, 0)
  222 +}
  223 +
  224 +/**
  225 + * Push a partly parsed array to the stack
  226 + *
  227 + * @param parser
  228 + * @param elem
  229 + * @param i
  230 + * @returns {undefined}
  231 + */
  232 +function pushArrayCache (parser, elem, pos) {
  233 + parser.arrayCache.push(elem)
  234 + parser.arrayPos.push(pos)
  235 +}
  236 +
  237 +/**
  238 + * Parse chunked redis array response
  239 + * @param parser
  240 + * @returns {*}
  241 + */
  242 +function parseArrayChunks (parser) {
  243 + var tmp = parser.arrayCache.pop()
  244 + var pos = parser.arrayPos.pop()
  245 + if (parser.arrayCache.length) {
  246 + var res = parseArrayChunks(parser)
  247 + if (!res) {
  248 + pushArrayCache(parser, tmp, pos)
  249 + return
  250 + }
  251 + tmp[pos++] = res
  252 + }
  253 + return parseArrayElements(parser, tmp, pos)
  254 +}
  255 +
  256 +/**
  257 + * Parse redis array response elements
  258 + * @param parser
  259 + * @param responses
  260 + * @param i
  261 + * @returns {*}
  262 + */
  263 +function parseArrayElements (parser, responses, i) {
  264 + var bufferLength = parser.buffer.length
  265 + while (i < responses.length) {
  266 + var offset = parser.offset
  267 + if (parser.offset >= bufferLength) {
  268 + pushArrayCache(parser, responses, i)
  269 + return
  270 + }
  271 + var response = parseType(parser, parser.buffer[parser.offset++])
  272 + if (response === undefined) {
  273 + if (!parser.arrayCache.length) {
  274 + parser.offset = offset
  275 + }
  276 + pushArrayCache(parser, responses, i)
  277 + return
  278 + }
  279 + responses[i] = response
  280 + i++
  281 + }
  282 +
  283 + return responses
  284 +}
  285 +
  286 +/**
  287 + * Called the appropriate parser for the specified type.
  288 + * @param parser
  289 + * @param type
  290 + * @returns {*}
  291 + */
  292 +function parseType (parser, type) {
  293 + switch (type) {
  294 + case 36: // $
  295 + return parseBulkString(parser)
  296 + case 58: // :
  297 + return parseInteger(parser)
  298 + case 43: // +
  299 + return parseSimpleString(parser)
  300 + case 42: // *
  301 + return parseArray(parser)
  302 + case 45: // -
  303 + return parseError(parser)
  304 + default:
  305 + return handleError(parser, new ParserError(
  306 + 'Protocol error, got ' + JSON.stringify(String.fromCharCode(type)) + ' as reply type byte',
  307 + JSON.stringify(parser.buffer),
  308 + parser.offset
  309 + ))
  310 + }
  311 +}
  312 +
  313 +// All allowed options including their typeof value
  314 +var optionTypes = {
  315 + returnError: 'function',
  316 + returnFatalError: 'function',
  317 + returnReply: 'function',
  318 + returnBuffers: 'boolean',
  319 + stringNumbers: 'boolean',
  320 + name: 'string'
  321 +}
  322 +
  323 +/**
  324 + * Javascript Redis Parser
  325 + * @param options
  326 + * @constructor
  327 + */
  328 +function JavascriptRedisParser (options) {
  329 + if (!(this instanceof JavascriptRedisParser)) {
  330 + return new JavascriptRedisParser(options)
  331 + }
  332 + if (!options || !options.returnError || !options.returnReply) {
  333 + throw new TypeError('Please provide all return functions while initiating the parser')
  334 + }
  335 + for (var key in options) {
  336 + // eslint-disable-next-line valid-typeof
  337 + if (optionTypes.hasOwnProperty(key) && typeof options[key] !== optionTypes[key]) {
  338 + throw new TypeError('The options argument contains the property "' + key + '" that is either unknown or of a wrong type')
  339 + }
  340 + }
  341 + if (options.name === 'hiredis') {
  342 + /* istanbul ignore next: hiredis is only supported for legacy usage */
  343 + try {
  344 + var Hiredis = require('./hiredis')
  345 + console.error(new TypeError('Using hiredis is discouraged. Please use the faster JS parser by removing the name option.').stack.replace('Error', 'Warning'))
  346 + return new Hiredis(options)
  347 + } catch (e) {
  348 + console.error(new TypeError('Hiredis is not installed. Please remove the `name` option. The (faster) JS parser is used instead.').stack.replace('Error', 'Warning'))
  349 + }
  350 + }
  351 + this.optionReturnBuffers = !!options.returnBuffers
  352 + this.optionStringNumbers = !!options.stringNumbers
  353 + this.returnError = options.returnError
  354 + this.returnFatalError = options.returnFatalError || options.returnError
  355 + this.returnReply = options.returnReply
  356 + this.name = 'javascript'
  357 + this.reset()
  358 +}
  359 +
  360 +/**
  361 + * Reset the parser values to the initial state
  362 + *
  363 + * @returns {undefined}
  364 + */
  365 +JavascriptRedisParser.prototype.reset = function () {
  366 + this.offset = 0
  367 + this.buffer = null
  368 + this.bigStrSize = 0
  369 + this.bigOffset = 0
  370 + this.totalChunkSize = 0
  371 + this.bufferCache = []
  372 + this.arrayCache = []
  373 + this.arrayPos = []
  374 +}
  375 +
  376 +/**
  377 + * Set the returnBuffers option
  378 + *
  379 + * @param returnBuffers
  380 + * @returns {undefined}
  381 + */
  382 +JavascriptRedisParser.prototype.setReturnBuffers = function (returnBuffers) {
  383 + if (typeof returnBuffers !== 'boolean') {
  384 + throw new TypeError('The returnBuffers argument has to be a boolean')
  385 + }
  386 + this.optionReturnBuffers = returnBuffers
  387 +}
  388 +
  389 +/**
  390 + * Set the stringNumbers option
  391 + *
  392 + * @param stringNumbers
  393 + * @returns {undefined}
  394 + */
  395 +JavascriptRedisParser.prototype.setStringNumbers = function (stringNumbers) {
  396 + if (typeof stringNumbers !== 'boolean') {
  397 + throw new TypeError('The stringNumbers argument has to be a boolean')
  398 + }
  399 + this.optionStringNumbers = stringNumbers
  400 +}
  401 +
  402 +/**
  403 + * Decrease the bufferPool size over time
  404 + * @returns {undefined}
  405 + */
  406 +function decreaseBufferPool () {
  407 + if (bufferPool.length > 50 * 1024) {
  408 + // Balance between increasing and decreasing the bufferPool
  409 + if (counter === 1 || notDecreased > counter * 2) {
  410 + // Decrease the bufferPool by 10% by removing the first 10% of the current pool
  411 + var sliceLength = Math.floor(bufferPool.length / 10)
  412 + if (bufferOffset <= sliceLength) {
  413 + bufferOffset = 0
  414 + } else {
  415 + bufferOffset -= sliceLength
  416 + }
  417 + bufferPool = bufferPool.slice(sliceLength, bufferPool.length)
  418 + } else {
  419 + notDecreased++
  420 + counter--
  421 + }
  422 + } else {
  423 + clearInterval(interval)
  424 + counter = 0
  425 + notDecreased = 0
  426 + interval = null
  427 + }
  428 +}
  429 +
  430 +/**
  431 + * Check if the requested size fits in the current bufferPool.
  432 + * If it does not, reset and increase the bufferPool accordingly.
  433 + *
  434 + * @param length
  435 + * @returns {undefined}
  436 + */
  437 +function resizeBuffer (length) {
  438 + if (bufferPool.length < length + bufferOffset) {
  439 + var multiplier = length > 1024 * 1024 * 75 ? 2 : 3
  440 + if (bufferOffset > 1024 * 1024 * 111) {
  441 + bufferOffset = 1024 * 1024 * 50
  442 + }
  443 + bufferPool = bufferAlloc(length * multiplier + bufferOffset)
  444 + bufferOffset = 0
  445 + counter++
  446 + if (interval === null) {
  447 + interval = setInterval(decreaseBufferPool, 50)
  448 + }
  449 + }
  450 +}
  451 +
  452 +/**
  453 + * Concat a bulk string containing multiple chunks
  454 + *
  455 + * Notes:
  456 + * 1) The first chunk might contain the whole bulk string including the \r
  457 + * 2) We are only safe to fully add up elements that are neither the first nor any of the last two elements
  458 + *
  459 + * @param parser
  460 + * @returns {String}
  461 + */
  462 +function concatBulkString (parser) {
  463 + var list = parser.bufferCache
  464 + var chunks = list.length
  465 + var offset = parser.bigStrSize - parser.totalChunkSize
  466 + parser.offset = offset
  467 + if (offset <= 2) {
  468 + if (chunks === 2) {
  469 + return list[0].toString('utf8', parser.bigOffset, list[0].length + offset - 2)
  470 + }
  471 + chunks--
  472 + offset = list[list.length - 2].length + offset
  473 + }
  474 + var res = decoder.write(list[0].slice(parser.bigOffset))
  475 + for (var i = 1; i < chunks - 1; i++) {
  476 + res += decoder.write(list[i])
  477 + }
  478 + res += decoder.end(list[i].slice(0, offset - 2))
  479 + return res
  480 +}
  481 +
  482 +/**
  483 + * Concat the collected chunks from parser.bufferCache.
  484 + *
  485 + * Increases the bufferPool size beforehand if necessary.
  486 + *
  487 + * @param parser
  488 + * @returns {Buffer}
  489 + */
  490 +function concatBulkBuffer (parser) {
  491 + var list = parser.bufferCache
  492 + var chunks = list.length
  493 + var length = parser.bigStrSize - parser.bigOffset - 2
  494 + var offset = parser.bigStrSize - parser.totalChunkSize
  495 + parser.offset = offset
  496 + if (offset <= 2) {
  497 + if (chunks === 2) {
  498 + return list[0].slice(parser.bigOffset, list[0].length + offset - 2)
  499 + }
  500 + chunks--
  501 + offset = list[list.length - 2].length + offset
  502 + }
  503 + resizeBuffer(length)
  504 + var start = bufferOffset
  505 + list[0].copy(bufferPool, start, parser.bigOffset, list[0].length)
  506 + bufferOffset += list[0].length - parser.bigOffset
  507 + for (var i = 1; i < chunks - 1; i++) {
  508 + list[i].copy(bufferPool, bufferOffset)
  509 + bufferOffset += list[i].length
  510 + }
  511 + list[i].copy(bufferPool, bufferOffset, 0, offset - 2)
  512 + bufferOffset += offset - 2
  513 + return bufferPool.slice(start, bufferOffset)
  514 +}
  515 +
  516 +/**
  517 + * Parse the redis buffer
  518 + * @param buffer
  519 + * @returns {undefined}
  520 + */
  521 +JavascriptRedisParser.prototype.execute = function execute (buffer) {
  522 + if (this.buffer === null) {
  523 + this.buffer = buffer
  524 + this.offset = 0
  525 + } else if (this.bigStrSize === 0) {
  526 + var oldLength = this.buffer.length
  527 + var remainingLength = oldLength - this.offset
  528 + var newBuffer = bufferAlloc(remainingLength + buffer.length)
  529 + this.buffer.copy(newBuffer, 0, this.offset, oldLength)
  530 + buffer.copy(newBuffer, remainingLength, 0, buffer.length)
  531 + this.buffer = newBuffer
  532 + this.offset = 0
  533 + if (this.arrayCache.length) {
  534 + var arr = parseArrayChunks(this)
  535 + if (!arr) {
  536 + return
  537 + }
  538 + this.returnReply(arr)
  539 + }
  540 + } else if (this.totalChunkSize + buffer.length >= this.bigStrSize) {
  541 + this.bufferCache.push(buffer)
  542 + var tmp = this.optionReturnBuffers ? concatBulkBuffer(this) : concatBulkString(this)
  543 + this.bigStrSize = 0
  544 + this.bufferCache = []
  545 + this.buffer = buffer
  546 + if (this.arrayCache.length) {
  547 + this.arrayCache[0][this.arrayPos[0]++] = tmp
  548 + tmp = parseArrayChunks(this)
  549 + if (!tmp) {
  550 + return
  551 + }
  552 + }
  553 + this.returnReply(tmp)
  554 + } else {
  555 + this.bufferCache.push(buffer)
  556 + this.totalChunkSize += buffer.length
  557 + return
  558 + }
  559 +
  560 + while (this.offset < this.buffer.length) {
  561 + var offset = this.offset
  562 + var type = this.buffer[this.offset++]
  563 + var response = parseType(this, type)
  564 + if (response === undefined) {
  565 + if (!this.arrayCache.length) {
  566 + this.offset = offset
  567 + }
  568 + return
  569 + }
  570 +
  571 + if (type === 45) {
  572 + this.returnError(response)
  573 + } else {
  574 + this.returnReply(response)
  575 + }
  576 + }
  577 +
  578 + this.buffer = null
  579 +}
  580 +
  581 +module.exports = JavascriptRedisParser
  1 +'use strict'
  2 +
  3 +var util = require('util')
  4 +var assert = require('assert')
  5 +var RedisError = require('./redisError')
  6 +var ADD_STACKTRACE = false
  7 +
  8 +function ParserError (message, buffer, offset) {
  9 + assert(buffer)
  10 + assert.strictEqual(typeof offset, 'number')
  11 + RedisError.call(this, message, ADD_STACKTRACE)
  12 + this.offset = offset
  13 + this.buffer = buffer
  14 + Error.captureStackTrace(this, ParserError)
  15 +}
  16 +
  17 +util.inherits(ParserError, RedisError)
  18 +
  19 +Object.defineProperty(ParserError.prototype, 'name', {
  20 + value: 'ParserError',
  21 + configurable: true,
  22 + writable: true
  23 +})
  24 +
  25 +module.exports = ParserError
  1 +'use strict'
  2 +
  3 +var util = require('util')
  4 +
  5 +function RedisError (message, stack) {
  6 + Object.defineProperty(this, 'message', {
  7 + value: message || '',
  8 + configurable: true,
  9 + writable: true
  10 + })
  11 + if (stack || stack === undefined) {
  12 + Error.captureStackTrace(this, RedisError)
  13 + }
  14 +}
  15 +
  16 +util.inherits(RedisError, Error)
  17 +
  18 +Object.defineProperty(RedisError.prototype, 'name', {
  19 + value: 'RedisError',
  20 + configurable: true,
  21 + writable: true
  22 +})
  23 +
  24 +module.exports = RedisError
  1 +'use strict'
  2 +
  3 +var util = require('util')
  4 +var RedisError = require('./redisError')
  5 +var ADD_STACKTRACE = false
  6 +
  7 +function ReplyError (message) {
  8 + var tmp = Error.stackTraceLimit
  9 + Error.stackTraceLimit = 2
  10 + RedisError.call(this, message, ADD_STACKTRACE)
  11 + Error.captureStackTrace(this, ReplyError)
  12 + Error.stackTraceLimit = tmp
  13 +}
  14 +
  15 +util.inherits(ReplyError, RedisError)
  16 +
  17 +Object.defineProperty(ReplyError.prototype, 'name', {
  18 + value: 'ReplyError',
  19 + configurable: true,
  20 + writable: true
  21 +})
  22 +
  23 +module.exports = ReplyError
  1 +{
  2 + "_args": [
  3 + [
  4 + {
  5 + "raw": "redis-parser@^2.6.0",
  6 + "scope": null,
  7 + "escapedName": "redis-parser",
  8 + "name": "redis-parser",
  9 + "rawSpec": "^2.6.0",
  10 + "spec": ">=2.6.0 <3.0.0",
  11 + "type": "range"
  12 + },
  13 + "/Users/fzy/project/koa2_Sequelize_project/node_modules/redis"
  14 + ]
  15 + ],
  16 + "_from": "redis-parser@>=2.6.0 <3.0.0",
  17 + "_id": "redis-parser@2.6.0",
  18 + "_inCache": true,
  19 + "_location": "/redis-parser",
  20 + "_nodeVersion": "7.7.3",
  21 + "_npmOperationalInternal": {
  22 + "host": "packages-12-west.internal.npmjs.com",
  23 + "tmp": "tmp/redis-parser-2.6.0.tgz_1491263124772_0.7531374620739371"
  24 + },
  25 + "_npmUser": {
  26 + "name": "bridgear",
  27 + "email": "ruben@bridgewater.de"
  28 + },
  29 + "_npmVersion": "4.1.2",
  30 + "_phantomChildren": {},
  31 + "_requested": {
  32 + "raw": "redis-parser@^2.6.0",
  33 + "scope": null,
  34 + "escapedName": "redis-parser",
  35 + "name": "redis-parser",
  36 + "rawSpec": "^2.6.0",
  37 + "spec": ">=2.6.0 <3.0.0",
  38 + "type": "range"
  39 + },
  40 + "_requiredBy": [
  41 + "/redis"
  42 + ],
  43 + "_resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
  44 + "_shasum": "52ed09dacac108f1a631c07e9b69941e7a19504b",
  45 + "_shrinkwrap": null,
  46 + "_spec": "redis-parser@^2.6.0",
  47 + "_where": "/Users/fzy/project/koa2_Sequelize_project/node_modules/redis",
  48 + "author": {
  49 + "name": "Ruben Bridgewater"
  50 + },
  51 + "bugs": {
  52 + "url": "https://github.com/NodeRedis/node-redis-parser/issues"
  53 + },
  54 + "dependencies": {},
  55 + "description": "Javascript Redis protocol (RESP) parser",
  56 + "devDependencies": {
  57 + "benchmark": "^2.1.0",
  58 + "codeclimate-test-reporter": "^0.4.0",
  59 + "hiredis": "^0.5.0",
  60 + "intercept-stdout": "^0.1.2",
  61 + "istanbul": "^0.4.0",
  62 + "mocha": "^3.1.2",
  63 + "standard": "^9.0.0"
  64 + },
  65 + "directories": {
  66 + "test": "test",
  67 + "lib": "lib"
  68 + },
  69 + "dist": {
  70 + "shasum": "52ed09dacac108f1a631c07e9b69941e7a19504b",
  71 + "tarball": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz"
  72 + },
  73 + "engines": {
  74 + "node": ">=0.10.0"
  75 + },
  76 + "gitHead": "eea04cad0c4f53fd1e9f7079b5f4ededf50f5945",
  77 + "homepage": "https://github.com/NodeRedis/node-redis-parser#readme",
  78 + "keywords": [
  79 + "redis",
  80 + "protocol",
  81 + "parser",
  82 + "database",
  83 + "javascript",
  84 + "node",
  85 + "nodejs",
  86 + "resp",
  87 + "hiredis"
  88 + ],
  89 + "license": "MIT",
  90 + "main": "index.js",
  91 + "maintainers": [
  92 + {
  93 + "name": "bridgear",
  94 + "email": "ruben@bridgewater.de"
  95 + }
  96 + ],
  97 + "name": "redis-parser",
  98 + "optionalDependencies": {},
  99 + "readme": "[![Build Status](https://travis-ci.org/NodeRedis/node-redis-parser.png?branch=master)](https://travis-ci.org/NodeRedis/node-redis-parser)\n[![Test Coverage](https://codeclimate.com/github/NodeRedis/node-redis-parser/badges/coverage.svg)](https://codeclimate.com/github/NodeRedis/node-redis-parser/coverage)\n[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)\n\n# redis-parser\n\nA high performance javascript redis parser built for [node_redis](https://github.com/NodeRedis/node_redis) and [ioredis](https://github.com/luin/ioredis). Parses all [RESP](http://redis.io/topics/protocol) data.\n\n## Install\n\nInstall with [NPM](https://npmjs.org/):\n\n npm install redis-parser\n\n## Usage\n\n```js\nvar Parser = require('redis-parser');\n\nvar myParser = new Parser(options);\n```\n\n### Options\n\n* `returnReply`: *function*; mandatory\n* `returnError`: *function*; mandatory\n* `returnFatalError`: *function*; optional, defaults to the returnError function\n* `returnBuffers`: *boolean*; optional, defaults to false\n* `stringNumbers`: *boolean*; optional, defaults to false\n\n### Functions\n\n* `reset()`: reset the parser to it's initial state\n* `setReturnBuffers(boolean)`: (JSParser only) set the returnBuffers option on/off without resetting the parser\n* `setStringNumbers(boolean)`: (JSParser only) set the stringNumbers option on/off without resetting the parser\n\n### Error classes\n\n* `RedisError` sub class of Error\n* `ReplyError` sub class of RedisError\n* `ParserError` sub class of RedisError\n\nAll Redis errors will be returned as `ReplyErrors` while a parser error is returned as `ParserError`. \nAll error classes are exported by the parser.\n\n### Example\n\n```js\nvar Parser = require(\"redis-parser\");\n\nfunction Library () {}\n\nLibrary.prototype.returnReply = function (reply) { ... }\nLibrary.prototype.returnError = function (err) { ... }\nLibrary.prototype.returnFatalError = function (err) { ... }\n\nvar lib = new Library();\n\nvar parser = new Parser({\n returnReply: function(reply) {\n lib.returnReply(reply);\n },\n returnError: function(err) {\n lib.returnError(err);\n },\n returnFatalError: function (err) {\n lib.returnFatalError(err);\n }\n});\n\nLibrary.prototype.streamHandler = function () {\n this.stream.on('data', function (buffer) {\n // Here the data (e.g. `new Buffer('$5\\r\\nHello\\r\\n'`)) is passed to the parser and the result is passed to either function depending on the provided data.\n parser.execute(buffer);\n });\n};\n```\nYou do not have to use the returnFatalError function. Fatal errors will be returned in the normal error function in that case.\n\nAnd if you want to return buffers instead of strings, you can do this by adding the `returnBuffers` option.\n\nIf you handle with big numbers that are to large for JS (Number.MAX_SAFE_INTEGER === 2^53 - 16) please use the `stringNumbers` option. That way all numbers are going to be returned as String and you can handle them safely.\n\n```js\n// Same functions as in the first example\n\nvar parser = new Parser({\n returnReply: function(reply) {\n lib.returnReply(reply);\n },\n returnError: function(err) {\n lib.returnError(err);\n },\n returnBuffers: true, // All strings are returned as Buffer e.g. <Buffer 48 65 6c 6c 6f>\n stringNumbers: true // All numbers are returned as String\n});\n\n// The streamHandler as above\n```\n\n## Protocol errors\n\nTo handle protocol errors (this is very unlikely to happen) gracefully you should add the returnFatalError option, reject any still running command (they might have been processed properly but the reply is just wrong), destroy the socket and reconnect. Note that while doing this no new command may be added, so all new commands have to be buffered in the meantime, otherwise a chunk might still contain partial data of a following command that was already processed properly but answered in the same chunk as the command that resulted in the protocol error.\n\n## Contribute\n\nThe parser is highly optimized but there may still be further optimizations possible.\n\n npm install\n npm test\n npm run benchmark\n\nCurrently the benchmark compares the performance against the hiredis parser:\n\n HIREDIS: $ multiple chunks in a bulk string x 859,880 ops/sec ±1.22% (82 runs sampled)\n HIREDIS BUF: $ multiple chunks in a bulk string x 608,869 ops/sec ±1.72% (85 runs sampled)\n JS PARSER: $ multiple chunks in a bulk string x 910,590 ops/sec ±0.87% (89 runs sampled)\n JS PARSER BUF: $ multiple chunks in a bulk string x 1,299,507 ops/sec ±2.18% (84 runs sampled)\n\n HIREDIS: + multiple chunks in a string x 1,787,203 ops/sec ±0.58% (96 runs sampled)\n HIREDIS BUF: + multiple chunks in a string x 943,584 ops/sec ±1.62% (87 runs sampled)\n JS PARSER: + multiple chunks in a string x 2,008,264 ops/sec ±1.01% (91 runs sampled)\n JS PARSER BUF: + multiple chunks in a string x 2,045,546 ops/sec ±0.78% (91 runs sampled)\n\n HIREDIS: $ 4mb bulk string x 310 ops/sec ±1.58% (75 runs sampled)\n HIREDIS BUF: $ 4mb bulk string x 471 ops/sec ±2.28% (78 runs sampled)\n JS PARSER: $ 4mb bulk string x 747 ops/sec ±2.43% (85 runs sampled)\n JS PARSER BUF: $ 4mb bulk string x 846 ops/sec ±5.52% (72 runs sampled)\n\n HIREDIS: + simple string x 2,324,866 ops/sec ±1.61% (90 runs sampled)\n HIREDIS BUF: + simple string x 1,085,823 ops/sec ±2.47% (82 runs sampled)\n JS PARSER: + simple string x 4,567,358 ops/sec ±1.97% (81 runs sampled)\n JS PARSER BUF: + simple string x 5,433,901 ops/sec ±0.66% (93 runs sampled)\n\n HIREDIS: : integer x 2,332,946 ops/sec ±0.47% (93 runs sampled)\n JS PARSER: : integer x 17,730,449 ops/sec ±0.73% (91 runs sampled)\n JS PARSER STR: : integer x 12,942,037 ops/sec ±0.51% (92 runs sampled)\n\n HIREDIS: : big integer x 2,012,572 ops/sec ±0.33% (93 runs sampled)\n JS PARSER: : big integer x 10,210,923 ops/sec ±0.94% (94 runs sampled)\n JS PARSER STR: : big integer x 4,453,320 ops/sec ±0.52% (94 runs sampled)\n\n HIREDIS: * array x 44,479 ops/sec ±0.55% (94 runs sampled)\n HIREDIS BUF: * array x 14,391 ops/sec ±1.04% (86 runs sampled)\n JS PARSER: * array x 53,796 ops/sec ±2.08% (79 runs sampled)\n JS PARSER BUF: * array x 72,428 ops/sec ±0.72% (93 runs sampled)\n\n HIREDIS: * big nested array x 217 ops/sec ±0.97% (83 runs sampled)\n HIREDIS BUF: * big nested array x 255 ops/sec ±2.28% (77 runs sampled)\n JS PARSER: * big nested array x 242 ops/sec ±1.10% (85 runs sampled)\n JS PARSER BUF: * big nested array x 375 ops/sec ±1.21% (88 runs sampled)\n\n HIREDIS: - error x 78,821 ops/sec ±0.80% (93 runs sampled)\n JS PARSER: - error x 143,382 ops/sec ±0.75% (92 runs sampled)\n\n Platform info:\n Ubuntu 16.10\n Node.js 7.4.0\n Intel(R) Core(TM) i7-5600U CPU\n\n## License\n\n[MIT](./LICENSE)\n",
  100 + "readmeFilename": "README.md",
  101 + "repository": {
  102 + "type": "git",
  103 + "url": "git+https://github.com/NodeRedis/node-redis-parser.git"
  104 + },
  105 + "scripts": {
  106 + "benchmark": "node ./benchmark",
  107 + "coverage": "node ./node_modules/istanbul/lib/cli.js cover --preserve-comments ./node_modules/mocha/bin/_mocha -- -R spec",
  108 + "coverage:check": "node ./node_modules/istanbul/lib/cli.js check-coverage --branch 100 --statement 100",
  109 + "lint": "standard --fix",
  110 + "posttest": "npm run lint && npm run coverage:check",
  111 + "test": "npm run coverage"
  112 + },
  113 + "version": "2.6.0"
  114 +}
  1 +node_modules/**
  2 +coverage/**
  3 +**.md
  4 +**.log
  1 +env:
  2 + node: true
  3 + es6: false
  4 +
  5 +rules:
  6 + # Possible Errors
  7 + # http://eslint.org/docs/rules/#possible-errors
  8 + comma-dangle: [2, "only-multiline"]
  9 + no-constant-condition: 2
  10 + no-control-regex: 2
  11 + no-debugger: 2
  12 + no-dupe-args: 2
  13 + no-dupe-keys: 2
  14 + no-duplicate-case: 2
  15 + no-empty: 2
  16 + no-empty-character-class: 2
  17 + no-ex-assign: 2
  18 + no-extra-boolean-cast : 2
  19 + no-extra-parens: [2, "functions"]
  20 + no-extra-semi: 2
  21 + no-func-assign: 2
  22 + no-invalid-regexp: 2
  23 + no-irregular-whitespace: 2
  24 + no-negated-in-lhs: 2
  25 + no-obj-calls: 2
  26 + no-regex-spaces: 2
  27 + no-sparse-arrays: 2
  28 + no-inner-declarations: 2
  29 + no-unexpected-multiline: 2
  30 + no-unreachable: 2
  31 + use-isnan: 2
  32 + valid-typeof: 2
  33 +
  34 + # Best Practices
  35 + # http://eslint.org/docs/rules/#best-practices
  36 + array-callback-return: 2
  37 + block-scoped-var: 2
  38 + dot-notation: 2
  39 + eqeqeq: 2
  40 + no-else-return: 2
  41 + no-extend-native: 2
  42 + no-floating-decimal: 2
  43 + no-extra-bind: 2
  44 + no-fallthrough: 2
  45 + no-labels: 2
  46 + no-lone-blocks: 2
  47 + no-loop-func: 2
  48 + no-multi-spaces: 2
  49 + no-multi-str: 2
  50 + no-native-reassign: 2
  51 + no-new-wrappers: 2
  52 + no-octal: 2
  53 + no-proto: 2
  54 + no-redeclare: 2
  55 + no-return-assign: 2
  56 + no-self-assign: 2
  57 + no-self-compare: 2
  58 + no-sequences: 2
  59 + no-throw-literal: 2
  60 + no-useless-call: 2
  61 + no-useless-concat: 2
  62 + no-useless-escape: 2
  63 + no-void: 2
  64 + no-unmodified-loop-condition: 2
  65 + yoda: 2
  66 +
  67 + # Strict Mode
  68 + # http://eslint.org/docs/rules/#strict-mode
  69 + strict: [2, "global"]
  70 +
  71 + # Variables
  72 + # http://eslint.org/docs/rules/#variables
  73 + no-delete-var: 2
  74 + no-shadow-restricted-names: 2
  75 + no-undef: 2
  76 + no-unused-vars: [2, {"args": "none"}]
  77 +
  78 + # http://eslint.org/docs/rules/#nodejs-and-commonjs
  79 + no-mixed-requires: 2
  80 + no-new-require: 2
  81 + no-path-concat: 2
  82 +
  83 + # Stylistic Issues
  84 + # http://eslint.org/docs/rules/#stylistic-issues
  85 + comma-spacing: 2
  86 + eol-last: 2
  87 + indent: [2, 4, {SwitchCase: 2}]
  88 + keyword-spacing: 2
  89 + max-len: [2, 200, 2]
  90 + new-parens: 2
  91 + no-mixed-spaces-and-tabs: 2
  92 + no-multiple-empty-lines: [2, {max: 2}]
  93 + no-trailing-spaces: 2
  94 + quotes: [2, "single", "avoid-escape"]
  95 + semi: 2
  96 + space-before-blocks: [2, "always"]
  97 + space-before-function-paren: [2, "always"]
  98 + space-in-parens: [2, "never"]
  99 + space-infix-ops: 2
  100 + space-unary-ops: 2
  101 +
  102 +globals:
  103 + it: true
  104 + describe: true
  105 + before: true
  106 + after: true
  107 + beforeEach: true
  108 + afterEach: true
  1 +_Thanks for wanting to report an issue you've found in node_redis. Please delete
  2 +this text and fill in the template below. Please note that the issue tracker is only
  3 +for bug reports or feature requests. If you have a question, please ask that on [gitter].
  4 +If unsure about something, just do as best as you're able._
  5 +
  6 +_Note that it will be much easier to fix the issue if a test case that reproduces
  7 +the problem is provided. It is of course not always possible to reduce your code
  8 +to a small test case, but it's highly appreciated to have as much data as possible.
  9 +Thank you!_
  10 +
  11 +* **Version**: What node_redis and what redis version is the issue happening on?
  12 +* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0 on Windows 7 / Ubuntu 15.10 / Azure)
  13 +* **Description**: Description of your issue, stack traces from errors and code that reproduces the issue
  14 +
  15 +[gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
  1 +### Pull Request check-list
  2 +
  3 +_Please make sure to review and check all of these items:_
  4 +
  5 +- [ ] Does `npm test` pass with this change (including linting)?
  6 +- [ ] Is the new or changed code fully tested?
  7 +- [ ] Is a documentation update included (if this change modifies existing APIs, or introduces new ones)?
  8 +
  9 +_NOTE: these things are not required to open a PR and can be done
  10 +afterwards / while the PR is open._
  11 +
  12 +### Description of change
  13 +
  14 +_Please provide a description of the change here._
  1 +examples/
  2 +benchmarks/
  3 +test/
  4 +.nyc_output/
  5 +coverage/
  6 +.tern-port
  7 +*.log
  8 +*.rdb
  9 +*.out
  10 +*.yml
  1 +LICENSE - "MIT License"
  2 +
  3 +Copyright (c) 2016 by NodeRedis
  4 +
  5 +Permission is hereby granted, free of charge, to any person
  6 +obtaining a copy of this software and associated documentation
  7 +files (the "Software"), to deal in the Software without
  8 +restriction, including without limitation the rights to use,
  9 +copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +copies of the Software, and to permit persons to whom the
  11 +Software is furnished to do so, subject to the following
  12 +conditions:
  13 +
  14 +The above copyright notice and this permission notice shall be
  15 +included in all copies or substantial portions of the Software.
  16 +
  17 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19 +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21 +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22 +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23 +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24 +OTHER DEALINGS IN THE SOFTWARE.