• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {FrameMap} from './frame_map';
18import {AbsoluteEntryIndex, AbsoluteFrameIndex, FramesRange} from './index_types';
19
20export class FrameMapBuilder {
21  private readonly lengthEntries: number;
22  private readonly lengthFrames: number;
23
24  // See comments in FrameMap about the semantics of these lookup tables
25  private readonly entryToStartFrame: Array<AbsoluteFrameIndex | undefined>;
26  private readonly entryToEndFrame: Array<AbsoluteFrameIndex | undefined>;
27  private readonly frameToStartEntry: Array<AbsoluteEntryIndex | undefined>;
28  private readonly frameToEndEntry: Array<AbsoluteEntryIndex | undefined>;
29
30  private isFinalized = false;
31
32  constructor(lengthEntries: number, lengthFrames: number) {
33    this.lengthEntries = lengthEntries;
34    this.lengthFrames = lengthFrames;
35
36    this.entryToStartFrame = new Array<AbsoluteFrameIndex | undefined>(this.lengthEntries).fill(
37      undefined
38    );
39    this.entryToEndFrame = new Array<AbsoluteFrameIndex | undefined>(this.lengthEntries).fill(
40      undefined
41    );
42    this.frameToStartEntry = new Array<AbsoluteEntryIndex | undefined>(this.lengthFrames).fill(
43      undefined
44    );
45    this.frameToEndEntry = new Array<AbsoluteEntryIndex | undefined>(this.lengthFrames).fill(
46      undefined
47    );
48  }
49
50  setFrames(entry: AbsoluteEntryIndex, range: FramesRange | undefined): FrameMapBuilder {
51    this.checkIsNotFinalized();
52    if (!range || range.start === range.end) {
53      return this;
54    }
55
56    this.setStartArrayValue(this.entryToStartFrame, entry, range.start);
57    this.setEndArrayValue(this.entryToEndFrame, entry, range.end);
58
59    for (let frame = range.start; frame < range.end; ++frame) {
60      this.setStartArrayValue(this.frameToStartEntry, frame, entry);
61      this.setEndArrayValue(this.frameToEndEntry, frame, entry + 1);
62    }
63
64    return this;
65  }
66
67  build(): FrameMap {
68    this.checkIsNotFinalized();
69    this.finalizeStartArray(this.entryToStartFrame);
70    this.finalizeEndArray(this.entryToEndFrame);
71    this.finalizeStartArray(this.frameToStartEntry);
72    this.finalizeEndArray(this.frameToEndEntry);
73    this.isFinalized = true;
74    return new FrameMap(
75      this.lengthEntries,
76      this.lengthFrames,
77      this.entryToStartFrame,
78      this.entryToEndFrame,
79      this.frameToStartEntry,
80      this.frameToEndEntry
81    );
82  }
83
84  private setStartArrayValue(array: Array<number | undefined>, index: number, value: number) {
85    const currentValue = array[index];
86    if (currentValue === undefined) {
87      array[index] = value;
88    } else {
89      array[index] = Math.min(currentValue, value);
90    }
91  }
92
93  private setEndArrayValue(array: Array<number | undefined>, index: number, value: number) {
94    const currentValue = array[index];
95    if (currentValue === undefined) {
96      array[index] = value;
97    } else {
98      array[index] = Math.max(currentValue, value);
99    }
100  }
101
102  private finalizeStartArray(array: Array<number | undefined>) {
103    let firstValidStart: number | undefined = undefined;
104    for (let i = array.length - 1; i >= 0; --i) {
105      if (array[i] === undefined) {
106        array[i] = firstValidStart;
107      } else {
108        firstValidStart = array[i];
109      }
110    }
111  }
112
113  private finalizeEndArray(array: Array<number | undefined>) {
114    let lastValidEnd: number | undefined = undefined;
115    for (let i = 0; i < array.length; ++i) {
116      if (array[i] === undefined) {
117        array[i] = lastValidEnd;
118      } else {
119        lastValidEnd = array[i];
120      }
121    }
122  }
123
124  private checkIsNotFinalized() {
125    if (this.isFinalized) {
126      throw new Error('Attemped to modify already finalized frame map.');
127    }
128  }
129}
130