github.com/dgraph-io/simdjson-go@v0.3.0/parse_number_amd64.go (about)

     1  //+build !noasm
     2  //+build !appengine
     3  //+build gc
     4  
     5  /*
     6   * MinIO Cloud Storage, (C) 2020 MinIO, Inc.
     7   *
     8   * Licensed under the Apache License, Version 2.0 (the "License");
     9   * you may not use this file except in compliance with the License.
    10   * You may obtain a copy of the License at
    11   *
    12   *     http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing, software
    15   * distributed under the License is distributed on an "AS IS" BASIS,
    16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17   * See the License for the specific language governing permissions and
    18   * limitations under the License.
    19   */
    20  
    21  package simdjson
    22  
    23  import (
    24  	"errors"
    25  	"math"
    26  	"strconv"
    27  )
    28  
    29  const (
    30  	isPartOfNumberFlag = 1 << iota
    31  	isFloatOnlyFlag
    32  	isMinusFlag
    33  	isEOVFlag
    34  	isDigitFlag
    35  	isMustHaveDigitNext
    36  )
    37  
    38  var isNumberRune = [256]uint8{
    39  	'0':  isPartOfNumberFlag | isDigitFlag,
    40  	'1':  isPartOfNumberFlag | isDigitFlag,
    41  	'2':  isPartOfNumberFlag | isDigitFlag,
    42  	'3':  isPartOfNumberFlag | isDigitFlag,
    43  	'4':  isPartOfNumberFlag | isDigitFlag,
    44  	'5':  isPartOfNumberFlag | isDigitFlag,
    45  	'6':  isPartOfNumberFlag | isDigitFlag,
    46  	'7':  isPartOfNumberFlag | isDigitFlag,
    47  	'8':  isPartOfNumberFlag | isDigitFlag,
    48  	'9':  isPartOfNumberFlag | isDigitFlag,
    49  	'.':  isPartOfNumberFlag | isFloatOnlyFlag | isMustHaveDigitNext,
    50  	'+':  isPartOfNumberFlag,
    51  	'-':  isPartOfNumberFlag | isMinusFlag | isMustHaveDigitNext,
    52  	'e':  isPartOfNumberFlag | isFloatOnlyFlag,
    53  	'E':  isPartOfNumberFlag | isFloatOnlyFlag,
    54  	',':  isEOVFlag,
    55  	'}':  isEOVFlag,
    56  	']':  isEOVFlag,
    57  	' ':  isEOVFlag,
    58  	'\t': isEOVFlag,
    59  	'\r': isEOVFlag,
    60  	'\n': isEOVFlag,
    61  	':':  isEOVFlag,
    62  }
    63  
    64  // parseNumber will parse the number starting in the buffer.
    65  // Any non-number characters at the end will be ignored.
    66  // Returns TagEnd if no valid value found be found.
    67  func parseNumber(buf []byte) (tag Tag, val, flags uint64, pos int) {
    68  	found := uint8(0)
    69  	for i, v := range buf {
    70  		t := isNumberRune[v]
    71  		if t == 0 {
    72  			//fmt.Println("aborting on", string(v), "in", string(buf[:i]))
    73  			return TagEnd, 0, 0, pos
    74  		}
    75  		if t == isEOVFlag {
    76  			break
    77  		}
    78  		if t&isMustHaveDigitNext > 0 {
    79  			// A period and minus must be followed by a digit
    80  			if len(buf) < i+2 || isNumberRune[buf[i+1]]&isDigitFlag == 0 {
    81  				return TagEnd, 0, 0, pos
    82  			}
    83  		}
    84  		found |= t
    85  		pos = i + 1
    86  	}
    87  	if pos == 0 {
    88  		return TagEnd, 0, 0, pos
    89  	}
    90  	const maxIntLen = 20
    91  
    92  	// Only try integers if we didn't find any float exclusive and it can fit in an integer.
    93  	if found&isFloatOnlyFlag == 0 && pos <= 20 {
    94  		if found&isMinusFlag == 0 {
    95  			if pos > 1 && buf[0] == '0' {
    96  				// Integers cannot have a leading zero.
    97  				return TagEnd, 0, 0, pos
    98  			}
    99  		} else {
   100  			if pos > 2 && buf[1] == '0' {
   101  				// Integers cannot have a leading zero after minus.
   102  				return TagEnd, 0, 0, pos
   103  			}
   104  		}
   105  		i64, err := strconv.ParseInt(string(buf[:pos]), 10, 64)
   106  		if err == nil {
   107  			return TagInteger, uint64(i64), 0, pos
   108  		}
   109  		if errors.Is(err, strconv.ErrRange) {
   110  			flags |= uint64(FloatOverflowedInteger)
   111  		}
   112  
   113  		if found&isMinusFlag == 0 {
   114  			u64, err := strconv.ParseUint(string(buf[:pos]), 10, 64)
   115  			if err == nil {
   116  				return TagUint, u64, 0, pos
   117  			}
   118  			if errors.Is(err, strconv.ErrRange) {
   119  				flags |= uint64(FloatOverflowedInteger)
   120  			}
   121  		}
   122  	} else if found&isFloatOnlyFlag == 0 {
   123  		flags |= uint64(FloatOverflowedInteger)
   124  	}
   125  
   126  	if pos > 1 && buf[0] == '0' && isNumberRune[buf[1]]&isFloatOnlyFlag == 0 {
   127  		// Float can only have have a leading 0 when followed by a period.
   128  		return TagEnd, 0, 0, pos
   129  	}
   130  	f64, err := strconv.ParseFloat(string(buf[:pos]), 64)
   131  	if err == nil {
   132  		return TagFloat, math.Float64bits(f64), flags, pos
   133  	}
   134  	return TagEnd, 0, 0, pos
   135  }