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

     1  /*
     2   * MinIO Cloud Storage, (C) 2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package simdjson
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  )
    23  
    24  // Object represents a JSON object.
    25  type Object struct {
    26  	// Complete tape
    27  	tape ParsedJson
    28  
    29  	// offset of the next entry to be decoded
    30  	off int
    31  }
    32  
    33  // Map will unmarshal into a map[string]interface{}
    34  // See Iter.Interface() for a reference on value types.
    35  func (o *Object) Map(dst map[string]interface{}) (map[string]interface{}, error) {
    36  	if dst == nil {
    37  		dst = make(map[string]interface{})
    38  	}
    39  	var tmp Iter
    40  	for {
    41  		name, t, err := o.NextElement(&tmp)
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  		if t == TypeNone {
    46  			// Done
    47  			break
    48  		}
    49  		dst[name], err = tmp.Interface()
    50  		if err != nil {
    51  			return nil, fmt.Errorf("parsing element %q: %w", name, err)
    52  		}
    53  	}
    54  	return dst, nil
    55  }
    56  
    57  // Parse will return all elements and iterators.
    58  // An optional destination can be given.
    59  // The Object will be consumed.
    60  func (o *Object) Parse(dst *Elements) (*Elements, error) {
    61  	if dst == nil {
    62  		dst = &Elements{
    63  			Elements: make([]Element, 0, 5),
    64  			Index:    make(map[string]int, 5),
    65  		}
    66  	} else {
    67  		dst.Elements = dst.Elements[:0]
    68  		for k := range dst.Index {
    69  			delete(dst.Index, k)
    70  		}
    71  	}
    72  	var tmp Iter
    73  	for {
    74  		name, t, err := o.NextElement(&tmp)
    75  		if err != nil {
    76  			return dst, err
    77  		}
    78  		if t == TypeNone {
    79  			// Done
    80  			break
    81  		}
    82  		dst.Index[name] = len(dst.Elements)
    83  		dst.Elements = append(dst.Elements, Element{
    84  			Name: name,
    85  			Type: t,
    86  			Iter: tmp,
    87  		})
    88  	}
    89  	return dst, nil
    90  }
    91  
    92  // FindKey will return a single named element.
    93  // An optional destination can be given.
    94  // The method will return nil if the element cannot be found.
    95  // This should only be used to locate a single key where the object is no longer needed.
    96  // The object will not be advanced.
    97  func (o *Object) FindKey(key string, dst *Element) *Element {
    98  	tmp := o.tape.Iter()
    99  	tmp.off = o.off
   100  	for {
   101  		typ := tmp.Advance()
   102  		// We want name and at least one value.
   103  		if typ != TypeString || tmp.off+1 >= len(tmp.tape.Tape) {
   104  			return nil
   105  		}
   106  		// Advance must be string or end of object
   107  		offset := tmp.cur
   108  		length := tmp.tape.Tape[tmp.off]
   109  		if int(length) != len(key) {
   110  			// Skip the value.
   111  			t := tmp.Advance()
   112  			if t == TypeNone {
   113  				return nil
   114  			}
   115  			continue
   116  		}
   117  		// Read name
   118  		name, err := tmp.tape.stringByteAt(offset, length)
   119  		if err != nil {
   120  			return nil
   121  		}
   122  
   123  		if string(name) != key {
   124  			// Skip the value
   125  			tmp.Advance()
   126  			continue
   127  		}
   128  		if dst == nil {
   129  			dst = &Element{}
   130  		}
   131  		dst.Name = key
   132  		dst.Type, err = tmp.AdvanceIter(&dst.Iter)
   133  		if err != nil {
   134  			return nil
   135  		}
   136  		return dst
   137  	}
   138  }
   139  
   140  // NextElement sets dst to the next element and returns the name.
   141  // TypeNone with nil error will be returned if there are no more elements.
   142  func (o *Object) NextElement(dst *Iter) (name string, t Type, err error) {
   143  	n, t, err := o.NextElementBytes(dst)
   144  	return string(n), t, err
   145  }
   146  
   147  // NextElementBytes sets dst to the next element and returns the name.
   148  // TypeNone with nil error will be returned if there are no more elements.
   149  // Contrary to NextElement this will not cause allocations.
   150  func (o *Object) NextElementBytes(dst *Iter) (name []byte, t Type, err error) {
   151  	if o.off >= len(o.tape.Tape) {
   152  		return nil, TypeNone, nil
   153  	}
   154  	// Advance must be string or end of object
   155  	v := o.tape.Tape[o.off]
   156  	switch Tag(v >> 56) {
   157  	case TagString:
   158  		// Read name:
   159  		// We want name and at least one value.
   160  		if o.off+2 >= len(o.tape.Tape) {
   161  			return nil, TypeNone, fmt.Errorf("parsing object element name: unexpected end of tape")
   162  		}
   163  		length := o.tape.Tape[o.off+1]
   164  		offset := v & JSONVALUEMASK
   165  		name, err = o.tape.stringByteAt(offset, length)
   166  		if err != nil {
   167  			return nil, TypeNone, fmt.Errorf("parsing object element name: %w", err)
   168  		}
   169  		o.off += 2
   170  	case TagObjectEnd:
   171  		return nil, TypeNone, nil
   172  	default:
   173  		return nil, TypeNone, fmt.Errorf("object: unexpected tag %c", byte(v>>56))
   174  	}
   175  
   176  	// Read element type
   177  	v = o.tape.Tape[o.off]
   178  	// Move to value (if any)
   179  	o.off++
   180  
   181  	// Set dst
   182  	dst.cur = v & JSONVALUEMASK
   183  	dst.t = Tag(v >> 56)
   184  	dst.off = o.off
   185  	dst.tape = o.tape
   186  	dst.calcNext(false)
   187  	elemSize := dst.addNext
   188  	dst.calcNext(true)
   189  	if dst.off+elemSize > len(dst.tape.Tape) {
   190  		return nil, TypeNone, errors.New("element extends beyond tape")
   191  	}
   192  	dst.tape.Tape = dst.tape.Tape[:dst.off+elemSize]
   193  
   194  	// Skip to next element
   195  	o.off += elemSize
   196  	return name, TagToType[dst.t], nil
   197  }
   198  
   199  // Element represents an element in an object.
   200  type Element struct {
   201  	// Name of the element
   202  	Name string
   203  	// Type of the element
   204  	Type Type
   205  	// Iter containing the element
   206  	Iter Iter
   207  }
   208  
   209  // Elements contains all elements in an object
   210  // kept in original order.
   211  // And index contains lookup for object keys.
   212  type Elements struct {
   213  	Elements []Element
   214  	Index    map[string]int
   215  }
   216  
   217  // Lookup a key in elements and return the element.
   218  // Returns nil if key doesn't exist.
   219  // Keys are case sensitive.
   220  func (e Elements) Lookup(key string) *Element {
   221  	idx, ok := e.Index[key]
   222  	if !ok {
   223  		return nil
   224  	}
   225  	return &e.Elements[idx]
   226  }
   227  
   228  // MarshalJSON will marshal the entire remaining scope of the iterator.
   229  func (e Elements) MarshalJSON() ([]byte, error) {
   230  	return e.MarshalJSONBuffer(nil)
   231  }
   232  
   233  // MarshalJSONBuffer will marshal all elements.
   234  // An optional buffer can be provided for fewer allocations.
   235  // Output will be appended to the destination.
   236  func (e Elements) MarshalJSONBuffer(dst []byte) ([]byte, error) {
   237  	dst = append(dst, '{')
   238  	for i, elem := range e.Elements {
   239  		dst = append(dst, '"')
   240  		dst = escapeBytes(dst, []byte(elem.Name))
   241  		dst = append(dst, '"', ':')
   242  		var err error
   243  		dst, err = elem.Iter.MarshalJSONBuffer(dst)
   244  		if err != nil {
   245  			return nil, err
   246  		}
   247  		if i < len(e.Elements)-1 {
   248  			dst = append(dst, ',')
   249  		}
   250  	}
   251  	dst = append(dst, '}')
   252  	return dst, nil
   253  }