Пример #1
0
        internal void AppendSingle(ReadOnlySpan <T> previousBuffer, T val)
        {
            if (CurrentMode == Mode.CopyOnNextAppend || CurrentMode == Mode.InPlace)
            {
                SwitchToCopy(previousBuffer);
            }

            switch (CurrentMode)
            {
            case Mode.Uninitialized:
                CurrentMode = Mode.Copy;
                ResizeCopy(1);
                Copy[0]    = val;
                StartIndex = 0;
                Length     = 1;
                break;

            case Mode.Copy:
                if (Copy.Length == Length)
                {
                    ResizeCopy(Copy.Length * 2);
                }
                Copy[Length] = val;
                Length++;
                break;

            default:
                Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}");
                break;
            }
        }
Пример #2
0
        DynamicRowConverter IElseSupporting <DynamicRowConverter> .Clone(ImmutableArray <DynamicRowConverter> newFallbacks, NullHandling?_, NullHandling?__)
        {
            switch (Mode)
            {
            case BackingMode.Constructor:
            {
                if (ConstructorForObject.HasValue)
                {
                    return(new DynamicRowConverter(ConstructorForObject.Value, newFallbacks));
                }

                if (ConstructorTakingParams.HasValue)
                {
                    return(new DynamicRowConverter(ConstructorTakingParams.Value, ParameterTypes.Value, ColumnsForParameters.Value, newFallbacks));
                }

                if (EmptyConstructor.HasValue)
                {
                    return(new DynamicRowConverter(EmptyConstructor.Value, Setters.Value, ColumnsForSetters.Value, newFallbacks));
                }
            }
            break;

            case BackingMode.Delegate: return(new DynamicRowConverter(TargetType, Delegate.Value, newFallbacks));

            case BackingMode.Method: return(new DynamicRowConverter(TargetType, Method.Value, newFallbacks));
            }

            Throw.ImpossibleException($"Unexpected {nameof(BackingMode)}: {Mode}");
            return(default);
Пример #3
0
        internal static T NonNullValue <T>(T?toCheck)
            where T : struct
        {
            if (toCheck == null)
            {
                Throw.ImpossibleException("Expected non-null value, but found null");
            }

            return(toCheck.Value);
        }
Пример #4
0
        private static TypeInfo?GetPairedType(
            Type forType,
#pragma warning disable CS0618 // This is obsolete to prevent clients from using them, but they are fine for us.
            GeneratedSourceVersionAttribute.GeneratedTypeKind forMode
#pragma warning restore CS0618
            )
        {
            var inAssembly     = forType.Assembly;
            var candidateTypes = inAssembly.GetTypes();

            TypeInfo?ret = null;

            foreach (var tRaw in candidateTypes)
            {
                var t = tRaw.GetTypeInfo();

#pragma warning disable CS0618 // This is obsolete to prevent clients from using them, but they are fine for us.
                var attr = t.GetCustomAttribute <GeneratedSourceVersionAttribute>();
#pragma warning restore CS0618

                if (attr is not object)
                {
                    continue;
                }

                var meantForType = attr.ForType.GetTypeInfo();
                if (meantForType != forType)
                {
                    continue;
                }

                var candidateMode = attr.Kind;
                if (candidateMode != forMode)
                {
                    continue;
                }

                var version = attr.Version;
                if (version != CURRENT_CESIL_VERSION)
                {
                    Throw.ImpossibleException($"Found a generated type ({t}) with an unexpected version ({version}), suggesting the generated source does not match the version ({CURRENT_CESIL_VERSION}) of Cesil in use.");
                }

                if (ret != null)
                {
                    Throw.ImpossibleException($"Found multiple generated types for {forType}");
                }

                ret = t;
            }

            return(ret);
        }
Пример #5
0
        internal readonly bool TryLookup(string key, out int value)
        {
            switch (Mode)
            {
            case Algorithm.AdaptiveRadixTrie: return(TryLookupAdaptiveRadixTrie(key, out value));

            case Algorithm.BinarySearch: return(TryLookupBinarySearch(key, out value));

            default:
                value = 0;
                Throw.ImpossibleException($"Unexpected {nameof(Algorithm)}: {Mode}");
                return(default);
            }
        }
Пример #6
0
        protected internal ReadWithCommentResultType EndOfData()
        {
            var res = StateMachine.EndOfData();

            switch (res)
            {
            case ReaderStateMachine.AdvanceResult.Skip_Character:
                // nothing to be done!
                return(ReadWithCommentResultType.NoValue);

            case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndCurrentCharacter:
            case ReaderStateMachine.AdvanceResult.Append_Character:
                Throw.ImpossibleException($"Attempted to append end of data with {nameof(ReaderStateMachine.Advance)} = {res}", Configuration.Options);
                return(default);

            case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndEndComment:
                Partial.AppendCarriageReturn(ReadOnlySpan <char> .Empty);
                return(ReadWithCommentResultType.HasComment);

            case ReaderStateMachine.AdvanceResult.Finished_Unescaped_Value:
                PushPendingCharactersToValue(false);
                return(ReadWithCommentResultType.HasValue);

            case ReaderStateMachine.AdvanceResult.Finished_Escaped_Value:
                PushPendingCharactersToValue(true);
                return(ReadWithCommentResultType.HasValue);

            case ReaderStateMachine.AdvanceResult.Finished_LastValueUnescaped_Record:
                if (Partial.PendingCharsCount > 0)
                {
                    PushPendingCharactersToValue(false);
                }
                return(ReadWithCommentResultType.HasValue);

            case ReaderStateMachine.AdvanceResult.Finished_LastValueEscaped_Record:
                if (Partial.PendingCharsCount > 0)
                {
                    PushPendingCharactersToValue(true);
                }
                return(ReadWithCommentResultType.HasValue);

            case ReaderStateMachine.AdvanceResult.Finished_Comment:
                return(ReadWithCommentResultType.HasComment);

            default:
                return(HandleUncommonAdvanceResults(res, null));
            }
        }
Пример #7
0
            private static byte CreateMask()
            {
                var  values = Enum.GetValues(typeof(T));
                byte ret    = 0;

                for (var i = 0; i < values.Length; i++)
                {
                    var o = values.GetValue(i);
                    if (o == null)
                    {
                        Throw.ImpossibleException("Shouldn't be possible");
                    }

                    ret |= (byte)o;
                }

                return(ret);
            }
Пример #8
0
        // only call this with the buffer we're actively reading from
        internal void Append(ReadOnlySpan <T> fromBuffer, int index, int length)
        {
            // we figured out that we can't be in place on
            //   the other buffer... but that doesn't actually
            //   matter until we next try to append.
            //
            // which is now, so copy everything out of the span
            //   into our local buffer and switch the mode
            //   to plain old Copy mode
            if (CurrentMode == Mode.CopyOnNextAppend)
            {
                SwitchToCopy(fromBuffer);
            }

            switch (CurrentMode)
            {
            case Mode.Uninitialized:
                CurrentMode = Mode.InPlace;
                StartIndex  = index;
                Length      = length;
                break;

            case Mode.Copy:
                var desiredSize = Length + length;
                if (desiredSize >= Copy.Length)
                {
                    ResizeCopy(desiredSize * 2);
                }

                fromBuffer.Slice(index, length).CopyTo(Copy.Slice(Length));
                Length += length;
                break;

            case Mode.InPlace:
                Length += length;
                break;

            default:
                Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}");
                break;
            }
        }
Пример #9
0
        internal void AssertNotPoisoned <T>(IBoundConfiguration <T> self)
        {
            if (Poison != null)
            {
                switch (Poison.Value)
                {
                case PoisonType.Cancelled:
                    Throw.InvalidOperationException("Object is in an invalid state, a previous operation was canceled");
                    return;

                case PoisonType.Exception:
                    Throw.InvalidOperationException("Object is in an invalid state, a previous operation raised an exception");
                    return;

                default:
                    Throw.ImpossibleException($"Unexpected {nameof(PoisonType)}: {Poison}", self);
                    return;
                }
            }
        }
Пример #10
0
        internal void Skipped()
        {
            // this means we can't jut blindly copy of out the backing buffer if
            //   another append comes through, so change state accordingly
            switch (CurrentMode)
            {
            case Mode.Uninitialized:
            case Mode.CopyOnNextAppend:
            case Mode.Copy:
                // nothing to do
                break;

            case Mode.InPlace:
                CurrentMode = Mode.CopyOnNextAppend;
                break;

            default:
                Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}");
                break;
            }
        }
Пример #11
0
        internal SurrogateTypeDescriber(ITypeDescriber typeDescriber, ITypeDescriber fallback, SurrogateTypeDescriberFallbackBehavior fallbackBehavior, ImmutableDictionary <TypeInfo, TypeInfo> surrogateTypes)
        {
            SurrogateTypes    = surrogateTypes;
            TypeDescriber     = typeDescriber;
            FallbackDescriber = fallback;

            switch (fallbackBehavior)
            {
            case SurrogateTypeDescriberFallbackBehavior.Throw:
                ThrowOnNoRegisteredSurrogate = true;
                break;

            case SurrogateTypeDescriberFallbackBehavior.UseFallback:
                ThrowOnNoRegisteredSurrogate = false;
                break;

            default:
                Throw.ImpossibleException($"Unexpected {nameof(SurrogateTypeDescriberFallbackBehavior)}: {fallbackBehavior}");
                break;
            }
        }
Пример #12
0
        internal ReadOnlyMemory <T> AsMemory(ReadOnlyMemory <T> fromBufferMem)
        {
            switch (CurrentMode)
            {
            case Mode.CopyOnNextAppend:
            case Mode.InPlace:
                // we're able to just read out of the buffer, no intermediate copy required
                return(fromBufferMem.Slice(StartIndex, Length));

            case Mode.Copy:
                // whelp, we had to make a copy... better return it
                return(CopyOwner.Value.Memory.Slice(StartIndex, Length));

            case Mode.Uninitialized:
                return(ReadOnlyMemory <T> .Empty);

            default:
                Throw.ImpossibleException($"Unexpected {nameof(Mode)}: {CurrentMode}");
                return(default);
            }
        }
Пример #13
0
        public InstanceProvider?GetInstanceProvider(TypeInfo forType)
        {
            var paired = GetPairedType(forType, DESERIALIZER_KIND);

            if (paired == null)
            {
                return(null);
            }

            var instanceMtd = paired.GetMethodNonNull("__InstanceProvider", PublicStatic);

#pragma warning disable CS0618 // This obsolete to prevent clients from using them, but they are fine for us.
            var forConstructorAttrs = instanceMtd.GetCustomAttributes <ConstructorInstanceProviderAttribute>();
#pragma warning restore CS0618

            if (forConstructorAttrs.Any())
            {
                var consOnTypes = forConstructorAttrs.Select(x => x.ForType).Distinct().ToImmutableArray();
                if (consOnTypes.Length > 1)
                {
                    Throw.ImpossibleException($"Generated type {paired} (for {forType}) claims multiple constructors for an InstanceProvider.");
                }

                var consOnType = consOnTypes.Single();
                var consParams = forConstructorAttrs.OrderBy(x => x.ParameterIndex).Select(i => i.ParameterType).ToArray();

                var cons = consOnType.GetConstructor(AllInstance, null, consParams, null);
                if (cons == null)
                {
                    Throw.ImpossibleException($"Generated type {paired} (for {forType}) claims a constructor for an InstanceProvider that could not be found.");
                }

                return(InstanceProvider.ForConstructorWithParametersInner(cons, paired));
            }

            return(InstanceProvider.ForMethodInner(instanceMtd, paired));
        }
Пример #14
0
        private static NullHandling DetermineNullabilityImpl <T>(T member, IEnumerable <CustomAttributeData> memberAttributes, IEnumerable <CustomAttributeData>?contextAttributes, TypeInfo memberType)
        {
            // from: https://github.com/dotnet/roslyn/blob/3182fd79a7790188d4facfc3fa2da22c598895f2/docs/features/nullable-metadata.md

            // nullable annotations are either on the member itself, or on the declaring type
            //   if on the member, it's a [Nullable]
            //   if on the type, it's a [NullableContext]
            //
            // Nullable can take either a byte or a byte[], NullableContext always takes a byte
            //
            // we only ever care about the first byte (even if we have an array)
            // because that refers to the "root"
            //
            // bytes are as follows:
            //   0 - null oblivious
            //   1 - non-null
            //   2 - allow-null
            //
            // the absense of a nullable annotation is equivalent to null oblivious
            //
            // null oblivious maps to whatever is "natural" that is:
            //  - reference types allow null
            //  - nullable value types allow null
            //  - value types forbid null

            // need to de-ref member type, because the "ref-ness" of it doesn't matter
            //   for nullability purposes
            var effectiveMemberType = memberType;

            while (effectiveMemberType.IsByRef)
            {
                effectiveMemberType = effectiveMemberType.GetElementTypeNonNull();
            }

            // value types can only have meaningful nullable annotations
            //   for their generic parameters, which means we never care
            //   about it's annotations
            //
            // since ints and whatnot are pretty common, this shortcut
            //   can save some real time on type describing.
            if (effectiveMemberType.IsValueType)
            {
                if (effectiveMemberType.IsNullableValueType(out _))
                {
                    return(NullHandling.AllowNull);
                }

                return(NullHandling.CannotBeNull);
            }

            // check for explicit attributes
            foreach (var attr in memberAttributes)
            {
                if (attr.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute")
                {
                    var arg     = attr.ConstructorArguments[0];
                    var argType = arg.ArgumentType.GetTypeInfo();

                    var val = arg.Value;
                    if (val == null)
                    {
                        Throw.ImpossibleException($@"NullableAttribute with null first argument (on member {member}), this should not be possible");
                        return(default);
Пример #15
0
        // create a delegate that will format the given value (pulled from a getter or a field) into
        //   a buffer, subject to shouldSerialize being null or returning true
        //   and return true if it was able to do so
        internal static ColumnWriterDelegate Create(TypeInfo type, Options options, Formatter formatter, NonNull<ShouldSerialize> shouldSerialize, Getter getter, bool emitDefaultValue)
        {
            var p1 = Expressions.Parameter_Object;
            var p2 = Expressions.Parameter_WriteContext_ByRef;
            var p3 = Expressions.Parameter_IBufferWriterOfChar;

            var statements = new List<Expression>();

            var p1AsType = Expression.Convert(p1, type);
            var l1 = Expression.Variable(type, "l1");

            var assignToL1 = Expression.Assign(l1, p1AsType);
            statements.Add(assignToL1);

            var end = Expression.Label(Types.Bool, "end");
            var returnTrue = Expression.Label("return-true");

            if (shouldSerialize.HasValue)
            {
                var ss = shouldSerialize.Value;

                if (ss.TakesNullability == NullHandling.ForbidNull && ss.Takes.Value.AllowsNullLikeValue())
                {
                    var takes = ss.Takes.Value;

                    var checkExp = Utils.MakeNullHandlingCheckExpression(takes, l1, $"{ss} does not accept null rows, but was given one at runtime");

                    statements.Add(checkExp);
                }

                var callShouldSerialize = ss.MakeExpression(l1, p2);

                var shouldntSerialize = Expression.Not(callShouldSerialize);
                var jumpToEnd = Expression.Goto(returnTrue);
                var jumpToEndIfShouldntSerialize = Expression.IfThen(shouldntSerialize, jumpToEnd);

                statements.Add(jumpToEndIfShouldntSerialize);
            }

            var columnType = getter.Returns;
            var l2 = Expression.Variable(columnType, "l2");

            var getExp = getter.MakeExpression(l1, p2);

            var assignToL2 = Expression.Assign(l2, getExp);
            statements.Add(assignToL2);


            if (getter.ReturnsNullability == NullHandling.ForbidNull && columnType.AllowsNullLikeValue())
            {
                // do we need to check that a null didn't happen at runtime?
                var checkExp = Utils.MakeNullHandlingCheckExpression(getter.Returns, l2, $"{getter} was forbidden from return null values, but did return one at runtime");
                statements.Add(checkExp);
            }

            if (!emitDefaultValue)
            {
                ConstantExpression defValue;
                if (columnType.IsValueType)
                {
                    defValue = Expression.Constant(Activator.CreateInstance(columnType));
                }
                else
                {
                    defValue = Expression.Constant(null, columnType);
                }

                Expression isDefault;
                // intentionally letting GetMethod return null here
                if (!columnType.IsValueType || columnType.IsEnum || columnType.IsPrimitive || columnType.GetMethod("op_Equality") != null)
                {
                    isDefault = Expression.Equal(l2, defValue);
                }
                else
                {
                    var equatableI = Types.IEquatable.MakeGenericType(columnType).GetTypeInfo();
                    if (columnType.GetInterfaces().Any(i => i == equatableI))
                    {
                        var equals = equatableI.GetMethodNonNull(nameof(IEquatable<object>.Equals));
                        var map = columnType.GetInterfaceMap(equatableI);

                        MethodInfo? equalsTyped = null;
                        for (var j = 0; j < map.InterfaceMethods.Length; j++)
                        {
                            if (map.InterfaceMethods[j] == equals)
                            {
                                equalsTyped = map.TargetMethods[j];
                                isDefault = Expression.Call(l2, equalsTyped, defValue);
                                goto done;
                            }
                        }

                        Throw.ImpossibleException($"Could not find typed {nameof(IEquatable<object>.Equals)} method, which shouldn't be possible", options);
                        return default;
                    }
                    else
                    {
                        var eqsUntyped = columnType.GetMethodNonNull(nameof(object.Equals));
                        var defAsObject = Expression.Convert(defValue, Types.Object);
                        isDefault = Expression.Call(l2, eqsUntyped, defAsObject);
                    }
                }

done:
                var ifIsDefaultReturnTrue = Expression.IfThen(isDefault, Expression.Goto(returnTrue));

                statements.Add(ifIsDefaultReturnTrue);
            }

            if (formatter.TakesNullability == NullHandling.ForbidNull && formatter.Takes.AllowsNullLikeValue())
            {
                // make sure formatter invariant hasn't been violated
                var checkExp = Utils.MakeNullHandlingCheckExpression(formatter.Takes, l2, $"{formatter} does not take null values, but received one at runtime");
                statements.Add(checkExp);
            }

            var callFormatter = formatter.MakeExpression(l2, p2, p3);

            statements.Add(Expression.Goto(end, callFormatter));

            statements.Add(Expression.Label(returnTrue));
            statements.Add(Expression.Goto(end, Expressions.Constant_True));

            statements.Add(Expression.Label(end, Expressions.Constant_False));

            var block = Expression.Block(new[] { l1, l2 }, statements);

            var del = Expression.Lambda<ColumnWriterDelegate>(block, p1, p2, p3);

            var compiled = del.Compile();

            return compiled;
        }
Пример #16
0
        private ReadWithCommentResultType ProcessBuffer(int bufferLen, out int unprocessedCharacters)
        {
            var buffSpan = Buffer.Buffer.Span;

            ReaderStateMachine.AdvanceResult?inBatchableResult = null;
            var consistentResultSince = -1;

            for (var i = 0; i < bufferLen; i++)
            {
                var c   = buffSpan[i];
                var res = StateMachine.Advance(c, false);

                var advanceIBy = 0;

                // we only see this _if_ there are multiple characters in the separator, which is rare
                if (res == ReaderStateMachine.AdvanceResult.LookAhead_MultiCharacterSeparator)
                {
                    var valSepLen = Configuration.ValueSeparatorMemory.Length;

                    // do we have enough in the buffer to look ahead?
                    var canCheckForSeparator = bufferLen - i >= valSepLen;
                    if (canCheckForSeparator)
                    {
                        var shouldMatch = buffSpan.Slice(i, valSepLen);
                        var eq          = Utils.AreEqual(shouldMatch, Configuration.ValueSeparatorMemory.Span);
                        if (eq)
                        {
                            // treat it like a value separator
                            res = StateMachine.AdvanceValueSeparator();
                            // advance further to the last character in the separator
                            advanceIBy = valSepLen - 1;
                        }
                        else
                        {
                            res = StateMachine.Advance(c, true);
                        }
                    }
                    else
                    {
                        // we don't have enough in the buffer... so deal with any running batches and ask for more
                        if (inBatchableResult != null)
                        {
                            switch (inBatchableResult.Value)
                            {
                            case ReaderStateMachine.AdvanceResult.Skip_Character:

                                // there's no distinction between skipping several characters and skipping one
                                //    so this doesn't need the length
                                Partial.SkipCharacter();
                                break;

                            case ReaderStateMachine.AdvanceResult.Append_Character:
                                var length = i - consistentResultSince;

                                Partial.AppendCharacters(buffSpan, consistentResultSince, length);
                                break;

                            default:
                                unprocessedCharacters = default;
                                Throw.ImpossibleException($"Unexpected {nameof(ReaderStateMachine.AdvanceResult)}: {inBatchableResult.Value}", Configuration.Options);
                                return(default);
                            }
                        }

                        // we need to keep everything
                        unprocessedCharacters = bufferLen - i;
                        return(ReadWithCommentResultType.NoValue);
                    }
                }

                var handledUpTo = i;

                // we _might_ need to modify i a bit if we just processed the fallout from a multi-char value separator
                i += advanceIBy;

                // try and batch skips and appends
                //   to save time on copying AND on
                //   basically pointless method calls
                if (inBatchableResult != null)
                {
                    if (res == inBatchableResult)
                    {
                        continue;
                    }
                    else
                    {
                        switch (inBatchableResult.Value)
                        {
                        case ReaderStateMachine.AdvanceResult.Skip_Character:

                            // there's no distinction between skipping several characters and skipping one
                            //    so this doesn't need the length
                            Partial.SkipCharacter();
                            break;

                        case ReaderStateMachine.AdvanceResult.Append_Character:
                            var length = handledUpTo - consistentResultSince;

                            Partial.AppendCharacters(buffSpan, consistentResultSince, length);
                            break;

                        default:
                            unprocessedCharacters = default;
                            Throw.ImpossibleException($"Unexpected {nameof(ReaderStateMachine.AdvanceResult)}: {inBatchableResult.Value}", Configuration.Options);
                            return(default);
                        }

                        inBatchableResult     = null;
                        consistentResultSince = -1;

                        // fall through into the switch to handle the current character
                    }
                }

                // inBatchableResult is always null here
                //   because if it's NOT null we either continue (if res == inBatchableResult),
                //   thereby not hitting this point, or set it to null (if res != inBatchableResult)
                // this means we don't need to handle the inBatchableResult != null cases in
                //   the following switch

                switch (res)
                {
                case ReaderStateMachine.AdvanceResult.Skip_Character:
                    inBatchableResult     = ReaderStateMachine.AdvanceResult.Skip_Character;
                    consistentResultSince = i;
                    continue;

                case ReaderStateMachine.AdvanceResult.Append_Character:
                    inBatchableResult     = ReaderStateMachine.AdvanceResult.Append_Character;
                    consistentResultSince = i;
                    continue;

                case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndCurrentCharacter:
                    Partial.AppendCarriageReturn(buffSpan);
                    Partial.AppendCharacters(buffSpan, i, 1);
                    break;

                case ReaderStateMachine.AdvanceResult.Append_ValueSeparator:
                    Partial.AppendCharactersFromDifferentBuffer(buffSpan, Configuration.ValueSeparatorMemory.Span);
                    break;

                case ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndValueSeparator:
                    Partial.AppendCarriageReturn(buffSpan);
                    Partial.AppendCharactersFromDifferentBuffer(buffSpan, Configuration.ValueSeparatorMemory.Span);
                    break;

                // cannot reach ReaderStateMachine.AdvanceResult.Append_CarriageReturnAndEndComment, because that only happens
                //   when the data ENDs

                case ReaderStateMachine.AdvanceResult.Finished_Unescaped_Value:
                    PushPendingCharactersToValue(false);
                    break;

                case ReaderStateMachine.AdvanceResult.Finished_Escaped_Value:
                    PushPendingCharactersToValue(true);
                    break;

                case ReaderStateMachine.AdvanceResult.Finished_LastValueUnescaped_Record:
                    if (Partial.PendingCharsCount > 0)
                    {
                        PushPendingCharactersToValue(false);
                    }

                    unprocessedCharacters = bufferLen - i - 1;
                    return(ReadWithCommentResultType.HasValue);

                case ReaderStateMachine.AdvanceResult.Finished_LastValueEscaped_Record:
                    if (Partial.PendingCharsCount > 0)
                    {
                        PushPendingCharactersToValue(true);
                    }

                    unprocessedCharacters = bufferLen - i - 1;
                    return(ReadWithCommentResultType.HasValue);

                case ReaderStateMachine.AdvanceResult.Finished_Comment:
                    unprocessedCharacters = bufferLen - i - 1;
                    return(ReadWithCommentResultType.HasComment);

                default:
                    HandleUncommonAdvanceResults(res, c);
                    break;
                }
            }

            // handle any batch that was still pending
            if (inBatchableResult != null)
            {
                switch (inBatchableResult.Value)
                {
                case ReaderStateMachine.AdvanceResult.Skip_Character:
                    // there's no distinction between skipping several characters and skipping one
                    //    so this doesn't need the length
                    Partial.SkipCharacter();
                    break;

                case ReaderStateMachine.AdvanceResult.Append_Character:
                    // we read all the up to the end, so length needs to include the last character
                    var length = bufferLen - consistentResultSince;

                    Partial.AppendCharacters(buffSpan, consistentResultSince, length);
                    break;

                default:
                    unprocessedCharacters = default;
                    Throw.ImpossibleException($"Unexpected {nameof(ReaderStateMachine.AdvanceResult)}: {inBatchableResult.Value}", Configuration.Options);
                    return(default);
                }
            }

            unprocessedCharacters = 0;
            return(ReadWithCommentResultType.NoValue);
        }
Пример #17
0
        /// <summary>
        /// Enumerate members which will be deserialized for the given type.
        ///
        /// Note that these members are generated ahead of time with a source gneerator,
        ///   and cannot be changed at runtime.
        /// </summary>
        public IEnumerable <DeserializableMember> EnumerateMembersToDeserialize(TypeInfo forType)
        {
            var paired = GetPairedType(forType, DESERIALIZER_KIND);

            if (paired == null)
            {
                return(Enumerable.Empty <DeserializableMember>());
            }

            var colNamesProp = paired.GetPropertyNonNull("__ColumnNames", PublicStatic);
            var colNames     = (ImmutableArray <string>)colNamesProp.GetValueNonNull(null);

            var ret = ImmutableArray.CreateBuilder <DeserializableMember>(colNames.Length);

            ParameterInfo[]? consPs = null;

            for (var i = 0; i < colNames.Length; i++)
            {
                var name = colNames[i];

                var colReaderName = $"__Column_{i}";
                var colReaderMtd  = paired.GetMethodNonNull(colReaderName, PublicInstance);

#pragma warning disable CS0618 // These are obsolete to prevent clients from using them, but they are fine for us.
                var isRequired = colReaderMtd.GetCustomAttribute <IsRequiredAttribute>() != null;
                var setterBackedByParameter = colReaderMtd.GetCustomAttribute <SetterBackedByConstructorParameterAttribute>();
                var setterIsInitOnly        = colReaderMtd.GetCustomAttribute <SetterBackedByInitOnlyPropertyAttribute>();
#pragma warning restore CS0618

                Setter setter;
                if (setterBackedByParameter == null && setterIsInitOnly == null)
                {
                    // directly a method
                    var setterName = $"__Column_{i}_Setter";
                    var setterMtd  = paired.GetMethodNonNull(setterName, PublicStatic);
                    setter = Setter.ForMethod(setterMtd);
                }
                else if (setterBackedByParameter != null)
                {
                    // parameter to constructor
                    if (consPs == null)
                    {
                        var ip = GetInstanceProvider(forType);
                        ip = Utils.NonNull(ip);

                        var cons = ip.Constructor.Value;
                        consPs = cons.GetParameters();
                    }

                    var consParameterIndex = setterBackedByParameter.Index;
                    if (consParameterIndex < 0 || consParameterIndex >= consPs.Length)
                    {
                        Throw.ImpossibleException($"Setter for column {i} claims to be backed by constructor parameter, but its position is out of bounds (index={consParameterIndex})");
                    }

                    var p = consPs[setterBackedByParameter.Index];
                    setter = Setter.ForConstructorParameter(p);
                }
                else
                {
                    // init only property
                    var initOnly = Utils.NonNull(setterIsInitOnly);

                    var prop = forType.GetProperty(initOnly.PropertyName, initOnly.BindingFlags);
                    if (prop == null)
                    {
                        Throw.ImpossibleException($"Setter for column {i} claims to be backed by init-only property {initOnly.PropertyName} with bindings ({initOnly.BindingFlags}), but it could not be found");
                    }

                    setter = Setter.ForProperty(prop);
                }

                var resetMethodName = $"__Column_{i}_Reset";
                var resetMtd        = paired.GetMethod(resetMethodName, PublicStatic);
                var reset           = (Reset?)resetMtd;

                var    parserMethodName = $"__Column_{i}_Parser";
                var    parserMtd        = paired.GetMethod(parserMethodName, PublicStatic);
                Parser?parser;
                if (parserMtd != null)
                {
                    parser = Parser.ForMethod(parserMtd);
                }
                else
                {
                    parser = Utils.NonNull(Parser.GetDefault(setter.Takes));
                }

                ret.Add(DeserializableMember.CreateInner(forType, name, setter, parser, isRequired ? MemberRequired.Yes : MemberRequired.No, reset, paired));
            }

            return(ret.ToImmutable());
        }