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