// ----------------------------------------------------------------------- // // // 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. // // // ----------------------------------------------------------------------- namespace Lucene.Net.Util { using System; using System.Collections.Generic; using System.Linq; using System.Text; using Lucene.Net.Support; /// /// TODO: port /// Still missing methods that have to do with CharRef and ArrayUtil.Grow /// /// /// /// This class might be transformed into a immutable value type depending on its use /// in the Lucene code base. /// /// public sealed class BytesRef : IComparable, IEquatable, ICloneable { /// /// Returns an array of empty bytes. /// public static readonly byte[] EmptyBytes = new byte[0]; private static readonly UTF16Comparer utf16Comparer = new UTF16Comparer(); private static readonly UnicodeComparer unicodeComparer = new UnicodeComparer(); /// /// Initializes a new instance of the class. /// public BytesRef() { this.Bytes = EmptyBytes; } /// /// Initializes a new instance of the class. /// /// The bytes. /// The offset. /// The length. public BytesRef(byte[] bytes, int offset = 0, int length = 0) { if (bytes == null) throw new ArgumentNullException("bytes"); if (length == 0) length = bytes.Length; this.Bytes = bytes; this.Offset = offset; this.Length = length; } /// /// Initializes a new instance of the class. /// /// The capacity. public BytesRef(int capacity) { if (capacity < 0) throw new ArgumentException("capacity can not be less than 0", "capacity"); this.Bytes = new byte[capacity]; } /// /// Initializes a new instance of the class. /// /// The text. /// The offset. /// The length. public BytesRef(char[] text, int offset = 0, int length = 0) { if (text == null) throw new ArgumentNullException("text"); if (length == 0) length = text.Length; this.Bytes = new byte[length]; this.Copy(text, offset, length); } /// /// Initializes a new instance of the class. /// /// The text. public BytesRef(string text) : this() { if (text == null) throw new ArgumentNullException("text"); this.Copy(text); } /// /// Initializes a new instance of the class. /// /// The target. public BytesRef(BytesRef target) : this() { if (target == null) throw new ArgumentNullException("target"); this.Copy(target); } /// /// Gets the UTF8 sorted as UTF16 comparer. /// /// The UTF8 sorted as UTF16 comparer. public static IComparer Utf8SortedAsUtf16Comparer { get { return utf16Comparer; } } /// /// Gets the UTF8 sorted as Unicode comparer. /// /// The UTF8 sorted as Unicode comparer. public static IComparer Utf8SortedAsUnicodeComparer { get { return unicodeComparer; } } /// /// Gets or sets the bytes. /// /// The bytes. public byte[] Bytes { get; set; } /// /// Gets or sets the length. /// /// The length. public int Length { get; set; } /// /// Gets or sets the offset. /// /// The offset. public int Offset { get; set; } /// /// Implements the operator !=. /// /// The x. /// The y. /// The result of the operator. public static bool operator !=(BytesRef left, BytesRef right) { if (left == null) return right != null; return !left.Equals(right); } /// /// Implements the operator ==. /// /// The x. /// The y. /// The result of the operator. public static bool operator ==(BytesRef left, BytesRef right) { if (left == null) return right == null; return left.Equals(right); } /// /// Implements the operator <. /// /// The x. /// The y. /// The result of the operator. public static bool operator <(BytesRef left, BytesRef right) { return left.CompareTo(right) < 0; } /// /// Implements the operator >. /// /// The x. /// The y. /// The result of the operator. public static bool operator >(BytesRef left, BytesRef right) { return left.CompareTo(right) > 0; } /// /// Byteses the equal. /// /// The target. /// An instance of . public bool Equals(BytesRef target) { if (this.Length != target.Length) return false; int targetOffset = target.Offset, end = this.Offset + this.Length; byte[] targetBytes = target.Bytes; for (int i = this.Offset; i < end; i++, targetOffset++) { if (this.Bytes[i] != targetBytes[targetOffset]) return false; } return true; } /// /// Copies the specified source. /// /// The source. public void Copy(BytesRef source) { if (this.Bytes.Length < source.Length) this.Bytes = new byte[source.Length]; Array.Copy(source.Bytes, source.Offset, this.Bytes, 0, source.Length); this.Length = source.Length; this.Offset = 0; } /// /// Copies the specified source. /// /// The source. public void Copy(string source) { UnicodeUtil.UTF16toUTF8(source, 0, source.Length, this); } /// /// Copies the specified source. /// /// The source. /// The offset. /// The length. public void Copy(char[] source, int offset = 0, int length = 0) { if (length == 0) length = source.Length; UnicodeUtil.UTF16toUTF8(source, offset, length, this); } /// /// Clones this instance. /// /// a clone. public BytesRef Clone() { return new BytesRef(this); } /// /// Compares this instance to the other instance. /// /// The other. /// An instance of . public int CompareTo(BytesRef other) { if (this == other) return 0; byte[] leftBytes = this.Bytes, rightBytes = other.Bytes; int leftOffset = this.Offset, rightOffset = other.Offset, leftStop; leftStop = leftOffset + Math.Min(this.Length, other.Length); while (leftOffset < leftStop) { int leftByte = leftBytes[leftOffset++] & 0xff; int rightByte = rightBytes[rightOffset++] & 0xff; int difference = leftByte - rightByte; if (difference != 0) return difference; } return this.Length - other.Length; } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { int hashCode = 0, end = this.Offset + this.Length; for (int i = this.Offset; i < end; i++) { hashCode = (31 * hashCode) + this.Bytes[i]; } return hashCode; } /// /// Grows the to the specified length. /// /// The length. public void Grow(int length) { this.Bytes = ArrayUtil.Grow(this.Bytes, length); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object obj) { BytesRef bytesRef = obj as BytesRef; if (bytesRef == null) return false; return this.Equals(bytesRef); } /// /// Starts with the specified . /// /// The other. /// An instance of . public bool StartsWith(BytesRef other) { return this.SliceEquals(other, 0); } /// /// Ends with the specified . /// /// The other. /// An instance of . public bool EndsWith(BytesRef other) { return this.SliceEquals(other, this.Length - other.Length); } /// /// Returns a that represents this instance. Should return /// hex encoded bytes that looks something like: [0x6c 0x75 0x63 0x65 0x6e 0x65] /// /// /// A that represents this instance. /// public override string ToString() { var sb = new StringBuilder(); int end = this.Offset + this.Length; sb.Append('['); for (int i = this.Offset; i < end; i++) { if (i > this.Offset) sb.Append(' '); sb.Append((this.Bytes[i] & 0xf).ToString("X")); } sb.Append(']'); return sb.ToString(); } object ICloneable.Clone() { return this.Clone(); } private bool SliceEquals(BytesRef other, int position) { if (position < 0 || this.Length - position < other.Length) return false; int leftOffset = this.Offset + position; int rightOffset = other.Offset; int rightStop = other.Offset + other.Length; while (rightOffset < rightStop) { if (this.Bytes[leftOffset++] != other.Bytes[rightOffset++]) return false; } return true; } private class UnicodeComparer : IComparer { /// /// Compares the two objects to determine if they /// are equal or to see if the is greater than or less than the . /// /// The left. /// The right. /// a int the represents the comparison. public int Compare(BytesRef left, BytesRef right) { byte[] leftBytes = left.Bytes, rightBytes = right.Bytes; int leftOffset = left.Offset, rightOffset = right.Offset, leftStop; leftStop = leftOffset + (left.Length < right.Length ? left.Length : right.Length); while (leftOffset < leftStop) { int leftByte = leftBytes[leftOffset++] & 0xff; int rightByte = rightBytes[rightOffset++] & 0xff; int difference = leftByte - rightByte; if (difference != 0) return difference; } return left.Length - right.Length; } } private class UTF16Comparer : IComparer { /// /// Compares the two parameters using a UTF16Sorted Comparison. /// /// /// /// See http://icu-project.org/docs/papers/utf16_code_point_order.html#utf-8-in-utf-16-order /// We know the terms are not equal, but, we may have to carefully /// fixup the bytes at the difference to match UTF16's sort order. /// /// /// NOTE: instead of moving supplementary code points (0xee and 0xef) to the unused 0xfe and 0xff, /// we move them to the unused 0xfc and 0xfd [reserved for future 6-byte character sequences] /// this reserves 0xff for preflex's term reordering (surrogate dance), and if Unicode grows such /// that 6-byte sequences are needed we have much bigger problems anyway. /// /// /// The left value that is being compared. /// The right value that is being compared. /// the comparison int. public int Compare(BytesRef left, BytesRef right) { byte[] leftBytes = left.Bytes, rightBytes = right.Bytes; int leftOffset = left.Offset, rightOffset = right.Offset, leftStop = 0; leftStop = leftOffset + (left.Length < right.Length ? left.Length : right.Length); while (leftOffset < leftStop) { int leftByte = leftBytes[leftOffset++] & 0xff; int rightByte = rightBytes[rightOffset++] & 0xff; if (leftByte != rightByte) { if (leftByte >= 0xee & rightByte >= 0xee) { if ((leftByte & 0xfe) == 0xee) leftByte += 0xe; if ((rightByte & 0xfe) == 0xee) rightByte += 0xe; return leftByte - rightByte; } } } return right.Length - left.Length; } } } }