...

Source file src/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/document.go

Documentation: go.mongodb.org/mongo-driver/x/bsonx/bsoncore

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package bsoncore
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"go.mongodb.org/mongo-driver/bson/bsontype"
    17  )
    18  
    19  // ValidationError is an error type returned when attempting to validate a document or array.
    20  type ValidationError string
    21  
    22  func (ve ValidationError) Error() string { return string(ve) }
    23  
    24  // NewDocumentLengthError creates and returns an error for when the length of a document exceeds the
    25  // bytes available.
    26  func NewDocumentLengthError(length, rem int) error {
    27  	return lengthError("document", length, rem)
    28  }
    29  
    30  func lengthError(bufferType string, length, rem int) error {
    31  	return ValidationError(fmt.Sprintf("%v length exceeds available bytes. length=%d remainingBytes=%d",
    32  		bufferType, length, rem))
    33  }
    34  
    35  // InsufficientBytesError indicates that there were not enough bytes to read the next component.
    36  type InsufficientBytesError struct {
    37  	Source    []byte
    38  	Remaining []byte
    39  }
    40  
    41  // NewInsufficientBytesError creates a new InsufficientBytesError with the given Document and
    42  // remaining bytes.
    43  func NewInsufficientBytesError(src, rem []byte) InsufficientBytesError {
    44  	return InsufficientBytesError{Source: src, Remaining: rem}
    45  }
    46  
    47  // Error implements the error interface.
    48  func (ibe InsufficientBytesError) Error() string {
    49  	return "too few bytes to read next component"
    50  }
    51  
    52  // Equal checks that err2 also is an ErrTooSmall.
    53  func (ibe InsufficientBytesError) Equal(err2 error) bool {
    54  	switch err2.(type) {
    55  	case InsufficientBytesError:
    56  		return true
    57  	default:
    58  		return false
    59  	}
    60  }
    61  
    62  // InvalidDepthTraversalError is returned when attempting a recursive Lookup when one component of
    63  // the path is neither an embedded document nor an array.
    64  type InvalidDepthTraversalError struct {
    65  	Key  string
    66  	Type bsontype.Type
    67  }
    68  
    69  func (idte InvalidDepthTraversalError) Error() string {
    70  	return fmt.Sprintf(
    71  		"attempt to traverse into %s, but it's type is %s, not %s nor %s",
    72  		idte.Key, idte.Type, bsontype.EmbeddedDocument, bsontype.Array,
    73  	)
    74  }
    75  
    76  // ErrMissingNull is returned when a document or array's last byte is not null.
    77  const ErrMissingNull ValidationError = "document or array end is missing null byte"
    78  
    79  // ErrInvalidLength indicates that a length in a binary representation of a BSON document or array
    80  // is invalid.
    81  const ErrInvalidLength ValidationError = "document or array length is invalid"
    82  
    83  // ErrNilReader indicates that an operation was attempted on a nil io.Reader.
    84  var ErrNilReader = errors.New("nil reader")
    85  
    86  // ErrEmptyKey indicates that no key was provided to a Lookup method.
    87  var ErrEmptyKey = errors.New("empty key provided")
    88  
    89  // ErrElementNotFound indicates that an Element matching a certain condition does not exist.
    90  var ErrElementNotFound = errors.New("element not found")
    91  
    92  // ErrOutOfBounds indicates that an index provided to access something was invalid.
    93  var ErrOutOfBounds = errors.New("out of bounds")
    94  
    95  // Document is a raw bytes representation of a BSON document.
    96  type Document []byte
    97  
    98  // NewDocumentFromReader reads a document from r. This function will only validate the length is
    99  // correct and that the document ends with a null byte.
   100  func NewDocumentFromReader(r io.Reader) (Document, error) {
   101  	return newBufferFromReader(r)
   102  }
   103  
   104  func newBufferFromReader(r io.Reader) ([]byte, error) {
   105  	if r == nil {
   106  		return nil, ErrNilReader
   107  	}
   108  
   109  	var lengthBytes [4]byte
   110  
   111  	// ReadFull guarantees that we will have read at least len(lengthBytes) if err == nil
   112  	_, err := io.ReadFull(r, lengthBytes[:])
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	length, _, _ := readi32(lengthBytes[:]) // ignore ok since we always have enough bytes to read a length
   118  	if length < 0 {
   119  		return nil, ErrInvalidLength
   120  	}
   121  	buffer := make([]byte, length)
   122  
   123  	copy(buffer, lengthBytes[:])
   124  
   125  	_, err = io.ReadFull(r, buffer[4:])
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	if buffer[length-1] != 0x00 {
   131  		return nil, ErrMissingNull
   132  	}
   133  
   134  	return buffer, nil
   135  }
   136  
   137  // Lookup searches the document, potentially recursively, for the given key. If there are multiple
   138  // keys provided, this method will recurse down, as long as the top and intermediate nodes are
   139  // either documents or arrays. If an error occurs or if the value doesn't exist, an empty Value is
   140  // returned.
   141  func (d Document) Lookup(key ...string) Value {
   142  	val, _ := d.LookupErr(key...)
   143  	return val
   144  }
   145  
   146  // LookupErr is the same as Lookup, except it returns an error in addition to an empty Value.
   147  func (d Document) LookupErr(key ...string) (Value, error) {
   148  	if len(key) < 1 {
   149  		return Value{}, ErrEmptyKey
   150  	}
   151  	length, rem, ok := ReadLength(d)
   152  	if !ok {
   153  		return Value{}, NewInsufficientBytesError(d, rem)
   154  	}
   155  
   156  	length -= 4
   157  
   158  	var elem Element
   159  	for length > 1 {
   160  		elem, rem, ok = ReadElement(rem)
   161  		length -= int32(len(elem))
   162  		if !ok {
   163  			return Value{}, NewInsufficientBytesError(d, rem)
   164  		}
   165  		// We use `KeyBytes` rather than `Key` to avoid a needless string alloc.
   166  		if string(elem.KeyBytes()) != key[0] {
   167  			continue
   168  		}
   169  		if len(key) > 1 {
   170  			tt := bsontype.Type(elem[0])
   171  			switch tt {
   172  			case bsontype.EmbeddedDocument:
   173  				val, err := elem.Value().Document().LookupErr(key[1:]...)
   174  				if err != nil {
   175  					return Value{}, err
   176  				}
   177  				return val, nil
   178  			case bsontype.Array:
   179  				// Convert to Document to continue Lookup recursion.
   180  				val, err := Document(elem.Value().Array()).LookupErr(key[1:]...)
   181  				if err != nil {
   182  					return Value{}, err
   183  				}
   184  				return val, nil
   185  			default:
   186  				return Value{}, InvalidDepthTraversalError{Key: elem.Key(), Type: tt}
   187  			}
   188  		}
   189  		return elem.ValueErr()
   190  	}
   191  	return Value{}, ErrElementNotFound
   192  }
   193  
   194  // Index searches for and retrieves the element at the given index. This method will panic if
   195  // the document is invalid or if the index is out of bounds.
   196  func (d Document) Index(index uint) Element {
   197  	elem, err := d.IndexErr(index)
   198  	if err != nil {
   199  		panic(err)
   200  	}
   201  	return elem
   202  }
   203  
   204  // IndexErr searches for and retrieves the element at the given index.
   205  func (d Document) IndexErr(index uint) (Element, error) {
   206  	return indexErr(d, index)
   207  }
   208  
   209  func indexErr(b []byte, index uint) (Element, error) {
   210  	length, rem, ok := ReadLength(b)
   211  	if !ok {
   212  		return nil, NewInsufficientBytesError(b, rem)
   213  	}
   214  
   215  	length -= 4
   216  
   217  	var current uint
   218  	var elem Element
   219  	for length > 1 {
   220  		elem, rem, ok = ReadElement(rem)
   221  		length -= int32(len(elem))
   222  		if !ok {
   223  			return nil, NewInsufficientBytesError(b, rem)
   224  		}
   225  		if current != index {
   226  			current++
   227  			continue
   228  		}
   229  		return elem, nil
   230  	}
   231  	return nil, ErrOutOfBounds
   232  }
   233  
   234  // DebugString outputs a human readable version of Document. It will attempt to stringify the
   235  // valid components of the document even if the entire document is not valid.
   236  func (d Document) DebugString() string {
   237  	if len(d) < 5 {
   238  		return "<malformed>"
   239  	}
   240  	var buf strings.Builder
   241  	buf.WriteString("Document")
   242  	length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length
   243  	buf.WriteByte('(')
   244  	buf.WriteString(strconv.Itoa(int(length)))
   245  	length -= 4
   246  	buf.WriteString("){")
   247  	var elem Element
   248  	var ok bool
   249  	for length > 1 {
   250  		elem, rem, ok = ReadElement(rem)
   251  		length -= int32(len(elem))
   252  		if !ok {
   253  			buf.WriteString(fmt.Sprintf("<malformed (%d)>", length))
   254  			break
   255  		}
   256  		buf.WriteString(elem.DebugString())
   257  	}
   258  	buf.WriteByte('}')
   259  
   260  	return buf.String()
   261  }
   262  
   263  // String outputs an ExtendedJSON version of Document. If the document is not valid, this method
   264  // returns an empty string.
   265  func (d Document) String() string {
   266  	if len(d) < 5 {
   267  		return ""
   268  	}
   269  	var buf strings.Builder
   270  	buf.WriteByte('{')
   271  
   272  	length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length
   273  
   274  	length -= 4
   275  
   276  	var elem Element
   277  	var ok bool
   278  	first := true
   279  	for length > 1 {
   280  		if !first {
   281  			buf.WriteByte(',')
   282  		}
   283  		elem, rem, ok = ReadElement(rem)
   284  		length -= int32(len(elem))
   285  		if !ok {
   286  			return ""
   287  		}
   288  		buf.WriteString(elem.String())
   289  		first = false
   290  	}
   291  	buf.WriteByte('}')
   292  
   293  	return buf.String()
   294  }
   295  
   296  // Elements returns this document as a slice of elements. The returned slice will contain valid
   297  // elements. If the document is not valid, the elements up to the invalid point will be returned
   298  // along with an error.
   299  func (d Document) Elements() ([]Element, error) {
   300  	length, rem, ok := ReadLength(d)
   301  	if !ok {
   302  		return nil, NewInsufficientBytesError(d, rem)
   303  	}
   304  
   305  	length -= 4
   306  
   307  	var elem Element
   308  	var elems []Element
   309  	for length > 1 {
   310  		elem, rem, ok = ReadElement(rem)
   311  		length -= int32(len(elem))
   312  		if !ok {
   313  			return elems, NewInsufficientBytesError(d, rem)
   314  		}
   315  		if err := elem.Validate(); err != nil {
   316  			return elems, err
   317  		}
   318  		elems = append(elems, elem)
   319  	}
   320  	return elems, nil
   321  }
   322  
   323  // Values returns this document as a slice of values. The returned slice will contain valid values.
   324  // If the document is not valid, the values up to the invalid point will be returned along with an
   325  // error.
   326  func (d Document) Values() ([]Value, error) {
   327  	return values(d)
   328  }
   329  
   330  func values(b []byte) ([]Value, error) {
   331  	length, rem, ok := ReadLength(b)
   332  	if !ok {
   333  		return nil, NewInsufficientBytesError(b, rem)
   334  	}
   335  
   336  	length -= 4
   337  
   338  	var elem Element
   339  	var vals []Value
   340  	for length > 1 {
   341  		elem, rem, ok = ReadElement(rem)
   342  		length -= int32(len(elem))
   343  		if !ok {
   344  			return vals, NewInsufficientBytesError(b, rem)
   345  		}
   346  		if err := elem.Value().Validate(); err != nil {
   347  			return vals, err
   348  		}
   349  		vals = append(vals, elem.Value())
   350  	}
   351  	return vals, nil
   352  }
   353  
   354  // Validate validates the document and ensures the elements contained within are valid.
   355  func (d Document) Validate() error {
   356  	length, rem, ok := ReadLength(d)
   357  	if !ok {
   358  		return NewInsufficientBytesError(d, rem)
   359  	}
   360  	if int(length) > len(d) {
   361  		return NewDocumentLengthError(int(length), len(d))
   362  	}
   363  	if d[length-1] != 0x00 {
   364  		return ErrMissingNull
   365  	}
   366  
   367  	length -= 4
   368  	var elem Element
   369  
   370  	for length > 1 {
   371  		elem, rem, ok = ReadElement(rem)
   372  		length -= int32(len(elem))
   373  		if !ok {
   374  			return NewInsufficientBytesError(d, rem)
   375  		}
   376  		err := elem.Validate()
   377  		if err != nil {
   378  			return err
   379  		}
   380  	}
   381  
   382  	if len(rem) < 1 || rem[0] != 0x00 {
   383  		return ErrMissingNull
   384  	}
   385  	return nil
   386  }
   387  

View as plain text