A game about forced loneliness, made by TACStudios
at master 682 lines 22 kB view raw
1using System; 2using NUnit.Framework; 3using Unity.Burst; 4using Unity.Collections; 5using Unity.Collections.LowLevel.Unsafe; 6using Unity.Collections.NotBurstCompatible; 7using Unity.Collections.Tests; 8using Unity.Jobs; 9using UnityEngine; 10using UnityEngine.TestTools; 11using System.Text.RegularExpressions; 12 13internal class NativeParallelMultiHashMapTests : CollectionsTestFixture 14{ 15 [Test] 16 [TestRequiresCollectionChecks] 17 public void NativeParallelMultiHashMap_UseAfterFree_UsesCustomOwnerTypeName() 18 { 19 var container = new NativeParallelMultiHashMap<int, int>(10, CommonRwdAllocator.Handle); 20 container.Add(0, 123); 21 container.Dispose(); 22 Assert.That(() => container.ContainsKey(0), 23 Throws.Exception.TypeOf<ObjectDisposedException>() 24 .With.Message.Contains($"The {container.GetType()} has been deallocated")); 25 } 26 27 [BurstCompile(CompileSynchronously = true)] 28 struct NativeParallelMultiHashMap_CreateAndUseAfterFreeBurst : IJob 29 { 30 public void Execute() 31 { 32 var container = new NativeParallelMultiHashMap<int, int>(10, Allocator.Temp); 33 container.Add(0, 17); 34 container.Dispose(); 35 container.Add(1, 42); 36 } 37 } 38 39 [Test] 40 [TestRequiresCollectionChecks] 41 public void NativeParallelMultiHashMap_CreateAndUseAfterFreeInBurstJob_UsesCustomOwnerTypeName() 42 { 43 // Make sure this isn't the first container of this type ever created, so that valid static safety data exists 44 var container = new NativeParallelMultiHashMap<int, int>(10, CommonRwdAllocator.Handle); 45 container.Dispose(); 46 47 var job = new NativeParallelMultiHashMap_CreateAndUseAfterFreeBurst 48 { 49 }; 50 51 // Two things: 52 // 1. This exception is logged, not thrown; thus, we use LogAssert to detect it. 53 // 2. Calling write operation after container.Dispose() emits an unintuitive error message. For now, all this test cares about is whether it contains the 54 // expected type name. 55 job.Run(); 56 LogAssert.Expect(LogType.Exception, 57 new Regex($"InvalidOperationException: The {Regex.Escape(container.GetType().ToString())} has been declared as \\[ReadOnly\\] in the job, but you are writing to it")); 58 } 59 60 [Test] 61 public void NativeParallelMultiHashMap_IsEmpty() 62 { 63 var container = new NativeParallelMultiHashMap<int, int>(0, Allocator.Persistent); 64 Assert.IsTrue(container.IsEmpty); 65 66 container.Add(0, 0); 67 Assert.IsFalse(container.IsEmpty); 68 Assert.AreEqual(1, container.Capacity); 69 ExpectedCount(ref container, 1); 70 71 container.Remove(0, 0); 72 Assert.IsTrue(container.IsEmpty); 73 74 container.Add(0, 0); 75 container.Clear(); 76 Assert.IsTrue(container.IsEmpty); 77 78 container.Dispose(); 79 } 80 81 [Test] 82 public void NativeParallelMultiHashMap_CountValuesForKey() 83 { 84 var hashMap = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 85 hashMap.Add(5, 7); 86 hashMap.Add(6, 9); 87 hashMap.Add(6, 10); 88 89 Assert.AreEqual(1, hashMap.CountValuesForKey(5)); 90 Assert.AreEqual(2, hashMap.CountValuesForKey(6)); 91 Assert.AreEqual(0, hashMap.CountValuesForKey(7)); 92 93 hashMap.Dispose(); 94 } 95 96 [Test] 97 public void NativeParallelMultiHashMap_RemoveKeyAndValue() 98 { 99 var hashMap = new NativeParallelMultiHashMap<int, long>(1, Allocator.Temp); 100 hashMap.Add(10, 0); 101 hashMap.Add(10, 1); 102 hashMap.Add(10, 2); 103 104 hashMap.Add(20, 2); 105 hashMap.Add(20, 2); 106 hashMap.Add(20, 1); 107 hashMap.Add(20, 2); 108 hashMap.Add(20, 1); 109 110 hashMap.Remove(10, 1L); 111 ExpectValues(hashMap, 10, new[] { 0L, 2L }); 112 ExpectValues(hashMap, 20, new[] { 1L, 1L, 2L, 2L, 2L }); 113 114 hashMap.Remove(20, 2L); 115 ExpectValues(hashMap, 10, new[] { 0L, 2L }); 116 ExpectValues(hashMap, 20, new[] { 1L, 1L }); 117 118 hashMap.Remove(20, 1L); 119 ExpectValues(hashMap, 10, new[] { 0L, 2L }); 120 ExpectValues(hashMap, 20, new long[0]); 121 122 hashMap.Dispose(); 123 } 124 125 [Test] 126 public void NativeParallelMultiHashMap_ValueIterator() 127 { 128 var hashMap = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 129 hashMap.Add(5, 0); 130 hashMap.Add(5, 1); 131 hashMap.Add(5, 2); 132 133 var list = new NativeList<int>(CommonRwdAllocator.Handle); 134 135 GCAllocRecorder.ValidateNoGCAllocs(() => 136 { 137 list.Clear(); 138 foreach (var value in hashMap.GetValuesForKey(5)) 139 list.Add(value); 140 }); 141 142 list.Sort(); 143 Assert.AreEqual(list.ToArrayNBC(), new int[] { 0, 1, 2 }); 144 145 foreach (var value in hashMap.GetValuesForKey(6)) 146 Assert.Fail(); 147 148 list.Dispose(); 149 hashMap.Dispose(); 150 } 151 152 [Test] 153 public void NativeParallelMultiHashMap_RemoveKeyValueDoesntDeallocate() 154 { 155 var hashMap = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp) { { 5, 1 } }; 156 157 hashMap.Remove(5, 5); 158 GCAllocRecorder.ValidateNoGCAllocs(() => 159 { 160 hashMap.Remove(5, 1); 161 }); 162 Assert.IsTrue(hashMap.IsEmpty); 163 164 hashMap.Dispose(); 165 } 166 167 static void ExpectedCount<TKey, TValue>(ref NativeParallelMultiHashMap<TKey, TValue> container, int expected) 168 where TKey : unmanaged, IEquatable<TKey> 169 where TValue : unmanaged 170 { 171 Assert.AreEqual(expected == 0, container.IsEmpty); 172 Assert.AreEqual(expected, container.Count()); 173 } 174 175 [Test] 176 public void NativeParallelMultiHashMap_RemoveOnEmptyMap_DoesNotThrow() 177 { 178 var hashMap = new NativeParallelMultiHashMap<int, int>(0, Allocator.Temp); 179 180 Assert.DoesNotThrow(() => hashMap.Remove(0)); 181 Assert.DoesNotThrow(() => hashMap.Remove(-425196)); 182 Assert.DoesNotThrow(() => hashMap.Remove(0, 0)); 183 Assert.DoesNotThrow(() => hashMap.Remove(-425196, 0)); 184 185 hashMap.Dispose(); 186 } 187 188 [Test] 189 public void NativeParallelMultiHashMap_RemoveFromMultiHashMap() 190 { 191 var hashMap = new NativeParallelMultiHashMap<int, int>(16, Allocator.Temp); 192 int iSquared; 193 // Make sure inserting values work 194 for (int i = 0; i < 8; ++i) 195 hashMap.Add(i, i * i); 196 for (int i = 0; i < 8; ++i) 197 hashMap.Add(i, i); 198 Assert.AreEqual(16, hashMap.Capacity, "HashMap grew larger than expected"); 199 // Make sure reading the inserted values work 200 for (int i = 0; i < 8; ++i) 201 { 202 NativeParallelMultiHashMapIterator<int> it; 203 Assert.IsTrue(hashMap.TryGetFirstValue(i, out iSquared, out it), "Failed get value from hash table"); 204 Assert.AreEqual(iSquared, i, "Got the wrong value from the hash table"); 205 Assert.IsTrue(hashMap.TryGetNextValue(out iSquared, ref it), "Failed get value from hash table"); 206 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 207 } 208 for (int rm = 0; rm < 8; ++rm) 209 { 210 Assert.AreEqual(2, hashMap.Remove(rm)); 211 NativeParallelMultiHashMapIterator<int> it; 212 Assert.IsFalse(hashMap.TryGetFirstValue(rm, out iSquared, out it), "Failed to remove value from hash table"); 213 for (int i = rm + 1; i < 8; ++i) 214 { 215 Assert.IsTrue(hashMap.TryGetFirstValue(i, out iSquared, out it), "Failed get value from hash table"); 216 Assert.AreEqual(iSquared, i, "Got the wrong value from the hash table"); 217 Assert.IsTrue(hashMap.TryGetNextValue(out iSquared, ref it), "Failed get value from hash table"); 218 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 219 } 220 } 221 // Make sure entries were freed 222 for (int i = 0; i < 8; ++i) 223 hashMap.Add(i, i * i); 224 for (int i = 0; i < 8; ++i) 225 hashMap.Add(i, i); 226 Assert.AreEqual(16, hashMap.Capacity, "HashMap grew larger than expected"); 227 hashMap.Dispose(); 228 } 229 230 void ExpectValues(NativeParallelMultiHashMap<int, long> hashMap, int key, long[] expectedValues) 231 { 232 var list = new NativeList<long>(CommonRwdAllocator.Handle); 233 foreach (var value in hashMap.GetValuesForKey(key)) 234 list.Add(value); 235 236 list.Sort(); 237 Assert.AreEqual(list.ToArrayNBC(), expectedValues); 238 list.Dispose(); 239 } 240 241 [Test] 242 public void NativeParallelMultiHashMap_GetKeys() 243 { 244 var container = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 245 for (int i = 0; i < 30; ++i) 246 { 247 container.Add(i, 2 * i); 248 container.Add(i, 3 * i); 249 } 250 var keys = container.GetKeyArray(Allocator.Temp); 251 var (unique, uniqueLength) = container.GetUniqueKeyArray(Allocator.Temp); 252 Assert.AreEqual(30, uniqueLength); 253 254 Assert.AreEqual(60, keys.Length); 255 keys.Sort(); 256 for (int i = 0; i < 30; ++i) 257 { 258 Assert.AreEqual(i, keys[i * 2 + 0]); 259 Assert.AreEqual(i, keys[i * 2 + 1]); 260 Assert.AreEqual(i, unique[i]); 261 } 262 } 263 264 [Test] 265 public void NativeParallelMultiHashMap_GetUniqueKeysEmpty() 266 { 267 var hashMap = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 268 var keys = hashMap.GetUniqueKeyArray(Allocator.Temp); 269 270 Assert.AreEqual(0, keys.Item1.Length); 271 Assert.AreEqual(0, keys.Item2); 272 } 273 274 [Test] 275 public void NativeParallelMultiHashMap_GetUniqueKeys() 276 { 277 var hashMap = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 278 for (int i = 0; i < 30; ++i) 279 { 280 hashMap.Add(i, 2 * i); 281 hashMap.Add(i, 3 * i); 282 } 283 var keys = hashMap.GetUniqueKeyArray(Allocator.Temp); 284 hashMap.Dispose(); 285 Assert.AreEqual(30, keys.Item2); 286 for (int i = 0; i < 30; ++i) 287 { 288 Assert.AreEqual(i, keys.Item1[i]); 289 } 290 keys.Item1.Dispose(); 291 } 292 293 [Test] 294 public void NativeParallelMultiHashMap_GetValues() 295 { 296 var hashMap = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 297 for (int i = 0; i < 30; ++i) 298 { 299 hashMap.Add(i, 30 + i); 300 hashMap.Add(i, 60 + i); 301 } 302 var values = hashMap.GetValueArray(Allocator.Temp); 303 hashMap.Dispose(); 304 305 Assert.AreEqual(60, values.Length); 306 values.Sort(); 307 for (int i = 0; i < 60; ++i) 308 { 309 Assert.AreEqual(30 + i, values[i]); 310 } 311 values.Dispose(); 312 } 313 314 [Test] 315 public void NativeParallelMultiHashMap_ForEach_FixedStringInHashMap() 316 { 317 using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" }) 318 { 319 var container = new NativeParallelMultiHashMap<FixedString128Bytes, float>(50, Allocator.Temp); 320 var seen = new NativeArray<int>(stringList.Length, Allocator.Temp); 321 foreach (var str in stringList) 322 { 323 container.Add(str, 0); 324 } 325 326 foreach (var pair in container) 327 { 328 int index = stringList.IndexOf(pair.Key); 329 Assert.AreEqual(stringList[index], pair.Key.ToString()); 330 seen[index] = seen[index] + 1; 331 } 332 333 for (int i = 0; i < stringList.Length; i++) 334 { 335 Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}"); 336 } 337 } 338 } 339 340 [Test] 341 public void NativeParallelMultiHashMap_ForEach([Values(10, 1000)]int n) 342 { 343 var seenKeys = new NativeArray<int>(n, Allocator.Temp); 344 var seenValues = new NativeArray<int>(n * 2, Allocator.Temp); 345 using (var container = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp)) 346 { 347 for (int i = 0; i < n; ++i) 348 { 349 container.Add(i, i); 350 container.Add(i, i + n); 351 } 352 353 var count = 0; 354 foreach (var kv in container) 355 { 356 if (kv.Value < n) 357 { 358 Assert.AreEqual(kv.Key, kv.Value); 359 } 360 else 361 { 362 Assert.AreEqual(kv.Key + n, kv.Value); 363 } 364 365 seenKeys[kv.Key] = seenKeys[kv.Key] + 1; 366 seenValues[kv.Value] = seenValues[kv.Value] + 1; 367 368 ++count; 369 } 370 371 Assert.AreEqual(container.Count(), count); 372 for (int i = 0; i < n; i++) 373 { 374 Assert.AreEqual(2, seenKeys[i], $"Incorrect key count {i}"); 375 Assert.AreEqual(1, seenValues[i], $"Incorrect value count {i}"); 376 Assert.AreEqual(1, seenValues[i + n], $"Incorrect value count {i + n}"); 377 } 378 } 379 } 380 381 struct NativeParallelMultiHashMap_ForEach_Job : IJob 382 { 383 [ReadOnly] 384 public NativeParallelMultiHashMap<int, int> Input; 385 386 [ReadOnly] 387 public int Num; 388 389 public void Execute() 390 { 391 var seenKeys = new NativeArray<int>(Num, Allocator.Temp); 392 var seenValues = new NativeArray<int>(Num * 2, Allocator.Temp); 393 394 var count = 0; 395 foreach (var kv in Input) 396 { 397 if (kv.Value < Num) 398 { 399 Assert.AreEqual(kv.Key, kv.Value); 400 } 401 else 402 { 403 Assert.AreEqual(kv.Key + Num, kv.Value); 404 } 405 406 seenKeys[kv.Key] = seenKeys[kv.Key] + 1; 407 seenValues[kv.Value] = seenValues[kv.Value] + 1; 408 409 ++count; 410 } 411 412 Assert.AreEqual(Input.Count(), count); 413 for (int i = 0; i < Num; i++) 414 { 415 Assert.AreEqual(2, seenKeys[i], $"Incorrect key count {i}"); 416 Assert.AreEqual(1, seenValues[i], $"Incorrect value count {i}"); 417 Assert.AreEqual(1, seenValues[i + Num], $"Incorrect value count {i + Num}"); 418 } 419 420 seenKeys.Dispose(); 421 seenValues.Dispose(); 422 } 423 } 424 425 [Test] 426 public void NativeParallelMultiHashMap_ForEach_From_Job([Values(10, 1000)] int n) 427 { 428 using (var container = new NativeParallelMultiHashMap<int, int>(1, CommonRwdAllocator.Handle)) 429 { 430 for (int i = 0; i < n; ++i) 431 { 432 container.Add(i, i); 433 container.Add(i, i + n); 434 } 435 436 new NativeParallelMultiHashMap_ForEach_Job 437 { 438 Input = container, 439 Num = n, 440 441 }.Run(); 442 } 443 } 444 445 [Test] 446 [TestRequiresCollectionChecks] 447 public void NativeParallelMultiHashMap_ForEach_Throws_When_Modified() 448 { 449 using (var container = new NativeParallelMultiHashMap<int, int>(32, CommonRwdAllocator.Handle)) 450 { 451 for (int i = 0; i < 30; ++i) 452 { 453 container.Add(i, 30 + i); 454 container.Add(i, 60 + i); 455 } 456 457 Assert.Throws<ObjectDisposedException>(() => 458 { 459 foreach (var kv in container) 460 { 461 container.Add(10, 10); 462 } 463 }); 464 465 Assert.Throws<ObjectDisposedException>(() => 466 { 467 foreach (var kv in container) 468 { 469 container.Remove(1); 470 } 471 }); 472 } 473 } 474 475 struct NativeParallelMultiHashMap_ForEachIterator : IJob 476 { 477 [ReadOnly] 478 public NativeParallelMultiHashMap<int, int>.KeyValueEnumerator Iter; 479 480 public void Execute() 481 { 482 while (Iter.MoveNext()) 483 { 484 } 485 } 486 } 487 488 [Test] 489 [TestRequiresCollectionChecks] 490 public void NativeParallelMultiHashMap_ForEach_Throws_Job_Iterator() 491 { 492 using (var container = new NativeParallelMultiHashMap<int, int>(32, CommonRwdAllocator.Handle)) 493 { 494 var jobHandle = new NativeParallelMultiHashMap_ForEachIterator 495 { 496 Iter = container.GetEnumerator() 497 498 }.Schedule(); 499 500 Assert.Throws<InvalidOperationException>(() => { container.Add(1, 1); }); 501 502 jobHandle.Complete(); 503 } 504 } 505 506 struct ParallelWriteToMultiHashMapJob : IJobParallelFor 507 { 508 [WriteOnly] 509 public NativeParallelMultiHashMap<int, int>.ParallelWriter Writer; 510 511 public void Execute(int index) 512 { 513 Writer.Add(index, 0); 514 } 515 } 516 517 [Test] 518 [TestRequiresCollectionChecks] 519 public void NativeParallelMultiHashMap_ForEach_Throws_When_Modified_From_Job() 520 { 521 using (var container = new NativeParallelMultiHashMap<int, int>(32, CommonRwdAllocator.Handle)) 522 { 523 var iter = container.GetEnumerator(); 524 525 var jobHandle = new ParallelWriteToMultiHashMapJob 526 { 527 Writer = container.AsParallelWriter() 528 529 }.Schedule(1, 2); 530 531 Assert.Throws<ObjectDisposedException>(() => 532 { 533 while (iter.MoveNext()) 534 { 535 } 536 }); 537 538 jobHandle.Complete(); 539 } 540 } 541 542 [Test] 543 public void NativeParallelMultiHashMap_GetKeysAndValues() 544 { 545 var container = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 546 for (int i = 0; i < 30; ++i) 547 { 548 container.Add(i, 30 + i); 549 container.Add(i, 60 + i); 550 } 551 var keysValues = container.GetKeyValueArrays(Allocator.Temp); 552 container.Dispose(); 553 554 Assert.AreEqual(60, keysValues.Keys.Length); 555 Assert.AreEqual(60, keysValues.Values.Length); 556 557 // ensure keys and matching values are aligned (though unordered) 558 for (int i = 0; i < 30; ++i) 559 { 560 var k0 = keysValues.Keys[i * 2 + 0]; 561 var k1 = keysValues.Keys[i * 2 + 1]; 562 var v0 = keysValues.Values[i * 2 + 0]; 563 var v1 = keysValues.Values[i * 2 + 1]; 564 565 if (v0 > v1) 566 (v0, v1) = (v1, v0); 567 568 Assert.AreEqual(k0, k1); 569 Assert.AreEqual(30 + k0, v0); 570 Assert.AreEqual(60 + k0, v1); 571 } 572 573 keysValues.Keys.Sort(); 574 for (int i = 0; i < 30; ++i) 575 { 576 Assert.AreEqual(i, keysValues.Keys[i * 2 + 0]); 577 Assert.AreEqual(i, keysValues.Keys[i * 2 + 1]); 578 } 579 580 keysValues.Values.Sort(); 581 for (int i = 0; i < 60; ++i) 582 { 583 Assert.AreEqual(30 + i, keysValues.Values[i]); 584 } 585 586 keysValues.Dispose(); 587 } 588 589 [Test] 590 public void NativeParallelMultiHashMap_ContainsKeyMultiHashMap() 591 { 592 var container = new NativeParallelMultiHashMap<int, int>(1, Allocator.Temp); 593 container.Add(5, 7); 594 595 container.Add(6, 9); 596 container.Add(6, 10); 597 598 Assert.IsTrue(container.ContainsKey(5)); 599 Assert.IsTrue(container.ContainsKey(6)); 600 Assert.IsFalse(container.ContainsKey(4)); 601 602 container.Dispose(); 603 } 604 605 [Test] 606 public void NativeParallelMultiHashMap_CustomAllocatorTest() 607 { 608 AllocatorManager.Initialize(); 609 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 610 ref var allocator = ref allocatorHelper.Allocator; 611 allocator.Initialize(); 612 613 using (var container = new NativeParallelMultiHashMap<int, int>(1, allocator.Handle)) 614 { 615 } 616 617 Assert.IsTrue(allocator.WasUsed); 618 allocator.Dispose(); 619 allocatorHelper.Dispose(); 620 AllocatorManager.Shutdown(); 621 } 622 623 [BurstCompile] 624 struct BurstedCustomAllocatorJob : IJob 625 { 626 [NativeDisableUnsafePtrRestriction] 627 public unsafe CustomAllocatorTests.CountingAllocator* Allocator; 628 629 public void Execute() 630 { 631 unsafe 632 { 633 using (var container = new NativeParallelMultiHashMap<int, int>(1, Allocator->Handle)) 634 { 635 } 636 } 637 } 638 } 639 640 [Test] 641 public unsafe void NativeParallelMultiHashMap_BurstedCustomAllocatorTest() 642 { 643 AllocatorManager.Initialize(); 644 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 645 ref var allocator = ref allocatorHelper.Allocator; 646 allocator.Initialize(); 647 648 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator); 649 unsafe 650 { 651 var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule(); 652 handle.Complete(); 653 } 654 655 Assert.IsTrue(allocator.WasUsed); 656 allocator.Dispose(); 657 allocatorHelper.Dispose(); 658 AllocatorManager.Shutdown(); 659 } 660 661 public struct NestedHashMap 662 { 663 public NativeParallelMultiHashMap<int, int> map; 664 } 665 666 [Test] 667 public void NativeParallelMultiHashMap_Nested() 668 { 669 var mapInner = new NativeParallelMultiHashMap<int, int>(16, CommonRwdAllocator.Handle); 670 NestedHashMap mapStruct = new NestedHashMap { map = mapInner }; 671 672 var mapNestedStruct = new NativeParallelMultiHashMap<int, NestedHashMap>(16, CommonRwdAllocator.Handle); 673 var mapNested = new NativeParallelMultiHashMap<int, NativeParallelMultiHashMap<int, int>>(16, CommonRwdAllocator.Handle); 674 675 mapNested.Add(14, mapInner); 676 mapNestedStruct.Add(17, mapStruct); 677 678 mapNested.Dispose(); 679 mapNestedStruct.Dispose(); 680 mapInner.Dispose(); 681 } 682}