diff options
Diffstat (limited to 'vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go')
-rw-r--r-- | vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go new file mode 100644 index 000000000..99ed524b7 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_writer.go @@ -0,0 +1,732 @@ +// 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 bsonrw + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "math" + "sort" + "strconv" + "strings" + "sync" + "time" + "unicode/utf8" + + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// ExtJSONValueWriterPool is a pool for ExtJSON ValueWriters. +type ExtJSONValueWriterPool struct { + pool sync.Pool +} + +// NewExtJSONValueWriterPool creates a new pool for ValueWriter instances that write to ExtJSON. +func NewExtJSONValueWriterPool() *ExtJSONValueWriterPool { + return &ExtJSONValueWriterPool{ + pool: sync.Pool{ + New: func() interface{} { + return new(extJSONValueWriter) + }, + }, + } +} + +// Get retrieves a ExtJSON ValueWriter from the pool and resets it to use w as the destination. +func (bvwp *ExtJSONValueWriterPool) Get(w io.Writer, canonical, escapeHTML bool) ValueWriter { + vw := bvwp.pool.Get().(*extJSONValueWriter) + if writer, ok := w.(*SliceWriter); ok { + vw.reset(*writer, canonical, escapeHTML) + vw.w = writer + return vw + } + vw.buf = vw.buf[:0] + vw.w = w + return vw +} + +// Put inserts a ValueWriter into the pool. If the ValueWriter is not a ExtJSON ValueWriter, nothing +// happens and ok will be false. +func (bvwp *ExtJSONValueWriterPool) Put(vw ValueWriter) (ok bool) { + bvw, ok := vw.(*extJSONValueWriter) + if !ok { + return false + } + + if _, ok := bvw.w.(*SliceWriter); ok { + bvw.buf = nil + } + bvw.w = nil + + bvwp.pool.Put(bvw) + return true +} + +type ejvwState struct { + mode mode +} + +type extJSONValueWriter struct { + w io.Writer + buf []byte + + stack []ejvwState + frame int64 + canonical bool + escapeHTML bool +} + +// NewExtJSONValueWriter creates a ValueWriter that writes Extended JSON to w. +func NewExtJSONValueWriter(w io.Writer, canonical, escapeHTML bool) (ValueWriter, error) { + if w == nil { + return nil, errNilWriter + } + + return newExtJSONWriter(w, canonical, escapeHTML), nil +} + +func newExtJSONWriter(w io.Writer, canonical, escapeHTML bool) *extJSONValueWriter { + stack := make([]ejvwState, 1, 5) + stack[0] = ejvwState{mode: mTopLevel} + + return &extJSONValueWriter{ + w: w, + buf: []byte{}, + stack: stack, + canonical: canonical, + escapeHTML: escapeHTML, + } +} + +func newExtJSONWriterFromSlice(buf []byte, canonical, escapeHTML bool) *extJSONValueWriter { + stack := make([]ejvwState, 1, 5) + stack[0] = ejvwState{mode: mTopLevel} + + return &extJSONValueWriter{ + buf: buf, + stack: stack, + canonical: canonical, + escapeHTML: escapeHTML, + } +} + +func (ejvw *extJSONValueWriter) reset(buf []byte, canonical, escapeHTML bool) { + if ejvw.stack == nil { + ejvw.stack = make([]ejvwState, 1, 5) + } + + ejvw.stack = ejvw.stack[:1] + ejvw.stack[0] = ejvwState{mode: mTopLevel} + ejvw.canonical = canonical + ejvw.escapeHTML = escapeHTML + ejvw.frame = 0 + ejvw.buf = buf + ejvw.w = nil +} + +func (ejvw *extJSONValueWriter) advanceFrame() { + if ejvw.frame+1 >= int64(len(ejvw.stack)) { // We need to grow the stack + length := len(ejvw.stack) + if length+1 >= cap(ejvw.stack) { + // double it + buf := make([]ejvwState, 2*cap(ejvw.stack)+1) + copy(buf, ejvw.stack) + ejvw.stack = buf + } + ejvw.stack = ejvw.stack[:length+1] + } + ejvw.frame++ +} + +func (ejvw *extJSONValueWriter) push(m mode) { + ejvw.advanceFrame() + + ejvw.stack[ejvw.frame].mode = m +} + +func (ejvw *extJSONValueWriter) pop() { + switch ejvw.stack[ejvw.frame].mode { + case mElement, mValue: + ejvw.frame-- + case mDocument, mArray, mCodeWithScope: + ejvw.frame -= 2 // we pop twice to jump over the mElement: mDocument -> mElement -> mDocument/mTopLevel/etc... + } +} + +func (ejvw *extJSONValueWriter) invalidTransitionErr(destination mode, name string, modes []mode) error { + te := TransitionError{ + name: name, + current: ejvw.stack[ejvw.frame].mode, + destination: destination, + modes: modes, + action: "write", + } + if ejvw.frame != 0 { + te.parent = ejvw.stack[ejvw.frame-1].mode + } + return te +} + +func (ejvw *extJSONValueWriter) ensureElementValue(destination mode, callerName string, addmodes ...mode) error { + switch ejvw.stack[ejvw.frame].mode { + case mElement, mValue: + default: + modes := []mode{mElement, mValue} + if addmodes != nil { + modes = append(modes, addmodes...) + } + return ejvw.invalidTransitionErr(destination, callerName, modes) + } + + return nil +} + +func (ejvw *extJSONValueWriter) writeExtendedSingleValue(key string, value string, quotes bool) { + var s string + if quotes { + s = fmt.Sprintf(`{"$%s":"%s"}`, key, value) + } else { + s = fmt.Sprintf(`{"$%s":%s}`, key, value) + } + + ejvw.buf = append(ejvw.buf, []byte(s)...) +} + +func (ejvw *extJSONValueWriter) WriteArray() (ArrayWriter, error) { + if err := ejvw.ensureElementValue(mArray, "WriteArray"); err != nil { + return nil, err + } + + ejvw.buf = append(ejvw.buf, '[') + + ejvw.push(mArray) + return ejvw, nil +} + +func (ejvw *extJSONValueWriter) WriteBinary(b []byte) error { + return ejvw.WriteBinaryWithSubtype(b, 0x00) +} + +func (ejvw *extJSONValueWriter) WriteBinaryWithSubtype(b []byte, btype byte) error { + if err := ejvw.ensureElementValue(mode(0), "WriteBinaryWithSubtype"); err != nil { + return err + } + + var buf bytes.Buffer + buf.WriteString(`{"$binary":{"base64":"`) + buf.WriteString(base64.StdEncoding.EncodeToString(b)) + buf.WriteString(fmt.Sprintf(`","subType":"%02x"}},`, btype)) + + ejvw.buf = append(ejvw.buf, buf.Bytes()...) + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteBoolean(b bool) error { + if err := ejvw.ensureElementValue(mode(0), "WriteBoolean"); err != nil { + return err + } + + ejvw.buf = append(ejvw.buf, []byte(strconv.FormatBool(b))...) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteCodeWithScope(code string) (DocumentWriter, error) { + if err := ejvw.ensureElementValue(mCodeWithScope, "WriteCodeWithScope"); err != nil { + return nil, err + } + + var buf bytes.Buffer + buf.WriteString(`{"$code":`) + writeStringWithEscapes(code, &buf, ejvw.escapeHTML) + buf.WriteString(`,"$scope":{`) + + ejvw.buf = append(ejvw.buf, buf.Bytes()...) + + ejvw.push(mCodeWithScope) + return ejvw, nil +} + +func (ejvw *extJSONValueWriter) WriteDBPointer(ns string, oid primitive.ObjectID) error { + if err := ejvw.ensureElementValue(mode(0), "WriteDBPointer"); err != nil { + return err + } + + var buf bytes.Buffer + buf.WriteString(`{"$dbPointer":{"$ref":"`) + buf.WriteString(ns) + buf.WriteString(`","$id":{"$oid":"`) + buf.WriteString(oid.Hex()) + buf.WriteString(`"}}},`) + + ejvw.buf = append(ejvw.buf, buf.Bytes()...) + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteDateTime(dt int64) error { + if err := ejvw.ensureElementValue(mode(0), "WriteDateTime"); err != nil { + return err + } + + t := time.Unix(dt/1e3, dt%1e3*1e6).UTC() + + if ejvw.canonical || t.Year() < 1970 || t.Year() > 9999 { + s := fmt.Sprintf(`{"$numberLong":"%d"}`, dt) + ejvw.writeExtendedSingleValue("date", s, false) + } else { + ejvw.writeExtendedSingleValue("date", t.Format(rfc3339Milli), true) + } + + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteDecimal128(d primitive.Decimal128) error { + if err := ejvw.ensureElementValue(mode(0), "WriteDecimal128"); err != nil { + return err + } + + ejvw.writeExtendedSingleValue("numberDecimal", d.String(), true) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteDocument() (DocumentWriter, error) { + if ejvw.stack[ejvw.frame].mode == mTopLevel { + ejvw.buf = append(ejvw.buf, '{') + return ejvw, nil + } + + if err := ejvw.ensureElementValue(mDocument, "WriteDocument", mTopLevel); err != nil { + return nil, err + } + + ejvw.buf = append(ejvw.buf, '{') + ejvw.push(mDocument) + return ejvw, nil +} + +func (ejvw *extJSONValueWriter) WriteDouble(f float64) error { + if err := ejvw.ensureElementValue(mode(0), "WriteDouble"); err != nil { + return err + } + + s := formatDouble(f) + + if ejvw.canonical { + ejvw.writeExtendedSingleValue("numberDouble", s, true) + } else { + switch s { + case "Infinity": + fallthrough + case "-Infinity": + fallthrough + case "NaN": + s = fmt.Sprintf(`{"$numberDouble":"%s"}`, s) + } + ejvw.buf = append(ejvw.buf, []byte(s)...) + } + + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteInt32(i int32) error { + if err := ejvw.ensureElementValue(mode(0), "WriteInt32"); err != nil { + return err + } + + s := strconv.FormatInt(int64(i), 10) + + if ejvw.canonical { + ejvw.writeExtendedSingleValue("numberInt", s, true) + } else { + ejvw.buf = append(ejvw.buf, []byte(s)...) + } + + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteInt64(i int64) error { + if err := ejvw.ensureElementValue(mode(0), "WriteInt64"); err != nil { + return err + } + + s := strconv.FormatInt(i, 10) + + if ejvw.canonical { + ejvw.writeExtendedSingleValue("numberLong", s, true) + } else { + ejvw.buf = append(ejvw.buf, []byte(s)...) + } + + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteJavascript(code string) error { + if err := ejvw.ensureElementValue(mode(0), "WriteJavascript"); err != nil { + return err + } + + var buf bytes.Buffer + writeStringWithEscapes(code, &buf, ejvw.escapeHTML) + + ejvw.writeExtendedSingleValue("code", buf.String(), false) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteMaxKey() error { + if err := ejvw.ensureElementValue(mode(0), "WriteMaxKey"); err != nil { + return err + } + + ejvw.writeExtendedSingleValue("maxKey", "1", false) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteMinKey() error { + if err := ejvw.ensureElementValue(mode(0), "WriteMinKey"); err != nil { + return err + } + + ejvw.writeExtendedSingleValue("minKey", "1", false) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteNull() error { + if err := ejvw.ensureElementValue(mode(0), "WriteNull"); err != nil { + return err + } + + ejvw.buf = append(ejvw.buf, []byte("null")...) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteObjectID(oid primitive.ObjectID) error { + if err := ejvw.ensureElementValue(mode(0), "WriteObjectID"); err != nil { + return err + } + + ejvw.writeExtendedSingleValue("oid", oid.Hex(), true) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteRegex(pattern string, options string) error { + if err := ejvw.ensureElementValue(mode(0), "WriteRegex"); err != nil { + return err + } + + var buf bytes.Buffer + buf.WriteString(`{"$regularExpression":{"pattern":`) + writeStringWithEscapes(pattern, &buf, ejvw.escapeHTML) + buf.WriteString(`,"options":"`) + buf.WriteString(sortStringAlphebeticAscending(options)) + buf.WriteString(`"}},`) + + ejvw.buf = append(ejvw.buf, buf.Bytes()...) + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteString(s string) error { + if err := ejvw.ensureElementValue(mode(0), "WriteString"); err != nil { + return err + } + + var buf bytes.Buffer + writeStringWithEscapes(s, &buf, ejvw.escapeHTML) + + ejvw.buf = append(ejvw.buf, buf.Bytes()...) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteSymbol(symbol string) error { + if err := ejvw.ensureElementValue(mode(0), "WriteSymbol"); err != nil { + return err + } + + var buf bytes.Buffer + writeStringWithEscapes(symbol, &buf, ejvw.escapeHTML) + + ejvw.writeExtendedSingleValue("symbol", buf.String(), false) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteTimestamp(t uint32, i uint32) error { + if err := ejvw.ensureElementValue(mode(0), "WriteTimestamp"); err != nil { + return err + } + + var buf bytes.Buffer + buf.WriteString(`{"$timestamp":{"t":`) + buf.WriteString(strconv.FormatUint(uint64(t), 10)) + buf.WriteString(`,"i":`) + buf.WriteString(strconv.FormatUint(uint64(i), 10)) + buf.WriteString(`}},`) + + ejvw.buf = append(ejvw.buf, buf.Bytes()...) + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteUndefined() error { + if err := ejvw.ensureElementValue(mode(0), "WriteUndefined"); err != nil { + return err + } + + ejvw.writeExtendedSingleValue("undefined", "true", false) + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteDocumentElement(key string) (ValueWriter, error) { + switch ejvw.stack[ejvw.frame].mode { + case mDocument, mTopLevel, mCodeWithScope: + var buf bytes.Buffer + writeStringWithEscapes(key, &buf, ejvw.escapeHTML) + + ejvw.buf = append(ejvw.buf, []byte(fmt.Sprintf(`%s:`, buf.String()))...) + ejvw.push(mElement) + default: + return nil, ejvw.invalidTransitionErr(mElement, "WriteDocumentElement", []mode{mDocument, mTopLevel, mCodeWithScope}) + } + + return ejvw, nil +} + +func (ejvw *extJSONValueWriter) WriteDocumentEnd() error { + switch ejvw.stack[ejvw.frame].mode { + case mDocument, mTopLevel, mCodeWithScope: + default: + return fmt.Errorf("incorrect mode to end document: %s", ejvw.stack[ejvw.frame].mode) + } + + // close the document + if ejvw.buf[len(ejvw.buf)-1] == ',' { + ejvw.buf[len(ejvw.buf)-1] = '}' + } else { + ejvw.buf = append(ejvw.buf, '}') + } + + switch ejvw.stack[ejvw.frame].mode { + case mCodeWithScope: + ejvw.buf = append(ejvw.buf, '}') + fallthrough + case mDocument: + ejvw.buf = append(ejvw.buf, ',') + case mTopLevel: + if ejvw.w != nil { + if _, err := ejvw.w.Write(ejvw.buf); err != nil { + return err + } + ejvw.buf = ejvw.buf[:0] + } + } + + ejvw.pop() + return nil +} + +func (ejvw *extJSONValueWriter) WriteArrayElement() (ValueWriter, error) { + switch ejvw.stack[ejvw.frame].mode { + case mArray: + ejvw.push(mValue) + default: + return nil, ejvw.invalidTransitionErr(mValue, "WriteArrayElement", []mode{mArray}) + } + + return ejvw, nil +} + +func (ejvw *extJSONValueWriter) WriteArrayEnd() error { + switch ejvw.stack[ejvw.frame].mode { + case mArray: + // close the array + if ejvw.buf[len(ejvw.buf)-1] == ',' { + ejvw.buf[len(ejvw.buf)-1] = ']' + } else { + ejvw.buf = append(ejvw.buf, ']') + } + + ejvw.buf = append(ejvw.buf, ',') + + ejvw.pop() + default: + return fmt.Errorf("incorrect mode to end array: %s", ejvw.stack[ejvw.frame].mode) + } + + return nil +} + +func formatDouble(f float64) string { + var s string + if math.IsInf(f, 1) { + s = "Infinity" + } else if math.IsInf(f, -1) { + s = "-Infinity" + } else if math.IsNaN(f) { + s = "NaN" + } else { + // Print exactly one decimalType place for integers; otherwise, print as many are necessary to + // perfectly represent it. + s = strconv.FormatFloat(f, 'G', -1, 64) + if !strings.ContainsRune(s, 'E') && !strings.ContainsRune(s, '.') { + s += ".0" + } + } + + return s +} + +var hexChars = "0123456789abcdef" + +func writeStringWithEscapes(s string, buf *bytes.Buffer, escapeHTML bool) { + buf.WriteByte('"') + start := 0 + for i := 0; i < len(s); { + if b := s[i]; b < utf8.RuneSelf { + if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) { + i++ + continue + } + if start < i { + buf.WriteString(s[start:i]) + } + switch b { + case '\\', '"': + buf.WriteByte('\\') + buf.WriteByte(b) + case '\n': + buf.WriteByte('\\') + buf.WriteByte('n') + case '\r': + buf.WriteByte('\\') + buf.WriteByte('r') + case '\t': + buf.WriteByte('\\') + buf.WriteByte('t') + case '\b': + buf.WriteByte('\\') + buf.WriteByte('b') + case '\f': + buf.WriteByte('\\') + buf.WriteByte('f') + default: + // This encodes bytes < 0x20 except for \t, \n and \r. + // If escapeHTML is set, it also escapes <, >, and & + // because they can lead to security holes when + // user-controlled strings are rendered into JSON + // and served to some browsers. + buf.WriteString(`\u00`) + buf.WriteByte(hexChars[b>>4]) + buf.WriteByte(hexChars[b&0xF]) + } + i++ + start = i + continue + } + c, size := utf8.DecodeRuneInString(s[i:]) + if c == utf8.RuneError && size == 1 { + if start < i { + buf.WriteString(s[start:i]) + } + buf.WriteString(`\ufffd`) + i += size + start = i + continue + } + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if c == '\u2028' || c == '\u2029' { + if start < i { + buf.WriteString(s[start:i]) + } + buf.WriteString(`\u202`) + buf.WriteByte(hexChars[c&0xF]) + i += size + start = i + continue + } + i += size + } + if start < len(s) { + buf.WriteString(s[start:]) + } + buf.WriteByte('"') +} + +type sortableString []rune + +func (ss sortableString) Len() int { + return len(ss) +} + +func (ss sortableString) Less(i, j int) bool { + return ss[i] < ss[j] +} + +func (ss sortableString) Swap(i, j int) { + oldI := ss[i] + ss[i] = ss[j] + ss[j] = oldI +} + +func sortStringAlphebeticAscending(s string) string { + ss := sortableString([]rune(s)) + sort.Sort(ss) + return string([]rune(ss)) +} |