mirror of
https://github.com/wader/fq.git
synced 2024-12-28 08:02:28 +03:00
ddd7b0e439
bson: support all non-deprecated types and fix int/uint bugs
139 lines
4.2 KiB
Go
139 lines
4.2 KiB
Go
package bson
|
|
|
|
// https://bsonspec.org/spec.html
|
|
|
|
import (
|
|
"embed"
|
|
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/interp"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
//go:embed bson.jq
|
|
//go:embed bson.md
|
|
var bsonFS embed.FS
|
|
|
|
func init() {
|
|
interp.RegisterFormat(
|
|
format.BSON,
|
|
&decode.Format{
|
|
Description: "Binary JSON",
|
|
DecodeFn: decodeBSON,
|
|
Functions: []string{"torepr"},
|
|
})
|
|
interp.RegisterFS(bsonFS)
|
|
}
|
|
|
|
const (
|
|
elementTypeDouble = 0x01
|
|
elementTypeString = 0x02
|
|
elementTypeDocument = 0x03
|
|
elementTypeArray = 0x04
|
|
elementTypeBinary = 0x05
|
|
elementTypeUndefined = 0x06
|
|
elementTypeObjectID = 0x07
|
|
elementTypeBoolean = 0x08
|
|
elementTypeDatetime = 0x09
|
|
elementTypeNull = 0x0a
|
|
elementTypeRegexp = 0x0b
|
|
elementTypeJavaScript = 0x0d
|
|
elementTypeInt32 = 0x10
|
|
elementTypeTimestamp = 0x11
|
|
elementTypeInt64 = 0x12
|
|
elementTypeDecimal128 = 0x13
|
|
elementTypeMinKey = 0xFF
|
|
elementTypeMaxKey = 0x7f
|
|
)
|
|
|
|
var elementTypeMap = scalar.UintMap{
|
|
elementTypeDouble: {Sym: "double", Description: "64-bit binary floating point"},
|
|
elementTypeString: {Sym: "string", Description: "UTF-8 string"},
|
|
elementTypeDocument: {Sym: "document", Description: "Embedded document"},
|
|
elementTypeArray: {Sym: "array", Description: "Array"},
|
|
elementTypeBinary: {Sym: "binary", Description: "Binary data"},
|
|
elementTypeUndefined: {Sym: "undefined", Description: "Undefined (deprecated)"},
|
|
elementTypeObjectID: {Sym: "object_id", Description: "ObjectId"},
|
|
elementTypeBoolean: {Sym: "boolean", Description: "Boolean"},
|
|
elementTypeDatetime: {Sym: "datetime", Description: "UTC datetime"},
|
|
elementTypeNull: {Sym: "null", Description: "Null value"},
|
|
elementTypeRegexp: {Sym: "regexp", Description: "Regular expression"},
|
|
elementTypeJavaScript: {Sym: "javascript", Description: "JavaScript code"},
|
|
elementTypeInt32: {Sym: "int32", Description: "32-bit integer"},
|
|
elementTypeTimestamp: {Sym: "timestamp", Description: "Timestamp"},
|
|
elementTypeInt64: {Sym: "int64", Description: "64-bit integer"},
|
|
elementTypeDecimal128: {Sym: "decimal128", Description: "128-bit decimal floating point"},
|
|
elementTypeMinKey: {Sym: "minkey", Description: "Min key"},
|
|
elementTypeMaxKey: {Sym: "maxkey", Description: "Max key"},
|
|
}
|
|
|
|
func decodeBSONDocument(d *decode.D) {
|
|
size := d.FieldS32("size")
|
|
d.FramedFn((size-4)*8, func(d *decode.D) {
|
|
d.FieldArray("elements", func(d *decode.D) {
|
|
for d.BitsLeft() > 8 {
|
|
d.FieldStruct("element", func(d *decode.D) {
|
|
typ := d.FieldU8("type", elementTypeMap)
|
|
d.FieldUTF8Null("name")
|
|
switch typ {
|
|
case elementTypeDouble:
|
|
d.FieldF64("value")
|
|
case elementTypeString:
|
|
length := d.FieldU32("length")
|
|
d.FieldUTF8NullFixedLen("value", int(length))
|
|
case elementTypeDocument:
|
|
d.FieldStruct("value", decodeBSONDocument)
|
|
case elementTypeArray:
|
|
d.FieldStruct("value", decodeBSONDocument)
|
|
case elementTypeBinary:
|
|
length := d.FieldS32("length")
|
|
d.FieldU8("subtype")
|
|
d.FieldRawLen("value", length*8)
|
|
case elementTypeUndefined:
|
|
//deprecated
|
|
case elementTypeObjectID:
|
|
d.FieldRawLen("value", 12*8)
|
|
case elementTypeBoolean:
|
|
d.FieldU8("value")
|
|
case elementTypeDatetime:
|
|
d.FieldS64("value")
|
|
case elementTypeNull:
|
|
d.FieldValueAny("value", nil)
|
|
case elementTypeRegexp:
|
|
d.FieldUTF8Null("value")
|
|
d.FieldUTF8Null("options")
|
|
case elementTypeJavaScript:
|
|
length := d.FieldS32("length")
|
|
d.FieldUTF8NullFixedLen("value", int(length))
|
|
case elementTypeInt32:
|
|
d.FieldS32("value")
|
|
case elementTypeTimestamp:
|
|
d.FieldU64("value")
|
|
case elementTypeInt64:
|
|
d.FieldS64("value")
|
|
case elementTypeDecimal128:
|
|
// TODO: Parse the IEEE 754 decimal128 value.
|
|
d.FieldRawLen("value", 128)
|
|
case elementTypeMinKey:
|
|
d.FieldValueAny("value", nil)
|
|
case elementTypeMaxKey:
|
|
d.FieldValueAny("value", nil)
|
|
default:
|
|
d.FieldRawLen("value", d.BitsLeft())
|
|
}
|
|
})
|
|
}
|
|
})
|
|
d.FieldU8("terminator", d.UintValidate(0))
|
|
})
|
|
}
|
|
|
|
func decodeBSON(d *decode.D) any {
|
|
d.Endian = decode.LittleEndian
|
|
|
|
decodeBSONDocument(d)
|
|
|
|
return nil
|
|
}
|