slice_codec.go raw
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 bsoncodec
8
9 import (
10 "fmt"
11 "reflect"
12
13 "go.mongodb.org/mongo-driver/bson/bsonoptions"
14 "go.mongodb.org/mongo-driver/bson/bsonrw"
15 "go.mongodb.org/mongo-driver/bson/bsontype"
16 "go.mongodb.org/mongo-driver/bson/primitive"
17 )
18
19 var defaultSliceCodec = NewSliceCodec()
20
21 // SliceCodec is the Codec used for slice values.
22 //
23 // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the
24 // SliceCodec registered.
25 type SliceCodec struct {
26 // EncodeNilAsEmpty causes EncodeValue to marshal nil Go slices as empty BSON arrays instead of
27 // BSON null.
28 //
29 // Deprecated: Use bson.Encoder.NilSliceAsEmpty instead.
30 EncodeNilAsEmpty bool
31 }
32
33 // NewSliceCodec returns a MapCodec with options opts.
34 //
35 // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the
36 // SliceCodec registered.
37 func NewSliceCodec(opts ...*bsonoptions.SliceCodecOptions) *SliceCodec {
38 sliceOpt := bsonoptions.MergeSliceCodecOptions(opts...)
39
40 codec := SliceCodec{}
41 if sliceOpt.EncodeNilAsEmpty != nil {
42 codec.EncodeNilAsEmpty = *sliceOpt.EncodeNilAsEmpty
43 }
44 return &codec
45 }
46
47 // EncodeValue is the ValueEncoder for slice types.
48 func (sc SliceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
49 if !val.IsValid() || val.Kind() != reflect.Slice {
50 return ValueEncoderError{Name: "SliceEncodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val}
51 }
52
53 if val.IsNil() && !sc.EncodeNilAsEmpty && !ec.nilSliceAsEmpty {
54 return vw.WriteNull()
55 }
56
57 // If we have a []byte we want to treat it as a binary instead of as an array.
58 if val.Type().Elem() == tByte {
59 byteSlice := make([]byte, val.Len())
60 reflect.Copy(reflect.ValueOf(byteSlice), val)
61 return vw.WriteBinary(byteSlice)
62 }
63
64 // If we have a []primitive.E we want to treat it as a document instead of as an array.
65 if val.Type() == tD || val.Type().ConvertibleTo(tD) {
66 d := val.Convert(tD).Interface().(primitive.D)
67
68 dw, err := vw.WriteDocument()
69 if err != nil {
70 return err
71 }
72
73 for _, e := range d {
74 err = encodeElement(ec, dw, e)
75 if err != nil {
76 return err
77 }
78 }
79
80 return dw.WriteDocumentEnd()
81 }
82
83 aw, err := vw.WriteArray()
84 if err != nil {
85 return err
86 }
87
88 elemType := val.Type().Elem()
89 encoder, err := ec.LookupEncoder(elemType)
90 if err != nil && elemType.Kind() != reflect.Interface {
91 return err
92 }
93
94 for idx := 0; idx < val.Len(); idx++ {
95 currEncoder, currVal, lookupErr := defaultValueEncoders.lookupElementEncoder(ec, encoder, val.Index(idx))
96 if lookupErr != nil && lookupErr != errInvalidValue {
97 return lookupErr
98 }
99
100 vw, err := aw.WriteArrayElement()
101 if err != nil {
102 return err
103 }
104
105 if lookupErr == errInvalidValue {
106 err = vw.WriteNull()
107 if err != nil {
108 return err
109 }
110 continue
111 }
112
113 err = currEncoder.EncodeValue(ec, vw, currVal)
114 if err != nil {
115 return err
116 }
117 }
118 return aw.WriteArrayEnd()
119 }
120
121 // DecodeValue is the ValueDecoder for slice types.
122 func (sc *SliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
123 if !val.CanSet() || val.Kind() != reflect.Slice {
124 return ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val}
125 }
126
127 switch vrType := vr.Type(); vrType {
128 case bsontype.Array:
129 case bsontype.Null:
130 val.Set(reflect.Zero(val.Type()))
131 return vr.ReadNull()
132 case bsontype.Undefined:
133 val.Set(reflect.Zero(val.Type()))
134 return vr.ReadUndefined()
135 case bsontype.Type(0), bsontype.EmbeddedDocument:
136 if val.Type().Elem() != tE {
137 return fmt.Errorf("cannot decode document into %s", val.Type())
138 }
139 case bsontype.Binary:
140 if val.Type().Elem() != tByte {
141 return fmt.Errorf("SliceDecodeValue can only decode a binary into a byte array, got %v", vrType)
142 }
143 data, subtype, err := vr.ReadBinary()
144 if err != nil {
145 return err
146 }
147 if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld {
148 return fmt.Errorf("SliceDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v", bsontype.Binary, subtype)
149 }
150
151 if val.IsNil() {
152 val.Set(reflect.MakeSlice(val.Type(), 0, len(data)))
153 }
154 val.SetLen(0)
155 val.Set(reflect.AppendSlice(val, reflect.ValueOf(data)))
156 return nil
157 case bsontype.String:
158 if sliceType := val.Type().Elem(); sliceType != tByte {
159 return fmt.Errorf("SliceDecodeValue can only decode a string into a byte array, got %v", sliceType)
160 }
161 str, err := vr.ReadString()
162 if err != nil {
163 return err
164 }
165 byteStr := []byte(str)
166
167 if val.IsNil() {
168 val.Set(reflect.MakeSlice(val.Type(), 0, len(byteStr)))
169 }
170 val.SetLen(0)
171 val.Set(reflect.AppendSlice(val, reflect.ValueOf(byteStr)))
172 return nil
173 default:
174 return fmt.Errorf("cannot decode %v into a slice", vrType)
175 }
176
177 var elemsFunc func(DecodeContext, bsonrw.ValueReader, reflect.Value) ([]reflect.Value, error)
178 switch val.Type().Elem() {
179 case tE:
180 dc.Ancestor = val.Type()
181 elemsFunc = defaultValueDecoders.decodeD
182 default:
183 elemsFunc = defaultValueDecoders.decodeDefault
184 }
185
186 elems, err := elemsFunc(dc, vr, val)
187 if err != nil {
188 return err
189 }
190
191 if val.IsNil() {
192 val.Set(reflect.MakeSlice(val.Type(), 0, len(elems)))
193 }
194
195 val.SetLen(0)
196 val.Set(reflect.Append(val, elems...))
197
198 return nil
199 }
200