@@ -17,7 +17,11 @@ namespace Xtensive.Orm.Tests
1717 public static class AssemblyExtensions
1818 {
1919 private static readonly Type ObjectType = typeof ( object ) ;
20+ private static readonly string MainTestAsseblyNsPrefix = "Xtensive.Orm.Tests." ; // keep the dot at the end
21+ private static readonly byte [ ] ThisAssemblyPkt = typeof ( AssemblyExtensions ) . Assembly . GetName ( ) . GetPublicKeyToken ( ) ;
22+
2023 private static readonly ConcurrentDictionary < Assembly , Type [ ] > TypesPerAssembly = new ( ) ;
24+ private static readonly ConcurrentDictionary < char , int > XtensiveOrmTestsNsAlphabeticIndex = new ( ) ;
2125
2226 public static System . Configuration . Configuration GetAssemblyConfiguration ( this Assembly assembly )
2327 {
@@ -26,41 +30,64 @@ public static System.Configuration.Configuration GetAssemblyConfiguration(this A
2630
2731 public static IReadOnlyList < Type > GetTypesFromNamespaceCaching ( this Assembly assembly , string @namespace )
2832 {
33+ if ( string . IsNullOrWhiteSpace ( @namespace ) )
34+ throw new ArgumentException ( "Namespace cannot be null, empty or contains only white spaces" ) ;
35+
2936 // these two dummy mentions to not forget to sync filtration algorithm here and in the classes,
3037 // in particular BaseType property, if the property changed then this algorighm should be changed as well
3138 _ = nameof ( Xtensive . IoC . ServiceTypeRegistrationProcessor . BaseType ) ;
3239 _ = nameof ( Xtensive . Orm . Configuration . DomainTypeRegistrationHandler . BaseType ) ;
3340
34- var assemblyTypes = TypesPerAssembly . GetOrAdd ( assembly , static ( a ) => {
41+ var assemblyNameInfo = assembly . GetName ( ) ;
42+ var isMainTestAssembly = assemblyNameInfo . Name == "Xtensive.Orm.Tests" && ! ThisAssemblyPkt . Except ( assemblyNameInfo . GetPublicKeyToken ( ) ) . Any ( ) ;
43+
44+ var assemblyTypes = TypesPerAssembly . GetOrAdd ( assembly , static ( a , isMain ) => {
3545 var allTypes = a . GetTypes ( ) ;
3646 var list = new List < Type > ( allTypes . Length ) ;
47+ var currentIndex = 0 ;
3748 foreach ( var t in allTypes ) {
3849 // we ignore compiler generated types because usuallty they are
3950 // at the end of sorted types
40- if ( t . IsSubclassOf ( ObjectType ) && t . GetCustomAttribute < CompilerGeneratedAttribute > ( ) == null )
51+ if ( t . IsSubclassOf ( ObjectType ) && t . GetCustomAttribute < CompilerGeneratedAttribute > ( ) == null ) {
4152 list . Add ( t ) ;
53+ if ( isMain ) {
54+ if ( t . Namespace != null && t . Namespace . StartsWith ( MainTestAsseblyNsPrefix , StringComparison . Ordinal ) ) {
55+ var firstLetter = t . Namespace [ MainTestAsseblyNsPrefix . Length ] ;
56+ // main test library has 5000+ types, to not enumerate them every type from the beginning
57+ // we try to have parts by first letter
58+ _ = XtensiveOrmTestsNsAlphabeticIndex . TryAdd ( firstLetter , currentIndex ) ;
59+ }
60+ }
61+ currentIndex ++ ;
62+ }
4263 }
4364 return list . ToArray ( ) ;
44- } ) ;
65+ } , isMainTestAssembly ) ;
4566
46- var range = FindRange ( assemblyTypes , @namespace ) ;
67+ var range = FindRange ( assemblyTypes , @namespace , isMainTestAssembly ) ;
4768 return new ArraySegment < Type > ( assemblyTypes , range . first , range . last - range . first + 1 ) ;
4869
4970
5071 //type.IsSubclassOf(BaseType) && (ns.IsNullOrEmpty() || (type.FullName.IndexOf(ns + ".", StringComparison.InvariantCulture) >= 0));
5172 }
5273
53- private static ( int first , int last ) FindRange ( Type [ ] types , string ns )
74+ private static ( int first , int last ) FindRange ( Type [ ] types , string ns , bool isMainAssembly )
5475 {
5576 const int windowSize = 10 ;
5677
78+ var searchStart = ( isMainAssembly )
79+ ? ( ns . StartsWith ( MainTestAsseblyNsPrefix ) )
80+ ? XtensiveOrmTestsNsAlphabeticIndex [ ns [ MainTestAsseblyNsPrefix . Length ] ]
81+ : 0 //types from root namespace
82+ : 0 ;
83+
5784 // we rely on the fact that types are sorted by full name, that means types of same namespace are go one by one
5885 // which gives us to optimize search - we find first type that has desired namespace, then we try to find last one
5986 // and then we return the part of original array as result
6087 var firstHit = - 1 ;
6188 var lastHit = - 1 ;
6289
63- for ( int headIndex = 0 , count = types . Length ; headIndex < count ; headIndex ++ ) {
90+ for ( int headIndex = searchStart , count = types . Length ; headIndex < count ; headIndex ++ ) {
6491 var head = types [ headIndex ] ;
6592 if ( head . FullName . IndexOf ( ns + "." , StringComparison . InvariantCulture ) >= 0 ) {
6693 firstHit = headIndex ;
@@ -69,21 +96,24 @@ private static (int first, int last) FindRange(Type[] types, string ns)
6996 }
7097
7198 var isOutOfRange = false ;
99+ lastHit = firstHit ;
72100 do {
73- lastHit = firstHit + windowSize ;
101+ lastHit = lastHit + windowSize ;
74102 if ( lastHit > types . Length - 1 ) {
75103 lastHit = types . Length - 1 ;
76104 }
77105 var tail = types [ lastHit ] ;
78106 if ( tail . FullName . IndexOf ( ns + "." , StringComparison . InvariantCulture ) < 0 )
79107 isOutOfRange = true ;
108+ if ( lastHit < firstHit )
109+ throw new Exception ( "There is something strage in the neighborhood! :-)" ) ;
80110 }
81111 while ( ! isOutOfRange ) ;
82112
83- for ( int tailIndex = lastHit ; tailIndex > firstHit ; tailIndex -- ) {
113+ for ( int tailIndex = lastHit ; tailIndex >= firstHit ; tailIndex -- ) {
84114 var tail = types [ tailIndex ] ;
115+ lastHit = tailIndex ;
85116 if ( tail . FullName . IndexOf ( ns + "." , StringComparison . InvariantCulture ) >= 0 ) {
86- lastHit = tailIndex ;
87117 break ;
88118 }
89119 }
0 commit comments