private static void WriteString(Stream/*!*/ stream, FormatDirective directive, MutableString str) { // TODO (opt): unneccessary copy of byte[] byte[] bytes = str != null ? str.ToByteArray() : Utils.EmptyBytes; int dataLen; int paddedLen; if (directive.Count.HasValue) { paddedLen = directive.Count.Value; dataLen = Math.Min(bytes.Length, paddedLen); } else { paddedLen = bytes.Length; dataLen = bytes.Length; } stream.Write(bytes, 0, dataLen); if (paddedLen > dataLen) { byte fill = (directive.Directive == 'A') ? (byte)' ' : (byte)0; for (int j = 0; j < (paddedLen - dataLen); j++) { stream.WriteByte(fill); } } if (directive.Directive == 'Z' && !directive.Count.HasValue) { stream.WriteByte((byte)0); } }
public static MutableString /*!*/ Pack(RubyContext /*!*/ context, RubyArray /*!*/ self, [DefaultProtocol, NotNull] MutableString /*!*/ format) { using (MutableStringStream stream = new MutableStringStream()) { BinaryWriter writer = new BinaryWriter(stream); int i = 0; foreach (FormatDirective directive in FormatDirective.Enumerate(format.ConvertToString())) { int remaining = (self.Count - i); int count = directive.Count.HasValue ? directive.Count.Value : remaining; if (count > remaining) { count = remaining; } MutableString str; switch (directive.Directive) { case '@': count = 0; stream.Position = directive.Count.HasValue ? directive.Count.Value : 1; break; case 'A': case 'a': case 'Z': count = 1; char[] cstr = Protocols.CastToString(context, self[i]).ToString().ToCharArray(); int len1 = directive.Count.HasValue ? directive.Count.Value : cstr.Length; int len2 = (len1 > cstr.Length) ? cstr.Length : len1; writer.Write(cstr, 0, len2); if (len1 > len2) { byte fill = (directive.Directive == 'A') ? (byte)' ' : (byte)0; for (int j = 0; j < (len1 - len2); j++) { writer.Write(fill); } } if (directive.Directive == 'Z' && !directive.Count.HasValue) { writer.Write((byte)0); } break; case 'Q': case 'q': for (int j = 0; j < count; j++) { writer.Write(Protocols.CastToUInt64Unchecked(context, self[i + j])); } break; case 'l': case 'i': for (int j = 0; j < count; j++) { writer.Write(unchecked ((int)Protocols.CastToUInt32Unchecked(context, self[i + j]))); } break; case 'L': case 'I': for (int j = 0; j < count; j++) { writer.Write(Protocols.CastToUInt32Unchecked(context, self[i + j])); } break; case 'N': // unsigned 4-byte big-endian WriteUInt32(writer, self, context, i, count, BitConverter.IsLittleEndian); break; case 'n': // unsigned 2-byte big-endian WriteUInt16(writer, self, context, i, count, BitConverter.IsLittleEndian); break; case 'V': // unsigned 4-byte little-endian WriteUInt32(writer, self, context, i, count, !BitConverter.IsLittleEndian); break; case 'v': // unsigned 2-byte little-endian WriteUInt16(writer, self, context, i, count, !BitConverter.IsLittleEndian); break; case 's': for (int j = 0; j < count; j++) { writer.Write(unchecked ((short)Protocols.CastToUInt32Unchecked(context, self[i + j]))); } break; case 'S': for (int j = 0; j < count; j++) { writer.Write(unchecked ((ushort)Protocols.CastToUInt32Unchecked(context, self[i + j]))); } break; case 'c': for (int j = 0; j < count; j++) { writer.Write(unchecked ((sbyte)Protocols.CastToUInt32Unchecked(context, self[i + j]))); } break; case 'C': for (int j = 0; j < count; j++) { writer.Write(unchecked ((byte)Protocols.CastToUInt32Unchecked(context, self[i + j]))); } break; case 'm': count = 1; str = Protocols.CastToString(context, self[i]); char[] base64 = Convert.ToBase64String(str.ToByteArray()).ToCharArray(); for (int j = 0; j < base64.Length; j += 60) { int len = base64.Length - j; if (len > 60) { len = 60; } writer.Write(base64, j, len); writer.Write('\n'); } break; case 'U': char[] buffer = new char[count]; for (int j = 0; j < count; j++) { buffer[j] = unchecked ((char)Protocols.CastToUInt32Unchecked(context, self[i + j])); } writer.Write(Encoding.UTF8.GetBytes(buffer)); break; case 'X': count = 0; int len3 = directive.Count.HasValue ? directive.Count.Value : 0; if (len3 > stream.Position) { throw RubyExceptions.CreateArgumentError("X outside of string"); } stream.Position -= len3; break; case 'x': count = 0; int len4 = directive.Count.HasValue ? directive.Count.Value : 0; for (int j = 0; j < len4; j++) { writer.Write((byte)0); } break; case 'h': case 'H': // MRI skips null, unlike in "m" directive: if (self[i] != null) { str = Protocols.CastToString(context, self[i]); FromHex(writer, str, directive.Count ?? str.GetByteCount(), directive.Directive == 'h'); } break; default: throw RubyExceptions.CreateArgumentError( String.Format("Unknown format directive '{0}'", directive.Directive)); } i += count; if (i >= self.Count) { break; } } stream.SetLength(stream.Position); return(stream.String); } }