/// <summary>
        /// Serializes counter (metric) data to the stream.
        /// </summary>
        /// <param name="stream">Stream to which data should be serialized. Stream should be writable and provide random access.</param>
        /// <param name="metricData">Collection of metric data to be serialized.</param>
        public void Serialize(Stream stream, IEnumerable <IReadOnlyMetric> metricData)
        {
            if (!stream.CanWrite || !stream.CanSeek)
            {
                throw new ArgumentException("Stream should be writable and provide random access.", nameof(stream));
            }

            try
            {
                using (var writer = new NoCloseBinaryWriter(stream, Encoding.UTF8))
                {
                    var startStreamPosition = stream.Position;

                    // Write version and type serializers info
                    writer.Write(this.version);

                    long crcOffSet     = 0;
                    long crcBodyOffSet = 0;
                    if (this.version >= 5)
                    {
                        // Add CRC
                        crcOffSet = stream.Position;
                        writer.Write((uint)0);
                        crcBodyOffSet = stream.Position;
                    }

                    writer.Write(TypeSerializerFlags);

                    // Reserve place to write type serializers data sections offsets
                    var offsetsPosition = stream.Position;
                    stream.Position += 2 * sizeof(long);

                    // Write metrics data
                    this.WriteMetricsData(writer, metricData);

                    // Write cached metrics metadata and offset
                    var serializerDataPosition = stream.Position;
                    stream.Position = offsetsPosition;
                    writer.Write(serializerDataPosition - startStreamPosition);
                    offsetsPosition = stream.Position;
                    stream.Position = serializerDataPosition;
                    SerializationUtils.WriteUInt32AsBase128(writer, (uint)this.metadatas.Count);
                    this.metadatas.ForEach(m => this.WriteMetricMetadata(writer, m));

                    // Write cached strings and offset
                    serializerDataPosition = stream.Position;
                    stream.Position        = offsetsPosition;
                    writer.Write(serializerDataPosition - startStreamPosition);
                    stream.Position = serializerDataPosition;
                    SerializationUtils.WriteUInt32AsBase128(writer, (uint)this.strings.Count);
                    this.strings.ForEach(writer.Write);
                    var endOfStream = stream.Position;

                    if (this.version >= 5)
                    {
                        stream.Position = crcBodyOffSet;
                        var crc = Crc.ComputeCrc(0, stream, stream.Length - crcBodyOffSet);
                        stream.Position = crcOffSet;
                        writer.Write(crc);
                    }

                    stream.Position = endOfStream;
                }
            }
            catch (IOException ioException)
            {
                throw new MetricSerializationException("Failed to serialize data.", ioException);
            }
            finally
            {
                this.nextStringIndex = 0;
                this.stringIndexes.Clear();
                this.strings.Clear();
                this.nextMetadataIndex = 0;
                this.metadataIndexes.Clear();
                this.metadatas.Clear();
                this.currentMetricDataBlockSize         = 0;
                this.currentMetadataDictionaryBlockSize = 0;
                this.currentStringDictionaryBlockSize   = 0;
            }
        }