2021-12-28 20:33:18 +03:00
|
|
|
package bson
|
|
|
|
|
|
|
|
// https://bsonspec.org/spec.html
|
|
|
|
// TODO: more types
|
|
|
|
|
|
|
|
import (
|
|
|
|
"embed"
|
|
|
|
|
|
|
|
"github.com/wader/fq/format"
|
|
|
|
"github.com/wader/fq/pkg/decode"
|
2022-07-16 19:39:57 +03:00
|
|
|
"github.com/wader/fq/pkg/interp"
|
2021-12-28 20:33:18 +03:00
|
|
|
"github.com/wader/fq/pkg/scalar"
|
|
|
|
)
|
|
|
|
|
2021-12-09 19:15:21 +03:00
|
|
|
//go:embed bson.jq
|
2022-09-10 19:28:54 +03:00
|
|
|
//go:embed bson.md
|
2021-12-28 20:33:18 +03:00
|
|
|
var bsonFS embed.FS
|
|
|
|
|
|
|
|
func init() {
|
2022-07-16 19:39:57 +03:00
|
|
|
interp.RegisterFormat(decode.Format{
|
2021-12-28 20:33:18 +03:00
|
|
|
Name: format.BSON,
|
|
|
|
Description: "Binary JSON",
|
|
|
|
DecodeFn: decodeBSON,
|
2022-09-10 19:28:54 +03:00
|
|
|
Functions: []string{"torepr"},
|
2021-12-28 20:33:18 +03:00
|
|
|
})
|
2022-07-27 14:20:48 +03:00
|
|
|
interp.RegisterFS(bsonFS)
|
2021-12-28 20:33:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
elementTypeDouble = 0x01
|
|
|
|
elementTypeString = 0x02
|
|
|
|
elementTypeDocument = 0x03
|
|
|
|
elementTypeArray = 0x04
|
|
|
|
elementTypeBinary = 0x05
|
|
|
|
elementTypeUndefined = 0x06
|
|
|
|
elementTypeObjectID = 0x07
|
|
|
|
elementTypeBoolean = 0x08
|
|
|
|
elementTypeDatatime = 0x09
|
|
|
|
elementTypeNull = 0x0a
|
|
|
|
elementTypeRegexp = 0x0b
|
|
|
|
elementTypeInt32 = 0x10
|
|
|
|
elementTypeTimestamp = 0x11
|
|
|
|
elementTypeInt64 = 0x12
|
|
|
|
)
|
|
|
|
|
2022-09-30 14:58:23 +03:00
|
|
|
var elementTypeMap = scalar.UintMap{
|
2021-12-28 20:33:18 +03:00
|
|
|
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"},
|
|
|
|
elementTypeDatatime: {Sym: "datatime", Description: "UTC datetime"},
|
|
|
|
elementTypeNull: {Sym: "null", Description: "Null value"},
|
|
|
|
elementTypeRegexp: {Sym: "regexp", Description: "Regular expression"},
|
|
|
|
elementTypeInt32: {Sym: "int32", Description: "32-bit integer"},
|
|
|
|
elementTypeTimestamp: {Sym: "timestamp", Description: "Timestamp"},
|
|
|
|
elementTypeInt64: {Sym: "int64", Description: "64-bit integer"},
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeBSONDocument(d *decode.D) {
|
|
|
|
size := d.FieldU32("size")
|
2022-01-13 20:34:59 +03:00
|
|
|
d.FramedFn(int64(size-4)*8, func(d *decode.D) {
|
2021-12-28 20:33:18 +03:00
|
|
|
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.FieldU32("length")
|
|
|
|
d.FieldU8("subtype")
|
|
|
|
d.FieldRawLen("value", int64(length)*8)
|
|
|
|
case elementTypeUndefined:
|
2022-01-17 12:49:43 +03:00
|
|
|
//deprecated
|
2021-12-28 20:33:18 +03:00
|
|
|
case elementTypeObjectID:
|
|
|
|
d.FieldRawLen("value", 12*8)
|
|
|
|
case elementTypeBoolean:
|
|
|
|
d.FieldU8("value")
|
|
|
|
case elementTypeDatatime:
|
|
|
|
d.FieldS32("value")
|
|
|
|
case elementTypeNull:
|
2022-09-30 14:58:23 +03:00
|
|
|
d.FieldValueAny("value", nil)
|
2021-12-28 20:33:18 +03:00
|
|
|
case elementTypeRegexp:
|
|
|
|
d.FieldUTF8Null("value")
|
|
|
|
d.FieldUTF8Null("options")
|
|
|
|
case elementTypeInt32:
|
|
|
|
d.FieldS32("value")
|
|
|
|
case elementTypeTimestamp:
|
|
|
|
d.FieldU64("value")
|
|
|
|
case elementTypeInt64:
|
|
|
|
d.FieldS64("value")
|
|
|
|
default:
|
|
|
|
d.FieldRawLen("value", d.BitsLeft())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2022-09-30 14:58:23 +03:00
|
|
|
d.FieldU8("terminator", d.UintValidate(0))
|
2021-12-28 20:33:18 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-07-19 19:33:50 +03:00
|
|
|
func decodeBSON(d *decode.D, _ any) any {
|
2021-12-28 20:33:18 +03:00
|
|
|
d.Endian = decode.LittleEndian
|
|
|
|
|
|
|
|
decodeBSONDocument(d)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|