1/* 2 * Copyright (c) 2021-2025 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 16package std.core; 17 18// NOTE: autogenerated file 19 20% template = ERB.new(File.read("Array_header.erb"), trim_mode: '%', eoutvar: '_sub04') 21<%= template.result(binding) %> 22 23// Range is [startIndex, endIndex), i.e. startIndex included and endIndex is excluded 24function checkRange(arrLen: int, startIndex: int, endIndex: int): boolean { 25 // Since mostly everywhere for loop is used from startIndex till endIndex exclusive, 26 // startIndex <= endIndex is used to cover empty array case 27 return ((0 <= startIndex) && (startIndex <= endIndex) && (endIndex <= arrLen)); 28} 29 30native function __alloc_array<T>(len: int, ofType: Object | null): FixedArray<T> 31 32class BuiltinArrayKeysIterator implements IterableIterator<number> { 33 private len: int 34 private idx: int = 0 35 private isDone: boolean = false 36 37 constructor(len: int) { 38 this.len = len 39 } 40 41 override next(): IteratorResult<number> { 42 if (this.isDone || this.idx >= this.len) { 43 this.isDone = true 44 return new IteratorResult<number>() 45 } 46 return new IteratorResult<number>(this.idx++ as number) 47 } 48 49 override $_iterator(): IterableIterator<number> { 50 return this 51 } 52} 53 54% require 'ostruct' 55% ctx = OpenStruct.new 56% $ctx = ctx 57% ctx.this = 'self' 58% ctx.this_len_int = 'self.length' 59% ctx.array_len_int = Proc.new { |v| "#{v}.length" } 60% ctx.access_public = 'export function' 61% ctx.access_private = 'function' 62% ctx.override = '' 63% ctx.get_unsafe = Proc.new { |t, i| "#{t}[#{i}]" } 64% ctx.set_unsafe = Proc.new { |t, i, v| "#{t}[#{i}] = #{v}" } 65% ctx.arr_method_call = Proc.new { |t, f, *args| "#{f}(#{args.prepend(t).join(', ')})" } 66% primitive_types = ['boolean', 'byte', 'short', 'int', 'long', 'float', 'double', 'char'] 67% # NOTE(kprokopenko): primitive_types + ['T'] 68% (primitive_types).each { |el_type| 69% ctx.this_type = "FixedArray<#{el_type}>" 70% ctx.this_arg = "self: #{ctx.this_type}, " 71% ctx.this_return_type = ctx.this_type 72% ctx.el_type = el_type 73% ctx.el_type_boxed = el_type[0].upcase + el_type[1..] 74% ctx.clone_this = 'cloneArray(self)' 75% ctx.make_buffer = Proc.new { |l, elt| 76% elt ||= ctx.el_type 77% if elt == 'T' 78% "__alloc_array<#{elt}>(#{l}, self)" 79% elsif elt == 'U' 80% "__alloc_array<#{elt}>(#{l}, null)" 81% else 82% "new #{elt}[#{l}]" 83% end 84% } 85% ctx.make_fixed_array = "FixedArray<#{el_type}>" 86% ctx.from_buffer = Proc.new { |b, elt| b } 87% ctx.array_of_type = Proc.new { |t| "FixedArray<#{t}>" } 88% ctx.this_generic = '' 89% ctx.this_generic_one = '' 90% ctx.this_iterator_generic = '' 91% if el_type == 'T' 92% ctx.this_generic = '<T>' 93% ctx.this_iterator_generic = '<T>' 94% ctx.this_generic_one = 'T, ' 95% end 96% ctx.this_call = Proc.new { |f| "#{f}#{ctx.this_generic}(self, " } 97function cloneArray<%= ctx.this_generic %>(self: <%= ctx.this_type %>): <%= ctx.this_type %> { 98 const ret : <%= ctx.make_fixed_array %> = <%= ctx.make_buffer.('self.length') %>;; 99 for (let i = 0; i < self.length; i++) { 100 ret[i] = self[i]; 101 } 102 return <%= ctx.from_buffer.('ret') %>; 103} 104% template = ERB.new(File.read("Array_common.erb"), trim_mode: '%', eoutvar: '_sub01') 105<%= template.result(ctx.instance_eval { binding }) %> 106% template = ERB.new(File.read("BuiltinArray.erb"), trim_mode: '%', eoutvar: '_sub02') 107% primitive_types.each { |mapped| 108% ctx.mapped_type = mapped 109% ctx.map_generic = ctx.this_generic 110<%# template.result(ctx.instance_eval { binding }) %> 111% } 112 113% ctx.mapped_type = ctx.el_type 114% ctx.map_generic = ctx.this_generic 115<%= template.result(ctx.instance_eval { binding }) %> 116 117% ctx.mapped_type = 'U' 118% ctx.map_generic = "<#{ctx.this_generic_one}U>" 119<%# template.result(ctx.instance_eval { binding }) %> 120 121/** 122 * Constructs a new `Array` instance and populates it with 123 * portion of a given array, filtered down to just the elements from the 124 * given array that pass the test implemented by the provided function. 125 * 126 * @param fn test function, applied to each element of an array. 127 * 128 * @returns New `Array` instance constructed from `this` with elements filtered using test function `fn`. 129 */ 130export function filter<%= ctx.this_generic %>(<%= ctx.this_arg %>fn: (v: <%= ctx.el_type %>, k: number, array: <%= ctx.this_type %>) => boolean): <%= ctx.this_type %> { 131 const mask : FixedArray<boolean> = new boolean[<%= ctx.this_len_int %>] 132 let cnt = 0 133 134 for (let i: int = 0; i < <%= ctx.this_len_int %>; i++) { 135 const val = self[i]; 136 if (fn(val, i, self)) { 137 mask[i] = true 138 cnt++; 139 } 140 } 141 const res : <%= ctx.make_fixed_array %> = <%= ctx.make_buffer.('cnt') %>; 142 let idx_store = 0; 143 for (let i: int = 0; i < <%= ctx.this_len_int %>; i++) { 144 if (mask[i]) { 145 res[idx_store++] = self[i] 146 } 147 } 148 return <%= ctx.from_buffer.('res') %>; 149} 150 151export function concat<%= ctx.this_generic %>(self: <%= ctx.this_type %>, fst: <%= ctx.this_type %>, ...more: FixedArray<<%= ctx.this_type %>>): <%= ctx.this_type %> { 152 const lnMin = self.length + fst.length; 153 let ln = lnMin; 154 for (let i = 0; i < more.length; i++) { 155 ln += more[i].length 156 } 157 const r : <%= ctx.make_fixed_array %> = <%= ctx.make_buffer.('ln', ctx.el_type) %>; 158 try { 159 copyTo(self, r, 0, 0, self.length); 160 copyTo(fst, r, self.length, 0, fst.length); 161 let idx = lnMin; 162 for (let i = 0; i < more.length; i++) { 163 copyTo(more[i], r, idx, 0, more[i].length); 164 idx += more[i].length; 165 } 166 } catch (e) { 167 // impossible 168 } 169 return <%= ctx.from_buffer.('r') %> 170} 171 172/** 173 * Reorders elements of `this` using comparator function. 174 * 175 * @param comparator function that defines the sort order. 176 * 177 * @note Mutating method 178 */ 179export function sort<%= ctx.this_generic %>(<%= ctx.this_arg %>comparator: (a: <%= ctx.el_type %>, b: <%= ctx.el_type %>) => number): <%= ctx.this_return_type %> { 180% sort_elem_type = ctx.el_type == 'T' ? 'NullishType' : ctx.el_type 181% from_sort_elem_type = ctx.el_type == 'T' ? ' as T' : '' 182 sort_subarray(self<%= ctx.el_type == 'T' ? ' as FixedArray<NullishType>' : '' %>, 0, self.length, (l: <%= sort_elem_type %>, r: <%= sort_elem_type %>): boolean => { 183 return comparator(l<%= from_sort_elem_type %>, r <%= from_sort_elem_type %>) < 0; 184 }); 185 return self; 186} 187 188/** 189 * Reorders elements of `this` using comparator function. 190 * 191 * @param comparator function that defines the sort order. 192 * 193 * @note Mutating method 194 */ 195export function sort<%= ctx.this_generic %>(<%= ctx.this_arg.chomp(', ') %>): <%= ctx.this_return_type %> { 196% if !primitive_types.include?(ctx.el_type) 197 sort_subarray(self<%= ctx.el_type == 'T' ? ' as FixedArray<NullishType>' : '' %>, 0, self.length, (l: <%= sort_elem_type %>, r: <%= sort_elem_type %>): boolean => { 198 return new String(l).compareTo(new String(r)) < 0; 199 }); 200% else 201 sort(self, 0, self.length); 202% end 203 return self; 204} 205 206export function keys<%= ctx.this_generic %>(self: <%= ctx.this_type %>): IterableIterator<number> { 207 return new BuiltinArrayKeysIterator(self.length); 208} 209 210% template = ERB.new(File.read("Array_common_top_scope.erb"), trim_mode: '%', eoutvar: '_sub03') 211<%= template.result(ctx.instance_eval { binding }).gsub(/[ \t]+$/, '') %> 212 213% } 214 215function builtin_insertion_sort<T>(arr: FixedArray<T>, startIndex: int, endIndex: int, comp: (lhs: T, rhs: T) => number): void { 216 for (let i = startIndex + 1; i < endIndex; i++) { 217 const tmp = arr[i]; 218 if (comp(tmp, arr[startIndex]) as int < 0) { 219 for (let j = i; j > startIndex; j--) { 220 arr[j] = arr[j - 1] 221 } 222 arr[startIndex] = tmp 223 } else { 224 let j = i - 1; 225 while (comp(tmp, arr[j]) as int < 0) { 226 arr[j + 1] = arr[j]; 227 j--; 228 } 229 arr[j + 1] = tmp; 230 } 231 } 232} 233 234function perform_merge<T>(arr: FixedArray<T>, startIndex: int, midIndex: int, endIndex: int, comp: (lhs: T, rhs: T) => number): void { 235 const len1 = midIndex - startIndex + 1; 236 const len2 = endIndex - midIndex; 237 let left : FixedArray<T | undefined> = new (T | undefined)[len1]; 238 let right : FixedArray<T | undefined> = new (T | undefined)[len2]; 239 for (let i = 0; i < len1; i++) { 240 left[i] = arr[startIndex + i]; 241 } 242 for (let i = 0; i < len2; i++) { 243 right[i] = arr[midIndex + 1 + i]; 244 } 245 let i = 0; 246 let j = 0; 247 let k = startIndex; 248 while (i < len1 && j < len2) { 249 if (comp(left[i]!, right[j]!) as int <= 0) { 250 arr[k] = left[i]!; 251 i++; 252 } else { 253 arr[k] = right[j]!; 254 j++; 255 } 256 k++; 257 } 258 while (i < len1) { 259 arr[k] = left[i]!; 260 k++; 261 i++; 262 } 263 while (j < len2) { 264 arr[k] = right[j]!; 265 k++; 266 j++; 267 } 268} 269 270export function sort_stable<T>(arr: FixedArray<T>, startIndex: int, endIndex: int, comp: (lhs: T, rhs: T) => number): void { 271 if (endIndex <= startIndex) { 272 return; 273 } 274 275 const INS_SORT_DELTA = 16 276 for (let i = startIndex; i < endIndex; i += INS_SORT_DELTA ) { 277 builtin_insertion_sort<T>(arr, i, min(i + INS_SORT_DELTA , endIndex), comp) 278 } 279 if ((endIndex - startIndex) < INS_SORT_DELTA) { 280 return; 281 } 282 for (let size = INS_SORT_DELTA; size < endIndex; size = 2 * size) { 283 for (let left = startIndex; left < endIndex; left += 2 * size) { 284 285 // Find ending point of left subarray and right subarray 286 const mid = min(left + size - 1, endIndex - 1); 287 const right = min((left + 2 * size - 1), (endIndex - 1)); 288 289 // Merge sub array arr[left.....mid] and arr[mid+1....right] 290 if (mid < right) { 291 perform_merge(arr, left, mid, right, comp); 292 } 293 } 294 } 295} 296 297function defaultComparatorStr(a: String, b: String): number { 298 return a.compareTo(b) 299} 300 301type buffStr = String | undefined 302function stringified_insertion_sort<T>(arr: FixedArray<T>, arrStr: FixedArray<buffStr>, startIndex: int, endIndex: int): void { 303 for (let i = startIndex + 1; i < endIndex; i++) { 304 const tmp = arr[i] 305 const tmpStr = arrStr[i]; 306 if (defaultComparatorStr(tmpStr!, arrStr[startIndex]!) < 0) { 307 for (let j = i; j > startIndex; j--) { 308 arr[j] = arr[j - 1] 309 arrStr[j] = arrStr[j - 1] 310 } 311 arrStr[startIndex] = tmpStr 312 arr[startIndex] = tmp 313 } else { 314 let j = i - 1; 315 while (defaultComparatorStr(tmpStr!, arrStr[j]!) < 0) { 316 arr[j + 1] = arr[j]; 317 arrStr[j + 1] = arrStr[j] 318 j--; 319 } 320 arr[j + 1] = tmp; 321 arrStr[j + 1] = tmpStr 322 } 323 } 324} 325 326function stringified_perform_merge<T>(arr: FixedArray<T>, arrStr: FixedArray<buffStr>, startIndex: int, midIndex: int, endIndex: int): void { 327 const len1 = midIndex - startIndex + 1; 328 const len2 = endIndex - midIndex; 329 type buffType = T | undefined 330 let left : FixedArray<buffType> = new buffType[len1] 331 let right : FixedArray<buffType> = new buffType[len2] 332 let leftStr : FixedArray<buffStr> = new buffStr[len1] 333 let rightStr : FixedArray<buffStr> = new buffStr[len2] 334 for (let i = 0; i < len1; i++) { 335 left[i] = arr[startIndex + i] 336 leftStr[i] = arrStr[startIndex + i] 337 } 338 for (let i = 0; i < len2; i++) { 339 right[i] = arr[midIndex + 1 + i] 340 rightStr[i] = arrStr[midIndex + 1 + i] 341 } 342 let i = 0; 343 let j = 0; 344 let k = startIndex; 345 while (i < len1 && j < len2) { 346 if (defaultComparatorStr(leftStr[i]!, rightStr[j]!) <= 0) { 347 arr[k] = left[i]!; 348 arrStr[k] = leftStr[i]!; 349 i++; 350 } else { 351 arr[k] = right[j]!; 352 arrStr[k] = rightStr[j]!; 353 j++; 354 } 355 k++; 356 } 357 while (i < len1) { 358 arr[k] = left[i]!; 359 arrStr[k] = leftStr[i]!; 360 k++; 361 i++; 362 } 363 while (j < len2) { 364 arr[k] = right[j]!; 365 arrStr[k] = rightStr[j]!; 366 k++; 367 j++; 368 } 369} 370 371export function sort_default<T>(arr: FixedArray<T>, arrStr: FixedArray<buffStr>, startIndex: int, endIndex: int): void { 372 if (endIndex <= startIndex) { 373 return; 374 } 375 const INS_SORT_DELTA = 16 376 for (let i = startIndex; i < endIndex; i += INS_SORT_DELTA ) { 377 stringified_insertion_sort<T>(arr, arrStr, i, min(i + INS_SORT_DELTA , endIndex)) 378 } 379 380 if ((endIndex - startIndex) < INS_SORT_DELTA) { 381 return; 382 } 383 for (let size = INS_SORT_DELTA; size < endIndex; size = 2 * size) { 384 for (let left = startIndex; left < endIndex; left += 2 * size) { 385 386 // Find ending point of left subarray and right subarray 387 const mid = min(left + size - 1, endIndex - 1); 388 const right = min((left + 2 * size - 1), (endIndex - 1)); 389 390 // Merge sub array arr[left.....mid] and arr[mid+1....right] 391 if (mid < right) { 392 stringified_perform_merge(arr, arrStr, left, mid, right); 393 } 394 } 395 } 396} 397 398/** 399 * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. 400 * 401 * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. 402 * 403 * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. 404 * 405 * @returns a result after applying callbackfn over all elements of the Array 406 */ 407export function reduce<U, T>(self: Array<U>, callbackfn: (previousValue: T, currentValue: U, index: number, array: Array<U>) => T, initialValue: T): T { 408 let acc = initialValue 409 for (let i = 0; i < self.length; i++) { 410 acc = callbackfn(acc, self[i], i as number, self) 411 } 412 return acc 413} 414