All files / json-pack/src/resp RespStreamingDecoder.ts

70.45% Statements 31/44
22.22% Branches 2/9
71.42% Functions 5/7
73.17% Lines 30/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 1152x 2x                                     2x 5x 5x                                     1580x                         1644x 1644x 1640x 1640x 1640x 155x 155x   1485x 1485x 1485x                         1x 1x 1x 1x 1x 1x 1x                               70x 70x 70x 70x 70x 70x 70x                  
import {StreamingReader} from '@jsonjoy.com/buffers/lib/StreamingReader';
import {RespDecoder} from './RespDecoder';
 
/**
 * Streaming decoder for RESP protocol. Can be used to decode data from
 * a stream where messages are arbitrary split into chunks.
 *
 * Example:
 *
 * ```ts
 * const decoder = new RespStreamingDecoder();
 *
 * decoder.push(new Uint8Array([43, 49, 13, 10]));
 *
 * let val;
 * while ((val = decoder.read()) !== undefined) {
 *   console.log(val);
 * }
 * ```
 */
export class RespStreamingDecoder {
  protected readonly reader = new StreamingReader();
  protected readonly decoder = new RespDecoder(this.reader);
 
  /**
   * When set to true, the decoder will attempt to decode RESP Bulk strings
   * (which are binary strings, i.e. Uint8Array) as UTF-8 strings. If the
   * string is not valid UTF-8, it will be returned as a Uint8Array.
   */
  public get tryUtf8(): boolean {
    return this.decoder.tryUtf8;
  }
  public set tryUtf8(value: boolean) {
    this.decoder.tryUtf8 = value;
  }
 
  /**
   * Add a chunk of data to be decoded.
   * @param uint8 `Uint8Array` chunk of data to be decoded.
   */
  public push(uint8: Uint8Array): void {
    this.reader.push(uint8);
  }
 
  /**
   * Decode one value from the stream. If `undefined` is returned, then
   * there is not enough data to decode or the stream is finished.
   *
   * There could be multiple values in the stream, so this method should be
   * called in a loop until `undefined` is returned.
   *
   * @return Decoded value or `undefined` if there is not enough data to decode.
   */
  public read(): unknown | undefined {
    const reader = this.reader;
    if (reader.size() === 0) return undefined;
    const x = reader.x;
    try {
      const val = this.decoder.readAny();
      reader.consume();
      return val;
    } catch (error) {
      if (error instanceof RangeError) {
        reader.x = x;
        return undefined;
      } else Ethrow error;
    }
  }
 
  /**
   * Decode only one RESP command from the stream, if the value is not a
   * command, an error will be thrown.
   *
   * @returns Redis command and its arguments or `undefined` if there is
   * not enough data to decode.
   */
  public readCmd(): [cmd: string, ...args: Uint8Array[]] | undefined {
    const reader = this.reader;
    Iif (reader.size() === 0) return undefined;
    const x = reader.x;
    try {
      const args = this.decoder.readCmd();
      reader.consume();
      return args;
    } catch (error) {
      if (error instanceof RangeError) {
        reader.x = x;
        return undefined;
      } else throw error;
    }
  }
 
  /**
   * Skips one value from the stream. If `undefined` is returned, then
   * there is not enough data to skip or the stream is finished.
   * @returns `null` if a value was skipped, `undefined` if there is not
   * enough data to skip.
   */
  public skip(): null | undefined {
    const reader = this.reader;
    Iif (reader.size() === 0) return undefined;
    const x = reader.x;
    try {
      this.decoder.skipAny();
      reader.consume();
      return null;
    } catch (error) {
      if (error instanceof RangeError) {
        reader.x = x;
        return undefined;
      } else throw error;
    }
  }
}