All files / json-crdt-patch/util/binary CrdtWriter.ts

100% Statements 129/129
100% Branches 34/34
100% Functions 3/3
100% Lines 127/127

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 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 223 224 225 226 227 228 229 230 231 232 233179x   179x                                                           958455x 73632x   884823x 884823x                                                             1234528x 728078x 506450x 343670x 343670x 343670x 343670x 162780x 4650x 4650x 4650x 4650x 4650x 158130x 789x 789x 789x 789x 789x 789x   157341x 157341x 157341x 157341x 235x 235x 235x 235x 235x 235x 235x 157106x 1118x 1118x 1118x 1118x 1118x 1118x 1118x 1118x 155988x 8415x 8415x 8415x 8415x 8415x 8415x 8415x 8415x 8415x   147573x 147573x 147573x 147573x 147573x 147573x 147573x 147573x 147573x 147573x                                                                     1208096x 1112153x   95943x 95943x 79782x 79782x 79782x 79782x 16161x 2188x 2188x 2188x 2188x 2188x 13973x 13845x 13845x 13845x 13845x 13845x 13845x   128x 128x 128x 128x 32x 32x 32x 32x 32x 32x 32x 96x 36x 36x 36x 36x 36x 36x 36x 36x 60x 36x 36x 36x 36x 36x 36x 36x 36x 36x   24x 24x 24x 24x 24x 24x 24x 24x 24x 24x            
import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer';
 
export class CrdtWriter extends Writer {
  /**
   * In the below encoding diagrams bits are annotated as follows:
   *
   * - "x" - vector table index, reference to the logical clock.
   * - "y" - time difference.
   * - "?" - whether the next byte is used for encoding.
   *
   * If x is less than 8 and y is less than 16, the relative ID is encoded as a
   * single byte:
   *
   * ```
   * +--------+
   * |0xxxyyyy|
   * +--------+
   * ```
   *
   * Otherwise the top bit of the first byte is set to 1; and x and y are encoded
   * separately using b1vuint28 and vuint39, respectively.
   *
   * ```
   *       x          y
   * +===========+=========+
   * | b1vuint28 | vuint39 |
   * +===========+=========+
   * ```
   *
   * The boolean flag of x b1vuint28 value is always set to 1.
   */
  public id(x: number, y: number): void {
    if (x <= 0b111 && y <= 0b1111) {
      this.u8((x << 4) | y);
    } else {
      this.b1vu56(1, x);
      this.vu57(y);
    }
  }
 
  /**
   * #### `vu57`
   *
   * `vu57` stands for *variable length unsigned 57 bit integer*. It consumes
   * up to 8 bytes. The maximum size of the decoded value is 57 bits.
   *
   * The high bit `?` of each octet indicates if the next byte should be
   * consumed, up to 8 bytes. When `?` is set to `0`, it means that the current
   * byte is the last byte of the encoded value.
   *
   * ```
   *  byte 1   byte 2   byte 3   byte 4   byte 5   byte 6   byte 7   byte 8
   * +--------+........+........+........+........+........+........+········+
   * |?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|zzzzzzzz|
   * +--------+........+........+........+........+........+........+········+
   *
   *            11111    2211111  2222222  3333332  4443333  4444444 55555555
   *   7654321  4321098  1098765  8765432  5432109  2109876  9876543 76543210
   *     |                        |                    |             |
   *     5th bit of z             |                    |             |
   *                              28th bit of z        |             57th bit of z
   *                                                   39th bit of z
   * ```
   *
   * @param num Number to encode as variable length unsigned 57 bit integer.
   */
  public vu57(num: number) {
    if (num <= 0b1111111) {
      this.u8(num);
    } else if (num <= 0b1111111_1111111) {
      this.ensureCapacity(2);
      const uint8 = this.uint8;
      uint8[this.x++] = 0b10000000 | (num & 0b01111111);
      uint8[this.x++] = num >>> 7;
    } else if (num <= 0b1111111_1111111_1111111) {
      this.ensureCapacity(3);
      const uint8 = this.uint8;
      uint8[this.x++] = 0b10000000 | (num & 0b01111111);
      uint8[this.x++] = 0b10000000 | ((num >>> 7) & 0b01111111);
      uint8[this.x++] = num >>> 14;
    } else if (num <= 0b1111111_1111111_1111111_1111111) {
      this.ensureCapacity(4);
      const uint8 = this.uint8;
      uint8[this.x++] = 0b10000000 | (num & 0b01111111);
      uint8[this.x++] = 0b10000000 | ((num >>> 7) & 0b01111111);
      uint8[this.x++] = 0b10000000 | ((num >>> 14) & 0b01111111);
      uint8[this.x++] = num >>> 21;
    } else {
      let lo32 = num | 0;
      if (lo32 < 0) lo32 += 4294967296;
      const hi32 = (num - lo32) / 4294967296;
      if (num <= 0b1111111_1111111_1111111_1111111_1111111) {
        this.ensureCapacity(5);
        const uint8 = this.uint8;
        uint8[this.x++] = 0b10000000 | (num & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 7) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 14) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 21) & 0b01111111);
        uint8[this.x++] = (hi32 << 4) | (num >>> 28);
      } else if (num <= 0b1111111_1111111_1111111_1111111_1111111_1111111) {
        this.ensureCapacity(6);
        const uint8 = this.uint8;
        uint8[this.x++] = 0b10000000 | (num & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 7) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 14) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 21) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((hi32 & 0b111) << 4) | (num >>> 28);
        uint8[this.x++] = hi32 >>> 3;
      } else if (num <= 0b1111111_1111111_1111111_1111111_1111111_1111111_1111111) {
        this.ensureCapacity(7);
        const uint8 = this.uint8;
        uint8[this.x++] = 0b10000000 | (num & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 7) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 14) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 21) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((hi32 & 0b111) << 4) | (num >>> 28);
        uint8[this.x++] = 0b10000000 | ((hi32 & 0b1111111_000) >>> 3);
        uint8[this.x++] = hi32 >>> 10;
      } else {
        this.ensureCapacity(8);
        const uint8 = this.uint8;
        uint8[this.x++] = 0b10000000 | (num & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 7) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 14) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 21) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((hi32 & 0b111) << 4) | (num >>> 28);
        uint8[this.x++] = 0b10000000 | ((hi32 & 0b1111111_000) >>> 3);
        uint8[this.x++] = 0b10000000 | ((hi32 & 0b1111111_0000000_000) >>> 10);
        uint8[this.x++] = hi32 >>> 17;
      }
    }
  }
 
  /**
   * #### `b1vu56`
   *
   * `b1vu56` stands for: 1 bit flag followed by variable length unsigned 56 bit integer.
   * It consumes up to 8 bytes.
   *
   * The high bit "?" of each byte indicates if the next byte should be
   * consumed, up to 8 bytes.
   *
   * - f - flag
   * - z - variable length unsigned 56 bit integer
   * - ? - whether the next byte is used for encoding
   *
   * ```
   * byte 1                                                         byte 8
   * +--------+........+........+........+........+........+........+········+
   * |f?zzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|?zzzzzzz|zzzzzzzz|
   * +--------+........+........+........+........+........+........+········+
   *
   *            1111     2111111  2222222  3333322  4433333  4444444 55555554
   *    654321  3210987  0987654  7654321  4321098  1098765  8765432 65432109
   *     |                        |                    |             |
   *     5th bit of z             |                    |             |
   *                              27th bit of z        |             56th bit of z
   *                                                   38th bit of z
   * ```
   *
   * @param num Number to encode as variable length unsigned 56 bit integer.
   */
  public b1vu56(flag: 0 | 1, num: number) {
    if (num <= 0b111111) {
      this.u8((flag << 7) | num);
    } else {
      const firstByteMask = (flag << 7) | 0b1000000;
      if (num <= 0b1111111_111111) {
        this.ensureCapacity(2);
        const uint8 = this.uint8;
        uint8[this.x++] = firstByteMask | (num & 0b00111111);
        uint8[this.x++] = num >>> 6;
      } else if (num <= 0b1111111_1111111_111111) {
        this.ensureCapacity(3);
        const uint8 = this.uint8;
        uint8[this.x++] = firstByteMask | (num & 0b00111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 6) & 0b01111111);
        uint8[this.x++] = num >>> 13;
      } else if (num <= 0b1111111_1111111_1111111_111111) {
        this.ensureCapacity(4);
        const uint8 = this.uint8;
        uint8[this.x++] = firstByteMask | (num & 0b00111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 6) & 0b01111111);
        uint8[this.x++] = 0b10000000 | ((num >>> 13) & 0b01111111);
        uint8[this.x++] = num >>> 20;
      } else {
        let lo32 = num | 0;
        if (lo32 < 0) lo32 += 4294967296;
        const hi32 = (num - lo32) / 4294967296;
        if (num <= 0b1111111_1111111_1111111_1111111_111111) {
          this.ensureCapacity(5);
          const uint8 = this.uint8;
          uint8[this.x++] = firstByteMask | (num & 0b00111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 6) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 13) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 20) & 0b01111111);
          uint8[this.x++] = (hi32 << 5) | (num >>> 27);
        } else if (num <= 0b1111111_1111111_1111111_1111111_1111111_111111) {
          this.ensureCapacity(6);
          const uint8 = this.uint8;
          uint8[this.x++] = firstByteMask | (num & 0b00111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 6) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 13) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 20) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((hi32 & 0b11) << 5) | (num >>> 27);
          uint8[this.x++] = hi32 >>> 2;
        } else if (num <= 0b1111111_1111111_1111111_1111111_1111111_1111111_111111) {
          this.ensureCapacity(7);
          const uint8 = this.uint8;
          uint8[this.x++] = firstByteMask | (num & 0b00111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 6) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 13) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 20) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((hi32 & 0b11) << 5) | (num >>> 27);
          uint8[this.x++] = 0b10000000 | ((hi32 & 0b1111111_00) >>> 2);
          uint8[this.x++] = hi32 >>> 9;
        } else {
          this.ensureCapacity(8);
          const uint8 = this.uint8;
          uint8[this.x++] = firstByteMask | (num & 0b00111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 6) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 13) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((num >>> 20) & 0b01111111);
          uint8[this.x++] = 0b10000000 | ((hi32 & 0b11) << 5) | (num >>> 27);
          uint8[this.x++] = 0b10000000 | ((hi32 & 0b1111111_00) >>> 2);
          uint8[this.x++] = 0b10000000 | ((hi32 & 0b1111111_0000000_00) >>> 9);
          uint8[this.x++] = hi32 >>> 16;
        }
      }
    }
  }
}