Don Newton | 379ae25 | 2019-04-01 12:17:06 -0400 | [diff] [blame^] | 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 | "bytes" |
| 11 | "fmt" |
| 12 | |
| 13 | "github.com/mongodb/mongo-go-driver/bson/bsontype" |
| 14 | ) |
| 15 | |
| 16 | // MalformedElementError represents a class of errors that RawElement methods return. |
| 17 | type MalformedElementError string |
| 18 | |
| 19 | func (mee MalformedElementError) Error() string { return string(mee) } |
| 20 | |
| 21 | // ErrElementMissingKey is returned when a RawElement is missing a key. |
| 22 | const ErrElementMissingKey MalformedElementError = "element is missing key" |
| 23 | |
| 24 | // ErrElementMissingType is returned when a RawElement is missing a type. |
| 25 | const ErrElementMissingType MalformedElementError = "element is missing type" |
| 26 | |
| 27 | // Element is a raw bytes representation of a BSON element. |
| 28 | type Element []byte |
| 29 | |
| 30 | // Key returns the key for this element. If the element is not valid, this method returns an empty |
| 31 | // string. If knowing if the element is valid is important, use KeyErr. |
| 32 | func (e Element) Key() string { |
| 33 | key, _ := e.KeyErr() |
| 34 | return key |
| 35 | } |
| 36 | |
| 37 | // KeyBytes returns the key for this element as a []byte. If the element is not valid, this method |
| 38 | // returns an empty string. If knowing if the element is valid is important, use KeyErr. This method |
| 39 | // will not include the null byte at the end of the key in the slice of bytes. |
| 40 | func (e Element) KeyBytes() []byte { |
| 41 | key, _ := e.KeyBytesErr() |
| 42 | return key |
| 43 | } |
| 44 | |
| 45 | // KeyErr returns the key for this element, returning an error if the element is not valid. |
| 46 | func (e Element) KeyErr() (string, error) { |
| 47 | key, err := e.KeyBytesErr() |
| 48 | return string(key), err |
| 49 | } |
| 50 | |
| 51 | // KeyBytesErr returns the key for this element as a []byte, returning an error if the element is |
| 52 | // not valid. |
| 53 | func (e Element) KeyBytesErr() ([]byte, error) { |
| 54 | if len(e) <= 0 { |
| 55 | return nil, ErrElementMissingType |
| 56 | } |
| 57 | idx := bytes.IndexByte(e[1:], 0x00) |
| 58 | if idx == -1 { |
| 59 | return nil, ErrElementMissingKey |
| 60 | } |
| 61 | return e[1 : idx+1], nil |
| 62 | } |
| 63 | |
| 64 | // Validate ensures the element is a valid BSON element. |
| 65 | func (e Element) Validate() error { |
| 66 | if len(e) < 1 { |
| 67 | return ErrElementMissingType |
| 68 | } |
| 69 | idx := bytes.IndexByte(e[1:], 0x00) |
| 70 | if idx == -1 { |
| 71 | return ErrElementMissingKey |
| 72 | } |
| 73 | return Value{Type: bsontype.Type(e[0]), Data: e[idx+2:]}.Validate() |
| 74 | } |
| 75 | |
| 76 | // CompareKey will compare this element's key to key. This method makes it easy to compare keys |
| 77 | // without needing to allocate a string. The key may be null terminated. If a valid key cannot be |
| 78 | // read this method will return false. |
| 79 | func (e Element) CompareKey(key []byte) bool { |
| 80 | if len(e) < 2 { |
| 81 | return false |
| 82 | } |
| 83 | idx := bytes.IndexByte(e[1:], 0x00) |
| 84 | if idx == -1 { |
| 85 | return false |
| 86 | } |
| 87 | if index := bytes.IndexByte(key, 0x00); index > -1 { |
| 88 | key = key[:index] |
| 89 | } |
| 90 | return bytes.Equal(e[1:idx+1], key) |
| 91 | } |
| 92 | |
| 93 | // Value returns the value of this element. If the element is not valid, this method returns an |
| 94 | // empty Value. If knowing if the element is valid is important, use ValueErr. |
| 95 | func (e Element) Value() Value { |
| 96 | val, _ := e.ValueErr() |
| 97 | return val |
| 98 | } |
| 99 | |
| 100 | // ValueErr returns the value for this element, returning an error if the element is not valid. |
| 101 | func (e Element) ValueErr() (Value, error) { |
| 102 | if len(e) <= 0 { |
| 103 | return Value{}, ErrElementMissingType |
| 104 | } |
| 105 | idx := bytes.IndexByte(e[1:], 0x00) |
| 106 | if idx == -1 { |
| 107 | return Value{}, ErrElementMissingKey |
| 108 | } |
| 109 | |
| 110 | val, rem, exists := ReadValue(e[idx+2:], bsontype.Type(e[0])) |
| 111 | if !exists { |
| 112 | return Value{}, NewInsufficientBytesError(e, rem) |
| 113 | } |
| 114 | return val, nil |
| 115 | } |
| 116 | |
| 117 | // String implements the fmt.String interface. The output will be in extended JSON format. |
| 118 | func (e Element) String() string { |
| 119 | if len(e) <= 0 { |
| 120 | return "" |
| 121 | } |
| 122 | t := bsontype.Type(e[0]) |
| 123 | idx := bytes.IndexByte(e[1:], 0x00) |
| 124 | if idx == -1 { |
| 125 | return "" |
| 126 | } |
| 127 | key, valBytes := []byte(e[1:idx+1]), []byte(e[idx+2:]) |
| 128 | val, _, valid := ReadValue(valBytes, t) |
| 129 | if !valid { |
| 130 | return "" |
| 131 | } |
| 132 | return fmt.Sprintf(`"%s": %v`, key, val) |
| 133 | } |
| 134 | |
| 135 | // DebugString outputs a human readable version of RawElement. It will attempt to stringify the |
| 136 | // valid components of the element even if the entire element is not valid. |
| 137 | func (e Element) DebugString() string { |
| 138 | if len(e) <= 0 { |
| 139 | return "<malformed>" |
| 140 | } |
| 141 | t := bsontype.Type(e[0]) |
| 142 | idx := bytes.IndexByte(e[1:], 0x00) |
| 143 | if idx == -1 { |
| 144 | return fmt.Sprintf(`bson.Element{[%s]<malformed>}`, t) |
| 145 | } |
| 146 | key, valBytes := []byte(e[1:idx+1]), []byte(e[idx+2:]) |
| 147 | val, _, valid := ReadValue(valBytes, t) |
| 148 | if !valid { |
| 149 | return fmt.Sprintf(`bson.Element{[%s]"%s": <malformed>}`, t, key) |
| 150 | } |
| 151 | return fmt.Sprintf(`bson.Element{[%s]"%s": %v}`, t, key, val) |
| 152 | } |