/// <summary>Encode a byte array into a radix-encoded string</summary> /// <param name="bytes">byte array to encode</param> /// <returns>The bytes in encoded into a radix-encoded string</returns> /// <remarks>If <paramref name="bytes"/> is zero length, returns an empty string</remarks> public string Encode(byte[] bytes) { Contract.Requires <ArgumentNullException>(bytes != null); Contract.Ensures(Contract.Result <string>() != null); // Don't really have to do this, our code will build this result (empty string), // but why not catch the condition before doing work? if (bytes.Length == 0) { return(string.Empty); } // if the array ends with zeros, having the capacity set to this will help us know how much // 'padding' we will need to add int result_length = EncodingCharsCount(bytes.Length); // List<> has a(n in-place) Reverse method. StringBuilder doesn't. That's why. var result = new List <char>(result_length); // HACK: BigInteger uses the last byte as the 'sign' byte. If the byte's MSB is set, // we need to pad the input with an extra 0 (ie, make it positive) if (IntegerMath.IsSigned(bytes[bytes.Length - 1])) { Array.Resize(ref bytes, bytes.Length + 1); } var dividend = new BigInteger(bytes); // IsZero's computation is less complex than evaluating "dividend > 0" // which invokes BigInteger.CompareTo(BigInteger) while (!dividend.IsZero) { dividend = BigInteger.DivRem(dividend, kRadixBig, out BigInteger remainder); int digit_index = System.Math.Abs((int)remainder); result.Add(kDigits[digit_index]); } if (kIncludeProceedingZeros) { for (int x = result.Count; x < result.Capacity; x++) { result.Add(kDigits[0]); // pad with the character that represents 'zero' } } // orientate the characters in big-endian ordering if (kEndian == Shell.EndianFormat.Little) { result.Reverse(); } // If we didn't end up adding padding, ToArray will end up returning a TrimExcess'd array, // so nothing wasted return(new string(result.ToArray())); }