/// <summary> /// Finds the last match whose index is less than or equal to "start". /// Captures are ordered in the same way as with forward match. This is different from .NET reverse matching. /// Start is a number of bytes if kcode is given, otherwise it's a number of characters. /// </summary> public MatchData LastMatch(MutableString /*!*/ input, int start) { string str; RubyEncoding kcode = null; Regex regex = Transform(ref kcode, input, 0, out str); Debug.Assert(str != null); if (kcode != null) { int byteCount; byte[] bytes = input.GetByteArray(out byteCount); if (start < 0) { start += byteCount; } // GetCharCount returns the number of whole characters: start = (start >= byteCount) ? str.Length : kcode.Encoding.GetCharCount(bytes, 0, start + 1) - 1; } else { if (start < 0) { start += str.Length; } if (start > str.Length) { start = str.Length; } } Match match; if (_hasGAnchor) { // This only makes some \G anchors work. It seems that CLR doesn't support \G if preceeded by some characters. // For example, this works in MRI but doesn't in CLR: "abcabczzz".rindex(/.+\G.+/, 3) match = regex.Match(str, start); } else { match = LastMatch(regex, str, start); if (match == null) { return(null); } } return(MatchData.Create(match, input, true, str)); }
/// <summary> /// Reads <paramref name="count"/> bytes from the stream and appends them to the given <paramref name="buffer"/>. /// If <paramref name="count"/> is <c>Int32.MaxValue</c> the stream is read to the end. /// Unless <paramref name="preserveEndOfLines"/> is set the line endings in the appended data are normalized to "\n". /// </summary> public int AppendBytes(MutableString /*!*/ buffer, int count, bool preserveEndOfLines) { ContractUtils.RequiresNotNull(buffer, "buffer"); ContractUtils.Requires(count >= 0, "count"); if (count == 0) { return(0); } bool readAll = count == Int32.MaxValue; buffer.SwitchToBytes(); int initialBufferSize = buffer.GetByteCount(); if (preserveEndOfLines) { AppendRawBytes(buffer, count); } else { // allocate 3 more bytes at the end for a backstop and possible LF: byte[] bytes = Utils.EmptyBytes; int done = initialBufferSize; bool eof; do { AppendRawBytes(buffer, readAll ? 1024 : count); int end = buffer.GetByteCount(); int bytesRead = end - done; if (bytesRead == 0) { break; } eof = bytesRead < count; buffer.EnsureCapacity(end + 3); int byteCount; bytes = buffer.GetByteArray(out byteCount); if (bytes[end - 1] == CR && PeekByte(0) == LF) { ReadByte(); bytes[end++] = LF; } // insert backstop: bytes[end] = CR; bytes[end + 1] = LF; int last = IndexOfCrLf(bytes, done); count -= last - done; done = last; while (last < end) { int next = IndexOfCrLf(bytes, last + 2); int chunk = next - last - 1; Buffer.BlockCopy(bytes, last + 1, bytes, done, chunk); done += chunk; count -= chunk; last = next; } buffer.Remove(done); } while (readAll || count > 0 && !eof); } if (readAll) { buffer.TrimExcess(); } return(buffer.GetByteCount() - initialBufferSize); }