blob: e3a396b0bea517731491364df4ffb1f7972d960a [file] [log] [blame]
using Lucene.Net.Support;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Lucene.Net.Util
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/// <summary>
/// Represents char[], as a slice (offset + Length) into an existing char[].
/// The <seealso cref="#chars"/> member should never be null; use
/// <seealso cref="#EMPTY_CHARS"/> if necessary.
/// @lucene.internal
/// </summary>
public sealed class CharsRef : IComparable<CharsRef>, ICharSequence, ICloneable
{
/// <summary>
/// An empty character array for convenience </summary>
public static readonly char[] EMPTY_CHARS = new char[0];
/// <summary>
/// The contents of the CharsRef. Should never be {@code null}.
/// </summary>
public char[] Chars { get; set; }
/// <summary>
/// Offset of first valid character. </summary>
public int Offset { get; internal set; }
/// <summary>
/// Length of used characters. </summary>
public int Length { get; set; }
/// <summary>
/// Creates a new <seealso cref="CharsRef"/> initialized an empty array zero-Length
/// </summary>
public CharsRef()
: this(EMPTY_CHARS, 0, 0)
{
}
/// <summary>
/// Creates a new <seealso cref="CharsRef"/> initialized with an array of the given
/// capacity
/// </summary>
public CharsRef(int capacity)
{
Chars = new char[capacity];
}
/// <summary>
/// Creates a new <seealso cref="CharsRef"/> initialized with the given array, offset and
/// Length
/// </summary>
public CharsRef(char[] chars, int offset, int Length)
{
this.Chars = chars;
this.Offset = offset;
this.Length = Length;
Debug.Assert(Valid);
}
/// <summary>
/// Creates a new <seealso cref="CharsRef"/> initialized with the given Strings character
/// array
/// </summary>
public CharsRef(string @string)
{
this.Chars = @string.ToCharArray();
this.Offset = 0;
this.Length = Chars.Length;
}
/// <summary>
/// Returns a shallow clone of this instance (the underlying characters are
/// <b>not</b> copied and will be shared by both the returned object and this
/// object.
/// </summary>
/// <seealso cref= #deepCopyOf </seealso>
public object Clone()
{
return new CharsRef(Chars, Offset, Length);
}
public override int GetHashCode()
{
const int prime = 31;
int result = 0;
int end = Offset + Length;
for (int i = Offset; i < end; i++)
{
result = prime * result + Chars[i];
}
return result;
}
public override bool Equals(object other)
{
if (other == null)
{
return false;
}
var @ref = other as CharsRef;
if (@ref != null)
{
return this.CharsEquals(@ref);
}
return false;
}
public bool CharsEquals(CharsRef other)
{
if (Length == other.Length)
{
int otherUpto = other.Offset;
char[] otherChars = other.Chars;
int end = Offset + Length;
for (int upto = Offset; upto < end; upto++, otherUpto++)
{
if (Chars[upto] != otherChars[otherUpto])
{
return false;
}
}
return true;
}
else
{
return false;
}
}
/// <summary>
/// Signed int order comparison </summary>
public int CompareTo(CharsRef other)
{
if (this == other)
{
return 0;
}
char[] aChars = this.Chars;
int aUpto = this.Offset;
char[] bChars = other.Chars;
int bUpto = other.Offset;
int aStop = aUpto + Math.Min(this.Length, other.Length);
while (aUpto < aStop)
{
int aInt = aChars[aUpto++];
int bInt = bChars[bUpto++];
if (aInt > bInt)
{
return 1;
}
else if (aInt < bInt)
{
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return this.Length - other.Length;
}
/// <summary>
/// Copies the given <seealso cref="CharsRef"/> referenced content into this instance.
/// </summary>
/// <param name="other">
/// the <seealso cref="CharsRef"/> to copy </param>
public void CopyChars(CharsRef other)
{
CopyChars(other.Chars, other.Offset, other.Length);
}
/// <summary>
/// Used to grow the reference array.
///
/// In general this should not be used as it does not take the offset into account.
/// @lucene.internal
/// </summary>
public void Grow(int newLength)
{
Debug.Assert(Offset == 0);
if (Chars.Length < newLength)
{
Chars = ArrayUtil.Grow(Chars, newLength);
}
}
/// <summary>
/// Copies the given array into this CharsRef.
/// </summary>
public void CopyChars(char[] otherChars, int otherOffset, int otherLength)
{
if (Chars.Length - Offset < otherLength)
{
Chars = new char[otherLength];
Offset = 0;
}
Array.Copy(otherChars, otherOffset, Chars, Offset, otherLength);
Length = otherLength;
}
/// <summary>
/// Appends the given array to this CharsRef
/// </summary>
public void Append(char[] otherChars, int otherOffset, int otherLength)
{
int newLen = Length + otherLength;
if (Chars.Length - Offset < newLen)
{
var newChars = new char[newLen];
Array.Copy(Chars, Offset, newChars, 0, Length);
Offset = 0;
Chars = newChars;
}
Array.Copy(otherChars, otherOffset, Chars, Length + Offset, otherLength);
Length = newLen;
}
public override string ToString()
{
return new string(Chars, Offset, Length);
}
public char CharAt(int index)
{
// NOTE: must do a real check here to meet the specs of CharSequence
if (index < 0 || index >= Length)
{
throw new System.IndexOutOfRangeException();
}
return Chars[Offset + index];
}
public ICharSequence SubSequence(int start, int end)
{
// NOTE: must do a real check here to meet the specs of CharSequence
if (start < 0 || end > Length || start > end)
{
throw new System.IndexOutOfRangeException();
}
return new CharsRef(Chars, Offset + start, end - start);
}
/// @deprecated this comparator is only a transition mechanism
[Obsolete("this comparator is only a transition mechanism")]
private static readonly IComparer<CharsRef> Utf16SortedAsUTF8SortOrder = new UTF16SortedAsUTF8Comparator();
/// @deprecated this comparator is only a transition mechanism
[Obsolete("this comparator is only a transition mechanism")]
public static IComparer<CharsRef> UTF16SortedAsUTF8Comparer
{
get
{
return Utf16SortedAsUTF8SortOrder;
}
}
/// @deprecated this comparator is only a transition mechanism
[Obsolete("this comparator is only a transition mechanism")]
private class UTF16SortedAsUTF8Comparator : IComparer<CharsRef>
{
// Only singleton
internal UTF16SortedAsUTF8Comparator()
{
}
public virtual int Compare(CharsRef a, CharsRef b)
{
if (a == b)
{
return 0;
}
char[] aChars = a.Chars;
int aUpto = a.Offset;
char[] bChars = b.Chars;
int bUpto = b.Offset;
int aStop = aUpto + Math.Min(a.Length, b.Length);
while (aUpto < aStop)
{
char aChar = aChars[aUpto++];
char bChar = bChars[bUpto++];
if (aChar != bChar)
{
// http://icu-project.org/docs/papers/utf16_code_point_order.html
/* aChar != bChar, fix up each one if they're both in or above the surrogate range, then compare them */
if (aChar >= 0xd800 && bChar >= 0xd800)
{//LUCENE TO-DO possible truncation or is char 16bit?
if (aChar >= 0xe000)
{
aChar -= (char)0x800;
}
else
{
aChar += (char)0x2000;
}
if (bChar >= 0xe000)
{
bChar -= (char)0x800;
}
else
{
bChar += (char)0x2000;
}
}
/* now aChar and bChar are in code point order */
return (int)aChar - (int)bChar; // int must be 32 bits wide
}
}
// One is a prefix of the other, or, they are equal:
return a.Length - b.Length;
}
}
/// <summary>
/// Creates a new CharsRef that points to a copy of the chars from
/// <code>other</code>
/// <p>
/// The returned CharsRef will have a Length of other.Length
/// and an offset of zero.
/// </summary>
public static CharsRef DeepCopyOf(CharsRef other)
{
CharsRef clone = new CharsRef();
clone.CopyChars(other);
return clone;
}
/// <summary>
/// Performs internal consistency checks.
/// Always returns true (or throws InvalidOperationException)
/// </summary>
public bool Valid
{
get
{
if (Chars == null)
{
throw new InvalidOperationException("chars is null");
}
if (Length < 0)
{
throw new InvalidOperationException("Length is negative: " + Length);
}
if (Length > Chars.Length)
{
throw new InvalidOperationException("Length is out of bounds: " + Length + ",chars.Length=" + Chars.Length);
}
if (Offset < 0)
{
throw new InvalidOperationException("offset is negative: " + Offset);
}
if (Offset > Chars.Length)
{
throw new InvalidOperationException("offset out of bounds: " + Offset + ",chars.Length=" + Chars.Length);
}
if (Offset + Length < 0)
{
throw new InvalidOperationException("offset+Length is negative: offset=" + Offset + ",Length=" + Length);
}
if (Offset + Length > Chars.Length)
{
throw new InvalidOperationException("offset+Length out of bounds: offset=" + Offset + ",Length=" + Length + ",chars.Length=" + Chars.Length);
}
return true;
}
}
}
}