1+ #if NET40
2+ // Licensed to the .NET Foundation under one or more agreements.
3+ // The .NET Foundation licenses this file to you under the MIT license.
4+ // See the LICENSE file in the project root for more information.
5+
6+ using System ;
7+
8+ namespace JavaScriptEngineSwitcher . ChakraCore . Polyfills . System . Buffers
9+ {
10+ internal sealed partial class DefaultArrayPool < T > : ArrayPool < T >
11+ {
12+ /// <summary>
13+ /// The default maximum length of each array in the pool (2^20)
14+ /// </summary>
15+ private const int DefaultMaxArrayLength = 1024 * 1024 ;
16+
17+ /// <summary>
18+ /// The default maximum number of arrays per bucket that are available for rent
19+ /// </summary>
20+ private const int DefaultMaxNumberOfArraysPerBucket = 50 ;
21+
22+ /// <summary>
23+ /// Lazily-allocated empty array used when arrays of length 0 are requested
24+ /// </summary>
25+ private static T [ ] s_emptyArray ; // we support contracts earlier than those with Array.Empty<T>()
26+
27+ private readonly Bucket [ ] _buckets ;
28+
29+ /// <summary>
30+ /// Gets an ID for the pool to use with events
31+ /// </summary>
32+ private int Id
33+ {
34+ get { return GetHashCode ( ) ; }
35+ }
36+
37+
38+ internal DefaultArrayPool ( )
39+ : this ( DefaultMaxArrayLength , DefaultMaxNumberOfArraysPerBucket )
40+ { }
41+
42+ internal DefaultArrayPool ( int maxArrayLength , int maxArraysPerBucket )
43+ {
44+ if ( maxArrayLength <= 0 )
45+ {
46+ throw new ArgumentOutOfRangeException ( "maxArrayLength" ) ;
47+ }
48+
49+ if ( maxArraysPerBucket <= 0 )
50+ {
51+ throw new ArgumentOutOfRangeException ( "maxArraysPerBucket" ) ;
52+ }
53+
54+ // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30.
55+ // Constrain the actual max used to those values.
56+ const int MinimumArrayLength = 0x10 , MaximumArrayLength = 0x40000000 ;
57+ if ( maxArrayLength > MaximumArrayLength )
58+ {
59+ maxArrayLength = MaximumArrayLength ;
60+ }
61+ else if ( maxArrayLength < MinimumArrayLength )
62+ {
63+ maxArrayLength = MinimumArrayLength ;
64+ }
65+
66+ // Create the buckets.
67+ int poolId = Id ;
68+ int maxBuckets = Utilities . SelectBucketIndex ( maxArrayLength ) ;
69+ var buckets = new Bucket [ maxBuckets + 1 ] ;
70+
71+ for ( int i = 0 ; i < buckets . Length ; i ++ )
72+ {
73+ buckets [ i ] = new Bucket ( Utilities . GetMaxSizeForBucket ( i ) , maxArraysPerBucket , poolId ) ;
74+ }
75+
76+ _buckets = buckets ;
77+ }
78+
79+
80+ public override T [ ] Rent ( int minimumLength )
81+ {
82+ // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though
83+ // pooling such an array isn't valuable) as it's a valid length array, and we want the pool
84+ // to be usable in general instead of using `new`, even for computed lengths.
85+ if ( minimumLength < 0 )
86+ {
87+ throw new ArgumentOutOfRangeException ( "minimumLength" ) ;
88+ }
89+ else if ( minimumLength == 0 )
90+ {
91+ // No need for events with the empty array. Our pool is effectively infinite
92+ // and we'll never allocate for rents and never store for returns.
93+ return s_emptyArray ?? ( s_emptyArray = new T [ 0 ] ) ;
94+ }
95+
96+ T [ ] buffer = null ;
97+
98+ int index = Utilities . SelectBucketIndex ( minimumLength ) ;
99+ if ( index < _buckets . Length )
100+ {
101+ // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the
102+ // next higher bucket and try that one, but only try at most a few buckets.
103+ const int MaxBucketsToTry = 2 ;
104+ int i = index ;
105+
106+ do
107+ {
108+ // Attempt to rent from the bucket. If we get a buffer from it, return it.
109+ buffer = _buckets [ i ] . Rent ( ) ;
110+ if ( buffer != null )
111+ {
112+ return buffer ;
113+ }
114+ }
115+ while ( ++ i < _buckets . Length && i != index + MaxBucketsToTry ) ;
116+
117+ // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding
118+ // to the appropriate bucket.
119+ buffer = new T [ _buckets [ index ] . _bufferLength ] ;
120+ }
121+ else
122+ {
123+ // The request was for a size too large for the pool. Allocate an array of exactly the requested
124+ // length. When it's returned to the pool, we'll simply throw it away.
125+ buffer = new T [ minimumLength ] ;
126+ }
127+
128+ return buffer ;
129+ }
130+
131+ public override void Return ( T [ ] array )
132+ {
133+ if ( array == null )
134+ {
135+ throw new ArgumentNullException ( "array" ) ;
136+ }
137+ else if ( array . Length == 0 )
138+ {
139+ // Ignore empty arrays. When a zero-length array is rented, we return a singleton
140+ // rather than actually taking a buffer out of the lowest bucket.
141+ return ;
142+ }
143+
144+ // Determine with what bucket this array length is associated
145+ int bucket = Utilities . SelectBucketIndex ( array . Length ) ;
146+
147+ // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool
148+ if ( bucket < _buckets . Length )
149+ {
150+ // Return the buffer to its bucket. In the future, we might consider having Return return false
151+ // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket,
152+ // just as how in Rent we allow renting from a higher-sized bucket.
153+ _buckets [ bucket ] . Return ( array ) ;
154+ }
155+ }
156+ }
157+ }
158+ #endif
0 commit comments