Skip to content

Releases: nats-io/jparse

jparse v1.2.3

19 Jun 21:07
487a0f2
Compare
Choose a tag to compare

Cleaned up JavaDocs, and got rid of commented-out code. Improved test code coverage.

jparse v1.2.2

19 Jun 16:51
96fbcb3
Compare
Choose a tag to compare

Updated JavaDocs and improved code coverage.

jparse v1.2.0

12 Apr 19:27
8d6dbd9
Compare
Choose a tag to compare

Release Notes for jparse v1.2.0

Improve floating point parsing speed and make it easier to port to other languages.

New Features and Improvements

  1. Added support for double precision parsing with the new ParseDouble class.
    This class provides a parseDouble method for parsing double-precision numbers
    from character arrays, increasing the range of supported numerical values and
    precision. (Also added support for ParseFloat.
  2. Extended the lookup table for the exponent calculations in both ParseFloat and
    ParseDouble classes. This enhancement ensures more efficient computation of
    larger exponents, improving the overall performance of the parsing methods.
  3. Expanded test coverage by adding a JUnit test class for ParseDouble.
    This test suite includes tests for valid and invalid
    inputs that should throw exceptions. The increased test coverage helps ensure
    the correctness and reliability of the new ParseDouble class.
  4. General code cleanup, refactoring, and comments have been updated for better
    readability and maintainability. The new code structure should make it easier
    for developers to understand the logic and contribute to the project.

Bug Fixes

  • No bug fixes in this release.

Upgrade Notes

  • To start using the new ParseDouble class for double-precision parsing, simply
    update your project dependencies to jparse v1.2.0.

Thank you for using jparse, and we hope you find the new features and improvements
helpful. As always, your feedback and contributions are highly appreciated.


General flow of ParseFloat and ParseDouble utility classes.

The ParseFloat class in the io.nats.jparse.source.support package is designed
to parse float numbers from a character array.

  1. A static lookup table called powersOf10 is defined, containing powers of 10
    from 10^0 to 10^18 as float values.

  2. The parseFloat method is the main entry point of the class. It takes three
    arguments: a character array chars, a startIndex, and an endIndex. The
    method parses the float number represented by the characters between
    startIndex and endIndex.

  3. Inside the parseFloat method, a negative flag and an accumulator variable
    result are initialized. The negative flag is set to true if the first
    character is a minus sign.

  4. The main loop iterates through the characters in the given range, identifying
    digits, decimal points, and exponent indicators.

    • For digits, the character is converted to its numerical value and accumulated
      in the result variable.
    • If a decimal point is encountered, the parseFractionPart method is called
      to handle the fractional part.
    • If an exponent indicator 'e' is encountered, the parseExponent method is
      called to handle the exponent part.
  5. The parseFractionPart method processes the fractional part of the float
    number. It keeps track of the current decimal position using the fraction
    variable and updates the result variable accordingly.

  6. The parseExponent method handles the exponent part of the float number. It
    determines the sign of the exponent and accumulates the exponent value. Then,
    it uses the powersOf10 lookup table to multiply or divide the result
    variable by the appropriate power of 10.

  7. Finally, if the negative flag was set, the result variable is negated, and
    the parsed float value is returned.

Throughout the code, if an unexpected character is encountered, an
UnexpectedCharacterException is thrown with a message indicating the character's index
and value.

Benchmark improvements

After improvements 



Benchmark                         Mode  Cnt        Score   Error  Units
BenchMark.jParseFastDoubleArray  thrpt    2  1326409.042          ops/s
BenchMark.jParseFastFloatArray   thrpt    2  1314308.391          ops/s

1326409.042
  769132.236

1314308.391
 717548.249


ParseFloat

package io.nats.jparse.source.support;

public class ParseFloat {

    static final float[] powersOf10 = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f, 1e7f,
            1e8f, 1e9f, 1e10f, 1e11f, 1e12f, 1e13f, 1e14f, 1e15f, 1e16f, 1e17f, 1e18f};



    public static float parseFloat(char[] chars, int startIndex, int endIndex) {
        boolean negative = false;
        int i = startIndex;
        float result = 0;

        // Check for a negative sign
        if (chars[i] == '-') {
            negative = true;
            i++;
        }

        loop:
        while (i < endIndex) {
            char ch = chars[i];
            switch (ch) {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    result = result * 10 + (ch - '0');
                    i++;
                    break;
                case '.':
                    result =  parseFractionPart(i + 1, endIndex, chars, result);
                    break loop;
                case 'e':
                    result = parseExponent(i+1, endIndex, chars, result);
                    break loop;
                default:
                    throw new UnexpectedCharacterException("parsing float", "Illegal character", ch, i);
            }
        }


        if (negative) {
            result = -result;
        }

        return result;
    }

    private static float parseFractionPart(int i, int endIndex, char[] chars, float result) {
        float fraction = 0.1f;
        while (i < endIndex) {
            char ch = chars[i];
            switch (ch) {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    result += (ch - '0') * fraction;
                    fraction /= 10;
                    i++;
                    break;

                case 'e':
                    return parseExponent(i + 1, endIndex, chars, result);

                default:
                    throw new UnexpectedCharacterException("float parsing fraction part", "Illegal character", ch, i);
            }

        }
        return result;
    }

    private static float parseExponent(int i, int endIndex, char[] chars, float result) {

        boolean exponentNegative = false;
        int exponent = 0;

        char sign =  chars[i];

        switch (sign) {
            case '-':
                exponentNegative = true;
                i++;
                break;
            case '+':
                i++;
                break;
        }

        while (i < endIndex) {
            char ch = chars[i];
            switch (chars[i]) {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    exponent = exponent * 10 + (ch - '0');
                    i++;
                    break;

                default:
                    throw new UnexpectedCharacterException("float parsing exponent part", "Illegal character", ch, i);

            }
        }


        if (exponentNegative) {
            exponent = -exponent;
        }

        // Use Lookup table for powers of 10

        // Calculate the power of 10
        if (!exponentNegative) {
            while (exponent >= powersOf10.length) {
                result *= 1e18f;
                exponent -= 18;
            }
            result *= powersOf10[exponent];
        } else {
            while (-exponent >= powersOf10.length) {
                result /= 1e18f;
                exponent += 18;
            }
            result /= powersOf10[-exponent];
        }

        return result;
    }
}

ParseDouble

The parse double is nearly the same.

package io.nats.jparse.source.support;

public class ParseDouble {

    static final double[] powersOf10 = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
            1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};


    public static double parseDouble(char[] chars, int startIndex, int endIndex) {


        boolean negative = false;
        int i = startIndex;
        double result = 0;

        // Check for a negative sign
        if (chars[i] == '-') {
            negative = true;
            i++;
        }

        loop:
        while (i < endIndex) {
            char ch = chars[i];
            switch (ch) {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    result = result * 10 + (ch - '0');
                    i++;
                    break;
                case '.':
                    result = parseFractionPart(i + 1, endIndex, chars, result);
                    break loop;
                case 'E':
                case 'e':
                    result = parseExponent(i + 1, endIndex, chars, result);
                    break loop;
                default:
                    throw new UnexpectedCharacterException("parsing double", "Illegal character", ch, i);
            }
        }


        if (negative) {
    ...
Read more

v1.1.3

11 Apr 19:34
34eb0e0
Compare
Choose a tag to compare

v1.1.3

v1.1.2 bug fix

11 Apr 19:24
6984ab1
Compare
Choose a tag to compare

Small bug fix for adding an array or object to the list of tokens. Internal.
Added build.

v1.1.0

14 Mar 03:18
5d03c84
Compare
Choose a tag to compare

Added support for JDK 1.8.

v1.0.2

14 Mar 01:29
49817e3
Compare
Choose a tag to compare

v1.0.2

Moved back to gradle 7.5, which Vertx Nats interface uses.
There were error messages with gradle 8 and no clear path forward.

v1.0.1

14 Mar 01:06
1cf75d3
Compare
Choose a tag to compare

v1.0.1

v1.0.0.1

14 Mar 00:51
450527d
Compare
Choose a tag to compare

Version 1

  • Added benchmarks where this parser is faster than mainstream parsers
  • Added a strict mode where this JSON parser is RFC-8259 compliant
  • Added an event-driven mode for streaming parsers.
  • There is a fast and strict mode for both index overlay and event-driven parser.
  • Added support for comments (not turned on by default) and keys without quotes (not turned on by default).