# Code coverage report for lib/parser/javascript.js

## Statements: 95.83% (115 / 120) Branches: 92% (46 / 50) Functions: 100% (9 / 9) Lines: 95.83% (115 / 120) Ignored: 1 statement, 1 branch

All files » lib/parser/ » javascript.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222

    13

    13
    1727
    1727

    13
    474
    474

    474
    474
    474

    13
    4
    4

    13

    13
    3138

    3138

    1091
    1091

    1091

    1091

    1091
    44
    1047
    388

    659
    2047

    320
    320

    320

    320

    320
    1727

    1452

    1452

    1452
    64

    1388
    1388

    1388

    1388
    1
    1

    1387
    300

    1087

    275
    275

    275
    3

    272
    3
    3

    269
    269

    269

    269
    1043

    1043

    1043
    1040

    266

    13
    1350

    1350

    1350
    3441

    3441
    1346

    2095
    2095

    2095
    1030

    1030
    1065
    39

    39
    1026
    247

    247
    779
    556

    556
    223

    223

    223

    219

    4
    4

    13

    1350
    468

    468

    882
    878
    878

    878

    4
    4

    13
    1727

    1727

    1727

    13
    3138

    3138
    9432

    9432
    1

    3138
    3138

    13
    3713

    13
    13
     'use strict';

    var util   = require("util");

    function Packet(type, size) {
        this.type = type;
        this.size = +size;
    }

    function ReplyParser(return_buffers) {
        this.name = exports.name;
        this.return_buffers = return_buffers;

        this._buffer            = null;
        this._offset            = 0;
        this._encoding          = "utf-8";
    }

    function IncompleteReadBuffer(message) {
        this.name = "IncompleteReadBuffer";
        this.message = message;
    }
    util.inherits(IncompleteReadBuffer, Error);

    ReplyParser.prototype._parseResult = function (type) {
        var start, end, offset, packetHeader;

        if (type === 43 || type === 45) { // + or -
            // up to the delimiter
            end = this._packetEndOffset() - 1;
            start = this._offset;

            // include the delimiter
            this._offset = end + 2;

            Iif (end > this._buffer.length) {
                this._offset = start;
                throw new IncompleteReadBuffer("Wait for more data.");
            }

            if (type === 45) {
                return new Error(this._buffer.toString(this._encoding, start, end));
            } else if (this.return_buffers) {
                return this._buffer.slice(start, end);
            }
            return this._buffer.toString(this._encoding, start, end);
        } else if (type === 58) { // :
            // up to the delimiter
            end = this._packetEndOffset() - 1;
            start = this._offset;

            // include the delimiter
            this._offset = end + 2;

            Iif (end > this._buffer.length) {
                this._offset = start;
                throw new IncompleteReadBuffer("Wait for more data.");
            }

            // return the coerced numeric value
            return +this._buffer.toString('ascii', start, end);
        } else if (type === 36) { // $
            // set a rewind point, as the packet could be larger than the
            // buffer in memory
            offset = this._offset - 1;

            packetHeader = new Packet(type, this.parseHeader());

            // packets with a size of -1 are considered null
            if (packetHeader.size === -1) {
                return null;
            }

            end = this._offset + packetHeader.size;
            start = this._offset;

            // set the offset to after the delimiter
            this._offset = end + 2;

            if (end > this._buffer.length) {
                this._offset = offset;
                throw new IncompleteReadBuffer("Wait for more data.");
            }

            if (this.return_buffers) {
                return this._buffer.slice(start, end);
            }
            return this._buffer.toString(this._encoding, start, end);
        } else { // *
            offset = this._offset;
            packetHeader = new Packet(type, this.parseHeader());

            if (packetHeader.size < 0) {
                return null;
            }

            if (packetHeader.size > this._bytesRemaining()) {
                this._offset = offset - 1;
                throw new IncompleteReadBuffer("Wait for more data.");
            }

            var reply = [];
            var ntype, i, res;

            offset = this._offset - 1;

            for (i = 0; i < packetHeader.size; i++) {
                ntype = this._buffer[this._offset++];

                Iif (this._offset > this._buffer.length) {
                    throw new IncompleteReadBuffer("Wait for more data.");
                }
                res = this._parseResult(ntype);
                reply.push(res);
            }

            return reply;
        }
    };

    ReplyParser.prototype.execute = function (buffer) {
        this.append(buffer);

        var type, ret, offset;

        while (true) {
            offset = this._offset;
            // at least 4 bytes: :1\r\n
            if (this._bytesRemaining() < 4) {
                break;
            }

            try {
                type = this._buffer[this._offset++];

                if (type === 43) { // +
                    ret = this._parseResult(type);

                    this.send_reply(ret);
                } else  if (type === 45) { // -
                    ret = this._parseResult(type);

                    this.send_error(ret);
                } else if (type === 58) { // :
                    ret = this._parseResult(type);

                    this.send_reply(ret);
                } else if (type === 36) { // $
                    ret = this._parseResult(type);

                    this.send_reply(ret);
                } else Eif (type === 42) { // 42 *
                    // set a rewind point. if a failure occurs,
                    // wait for the next execute()/append() and try again
                    offset = this._offset - 1;

                    ret = this._parseResult(type);

                    this.send_reply(ret);
                }
            } catch (err) {
                // catch the error (not enough data), rewind, and wait
                // for the next packet to appear
                this._offset = offset;
                break;
            }
        }
    };

    ReplyParser.prototype.append = function (newBuffer) {

        // first run
        if (this._buffer === null) {
            this._buffer = newBuffer;

            return;
        }

        // out of data
        if (this._offset >= this._buffer.length) {
            this._buffer = newBuffer;
            this._offset = 0;

            return;
        }

        this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]);
        this._offset = 0;
    };

    ReplyParser.prototype.parseHeader = function () {
        var end   = this._packetEndOffset(),
            value = this._buffer.toString('ascii', this._offset, end - 1);

        this._offset = end + 1;

        return value;
    };

    ReplyParser.prototype._packetEndOffset = function () {
        var offset = this._offset;

        while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) {
            offset++;

            /* istanbul ignore if: activate the js parser out of memory test to test this */
            Iif (offset >= this._buffer.length) {
                throw new IncompleteReadBuffer("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")");
            }
        }

        offset++;
        return offset;
    };

    ReplyParser.prototype._bytesRemaining = function () {
        return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset);
    };

    exports.Parser = ReplyParser;
    exports.name = "javascript";