예제 #1
0
        public static async ValueTask <(BucketBytes, BucketEol)> ReadUntilEolFullAsync(this Bucket bucket, BucketEol acceptableEols, BucketEolState?eolState = null, int requested = int.MaxValue)
        {
            if (bucket is null)
            {
                throw new ArgumentNullException(nameof(bucket));
            }

            ByteCollector result = new();

            int rq = requested;

            if (eolState?._kept.HasValue ?? false)
            {
                var kept = eolState._kept !.Value;
                eolState._kept = null;

                switch (kept)
                {
                case (byte)'\n' when(0 != (acceptableEols & BucketEol.LF)):
                    return(new BucketBytes(new[] { kept }, 0, 1), BucketEol.LF);

                case (byte)'\r' when(0 != (acceptableEols & BucketEol.CR) && (acceptableEols & BucketEol.CRLF) == 0):
                    return(new BucketBytes(new[] { kept }, 0, 1), BucketEol.CR);

                case (byte)'\0' when(0 != (acceptableEols & BucketEol.Zero)):
                    return(new BucketBytes(new[] { kept }, 0, 1), BucketEol.Zero);

                case (byte)'\r' when(0 != (acceptableEols & BucketEol.CRLF)):
                    rq = 1;
                    goto default;

                default:
                    result.Append(kept);
                    break;
                }
            }
            else if ((BucketEol.CRLF | BucketEol.CR) == (acceptableEols & (BucketEol.CRLF | BucketEol.CR)) && eolState is null)
            {
                throw new ArgumentNullException(nameof(eolState));
            }

            while (true)
            {
                BucketBytes bb;
                BucketEol   eol;

                (bb, eol) = await bucket.ReadUntilEolAsync(acceptableEols, rq).ConfigureAwait(false);

                if (eol != BucketEol.None && eol != BucketEol.CRSplit && result.IsEmpty)
                {
                    return(bb, eol);
                }
                else if (bb.IsEmpty)
                {
                    if (bb.IsEof)
                    {
                        return(result.ToResultOrEof(), eol);
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                }
                else if (rq == 1 && (acceptableEols & BucketEol.CRLF) != 0 && result.SequenceEqual(new[] { (byte)'\r' }))
                {
                    if (bb[0] == '\n')
                    {
                        return(result.AsBytes(bb), BucketEol.CRLF);
                    }
                    else if ((acceptableEols & BucketEol.CR) != 0)
                    {
                        eolState !._kept = bb[0];
                        return(new[] { (byte)'\r' }, BucketEol.CR);
                    }
                    else if (eol != BucketEol.None)
                    {
                        return(result.AsBytes(bb), eol);  // '\0' case
                    }
                }

                rq = (requested -= bb.Length);
                if (requested == 0)
                {
                    return(result.AsBytes(bb), eol);
                }

                result.Append(bb);

                if (eol == BucketEol.CRSplit)
                {
                    // Bad case. We may have a \r that might be a \n

                    var poll = await bucket.PollReadAsync(1).ConfigureAwait(false);

                    if (!poll.Data.IsEmpty)
                    {
                        var b = poll[0];
                        bb = await poll.ReadAsync(1).ConfigureAwait(false);

                        if (b == '\n')
                        {
                            // Phew, we were lucky. We got a \r\n
                            return(result.AsBytes(bb), BucketEol.CRLF);
                        }
                        else if (0 != (acceptableEols & BucketEol.CR))
                        {
                            // We return the part ending with '\r'
                            eolState !._kept = b; // And keep the next byte
                            return(result.AsBytes(), BucketEol.CR);
                        }
                        else if (b == '\0' && 0 != (acceptableEols & BucketEol.Zero))
                        {
                            // Another edge case :(
                            return(result.AsBytes(bb), BucketEol.Zero);
                        }
                        else
                        {
                            result.Append(b);
                            continue;
                        }
                    }
                    else
                    {
                        // We are at eof, so no issues with future reads
                        eol = (0 != (acceptableEols & BucketEol.CR) ? BucketEol.CR : BucketEol.None);

                        return(result.AsBytes(), eol);
                    }
                }
                else if (eol == BucketEol.None)
                {
                    continue;
                }
                else
                {
                    return(result.AsBytes(), eol);
                }
            }
        }