All files / sportident/src/storage SiInt.ts

100% Statements 43/43
100% Branches 9/9
100% Functions 11/11
100% Lines 41/41

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 8528x 28x 28x                   28x       11588x 14059x               78x       3x       6x 6x 1x       5x       13235x       11671x 3535x   8136x 8136x 8136x 9583x 9583x 9583x 9583x 9583x 9583x 9583x   8136x       93x 30x   63x 63x 63x 81x 81x 81x 81x 81x 81x 81x 81x 81x   63x      
import _ from 'lodash';
import {ISiDataType, ISiStorageData, ValueFromStringError} from './interfaces';
import {ModifyUndefinedException, SiDataType} from './SiDataType';
 
export type SiIntegerPartDefinition = [number, number, number]|[number];
 
export interface SiIntegerPart {
    byteOffset: number;
    startBit: number;
    endBit: number;
}
 
export class SiInt extends SiDataType<number> implements ISiDataType<number> {
    public parts: SiIntegerPart[];
 
    constructor(parts: SiIntegerPartDefinition[]) {
        super();
        this.parts = parts.map((rawPart) => ({
            byteOffset: rawPart[0],
            startBit: rawPart.length === 3 ? rawPart[1] : 0,
            endBit: rawPart.length === 3 ? rawPart[2] : 8,
        }));
    }
 
    typeSpecificIsValueValid(value: number): boolean {
        return _.isInteger(value) && value >= 0;
    }
 
    typeSpecificValueToString(value: number): string {
        return value.toString();
    }
 
    typeSpecificValueFromString(string: string): number|ValueFromStringError {
        const intValue = parseInt(string, 10);
        if (!_.isInteger(intValue)) {
            return new ValueFromStringError(
                `Value for SiInt must be integer, not "${string}"`,
            );
        }
        return intValue;
    }
 
    isUndefined(data: ISiStorageData): boolean {
        return this.parts.some((part) => data.get(part.byteOffset) === undefined);
    }
 
    typeSpecificExtractFromData(data: ISiStorageData): number|undefined {
        if (this.isUndefined(data)) {
            return undefined;
        }
        let bitOffset = 0;
        let intValue = 0;
        this.parts.forEach((part) => {
            const {byteOffset, startBit, endBit} = part;
            const bitLength = endBit - startBit;
            const lengthMask = (0x01 << bitLength) - 1;
            const existingByte = data.get(byteOffset) as number;
            const partValue = (existingByte >> startBit) & lengthMask;
            intValue |= (partValue << bitOffset);
            bitOffset += bitLength;
        });
        return intValue;
    }
 
    typeSpecificUpdateData(data: ISiStorageData, newValue: number): ISiStorageData {
        if (this.isUndefined(data)) {
            throw new ModifyUndefinedException();
        }
        let bitOffset = 0;
        let tempData = data;
        this.parts.forEach((part) => {
            const {byteOffset, startBit, endBit} = part;
            const bitLength = endBit - startBit;
            const lengthMask = (0x01 << bitLength) - 1;
            const newPartValue = (newValue >> bitOffset) & lengthMask;
            const existingByte = tempData.get(byteOffset) as number;
            const preservationMask = (lengthMask << startBit) ^ 0xFF;
            const newByte = (existingByte & preservationMask) | (newPartValue << startBit);
            tempData = tempData.set(byteOffset, newByte);
            bitOffset += bitLength;
        });
        return tempData;
    }
}