// // BEncodedString.cs // // Authors: // Alan McGovern alan.mcgovern@gmail.com // // Copyright (C) 2006 Alan McGovern // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.IO; using System.Collections; using System.Text; using Tancoder.Torrent.Common; using Tancoder.Torrent.Client.Messages; namespace Tancoder.Torrent.BEncoding { /// /// Class representing a BEncoded string /// public class BEncodedString : BEncodedValue, IComparable { #region Member Variables /// /// The value of the BEncodedString /// public string Text { get { return Encoding.UTF8.GetString(textBytes); } set { textBytes = Encoding.UTF8.GetBytes(value); } } /// /// The underlying byte[] associated with this BEncodedString /// public byte[] TextBytes { get { return this.textBytes; } } private byte[] textBytes; #endregion #region Constructors /// /// Create a new BEncodedString using UTF8 encoding /// public BEncodedString() : this(new byte[0]) { } /// /// Create a new BEncodedString using UTF8 encoding /// /// public BEncodedString(char[] value) : this(System.Text.Encoding.UTF8.GetBytes(value)) { } /// /// Create a new BEncodedString using UTF8 encoding /// /// Initial value for the string public BEncodedString(string value) : this(System.Text.Encoding.UTF8.GetBytes(value)) { } /// /// Create a new BEncodedString using UTF8 encoding /// /// public BEncodedString(byte[] value) { this.textBytes = value; } public static implicit operator BEncodedString(string value) { return new BEncodedString(value); } public static implicit operator BEncodedString(char[] value) { return new BEncodedString(value); } public static implicit operator BEncodedString(byte[] value) { return new BEncodedString(value); } #endregion #region Encode/Decode Methods /// /// Encodes the BEncodedString to a byte[] using the supplied Encoding /// /// The buffer to encode the string to /// The offset at which to save the data to /// The encoding to use /// The number of bytes encoded public override int Encode(byte[] buffer, int offset) { int written = offset; written += Message.WriteAscii(buffer, written, textBytes.Length.ToString ()); written += Message.WriteAscii(buffer, written, ":"); written += Message.Write(buffer, written, textBytes); return written - offset; } /// /// Decodes a BEncodedString from the supplied StreamReader /// /// The StreamReader containing the BEncodedString internal override void DecodeInternal(RawReader reader) { if (reader == null) throw new ArgumentNullException("reader"); int letterCount; string length = string.Empty; while ((reader.PeekByte() != -1) && (reader.PeekByte() != ':')) // read in how many characters length += (char)reader.ReadByte(); // the string is if (reader.ReadByte() != ':') // remove the ':' throw new BEncodingException(string.Format("Invalid data found at {0}. Aborting", reader.Position)); if (!int.TryParse(length, out letterCount)) throw new BEncodingException(string.Format("Invalid BEncodedString. Length was '{0}' instead of a number", length)); this.textBytes = new byte[letterCount]; if (reader.Read(textBytes, 0, letterCount) != letterCount) throw new BEncodingException("Couldn't decode string"); } #endregion #region Helper Methods public string Hex { get { return BitConverter.ToString(TextBytes); } } public override int LengthInBytes() { // The length is equal to the length-prefix + ':' + length of data int prefix = 1; // Account for ':' // Count the number of characters needed for the length prefix for (int i = textBytes.Length; i != 0; i = i/10) prefix += 1; if (textBytes.Length == 0) prefix++; return prefix + textBytes.Length; } public int CompareTo(object other) { return CompareTo(other as BEncodedString); } public int CompareTo(BEncodedString other) { if (other == null) return 1; int difference=0; int length = this.textBytes.Length > other.textBytes.Length ? other.textBytes.Length : this.textBytes.Length; for (int i = 0; i < length; i++) if ((difference = this.textBytes[i].CompareTo(other.textBytes[i])) != 0) return difference; if (this.textBytes.Length == other.textBytes.Length) return 0; return this.textBytes.Length > other.textBytes.Length ? 1 : -1; } #endregion #region Overridden Methods public override bool Equals(object obj) { if (obj == null) return false; BEncodedString other; if (obj is string) other = new BEncodedString((string)obj); else if (obj is BEncodedString) other = (BEncodedString)obj; else return false; return Toolbox.ByteMatch(this.textBytes, other.textBytes); } public override int GetHashCode() { int hash = 0; for (int i = 0; i < this.textBytes.Length; i++) hash += this.textBytes[i]; return hash; } public override string ToString() { return System.Text.Encoding.UTF8.GetString(textBytes); } #endregion } }