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 { crc32 } from './crc32'; 17 18/** Special flag character for delimiting HDLC frames. */ 19export const FLAG = 0x7e; 20 21/** Special character for escaping other special characters in a frame. */ 22export const ESCAPE = 0x7d; 23 24/** Characters allowed after a 0x7d escape character. */ 25export const VALID_ESCAPED_BYTES = [0x5d, 0x5e]; 26 27/** Frame control for unnumbered information */ 28export const UI_FRAME_CONTROL = frameControl(0x00); 29 30/** Maximum allowed HDLC address (uint64_t in C++). */ 31const MAX_ADDRESS = 2 ** 64 - 1; 32 33/** 34 * Bitwise OR operation on numbers up to MAX_ADDRESS size. 35 * Native bitwise operators only support signed Int32. 36 */ 37function bitwiseOr(x: number, y: number) { 38 const highMask = 0x80000000; 39 const lowMask = 0x7fffffff; 40 const highX = ~~(x / highMask); 41 const highY = ~~(y / highMask); 42 const lowX = x & lowMask; 43 const lowY = y & lowMask; 44 const highOr = highX | highY; 45 const lowOr = lowX | lowY; 46 return highOr * highMask + lowOr; 47} 48 49function getUint8ArraySubset(array: Uint8Array, start: number, end: number) { 50 const subset = new Uint8Array(array.buffer, start, end - start); 51 return subset; 52} 53 54/** Calculates the CRC32 of |data| */ 55export function frameCheckSequence(data: Uint8Array): Uint8Array { 56 const crc = crc32( 57 getUint8ArraySubset(data, data.byteOffset, data.byteLength), 58 ); 59 const arr = new ArrayBuffer(4); 60 const view = new DataView(arr); 61 view.setUint32(0, crc, true); // litteEndian = true 62 return new Uint8Array(arr); 63} 64 65/** Escapes or unescapes a byte, which should have been preceeded by 0x7d */ 66export function escape(byte: number): number { 67 return byte ^ 0x20; 68} 69 70/** Encodes an HDLC address as a one-terminated LSB varint. */ 71export function encodeAddress(address: number): Uint8Array { 72 const byteList = []; 73 // eslint-disable-next-line no-constant-condition 74 while (true) { 75 byteList.push((address & 0x7f) << 1); 76 address >>= 7; 77 if (address === 0) { 78 break; 79 } 80 } 81 82 const result = Uint8Array.from(byteList); 83 result[result.length - 1] |= 0x1; 84 return result; 85} 86 87/** Decodes an HDLC address from a frame, returning it and its size. */ 88export function decodeAddress(frame: Uint8Array): [number, number] { 89 let result = 0; 90 let length = 0; 91 92 while (length < frame.length) { 93 const byte = frame[length]; 94 const shift = (byte >> 1) * 2 ** (length * 7); 95 result = bitwiseOr(result, shift); 96 length += 1; 97 98 if (shift > MAX_ADDRESS || result > MAX_ADDRESS) { 99 return [-1, 0]; 100 } 101 if ((byte & 0x1) === 0x1) { 102 break; 103 } 104 } 105 return [result, length]; 106} 107 108function frameControl(frameType: number): Uint8Array { 109 return Uint8Array.from([0x03 | frameType]); 110} 111