1*90c8c64dSAndroid Build Coastguard Worker/* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Workerimport { 18*90c8c64dSAndroid Build Coastguard Worker AbsoluteEntryIndex, 19*90c8c64dSAndroid Build Coastguard Worker AbsoluteFrameIndex, 20*90c8c64dSAndroid Build Coastguard Worker EntriesRange, 21*90c8c64dSAndroid Build Coastguard Worker FramesRange, 22*90c8c64dSAndroid Build Coastguard Worker} from './index_types'; 23*90c8c64dSAndroid Build Coastguard Worker 24*90c8c64dSAndroid Build Coastguard Workerexport class FrameMap { 25*90c8c64dSAndroid Build Coastguard Worker readonly lengthEntries: number; 26*90c8c64dSAndroid Build Coastguard Worker readonly lengthFrames: number; 27*90c8c64dSAndroid Build Coastguard Worker 28*90c8c64dSAndroid Build Coastguard Worker // These lookup tables allow to convert a "[start_entry; end_entry[" range 29*90c8c64dSAndroid Build Coastguard Worker // to "[start_frame; end_frame[" in O(1) time. 30*90c8c64dSAndroid Build Coastguard Worker // 31*90c8c64dSAndroid Build Coastguard Worker // entryToStartFrame[i] is: 32*90c8c64dSAndroid Build Coastguard Worker // - start_frame of entry_i 33*90c8c64dSAndroid Build Coastguard Worker // - start_frame of the first entry_j (j > i), if entry_i has no associated frames 34*90c8c64dSAndroid Build Coastguard Worker // - undefined, if all the entries with index >= i have no associated frames 35*90c8c64dSAndroid Build Coastguard Worker // 36*90c8c64dSAndroid Build Coastguard Worker // entryToEndFrame[i] is: 37*90c8c64dSAndroid Build Coastguard Worker // - end_frame of entry_i 38*90c8c64dSAndroid Build Coastguard Worker // - end_frame of the last entry_j (j < i), if entry_i has no associated frames 39*90c8c64dSAndroid Build Coastguard Worker // - undefined, if all the entries with index <= i have no associated frames 40*90c8c64dSAndroid Build Coastguard Worker private readonly entryToStartFrame: Array<AbsoluteFrameIndex | undefined>; 41*90c8c64dSAndroid Build Coastguard Worker private readonly entryToEndFrame: Array<AbsoluteFrameIndex | undefined>; 42*90c8c64dSAndroid Build Coastguard Worker 43*90c8c64dSAndroid Build Coastguard Worker // These lookup tables allow to convert a "[start_frame; end_frame[" range 44*90c8c64dSAndroid Build Coastguard Worker // to "[start_entry; end_entry[" in O(1) time. 45*90c8c64dSAndroid Build Coastguard Worker // 46*90c8c64dSAndroid Build Coastguard Worker // frameToStartEntry[i] is: 47*90c8c64dSAndroid Build Coastguard Worker // - start_entry of frame_i 48*90c8c64dSAndroid Build Coastguard Worker // - start_entry of the first frame_j (j > i), if frame_i has no associated entries 49*90c8c64dSAndroid Build Coastguard Worker // - undefined, if all the frames with index >= i have no associated entries 50*90c8c64dSAndroid Build Coastguard Worker // 51*90c8c64dSAndroid Build Coastguard Worker // frameToEndEntry[i] is: 52*90c8c64dSAndroid Build Coastguard Worker // - end_entry of frame_i 53*90c8c64dSAndroid Build Coastguard Worker // - end_entry of the last frame_j (j < i), if frame_i has no associated entries 54*90c8c64dSAndroid Build Coastguard Worker // - undefined, if all the frames with index <= i have no associated entries 55*90c8c64dSAndroid Build Coastguard Worker private readonly frameToStartEntry: Array<AbsoluteEntryIndex | undefined>; 56*90c8c64dSAndroid Build Coastguard Worker private readonly frameToEndEntry: Array<AbsoluteEntryIndex | undefined>; 57*90c8c64dSAndroid Build Coastguard Worker 58*90c8c64dSAndroid Build Coastguard Worker constructor( 59*90c8c64dSAndroid Build Coastguard Worker lengthEntries: number, 60*90c8c64dSAndroid Build Coastguard Worker lengthFrames: number, 61*90c8c64dSAndroid Build Coastguard Worker entryToStartFrame: Array<AbsoluteFrameIndex | undefined>, 62*90c8c64dSAndroid Build Coastguard Worker entryToEndFrame: Array<AbsoluteFrameIndex | undefined>, 63*90c8c64dSAndroid Build Coastguard Worker frameToStartEntry: Array<AbsoluteEntryIndex | undefined>, 64*90c8c64dSAndroid Build Coastguard Worker frameToEndEntry: Array<AbsoluteEntryIndex | undefined>, 65*90c8c64dSAndroid Build Coastguard Worker ) { 66*90c8c64dSAndroid Build Coastguard Worker this.lengthEntries = lengthEntries; 67*90c8c64dSAndroid Build Coastguard Worker this.lengthFrames = lengthFrames; 68*90c8c64dSAndroid Build Coastguard Worker this.entryToStartFrame = entryToStartFrame; 69*90c8c64dSAndroid Build Coastguard Worker this.entryToEndFrame = entryToEndFrame; 70*90c8c64dSAndroid Build Coastguard Worker this.frameToStartEntry = frameToStartEntry; 71*90c8c64dSAndroid Build Coastguard Worker this.frameToEndEntry = frameToEndEntry; 72*90c8c64dSAndroid Build Coastguard Worker } 73*90c8c64dSAndroid Build Coastguard Worker 74*90c8c64dSAndroid Build Coastguard Worker getFramesRange(entries: EntriesRange): FramesRange | undefined { 75*90c8c64dSAndroid Build Coastguard Worker entries = this.clampEntriesRangeToFitBounds(entries); 76*90c8c64dSAndroid Build Coastguard Worker if (entries.start >= entries.end) { 77*90c8c64dSAndroid Build Coastguard Worker return undefined; 78*90c8c64dSAndroid Build Coastguard Worker } 79*90c8c64dSAndroid Build Coastguard Worker 80*90c8c64dSAndroid Build Coastguard Worker const startFrame = this.getStartFrameOfFirstGreaterOrEqualMappedEntry( 81*90c8c64dSAndroid Build Coastguard Worker entries.start, 82*90c8c64dSAndroid Build Coastguard Worker ); 83*90c8c64dSAndroid Build Coastguard Worker const endFrame = this.getEndFrameOfLastLowerOrEqualMappedEntry( 84*90c8c64dSAndroid Build Coastguard Worker entries.end - 1, 85*90c8c64dSAndroid Build Coastguard Worker ); 86*90c8c64dSAndroid Build Coastguard Worker 87*90c8c64dSAndroid Build Coastguard Worker if ( 88*90c8c64dSAndroid Build Coastguard Worker startFrame === undefined || 89*90c8c64dSAndroid Build Coastguard Worker endFrame === undefined || 90*90c8c64dSAndroid Build Coastguard Worker startFrame >= endFrame 91*90c8c64dSAndroid Build Coastguard Worker ) { 92*90c8c64dSAndroid Build Coastguard Worker return undefined; 93*90c8c64dSAndroid Build Coastguard Worker } 94*90c8c64dSAndroid Build Coastguard Worker 95*90c8c64dSAndroid Build Coastguard Worker return {start: startFrame, end: endFrame}; 96*90c8c64dSAndroid Build Coastguard Worker } 97*90c8c64dSAndroid Build Coastguard Worker 98*90c8c64dSAndroid Build Coastguard Worker getFullTraceFramesRange(): FramesRange | undefined { 99*90c8c64dSAndroid Build Coastguard Worker return this.getFramesRange({start: 0, end: this.lengthEntries}); 100*90c8c64dSAndroid Build Coastguard Worker } 101*90c8c64dSAndroid Build Coastguard Worker 102*90c8c64dSAndroid Build Coastguard Worker getEntriesRange(frames: FramesRange): EntriesRange | undefined { 103*90c8c64dSAndroid Build Coastguard Worker frames = this.clampFramesRangeToFitBounds(frames); 104*90c8c64dSAndroid Build Coastguard Worker if (frames.start >= frames.end) { 105*90c8c64dSAndroid Build Coastguard Worker return undefined; 106*90c8c64dSAndroid Build Coastguard Worker } 107*90c8c64dSAndroid Build Coastguard Worker 108*90c8c64dSAndroid Build Coastguard Worker const startEntry = this.getStartEntryOfFirstGreaterOrEqualMappedFrame( 109*90c8c64dSAndroid Build Coastguard Worker frames.start, 110*90c8c64dSAndroid Build Coastguard Worker ); 111*90c8c64dSAndroid Build Coastguard Worker const endEntry = this.getEndEntryOfLastLowerOrEqualMappedFrame( 112*90c8c64dSAndroid Build Coastguard Worker frames.end - 1, 113*90c8c64dSAndroid Build Coastguard Worker ); 114*90c8c64dSAndroid Build Coastguard Worker 115*90c8c64dSAndroid Build Coastguard Worker if ( 116*90c8c64dSAndroid Build Coastguard Worker startEntry === undefined || 117*90c8c64dSAndroid Build Coastguard Worker endEntry === undefined || 118*90c8c64dSAndroid Build Coastguard Worker startEntry >= endEntry 119*90c8c64dSAndroid Build Coastguard Worker ) { 120*90c8c64dSAndroid Build Coastguard Worker return undefined; 121*90c8c64dSAndroid Build Coastguard Worker } 122*90c8c64dSAndroid Build Coastguard Worker 123*90c8c64dSAndroid Build Coastguard Worker return {start: startEntry, end: endEntry}; 124*90c8c64dSAndroid Build Coastguard Worker } 125*90c8c64dSAndroid Build Coastguard Worker 126*90c8c64dSAndroid Build Coastguard Worker private getStartFrameOfFirstGreaterOrEqualMappedEntry( 127*90c8c64dSAndroid Build Coastguard Worker entry: AbsoluteEntryIndex, 128*90c8c64dSAndroid Build Coastguard Worker ): AbsoluteFrameIndex | undefined { 129*90c8c64dSAndroid Build Coastguard Worker if (entry < 0 || entry >= this.lengthEntries) { 130*90c8c64dSAndroid Build Coastguard Worker throw new Error(`Entry index out of bounds: ${entry}`); 131*90c8c64dSAndroid Build Coastguard Worker } 132*90c8c64dSAndroid Build Coastguard Worker return this.entryToStartFrame[entry]; 133*90c8c64dSAndroid Build Coastguard Worker } 134*90c8c64dSAndroid Build Coastguard Worker 135*90c8c64dSAndroid Build Coastguard Worker private getEndFrameOfLastLowerOrEqualMappedEntry( 136*90c8c64dSAndroid Build Coastguard Worker entry: AbsoluteEntryIndex, 137*90c8c64dSAndroid Build Coastguard Worker ): AbsoluteFrameIndex | undefined { 138*90c8c64dSAndroid Build Coastguard Worker if (entry < 0 || entry >= this.lengthEntries) { 139*90c8c64dSAndroid Build Coastguard Worker throw new Error(`Entry index out of bounds: ${entry}`); 140*90c8c64dSAndroid Build Coastguard Worker } 141*90c8c64dSAndroid Build Coastguard Worker return this.entryToEndFrame[entry]; 142*90c8c64dSAndroid Build Coastguard Worker } 143*90c8c64dSAndroid Build Coastguard Worker 144*90c8c64dSAndroid Build Coastguard Worker private getStartEntryOfFirstGreaterOrEqualMappedFrame( 145*90c8c64dSAndroid Build Coastguard Worker frame: AbsoluteFrameIndex, 146*90c8c64dSAndroid Build Coastguard Worker ): AbsoluteEntryIndex | undefined { 147*90c8c64dSAndroid Build Coastguard Worker if (frame < 0 || frame >= this.lengthFrames) { 148*90c8c64dSAndroid Build Coastguard Worker throw new Error(`Frame index out of bounds: ${frame}`); 149*90c8c64dSAndroid Build Coastguard Worker } 150*90c8c64dSAndroid Build Coastguard Worker return this.frameToStartEntry[frame]; 151*90c8c64dSAndroid Build Coastguard Worker } 152*90c8c64dSAndroid Build Coastguard Worker 153*90c8c64dSAndroid Build Coastguard Worker private getEndEntryOfLastLowerOrEqualMappedFrame( 154*90c8c64dSAndroid Build Coastguard Worker frame: AbsoluteFrameIndex, 155*90c8c64dSAndroid Build Coastguard Worker ): AbsoluteEntryIndex | undefined { 156*90c8c64dSAndroid Build Coastguard Worker if (frame < 0 || frame >= this.lengthFrames) { 157*90c8c64dSAndroid Build Coastguard Worker throw new Error(`Frame index out of bounds: ${frame}`); 158*90c8c64dSAndroid Build Coastguard Worker } 159*90c8c64dSAndroid Build Coastguard Worker return this.frameToEndEntry[frame]; 160*90c8c64dSAndroid Build Coastguard Worker } 161*90c8c64dSAndroid Build Coastguard Worker 162*90c8c64dSAndroid Build Coastguard Worker private clampEntriesRangeToFitBounds(entries: EntriesRange): EntriesRange { 163*90c8c64dSAndroid Build Coastguard Worker return { 164*90c8c64dSAndroid Build Coastguard Worker start: Math.max(entries.start, 0), 165*90c8c64dSAndroid Build Coastguard Worker end: Math.min(entries.end, this.lengthEntries), 166*90c8c64dSAndroid Build Coastguard Worker }; 167*90c8c64dSAndroid Build Coastguard Worker } 168*90c8c64dSAndroid Build Coastguard Worker 169*90c8c64dSAndroid Build Coastguard Worker private clampFramesRangeToFitBounds(frames: FramesRange): FramesRange { 170*90c8c64dSAndroid Build Coastguard Worker return { 171*90c8c64dSAndroid Build Coastguard Worker start: Math.max(frames.start, 0), 172*90c8c64dSAndroid Build Coastguard Worker end: Math.min(frames.end, this.lengthFrames), 173*90c8c64dSAndroid Build Coastguard Worker }; 174*90c8c64dSAndroid Build Coastguard Worker } 175*90c8c64dSAndroid Build Coastguard Worker} 176