All files / json-pack/src/rm RmRecordEncoder.ts

73.8% Statements 31/42
55.55% Branches 5/9
100% Functions 8/8
73.8% Lines 31/42

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 865x     5x 5x   5x 33x     5x 5x       22x 22x       38x       24x 24x 24x 24x 24x 24x                       2x 2x 2x                 3x 3x 3x 3x                         3x 3x 3x 3x 3x 3x 3x 3x                    
import {Writer} from '@jsonjoy.com/buffers/lib/Writer';
import type {IWriter, IWriterGrowable} from '@jsonjoy.com/buffers';
 
const RM_HEADER_SIZE = 4;
const MAX_SINGLE_FRAME_SIZE = 0x7fffffff;
 
export class RmRecordEncoder<W extends IWriter & IWriterGrowable = IWriter & IWriterGrowable> {
  constructor(public readonly writer: W = new Writer() as any) {}
 
  public encodeHdr(fin: 0 | 1, length: number): Uint8Array {
    this.writeHdr(fin, length);
    return this.writer.flush();
  }
 
  public encodeRecord(record: Uint8Array): Uint8Array {
    this.writeRecord(record);
    return this.writer.flush();
  }
 
  public writeHdr(fin: 0 | 1, length: number): void {
    this.writer.u32((fin ? 0b10000000_00000000_00000000_00000000 : 0) + length);
  }
 
  public writeRecord(record: Uint8Array): void {
    const length = record.length;
    Eif (length <= 2147483647) {
      const writer = this.writer;
      writer.u32(0b10000000_00000000_00000000_00000000 + length);
      writer.buf(record, length);
      return;
    }
    let offset = 0;
    while (offset < length) {
      const fragmentLength = Math.min(length - offset, 0x7fffffff);
      const fin = fragmentLength + offset >= length ? 1 : 0;
      this.writeFragment(record, offset, fragmentLength, fin);
      offset += fragmentLength;
    }
  }
 
  public writeFragment(record: Uint8Array, offset: number, length: number, fin: 0 | 1): void {
    this.writeHdr(fin, length);
    const fragment = record.subarray(offset, offset + length);
    this.writer.buf(fragment, length);
  }
 
  /**
   * To write an RM record in one pass this method reserves space for the RM
   * header, and returns the state, which needs to passed to `endRmRecord` to
   * finalize the RM header.
   */
  public startRecord(): number {
    const writer = this.writer;
    const rmHeaderPosition = writer.x - writer.x0;
    writer.x += RM_HEADER_SIZE;
    return rmHeaderPosition;
  }
 
  /**
   * Finalize the RM header started by `startRmRecord`.
   *
   * @param rmHeaderPosition The position returned by `startRmRecord`
   * @remarks This method will check if the data written after `startRmRecord`
   * fits into a single RM frame. If it does, it will write the RM header in
   * place. If it doesn't, it will move the data to a new location and write
   * it as multiple RM frames.
   */
  public endRecord(rmHeaderPosition: number): void {
    const writer = this.writer;
    const rmFrameStart = writer.x0 + rmHeaderPosition;
    const totalSize = writer.x - rmFrameStart - RM_HEADER_SIZE;
    if (totalSize <= MAX_SINGLE_FRAME_SIZE) {
      const currentX = writer.x;
      writer.x = rmFrameStart;
      this.writeHdr(1, totalSize);
      writer.x = currentX;
    } else E{
      const currentX = writer.x;
      writer.x = rmFrameStart;
      const data = writer.uint8.subarray(rmFrameStart + RM_HEADER_SIZE, currentX);
      writer.reset();
      this.writeRecord(data);
    }
  }
}