edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C445606 File: Initializers.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C445606 (server) 5/20/2008 10:45 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;RubyArrayPack @@ -51,7 +51,7 @@ ExtendModule(typeof(System.Collections.Generic.IDictionary), new System.Action(LoadSystem__Collections__Generic__IDictionary_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); ExtendModule(typeof(System.Collections.IEnumerable), new System.Action(LoadSystem__Collections__IEnumerable_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); ExtendModule(typeof(System.Collections.IList), new System.Action(LoadSystem__Collections__IList_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); - ExtendModule(typeof(System.IComparable), new System.Action(LoadSystem__IComparable_Instance), null, new Ruby.Builtins.RubyModule[] {def25, }); + Ruby.Builtins.RubyModule def29 = ExtendModule(typeof(System.IComparable), new System.Action(LoadSystem__IComparable_Instance), null, new Ruby.Builtins.RubyModule[] {def25, }); DefineGlobalClass("Time", typeof(System.DateTime), classRef0, new System.Action(LoadTime_Instance), new System.Action(LoadTime_Class), new Ruby.Builtins.RubyModule[] {def25, }, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.TimeOps.Create), }); @@ -65,7 +65,7 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ArrayOps.CreateArray), }); DefineGlobalClass("Binding", typeof(Ruby.Builtins.Binding), Context.ObjectClass, null, null, Ruby.Builtins.RubyModule.EmptyArray, null); - DefineGlobalClass("ClrString", typeof(System.String), Context.ObjectClass, new System.Action(LoadClrString_Instance), null, Ruby.Builtins.RubyModule.EmptyArray, null); + DefineGlobalClass("ClrString", typeof(System.String), Context.ObjectClass, new System.Action(LoadClrString_Instance), null, new Ruby.Builtins.RubyModule[] {def29, }, null); DefineGlobalClass("Dir", typeof(Ruby.Builtins.RubyDir), Context.ObjectClass, new System.Action(LoadDir_Instance), new System.Action(LoadDir_Class), new Ruby.Builtins.RubyModule[] {def16, }, null); Ruby.Builtins.RubyClass def26 = Context.ExceptionClass = DefineGlobalClass("Exception", typeof(System.Exception), Context.ObjectClass, new System.Action(LoadException_Instance), new System.Action(LoadException_Class), Ruby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ExceptionOps.Factory), @@ -329,6 +329,11 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ArrayOps.Initialize), }); + module.DefineLibraryMethod("pack", 0x29, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ArrayOps.Pack), + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ArrayOps.Pack), + }); + module.DefineLibraryMethod("reverse!", 0x29, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ArrayOps.InPlaceReverse), }); @@ -3906,6 +3911,11 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.MutableStringOps.TrInPlace), }); + module.DefineLibraryMethod("unpack", 0x29, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.MutableStringOps.Unpack), + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.MutableStringOps.Unpack), + }); + module.DefineLibraryMethod("upcase", 0x29, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.MutableStringOps.UpCase), }); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs;C443395 File: ArrayOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs;C443395 (server) 5/21/2008 9:50 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ArrayOps.cs;RubyArrayPack @@ -17,6 +17,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -189,6 +190,185 @@ return self; } + #region class FormatDirective is used by Array.pack and String.unpack + + internal struct FormatDirective { + internal readonly char Directive; + internal readonly int? Count; + private static readonly Dictionary _native; + + static FormatDirective() { + bool is64bit = (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8); + _native = new Dictionary(6); + _native['s'] = 's'; + _native['S'] = 'S'; + _native['i'] = 'i'; + _native['I'] = 'I'; + _native['l'] = is64bit ? 'i' : 'q'; + _native['L'] = is64bit ? 'I' : 'Q'; + } + + internal FormatDirective(char directive, int? count) { + Directive = directive; + Count = count; + } + internal static IEnumerable/*!*/ Enumerate(string format) { + for (int i = 0; i < format.Length; i++) { + char c = format[i]; + if (!Char.IsLetter(c) && c != '@') { + continue; + } + i++; + int? count = 1; + char c2 = (i < format.Length) ? format[i] : (char)0; + if (c2 == '_') { + char tmp; + if (!_native.TryGetValue(c, out tmp)) { + throw RubyExceptions.CreateArgumentError("'_' allowed only after types sSiIlL"); + } + c = tmp; + i++; + c2 = (i < format.Length) ? format[i] : (char)0; + } + if (Char.IsDigit(c2)) { + int pos1 = i; + i++; + while (i < format.Length && Char.IsDigit(format[i])) { + i++; + } + count = Int32.Parse(format.Substring(pos1, (i - pos1))); + i--; + } else if (c2 == '*') { + count = null; + } else { + i--; + } + + yield return new FormatDirective(c, count); + } + } + } + + #endregion + + [RubyMethod("pack")] + public static MutableString/*!*/ Pack(CodeContext/*!*/ context, RubyArray/*!*/ self, [NotNull]MutableString/*!*/ format) { + using (MutableStringStream stream = new MutableStringStream()) { + BinaryWriter writer = new BinaryWriter(stream); + int i = 0; + foreach (FormatDirective directive in FormatDirective.Enumerate(format.ToString())) { + int remaining = (self.Count - i); + int count = directive.Count.HasValue ? directive.Count.Value : remaining; + if (count > remaining) { + count = remaining; + } + 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 'c': + for (int j = 0; j < count; j++) { + writer.Write((sbyte)Protocols.CastToFixnum(context, self[i + j])); + } + break; + case 'C': + for (int j = 0; j < count; j++) { + writer.Write((byte)Protocols.CastToFixnum(context, self[i + j])); + } + break; + case 'i': + for (int j = 0; j < count; j++) { + writer.Write((int)Protocols.CastToFixnum(context, self[i + j])); + } + break; + case 'I': + for (int j = 0; j < count; j++) { + writer.Write((uint)Protocols.CastToFixnum(context, self[i + j])); + } + break; + case 'm': + count = 1; + MutableString 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 's': + for (int j = 0; j < count; j++) { + writer.Write((short)Protocols.CastToFixnum(context, self[i + j])); + } + break; + case 'S': + for (int j = 0; j < count; j++) { + writer.Write((ushort)Protocols.CastToFixnum(context, self[i + j])); + } + break; + case 'U': + char[] buffer = new char[count]; + for (int j = 0; j < count; j++) { + buffer[j] = (char)Protocols.CastToFixnum(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; + 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; + } + } + + [RubyMethod("pack")] + public static MutableString/*!*/ Pack(CodeContext/*!*/ context, RubyArray/*!*/ self, object format) { + return Pack(context, self, Protocols.CastToString(context, format)); + } + //----------------------------------------------------- Array#reverse_each // array.reverse_each {|item| block } //------------------------------------------------------------------------ =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/MutableStringOps.cs;C444052 File: MutableStringOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/MutableStringOps.cs;C444052 (server) 5/21/2008 9:52 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/MutableStringOps.cs;RubyArrayPack @@ -3116,5 +3116,169 @@ Kernel.RequiresNotFrozen(context, self); return self.Reverse(); } + + private static bool HasCapacity(Stream/*!*/ s, int? n) { + if (s.Length < (s.Position + n)) { + s.Position = s.Length; + return false; + } else { + return true; + } + } + + private static int CalculateCounts(Stream/*!*/ s, int? count, int size, out int leftover) { + int remaining = (int)(s.Length - s.Position); + int maxCount = remaining / size; + if (!count.HasValue) { + leftover = 0; + return maxCount; + } else if (count.Value <= maxCount) { + leftover = 0; + return count.Value; + } else { + leftover = count.Value - maxCount; + return maxCount; + } + } + + [RubyMethod("unpack")] + public static RubyArray/*!*/ Unpack(CodeContext/*!*/ context, MutableString/*!*/ self, [NotNull]MutableString/*!*/ format) { + RubyArray result = new RubyArray(1 + self.Length / 2); + using (MutableStringStream stream = new MutableStringStream(self)) { + BinaryReader reader = new BinaryReader(stream); + foreach (ArrayOps.FormatDirective directive in ArrayOps.FormatDirective.Enumerate(format.ToString())) { + int count, maxCount; + byte[] buffer; + MutableString str; + int nilCount = 0; + switch (directive.Directive) { + case '@': + stream.Position = directive.Count.HasValue ? directive.Count.Value : stream.Position; + break; + case 'A': + case 'a': + maxCount = (int)(stream.Length - stream.Position); + count = directive.Count.HasValue ? directive.Count.Value : maxCount; + if (count > maxCount) { + count = maxCount; + } + buffer = reader.ReadBytes(count); + str = MutableString.CreateBinary(buffer); + if (directive.Directive == 'A') { + // TODO: efficiency? + for (int pos = count - 1; pos >= 0; pos--) { + if (buffer[pos] != 0 && buffer[pos] != 0x20) { + break; + } + str.Remove(pos, 1); + } + } + result.Add(str); + break; + case 'Z': + maxCount = (int)(stream.Length - stream.Position); + count = directive.Count.HasValue ? directive.Count.Value : maxCount; + if (count > maxCount) { + count = maxCount; + } + buffer = reader.ReadBytes(count); + str = MutableString.CreateBinary(buffer); + for (int pos = 0; pos < count; pos++) { + if (buffer[pos] == 0) { + str.Remove(pos, count - pos); + break; + } + } + result.Add(str); + break; + case 'c': + count = CalculateCounts(stream, directive.Count, sizeof(sbyte), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadSByte()); + } + break; + case 'C': + count = CalculateCounts(stream, directive.Count, sizeof(byte), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadByte()); + } + break; + case 'i': + count = CalculateCounts(stream, directive.Count, sizeof(int), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadInt32()); + } + break; + case 'I': + count = CalculateCounts(stream, directive.Count, sizeof(uint), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadUInt32()); + } + break; + case 'm': + // TODO: Recognize "==" as end of base 64 encoding + int len = (int)(stream.Length - stream.Position); + char[] base64 = reader.ReadChars(len); + byte[] data = Convert.FromBase64CharArray(base64, 0, len); + result.Add(MutableString.CreateBinary(data)); + break; + case 's': + count = CalculateCounts(stream, directive.Count, sizeof(short), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadInt16()); + } + break; + case 'S': + count = CalculateCounts(stream, directive.Count, sizeof(ushort), out nilCount); + for (int j = 0; j < count; j++) { + result.Add((int)reader.ReadUInt16()); + } + break; + case 'U': + maxCount = (int)(stream.Length - stream.Position); + count = directive.Count.HasValue ? directive.Count.Value : maxCount; + int readCount = directive.Count.HasValue ? Encoding.UTF8.GetMaxByteCount(count) : count; + if (readCount > maxCount) { + readCount = maxCount; + } + long startPosition = stream.Position; + char[] charData = Encoding.UTF8.GetChars(reader.ReadBytes(readCount)); + if (charData.Length > count) { + int actualCount = Encoding.UTF8.GetByteCount(charData, 0, count); + stream.Position = startPosition + actualCount; + } else if (charData.Length < count) { + count = charData.Length; + } + for (int j = 0; j < count; j++) { + result.Add((int)charData[j]); + } + break; + case 'X': + 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': + int len4 = directive.Count.HasValue ? directive.Count.Value : 0; + stream.Position += len4; + break; + default: + throw RubyExceptions.CreateArgumentError( + String.Format("Unknown format directive '{0}'", directive.Directive)); + } + for (int i = 0; i < nilCount; i++) { + result.Add(null); + } + } + } + return result; + } + + [RubyMethod("unpack")] + public static RubyArray/*!*/ Unpack(CodeContext/*!*/ context, MutableString/*!*/ self, object format) { + return Unpack(context, self, Protocols.CastToString(context, format)); + } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/StringIO/StringIO.cs;C444795 File: StringIO.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/StringIO/StringIO.cs;C444795 (server) 5/21/2008 10:03 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/StringIO/StringIO.cs;RubyArrayPack @@ -27,124 +27,6 @@ [RubyClass("StringIO")] public class StringIO : RubyIO { - #region MutableStringStream - - protected class MutableStringStream : Stream { - private MutableString/*!*/ _string; - private int _position; - - internal MutableStringStream(MutableString/*!*/ basis) { - _string = basis; - _position = 0; - } - - internal MutableString String { - get { return _string; } - set { - _string = value; - _position = 0; - } - } - - public override bool CanRead { - get { return true; } - } - - public override bool CanSeek { - get { return true; } - } - - public override bool CanWrite { - get { return true; } - } - - public override void Flush() { - } - - public override long Length { - get { return _string.ConvertToBytes().Length; } - } - - public override long Position { - get { - return _position; - } - set { - if (value < 0) { - throw RubyExceptions.CreateIOError("Invalid argument"); - } - _position = (int)value; - } - } - - public override int Read(byte[] buffer, int offset, int count) { - _string.ConvertToBytes(); - int maxReadLen = _string.Length - _position; - if (count > maxReadLen) { - count = maxReadLen; - } - for (int i = 0; i < count; i++) { - buffer[offset + i] = _string.GetByte(_position++); - } - return count; - } - - public override int ReadByte() { - if (_position >= _string.Length) { - return -1; - } - return _string.GetByte(_position++); - } - - public override long Seek(long offset, SeekOrigin origin) { - _string.ConvertToBytes(); - - int position = _position; - switch (origin) { - case SeekOrigin.Begin: - position = (int)offset; - break; - case SeekOrigin.End: - position = _string.Length + (int)offset; - break; - case SeekOrigin.Current: - position += (int)offset; - break; - } - Position = position; - return Position; - } - - public override void SetLength(long value) { - _string.ConvertToBytes(); - if (_string.Length < value) { - _string.Append(new byte[(value - _string.Length)]); - } else if (_string.Length > value) { - _string.Remove((int)value, _string.Length - (int)value); - } - } - - public override void Write(byte[] buffer, int offset, int count) { - _string.ConvertToBytes(); - if ((_position + count) > _string.Length) { - SetLength(_position + count); - } - for (int i = offset; i < offset + count; i++) { - _string.SetByte(_position++, buffer[i]); - } - } - - public override void WriteByte(byte value) { - _string.ConvertToBytes(); - if ((_position + 1) > _string.Length) { - SetLength(_position + 1); - } - _string.SetByte(_position++, value); - } - } - - #endregion - private static DynamicSite _OpenSite = CallSiteFactory.CreateSimpleCallSite(RubyContext.RubyBinder); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj;C444052 File: Ruby.csproj =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj;C444052 (server) 5/21/2008 10:02 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj;RubyArrayPack @@ -94,6 +94,7 @@ + =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj.vspscc;C390406 add: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/MutableStringStream.cs File: MutableStringStream.cs =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/MutableStringStream.cs;RubyArrayPack @@ -1,0 +1,137 @@ +?/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System.IO; + +using Ruby.Runtime; + +namespace Ruby.Builtins { + public class MutableStringStream : Stream { + private MutableString/*!*/ _string; + private int _position; + + public MutableStringStream() : this(MutableString.CreateEmpty()) { + } + + public MutableStringStream(MutableString/*!*/ basis) { + _string = basis; + _position = 0; + } + + public MutableString String { + get { return _string; } + set { + _string = value; + _position = 0; + } + } + + public override bool CanRead { + get { return true; } + } + + public override bool CanSeek { + get { return true; } + } + + public override bool CanWrite { + get { return true; } + } + + public override void Flush() { + } + + public override long Length { + get { return _string.ConvertToBytes().Length; } + } + + public override long Position { + get { + return _position; + } + set { + if (value < 0) { + throw RubyExceptions.CreateIOError("Invalid argument"); + } + _position = (int)value; + } + } + + public override int Read(byte[] buffer, int offset, int count) { + _string.ConvertToBytes(); + int maxReadLen = _string.Length - _position; + if (count > maxReadLen) { + count = maxReadLen; + } + for (int i = 0; i < count; i++) { + buffer[offset + i] = _string.GetByte(_position++); + } + return count; + } + + public override int ReadByte() { + if (_position >= _string.Length) { + return -1; + } + return _string.GetByte(_position++); + } + + public override long Seek(long offset, SeekOrigin origin) { + _string.ConvertToBytes(); + + int position = _position; + switch (origin) { + case SeekOrigin.Begin: + position = (int)offset; + break; + case SeekOrigin.End: + position = _string.Length + (int)offset; + break; + case SeekOrigin.Current: + position += (int)offset; + break; + } + Position = position; + return Position; + } + + public override void SetLength(long value) { + _string.ConvertToBytes(); + if (_string.Length < value) { + _string.Append(new byte[(value - _string.Length)]); + } else if (_string.Length > value) { + _string.Remove((int)value, _string.Length - (int)value); + } + } + + public override void Write(byte[] buffer, int offset, int count) { + _string.ConvertToBytes(); + if ((_position + count) > _string.Length) { + SetLength(_position + count); + } + for (int i = offset; i < offset + count; i++) { + _string.SetByte(_position++, buffer[i]); + } + } + + public override void WriteByte(byte value) { + _string.ConvertToBytes(); + if ((_position + 1) > _string.Length) { + SetLength(_position + 1); + } + _string.SetByte(_position++, value); + } + } +} ===================================================================