/src/lib/storage/collection/array.e
Specman e | 495 lines | 396 code | 43 blank | 56 comment | 26 complexity | dcb44b1da02b7ad7b6fe6d62411c7e89 MD5 | raw file
1-- This file is part of a Liberty Eiffel library. 2-- See the full copyright at the end. 3-- 4class ARRAY[E_] 5 -- 6 -- General purpose resizable ARRAYs as they are define in the Eiffel language definition. 7 -- The `lower' bound can be any arbitrary value, even a negative one. 8 -- 9 -- This implementation uses only one chunk of memory, the `storage' area which is a NATIVE_ARRAY. One must 10 -- keep in mind that this internal `storage' area is always kept left align. Thus, you can expect good 11 -- performances while using an ARRAY to modelize a stack behavior with `add_last' / `last' / `remove_last'. 12 -- Conversely `add_first' and `remove_first' are likely to slow down your program if they are too often 13 -- used. If the fact that `lower' is always stuck to 0 is not a problem for you, also consider FAST_ARRAY to 14 -- get better performances. 15 -- 16 17inherit 18 COLLECTION[E_] 19 redefine default_create 20 end 21 ARRAYED_COLLECTION[E_] 22 redefine default_create 23 end 24 25insert 26 NATIVE_ARRAY_COLLECTOR[E_] 27 undefine default_create, out_in_tagged_out_memory 28 redefine default_create 29 end 30 31create {ANY} 32 default_create, make, with_capacity, from_collection, manifest_creation 33 34feature {} 35 default_create 36 do 37 make(0, -1) 38 end 39 40feature {ANY} 41 lower: INTEGER 42 -- Lower index bound. 43 44feature {ANY} -- Creation and Modification: 45 make (min_index, max_index: INTEGER) 46 -- Prepare the array to hold values for indexes in range 47 -- [`min_index' .. `max_index']. Set all values to default. 48 -- When `max_index' = `min_index' - 1, the array `is_empty'. 49 require 50 valid_bounds: min_index <= max_index + 1 51 do 52 ensure_capacity_and_bounds(max_index - min_index + 1, min_index, max_index) 53 ensure 54 lower_set: lower = min_index 55 upper_set: upper = max_index 56 items_set: all_default 57 end 58 59 with_capacity (needed_capacity, low: INTEGER) 60 -- Create an empty array with `capacity' initialized 61 -- at least to `needed_capacity' and `lower' set to `low'. 62 require 63 needed_capacity >= 0 64 do 65 ensure_capacity_and_bounds(needed_capacity, low, low - 1) 66 ensure 67 is_empty 68 needed_capacity <= capacity 69 lower = low 70 end 71 72feature {} 73 ensure_capacity_and_bounds (needed_capacity, low, up: INTEGER) 74 require 75 up >= low - 1 76 needed_capacity >= up - low + 1 77 local 78 value: like item 79 do 80 if value = Void and then capacity > 0 then 81 -- Be sure to release all objects. Useful only if the objects are references. 82 clear_all 83 end 84 lower := low 85 upper := up 86 if needed_capacity > 0 then 87 if capacity < needed_capacity then 88 storage := storage.calloc(needed_capacity) 89 capacity := needed_capacity 90 elseif value /= Void and then up >= low then 91 -- Ensure the postcondition. Useful only for expanded objects since references were purged above. 92 clear_all 93 end 94 end 95 next_generation 96 ensure 97 needed_capacity <= capacity 98 lower = low 99 upper = up 100 all_default 101 end 102 103feature {ANY} -- Modification: 104 resize (min_index, max_index: INTEGER) 105 -- Resize to bounds `min_index' and `max_index'. Do not lose any 106 -- item whose index is in both [`lower' .. `upper'] and 107 -- [`min_index' .. `max_index']. New positions if any are 108 -- initialized with the appropriate default value. 109 require 110 min_index <= max_index + 1 111 local 112 needed, offset, intersize: INTEGER 113 do 114 needed := max_index - min_index + 1 115 if needed > 0 then 116 if needed > capacity then 117 if capacity = 0 then 118 storage := storage.calloc(needed) 119 capacity := needed 120 else 121 storage := storage.realloc(capacity, needed) 122 capacity := needed 123 end 124 end 125 offset := lower - min_index 126 intersize := max_index.min(upper) - min_index.max(lower) + 1 127 if intersize > 0 then 128 if offset = 0 then 129 if intersize <= upper - lower then 130 storage.clear(intersize, upper - lower) 131 end 132 elseif offset < 0 then 133 storage.move(-offset, intersize - offset - 1, offset) 134 storage.clear(intersize, upper - lower) 135 else 136 storage.move(0, intersize - 1, offset) 137 storage.clear(0, offset - 1) 138 if intersize + offset <= upper - lower then 139 storage.clear(intersize + offset, upper - lower) 140 end 141 end 142 else 143 storage.clear(0, upper - lower) 144 end 145 elseif upper >= lower then 146 storage.clear(0, upper - lower) 147 end 148 lower := min_index 149 upper := max_index 150 next_generation 151 ensure 152 lower = min_index 153 upper = max_index 154 end 155 156 reindex (new_lower: INTEGER) 157 -- Change indexing to take in account the expected `new_lower' 158 -- index. The `upper' index is translated accordingly. 159 local 160 i: INTEGER 161 do 162 i := new_lower - lower 163 lower := lower + i 164 upper := upper + i 165 next_generation 166 ensure 167 lower = new_lower 168 count = old count 169 end 170 171feature {ANY} -- Implementation of deferred: 172 count: INTEGER 173 do 174 Result := upper - lower + 1 175 end 176 177 is_empty: BOOLEAN 178 do 179 Result := upper < lower 180 end 181 182 subarray (min, max: INTEGER): like Current 183 do 184 Result := slice(min, max) 185 Result.reindex(min) 186 ensure then 187 Result.lower = min 188 end 189 190 item (i: INTEGER): E_ 191 do 192 Result := storage.item(i - lower) 193 end 194 195 put (element: like item; i: INTEGER) 196 do 197 storage.put(element, i - lower) 198 next_generation 199 end 200 201 force (element: like item; index: INTEGER) 202 require else 203 True 204 do 205 if upper < index then 206 if index = upper + 1 then 207 add_last(element) 208 else 209 resize(lower, index) 210 put(element, index) 211 end 212 elseif index < lower then 213 resize(index, upper) 214 put(element, index) 215 else 216 put(element, index) 217 end 218 ensure then 219 lower = index.min(old lower) 220 end 221 222 copy (other: like Current) 223 local 224 needed_capacity: INTEGER 225 do 226 lower := other.lower 227 upper := other.upper 228 needed_capacity := upper - lower + 1 229 if capacity < needed_capacity then 230 storage := storage.calloc(needed_capacity) 231 capacity := needed_capacity 232 end 233 if needed_capacity > 0 then 234 storage.copy_from(other.storage, needed_capacity - 1) 235 end 236 next_generation 237 end 238 239 set_all_with (v: like item) 240 do 241 storage.set_all_with(v, upper - lower) 242 next_generation 243 end 244 245 remove_first 246 do 247 storage.remove_first(upper - lower) 248 lower := lower + 1 249 next_generation 250 ensure then 251 upper = old upper 252 end 253 254 remove_head (n: INTEGER) 255 do 256 storage.move(n, upper - lower, -n) 257 lower := lower + n 258 next_generation 259 ensure then 260 upper = old upper 261 end 262 263 remove (index: INTEGER) 264 do 265 storage.remove(index - lower, upper - lower) 266 upper := upper - 1 267 next_generation 268 end 269 270 clear_count, clear_count_and_capacity 271 do 272 upper := lower - 1 273 next_generation 274 ensure then 275 capacity = old capacity 276 end 277 278 add_first (element: like item) 279 do 280 if upper < lower then 281 add_last(element) 282 else 283 add_last(element) 284 move(lower, upper - 1, 1) 285 put(element, lower) 286 end 287 end 288 289 add_last (element: like item) 290 local 291 new_capacity: INTEGER 292 do 293 if capacity < count + 1 then 294 if capacity = 0 then 295 new_capacity := 16 296 storage := storage.calloc(new_capacity) 297 capacity := new_capacity 298 else 299 new_capacity := capacity * 2 300 storage := storage.realloc(capacity, new_capacity) 301 capacity := new_capacity 302 end 303 end 304 upper := upper + 1 305 put(element, upper) 306 end 307 308 from_collection (model: TRAVERSABLE[like item]) 309 local 310 i, up: INTEGER 311 do 312 from 313 with_capacity(model.count, model.lower) 314 i := model.lower 315 up := model.upper 316 upper := up 317 until 318 i > up 319 loop 320 put(model.item(i), i) 321 i := i + 1 322 end 323 ensure then 324 lower = model.lower 325 upper = model.upper 326 end 327 328 all_default: BOOLEAN 329 do 330 Result := storage.all_default(upper - lower) 331 end 332 333 occurrences (element: like item): INTEGER 334 do 335 Result := storage.occurrences(element, upper - lower) 336 end 337 338 fast_occurrences (element: like item): INTEGER 339 do 340 Result := storage.fast_occurrences(element, upper - lower) 341 end 342 343 first_index_of (element: like item): INTEGER 344 do 345 if upper >= lower then 346 Result := lower + storage.first_index_of(element, upper - lower) 347 else 348 Result := lower 349 end 350 end 351 352 index_of (element: like item; start_index: INTEGER): INTEGER 353 do 354 if upper >= lower then 355 Result := lower + storage.index_of(element, start_index - lower, upper - lower) 356 else 357 Result := lower 358 end 359 end 360 361 reverse_index_of (element: like item; start_index: INTEGER): INTEGER 362 do 363 if upper >= lower then 364 Result := lower + storage.reverse_index_of(element, start_index - lower) 365 else 366 Result := lower 367 end 368 end 369 370 fast_first_index_of (element: like item): INTEGER 371 do 372 if upper >= lower then 373 Result := lower + storage.fast_first_index_of(element, upper - lower) 374 else 375 Result := lower 376 end 377 end 378 379 fast_index_of (element: like item; start_index: INTEGER): INTEGER 380 do 381 if upper >= lower then 382 Result := lower + storage.fast_index_of(element, start_index - lower, upper - lower) 383 else 384 Result := lower 385 end 386 end 387 388 fast_reverse_index_of (element: like item; start_index: INTEGER): INTEGER 389 do 390 if upper >= lower then 391 Result := lower + storage.fast_reverse_index_of(element, start_index - lower) 392 else 393 Result := lower 394 end 395 end 396 397 fast_is_equal (other: like Current): BOOLEAN 398 do 399 if Current = other then 400 Result := True 401 elseif lower = other.lower and then upper = other.upper then 402 Result := storage.fast_memcmp(other.storage, count) 403 end 404 end 405 406 is_equal (other: like Current): BOOLEAN 407 do 408 if Current = other then 409 Result := True 410 elseif lower = other.lower and then upper = other.upper then 411 Result := storage.memcmp(other.storage, count) 412 end 413 end 414 415 slice (min, max: INTEGER): like Current 416 local 417 null: POINTER 418 do 419 Result := standard_twin 420 Result.set_upper(Result.lower - 1) 421 Result.from_external(null, 0) 422 423 if max >= min then 424 -- Slice not empty 425 Result.make(lower, lower + max - min) 426 Result.storage.slice_copy(0, storage, min - lower, max - lower) 427 else 428 Result.with_capacity(0, lower) 429 end 430 end 431 432 new_iterator: ITERATOR[E_] 433 do 434 create {ITERATOR_ON_TRAVERSABLE[E_]} Result.make(Current) 435 end 436 437feature {} -- Garbage collector tuning (very low-level): 438 mark_native_arrays 439 -- For performance reasons, the unused area of `storage' is always left as it is when 440 -- some elements are removed. No time is lost to clean the released area with a Void 441 -- or a 0 value. (Look for example the `remove_last' implementation.) 442 -- Thus, the unused area of `storage' may contains references of actually unreachable 443 -- objects. The following `mark_native_arrays' actually replace the 444 -- default behavior (the call is automatic) in order to mark only reachable objects. 445 local 446 i: INTEGER 447 do 448 from 449 i := count - 1 450 until 451 i < 0 452 loop 453 mark_item(storage, i) 454 i := i - 1 455 end 456 end 457 458feature {} -- Implement manifest generic creation (very low-level): 459 manifest_make (needed_capacity: INTEGER; initial_lower: INTEGER) 460 -- Manifest creation of an ARRAY[E_] with `lower' set to `initial_lower'. 461 require 462 needed_capacity > 0 463 do 464 make(initial_lower, initial_lower + needed_capacity - 1) 465 end 466 467 manifest_put (index: INTEGER; element: like item) 468 do 469 check 470 index < capacity 471 end 472 storage.put(element, index) 473 end 474 475end -- class ARRAY 476-- 477-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file. 478-- 479-- Permission is hereby granted, free of charge, to any person obtaining a copy 480-- of this software and associated documentation files (the "Software"), to deal 481-- in the Software without restriction, including without limitation the rights 482-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 483-- copies of the Software, and to permit persons to whom the Software is 484-- furnished to do so, subject to the following conditions: 485-- 486-- The above copyright notice and this permission notice shall be included in 487-- all copies or substantial portions of the Software. 488-- 489-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 490-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 491-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 492-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 493-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 494-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 495-- THE SOFTWARE.