xterm
[VSoRC/.git] / node_modules / xterm / src / common / parser / Params.ts
1 /**
2  * Copyright (c) 2019 The xterm.js authors. All rights reserved.
3  * @license MIT
4  */
5 import { IParams, ParamsArray } from 'common/parser/Types';
6
7 // max value supported for a single param/subparam (clamped to positive int32 range)
8 const MAX_VALUE = 0x7FFFFFFF;
9 // max allowed subparams for a single sequence (hardcoded limitation)
10 const MAX_SUBPARAMS = 256;
11
12 /**
13  * Params storage class.
14  * This type is used by the parser to accumulate sequence parameters and sub parameters
15  * and transmit them to the input handler actions.
16  *
17  * NOTES:
18  *  - params object for action handlers is borrowed, use `.toArray` or `.clone` to get a copy
19  *  - never read beyond `params.length - 1` (likely to contain arbitrary data)
20  *  - `.getSubParams` returns a borrowed typed array, use `.getSubParamsAll` for cloned sub params
21  *  - hardcoded limitations:
22  *    - max. value for a single (sub) param is 2^31 - 1 (greater values are clamped to that)
23  *    - max. 256 sub params possible
24  *    - negative values are not allowed beside -1 (placeholder for default value)
25  *
26  * About ZDM (Zero Default Mode):
27  * ZDM is not orchestrated by this class. If the parser is in ZDM,
28  * it should add 0 for empty params, otherwise -1. This does not apply
29  * to subparams, empty subparams should always be added with -1.
30  */
31 export class Params implements IParams {
32   // params store and length
33   public params: Int32Array;
34   public length: number;
35
36   // sub params store and length
37   protected _subParams: Int32Array;
38   protected _subParamsLength: number;
39
40   // sub params offsets from param: param idx --> [start, end] offset
41   private _subParamsIdx: Uint16Array;
42   private _rejectDigits: boolean;
43   private _rejectSubDigits: boolean;
44   private _digitIsSub: boolean;
45
46   /**
47    * Create a `Params` type from JS array representation.
48    */
49   public static fromArray(values: ParamsArray): Params {
50     const params = new Params();
51     if (!values.length) {
52       return params;
53     }
54     // skip leading sub params
55     for (let i = (values[0] instanceof Array) ? 1 : 0; i < values.length; ++i) {
56       const value = values[i];
57       if (value instanceof Array) {
58         for (let k = 0; k < value.length; ++k) {
59           params.addSubParam(value[k]);
60         }
61       } else {
62         params.addParam(value);
63       }
64     }
65     return params;
66   }
67
68   /**
69    * @param maxLength max length of storable parameters
70    * @param maxSubParamsLength max length of storable sub parameters
71    */
72   constructor(public maxLength: number = 32, public maxSubParamsLength: number = 32) {
73     if (maxSubParamsLength > MAX_SUBPARAMS) {
74       throw new Error('maxSubParamsLength must not be greater than 256');
75     }
76     this.params = new Int32Array(maxLength);
77     this.length = 0;
78     this._subParams = new Int32Array(maxSubParamsLength);
79     this._subParamsLength = 0;
80     this._subParamsIdx = new Uint16Array(maxLength);
81     this._rejectDigits = false;
82     this._rejectSubDigits = false;
83     this._digitIsSub = false;
84   }
85
86   /**
87    * Clone object.
88    */
89   public clone(): Params {
90     const newParams = new Params(this.maxLength, this.maxSubParamsLength);
91     newParams.params.set(this.params);
92     newParams.length = this.length;
93     newParams._subParams.set(this._subParams);
94     newParams._subParamsLength = this._subParamsLength;
95     newParams._subParamsIdx.set(this._subParamsIdx);
96     newParams._rejectDigits = this._rejectDigits;
97     newParams._rejectSubDigits = this._rejectSubDigits;
98     newParams._digitIsSub = this._digitIsSub;
99     return newParams;
100   }
101
102   /**
103    * Get a JS array representation of the current parameters and sub parameters.
104    * The array is structured as follows:
105    *    sequence: "1;2:3:4;5::6"
106    *    array   : [1, 2, [3, 4], 5, [-1, 6]]
107    */
108   public toArray(): ParamsArray {
109     const res: ParamsArray = [];
110     for (let i = 0; i < this.length; ++i) {
111       res.push(this.params[i]);
112       const start = this._subParamsIdx[i] >> 8;
113       const end = this._subParamsIdx[i] & 0xFF;
114       if (end - start > 0) {
115         res.push(Array.prototype.slice.call(this._subParams, start, end));
116       }
117     }
118     return res;
119   }
120
121   /**
122    * Reset to initial empty state.
123    */
124   public reset(): void {
125     this.length = 0;
126     this._subParamsLength = 0;
127     this._rejectDigits = false;
128     this._rejectSubDigits = false;
129     this._digitIsSub = false;
130   }
131
132   /**
133    * Add a parameter value.
134    * `Params` only stores up to `maxLength` parameters, any later
135    * parameter will be ignored.
136    * Note: VT devices only stored up to 16 values, xterm seems to
137    * store up to 30.
138    */
139   public addParam(value: number): void {
140     this._digitIsSub = false;
141     if (this.length >= this.maxLength) {
142       this._rejectDigits = true;
143       return;
144     }
145     if (value < -1) {
146       throw new Error('values lesser than -1 are not allowed');
147     }
148     this._subParamsIdx[this.length] = this._subParamsLength << 8 | this._subParamsLength;
149     this.params[this.length++] = value > MAX_VALUE ? MAX_VALUE : value;
150   }
151
152   /**
153    * Add a sub parameter value.
154    * The sub parameter is automatically associated with the last parameter value.
155    * Thus it is not possible to add a subparameter without any parameter added yet.
156    * `Params` only stores up to `subParamsLength` sub parameters, any later
157    * sub parameter will be ignored.
158    */
159   public addSubParam(value: number): void {
160     this._digitIsSub = true;
161     if (!this.length) {
162       return;
163     }
164     if (this._rejectDigits || this._subParamsLength >= this.maxSubParamsLength) {
165       this._rejectSubDigits = true;
166       return;
167     }
168     if (value < -1) {
169       throw new Error('values lesser than -1 are not allowed');
170     }
171     this._subParams[this._subParamsLength++] = value > MAX_VALUE ? MAX_VALUE : value;
172     this._subParamsIdx[this.length - 1]++;
173   }
174
175   /**
176    * Whether parameter at index `idx` has sub parameters.
177    */
178   public hasSubParams(idx: number): boolean {
179     return ((this._subParamsIdx[idx] & 0xFF) - (this._subParamsIdx[idx] >> 8) > 0);
180   }
181
182   /**
183    * Return sub parameters for parameter at index `idx`.
184    * Note: The values are borrowed, thus you need to copy
185    * the values if you need to hold them in nonlocal scope.
186    */
187   public getSubParams(idx: number): Int32Array | null {
188     const start = this._subParamsIdx[idx] >> 8;
189     const end = this._subParamsIdx[idx] & 0xFF;
190     if (end - start > 0) {
191       return this._subParams.subarray(start, end);
192     }
193     return null;
194   }
195
196   /**
197    * Return all sub parameters as {idx: subparams} mapping.
198    * Note: The values are not borrowed.
199    */
200   public getSubParamsAll(): {[idx: number]: Int32Array} {
201     const result: {[idx: number]: Int32Array} = {};
202     for (let i = 0; i < this.length; ++i) {
203       const start = this._subParamsIdx[i] >> 8;
204       const end = this._subParamsIdx[i] & 0xFF;
205       if (end - start > 0) {
206         result[i] = this._subParams.slice(start, end);
207       }
208     }
209     return result;
210   }
211
212   /**
213    * Add a single digit value to current parameter.
214    * This is used by the parser to account digits on a char by char basis.
215    */
216   public addDigit(value: number): void {
217     let length;
218     if (this._rejectDigits
219       || !(length = this._digitIsSub ? this._subParamsLength : this.length)
220       || (this._digitIsSub && this._rejectSubDigits)
221     ) {
222       return;
223     }
224
225     const store = this._digitIsSub ? this._subParams : this.params;
226     const cur = store[length - 1];
227     store[length - 1] = ~cur ? Math.min(cur * 10 + value, MAX_VALUE) : value;
228   }
229 }