<commit_before>﻿using FullSerializer.Internal;
using System;
using System.Collections.Generic;

namespace FullSerializer {
    public class fsSerializer {
        /// <summary>
        /// Key used after a cycle has been encountered.
        /// </summary>
        private const string ReferenceIdString = "ReferenceId";

        /// <summary>
        /// Key used for an initial object reference id.
        /// </summary>
        private const string SourceIdString = "SourceId";

        /// <summary>
        /// Key used for the data for an initial object reference.
        /// </summary>
        private const string SourceDataString = "Data";

        /// <summary>
        /// Key used to identify the runtime type of an object.
        /// </summary>
        private const string TypeString = "Type";

        /// <summary>
        /// Key used for the serialized content of the type identified object.
        /// </summary>
        private const string TypeDataString = "Data";

        /// <summary>
        /// A cache from type to it's converter.
        /// </summary>
        private Dictionary<Type, fsConverter> _cachedConverters;

        /// <summary>
        /// Converters that are available.
        /// </summary>
        private List<fsConverter> _converters;

        /// <summary>
        /// Reference manager for cycle detection.
        /// </summary>
        private fsCyclicReferenceManager _references;

        public fsSerializer() {
            _cachedConverters = new Dictionary<Type, fsConverter>();
            _references = new fsCyclicReferenceManager();

            _converters = new List<fsConverter>() {
                new fsNullableConverter() { Serializer = this },
                new fsTypeConverter() { Serializer = this },
                new fsDateConverter() { Serializer = this },
                new fsEnumConverter() { Serializer = this },
                new fsPrimitiveConverter() { Serializer = this },
                new fsArrayConverter() { Serializer = this },
                new fsIEnumerableConverter() { Serializer = this },
                new fsReflectedConverter() { Serializer = this }
            };

            Context = new fsContext();
        }

        /// <summary>
        /// A context object that fsConverters can use to customize how they operate.
        /// </summary>
        public fsContext Context;

        /// <summary>
        /// Adds a new converter that can be used to customize how an object is serialized and
        /// deserialized.
        /// </summary>
        public void AddConverter(fsConverter converter) {
            if (converter.Serializer != null) {
                throw new InvalidOperationException("Cannot add a single converter instance to " +
                    "multiple JsonConverters -- please construct a new instance for " + converter);
            }

            _converters.Insert(0, converter);
            converter.Serializer = this;

            // We need to reset our cached converter set, as it could be invalid with the new
            // converter. Ideally, _cachedConverters should be empty, but there is no guarantee.
            _cachedConverters = new Dictionary<Type, fsConverter>();
        }

        /// <summary>
        /// Fetches a converter that can serialize/deserialize the given type.
        /// </summary>
        private fsConverter GetConverter(Type type) {
            fsConverter converter = null;

            if (_cachedConverters.TryGetValue(type, out converter) == false) {
                for (int i = 0; i < _converters.Count; ++i) {
                    if (_converters[i].CanProcess(type)) {
                        converter = _converters[i];
                        _cachedConverters[type] = converter;
                        break;
                    }
                }
            }

            if (converter == null) {
                throw new InvalidOperationException("Internal error -- could not find a converter for " + type);
            }
            return converter;
        }

        /// <summary>
        /// Helper method that simply forwards the call to TrySerialize(typeof(T), instance, out data);
        /// </summary>
        public fsFailure TrySerialize<T>(T instance, out fsData data) {
            return TrySerialize(typeof(T), instance, out data);
        }

        /// <summary>
        /// Generic wrapper around TryDeserialize that simply forwards the call.
        /// </summary>
        public fsFailure TryDeserialize<T>(fsData data, ref T instance) {
            object boxed = instance;
            var fail = TryDeserialize(data, typeof(T), ref boxed);
            if (fail.Succeeded) {
                instance = (T)boxed;
            }
            return fail;
        }

        /// <summary>
        /// Serialize the given value.
        /// </summary>
        /// <param name="storageType">The type of field/property that stores the object instance. This is
        /// important particularly for inheritance, as a field storing an IInterface instance
        /// should have type information included.</param>
        /// <param name="instance">The actual object instance to serialize.</param>
        /// <param name="data">The serialized state of the object.</param>
        /// <returns>If serialization was successful.</returns>
        public fsFailure TrySerialize(Type storageType, object instance, out fsData data) {
            if (instance == null) {
                data = new fsData();
                return fsFailure.Success;
            }

            // Not a cyclic type, ignore cycles
            if (GetConverter(storageType).RequestCycleSupport(storageType) == false) {
                return InternalSerialize(storageType, instance, out data);
            }

            // We have to handle cycles.
            try {
                _references.Enter();

                // This is the second time we've encountered this object instance. Just serialize a<commit_msg>Fix for cyclic type serialization<commit_after>﻿using FullSerializer.Internal;
using System;
using System.Collections.Generic;

namespace FullSerializer {
    public class fsSerializer {
        /// <summary>
        /// Key used after a cycle has been encountered.
        /// </summary>
        private const string ReferenceIdString = "ReferenceId";

        /// <summary>
        /// Key used for an initial object reference id.
        /// </summary>
        private const string SourceIdString = "SourceId";

        /// <summary>
        /// Key used for the data for an initial object reference.
        /// </summary>
        private const string SourceDataString = "Data";

        /// <summary>
        /// Key used to identify the runtime type of an object.
        /// </summary>
        private const string TypeString = "Type";

        /// <summary>
        /// Key used for the serialized content of the type identified object.
        /// </summary>
        private const string TypeDataString = "Data";

        /// <summary>
        /// A cache from type to it's converter.
        /// </summary>
        private Dictionary<Type, fsConverter> _cachedConverters;

        /// <summary>
        /// Converters that are available.
        /// </summary>
        private List<fsConverter> _converters;

        /// <summary>
        /// Reference manager for cycle detection.
        /// </summary>
        private fsCyclicReferenceManager _references;

        public fsSerializer() {
            _cachedConverters = new Dictionary<Type, fsConverter>();
            _references = new fsCyclicReferenceManager();

            _converters = new List<fsConverter>() {
                new fsNullableConverter() { Serializer = this },
                new fsTypeConverter() { Serializer = this },
                new fsDateConverter() { Serializer = this },
                new fsEnumConverter() { Serializer = this },
                new fsPrimitiveConverter() { Serializer = this },
                new fsArrayConverter() { Serializer = this },
                new fsIEnumerableConverter() { Serializer = this },
                new fsReflectedConverter() { Serializer = this }
            };

            Context = new fsContext();
        }

        /// <summary>
        /// A context object that fsConverters can use to customize how they operate.
        /// </summary>
        public fsContext Context;

        /// <summary>
        /// Adds a new converter that can be used to customize how an object is serialized and
        /// deserialized.
        /// </summary>
        public void AddConverter(fsConverter converter) {
            if (converter.Serializer != null) {
                throw new InvalidOperationException("Cannot add a single converter instance to " +
                    "multiple JsonConverters -- please construct a new instance for " + converter);
            }

            _converters.Insert(0, converter);
            converter.Serializer = this;

            // We need to reset our cached converter set, as it could be invalid with the new
            // converter. Ideally, _cachedConverters should be empty, but there is no guarantee.
            _cachedConverters = new Dictionary<Type, fsConverter>();
        }

        /// <summary>
        /// Fetches a converter that can serialize/deserialize the given type.
        /// </summary>
        private fsConverter GetConverter(Type type) {
            fsConverter converter = null;

            if (_cachedConverters.TryGetValue(type, out converter) == false) {
                for (int i = 0; i < _converters.Count; ++i) {
                    if (_converters[i].CanProcess(type)) {
                        converter = _converters[i];
                        _cachedConverters[type] = converter;
                        break;
                    }
                }
            }

            if (converter == null) {
                throw new InvalidOperationException("Internal error -- could not find a converter for " + type);
            }
            return converter;
        }

        /// <summary>
        /// Helper method that simply forwards the call to TrySerialize(typeof(T), instance, out data);
        /// </summary>
        public fsFailure TrySerialize<T>(T instance, out fsData data) {
            return TrySerialize(typeof(T), instance, out data);
        }

        /// <summary>
        /// Generic wrapper around TryDeserialize that simply forwards the call.
        /// </summary>
        public fsFailure TryDeserialize<T>(fsData data, ref T instance) {
            object boxed = instance;
            var fail = TryDeserialize(data, typeof(T), ref boxed);
            if (fail.Succeeded) {
                instance = (T)boxed;
            }
            return fail;
        }

        /// <summary>
        /// Serialize the given value.
        /// </summary>
        /// <param name="storageType">The type of field/property that stores the object instance. This is
        /// important particularly for inheritance, as a field storing an IInterface instance
        /// should have type information included.</param>
        /// <param name="instance">The actual object instance to serialize.</param>
        /// <param name="data">The serialized state of the object.</param>
        /// <returns>If serialization was successful.</returns>
        public fsFailure TrySerialize(Type storageType, object instance, out fsData data) {
            if (instance == null) {
                data = new fsData();
                return fsFailure.Success;
            }

            // Not a cyclic type, ignore cycles
            if (GetConverter(instance.GetType()).RequestCycleSupport(instance.GetType()) == false) {
                return InternalSerialize(storageType, instance, out data);
            }

            // We have to handle cycles.
            try {
                _references.Enter();

                // This is the second time we've encountered this object instance. Just serialize a
{'fraction_non_alphanumeric': 0.09767441860465116, 'fraction_numerical': 0.0003322259136212625, 'mean_word_length': 1.7162192646063614, 'pattern_counts': {'":': 0, '<': 91, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4785574', 'n_tokens_mistral': 2879, 'n_tokens_neox': 2741, 'n_words': 969}