/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace MongoDB.Bson.IO
{
///
/// Represents a fast converter from integer indexes to UTF8 BSON array element names.
///
internal interface IArrayElementNameAccelerator
{
///
/// Gets the element name bytes.
///
/// The index.
/// The element name bytes.
byte[] GetElementNameBytes(int index);
}
///
/// Represents a fast converter from integer indexes to UTF8 BSON array element names.
///
internal class ArrayElementNameAccelerator : IArrayElementNameAccelerator
{
#region static
// static fields
private static IArrayElementNameAccelerator __default = new ArrayElementNameAccelerator(1000);
// static properties
///
/// Gets or sets the default array element name accelerator.
///
public static IArrayElementNameAccelerator Default
{
get { return __default; }
set { __default = value; }
}
#endregion
// fields
private readonly byte[][] _cachedElementNames;
// constructors
///
/// Initializes a new instance of the class.
///
/// The number of cached element names.
public ArrayElementNameAccelerator(int numberOfCachedElementNames)
{
_cachedElementNames = new byte[numberOfCachedElementNames][];
for (int index = 0; index < numberOfCachedElementNames; index++)
{
_cachedElementNames[index] = CreateElementNameBytes(index);
}
}
// methods
private byte[] CreateElementNameBytes(int index)
{
// unrolled loop optimized for index values >= 1000 and < 10,000
const int asciiZero = 48;
var n = index;
var a = (byte)(asciiZero + n % 10);
n = n / 10;
var b = (byte)(asciiZero + n % 10);
n = n / 10;
var c = (byte)(asciiZero + n % 10);
n = n / 10;
var d = (byte)(asciiZero + n % 10);
n = n / 10;
if (n == 0)
{
if (d != (byte)asciiZero) { return new[] { d, c, b, a }; }
if (c != (byte)asciiZero) { return new[] { c, b, a }; }
if (b != (byte)asciiZero) { return new[] { b, a }; }
return new[] { a };
}
var e = (byte)(asciiZero + n % 10);
n = n / 10;
if (n == 0) { return new[] { e, d, c, b, a }; }
// really large indexes should be extremely rare and not worth optimizing further
return Utf8Encodings.Strict.GetBytes(index.ToString());
}
///
/// Gets the element name bytes.
///
/// The index.
///
/// The element name bytes.
///
public byte[] GetElementNameBytes(int index)
{
if (index < 0)
{
throw new ArgumentOutOfRangeException("index", "index is negative.");
}
if (index < _cachedElementNames.Length)
{
return _cachedElementNames[index];
}
return CreateElementNameBytes(index);
}
}
}