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