[Git][java-team/hdrhistogram][upstream] New upstream version 2.1.11

Emmanuel Bourg gitlab at salsa.debian.org
Sat Jan 19 23:13:49 GMT 2019


Emmanuel Bourg pushed to branch upstream at Debian Java Maintainers / hdrhistogram


Commits:
ecf9920b by Emmanuel Bourg at 2019-01-19T22:33:10Z
New upstream version 2.1.11
- - - - -


29 changed files:

- GoogleChartsExample/plotFiles.html
- README.md
- pom.xml
- src/main/java/org/HdrHistogram/AbstractHistogram.java
- − src/main/java/org/HdrHistogram/AbstractHistogramLogReader.java
- src/main/java/org/HdrHistogram/Base64Helper.java
- src/main/java/org/HdrHistogram/ConcurrentHistogram.java
- src/main/java/org/HdrHistogram/DoubleHistogram.java
- src/main/java/org/HdrHistogram/DoubleRecorder.java
- + src/main/java/org/HdrHistogram/DoubleValueRecorder.java
- src/main/java/org/HdrHistogram/Histogram.java
- src/main/java/org/HdrHistogram/HistogramLogProcessor.java
- src/main/java/org/HdrHistogram/HistogramLogReader.java
- + src/main/java/org/HdrHistogram/HistogramLogScanner.java
- src/main/java/org/HdrHistogram/IntCountsHistogram.java
- src/main/java/org/HdrHistogram/LinearIterator.java
- src/main/java/org/HdrHistogram/Recorder.java
- src/main/java/org/HdrHistogram/ShortCountsHistogram.java
- src/main/java/org/HdrHistogram/SingleWriterDoubleRecorder.java
- src/main/java/org/HdrHistogram/SingleWriterRecorder.java
- src/main/java/org/HdrHistogram/SynchronizedDoubleHistogram.java
- src/main/java/org/HdrHistogram/SynchronizedHistogram.java
- + src/main/java/org/HdrHistogram/ValueRecorder.java
- src/perf/java/org/HdrHistogram/HistogramPerfTest.java
- src/test/java/org/HdrHistogram/DoubleHistogramTest.java
- src/test/java/org/HdrHistogram/HistogramAutosizingTest.java
- src/test/java/org/HdrHistogram/HistogramDataAccessTest.java
- src/test/java/org/HdrHistogram/HistogramTest.java
- src/test/java/org/HdrHistogram/RecorderTest.java


Changes:

=====================================
GoogleChartsExample/plotFiles.html
=====================================
@@ -188,7 +188,6 @@
 
         var names = [];
         var histos = [];
-        var fileCount = 0;
 
         fileDisplayArea.innerText = "file selected...\n";
 
@@ -375,7 +374,7 @@ Percentile range:
 <script type="text/javascript">
     function showValue(newValue) {
         var x = Math.pow(10, newValue);
-        percentile = 100.0 - (100.0 / x);
+        var percentile = 100.0 - (100.0 / x);
         document.getElementById("percentileRange").innerHTML=percentile + "%";
         maxPercentile = x;
         drawChart();


=====================================
README.md
=====================================
@@ -2,15 +2,19 @@ HdrHistogram
 ----------------------------------------------
 [![Gitter](https://img.shields.io/gitter/room/gitterHQ/gitter.svg)](https://gitter.im/HdrHistogram/HdrHistogram?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Build Status](https://travis-ci.org/HdrHistogram/HdrHistogram.svg?branch=master)](https://travis-ci.org/HdrHistogram/HdrHistogram) 
-[![Javadoc](http://javadoc-emblem.rhcloud.com/doc/org.hdrhistogram/HdrHistogram/badge.svg)](http://www.javadoc.io/doc/org.hdrhistogram/HdrHistogram)
+[![Javadocs](http://www.javadoc.io/badge/org.hdrhistogram/HdrHistogram.svg)](http://www.javadoc.io/doc/org.hdrhistogram/HdrHistogram)
+[![Total alerts](https://img.shields.io/lgtm/alerts/g/HdrHistogram/HdrHistogram.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/HdrHistogram/HdrHistogram/alerts/)
+[![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/HdrHistogram/HdrHistogram.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/HdrHistogram/HdrHistogram/context:java)
 
 ----------------------------------------------------------------------------
 HdrHistogram: A High Dynamic Range (HDR) Histogram
 
-This respository currently includes Java and C# implementations of
-HdrHistogram, C, Python, Erlang, and Go ports can be found in other
-respositories. All of which share common concepts and data
-representation capabilities.
+This respository currently includes a Java implementation of
+HdrHistogram. C, C#/.NET, Python, Javascript, Rust, Erlang, and Go ports
+can be found in other respositories. All of which share common concepts
+and data representation capabilities. Look at repositories under the
+[HdrHistogram organization](https://github.com/HdrHistogram) for various
+implementations and useful tools.
 
 Note: The below is an excerpt from a Histogram JavaDoc. While much
 of it generally applies to other language implementations as well,


=====================================
pom.xml
=====================================
@@ -2,15 +2,9 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
-    <parent>
-        <groupId>org.sonatype.oss</groupId>
-        <artifactId>oss-parent</artifactId>
-        <version>7</version>
-    </parent>
-
     <groupId>org.hdrhistogram</groupId>
     <artifactId>HdrHistogram</artifactId>
-    <version>2.1.10</version>
+    <version>2.1.11</version>
 
     <name>HdrHistogram</name>
 
@@ -52,7 +46,7 @@
         <url>scm:git:git://github.com/HdrHistogram/HdrHistogram.git</url>
         <connection>scm:git:git://github.com/HdrHistogram/HdrHistogram.git</connection>
         <developerConnection>scm:git:git at github.com:HdrHistogram/HdrHistogram.git</developerConnection>
-      <tag>HdrHistogram-2.1.10</tag>
+      <tag>HdrHistogram-2.1.11</tag>
     </scm>
 
 
@@ -127,7 +121,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>2.3.2</version>
+                <version>3.8.0</version>
                 <configuration>
                     <source>1.6</source>
                     <target>1.6</target>
@@ -145,9 +139,12 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-release-plugin</artifactId>
-                <version>2.5</version>
+                <version>2.5.3</version>
                 <configuration>
-                    <arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments>
+                    <autoVersionSubmodules>true</autoVersionSubmodules>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <releaseProfiles>release</releaseProfiles>
+                    <goals>deploy</goals>
                 </configuration>
             </plugin>
             <plugin>
@@ -203,12 +200,44 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.sonatype.plugins</groupId>
+                <artifactId>nexus-staging-maven-plugin</artifactId>
+                <version>1.6.7</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <serverId>ossrh</serverId>
+                    <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 
     <profiles>
         <profile>
-            <id>release-sign-artifacts</id>
+            <id>deploy</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.5</version>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>release</id>
             <activation>
                 <property>
                     <name>performRelease</name>
@@ -220,10 +249,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-gpg-plugin</artifactId>
-                        <version>1.4</version>
-                        <configuration>
-                            <passphrase>${gpg.passphrase}</passphrase>
-                        </configuration>
+                        <version>1.5</version>
                         <executions>
                             <execution>
                                 <id>sign-artifacts</id>


=====================================
src/main/java/org/HdrHistogram/AbstractHistogram.java
=====================================
@@ -70,10 +70,12 @@ abstract class AbstractHistogramBase extends EncodableHistogram {
         return doubleToIntegerValueConversionRatio;
     }
 
-    void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+    void nonConcurrentSetIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
         this.integerToDoubleValueConversionRatio = integerToDoubleValueConversionRatio;
         this.doubleToIntegerValueConversionRatio = 1.0/integerToDoubleValueConversionRatio;
     }
+
+    abstract void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio);
 }
 
 /**
@@ -95,7 +97,7 @@ abstract class AbstractHistogramBase extends EncodableHistogram {
  * See package description for {@link org.HdrHistogram} for details.
  *
  */
-public abstract class AbstractHistogram extends AbstractHistogramBase implements Serializable {
+public abstract class AbstractHistogram extends AbstractHistogramBase implements ValueRecorder, Serializable {
 
     // "Hot" accessed fields (used in the the value recording code path) are bunched here, such
     // that they will have a good chance of ending up in the same cache line as the totalCounts and
@@ -422,6 +424,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
      * @param value The value to be recorded
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValue(final long value) throws ArrayIndexOutOfBoundsException {
         recordSingleValue(value);
     }
@@ -433,6 +436,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
      * @param count The number of occurrences of this value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithCount(final long value, final long count) throws ArrayIndexOutOfBoundsException {
         recordCountAtValue(count, value);
     }
@@ -458,6 +462,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
      *                                           than expectedIntervalBetweenValueSamples
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
             throws ArrayIndexOutOfBoundsException {
         recordSingleValueWithExpectedInterval(value, expectedIntervalBetweenValueSamples);
@@ -529,7 +534,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
 
     private void handleRecordException(final long count, final long value, Exception ex) {
         if (!autoResize) {
-            throw new ArrayIndexOutOfBoundsException("value outside of histogram covered range. Caused by: " + ex);
+            throw new ArrayIndexOutOfBoundsException("value " + value + " outside of histogram covered range. Caused by: " + ex);
         }
         resize(value);
         int countsIndex = countsArrayIndex(value);
@@ -574,6 +579,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
     /**
      * Reset the contents and stats of this histogram
      */
+    @Override
     public void reset() {
         clearCounts();
         resetMaxValue(0);
@@ -831,7 +837,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
         long maxValueBeforeShift = maxValueUpdater.getAndSet(this, 0);
         long minNonZeroValueBeforeShift = minNonZeroValueUpdater.getAndSet(this, Long.MAX_VALUE);
 
-        boolean lowestHalfBucketPopulated = (minNonZeroValueBeforeShift < subBucketHalfCount);
+        boolean lowestHalfBucketPopulated = (minNonZeroValueBeforeShift < (subBucketHalfCount << unitMagnitude));
 
         // Perform the shift:
         shiftNormalizingIndexByOffset(shiftAmount, lowestHalfBucketPopulated, newIntegerToDoubleValueConversionRatio);
@@ -848,19 +854,27 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
         // Save and clear the 0 value count:
         long zeroValueCount = getCountAtIndex(0);
         setCountAtIndex(0, 0);
+        int preShiftZeroIndex = normalizeIndex(0, getNormalizingIndexOffset(), countsArrayLength);
 
         setNormalizingIndexOffset(getNormalizingIndexOffset() + shiftAmount);
 
         // Deal with lower half bucket if needed:
         if (lowestHalfBucketPopulated) {
-            shiftLowestHalfBucketContentsLeft(shiftAmount);
+            if (shiftAmount <= 0) {
+                // Shifts with lowest half bucket populated can only be to the left.
+                // Any right shift logic calling this should have already verified that
+                // the lowest half bucket is not populated.
+                throw new ArrayIndexOutOfBoundsException(
+                        "Attempt to right-shift with already-recorded value counts that would underflow and lose precision");
+            }
+            shiftLowestHalfBucketContentsLeft(shiftAmount, preShiftZeroIndex);
         }
 
         // Restore the 0 value count:
         setCountAtIndex(0, zeroValueCount);
     }
 
-    private void shiftLowestHalfBucketContentsLeft(int shiftAmount) {
+    private void shiftLowestHalfBucketContentsLeft(int shiftAmount, int preShiftZeroIndex) {
         final int numberOfBinaryOrdersOfMagnitude = shiftAmount >> subBucketHalfCountMagnitude;
 
         // The lowest half-bucket (not including the 0 value) is special: unlike all other half
@@ -881,9 +895,9 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
         for (int fromIndex = 1; fromIndex < subBucketHalfCount; fromIndex++) {
             long toValue = valueFromIndex(fromIndex) << numberOfBinaryOrdersOfMagnitude;
             int toIndex = countsArrayIndex(toValue);
-            long countAtFromIndex = getCountAtNormalizedIndex(fromIndex);
+            long countAtFromIndex = getCountAtNormalizedIndex(fromIndex + preShiftZeroIndex);
             setCountAtIndex(toIndex, countAtFromIndex);
-            setCountAtNormalizedIndex(fromIndex, 0);
+            setCountAtNormalizedIndex(fromIndex + preShiftZeroIndex, 0);
         }
 
         // Note that the above loop only creates O(N) work for histograms that have values in
@@ -1008,9 +1022,25 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
         if (getMinNonZeroValue() != that.getMinNonZeroValue()) {
             return false;
         }
-        for (int i = 0; i < countsArrayLength; i++) {
-            if (getCountAtIndex(i) != that.getCountAtIndex(i)) {
-                return false;
+        // 2 histograms may be equal but have different underlying array sizes. This can happen for instance due to
+        // resizing.
+        if (countsArrayLength == that.countsArrayLength) {
+            for (int i = 0; i < countsArrayLength; i++) {
+                if (getCountAtIndex(i) != that.getCountAtIndex(i)) {
+                    return false;
+                }
+            }
+        }
+        else
+        {
+            // Comparing the values is valid here because we have already confirmed the histograms have the same total
+            // count. It would not be correct otherwise.
+            for (HistogramIterationValue value : this.recordedValues()) {
+                long countAtValueIteratedTo = value.getCountAtValueIteratedTo();
+                long valueIteratedTo = value.getValueIteratedTo();
+                if (that.getCountAtValue(valueIteratedTo) != countAtValueIteratedTo) {
+                    return false;
+                }
             }
         }
         return true;
@@ -1283,7 +1313,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
         while (recordedValuesIterator.hasNext()) {
             HistogramIterationValue iterationValue = recordedValuesIterator.next();
             totalValue += medianEquivalentValue(iterationValue.getValueIteratedTo())
-                    * iterationValue.getCountAtValueIteratedTo();
+                    * (double) iterationValue.getCountAtValueIteratedTo();
         }
         return (totalValue * 1.0) / getTotalCount();
     }


=====================================
src/main/java/org/HdrHistogram/AbstractHistogramLogReader.java deleted
=====================================
@@ -1,213 +0,0 @@
-/**
- * Written by Gil Tene of Azul Systems, and released to the public domain,
- * as explained at http://creativecommons.org/publicdomain/zero/1.0/
- *
- * @author Gil Tene
- */
-
-package org.HdrHistogram;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Locale;
-import java.util.Scanner;
-import java.util.zip.DataFormatException;
-
-class AbstractHistogramLogReader {
-
-    protected final Scanner scanner;
-    private double startTimeSec = 0.0;
-
-    /**
-     * Constructs a new HistogramLogReader that produces intervals read from the specified file name.
-     * @param inputFileName The name of the file to read from
-     * @throws java.io.FileNotFoundException when unable to find inputFileName
-     */
-    public AbstractHistogramLogReader(final String inputFileName) throws FileNotFoundException {
-        scanner = new Scanner(new File(inputFileName));
-        initScanner();
-    }
-
-    /**
-     * Constructs a new HistogramLogReader that produces intervals read from the specified InputStream.
-     * @param inputStream The InputStream to read from
-     */
-    public AbstractHistogramLogReader(final InputStream inputStream) {
-        scanner = new Scanner(inputStream);
-        initScanner();
-    }
-
-    /**
-     * Constructs a new HistogramLogReader that produces intervals read from the specified file.
-     * @param inputFile The File to read from
-     * @throws java.io.FileNotFoundException when unable to find inputFile
-     */
-    public AbstractHistogramLogReader(final File inputFile) throws FileNotFoundException {
-        scanner = new Scanner(inputFile);
-        initScanner();
-    }
-
-
-    private void initScanner() {
-        scanner.useLocale(Locale.US);
-        scanner.useDelimiter("[ ,\\r\\n]");
-    }
-
-    /**
-     * get the latest start time found in the file so far (or 0.0),
-     * per the log file format explained above. Assuming the "#[StartTime:" comment
-     * line precedes the actual intervals recorded in the file, getStartTimeSec() can
-     * be safely used after each interval is read to determine's the offset of that
-     * interval's timestamp from the epoch.
-     * @return latest Start Time found in the file (or 0.0 if non found)
-     */
-    public double getStartTimeSec() {
-        return startTimeSec;
-    }
-
-    protected void setStartTimeSec(double startTimeSec) {
-        this.startTimeSec = startTimeSec;
-    }
-
-    /**
-     * Read the next interval histogram from the log, if interval falls within a time range.
-     * <p>
-     * Returns a histogram object if an interval line was found with an
-     * associated start timestamp value that falls between startTimeSec and
-     * endTimeSec, or null if no such interval line is found. Note that
-     * the range is assumed to be in seconds relative to the actual
-     * timestamp value found in each interval line in the log, and not
-     * in absolute time.
-     *  <p>
-     * Timestamps are assumed to appear in order in the log file, and as such
-     * this method will return a null upon encountering a timestamp larger than
-     * rangeEndTimeSec.
-     * <p>
-     * The histogram returned will have it's timestamp set to the absolute
-     * timestamp calculated from adding the interval's indicated timestamp
-     * value to the latest [optional] start time found in the log.
-     * <p>
-     * Upon encountering any unexpected format errors in reading the next
-     * interval from the file, this method will return a null.
-     * @param startTimeSec The (non-absolute time) start of the expected
-     *                     time range, in seconds.
-     * @param endTimeSec The (non-absolute time) end of the expected time
-     *                   range, in seconds.
-     * @return a histogram, or a null if no appropriate interval found
-     */
-    public EncodableHistogram nextIntervalHistogram(final Double startTimeSec,
-                                           final Double endTimeSec) {
-        return nextIntervalHistogram(startTimeSec, endTimeSec, false);
-    }
-
-    /**
-     * Read the next interval histogram from the log, if interval falls within an absolute time range
-     * <p>
-     * Returns a histogram object if an interval line was found with an
-     * associated absolute start timestamp value that falls between
-     * absoluteStartTimeSec and absoluteEndTimeSec, or null if no such
-     * interval line is found.
-     * <p>
-     * Timestamps are assumed to appear in order in the log file, and as such
-     * this method will return a null upon encountering a timestamp larger than
-     * rangeEndTimeSec.
-     * <p>
-     * The histogram returned will have it's timestamp set to the absolute
-     * timestamp calculated from adding the interval's indicated timestamp
-     * value to the latest [optional] start time found in the log.
-     * <p>
-     * Absolute timestamps are calculated by adding the timestamp found
-     * with the recorded interval to the [latest, optional] start time
-     * found in the log. The start time is indicated in the log with
-     * a "#[StartTime: " followed by the start time in seconds.
-     * <p>
-     * Upon encountering any unexpected format errors in reading the next
-     * interval from the file, this method will return a null.
-     * @param absoluteStartTimeSec The (absolute time) start of the expected
-     *                             time range, in seconds.
-     * @param absoluteEndTimeSec The (absolute time) end of the expected
-     *                           time range, in seconds.
-     * @return A histogram, or a null if no appropriate interval found
-     */
-    public EncodableHistogram nextAbsoluteIntervalHistogram(final Double absoluteStartTimeSec,
-                                                   final Double absoluteEndTimeSec) {
-        return nextIntervalHistogram(absoluteStartTimeSec, absoluteEndTimeSec, true);
-    }
-
-
-    /**
-     * Read the next interval histogram from the log. Returns a Histogram object if
-     * an interval line was found, or null if not.
-     * <p>Upon encountering any unexpected format errors in reading the next interval
-     * from the file, this method will return a null.
-     * @return a DecodedInterval, or a null if no appropriate interval found
-     */
-    public EncodableHistogram nextIntervalHistogram() {
-        return nextIntervalHistogram(0.0, Long.MAX_VALUE * 1.0, true);
-    }
-
-    private EncodableHistogram nextIntervalHistogram(final Double rangeStartTimeSec,
-                                            final Double rangeEndTimeSec, boolean absolute) {
-        while (scanner.hasNextLine()) {
-            try {
-                if (scanner.hasNext("\\#.*")) {
-                    // comment line
-                    if (scanner.hasNext("#\\[StartTime:")) {
-                        scanner.next("#\\[StartTime:");
-                        if (scanner.hasNextDouble()) {
-                            setStartTimeSec(scanner.nextDouble()); // start time represented as seconds since epoch
-                        }
-                    }
-                    scanner.nextLine();
-                    continue;
-                }
-
-                if (scanner.hasNext("\"StartTimestamp\".*")) {
-                    // Legend line
-                    scanner.nextLine();
-                    continue;
-                }
-
-                // Decode: startTimestamp, intervalLength, maxTime, histogramPayload
-
-                final double offsetStartTimeStampSec = scanner.nextDouble(); // Timestamp start is expect to be in seconds
-                final double absoluteStartTimeStampSec = getStartTimeSec() + offsetStartTimeStampSec;
-
-                final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds
-                final double offsetEndTimeStampSec = offsetStartTimeStampSec + intervalLengthSec;
-                final double absoluteEndTimeStampSec = getStartTimeSec() + offsetEndTimeStampSec;
-
-                final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec;
-
-                if (startTimeStampToCheckRangeOn < rangeStartTimeSec) {
-                    scanner.nextLine();
-                    continue;
-                }
-
-                if (startTimeStampToCheckRangeOn > rangeEndTimeSec) {
-                    return null;
-                }
-
-                scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram.
-                final String compressedPayloadString = scanner.next();
-                final ByteBuffer buffer = ByteBuffer.wrap(
-                        Base64Helper.parseBase64Binary(compressedPayloadString));
-
-                EncodableHistogram histogram = Histogram.decodeFromCompressedByteBuffer(buffer, 0);
-
-                histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0));
-                histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0));
-
-                return histogram;
-
-            } catch (java.util.NoSuchElementException ex) {
-                return null;
-            } catch (DataFormatException ex) {
-                return null;
-            }
-        }
-        return null;
-    }
-}


=====================================
src/main/java/org/HdrHistogram/Base64Helper.java
=====================================
@@ -10,15 +10,20 @@ package org.HdrHistogram;
 import java.lang.reflect.Method;
 
 /**
- * Base64Helper exists to bridge gaps between Java SE platform support of Base64 encoding and decoding.
+ * Base64Helper exists to bridge inconsistencies in Java SE support of Base64 encoding and decoding.
  * Earlier Java SE platforms (up to and including Java SE 8) supported base64 encode/decode via the
  * javax.xml.bind.DatatypeConverter class, which was deprecated and eventually removed in Java SE 9.
- * Later Java SE platforms Java SE 8 and later support supported base64 encode/decode via the
+ * Later Java SE platforms (Java SE 8 and later) support base64 encode/decode via the
  * java.util.Base64 class (first introduced in Java SE 8, and not available on e.g. Java SE 6 or 7).
  *
+ * This makes it "hard" to write a single piece of source code that deals with base64 encodings and
+ * will compile and run on e.g. Java SE 7 AND Java SE 9. And such common source is a common need for
+ * libraries. This class is intended to encapsulate this "hard"-ness and hide the ugly pretzle-twising
+ * needed under the covers.
+ *
  * Base64Helper provides a common API that works across Java SE 6..9 (and beyond hopefully), and
- * uses late binding (Reflection) to avoid javac-compile-time dependencies on a specific Java SE
- * version (e.g. beyond 6 or before 9).
+ * uses late binding (Reflection) internally to avoid javac-compile-time dependencies on a specific
+ * Java SE version (e.g. beyond 7 or before 9).
  *
  */
 public class Base64Helper {


=====================================
src/main/java/org/HdrHistogram/ConcurrentHistogram.java
=====================================
@@ -211,6 +211,8 @@ public class ConcurrentHistogram extends Histogram {
             assert (countsArrayLength == activeCounts.length());
             assert (countsArrayLength == inactiveCounts.length());
 
+            assert (activeCounts.getNormalizingIndexOffset() == inactiveCounts.getNormalizingIndexOffset());
+
             if (normalizingIndexOffset == activeCounts.getNormalizingIndexOffset()) {
                 return; // Nothing to do.
             }
@@ -225,13 +227,13 @@ public class ConcurrentHistogram extends Histogram {
 
             // Handle the inactive lowest half bucket:
             if ((shiftedAmount > 0) && lowestHalfBucketPopulated) {
-                shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount);
+                shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount, zeroIndex);
             }
 
             // Restore the inactive 0 value count:
             zeroIndex = normalizeIndex(0, inactiveCounts.getNormalizingIndexOffset(), inactiveCounts.length());
             inactiveCounts.lazySet(zeroIndex, inactiveZeroValueCount);
-            
+
             inactiveCounts.doubleToIntegerValueConversionRatio = 1.0 / newIntegerToDoubleValueConversionRatio;
 
             // switch active and inactive:
@@ -251,7 +253,7 @@ public class ConcurrentHistogram extends Histogram {
 
             // Handle the newly inactive lowest half bucket:
             if ((shiftedAmount > 0) && lowestHalfBucketPopulated) {
-                shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount);
+                shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount, zeroIndex);
             }
 
             // Restore the newly inactive 0 value count:
@@ -275,7 +277,7 @@ public class ConcurrentHistogram extends Histogram {
         }
     }
 
-    private void shiftLowestInactiveHalfBucketContentsLeft(final int shiftAmount) {
+    private void shiftLowestInactiveHalfBucketContentsLeft(final int shiftAmount, final int preShiftZeroIndex) {
         final int numberOfBinaryOrdersOfMagnitude = shiftAmount >> subBucketHalfCountMagnitude;
 
         // The lowest inactive half-bucket (not including the 0 value) is special: unlike all other half
@@ -298,9 +300,9 @@ public class ConcurrentHistogram extends Histogram {
             int toIndex = countsArrayIndex(toValue);
             int normalizedToIndex =
                     normalizeIndex(toIndex, inactiveCounts.getNormalizingIndexOffset(), inactiveCounts.length());
-            long countAtFromIndex = inactiveCounts.get(fromIndex);
+            long countAtFromIndex = inactiveCounts.get(fromIndex + preShiftZeroIndex);
             inactiveCounts.lazySet(normalizedToIndex, countAtFromIndex);
-            inactiveCounts.lazySet(fromIndex, 0);
+            inactiveCounts.lazySet(fromIndex + preShiftZeroIndex, 0);
         }
 
         // Note that the above loop only creates O(N) work for histograms that have values in
@@ -344,34 +346,25 @@ public class ConcurrentHistogram extends Histogram {
                 return;
             }
 
-            int oldNormalizedZeroIndex =
-                    normalizeIndex(0, inactiveCounts.getNormalizingIndexOffset(), inactiveCounts.length());
-
-            // Resize the current inactiveCounts:
-            AtomicLongArray oldInactiveCounts = inactiveCounts;
-            inactiveCounts =
+            // Allocate both counts arrays here, so if one allocation fails, neither will "take":
+            AtomicLongArrayWithNormalizingOffset newInactiveCounts1 =
                     new AtomicLongArrayWithNormalizingOffset(
                             newArrayLength,
                             inactiveCounts.getNormalizingIndexOffset()
                     );
+            AtomicLongArrayWithNormalizingOffset newInactiveCounts2 =
+                    new AtomicLongArrayWithNormalizingOffset(
+                            newArrayLength,
+                            activeCounts.getNormalizingIndexOffset()
+                    );
+
+
+            // Resize the current inactiveCounts:
+            AtomicLongArrayWithNormalizingOffset oldInactiveCounts = inactiveCounts;
+            inactiveCounts = newInactiveCounts1;
+
             // Copy inactive contents to newly sized inactiveCounts:
-            for (int i = 0 ; i < oldInactiveCounts.length(); i++) {
-                inactiveCounts.lazySet(i, oldInactiveCounts.get(i));
-            }
-            if (oldNormalizedZeroIndex != 0) {
-                // We need to shift the stuff from the zero index and up to the end of the array:
-                int newNormalizedZeroIndex = oldNormalizedZeroIndex + countsDelta;
-                int lengthToCopy = (newArrayLength - countsDelta) - oldNormalizedZeroIndex;
-                int src, dst;
-                for (src = oldNormalizedZeroIndex, dst =  newNormalizedZeroIndex;
-                     src < oldNormalizedZeroIndex + lengthToCopy;
-                     src++, dst++) {
-                    inactiveCounts.lazySet(dst, oldInactiveCounts.get(src));
-                }
-                for (dst = oldNormalizedZeroIndex; dst < newNormalizedZeroIndex; dst++) {
-                    inactiveCounts.lazySet(dst, 0);
-                }
-            }
+            copyInactiveCountsContentsOnResize(oldInactiveCounts, countsDelta);
 
             // switch active and inactive:
             AtomicLongArrayWithNormalizingOffset tmp = activeCounts;
@@ -382,29 +375,10 @@ public class ConcurrentHistogram extends Histogram {
 
             // Resize the newly inactiveCounts:
             oldInactiveCounts = inactiveCounts;
-            inactiveCounts =
-                    new AtomicLongArrayWithNormalizingOffset(
-                            newArrayLength,
-                            inactiveCounts.getNormalizingIndexOffset()
-                    );
+            inactiveCounts = newInactiveCounts2;
+
             // Copy inactive contents to newly sized inactiveCounts:
-            for (int i = 0 ; i < oldInactiveCounts.length(); i++) {
-                inactiveCounts.lazySet(i, oldInactiveCounts.get(i));
-            }
-            if (oldNormalizedZeroIndex != 0) {
-                // We need to shift the stuff from the zero index and up to the end of the array:
-                int newNormalizedZeroIndex = oldNormalizedZeroIndex + countsDelta;
-                int lengthToCopy = (newArrayLength - countsDelta) - oldNormalizedZeroIndex;
-                int src, dst;
-                for (src = oldNormalizedZeroIndex, dst =  newNormalizedZeroIndex;
-                     src < oldNormalizedZeroIndex + lengthToCopy;
-                     src++, dst++) {
-                    inactiveCounts.lazySet(dst, oldInactiveCounts.get(src));
-                }
-                for (dst = oldNormalizedZeroIndex; dst < newNormalizedZeroIndex; dst++) {
-                    inactiveCounts.lazySet(dst, 0);
-                }
-            }
+            copyInactiveCountsContentsOnResize(oldInactiveCounts, countsDelta);
 
             // switch active and inactive again:
             tmp = activeCounts;
@@ -427,6 +401,34 @@ public class ConcurrentHistogram extends Histogram {
         }
     }
 
+    private void copyInactiveCountsContentsOnResize(
+            AtomicLongArrayWithNormalizingOffset oldInactiveCounts, int countsDelta) {
+        int oldNormalizedZeroIndex =
+                normalizeIndex(0,
+                        oldInactiveCounts.getNormalizingIndexOffset(),
+                        oldInactiveCounts.length());
+
+        // Copy old inactive contents to (current) newly sized inactiveCounts:
+        for (int i = 0; i < oldInactiveCounts.length(); i++) {
+            inactiveCounts.lazySet(i, oldInactiveCounts.get(i));
+        }
+        if (oldNormalizedZeroIndex != 0) {
+            // We need to shift the stuff from the zero index and up to the end of the array:
+            int newNormalizedZeroIndex = oldNormalizedZeroIndex + countsDelta;
+            int lengthToCopy = (inactiveCounts.length() - countsDelta) - oldNormalizedZeroIndex;
+            int src, dst;
+            for (src = oldNormalizedZeroIndex, dst = newNormalizedZeroIndex;
+                 src < oldNormalizedZeroIndex + lengthToCopy;
+                 src++, dst++) {
+                inactiveCounts.lazySet(dst, oldInactiveCounts.get(src));
+            }
+            for (dst = oldNormalizedZeroIndex; dst < newNormalizedZeroIndex; dst++) {
+                inactiveCounts.lazySet(dst, 0);
+            }
+        }
+    }
+
+
     @Override
     public void setAutoResize(final boolean autoResize) {
         this.autoResize = true;


=====================================
src/main/java/org/HdrHistogram/DoubleHistogram.java
=====================================
@@ -50,7 +50,7 @@ import java.util.zip.Deflater;
  * <p>
  * See package description for {@link org.HdrHistogram} for details.
  */
-public class DoubleHistogram extends EncodableHistogram implements Serializable {
+public class DoubleHistogram extends EncodableHistogram implements DoubleValueRecorder, Serializable {
     private static final double highestAllowedValueEver; // A value that will keep us from multiplying into infinity.
 
     private long configuredHighestToLowestValueRatio;
@@ -287,8 +287,9 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
      * Record a value in the histogram
      *
      * @param value The value to be recorded
-     * @throws ArrayIndexOutOfBoundsException (may throw) if value is cannot be covered by the histogram's range
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
      */
+    @Override
     public void recordValue(final double value) throws ArrayIndexOutOfBoundsException {
         recordSingleValue(value);
     }
@@ -298,8 +299,9 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
      *
      * @param value The value to be recorded
      * @param count The number of occurrences of this value to record
-     * @throws ArrayIndexOutOfBoundsException (may throw) if value is cannot be covered by the histogram's range
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
      */
+    @Override
     public void recordValueWithCount(final double value, final long count) throws ArrayIndexOutOfBoundsException {
         recordCountAtValue(count, value);
     }
@@ -323,8 +325,9 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
      * @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
      *                                           auto-generated value records as appropriate if value is larger
      *                                           than expectedIntervalBetweenValueSamples
-     * @throws ArrayIndexOutOfBoundsException (may throw) if value is cannot be covered by the histogram's range
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
      */
+    @Override
     public void recordValueWithExpectedInterval(final double value, final double expectedIntervalBetweenValueSamples)
             throws ArrayIndexOutOfBoundsException {
         recordValueWithCountAndExpectedInterval(value, 1, expectedIntervalBetweenValueSamples);
@@ -573,8 +576,11 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
     /**
      * Reset the contents and stats of this histogram
      */
+    @Override
     public void reset() {
-        integerValuesHistogram.clearCounts();
+        integerValuesHistogram.reset();
+        double initialLowestValueInAutoRange = Math.pow(2.0, 800);
+        init(configuredHighestToLowestValueRatio, initialLowestValueInAutoRange, integerValuesHistogram);
     }
 
     //


=====================================
src/main/java/org/HdrHistogram/DoubleRecorder.java
=====================================
@@ -22,10 +22,23 @@ import java.util.concurrent.atomic.AtomicLong;
  * {@link DoubleRecorder#recordValueWithExpectedInterval} calls.
  * Recording calls are wait-free on architectures that support atomic increment operations, and
  * are lock-free on architectures that do not.
- *
+ * <p>
+ * A common pattern for using a {@link DoubleRecorder} looks like this:
+ * <br><pre><code>
+ * DoubleRecorder recorder = new DoubleRecorder(2); // Two decimal point accuracy
+ * DoubleHistogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ *   ...
+ *   // Get interval histogram, recycling previous interval histogram:
+ *   intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ *   histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ *   ...
+ * [end of loop construct]
+ * </code></pre>
  */
 
-public class DoubleRecorder {
+public class DoubleRecorder implements DoubleValueRecorder {
     private static AtomicLong instanceIdSequencer = new AtomicLong(1);
     private final long instanceId = instanceIdSequencer.getAndIncrement();
 
@@ -70,6 +83,7 @@ public class DoubleRecorder {
      * @param value the value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValue(final double value) {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
         try {
@@ -86,6 +100,7 @@ public class DoubleRecorder {
      * @param count The number of occurrences of this value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithCount(final double value, final long count) throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
         try {
@@ -111,6 +126,7 @@ public class DoubleRecorder {
      *                                           than expectedIntervalBetweenValueSamples
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithExpectedInterval(final double value, final double expectedIntervalBetweenValueSamples)
             throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
@@ -157,13 +173,49 @@ public class DoubleRecorder {
      * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
      * counts for the next interval
      *
-     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     * @param histogramToRecycle a previously returned interval histogram (from this instance of
+     *                           {@link DoubleRecorder}) that may be recycled to avoid allocation and
      *                           copy operations.
      * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
      */
     public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle) {
+        return getIntervalHistogram(histogramToRecycle, true);
+    }
+
+    /**
+     * Get an interval histogram, which will include a stable, consistent view of all value counts
+     * accumulated since the last interval histogram was taken.
+     * <p>
+     * {@link DoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)}
+     * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+     * and content copying operations, and is therefore significantly more efficient for repeated use than
+     * {@link DoubleRecorder#getIntervalHistogram()} and
+     * {@link DoubleRecorder#getIntervalHistogramInto getIntervalHistogramInto()}. The provided
+     * {@code histogramToRecycle} must
+     * be either be null or an interval histogram returned by a previous call to
+     * {@link DoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} or
+     * {@link DoubleRecorder#getIntervalHistogram()}.
+     * <p>
+     * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+     * the same interval histogram instance is recycled more than once, behavior is undefined.
+     * <p>
+     * Calling {@link DoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+     * counts for the next interval
+     *
+     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     *                           copy operations.
+     * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+     *                                 instance of {@link DoubleRecorder}. If false, will allow recycling histograms
+     *                                 previously returned by other instances of {@link DoubleRecorder}.
+     * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+     */
+    public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle,
+                                                             boolean enforeContainingInstance) {
         // Verify that replacement histogram can validly be used as an inactive histogram replacement:
-        validateFitAsReplacementHistogram(histogramToRecycle);
+        validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
         inactiveHistogram = (InternalConcurrentDoubleHistogram) histogramToRecycle;
         performIntervalSample();
         DoubleHistogram sampledHistogram = inactiveHistogram;
@@ -188,6 +240,7 @@ public class DoubleRecorder {
     /**
      * Reset any value counts accumulated thus far.
      */
+    @Override
     public synchronized void reset() {
         // the currently inactive histogram is reset each time we flip. So flipping twice resets both:
         performIntervalSample();
@@ -245,15 +298,17 @@ public class DoubleRecorder {
         }
     }
 
-    void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram) {
+    private void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram,
+                                                   boolean enforeContainingInstance) {
         boolean bad = true;
         if (replacementHistogram == null) {
             bad = false;
         } else if ((replacementHistogram instanceof InternalConcurrentDoubleHistogram)
                 &&
-                (((InternalConcurrentDoubleHistogram) replacementHistogram).containingInstanceId ==
-                        activeHistogram.containingInstanceId)
-                ) {
+                ((!enforeContainingInstance) ||
+                        (((InternalConcurrentDoubleHistogram) replacementHistogram).containingInstanceId ==
+                                activeHistogram.containingInstanceId)
+                )) {
             bad = false;
         }
 


=====================================
src/main/java/org/HdrHistogram/DoubleValueRecorder.java
=====================================
@@ -0,0 +1,47 @@
+package org.HdrHistogram;
+
+public interface DoubleValueRecorder {
+
+    /**
+     * Record a value
+     *
+     * @param value The value to be recorded
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+     */
+    void recordValue(double value) throws ArrayIndexOutOfBoundsException;
+
+    /**
+     * Record a value (adding to the value's current count)
+     *
+     * @param value The value to be recorded
+     * @param count The number of occurrences of this value to record
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+     */
+    void recordValueWithCount(double value, long count) throws ArrayIndexOutOfBoundsException;
+
+    /**
+     * Record a value.
+     * <p>
+     * To compensate for the loss of sampled values when a recorded value is larger than the expected
+     * interval between value samples, will auto-generate an additional series of decreasingly-smaller
+     * (down to the expectedIntervalBetweenValueSamples) value records.
+     * <p>
+     * Note: This is a at-recording correction method, as opposed to the post-recording correction method provided
+     * by {@link DoubleHistogram#copyCorrectedForCoordinatedOmission(double)}.
+     * The two methods are mutually exclusive, and only one of the two should be be used on a given data set to correct
+     * for the same coordinated omission issue.
+     *
+     * @param value                               The value to record
+     * @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
+     *                                            auto-generated value records as appropriate if value is larger
+     *                                            than expectedIntervalBetweenValueSamples
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+     */
+    void recordValueWithExpectedInterval(double value, double expectedIntervalBetweenValueSamples)
+            throws ArrayIndexOutOfBoundsException;
+
+    /**
+     * Reset the contents and collected stats
+     */
+    void reset();
+}


=====================================
src/main/java/org/HdrHistogram/Histogram.java
=====================================
@@ -87,6 +87,11 @@ public class Histogram extends AbstractHistogram {
         this.normalizingIndexOffset = normalizingIndexOffset;
     }
 
+    @Override
+    void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+        nonConcurrentSetIntegerToDoubleValueConversionRatio(integerToDoubleValueConversionRatio);
+    }
+
     @Override
     void shiftNormalizingIndexByOffset(int offsetToAdd,
                                        boolean lowestHalfBucketPopulated,


=====================================
src/main/java/org/HdrHistogram/HistogramLogProcessor.java
=====================================
@@ -51,36 +51,37 @@ import java.util.*;
  */
 public class HistogramLogProcessor extends Thread {
 
-    public static final String versionString = "Histogram Log Processor version " + Version.version;
+    static final String versionString = "Histogram Log Processor version " + Version.version;
 
     private final HistogramLogProcessorConfiguration config;
 
     private HistogramLogReader logReader;
 
     private static class HistogramLogProcessorConfiguration {
-        public boolean verbose = false;
-        public String outputFileName = null;
-        public String inputFileName = null;
-        public String tag = null;
+        boolean verbose = false;
+        String outputFileName = null;
+        String inputFileName = null;
+        String tag = null;
 
-        public double rangeStartTimeSec = 0.0;
-        public double rangeEndTimeSec = Double.MAX_VALUE;
+        double rangeStartTimeSec = 0.0;
+        double rangeEndTimeSec = Double.MAX_VALUE;
 
-        public boolean logFormatCsv = false;
-        public boolean listTags = false;
-        public boolean allTags = false;
+        boolean logFormatCsv = false;
+        boolean listTags = false;
+        boolean allTags = false;
 
-        public boolean movingWindow = false;
-        public double movingWindowPercentileToReport = 99.0;
-        public long movingWindowLengthInMsec = 60000; // 1 minute
+        boolean movingWindow = false;
+        double movingWindowPercentileToReport = 99.0;
+        long movingWindowLengthInMsec = 60000; // 1 minute
 
-        public int percentilesOutputTicksPerHalf = 5;
-        public Double outputValueUnitRatio = 1000000.0; // default to msec units for output.
+        int percentilesOutputTicksPerHalf = 5;
+        Double outputValueUnitRatio = 1000000.0; // default to msec units for output.
 
-        public boolean error = false;
-        public String errorMessage = "";
+        double expectedIntervalForCoordinatedOmissionCorrection = 0.0;
 
-        public HistogramLogProcessorConfiguration(final String[] args) {
+        String errorMessage = "";
+
+        HistogramLogProcessorConfiguration(final String[] args) {
             boolean askedForHelp= false;
             try {
                 for (int i = 0; i < args.length; ++i) {
@@ -93,25 +94,28 @@ public class HistogramLogProcessor extends Thread {
                     } else if (args[i].equals("-alltags")) {
                         allTags = true;
                     } else if (args[i].equals("-i")) {
-                        inputFileName = args[++i];
+                        inputFileName = args[++i];              // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-tag")) {
-                        tag = args[++i];
+                        tag = args[++i];                        // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-mwp")) {
-                        movingWindowPercentileToReport = Double.parseDouble(args[++i]);
+                        movingWindowPercentileToReport = Double.parseDouble(args[++i]); // lgtm [java/index-out-of-bounds]
                         movingWindow = true;
                     } else if (args[i].equals("-mwpl")) {
-                        movingWindowLengthInMsec = Long.parseLong(args[++i]);
+                        movingWindowLengthInMsec = Long.parseLong(args[++i]);   // lgtm [java/index-out-of-bounds]
                         movingWindow = true;
                     } else if (args[i].equals("-start")) {
-                        rangeStartTimeSec = Double.parseDouble(args[++i]);
+                        rangeStartTimeSec = Double.parseDouble(args[++i]);      // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-end")) {
-                        rangeEndTimeSec = Double.parseDouble(args[++i]);
+                        rangeEndTimeSec = Double.parseDouble(args[++i]);        // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-o")) {
-                        outputFileName = args[++i];
+                        outputFileName = args[++i];                             // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-percentilesOutputTicksPerHalf")) {
-                        percentilesOutputTicksPerHalf = Integer.parseInt(args[++i]);
+                        percentilesOutputTicksPerHalf = Integer.parseInt(args[++i]);    // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-outputValueUnitRatio")) {
-                        outputValueUnitRatio = Double.parseDouble(args[++i]);
+                        outputValueUnitRatio = Double.parseDouble(args[++i]);   // lgtm [java/index-out-of-bounds]
+                    } else if (args[i].equals("-correctLogWithKnownCoordinatedOmission")) {
+                        expectedIntervalForCoordinatedOmissionCorrection =
+                                Double.parseDouble(args[++i]);  // lgtm [java/index-out-of-bounds]
                     } else if (args[i].equals("-h")) {
                         askedForHelp = true;
                         throw new Exception("Help: " + args[i]);
@@ -119,9 +123,7 @@ public class HistogramLogProcessor extends Thread {
                         throw new Exception("Invalid args: " + args[i]);
                     }
                 }
-
             } catch (Exception e) {
-                error = true;
                 errorMessage = "Error: " + versionString + " launched with the following args:\n";
 
                 for (String arg : args) {
@@ -135,22 +137,28 @@ public class HistogramLogProcessor extends Thread {
                 final String validArgs =
                         "\"[-csv] [-v] [-i inputFileName] [-o outputFileName] [-tag tag] " +
                                 "[-start rangeStartTimeSec] [-end rangeEndTimeSec] " +
-                                "[-outputValueUnitRatio r] [-listtags]";
+                                "[-outputValueUnitRatio r] [-correctLogWithKnownCoordinatedOmission i] [-listtags]";
 
                 System.err.println("valid arguments = " + validArgs);
 
                 System.err.println(
-                        " [-h]                        help\n" +
-                                " [-v]                        Provide verbose error output\n" +
-                                " [-csv]                      Use CSV format for output log files\n" +
-                                " [-i logFileName]            File name of Histogram Log to process (default is standard input)\n" +
-                                " [-o outputFileName]         File name to output to (default is standard output)\n" +
-                                " [-tag tag]                  The tag (default no tag) of the histogram lines to be processed\n" +
-                                " [-start rangeStartTimeSec]  The start time for the range in the file, in seconds (default 0.0)\n" +
-                                " [-end rangeEndTimeSec]      The end time for the range in the file, in seconds (default is infinite)\n" +
-                                " [-outputValueUnitRatio r]   The scaling factor by which to divide histogram recorded values units\n" +
-                                "                             in output. [default = 1000000.0 (1 msec in nsec)]\n" +
-                                " [-listtags]                 list all tags found on histogram lines the input file."
+                            " [-h]                                         help\n" +
+                            " [-v]                                         Provide verbose error output\n" +
+                            " [-csv]                                       Use CSV format for output log files\n" +
+                            " [-i logFileName]                             File name of Histogram Log to process (default is standard input)\n" +
+                            " [-o outputFileName]                          File name to output to (default is standard output)\n" +
+                            " [-tag tag]                                   The tag (default no tag) of the histogram lines to be processed\n" +
+                            " [-start rangeStartTimeSec]                   The start time for the range in the file, in seconds (default 0.0)\n" +
+                            " [-end rangeEndTimeSec]                       The end time for the range in the file, in seconds (default is infinite)\n" +
+                            " [-outputValueUnitRatio r]                    The scaling factor by which to divide histogram recorded values units\n" +
+                            "                                              in output. [default = 1000000.0 (1 msec in nsec)]\n" +
+                            " [-correctLogWithKnownCoordinatedOmission i]  When the supplied expected interval i is than 0, performs coordinated\n" +
+                            "                                              omission corection on the input log's interval histograms by adding\n" +
+                            "                                              missing values as appropriate based on the supplied expected interval\n" +
+                            "                                              value i (in wahtever units the log histograms were recorded with). This\n" +
+                            "                                              feature should only be used when the input log is known to have been\n" +
+                            "                                              recorded with coordinated ommisions, and when an expected interval is known.\n" +
+                            " [-listtags]                                  list all tags found on histogram lines the input file."
                 );
                 System.exit(1);
             }
@@ -172,12 +180,33 @@ public class HistogramLogProcessor extends Thread {
                 startTime, (new Date((long) (startTime * 1000))).toString());
     }
 
-    int lineNumber = 0;
+    EncodableHistogram copyCorrectedForCoordinatedOmission(final EncodableHistogram inputHistogram) {
+        EncodableHistogram histogram = inputHistogram;
+        if (histogram instanceof DoubleHistogram) {
+            if (config.expectedIntervalForCoordinatedOmissionCorrection > 0.0) {
+                histogram = ((DoubleHistogram) histogram).copyCorrectedForCoordinatedOmission(
+                        config.expectedIntervalForCoordinatedOmissionCorrection);
+            }
+        } else if (histogram instanceof Histogram) {
+            long expectedInterval = (long) config.expectedIntervalForCoordinatedOmissionCorrection;
+            if (expectedInterval > 0) {
+                histogram = ((Histogram) histogram).copyCorrectedForCoordinatedOmission(expectedInterval);
+            }
+        }
+        return histogram;
+    }
+
+    private int lineNumber = 0;
 
     private EncodableHistogram getIntervalHistogram() {
         EncodableHistogram histogram = null;
         try {
             histogram = logReader.nextIntervalHistogram(config.rangeStartTimeSec, config.rangeEndTimeSec);
+            if (config.expectedIntervalForCoordinatedOmissionCorrection > 0.0) {
+                // Apply Coordinated Omission correction to log histograms when arguments indicate that
+                // such correction is desired, and an expected interval is provided.
+                histogram = copyCorrectedForCoordinatedOmission(histogram);
+            }
         } catch (RuntimeException ex) {
             System.err.println("Log file parsing error at line number " + lineNumber +
                     ": line appears to be malformed.");
@@ -213,14 +242,11 @@ public class HistogramLogProcessor extends Thread {
         PrintStream timeIntervalLog = null;
         PrintStream movingWindowLog = null;
         PrintStream histogramPercentileLog = System.out;
-        Double firstStartTime = 0.0;
+        double firstStartTime = 0.0;
         boolean timeIntervalLogLegendWritten = false;
         boolean movingWindowLogLegendWritten = false;
 
-//        EncodableHistogram[] movingWindow = new EncodableHistogram[config.movingWindowIntervalCount];
-        EncodableHistogram movingWindowSumHistogram = null;
         Queue<EncodableHistogram> movingWindowQueue = new LinkedList<EncodableHistogram>();
-        int movingWindowIndex = 0;
 
         if (config.listTags) {
             Set<String> tags = new TreeSet<String>();
@@ -283,35 +309,36 @@ public class HistogramLogProcessor extends Thread {
             }
 
             EncodableHistogram intervalHistogram = getIntervalHistogram(config.tag);
+            boolean logUsesDoubleHistograms = (intervalHistogram instanceof DoubleHistogram);
 
-            Histogram accumulatedRegularHistogram = null;
-            DoubleHistogram accumulatedDoubleHistogram = null;
+            Histogram accumulatedRegularHistogram = logUsesDoubleHistograms ?
+                    new Histogram(3) :
+                    ((Histogram) intervalHistogram).copy();
+            accumulatedRegularHistogram.reset();
+            accumulatedRegularHistogram.setAutoResize(true);
+
+            DoubleHistogram accumulatedDoubleHistogram = logUsesDoubleHistograms ?
+                    ((DoubleHistogram) intervalHistogram).copy() :
+                    new DoubleHistogram(3);
+            accumulatedDoubleHistogram.reset();
+            accumulatedDoubleHistogram.setAutoResize(true);
+
+
+            EncodableHistogram movingWindowSumHistogram = logUsesDoubleHistograms ?
+                    new DoubleHistogram(3) :
+                    new Histogram(3);
 
-            if (intervalHistogram != null) {
-                // Shape the accumulated histogram like the histograms in the log file (but clear their contents):
-                if (intervalHistogram instanceof DoubleHistogram) {
-                    accumulatedDoubleHistogram = ((DoubleHistogram) intervalHistogram).copy();
-                    accumulatedDoubleHistogram.reset();
-                    accumulatedDoubleHistogram.setAutoResize(true);
-                    movingWindowSumHistogram = new DoubleHistogram(3);
-                } else {
-                    accumulatedRegularHistogram = ((Histogram) intervalHistogram).copy();
-                    accumulatedRegularHistogram.reset();
-                    accumulatedRegularHistogram.setAutoResize(true);
-                    movingWindowSumHistogram = new Histogram(3);
-                }
-            }
 
             while (intervalHistogram != null) {
 
                 // handle accumulated histogram:
                 if (intervalHistogram instanceof DoubleHistogram) {
-                    if (accumulatedDoubleHistogram == null) {
+                    if (!logUsesDoubleHistograms) {
                         throw new IllegalStateException("Encountered a DoubleHistogram line in a log of Histograms.");
                     }
                     accumulatedDoubleHistogram.add((DoubleHistogram) intervalHistogram);
                 } else {
-                    if (accumulatedRegularHistogram == null) {
+                    if (logUsesDoubleHistograms) {
                         throw new IllegalStateException("Encountered a Histogram line in a log of DoubleHistograms.");
                     }
                     accumulatedRegularHistogram.add((Histogram) intervalHistogram);
@@ -366,7 +393,7 @@ public class HistogramLogProcessor extends Thread {
                         }
                     }
 
-                    if (intervalHistogram instanceof DoubleHistogram) {
+                    if (logUsesDoubleHistograms) {
                         timeIntervalLog.format(Locale.US, logFormat,
                                 ((intervalHistogram.getEndTimeStamp() / 1000.0) - logReader.getStartTimeSec()),
                                 // values recorded during the last reporting interval
@@ -420,7 +447,7 @@ public class HistogramLogProcessor extends Thread {
                                 ((DoubleHistogram) movingWindowSumHistogram).getTotalCount(),
                                 ((DoubleHistogram) movingWindowSumHistogram).getValueAtPercentile(config.movingWindowPercentileToReport) / config.outputValueUnitRatio,
                                 ((DoubleHistogram) movingWindowSumHistogram).getMaxValue() / config.outputValueUnitRatio
-                                );
+                        );
                     } else {
                         movingWindowLog.format(Locale.US, movingWindowLogFormat,
                                 ((intervalHistogram.getEndTimeStamp() / 1000.0) - logReader.getStartTimeSec()),
@@ -428,7 +455,7 @@ public class HistogramLogProcessor extends Thread {
                                 ((Histogram) movingWindowSumHistogram).getTotalCount(),
                                 ((Histogram) movingWindowSumHistogram).getValueAtPercentile(config.movingWindowPercentileToReport) / config.outputValueUnitRatio,
                                 ((Histogram) movingWindowSumHistogram).getMaxValue() / config.outputValueUnitRatio
-                                );
+                        );
                     }
 
                 }
@@ -436,21 +463,21 @@ public class HistogramLogProcessor extends Thread {
                 intervalHistogram = getIntervalHistogram(config.tag);
             }
 
-            if (accumulatedDoubleHistogram != null) {
+            if (logUsesDoubleHistograms) {
                 accumulatedDoubleHistogram.outputPercentileDistribution(histogramPercentileLog,
                         config.percentilesOutputTicksPerHalf, config.outputValueUnitRatio, config.logFormatCsv);
             } else {
-                if (accumulatedRegularHistogram == null) {
-                    // If there were no histograms in the log file, we still need an empty histogram for the
-                    // one line output (shape/range doesn't matter because it is empty):
-                    accumulatedRegularHistogram = new Histogram(1000000L, 2);
-                }
                 accumulatedRegularHistogram.outputPercentileDistribution(histogramPercentileLog,
                         config.percentilesOutputTicksPerHalf, config.outputValueUnitRatio, config.logFormatCsv);
             }
         } finally {
-            if (config.outputFileName != null) {
+            if (timeIntervalLog != null) {
                 timeIntervalLog.close();
+            }
+            if (movingWindowLog != null) {
+                movingWindowLog.close();
+            }
+            if (histogramPercentileLog != System.out) {
                 histogramPercentileLog.close();
             }
         }
@@ -460,15 +487,22 @@ public class HistogramLogProcessor extends Thread {
      * Construct a {@link org.HdrHistogram.HistogramLogProcessor} with the given arguments
      * (provided in command line style).
      * <pre>
-     * [-h]                        help
-     * [-csv]                      Use CSV format for output log files
-     * [-i logFileName]            File name of Histogram Log to process (default is standard input)
-     * [-o outputFileName]         File name to output to (default is standard output)
-     *                             (will replace occurrences of %pid and %date with appropriate information)
-     * [-start rangeStartTimeSec]  The start time for the range in the file, in seconds (default 0.0)
-     * [-end rangeEndTimeSec]      The end time for the range in the file, in seconds (default is infinite)
-     * [-outputValueUnitRatio r]   The scaling factor by which to divide histogram recorded values units
-     *                             in output. [default = 1000000.0 (1 msec in nsec)]"
+     * [-h]                                                        help
+     * [-csv]                                                      Use CSV format for output log files
+     * [-i logFileName]                                            File name of Histogram Log to process (default is standard input)
+     * [-o outputFileName]                                         File name to output to (default is standard output)
+     *                                                             (will replace occurrences of %pid and %date with appropriate information)
+     * [-tag tag]                                                  The tag (default no tag) of the histogram lines to be processed\n
+     * [-start rangeStartTimeSec]                                  The start time for the range in the file, in seconds (default 0.0)
+     * [-end rangeEndTimeSec]                                      The end time for the range in the file, in seconds (default is infinite)
+     * [-correctLogWithKnownCoordinatedOmission expectedInterval]  When the supplied expected interval i is than 0, performs coordinated
+     *                                                             omission corection on the input log's interval histograms by adding
+     *                                                             missing values as appropriate based on the supplied expected interval
+     *                                                             value i (in wahtever units the log histograms were recorded with). This
+     *                                                             feature should only be used when the input log is known to have been
+     *                                                             recorded with coordinated ommisions, and when an expected interval is known.
+     * [-outputValueUnitRatio r]                                   The scaling factor by which to divide histogram recorded values units
+     *                                                             in output. [default = 1000000.0 (1 msec in nsec)]"
      * </pre>
      * @param args command line arguments
      * @throws FileNotFoundException if specified input file is not found


=====================================
src/main/java/org/HdrHistogram/HistogramLogReader.java
=====================================
@@ -7,12 +7,7 @@
 
 package org.HdrHistogram;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Locale;
-import java.util.Scanner;
+import java.io.*;
 import java.util.zip.DataFormatException;
 
 /**
@@ -56,13 +51,117 @@ import java.util.zip.DataFormatException;
  * that may be added to timestamps in the file to determine an absolute
  * timestamp (e.g. since the epoch) for each interval.
  */
-public class HistogramLogReader {
+public class HistogramLogReader implements Closeable {
+
+    private final HistogramLogScanner scanner;
+    private final HistogramLogScanner.EventHandler handler = new HistogramLogScanner.EventHandler()
+    {
+        @Override
+        public boolean onComment(String comment)
+        {
+            return false;
+        }
+
+        @Override
+        public boolean onBaseTime(double secondsSinceEpoch)
+        {
+            baseTimeSec = secondsSinceEpoch; // base time represented as seconds since epoch
+            observedBaseTime = true;
+            return false;
+        }
+
+        @Override
+        public boolean onStartTime(double secondsSinceEpoch)
+        {
+            startTimeSec = secondsSinceEpoch; // start time represented as seconds since epoch
+            observedStartTime = true;
+            return false;
+        }
+
+        @Override
+        public boolean onHistogram(String tag, double timestamp, double length,
+            HistogramLogScanner.EncodableHistogramSupplier lazyReader) {
+            final double logTimeStampInSec = timestamp; // Timestamp is expected to be in seconds
+
+            if (!observedStartTime) {
+                // No explicit start time noted. Use 1st observed time:
+                startTimeSec = logTimeStampInSec;
+                observedStartTime = true;
+            }
+            if (!observedBaseTime) {
+                // No explicit base time noted. Deduce from 1st observed time (compared to start time):
+                if (logTimeStampInSec < startTimeSec - (365 * 24 * 3600.0)) {
+                    // Criteria Note: if log timestamp is more than a year in the past (compared to
+                    // StartTime), we assume that timestamps in the log are not absolute
+                    baseTimeSec = startTimeSec;
+                } else {
+                    // Timestamps are absolute
+                    baseTimeSec = 0.0;
+                }
+                observedBaseTime = true;
+            }
+
+            final double absoluteStartTimeStampSec = logTimeStampInSec + baseTimeSec;
+            final double offsetStartTimeStampSec = absoluteStartTimeStampSec - startTimeSec;
+
+            final double intervalLengthSec = length; // Timestamp length is expect to be in seconds
+            final double absoluteEndTimeStampSec = absoluteStartTimeStampSec + intervalLengthSec;
+
+            final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec;
 
-    private final Scanner scanner;
+            if (startTimeStampToCheckRangeOn < rangeStartTimeSec) {
+                // keep on trucking
+                return false;
+            }
+
+            if (startTimeStampToCheckRangeOn > rangeEndTimeSec) {
+                // after limit we stop on each line
+                return true;
+            }
+            EncodableHistogram histogram;
+            try
+            {
+                histogram = lazyReader.read();
+            }
+            catch (DataFormatException e)
+            {
+                // stop after exception
+                return true;
+            }
+
+            histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0));
+            histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0));
+            histogram.setTag(tag);
+            nextHistogram = histogram;
+            return true;
+        }
+
+        @Override
+        public boolean onException(Throwable t) {
+            
+            // We ignore NoSuchElementException, but stop processing.
+            // Next call to nextIntervalHistogram may return null.
+            if (t instanceof java.util.NoSuchElementException){
+                return true;
+            }
+            // rethrow
+            if (t instanceof RuntimeException)
+                throw (RuntimeException)t;
+            else
+                throw new RuntimeException(t);
+        }
+    };
+    
     private double startTimeSec = 0.0;
     private boolean observedStartTime = false;
     private double baseTimeSec = 0.0;
     private boolean observedBaseTime = false;
+    
+    // scanner handling state
+    private boolean absolute;
+    private double rangeStartTimeSec;
+    private double rangeEndTimeSec;
+    private EncodableHistogram nextHistogram;
 
     /**
      * Constructs a new HistogramLogReader that produces intervals read from the specified file name.
@@ -70,8 +169,7 @@ public class HistogramLogReader {
      * @throws java.io.FileNotFoundException when unable to find inputFileName
      */
     public HistogramLogReader(final String inputFileName) throws FileNotFoundException {
-        scanner = new Scanner(new File(inputFileName));
-        initScanner();
+        scanner = new HistogramLogScanner(new File(inputFileName));
     }
 
     /**
@@ -79,8 +177,7 @@ public class HistogramLogReader {
      * @param inputStream The InputStream to read from
      */
     public HistogramLogReader(final InputStream inputStream) {
-        scanner = new Scanner(inputStream);
-        initScanner();
+        scanner = new HistogramLogScanner(inputStream);
     }
 
     /**
@@ -89,14 +186,7 @@ public class HistogramLogReader {
      * @throws java.io.FileNotFoundException when unable to find inputFile
      */
     public HistogramLogReader(final File inputFile) throws FileNotFoundException {
-        scanner = new Scanner(inputFile);
-        initScanner();
-    }
-
-
-    private void initScanner() {
-        scanner.useLocale(Locale.US);
-        scanner.useDelimiter("[, \\r\\n]");
+        scanner = new HistogramLogScanner(inputFile);
     }
 
     /**
@@ -195,105 +285,26 @@ public class HistogramLogReader {
 
     private EncodableHistogram nextIntervalHistogram(final double rangeStartTimeSec,
                                             final double rangeEndTimeSec, boolean absolute) {
-        while (scanner.hasNextLine()) {
-            try {
-                if (scanner.hasNext("\\#.*")) {
-                    // comment line.
-                    // Look for explicit start time or base time notes in comments:
-                    if (scanner.hasNext("#\\[StartTime:")) {
-                        scanner.next("#\\[StartTime:");
-                        if (scanner.hasNextDouble()) {
-                            startTimeSec = scanner.nextDouble(); // start time represented as seconds since epoch
-                            observedStartTime = true;
-                        }
-                    } else if (scanner.hasNext("#\\[BaseTime:")) {
-                        scanner.next("#\\[BaseTime:");
-                        if (scanner.hasNextDouble()) {
-                            baseTimeSec = scanner.nextDouble(); // base time represented as seconds since epoch
-                            observedBaseTime = true;
-                        }
-                    }
-                    continue;
-                }
-
-                if (scanner.hasNext("\"StartTimestamp\".*")) {
-                    // Legend line
-                    continue;
-                }
-
-                String tagString = null;
-                if (scanner.hasNext("Tag\\=.*")) {
-                    tagString = scanner.next("Tag\\=.*").substring(4);
-                }
-
-                // Decode: startTimestamp, intervalLength, maxTime, histogramPayload
-
-                final double logTimeStampInSec = scanner.nextDouble(); // Timestamp is expected to be in seconds
-
-                if (!observedStartTime) {
-                    // No explicit start time noted. Use 1st observed time:
-                    startTimeSec = logTimeStampInSec;
-                    observedStartTime = true;
-                }
-                if (!observedBaseTime) {
-                    // No explicit base time noted. Deduce from 1st observed time (compared to start time):
-                    if (logTimeStampInSec < startTimeSec - (365 * 24 * 3600.0)) {
-                        // Criteria Note: if log timestamp is more than a year in the past (compared to
-                        // StartTime), we assume that timestamps in the log are not absolute
-                        baseTimeSec = startTimeSec;
-                    } else {
-                        // Timestamps are absolute
-                        baseTimeSec = 0.0;
-                    }
-                    observedBaseTime = true;
-                }
-
-                final double absoluteStartTimeStampSec = logTimeStampInSec + baseTimeSec;
-                final double offsetStartTimeStampSec = absoluteStartTimeStampSec - startTimeSec;
-
-                final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds
-                final double absoluteEndTimeStampSec = absoluteStartTimeStampSec + intervalLengthSec;
-
-                final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec;
-
-                if (startTimeStampToCheckRangeOn < rangeStartTimeSec) {
-                    continue;
-                }
-
-                if (startTimeStampToCheckRangeOn > rangeEndTimeSec) {
-                    return null;
-                }
-
-                scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram.
-                final String compressedPayloadString = scanner.next();
-                final ByteBuffer buffer = ByteBuffer.wrap(
-                        Base64Helper.parseBase64Binary(compressedPayloadString));
-
-                EncodableHistogram histogram = EncodableHistogram.decodeFromCompressedByteBuffer(buffer, 0);
-
-                histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0));
-                histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0));
-                histogram.setTag(tagString);
-
-                return histogram;
-
-            } catch (java.util.NoSuchElementException ex) {
-                return null;
-            } catch (DataFormatException ex) {
-                return null;
-            } finally {
-                scanner.nextLine(); // Move to next line.
-            }
-        }
-        return null;
+        this.rangeStartTimeSec = rangeStartTimeSec;
+        this.rangeEndTimeSec = rangeEndTimeSec;
+        this.absolute = absolute;
+        scanner.process(handler);
+        EncodableHistogram histogram = this.nextHistogram;
+        nextHistogram = null;
+        return histogram;
     }
 
     /**
-     * Indicates whther or not additional intervals may exist in the log
-     * @return ture if additional intervals may exist in the log
+     * Indicates whether or not additional intervals may exist in the log
+     * @return true if additional intervals may exist in the log
      */
     public boolean hasNext() {
         return scanner.hasNextLine();
     }
 
+    @Override
+    public void close()
+    {
+        scanner.close();
+    }
 }


=====================================
src/main/java/org/HdrHistogram/HistogramLogScanner.java
=====================================
@@ -0,0 +1,198 @@
+/**
+ * Written by Gil Tene of Azul Systems, and released to the public domain,
+ * as explained at http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * @author Gil Tene
+ */
+
+package org.HdrHistogram;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.Scanner;
+import java.util.zip.DataFormatException;
+
+public class HistogramLogScanner implements Closeable {
+
+    // can't use lambdas, and anyway we need to let the handler take the exception
+    public interface EncodableHistogramSupplier
+    {
+        EncodableHistogram read() throws DataFormatException;
+    }
+
+    /**
+     * Handles log events, return true to stop processing.
+     */
+    public interface EventHandler
+    {
+        boolean onComment(String comment);
+        boolean onBaseTime(double secondsSinceEpoch);
+        boolean onStartTime(double secondsSinceEpoch);
+
+        /**
+         *  A lazy reader is provided to allow fast skipping of bulk of work where tag or timestamp are to be used as
+         *  a basis for filtering the {@link EncodableHistogram} anyway. The reader is to be called only once.
+         *  
+         * @param tag histogram tag or null if none exist
+         * @param timestamp logged timestamp
+         * @param length logged interval length
+         * @param lazyReader to be called if the histogram needs to be deserialized, given the tag/timestamp etc.
+         * @return
+         */
+        boolean onHistogram(String tag, double timestamp, double length, EncodableHistogramSupplier lazyReader);
+        boolean onException(Throwable t);
+    }
+    
+    private static class LazyHistogramReader implements EncodableHistogramSupplier {
+
+        private final Scanner scanner;
+        private boolean gotIt = true;
+
+        private LazyHistogramReader(Scanner scanner)
+        {
+            this.scanner = scanner;
+        }
+
+        private void allowGet()
+        {
+            gotIt = false;
+        }
+        
+        @Override
+        public EncodableHistogram read() throws DataFormatException
+        {
+            // prevent double calls to this method
+            if (gotIt)
+                throw new IllegalStateException();
+            gotIt = true;
+            
+            final String compressedPayloadString = scanner.next();
+            final ByteBuffer buffer = ByteBuffer.wrap(Base64Helper.parseBase64Binary(compressedPayloadString));
+
+            EncodableHistogram histogram = EncodableHistogram.decodeFromCompressedByteBuffer(buffer, 0);
+
+            return histogram;       
+        }
+    }
+
+    private final LazyHistogramReader lazyReader;
+    protected final Scanner scanner;
+    
+    /**
+     * Constructs a new HistogramLogReader that produces intervals read from the specified file name.
+     * @param inputFileName The name of the file to read from
+     * @throws java.io.FileNotFoundException when unable to find inputFileName
+     */
+    public HistogramLogScanner(final String inputFileName) throws FileNotFoundException {
+        this(new Scanner(new File(inputFileName)));
+    }
+
+    /**
+     * Constructs a new HistogramLogReader that produces intervals read from the specified InputStream. Note that
+     * log readers constructed through this constructor do not assume ownership of stream and will not close it on
+     * {@link #close()}.
+     * 
+     * @param inputStream The InputStream to read from
+     */
+    public HistogramLogScanner(final InputStream inputStream) {
+        this(new Scanner(inputStream));
+    }
+
+    /**
+     * Constructs a new HistogramLogReader that produces intervals read from the specified file.
+     * @param inputFile The File to read from
+     * @throws java.io.FileNotFoundException when unable to find inputFile
+     */
+    public HistogramLogScanner(final File inputFile) throws FileNotFoundException {
+        this(new Scanner(inputFile));
+    }
+
+    private HistogramLogScanner(Scanner scanner)
+    {
+        this.scanner = scanner;
+        this.lazyReader = new LazyHistogramReader(scanner);
+        initScanner();
+    }
+
+    private void initScanner() {
+        scanner.useLocale(Locale.US);
+        scanner.useDelimiter("[ ,\\r\\n]");
+    }
+
+    /**
+     * Close underlying scanner.
+     */
+    @Override
+    public void close()
+    {
+        scanner.close();
+    }
+
+    public void process(EventHandler handler) {
+        while (scanner.hasNextLine()) {
+            try {
+                if (scanner.hasNext("\\#.*")) {
+                    // comment line.
+                    // Look for explicit start time or base time notes in comments:
+                    if (scanner.hasNext("#\\[StartTime:")) {
+                        scanner.next("#\\[StartTime:");
+                        if (scanner.hasNextDouble()) {
+                            double startTimeSec = scanner.nextDouble(); // start time represented as seconds since epoch
+                            if (handler.onStartTime(startTimeSec)) {
+                                return;
+                            }
+                        }
+                    } else if (scanner.hasNext("#\\[BaseTime:")) {
+                        scanner.next("#\\[BaseTime:");
+                        if (scanner.hasNextDouble()) {
+                            double baseTimeSec = scanner.nextDouble(); // base time represented as seconds since epoch
+                            if (handler.onBaseTime(baseTimeSec))
+                            {
+                                return;
+                            }
+                        }
+                    } else if (handler.onComment(scanner.next("\\#.*"))) {
+                        return;
+                    }
+                    continue;
+                }
+
+                if (scanner.hasNext("\"StartTimestamp\".*")) {
+                    // Legend line
+                    continue;
+                }
+
+                String tagString = null;
+                if (scanner.hasNext("Tag\\=.*")) {
+                    tagString = scanner.next("Tag\\=.*").substring(4);
+                }
+
+                // Decode: startTimestamp, intervalLength, maxTime, histogramPayload
+                final double logTimeStampInSec = scanner.nextDouble(); // Timestamp is expected to be in seconds
+                final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds
+                scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram.
+                
+                lazyReader.allowGet();
+                if (handler.onHistogram(tagString, logTimeStampInSec, intervalLengthSec, lazyReader))
+                    return;
+
+            } catch (Throwable ex) {
+                if (handler.onException(ex))
+                    return;
+            } finally {
+                scanner.nextLine(); // Move to next line.
+            }
+        }
+        return;
+    }
+
+    /**
+     * Indicates whether or not additional intervals may exist in the log
+     * 
+     * @return true if additional intervals may exist in the log
+     */
+    public boolean hasNextLine() {
+        return scanner.hasNextLine();
+    }
+}


=====================================
src/main/java/org/HdrHistogram/IntCountsHistogram.java
=====================================
@@ -81,6 +81,12 @@ public class IntCountsHistogram extends AbstractHistogram {
         this.normalizingIndexOffset = normalizingIndexOffset;
     }
 
+
+    @Override
+    void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+        nonConcurrentSetIntegerToDoubleValueConversionRatio(integerToDoubleValueConversionRatio);
+    }
+
     @Override
     void shiftNormalizingIndexByOffset(int offsetToAdd,
                                        boolean lowestHalfBucketPopulated,


=====================================
src/main/java/org/HdrHistogram/LinearIterator.java
=====================================
@@ -48,11 +48,14 @@ public class LinearIterator extends AbstractHistogramIterator implements Iterato
         if (super.hasNext()) {
             return true;
         }
-        // If the next iterate will not move to the next sub bucket index (which is empty if
+        // If the next iteration will not move to the next sub bucket index (which is empty if
         // if we reached this point), then we are not yet done iterating (we want to iterate
         // until we are no longer on a value that has a count, rather than util we first reach
         // the last value that has a count. The difference is subtle but important)...
-        return (currentStepHighestValueReportingLevel + 1 < nextValueAtIndex);
+        // When this is called, we're about to begin the "next" iteration, so
+        // currentStepHighestValueReportingLevel has already been incremented, and we use it
+        // without incrementing its value.
+        return (currentStepHighestValueReportingLevel < nextValueAtIndex);
     }
 
     @Override


=====================================
src/main/java/org/HdrHistogram/Recorder.java
=====================================
@@ -22,10 +22,24 @@ import java.util.concurrent.atomic.AtomicLong;
  * {@link Recorder#recordValueWithExpectedInterval} calls.
  * Recording calls are wait-free on architectures that support atomic increment operations, and
  * are lock-free on architectures that do not.
+ * <p>
+ * A common pattern for using a {@link Recorder} looks like this:
+ * <br><pre><code>
+ * Recorder recorder = new Recorder(2); // Two decimal point accuracy
+ * Histogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ *   ...
+ *   // Get interval histogram, recycling previous interval histogram:
+ *   intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ *   histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ *   ...
+ * [end of loop construct]
+ * </code></pre>
  *
  */
 
-public class Recorder {
+public class Recorder implements ValueRecorder {
     private static AtomicLong instanceIdSequencer = new AtomicLong(1);
     private final long instanceId = instanceIdSequencer.getAndIncrement();
 
@@ -93,6 +107,7 @@ public class Recorder {
      * @param value the value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValue(final long value) throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
         try {
@@ -109,6 +124,7 @@ public class Recorder {
      * @param count The number of occurrences of this value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithCount(final long value, final long count) throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
         try {
@@ -134,6 +150,7 @@ public class Recorder {
      *                                           than expectedIntervalBetweenValueSamples
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
             throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
@@ -180,13 +197,49 @@ public class Recorder {
      * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
      * counts for the next interval
      *
-     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     * @param histogramToRecycle a previously returned interval histogram (from this instance of
+     *                           {@link Recorder}) that may be recycled to avoid allocation and
      *                           copy operations.
      * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
      */
     public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle) {
+        return getIntervalHistogram(histogramToRecycle, true);
+    }
+
+    /**
+     * Get an interval histogram, which will include a stable, consistent view of all value counts
+     * accumulated since the last interval histogram was taken.
+     * <p>
+     * {@link Recorder#getIntervalHistogram(Histogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)}
+     * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+     * and content copying operations, and is therefore significantly more efficient for repeated use than
+     * {@link Recorder#getIntervalHistogram()} and
+     * {@link Recorder#getIntervalHistogramInto getIntervalHistogramInto()}. The provided
+     * {@code histogramToRecycle} must
+     * be either be null or an interval histogram returned by a previous call to
+     * {@link Recorder#getIntervalHistogram(Histogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} or
+     * {@link Recorder#getIntervalHistogram()}.
+     * <p>
+     * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+     * the same interval histogram instance is recycled more than once, behavior is undefined.
+     * <p>
+     * Calling {@link Recorder#getIntervalHistogram(Histogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+     * counts for the next interval
+     *
+     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     *                           copy operations.
+     * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+     *                                 instance of {@link Recorder}. If false, will allow recycling histograms
+     *                                 previously returned by other instances of {@link Recorder}.
+     * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+     */
+    public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle,
+                                                       boolean enforeContainingInstance) {
         // Verify that replacement histogram can validly be used as an inactive histogram replacement:
-        validateFitAsReplacementHistogram(histogramToRecycle);
+        validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
         inactiveHistogram = histogramToRecycle;
         performIntervalSample();
         Histogram sampledHistogram = inactiveHistogram;
@@ -211,6 +264,7 @@ public class Recorder {
     /**
      * Reset any value counts accumulated thus far.
      */
+    @Override
     public synchronized void reset() {
         // the currently inactive histogram is reset each time we flip. So flipping twice resets both:
         performIntervalSample();
@@ -278,30 +332,34 @@ public class Recorder {
         }
     }
 
-    void validateFitAsReplacementHistogram(Histogram replacementHistogram) {
+    private void validateFitAsReplacementHistogram(Histogram replacementHistogram,
+                                                   boolean enforeContainingInstance) {
         boolean bad = true;
         if (replacementHistogram == null) {
             bad = false;
         } else if (replacementHistogram instanceof InternalAtomicHistogram) {
             if ((activeHistogram instanceof InternalAtomicHistogram)
                     &&
-                    (((InternalAtomicHistogram)replacementHistogram).containingInstanceId ==
-                            ((InternalAtomicHistogram)activeHistogram).containingInstanceId)
-                    ) {
+                    ((!enforeContainingInstance) ||
+                            (((InternalAtomicHistogram)replacementHistogram).containingInstanceId ==
+                                    ((InternalAtomicHistogram)activeHistogram).containingInstanceId)
+                    )) {
                 bad = false;
             }
         } else if (replacementHistogram instanceof InternalConcurrentHistogram) {
             if ((activeHistogram instanceof InternalConcurrentHistogram)
                     &&
-                    (((InternalConcurrentHistogram)replacementHistogram).containingInstanceId ==
-                            ((InternalConcurrentHistogram)activeHistogram).containingInstanceId)
-                    ) {
+                    ((!enforeContainingInstance) ||
+                            (((InternalConcurrentHistogram)replacementHistogram).containingInstanceId ==
+                                    ((InternalConcurrentHistogram)activeHistogram).containingInstanceId)
+                    )) {
                 bad = false;
             }
         }
         if (bad) {
             throw new IllegalArgumentException("replacement histogram must have been obtained via a previous" +
-                    " getIntervalHistogram() call from this " + this.getClass().getName() +" instance");
+                    " getIntervalHistogram() call from this " + this.getClass().getName() +
+                    (enforeContainingInstance ? " insatnce" : " class"));
         }
     }
 }


=====================================
src/main/java/org/HdrHistogram/ShortCountsHistogram.java
=====================================
@@ -80,6 +80,11 @@ public class ShortCountsHistogram extends AbstractHistogram {
         this.normalizingIndexOffset = normalizingIndexOffset;
     }
 
+    @Override
+    void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+        nonConcurrentSetIntegerToDoubleValueConversionRatio(integerToDoubleValueConversionRatio);
+    }
+
     @Override
     void shiftNormalizingIndexByOffset(int offsetToAdd,
                                        boolean lowestHalfBucketPopulated,


=====================================
src/main/java/org/HdrHistogram/SingleWriterDoubleRecorder.java
=====================================
@@ -20,7 +20,20 @@ import java.util.concurrent.atomic.AtomicLong;
  * call {@link SingleWriterDoubleRecorder#recordValue} or
  * {@link SingleWriterDoubleRecorder#recordValueWithExpectedInterval} at any point in time.
  * It DOES NOT support concurrent recording calls.
- *
+ * <p>
+ * A common pattern for using a {@link SingleWriterDoubleRecorder} looks like this:
+ * <br><pre><code>
+ * SingleWriterDoubleRecorder recorder = new SingleWriterDoubleRecorder(2); // Two decimal point accuracy
+ * DoubleHistogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ *   ...
+ *   // Get interval histogram, recycling previous interval histogram:
+ *   intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ *   histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ *   ...
+ * [end of loop construct]
+ * </code></pre>
  */
 
 public class SingleWriterDoubleRecorder {
@@ -156,13 +169,50 @@ public class SingleWriterDoubleRecorder {
      * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
      * counts for the next interval
      *
-     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     * @param histogramToRecycle a previously returned interval histogram (from this instance of
+     *                           {@link SingleWriterDoubleRecorder}) that may be recycled to avoid allocation and
      *                           copy operations.
      * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
      */
     public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle) {
+        return getIntervalHistogram(histogramToRecycle, true);
+    }
+
+    /**
+     * Get an interval histogram, which will include a stable, consistent view of all value counts
+     * accumulated since the last interval histogram was taken.
+     * <p>
+     * {@link SingleWriterDoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)}
+     * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+     * and content copying operations, and is therefore significantly more efficient for repeated use than
+     * {@link SingleWriterDoubleRecorder#getIntervalHistogram()} and
+     * {@link SingleWriterDoubleRecorder#getIntervalHistogramInto getIntervalHistogramInto()}. The
+     * provided {@code histogramToRecycle} must
+     * be either be null or an interval histogram returned by a previous call to
+     * {@link SingleWriterDoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} or
+     * {@link SingleWriterDoubleRecorder#getIntervalHistogram()}.
+     * <p>
+     * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+     * the same interval histogram instance is recycled more than once, behavior is undefined.
+     * <p>
+     * Calling
+     * {@link SingleWriterDoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+     * counts for the next interval
+     *
+     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     *                           copy operations.
+     * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+     *                                 instance of {@link SingleWriterDoubleRecorder}. If false, will allow recycling histograms
+     *                                 previously returned by other instances of {@link SingleWriterDoubleRecorder}.
+     * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+     */
+    public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle,
+                                                             boolean enforeContainingInstance) {
         // Verify that replacement histogram can validly be used as an inactive histogram replacement:
-        validateFitAsReplacementHistogram(histogramToRecycle);
+        validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
         inactiveHistogram = (InternalDoubleHistogram) histogramToRecycle;
         performIntervalSample();
         DoubleHistogram sampledHistogram = inactiveHistogram;
@@ -244,20 +294,22 @@ public class SingleWriterDoubleRecorder {
         }
     }
 
-    void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram) {
+    private void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram,
+                                                   boolean enforeContainingInstance) {
         boolean bad = true;
         if (replacementHistogram == null) {
             bad = false;
         } else if ((replacementHistogram instanceof InternalDoubleHistogram)
                 &&
-                (((InternalDoubleHistogram) replacementHistogram).containingInstanceId ==
+                ((!enforeContainingInstance) ||
+                        (((InternalDoubleHistogram) replacementHistogram).containingInstanceId ==
                         activeHistogram.containingInstanceId)
-                ) {
+                )) {
             bad = false;
         }
 
         if (bad) {
-            throw new IllegalArgumentException("replacement histogram must have been obtained via a previous" +
+            throw new IllegalArgumentException("replacement histogram must have been obtained via a previous " +
                     "getIntervalHistogram() call from this " + this.getClass().getName() +" instance");
         }
     }


=====================================
src/main/java/org/HdrHistogram/SingleWriterRecorder.java
=====================================
@@ -21,9 +21,23 @@ import java.util.concurrent.atomic.AtomicLong;
  * call {@link SingleWriterRecorder#recordValue} or
  * {@link SingleWriterRecorder#recordValueWithExpectedInterval} at any point in time.
  * It DOES NOT safely support concurrent recording calls.
+ *  * <p>
+ * A common pattern for using a {@link SingleWriterRecorder} looks like this:
+ * <br><pre><code>
+ * SingleWriterRecorder recorder = new SingleWriterRecorder(2); // Two decimal point accuracy
+ * Histogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ *   ...
+ *   // Get interval histogram, recycling previous interval histogram:
+ *   intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ *   histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ *   ...
+ * [end of loop construct]
+ * </code></pre>
  */
 
-public class SingleWriterRecorder {
+public class SingleWriterRecorder implements ValueRecorder {
     private static AtomicLong instanceIdSequencer = new AtomicLong(1);
     private final long instanceId = instanceIdSequencer.getAndIncrement();
 
@@ -92,6 +106,7 @@ public class SingleWriterRecorder {
      * @param value the value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValue(final long value) {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
         try {
@@ -108,6 +123,7 @@ public class SingleWriterRecorder {
      * @param count The number of occurrences of this value to record
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithCount(final long value, final long count) throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
         try {
@@ -133,6 +149,7 @@ public class SingleWriterRecorder {
      *                                           than expectedIntervalBetweenValueSamples
      * @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
      */
+    @Override
     public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
             throws ArrayIndexOutOfBoundsException {
         long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
@@ -179,13 +196,49 @@ public class SingleWriterRecorder {
      * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
      * counts for the next interval
      *
-     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     * @param histogramToRecycle a previously returned interval histogram (from this instance of
+     *                           {@link SingleWriterRecorder}) that may be recycled to avoid allocation and
      *                           copy operations.
      * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
      */
     public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle) {
+        return getIntervalHistogram(histogramToRecycle, true);
+    }
+
+    /**
+     * Get an interval histogram, which will include a stable, consistent view of all value counts
+     * accumulated since the last interval histogram was taken.
+     * <p>
+     * {@link SingleWriterRecorder#getIntervalHistogram(Histogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)}
+     * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+     * and content copying operations, and is therefore significantly more efficient for repeated use than
+     * {@link SingleWriterRecorder#getIntervalHistogram()} and
+     * {@link SingleWriterRecorder#getIntervalHistogramInto getIntervalHistogramInto()}. The provided
+     * {@code histogramToRecycle} must
+     * be either be null or an interval histogram returned by a previous call to
+     * {@link SingleWriterRecorder#getIntervalHistogram(Histogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} or
+     * {@link SingleWriterRecorder#getIntervalHistogram()}.
+     * <p>
+     * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+     * the same interval histogram instance is recycled more than once, behavior is undefined.
+     * <p>
+     * Calling {@link SingleWriterRecorder#getIntervalHistogram(Histogram histogramToRecycle)
+     * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+     * counts for the next interval
+     *
+     * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+     *                           copy operations.
+     * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+     *                                 instance of {@link SingleWriterRecorder}. If false, will allow recycling histograms
+     *                                 previously returned by other instances of {@link SingleWriterRecorder}.
+     * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+     */
+    public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle,
+                                                       boolean enforeContainingInstance) {
         // Verify that replacement histogram can validly be used as an inactive histogram replacement:
-        validateFitAsReplacementHistogram(histogramToRecycle);
+        validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
         inactiveHistogram = (InternalHistogram) histogramToRecycle;
         performIntervalSample();
         Histogram sampledHistogram = inactiveHistogram;
@@ -210,6 +263,7 @@ public class SingleWriterRecorder {
     /**
      * Reset any value counts accumulated thus far.
      */
+    @Override
     public synchronized void reset() {
         // the currently inactive histogram is reset each time we flip. So flipping twice resets both:
         performIntervalSample();
@@ -268,21 +322,24 @@ public class SingleWriterRecorder {
         }
     }
 
-    void validateFitAsReplacementHistogram(Histogram replacementHistogram) {
+    private void validateFitAsReplacementHistogram(Histogram replacementHistogram,
+                                                   boolean enforeContainingInstance) {
         boolean bad = true;
         if (replacementHistogram == null) {
             bad = false;
         } else if ((replacementHistogram instanceof InternalHistogram)
                 &&
-                (((InternalHistogram) replacementHistogram).containingInstanceId ==
-                        activeHistogram.containingInstanceId)
-                ) {
+                ((!enforeContainingInstance) ||
+                        (((InternalHistogram) replacementHistogram).containingInstanceId ==
+                                activeHistogram.containingInstanceId)
+                )) {
             bad = false;
         }
 
         if (bad) {
-            throw new IllegalArgumentException("replacement histogram must have been obtained via a previous" +
-                    "getIntervalHistogram() call from this " + this.getClass().getName() +" instance");
+            throw new IllegalArgumentException("replacement histogram must have been obtained via a previous " +
+                    "getIntervalHistogram() call from this " + this.getClass().getName() +
+                    (enforeContainingInstance ? " insatnce" : " class"));
         }
     }
 }


=====================================
src/main/java/org/HdrHistogram/SynchronizedDoubleHistogram.java
=====================================
@@ -264,6 +264,11 @@ public class SynchronizedDoubleHistogram extends DoubleHistogram {
         }
     }
 
+    @Override
+    public synchronized int hashCode() {
+        return super.hashCode();
+    }
+
     @Override
     public synchronized long getTotalCount() {
         return super.getTotalCount();


=====================================
src/main/java/org/HdrHistogram/SynchronizedHistogram.java
=====================================
@@ -309,6 +309,11 @@ public class SynchronizedHistogram extends Histogram {
         }
     }
 
+    @Override
+    public synchronized int hashCode() {
+        return super.hashCode();
+    }
+
     @Override
     public synchronized long getLowestDiscernibleValue() {
         return super.getLowestDiscernibleValue();


=====================================
src/main/java/org/HdrHistogram/ValueRecorder.java
=====================================
@@ -0,0 +1,47 @@
+package org.HdrHistogram;
+
+public interface ValueRecorder {
+
+    /**
+     * Record a value
+     *
+     * @param value The value to be recorded
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+     */
+    void recordValue(long value) throws ArrayIndexOutOfBoundsException;
+
+    /**
+     * Record a value (adding to the value's current count)
+     *
+     * @param value The value to be recorded
+     * @param count The number of occurrences of this value to record
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+     */
+    void recordValueWithCount(long value, long count) throws ArrayIndexOutOfBoundsException;
+
+    /**
+     * Record a value.
+     * <p>
+     * To compensate for the loss of sampled values when a recorded value is larger than the expected
+     * interval between value samples, will auto-generate an additional series of decreasingly-smaller
+     * (down to the expectedIntervalBetweenValueSamples) value records.
+     * <p>
+     * Note: This is a at-recording correction method, as opposed to the post-recording correction method provided
+     * by {@link AbstractHistogram#copyCorrectedForCoordinatedOmission(long)}.
+     * The two methods are mutually exclusive, and only one of the two should be be used on a given data set to correct
+     * for the same coordinated omission issue.
+     *
+     * @param value                               The value to record
+     * @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
+     *                                            auto-generated value records as appropriate if value is larger
+     *                                            than expectedIntervalBetweenValueSamples
+     * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+     */
+    void recordValueWithExpectedInterval(long value, long expectedIntervalBetweenValueSamples)
+            throws ArrayIndexOutOfBoundsException;
+
+    /**
+     * Reset the contents and collected stats
+     */
+    void reset();
+}


=====================================
src/perf/java/org/HdrHistogram/HistogramPerfTest.java
=====================================
@@ -18,7 +18,7 @@ public class HistogramPerfTest {
     static final int numberOfSignificantValueDigits = 3;
     static final long testValueLevel = 12340;
     static final long warmupLoopLength = 50000;
-    static final long rawTimingLoopCount = 500000000L;
+    static final long rawTimingLoopCount = 800000000L;
     static final long rawDoubleTimingLoopCount = 300000000L;
     static final long singleWriterIntervalTimingLoopCount = 100000000L;
     static final long singleWriterDoubleIntervalTimingLoopCount = 100000000L;
@@ -27,6 +27,11 @@ public class HistogramPerfTest {
     static final long atomicTimingLoopCount = 80000000L;
     static final long concurrentTimingLoopCount = 50000000L;
 
+    void recordLoop(AbstractHistogram histogram, long loopCount) {
+        for (long i = 0; i < loopCount; i++)
+            histogram.recordValue(testValueLevel + (i & 0x8000));
+    }
+
     void recordLoopWithExpectedInterval(AbstractHistogram histogram, long loopCount, long expectedInterval) {
         for (long i = 0; i < loopCount; i++)
             histogram.recordValueWithExpectedInterval(testValueLevel + (i & 0x8000), expectedInterval);
@@ -74,6 +79,35 @@ public class HistogramPerfTest {
         return sum;
     }
 
+    public void testRawRecordingSpeedSingleValue(String label, AbstractHistogram histogram, long timingLoopCount) throws Exception {
+        System.out.println("\nTiming recording speed with single value per recording:");
+        // Warm up:
+        long startTime = System.nanoTime();
+        recordLoop(histogram, warmupLoopLength);
+        long endTime = System.nanoTime();
+        long deltaUsec = (endTime - startTime) / 1000L;
+        long rate = 1000000 * warmupLoopLength / deltaUsec;
+        System.out.println(label + "Warmup: " + warmupLoopLength + " value recordings completed in " +
+                deltaUsec + " usec, rate = " + rate + " value recording calls per sec.");
+        histogram.reset();
+        // Wait a bit to make sure compiler had a cache to do it's stuff:
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+        }
+        startTime = System.nanoTime();
+        recordLoop(histogram, timingLoopCount);
+        endTime = System.nanoTime();
+        deltaUsec = (endTime - startTime) / 1000L;
+        rate = 1000000 * timingLoopCount / deltaUsec;
+        System.out.println(label + "Hot code timing:");
+        System.out.println(label + timingLoopCount + " value recordings completed in " +
+                deltaUsec + " usec, rate = " + rate + " value recording calls per sec.");
+        rate = 1000000 * histogram.getTotalCount() / deltaUsec;
+        System.out.println(label + histogram.getTotalCount() + " raw recorded entries completed in " +
+                deltaUsec + " usec, rate = " + rate + " recorded values per sec.");
+    }
+
     public void testRawRecordingSpeedAtExpectedInterval(String label, AbstractHistogram histogram,
                                                         long expectedInterval, long timingLoopCount) throws Exception {
         System.out.println("\nTiming recording speed with expectedInterval = " + expectedInterval + " :");
@@ -258,6 +292,14 @@ public class HistogramPerfTest {
                 deltaUsec + " usec, rate = " + rate + " recorded values per sec.");
     }
 
+    @Test
+    public void testRawRecordingSpeedSingleValue() throws Exception {
+        AbstractHistogram histogram;
+        histogram = new Histogram(highestTrackableValue, numberOfSignificantValueDigits);
+        System.out.println("\n\nTiming Histogram:");
+        testRawRecordingSpeedSingleValue("Histogram: ", histogram, rawTimingLoopCount);
+    }
+
     @Test
     public void testRawRecordingSpeed() throws Exception {
         AbstractHistogram histogram;


=====================================
src/test/java/org/HdrHistogram/DoubleHistogramTest.java
=====================================
@@ -144,9 +144,17 @@ public class DoubleHistogramTest {
     public void testReset() throws Exception {
         DoubleHistogram histogram = new DoubleHistogram(trackableValueRangeSize, numberOfSignificantValueDigits);
         histogram.recordValue(testValueLevel);
+        histogram.recordValue(10);
+        histogram.recordValue(100);
+        Assert.assertEquals(histogram.getMinValue(), Math.min(10.0, testValueLevel), 1.0);
+        Assert.assertEquals(histogram.getMaxValue(), Math.max(100.0, testValueLevel), 1.0);
         histogram.reset();
         assertEquals(0L, histogram.getCountAtValue(testValueLevel));
         assertEquals(0L, histogram.getTotalCount());
+        histogram.recordValue(20);
+        histogram.recordValue(80);
+        Assert.assertEquals(histogram.getMinValue(), 20.0, 1.0);
+        Assert.assertEquals(histogram.getMaxValue(), 80.0, 1.0);
     }
 
     @Test


=====================================
src/test/java/org/HdrHistogram/HistogramAutosizingTest.java
=====================================
@@ -28,6 +28,23 @@ public class HistogramAutosizingTest {
         Assert.assertEquals(55296, histogram.countsArrayLength);
     }
 
+    @Test
+    public void testHistogramEqualsAfterResizing() throws Exception {
+        Histogram histogram = new Histogram(3);
+        histogram.recordValue((1L << 62) - 1);
+        Assert.assertEquals(52, histogram.bucketCount);
+        Assert.assertEquals(54272, histogram.countsArrayLength);
+        histogram.recordValue(Long.MAX_VALUE);
+        Assert.assertEquals(53, histogram.bucketCount);
+        Assert.assertEquals(55296, histogram.countsArrayLength);
+        histogram.reset();
+        histogram.recordValue((1L << 62) - 1);
+        
+        Histogram histogram1 = new Histogram(3);
+        histogram1.recordValue((1L << 62) - 1);
+        Assert.assertEquals(histogram, histogram1);
+    }
+
     @Test
     public void testDoubleHistogramAutoSizingEdges() throws Exception {
         DoubleHistogram histogram = new DoubleHistogram(3);


=====================================
src/test/java/org/HdrHistogram/HistogramDataAccessTest.java
=====================================
@@ -605,4 +605,96 @@ public class HistogramDataAccessTest {
 
         Assert.assertTrue("Histograms should be equal", histogram1.equals(histogram2));
     }
+    
+    @Test
+    public void testLinearIteratorVisitsBucketsWiderThanStepSizeMultipleTimes() {
+        Histogram h = new Histogram(1, Long.MAX_VALUE, 3);
+
+        h.recordValue(1);
+        h.recordValue(2047);
+        // bucket size 2
+        h.recordValue(2048);
+        h.recordValue(2049);
+        h.recordValue(4095);
+        // bucket size 4
+        h.recordValue(4096);
+        h.recordValue(4097);
+        h.recordValue(4098);
+        h.recordValue(4099);
+        // 2nd bucket in size 4
+        h.recordValue(4100);
+
+        // sadly verbose helper class to hang on to iteration information for later comparison
+        class IteratorValueSnapshot {
+            private final long value;
+            private final long count;
+
+            private IteratorValueSnapshot(HistogramIterationValue iv) {
+                this.value = iv.getValueIteratedTo();
+                this.count = iv.getCountAddedInThisIterationStep();
+            }
+            
+            private IteratorValueSnapshot(long value, long count) {
+                this.value = value;
+                this.count = count;
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) { return true; }
+                if (o == null || getClass() != o.getClass()) { return false; }
+
+                IteratorValueSnapshot that = (IteratorValueSnapshot) o;
+
+                if (value != that.value) { return false; }
+                return count == that.count;
+            }
+
+            @Override
+            public int hashCode() {
+                int result = (int) (value ^ (value >>> 32));
+                result = 31 * result + (int) (count ^ (count >>> 32));
+                return result;
+            }
+
+            @Override
+            public String toString() {
+                return "IteratorValueSnapshot{" +
+                        "value=" + value +
+                        ", count=" + count +
+                        '}';
+            }
+        }
+
+        List<IteratorValueSnapshot> snapshots = new ArrayList<IteratorValueSnapshot>();
+
+        for (HistogramIterationValue iv : h.linearBucketValues(1)) {
+            snapshots.add(new IteratorValueSnapshot(iv));
+        }
+
+        // bucket size 1
+        Assert.assertEquals(new IteratorValueSnapshot(0, 0), snapshots.get(0));
+        Assert.assertEquals(new IteratorValueSnapshot(1, 1), snapshots.get(1));
+        Assert.assertEquals(new IteratorValueSnapshot(2046, 0), snapshots.get(2046));
+        Assert.assertEquals(new IteratorValueSnapshot(2047, 1), snapshots.get(2047));
+        // bucket size 2
+        Assert.assertEquals(new IteratorValueSnapshot(2048, 2), snapshots.get(2048));
+        Assert.assertEquals(new IteratorValueSnapshot(2049, 0), snapshots.get(2049));
+        Assert.assertEquals(new IteratorValueSnapshot(2050, 0), snapshots.get(2050));
+        Assert.assertEquals(new IteratorValueSnapshot(2051, 0), snapshots.get(2051));
+        Assert.assertEquals(new IteratorValueSnapshot(4094, 1), snapshots.get(4094));
+        Assert.assertEquals(new IteratorValueSnapshot(4095, 0), snapshots.get(4095));
+        // bucket size 4
+        Assert.assertEquals(new IteratorValueSnapshot(4096, 4), snapshots.get(4096));
+        Assert.assertEquals(new IteratorValueSnapshot(4097, 0), snapshots.get(4097));
+        Assert.assertEquals(new IteratorValueSnapshot(4098, 0), snapshots.get(4098));
+        Assert.assertEquals(new IteratorValueSnapshot(4099, 0), snapshots.get(4099));
+        // also size 4, last bucket
+        Assert.assertEquals(new IteratorValueSnapshot(4100, 1), snapshots.get(4100));
+        Assert.assertEquals(new IteratorValueSnapshot(4101, 0), snapshots.get(4101));
+        Assert.assertEquals(new IteratorValueSnapshot(4102, 0), snapshots.get(4102));
+        Assert.assertEquals(new IteratorValueSnapshot(4103, 0), snapshots.get(4103));
+
+        Assert.assertEquals(4104, snapshots.size());
+    }
 }


=====================================
src/test/java/org/HdrHistogram/HistogramTest.java
=====================================
@@ -377,10 +377,18 @@ public class HistogramTest {
     public void testReset() throws Exception {
         Histogram histogram = new Histogram(highestTrackableValue, numberOfSignificantValueDigits);
         histogram.recordValue(testValueLevel);
+        histogram.recordValue(10);
+        histogram.recordValue(100);
+        Assert.assertEquals(histogram.getMinValue(), Math.min(10, testValueLevel));
+        Assert.assertEquals(histogram.getMaxValue(), Math.max(100, testValueLevel));
         histogram.reset();
         Assert.assertEquals(0L, histogram.getCountAtValue(testValueLevel));
         Assert.assertEquals(0L, histogram.getTotalCount());
         verifyMaxValue(histogram);
+        histogram.recordValue(20);
+        histogram.recordValue(80);
+        Assert.assertEquals(histogram.getMinValue(), 20);
+        Assert.assertEquals(histogram.getMaxValue(), 80);
     }
 
     @Test


=====================================
src/test/java/org/HdrHistogram/RecorderTest.java
=====================================
@@ -119,4 +119,135 @@ public class RecorderTest {
         Histogram histogram = recorder.getIntervalHistogram();
     }
 
+    // Recorder Recycling tests:
+
+    @Test
+    public void testRecycling() throws Exception {
+        Recorder recorder = new Recorder(3);
+        Histogram histogramA = recorder.getIntervalHistogram();
+        Histogram histogramB = recorder.getIntervalHistogram(histogramA);
+        Histogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecyclingContainingClassEnforcement() throws Exception {
+        Histogram histToRecycle = new Histogram(3);
+        Recorder recorder = new Recorder(3);
+        Histogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRecyclingContainingInstanceEnforcement() throws Exception {
+        Recorder recorder1 = new Recorder(3);
+        Recorder recorder2 = new Recorder(3);
+        Histogram histToRecycle = recorder1.getIntervalHistogram();
+        Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test
+    public void testRecyclingContainingInstanceNonEnforcement() throws Exception {
+        Recorder recorder1 = new Recorder(3);
+        Recorder recorder2 = new Recorder(3);
+        Histogram histToRecycle = recorder1.getIntervalHistogram();
+        Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+    }
+
+    // SingleWriterRecorder Recycling tests:
+
+    @Test
+    public void testSWRecycling() throws Exception {
+        SingleWriterRecorder recorder = new SingleWriterRecorder(3);
+        Histogram histogramA = recorder.getIntervalHistogram();
+        Histogram histogramB = recorder.getIntervalHistogram(histogramA);
+        Histogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSWRecyclingContainingClassEnforcement() throws Exception {
+        Histogram histToRecycle = new Histogram(3);
+        SingleWriterRecorder recorder = new SingleWriterRecorder(3);
+        Histogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSWRecyclingContainingInstanceEnforcement() throws Exception {
+        SingleWriterRecorder recorder1 = new SingleWriterRecorder(3);
+        SingleWriterRecorder recorder2 = new SingleWriterRecorder(3);
+        Histogram histToRecycle = recorder1.getIntervalHistogram();
+        Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test
+    public void testSWRecyclingContainingInstanceNonEnforcement() throws Exception {
+        SingleWriterRecorder recorder1 = new SingleWriterRecorder(3);
+        SingleWriterRecorder recorder2 = new SingleWriterRecorder(3);
+        Histogram histToRecycle = recorder1.getIntervalHistogram();
+        Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+    }
+
+    // DoubleRecorder Recycling tests:
+
+    @Test
+    public void testDRecycling() throws Exception {
+        DoubleRecorder recorder = new DoubleRecorder(3);
+        DoubleHistogram histogramA = recorder.getIntervalHistogram();
+        DoubleHistogram histogramB = recorder.getIntervalHistogram(histogramA);
+        DoubleHistogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testDRecyclingContainingClassEnforcement() throws Exception {
+        DoubleHistogram histToRecycle = new DoubleHistogram(3);
+        DoubleRecorder recorder = new DoubleRecorder(3);
+        DoubleHistogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testDRecyclingContainingInstanceEnforcement() throws Exception {
+        DoubleRecorder recorder1 = new DoubleRecorder(3);
+        DoubleRecorder recorder2 = new DoubleRecorder(3);
+        DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+        DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test
+    public void testDRecyclingContainingInstanceNonEnforcement() throws Exception {
+        DoubleRecorder recorder1 = new DoubleRecorder(3);
+        DoubleRecorder recorder2 = new DoubleRecorder(3);
+        DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+        DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+    }
+
+    // SingleWriterDoubleRecorder Recycling tests:
+
+    @Test
+    public void testSWDRecycling() throws Exception {
+        SingleWriterDoubleRecorder recorder = new SingleWriterDoubleRecorder(3);
+        DoubleHistogram histogramA = recorder.getIntervalHistogram();
+        DoubleHistogram histogramB = recorder.getIntervalHistogram(histogramA);
+        DoubleHistogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSWDRecyclingContainingClassEnforcement() throws Exception {
+        DoubleHistogram histToRecycle = new DoubleHistogram(3);
+        SingleWriterDoubleRecorder recorder = new SingleWriterDoubleRecorder(3);
+        DoubleHistogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSWDRecyclingContainingInstanceEnforcement() throws Exception {
+        SingleWriterDoubleRecorder recorder1 = new SingleWriterDoubleRecorder(3);
+        SingleWriterDoubleRecorder recorder2 = new SingleWriterDoubleRecorder(3);
+        DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+        DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+    }
+
+    @Test
+    public void testSWDRecyclingContainingInstanceNonEnforcement() throws Exception {
+        SingleWriterDoubleRecorder recorder1 = new SingleWriterDoubleRecorder(3);
+        SingleWriterDoubleRecorder recorder2 = new SingleWriterDoubleRecorder(3);
+        DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+        DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+    }
 }



View it on GitLab: https://salsa.debian.org/java-team/hdrhistogram/commit/ecf9920b08d12eb87eb1d010e2061570497f4e71

-- 
View it on GitLab: https://salsa.debian.org/java-team/hdrhistogram/commit/ecf9920b08d12eb87eb1d010e2061570497f4e71
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20190119/c5d5bbcb/attachment.html>


More information about the pkg-java-commits mailing list