432 lines
9.0 KiB
Go
432 lines
9.0 KiB
Go
// Copyright (C) MongoDB, Inc. 2017-present.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
package bson
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"gitea.psichedelico.com/go/bson/x/bsonx/bsoncore"
|
|
)
|
|
|
|
// copyDocument handles copying one document from the src to the dst.
|
|
func copyDocument(dst ValueWriter, src ValueReader) error {
|
|
dr, err := src.ReadDocument()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dw, err := dst.WriteDocument()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return copyDocumentCore(dw, dr)
|
|
}
|
|
|
|
// copyArrayFromBytes copies the values from a BSON array represented as a
|
|
// []byte to a ValueWriter.
|
|
func copyArrayFromBytes(dst ValueWriter, src []byte) error {
|
|
aw, err := dst.WriteArray()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = copyBytesToArrayWriter(aw, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return aw.WriteArrayEnd()
|
|
}
|
|
|
|
// copyDocumentFromBytes copies the values from a BSON document represented as a
|
|
// []byte to a ValueWriter.
|
|
func copyDocumentFromBytes(dst ValueWriter, src []byte) error {
|
|
dw, err := dst.WriteDocument()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = copyBytesToDocumentWriter(dw, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return dw.WriteDocumentEnd()
|
|
}
|
|
|
|
type writeElementFn func(key string) (ValueWriter, error)
|
|
|
|
// copyBytesToArrayWriter copies the values from a BSON Array represented as a []byte to an
|
|
// ArrayWriter.
|
|
func copyBytesToArrayWriter(dst ArrayWriter, src []byte) error {
|
|
wef := func(_ string) (ValueWriter, error) {
|
|
return dst.WriteArrayElement()
|
|
}
|
|
|
|
return copyBytesToValueWriter(src, wef)
|
|
}
|
|
|
|
// copyBytesToDocumentWriter copies the values from a BSON document represented as a []byte to a
|
|
// DocumentWriter.
|
|
func copyBytesToDocumentWriter(dst DocumentWriter, src []byte) error {
|
|
wef := func(key string) (ValueWriter, error) {
|
|
return dst.WriteDocumentElement(key)
|
|
}
|
|
|
|
return copyBytesToValueWriter(src, wef)
|
|
}
|
|
|
|
func copyBytesToValueWriter(src []byte, wef writeElementFn) error {
|
|
// TODO(skriptble): Create errors types here. Anything that is a tag should be a property.
|
|
length, rem, ok := bsoncore.ReadLength(src)
|
|
if !ok {
|
|
return fmt.Errorf("couldn't read length from src, not enough bytes. length=%d", len(src))
|
|
}
|
|
if len(src) < int(length) {
|
|
return fmt.Errorf("length read exceeds number of bytes available. length=%d bytes=%d", len(src), length)
|
|
}
|
|
rem = rem[:length-4]
|
|
|
|
var t bsoncore.Type
|
|
var key string
|
|
var val bsoncore.Value
|
|
for {
|
|
t, rem, ok = bsoncore.ReadType(rem)
|
|
if !ok {
|
|
return io.EOF
|
|
}
|
|
if t == bsoncore.Type(0) {
|
|
if len(rem) != 0 {
|
|
return fmt.Errorf("document end byte found before end of document. remaining bytes=%v", rem)
|
|
}
|
|
break
|
|
}
|
|
|
|
key, rem, ok = bsoncore.ReadKey(rem)
|
|
if !ok {
|
|
return fmt.Errorf("invalid key found. remaining bytes=%v", rem)
|
|
}
|
|
|
|
// write as either array element or document element using writeElementFn
|
|
vw, err := wef(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
val, rem, ok = bsoncore.ReadValue(rem, t)
|
|
if !ok {
|
|
return fmt.Errorf("not enough bytes available to read type. bytes=%d type=%s", len(rem), t)
|
|
}
|
|
err = copyValueFromBytes(vw, Type(t), val.Data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// copyDocumentToBytes copies an entire document from the ValueReader and
|
|
// returns it as bytes.
|
|
func copyDocumentToBytes(src ValueReader) ([]byte, error) {
|
|
return appendDocumentBytes(nil, src)
|
|
}
|
|
|
|
// appendDocumentBytes functions the same as CopyDocumentToBytes, but will
|
|
// append the result to dst.
|
|
func appendDocumentBytes(dst []byte, src ValueReader) ([]byte, error) {
|
|
if br, ok := src.(bytesReader); ok {
|
|
_, dst, err := br.readValueBytes(dst)
|
|
return dst, err
|
|
}
|
|
|
|
vw := vwPool.Get().(*valueWriter)
|
|
defer putValueWriter(vw)
|
|
|
|
vw.reset(dst)
|
|
|
|
err := copyDocument(vw, src)
|
|
dst = vw.buf
|
|
return dst, err
|
|
}
|
|
|
|
// appendArrayBytes copies an array from the ValueReader to dst.
|
|
func appendArrayBytes(dst []byte, src ValueReader) ([]byte, error) {
|
|
if br, ok := src.(bytesReader); ok {
|
|
_, dst, err := br.readValueBytes(dst)
|
|
return dst, err
|
|
}
|
|
|
|
vw := vwPool.Get().(*valueWriter)
|
|
defer putValueWriter(vw)
|
|
|
|
vw.reset(dst)
|
|
|
|
err := copyArray(vw, src)
|
|
dst = vw.buf
|
|
return dst, err
|
|
}
|
|
|
|
// copyValueFromBytes will write the value represtend by t and src to dst.
|
|
func copyValueFromBytes(dst ValueWriter, t Type, src []byte) error {
|
|
if wvb, ok := dst.(bytesWriter); ok {
|
|
return wvb.writeValueBytes(t, src)
|
|
}
|
|
|
|
vr := newDocumentReader(bytes.NewReader(src))
|
|
vr.pushElement(t)
|
|
|
|
return copyValue(dst, vr)
|
|
}
|
|
|
|
// copyValueToBytes copies a value from src and returns it as a Type and a
|
|
// []byte.
|
|
func copyValueToBytes(src ValueReader) (Type, []byte, error) {
|
|
if br, ok := src.(bytesReader); ok {
|
|
return br.readValueBytes(nil)
|
|
}
|
|
|
|
vw := vwPool.Get().(*valueWriter)
|
|
defer putValueWriter(vw)
|
|
|
|
vw.reset(nil)
|
|
vw.push(mElement)
|
|
|
|
err := copyValue(vw, src)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
|
|
return Type(vw.buf[0]), vw.buf[2:], nil
|
|
}
|
|
|
|
// copyValue will copy a single value from src to dst.
|
|
func copyValue(dst ValueWriter, src ValueReader) error {
|
|
var err error
|
|
switch src.Type() {
|
|
case TypeDouble:
|
|
var f64 float64
|
|
f64, err = src.ReadDouble()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteDouble(f64)
|
|
case TypeString:
|
|
var str string
|
|
str, err = src.ReadString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = dst.WriteString(str)
|
|
case TypeEmbeddedDocument:
|
|
err = copyDocument(dst, src)
|
|
case TypeArray:
|
|
err = copyArray(dst, src)
|
|
case TypeBinary:
|
|
var data []byte
|
|
var subtype byte
|
|
data, subtype, err = src.ReadBinary()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteBinaryWithSubtype(data, subtype)
|
|
case TypeUndefined:
|
|
err = src.ReadUndefined()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteUndefined()
|
|
case TypeObjectID:
|
|
var oid ObjectID
|
|
oid, err = src.ReadObjectID()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteObjectID(oid)
|
|
case TypeBoolean:
|
|
var b bool
|
|
b, err = src.ReadBoolean()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteBoolean(b)
|
|
case TypeDateTime:
|
|
var dt int64
|
|
dt, err = src.ReadDateTime()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteDateTime(dt)
|
|
case TypeNull:
|
|
err = src.ReadNull()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteNull()
|
|
case TypeRegex:
|
|
var pattern, options string
|
|
pattern, options, err = src.ReadRegex()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteRegex(pattern, options)
|
|
case TypeDBPointer:
|
|
var ns string
|
|
var pointer ObjectID
|
|
ns, pointer, err = src.ReadDBPointer()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteDBPointer(ns, pointer)
|
|
case TypeJavaScript:
|
|
var js string
|
|
js, err = src.ReadJavascript()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteJavascript(js)
|
|
case TypeSymbol:
|
|
var symbol string
|
|
symbol, err = src.ReadSymbol()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteSymbol(symbol)
|
|
case TypeCodeWithScope:
|
|
var code string
|
|
var srcScope DocumentReader
|
|
code, srcScope, err = src.ReadCodeWithScope()
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
var dstScope DocumentWriter
|
|
dstScope, err = dst.WriteCodeWithScope(code)
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = copyDocumentCore(dstScope, srcScope)
|
|
case TypeInt32:
|
|
var i32 int32
|
|
i32, err = src.ReadInt32()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteInt32(i32)
|
|
case TypeTimestamp:
|
|
var t, i uint32
|
|
t, i, err = src.ReadTimestamp()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteTimestamp(t, i)
|
|
case TypeInt64:
|
|
var i64 int64
|
|
i64, err = src.ReadInt64()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteInt64(i64)
|
|
case TypeDecimal128:
|
|
var d128 Decimal128
|
|
d128, err = src.ReadDecimal128()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteDecimal128(d128)
|
|
case TypeMinKey:
|
|
err = src.ReadMinKey()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteMinKey()
|
|
case TypeMaxKey:
|
|
err = src.ReadMaxKey()
|
|
if err != nil {
|
|
break
|
|
}
|
|
err = dst.WriteMaxKey()
|
|
default:
|
|
err = fmt.Errorf("cannot copy unknown BSON type %s", src.Type())
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func copyArray(dst ValueWriter, src ValueReader) error {
|
|
ar, err := src.ReadArray()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
aw, err := dst.WriteArray()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
vr, err := ar.ReadValue()
|
|
if errors.Is(err, ErrEOA) {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
vw, err := aw.WriteArrayElement()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = copyValue(vw, vr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return aw.WriteArrayEnd()
|
|
}
|
|
|
|
func copyDocumentCore(dw DocumentWriter, dr DocumentReader) error {
|
|
for {
|
|
key, vr, err := dr.ReadElement()
|
|
if errors.Is(err, ErrEOD) {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
vw, err := dw.WriteDocumentElement(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = copyValue(vw, vr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return dw.WriteDocumentEnd()
|
|
}
|
|
|
|
// bytesReader is the interface used to read BSON bytes from a valueReader.
|
|
//
|
|
// The bytes of the value will be appended to dst.
|
|
type bytesReader interface {
|
|
readValueBytes(dst []byte) (Type, []byte, error)
|
|
}
|
|
|
|
// bytesWriter is the interface used to write BSON bytes to a valueWriter.
|
|
type bytesWriter interface {
|
|
writeValueBytes(t Type, b []byte) error
|
|
}
|