• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15/** Low-level HDLC protocol features. */
16import {Buffer} from 'buffer';
17import {crc32} from 'crc';
18
19/** Special flag character for delimiting HDLC frames. */
20export const FLAG = 0x7e;
21
22/** Special character for escaping other special characters in a frame. */
23export const ESCAPE = 0x7d;
24
25/** Characters allowed after a 0x7d escape character. */
26export const VALID_ESCAPED_BYTES = [0x5d, 0x5e];
27
28/** Frame control for unnumbered information */
29export const UI_FRAME_CONTROL = frameControl(0x00);
30
31/** Maximum allowed HDLC address (uint64_t in C++). */
32const MAX_ADDRESS = 2 ** 64 - 1;
33
34/**
35 * Bitwise OR operation on numbers up to MAX_ADDRESS size.
36 * Native bitwise operators only support signed Int32.
37 */
38function bitwiseOr(x: number, y: number) {
39  const highMask = 0x80000000;
40  const lowMask = 0x7fffffff;
41  const highX = ~~(x / highMask);
42  const highY = ~~(y / highMask);
43  const lowX = x & lowMask;
44  const lowY = y & lowMask;
45  const highOr = highX | highY;
46  const lowOr = lowX | lowY;
47  return highOr * highMask + lowOr;
48}
49
50/** Calculates the CRC32 of |data| */
51export function frameCheckSequence(data: Uint8Array): Uint8Array {
52  const crc = crc32(Buffer.from(data.buffer, data.byteOffset, data.byteLength));
53  const arr = new ArrayBuffer(4);
54  const view = new DataView(arr);
55  view.setUint32(0, crc, true); // litteEndian = true
56  return new Uint8Array(arr);
57}
58
59/** Escapes or unescapes a byte, which should have been preceeded by 0x7d */
60export function escape(byte: number): number {
61  return byte ^ 0x20;
62}
63
64/** Encodes an HDLC address as a one-terminated LSB varint. */
65export function encodeAddress(address: number): Uint8Array {
66  const byteList = [];
67  while (true) {
68    byteList.push((address & 0x7f) << 1);
69    address >>= 7;
70    if (address === 0) {
71      break;
72    }
73  }
74
75  const result = Uint8Array.from(byteList);
76  result[result.length - 1] |= 0x1;
77  return result;
78}
79
80/** Decodes an HDLC address from a frame, returning it and its size. */
81export function decodeAddress(frame: Uint8Array): [number, number] {
82  let result = 0;
83  let length = 0;
84
85  while (length < frame.length) {
86    const byte = frame[length];
87    const shift = (byte >> 1) * 2 ** (length * 7);
88    result = bitwiseOr(result, shift);
89    length += 1;
90
91    if (shift > MAX_ADDRESS || result > MAX_ADDRESS) {
92      return [-1, 0];
93    }
94    if ((byte & 0x1) === 0x1) {
95      break;
96    }
97  }
98  return [result, length];
99}
100
101function frameControl(frameType: number): Uint8Array {
102  return Uint8Array.from([0x03 | frameType]);
103}
104