@@ -19,6 +19,7 @@ import (
1919 "encoding/base64"
2020 "fmt"
2121 "math/big"
22+ "net"
2223 "reflect"
2324 "strconv"
2425 "time"
@@ -90,7 +91,7 @@ var oidTag = []byte("objectId")
9091
9192func objectIdEncoder (ctx EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
9293 if ctx .Target != TargetCollection {
93- return dst , & UnsupportedValueError { Msg : "ObjectId is only supported for collections" }
94+ return dst , ctx . unsupportedValueError ( "ObjectId is only supported for collections" )
9495 }
9596
9697 return encodeDollarDatatype (dst , oidTag , func (dst []byte ) ([]byte , error ) {
@@ -103,7 +104,7 @@ func objectIdEncoder(ctx EncodeCtx, dst []byte, p unsafe.Pointer) ([]byte, error
103104
104105func objectIdDecoder (ctx DecodeCtx , src []byte , p unsafe.Pointer ) ([]byte , error ) {
105106 if ctx .Target != TargetCollection {
106- return src , & UnsupportedValueError { Msg : "ObjectId is only supported for collections" }
107+ return src , ctx . unsupportedValueError ( src , "ObjectId is only supported for collections" )
107108 }
108109
109110 src , oid , err := parseDollarDatatype (ctx , src , oidTag , func (ctx DecodeCtx , b []byte ) ([]byte , datatypes.ObjectId , error ) {
@@ -179,7 +180,7 @@ func timeDecoder(ctx DecodeCtx, src []byte, p unsafe.Pointer) ([]byte, error) {
179180
180181func dateOnlyEncoder (ctx EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
181182 if ctx .Target == TargetCollection {
182- return nil , & UnsupportedValueError { Msg : "DateOnly is not supported for collections" }
183+ return nil , ctx . unsupportedValueError ( "DateOnly is not supported for collections" )
183184 }
184185
185186 d := (* datatypes .DateOnly )(p )
@@ -191,7 +192,7 @@ func dateOnlyEncoder(ctx EncodeCtx, dst []byte, p unsafe.Pointer) ([]byte, error
191192
192193func dateOnlyDecoder (ctx DecodeCtx , src []byte , p unsafe.Pointer ) ([]byte , error ) {
193194 if ctx .Target == TargetCollection {
194- return src , & UnsupportedValueError { Msg : "DateOnly is not supported for collections" }
195+ return src , ctx . unsupportedValueError ( src , "DateOnly is not supported for collections" )
195196 }
196197
197198 srcAfter , str , _ , err := parseStringUnquote (ctx , src )
@@ -214,7 +215,7 @@ func dateOnlyDecoder(ctx DecodeCtx, src []byte, p unsafe.Pointer) ([]byte, error
214215
215216func timeOnlyEncoder (ctx EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
216217 if ctx .Target == TargetCollection {
217- return nil , & UnsupportedValueError { Msg : "TimeOnly is not supported for collections" }
218+ return nil , ctx . unsupportedValueError ( "TimeOnly is not supported for collections" )
218219 }
219220
220221 t := (* datatypes .TimeOnly )(p )
@@ -226,7 +227,7 @@ func timeOnlyEncoder(ctx EncodeCtx, dst []byte, p unsafe.Pointer) ([]byte, error
226227
227228func timeOnlyDecoder (ctx DecodeCtx , src []byte , p unsafe.Pointer ) ([]byte , error ) {
228229 if ctx .Target == TargetCollection {
229- return src , & UnsupportedValueError { Msg : "TimeOnly is not supported for collections" }
230+ return src , ctx . unsupportedValueError ( src , "TimeOnly is not supported for collections" )
230231 }
231232
232233 srcAfter , str , _ , err := parseStringUnquote (ctx , src )
@@ -355,27 +356,37 @@ func vectorDecoder(ctx DecodeCtx, src []byte, p unsafe.Pointer) ([]byte, error)
355356}
356357
357358// ================================
358- // | Binary data ([]byte) - encoded as {"$binary":"<base64>"} in all contexts,
359+ // | Binary data ([]byte) - encoded as {"$binary":"<base64>"} in tables and collections, and just "<base64>" otherwise
359360// | but can be decoded from either that format or from a base64 string
361+ // |
362+ // | NOTE: There's technically no reason to ever encode a []byte outside of encoding to a collection/table, but the
363+ // | special case just exists to help the fuzz testing pass
360364// ================================
361365
362- func binaryEncoder (_ EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
363- return encodeDollarDatatype (dst , []byte ("binary" ), func (dst []byte ) ([]byte , error ) {
364- return encodeBytesAsBase64 (dst , * (* []byte )(p )), nil
365- })
366+ func binaryEncoder (ctx EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
367+ if ctx .Target == TargetCollection || ctx .Target == TargetTable {
368+ return encodeDollarDatatype (dst , []byte ("binary" ), func (dst []byte ) ([]byte , error ) {
369+ return encodeBytesAsBase64 (dst , * (* []byte )(p )), nil
370+ })
371+ }
372+ return encodeBytesAsBase64 (dst , * (* []byte )(p )), nil
366373}
367374
368375func binaryDecoder (ctx DecodeCtx , src []byte , p unsafe.Pointer ) ([]byte , error ) {
369376 src = skipWS (src )
370377
378+ if b , ok := consumeNull (src ); ok {
379+ * (* []byte )(p ) = nil
380+ return b , nil
381+ }
382+
371383 if len (src ) != 0 && src [0 ] == '"' {
372- src , str , _ , err := parseStringUnquote (ctx , src )
373- if err != nil {
374- return src , err
375- }
384+ src , data , err := decodeBytesFromBase64 (ctx , src )
376385
377- * (* []byte )(p ) = str
378- return src , nil
386+ if err == nil {
387+ * (* []byte )(p ) = data
388+ }
389+ return src , err
379390 }
380391
381392 if len (src ) != 0 && src [0 ] == '{' {
@@ -444,7 +455,7 @@ func decodeBytesFromBase64(ctx DecodeCtx, src []byte) ([]byte, []byte, error) {
444455
445456func durationEncoder (ctx EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
446457 if ctx .Target == TargetCollection {
447- return nil , & UnsupportedValueError { Msg : "Duration is not supported for collections" }
458+ return nil , ctx . unsupportedValueError ( "Duration is not supported for collections" )
448459 }
449460 d := (* datatypes .Duration )(p )
450461 dst = append (dst , '"' )
@@ -455,7 +466,7 @@ func durationEncoder(ctx EncodeCtx, dst []byte, p unsafe.Pointer) ([]byte, error
455466
456467func durationDecoder (ctx DecodeCtx , src []byte , p unsafe.Pointer ) ([]byte , error ) {
457468 if ctx .Target == TargetCollection {
458- return src , & UnsupportedValueError { Msg : "Duration is not supported for collections" }
469+ return src , ctx . unsupportedValueError ( src , "Duration is not supported for collections" )
459470 }
460471 src , str , _ , err := parseStringUnquote (ctx , src )
461472 if err != nil {
@@ -469,6 +480,37 @@ func durationDecoder(ctx DecodeCtx, src []byte, p unsafe.Pointer) ([]byte, error
469480 return src , nil
470481}
471482
483+ // ================================
484+ // | net.IP - encoded as a plain quoted string in all contexts
485+ // ================================
486+
487+ func ipEncoder (ctx EncodeCtx , dst []byte , p unsafe.Pointer ) ([]byte , error ) {
488+ if ctx .Target == TargetCollection {
489+ return nil , ctx .unsupportedValueError ("net.IP is not supported for collections" )
490+ }
491+ ip := (* net .IP )(p )
492+ dst = append (dst , '"' )
493+ dst = append (dst , ip .String ()... )
494+ dst = append (dst , '"' )
495+ return dst , nil
496+ }
497+
498+ func ipDecoder (ctx DecodeCtx , src []byte , p unsafe.Pointer ) ([]byte , error ) {
499+ if ctx .Target == TargetCollection {
500+ return src , ctx .unsupportedValueError (src , "net.IP is not supported for collections" )
501+ }
502+ srcAfter , str , _ , err := parseStringUnquote (ctx , src )
503+ if err != nil {
504+ return srcAfter , err
505+ }
506+ ip := net .ParseIP (unsafeString (str ))
507+ if ip == nil {
508+ return srcAfter , ctx .syntaxError (src , "invalid IP string" )
509+ }
510+ * (* net .IP )(p ) = ip
511+ return srcAfter , nil
512+ }
513+
472514// ================================
473515// | Helpers
474516// ================================
0 commit comments