github.com/minio/simdjson-go@v0.4.6-0.20231116094823-04d21cddf993/parse_json_amd64.go (about)

     1  //go:build !noasm && !appengine && gc
     2  // +build !noasm,!appengine,gc
     3  
     4  /*
     5   * MinIO Cloud Storage, (C) 2020 MinIO, Inc.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19  
    20  package simdjson
    21  
    22  import (
    23  	"bytes"
    24  	"errors"
    25  	"sync"
    26  )
    27  
    28  func (pj *internalParsedJson) initialize(size int) {
    29  	// Estimate the tape size to be about 15% of the length of the JSON message
    30  	avgTapeSize := size * 15 / 100
    31  	if cap(pj.Tape) < avgTapeSize {
    32  		pj.Tape = make([]uint64, 0, avgTapeSize)
    33  	}
    34  	pj.Tape = pj.Tape[:0]
    35  
    36  	stringsSize := size / 10
    37  	if stringsSize < 128 {
    38  		stringsSize = 128 // always allocate at least 128 for the string buffer
    39  	}
    40  	if pj.Strings != nil && cap(pj.Strings.B) >= stringsSize {
    41  		pj.Strings.B = pj.Strings.B[:0]
    42  	} else {
    43  		pj.Strings = &TStrings{make([]byte, 0, stringsSize)}
    44  	}
    45  	if cap(pj.containingScopeOffset) < maxdepth {
    46  		pj.containingScopeOffset = make([]uint64, 0, maxdepth)
    47  	}
    48  	pj.containingScopeOffset = pj.containingScopeOffset[:0]
    49  	pj.indexesChan = indexChan{}
    50  }
    51  
    52  func (pj *internalParsedJson) parseMessage(msg []byte, ndjson bool) (err error) {
    53  	// Cache message so we can point directly to strings
    54  	// TODO: Find out why TestVerifyTape/instruments fails without bytes.TrimSpace
    55  	pj.Message = bytes.TrimSpace(msg)
    56  	pj.initialize(len(pj.Message))
    57  
    58  	if ndjson {
    59  		pj.ndjson = 1
    60  	} else {
    61  		pj.ndjson = 0
    62  	}
    63  
    64  	// Make the capacity of the channel smaller than the number of slots.
    65  	// This way the sender will automatically block until the consumer
    66  	// has finished the slot it is working on.
    67  	if pj.indexChans == nil {
    68  		pj.indexChans = make(chan indexChan, indexSlots-2)
    69  	}
    70  	pj.buffersOffset = ^uint64(0)
    71  
    72  	var errStage1 error
    73  
    74  	// Do long inputs async
    75  	if len(pj.Message) > 8<<10 {
    76  		var wg sync.WaitGroup
    77  		wg.Add(1)
    78  		go func() {
    79  			defer wg.Done()
    80  			if ok, done := pj.unifiedMachine(); !ok {
    81  				err = errors.New("Bad parsing while executing stage 2")
    82  				// Keep consuming...
    83  				if !done {
    84  					for idx := range pj.indexChans {
    85  						if idx.index == -1 {
    86  							break
    87  						}
    88  					}
    89  				}
    90  			}
    91  		}()
    92  		if !pj.findStructuralIndices() {
    93  			errStage1 = errors.New("Failed to find all structural indices for stage 1")
    94  		}
    95  		wg.Wait()
    96  	} else {
    97  		if !pj.findStructuralIndices() {
    98  			// drain the channel until empty
    99  			for idx := range pj.indexChans {
   100  				if idx.index == -1 {
   101  					break
   102  				}
   103  			}
   104  			return errors.New("Failed to find all structural indices for stage 1")
   105  		}
   106  		if ok, _ := pj.unifiedMachine(); !ok {
   107  			// drain the channel until empty
   108  			for {
   109  				select {
   110  				case idx := <-pj.indexChans:
   111  					if idx.index == -1 {
   112  						return errors.New("Bad parsing while executing stage 2")
   113  					}
   114  					// Already drained.
   115  				default:
   116  					return errors.New("Bad parsing while executing stage 2")
   117  				}
   118  			}
   119  		}
   120  		return nil
   121  	}
   122  
   123  	if errStage1 != nil {
   124  		return errStage1
   125  	}
   126  	return
   127  }