• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://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,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16interface ToleranceRange {
17  leftToleranceEdge: number;
18  rightToleranceEdge: number;
19  prefetchCountMaxRatioLeft: number;
20  prefetchCountMaxRatioRight: number;
21}
22
23type UpdateResult = 'ratio-changed' | 'ratio-not-changed';
24
25class PrefetchRangeRatio {
26  private readonly itemsOnScreen: ItemsOnScreenProvider;
27  private readonly fetchedRegistry: FetchedRegistry;
28  private readonly TOLERANCE_RANGES: [ToleranceRange, ToleranceRange] = [
29    {
30      leftToleranceEdge: 180,
31      rightToleranceEdge: 250,
32      prefetchCountMaxRatioLeft: 0.5,
33      prefetchCountMaxRatioRight: 1,
34    },
35    {
36      leftToleranceEdge: 3000,
37      rightToleranceEdge: 4000,
38      prefetchCountMaxRatioLeft: 1,
39      prefetchCountMaxRatioRight: 0.25,
40    },
41  ];
42  private readonly ACTIVE_DEGREE = 0.5;
43  private readonly VISIBLE_DEGREE = 2.5;
44  private meanPrefetchTime = 0;
45  private leftToleranceEdge = Number.MIN_VALUE;
46  private rightToleranceEdge = 250;
47  private oldRatio = 0;
48
49  constructor(itemsOnScreen: ItemsOnScreenProvider, fetchedRegistry: FetchedRegistry) {
50    this.itemsOnScreen = itemsOnScreen;
51    this.fetchedRegistry = fetchedRegistry;
52  }
53
54  private _range = RatioRange.newEmpty();
55
56  get range(): RatioRange {
57    return this._range;
58  }
59
60  private _maxRatio = 0.5;
61
62  get maxRatio(): number {
63    return this._maxRatio;
64  }
65
66  private updateTiming(prefetchDuration: number): void {
67    // Check if not from file storage
68    if (prefetchDuration > 20) {
69      const weight = 0.95;
70      this.meanPrefetchTime = this.meanPrefetchTime * weight + (1 - weight) * prefetchDuration;
71    }
72    Logger.log(`prefetchDifference prefetchDur=${prefetchDuration}, meanPrefetchDur=${this.meanPrefetchTime}`);
73  }
74
75  update(prefetchDuration: number): UpdateResult {
76    this.updateTiming(prefetchDuration);
77
78    if (this.meanPrefetchTime >= this.leftToleranceEdge && this.meanPrefetchTime <= this.rightToleranceEdge) {
79      return 'ratio-not-changed';
80    }
81
82    let ratioChanged = false;
83
84    if (this.meanPrefetchTime > this.rightToleranceEdge) {
85      for (let i = 0; i < this.TOLERANCE_RANGES.length; i++) {
86        const limit = this.TOLERANCE_RANGES[i];
87        if (this.meanPrefetchTime > limit.rightToleranceEdge) {
88          ratioChanged = true;
89          this._maxRatio = limit.prefetchCountMaxRatioRight;
90          this.leftToleranceEdge = limit.leftToleranceEdge;
91          if (i + 1 !== this.TOLERANCE_RANGES.length) {
92            this.rightToleranceEdge = this.TOLERANCE_RANGES[i + 1].rightToleranceEdge;
93          } else {
94            this.rightToleranceEdge = Number.MAX_VALUE;
95          }
96        }
97      }
98    } else if (this.meanPrefetchTime < this.leftToleranceEdge) {
99      for (let i = this.TOLERANCE_RANGES.length - 1; i >= 0; i--) {
100        const limit = this.TOLERANCE_RANGES[i];
101        if (this.meanPrefetchTime < limit.leftToleranceEdge) {
102          ratioChanged = true;
103          this._maxRatio = limit.prefetchCountMaxRatioLeft;
104          this.rightToleranceEdge = limit.rightToleranceEdge;
105          if (i !== 0) {
106            this.leftToleranceEdge = this.TOLERANCE_RANGES[i - 1].leftToleranceEdge;
107          } else {
108            this.leftToleranceEdge = Number.MIN_VALUE;
109          }
110        }
111      }
112    }
113    return ratioChanged ? 'ratio-changed' : 'ratio-not-changed';
114  }
115
116  calculateRatio(prefetchCount: number, totalCount: number): number {
117    const visibleRange = this.itemsOnScreen.visibleRange;
118
119    const start = Math.max(0, visibleRange.start - prefetchCount);
120    const end = Math.min(totalCount, visibleRange.end + prefetchCount);
121    const evaluatedPrefetchRange = new IndexRange(start, end);
122
123    const completedActive = this.fetchedRegistry.getFetchedInRange(evaluatedPrefetchRange);
124    const completedVisible = this.fetchedRegistry.getFetchedInRange(visibleRange);
125
126    return Math.min(
127      1,
128      Math.pow(completedActive / evaluatedPrefetchRange.length, this.ACTIVE_DEGREE) *
129        Math.pow(completedVisible / visibleRange.length, this.VISIBLE_DEGREE),
130    );
131  }
132
133  updateRatioRange(ratio: number): void {
134    if (ratio > this.oldRatio) {
135      this._range = new RatioRange(new RangeEdge(this.oldRatio, false), new RangeEdge(ratio, true));
136    } else {
137      this._range = new RatioRange(new RangeEdge(ratio, true), new RangeEdge(this.oldRatio, false));
138    }
139    this.oldRatio = ratio;
140  }
141}
142