mirror of
https://github.com/wader/fq.git
synced 2024-11-23 09:56:07 +03:00
Merge branch 'wader_master' into postgres
This commit is contained in:
commit
6588e1dd88
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GOLANGCILINT_VERSION: 1.49.0
|
||||
GOLANGCILINT_VERSION: 1.50.0
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -154,14 +154,18 @@
|
||||
"tfhd",
|
||||
"tfra",
|
||||
"tmpl",
|
||||
"toactual",
|
||||
"toarray",
|
||||
"toboolean",
|
||||
"tobytes",
|
||||
"tobytesrange",
|
||||
"todescription",
|
||||
"toimage",
|
||||
"tojson",
|
||||
"tojvalue",
|
||||
"topath",
|
||||
"torepr",
|
||||
"tosym",
|
||||
"tovalue",
|
||||
"toxml",
|
||||
"toxmlentities",
|
||||
|
175
CHANGES.md
175
CHANGES.md
@ -1,3 +1,178 @@
|
||||
# 0.0.10
|
||||
|
||||
## Changes
|
||||
|
||||
- Add `bplist` Apple Binary Property List decoder. Thanks David McDonald @dgmcdona #427
|
||||
- Add `markdown` decoder. #422
|
||||
- Fix panic when interrupting (ctrl-c) JSON output (`fq tovalue file ` etc), #440
|
||||
- Fix issue using `debug` (and some other native go iterator functions) inside `path(...)`, which is used by assign (`... = ...`) expressions etc. #439
|
||||
- Fix issue and also make `toactual` and `tosym` work more similar to `tovalue`. #432
|
||||
- Fix issue with unknown fields (gaps found after decoding) where one continuous gap could end up split into two of more unknown fields. #431
|
||||
- More format documentation and also nicer help output. Also now all documentation is in markdown format. #430 #422
|
||||
```
|
||||
# or help(matroska) in the REPL
|
||||
$ fq -h matroska
|
||||
matroska: Matroska file decoder
|
||||
|
||||
Decode examples
|
||||
===============
|
||||
|
||||
# Decode file as matroska
|
||||
$ fq -d matroska . file
|
||||
# Decode value as matroska
|
||||
... | matroska
|
||||
|
||||
Lookup element using path
|
||||
=========================
|
||||
|
||||
$ fq 'matroska_path(".Segment.Tracks[0)")' file.mkv
|
||||
|
||||
Get path to element
|
||||
===================
|
||||
|
||||
$ fq 'grep_by(.id == "Tracks") | matroska_path' file.mkv
|
||||
|
||||
References
|
||||
==========
|
||||
- https://tools.ietf.org/html/draft-ietf-cellar-ebml-00
|
||||
- https://matroska.org/technical/specs/index.html
|
||||
- https://www.matroska.org/technical/basics.html
|
||||
- https://www.matroska.org/technical/codec_specs.html
|
||||
- https://wiki.xiph.org/MatroskaOpus
|
||||
```
|
||||
|
||||
## Decoder changes
|
||||
|
||||
- `ar` Allow empty integer strings. For example owner id can be an empty string. #428
|
||||
- `bitcoin_blkdat` Assert that there is a header. As the format is part of the probe group this speeds up probing. #402
|
||||
- `bplist` Add Apple Binary Property List decoder.
|
||||
```sh
|
||||
$ fq '.objects.entries[0] | .key, .value' Info.plist
|
||||
│00 01 02 03 04 05 06 07 08 09│0123456789│.objects.entries[0].key{}:
|
||||
0x32│ 5c │ \ │ type: "ascii_string" (5) (ASCII encoded string)
|
||||
0x32│ 5c │ \ │ size_bits: 12
|
||||
│ │ │ size: 12
|
||||
0x32│ 43 46 42 75│ CFBu│ value: "CFBundleName"
|
||||
0x3c│6e 64 6c 65 4e 61 6d 65 │ndleName │
|
||||
│00 01 02 03 04 05 06 07 08 09│0123456789│.objects.entries[0].value{}:
|
||||
0x1ea│ 5f │ _ │ type: "ascii_string" (5) (ASCII encoded string)
|
||||
0x1ea│ 5f │ _ │ size_bits: 15
|
||||
0x1ea│ 10 │ . │ large_size_marker: 1 (valid)
|
||||
0x1ea│ 10 │ . │ exponent: 0
|
||||
0x1ea│ 18 │ . │ size_bigint: 24
|
||||
│ │ │ size: 24
|
||||
0x1ea│ 41 70 70 6c│ Appl│ value: "AppleProResCodecEmbedded"
|
||||
0x1f4│65 50 72 6f 52 65 73 43 6f 64│eProResCod│
|
||||
0x1fe│65 63 45 6d 62 65 64 64 65 64│ecEmbedded│
|
||||
```
|
||||
- Supports `torepr`
|
||||
```sh
|
||||
$ fq torepr.CFBundleName Info.plist
|
||||
"AppleProResCodecEmbedded"
|
||||
```
|
||||
- `elf`
|
||||
- More robust decoding when string tables are missing. #417<br>
|
||||
```sh
|
||||
# extract entry opcodes and disassemble with ndisasm
|
||||
$ fq -n '"f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAIAAAAAAAsDxmvwYADwU=" | frombase64 | . as $b | elf | $b[.header.entry-.program_headers[0].vaddr:]' \
|
||||
| ndisasm -b 64 -
|
||||
00000000 B03C mov al,0x3c
|
||||
00000002 66BF0600 mov di,0x6
|
||||
00000006 0F05 syscall
|
||||
```
|
||||
- Now decodes program header notes. #421
|
||||
- `markdown` Add decoder. Is used in fq to render CLI help. #422
|
||||
```sh
|
||||
# array with all level 1 and 2 headers
|
||||
$ fq -d markdown '[.. | select(.type=="heading" and .level<=2)?.children[0]]' README.md
|
||||
[
|
||||
"fq",
|
||||
"Usage",
|
||||
"Presentations",
|
||||
"Install",
|
||||
"TODO and ideas",
|
||||
"Development and adding a new decoder",
|
||||
"Thanks and related projects",
|
||||
"License"
|
||||
]
|
||||
```
|
||||
- `matroska` Add support for sample lacing. Used by FLAC samples etc. #404
|
||||
- `mp4` More codec names and also use official names from mp4ra.org. #424<br>
|
||||
```sh
|
||||
# show details of first two track in file
|
||||
$ fq -o line_bytes=10 '.tracks[0,1]' big_buck_bunny.mp4
|
||||
│00 01 02 03 04 05 06 07 08 09│0123456789│.tracks[0]{}: track
|
||||
0x00910a│20 68 10 01 a0 40 0e 20 8c 1b│ h...@. ..│ samples[0:1295]:
|
||||
0x009114│c2 2b 99 09 84 42 60 a8 c4 60│.+...B`..`│
|
||||
* │until 0x541697.7 (5473678) │ │
|
||||
│ │ │ id: 1
|
||||
│ │ │ data_format: "mp4a" (MPEG-4 Audio)
|
||||
│00 01 02 03 04 05 06 07 08 09│0123456789│.tracks[1]{}: track
|
||||
0x00a5e6│ 00│ .│ samples[0:1440]:
|
||||
0x00a5f0│00 00 0c 06 00 07 8b 71 b0 00│.......q..│
|
||||
0x00a5fa│00 03 00 40 80 00 00 00 15 06│...@......│
|
||||
* │until 0x540959.7 (5464939) │ │
|
||||
│ │ │ id: 2
|
||||
│ │ │ data_format: "avc1" (Advanced Video Coding)
|
||||
|
||||
```
|
||||
- `html` Handle leading doc type and processing directives. #414
|
||||
|
||||
## Changelog
|
||||
|
||||
* a77cec92 Added documentation and tests, fixed bad date parsing
|
||||
* d784db69 Adds support for Apple Binary Plist, version 00
|
||||
* 5711f290 Code fixes from PR, still need to add tests and testdata
|
||||
* 6b04f2de Documentation cleanup
|
||||
* bcccde23 Fixes and embeds documentation
|
||||
* ebae938d Fixes bug in integer parsing
|
||||
* 368d183b Size check on nBits to save memory
|
||||
* 84ca1010 Update docker-golang from 1.19.0 to 1.19.1
|
||||
* c47c3866 Update github-go-version from 1.19.0, 1.19.0, 1.19.0 to 1.19.1
|
||||
* 816169b6 Update github-golangci-lint to 1.50.0 from 1.49.0
|
||||
* 21f2980e Update make-golangci-lint to 1.50.0 from 1.49.0
|
||||
* 5f619940 adds function for decoding fixed sized arrays
|
||||
* f08f44f1 ar: Integer strings might be empty
|
||||
* 004406de bitcoin_blkdat,bitcoin_block: Make sure there is a header if blkdat
|
||||
* 421b2b30 bplist: Fix unknown field for singletons and add torepr tests
|
||||
* 16b01211 bplist: Make torepr convert to values
|
||||
* fe64530e csv: Add tsv and header example
|
||||
* cb3dc802 decode,tar: Add scalar description and Try* helpers
|
||||
* a6429ffe decode: Remove RangeSorted flag as we can decide on array/struct instead
|
||||
* a468684a deps: Manual update ones not using bump
|
||||
* a7a101ca doc,help: Nicer format help and move help tests into each format
|
||||
* 725ab1b1 doc,html,xml: Add more documentation and examples
|
||||
* abd19ed8 doc: Fix format sections a bit
|
||||
* 0fdc03a4 doc: Fix some incorrect example prompts
|
||||
* 5382d46a elf: Basic program header notes decoding
|
||||
* 12105d8c elf: Treat missing string tables as empty to be more robust
|
||||
* 3deceeeb fixes from PR comments
|
||||
* 226a9a3e generics: Use more from x/exp
|
||||
* 404b1704 gojq: Update fq fork
|
||||
* 376f0ebb gojq: Update rebased fq fork
|
||||
* 87b2c6c1 help,doc: Use markdown for format documentation again
|
||||
* 8016352b html: Handle html with leading doctype etc
|
||||
* 768df301 interp,decode: For struct use map to lookup field
|
||||
* c4219d69 interp: Fix interrupt panic for cli eval
|
||||
* 00ee10a1 interp: Make to{actual,sym} behave similar to tovalue
|
||||
* 00a50662 markdown: Add decoder
|
||||
* 7749e1b5 matroska: Add proper lacing support
|
||||
* 20a15372 mp4: Fix data_format typo
|
||||
* 2655ba09 mp4: More codec names (from mp4ra.org)
|
||||
* 7cd43b49 perfomance: increase performance by map usage
|
||||
* 6a6fec54 range,decode: Use own range sort impl to speed up a bit
|
||||
* 0f35fe48 ranges,decode: Correctly skip empty ranges when adding unknown fields
|
||||
* ea81efec readline: Update rebased fq fork
|
||||
* 369f4016 removed unneccessary type conversions
|
||||
* 3198602d removed unused return type
|
||||
* 7d865343 sortex: Package with type safe sort helpers
|
||||
* 808202fa test: Skip go test with -race by default
|
||||
* 12836abe updates fqtest
|
||||
* 1e47f4f2 updates tests post integer-bug fix
|
||||
* 3d8ea1de updates torepr for data type
|
||||
* 1385b5d0 wasm: Add some documentation
|
||||
* d6316d5c wav: Decode smpl chunk
|
||||
|
||||
# 0.0.9
|
||||
|
||||
## Changes
|
||||
|
2
Makefile
2
Makefile
@ -69,7 +69,7 @@ gogenerate:
|
||||
lint:
|
||||
# bump: make-golangci-lint /golangci-lint@v([\d.]+)/ git:https://github.com/golangci/golangci-lint.git|^1
|
||||
# bump: make-golangci-lint link "Release notes" https://github.com/golangci/golangci-lint/releases/tag/v$LATEST
|
||||
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49.0 run
|
||||
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.0 run
|
||||
|
||||
.PHONY: depgraph.svg
|
||||
depgraph.svg:
|
||||
|
@ -51,6 +51,7 @@ bitcoin_blkdat,
|
||||
[bitcoin_block](doc/formats.md#bitcoin_block),
|
||||
bitcoin_script,
|
||||
bitcoin_transaction,
|
||||
[bplist](doc/formats.md#bplist),
|
||||
bsd_loopback_frame,
|
||||
[bson](doc/formats.md#bson),
|
||||
bzip2,
|
||||
|
@ -27,6 +27,7 @@
|
||||
|[`bitcoin_block`](#bitcoin_block) |Bitcoin block |<sub>`bitcoin_transaction`</sub>|
|
||||
|`bitcoin_script` |Bitcoin script |<sub></sub>|
|
||||
|`bitcoin_transaction` |Bitcoin transaction |<sub>`bitcoin_script`</sub>|
|
||||
|[`bplist`](#bplist) |Apple Binary Property List |<sub></sub>|
|
||||
|`bsd_loopback_frame` |BSD loopback frame |<sub>`inet_packet`</sub>|
|
||||
|[`bson`](#bson) |Binary JSON |<sub></sub>|
|
||||
|`bzip2` |bzip2 compression |<sub>`probe`</sub>|
|
||||
@ -119,7 +120,7 @@
|
||||
|`inet_packet` |Group |<sub>`ipv4_packet` `ipv6_packet`</sub>|
|
||||
|`ip_packet` |Group |<sub>`icmp` `icmpv6` `tcp_segment` `udp_datagram`</sub>|
|
||||
|`link_frame` |Group |<sub>`bsd_loopback_frame` `ether8023_frame` `sll2_packet` `sll_packet`</sub>|
|
||||
|`probe` |Group |<sub>`adts` `ar` `avro_ocf` `bitcoin_blkdat` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `wasm` `wav` `webp` `xml` `yaml` `zip`</sub>|
|
||||
|`probe` |Group |<sub>`adts` `ar` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `elf` `flac` `gif` `gzip` `jpeg` `json` `jsonl` `macho` `macho_fat` `matroska` `mp3` `mp4` `mpeg_ts` `ogg` `pcap` `pcapng` `png` `tar` `tiff` `toml` `wasm` `wav` `webp` `xml` `yaml` `zip`</sub>|
|
||||
|`tcp_stream` |Group |<sub>`dns_tcp` `rtmp`</sub>|
|
||||
|`udp_payload` |Group |<sub>`dns`</sub>|
|
||||
|
||||
@ -260,6 +261,46 @@ Decode value as bitcoin_block
|
||||
... | bitcoin_block({has_header:false})
|
||||
```
|
||||
|
||||
## bplist
|
||||
|
||||
### Show full decoding
|
||||
```sh
|
||||
$ fq -d bplist dv Info.plist
|
||||
```
|
||||
|
||||
### Timestamps
|
||||
Timestamps in Apple Binary Property Lists are encoded as Cocoa Core Data
|
||||
timestamps, where the raw value is the floating point number of seconds since
|
||||
January 1, 2001. By default, `fq` will render the raw floating point value. In
|
||||
order to get the raw value or string description, use the `todescription`
|
||||
function, you can use the `tovalue` and `todescription` functions:
|
||||
|
||||
```sh
|
||||
$ fq 'torepr.SomeTimeStamp | tovalue' Info.plist
|
||||
685135328
|
||||
|
||||
$ fq 'torepr.SomeTimeStamp | todescription' Info.plist
|
||||
"2022-09-17T19:22:08Z"
|
||||
```
|
||||
|
||||
|
||||
### Get JSON representation
|
||||
```sh
|
||||
$ fq torepr com.apple.UIAutomation.plist
|
||||
{
|
||||
"UIAutomationEnabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### Authors
|
||||
- David McDonald
|
||||
[@dgmcdona](https://github.com/dgmcdona)
|
||||
|
||||
### References
|
||||
- http://fileformats.archiveteam.org/wiki/Property_List/Binary
|
||||
- https://medium.com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd
|
||||
- https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c
|
||||
|
||||
## bson
|
||||
|
||||
### Convert represented value to JSON
|
||||
@ -832,7 +873,7 @@ Elements are arrays of the shape `["#text": "body text", "attr_name", {key: "att
|
||||
|
||||
```sh
|
||||
# decode as array
|
||||
✗ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -d xml -o array=true
|
||||
$ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -d xml -o array=true
|
||||
[
|
||||
"a",
|
||||
null,
|
||||
|
1784
doc/formats.svg
1784
doc/formats.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 132 KiB |
@ -372,8 +372,8 @@ unary uses input and if more than one argument all as arguments ignoring the inp
|
||||
- `parents` output parents of value
|
||||
- `topath` path of value. Use `path_to_expr` to get a string representation.
|
||||
- `tovalue`, `tovalue($opts)` symbolic value if available otherwise actual value
|
||||
- `toactual` actual value (decoded etc)
|
||||
- `tosym` symbolic value (mapped etc)
|
||||
- `toactual`, `toactual($opts)` actual value (usually the decoded value)
|
||||
- `tosym`, `tosym($opts)` symbolic value (mapped etc)
|
||||
- `todescription` description of value
|
||||
- `torepr` convert decode value into what it reptresents. For example convert msgpack decode value
|
||||
into a value representing its JSON representation.
|
||||
|
@ -4,6 +4,7 @@ $ fq -n _registry.groups.probe
|
||||
"ar",
|
||||
"avro_ocf",
|
||||
"bitcoin_blkdat",
|
||||
"bplist",
|
||||
"bzip2",
|
||||
"elf",
|
||||
"flac",
|
||||
@ -56,6 +57,7 @@ bitcoin_blkdat Bitcoin blk.dat
|
||||
bitcoin_block Bitcoin block
|
||||
bitcoin_script Bitcoin script
|
||||
bitcoin_transaction Bitcoin transaction
|
||||
bplist Apple Binary Property List
|
||||
bsd_loopback_frame BSD loopback frame
|
||||
bson Binary JSON
|
||||
bzip2 bzip2 compression
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
_ "github.com/wader/fq/format/avro"
|
||||
_ "github.com/wader/fq/format/bencode"
|
||||
_ "github.com/wader/fq/format/bitcoin"
|
||||
_ "github.com/wader/fq/format/bplist"
|
||||
_ "github.com/wader/fq/format/bson"
|
||||
_ "github.com/wader/fq/format/bzip2"
|
||||
_ "github.com/wader/fq/format/cbor"
|
||||
|
@ -4,6 +4,7 @@ package bitcoin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/wader/fq/format"
|
||||
"github.com/wader/fq/pkg/decode"
|
||||
@ -62,7 +63,7 @@ func decodeBitcoinBlock(d *decode.D, in interface{}) interface{} {
|
||||
d.FieldU32("version", scalar.ActualHex)
|
||||
d.FieldRawLen("previous_block_hash", 32*8, rawHexReverse)
|
||||
d.FieldRawLen("merkle_root", 32*8, rawHexReverse)
|
||||
d.FieldU32("time", scalar.DescriptionActualUUnixTime)
|
||||
d.FieldU32("time", scalar.DescriptionUnixTimeFn(scalar.S.TryActualU, time.RFC3339))
|
||||
d.FieldU32("bits", scalar.ActualHex)
|
||||
d.FieldU32("nonce", scalar.ActualHex)
|
||||
})
|
||||
|
236
format/bplist/bplist.go
Normal file
236
format/bplist/bplist.go
Normal file
@ -0,0 +1,236 @@
|
||||
package bplist
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"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 bplist.jq bplist.md
|
||||
var bplistFS embed.FS
|
||||
|
||||
func init() {
|
||||
interp.RegisterFormat(decode.Format{
|
||||
Name: format.BPLIST,
|
||||
ProbeOrder: format.ProbeOrderBinUnique,
|
||||
Description: "Apple Binary Property List",
|
||||
Groups: []string{format.PROBE},
|
||||
DecodeFn: bplistDecode,
|
||||
Functions: []string{"torepr"},
|
||||
})
|
||||
interp.RegisterFS(bplistFS)
|
||||
}
|
||||
|
||||
const (
|
||||
elementTypeNullOrBoolOrFill = 0x00
|
||||
elementTypeInt = 0x01
|
||||
elementTypeReal = 0x02
|
||||
elementTypeDate = 0x03
|
||||
elementTypeData = 0x04
|
||||
elementTypeASCIIString = 0x05
|
||||
elementTypeUnicodeString = 0x06
|
||||
elementTypeUID = 0x08
|
||||
elementTypeArray = 0x0a
|
||||
elementTypeSet = 0x0c
|
||||
elementTypeDict = 0x0d
|
||||
)
|
||||
|
||||
const (
|
||||
null = 0x00
|
||||
boolFalse = 0x08
|
||||
boolTrue = 0x09
|
||||
)
|
||||
|
||||
var elementTypeMap = scalar.UToScalar{
|
||||
elementTypeNullOrBoolOrFill: {Sym: "singleton", Description: "Singleton value (null/bool)"},
|
||||
elementTypeInt: {Sym: "int", Description: "Integer"},
|
||||
elementTypeReal: {Sym: "real", Description: "Floating Point Number"},
|
||||
elementTypeDate: {Sym: "date", Description: "Date, 4 or 8 byte float"},
|
||||
elementTypeData: {Sym: "data", Description: "Binary data"},
|
||||
elementTypeASCIIString: {Sym: "ascii_string", Description: "ASCII encoded string"},
|
||||
elementTypeUnicodeString: {Sym: "unicode_string", Description: "Unicode string"},
|
||||
elementTypeUID: {Sym: "uid", Description: "UID"},
|
||||
elementTypeArray: {Sym: "array", Description: "Array"},
|
||||
elementTypeSet: {Sym: "set", Description: "Set"},
|
||||
elementTypeDict: {Sym: "dict", Description: "Dictionary"},
|
||||
}
|
||||
|
||||
var cocoaTimeEpochDate = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// decodes the number of bits required to store the following object
|
||||
func decodeSize(d *decode.D, sms ...scalar.Mapper) uint64 {
|
||||
n := d.FieldU4("size_bits")
|
||||
if n != 0x0f {
|
||||
return n
|
||||
}
|
||||
|
||||
d.FieldU4("large_size_marker", d.AssertU(0b0001))
|
||||
|
||||
// get the exponent value
|
||||
n = d.FieldU4("exponent")
|
||||
|
||||
// calculate the number of bytes encoding the size
|
||||
n = 1 << n
|
||||
|
||||
// decode that many bytes as big endian
|
||||
n = d.FieldUFn(
|
||||
"size_bigint",
|
||||
func(d *decode.D) uint64 {
|
||||
v := d.UBigInt(int(n * 8))
|
||||
d.AssertBigIntRange(big.NewInt(1), big.NewInt(math.MaxInt64))
|
||||
return v.Uint64()
|
||||
}, sms...)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func decodeItem(d *decode.D, p *plist) {
|
||||
m := d.FieldU4("type", elementTypeMap)
|
||||
switch m {
|
||||
case elementTypeNullOrBoolOrFill:
|
||||
d.FieldU4("value", scalar.UToScalar{
|
||||
null: scalar.S{Sym: nil},
|
||||
boolTrue: scalar.S{Sym: true},
|
||||
boolFalse: scalar.S{Sym: false},
|
||||
})
|
||||
case elementTypeInt:
|
||||
n := d.FieldUFn("size", func(d *decode.D) uint64 {
|
||||
return 1 << d.U4()
|
||||
})
|
||||
if n*8 <= 64 {
|
||||
d.FieldU("value", int(n*8))
|
||||
} else {
|
||||
d.FieldUBigInt("value", int(n))
|
||||
}
|
||||
case elementTypeReal:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldF("value", int(n))
|
||||
case elementTypeDate:
|
||||
n := 1 << decodeSize(d, d.AssertU(4, 8))
|
||||
d.FieldValueU("size", uint64(n))
|
||||
d.FieldF("value", n*8, scalar.DescriptionTimeFn(scalar.S.TryActualF, cocoaTimeEpochDate, time.RFC3339))
|
||||
case elementTypeData:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldRawLen("value", int64(n*8))
|
||||
case elementTypeASCIIString:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldUTF8("value", int(n))
|
||||
case elementTypeUnicodeString:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldUTF16("value", int(n))
|
||||
case elementTypeUID:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldUBigInt("value", int(n)).Uint64()
|
||||
case elementTypeArray:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldStructNArray("entries", "entry", int64(n),
|
||||
func(d *decode.D) {
|
||||
idx := d.FieldU8("object_index")
|
||||
decodeReference(d, p, idx)
|
||||
})
|
||||
case elementTypeSet:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldStructNArray("entries", "entry", int64(n),
|
||||
func(d *decode.D) {
|
||||
idx := d.FieldU8("object_index")
|
||||
decodeReference(d, p, idx)
|
||||
})
|
||||
case elementTypeDict:
|
||||
n := decodeSize(d)
|
||||
d.FieldValueU("size", n)
|
||||
d.FieldStructNArray("entries", "entry", int64(n),
|
||||
func(d *decode.D) {
|
||||
var ki, vi uint64
|
||||
ki = d.FieldU8("key_index")
|
||||
d.SeekRel(int64((n-1)*p.t.objRefSize)*8, func(d *decode.D) {
|
||||
vi = d.FieldU8("value_index")
|
||||
})
|
||||
d.FieldStruct("key", func(d *decode.D) {
|
||||
decodeReference(d, p, ki)
|
||||
})
|
||||
d.FieldStruct("value", func(d *decode.D) {
|
||||
decodeReference(d, p, vi)
|
||||
})
|
||||
})
|
||||
default:
|
||||
d.Errorf("unknown type marker: %d", m)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeReference(d *decode.D, p *plist, idx uint64) {
|
||||
if idx > uint64(len(p.o)) {
|
||||
// prevent a panic
|
||||
d.Errorf("index %d out of bounds for object table size %d", idx, len(p.o))
|
||||
return
|
||||
}
|
||||
|
||||
d.SeekAbs(int64(p.o[idx]*8), func(d *decode.D) {
|
||||
decodeItem(d, p)
|
||||
})
|
||||
}
|
||||
|
||||
type trailer struct {
|
||||
offTblOffSize uint64
|
||||
objRefSize uint64
|
||||
nObjects uint64
|
||||
topObjectOffset uint64
|
||||
offsetTableStart uint64
|
||||
}
|
||||
|
||||
type plist struct {
|
||||
t trailer
|
||||
o []uint64
|
||||
}
|
||||
|
||||
func bplistDecode(d *decode.D, _ any) any {
|
||||
d.FieldStruct("header", func(d *decode.D) {
|
||||
d.FieldUTF8("magic", 6, d.AssertStr("bplist"))
|
||||
d.FieldUTF8("version", 2, d.AssertStr("00"))
|
||||
})
|
||||
|
||||
p := new(plist)
|
||||
|
||||
d.SeekAbs(d.Len()-32*8, func(d *decode.D) {
|
||||
d.FieldStruct("trailer", func(d *decode.D) {
|
||||
d.FieldU40("unused")
|
||||
d.FieldS8("sort_version")
|
||||
p.t.offTblOffSize = d.FieldU8("offset_table_offset_size", d.AssertURange(1, 8))
|
||||
p.t.objRefSize = d.FieldU8("object_reference_size", d.AssertURange(1, 8))
|
||||
p.t.nObjects = d.FieldU64("object_count")
|
||||
p.t.topObjectOffset = d.FieldU64("top_object_offset")
|
||||
p.t.offsetTableStart = d.FieldU64("offset_table_start")
|
||||
})
|
||||
})
|
||||
|
||||
d.SeekAbs(int64(p.t.offsetTableStart*8), func(d *decode.D) {
|
||||
i := uint64(0)
|
||||
d.FieldArrayLoop("offset_table",
|
||||
func() bool { return i < p.t.nObjects },
|
||||
func(d *decode.D) {
|
||||
off := d.FieldU("element", 8*int(p.t.offTblOffSize))
|
||||
p.o = append(p.o, off)
|
||||
i++
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
d.FieldStruct("objects",
|
||||
func(d *decode.D) {
|
||||
decodeReference(d, p, 0)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
30
format/bplist/bplist.jq
Normal file
30
format/bplist/bplist.jq
Normal file
@ -0,0 +1,30 @@
|
||||
def _bplist_torepr:
|
||||
def _f:
|
||||
( if .type == "singleton" then .value | tovalue
|
||||
elif .type == "int" then .value | tovalue
|
||||
elif .type == "real" then .value | tovalue
|
||||
elif .type == "date" then .value | tovalue
|
||||
elif .type == "data" then .value | tovalue
|
||||
elif .type == "ascii_string" then .value | tovalue
|
||||
elif .type == "unicode_string" then .value | tovalue
|
||||
elif .type == "uid" then .value | tovalue
|
||||
elif .type == "array" then
|
||||
( .entries
|
||||
| map(_f)
|
||||
)
|
||||
elif .type == "set" then
|
||||
( .entries
|
||||
| map(_f)
|
||||
)
|
||||
elif .type == "dict" then
|
||||
( .entries
|
||||
| map({key: (.key | _f), value: (.value | _f)})
|
||||
| from_entries
|
||||
)
|
||||
else error("unknown type: \(.type)")
|
||||
end
|
||||
);
|
||||
( .objects
|
||||
| _f
|
||||
);
|
||||
|
37
format/bplist/bplist.md
Normal file
37
format/bplist/bplist.md
Normal file
@ -0,0 +1,37 @@
|
||||
### Show full decoding
|
||||
```sh
|
||||
$ fq d Info.plist
|
||||
```
|
||||
|
||||
### Timestamps
|
||||
Timestamps in Apple Binary Property Lists are encoded as Cocoa Core Data
|
||||
timestamps, where the raw value is the floating point number of seconds since
|
||||
January 1, 2001. By default, `fq` will render the raw floating point value. In
|
||||
order to get the raw value or string description, use the `todescription`
|
||||
function, you can use the `tovalue` and `todescription` functions:
|
||||
|
||||
```sh
|
||||
$ fq 'torepr.SomeTimeStamp | tovalue' Info.plist
|
||||
685135328
|
||||
|
||||
$ fq 'torepr.SomeTimeStamp | todescription' Info.plist
|
||||
"2022-09-17T19:22:08Z"
|
||||
```
|
||||
|
||||
|
||||
### Get JSON representation
|
||||
```sh
|
||||
$ fq torepr com.apple.UIAutomation.plist
|
||||
{
|
||||
"UIAutomationEnabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### Authors
|
||||
- David McDonald
|
||||
[@dgmcdona](https://github.com/dgmcdona)
|
||||
|
||||
### References
|
||||
- http://fileformats.archiveteam.org/wiki/Property_List/Binary
|
||||
- https://medium.com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd
|
||||
- https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c
|
BIN
format/bplist/testdata/Info.plist
vendored
Normal file
BIN
format/bplist/testdata/Info.plist
vendored
Normal file
Binary file not shown.
2424
format/bplist/testdata/bplist.fqtest
vendored
Normal file
2424
format/bplist/testdata/bplist.fqtest
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
format/bplist/testdata/com.apple.UIAutomation.plist
vendored
Normal file
BIN
format/bplist/testdata/com.apple.UIAutomation.plist
vendored
Normal file
Binary file not shown.
@ -45,6 +45,7 @@ const (
|
||||
BITCOIN_BLOCK = "bitcoin_block"
|
||||
BITCOIN_SCRIPT = "bitcoin_script"
|
||||
BITCOIN_TRANSACTION = "bitcoin_transaction"
|
||||
BPLIST = "bplist"
|
||||
BSD_LOOPBACK_FRAME = "bsd_loopback_frame"
|
||||
BSON = "bson"
|
||||
BZIP2 = "bzip2"
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"compress/flate"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/wader/fq/format"
|
||||
"github.com/wader/fq/pkg/decode"
|
||||
@ -74,7 +75,7 @@ func gzDecode(d *decode.D, _ any) any {
|
||||
hasComment = d.FieldBool("comment")
|
||||
d.FieldU3("reserved")
|
||||
})
|
||||
d.FieldU32("mtime", scalar.DescriptionActualUUnixTime)
|
||||
d.FieldU32("mtime", scalar.DescriptionUnixTimeFn(scalar.S.TryActualU, time.RFC3339))
|
||||
switch compressionMethod {
|
||||
case delfateMethod:
|
||||
d.FieldU8("extra_flags", deflateExtraFlagsNames)
|
||||
|
@ -210,7 +210,7 @@ func decodeLang(d *decode.D) string {
|
||||
|
||||
// Quicktime time seconds in January 1, 1904 UTC
|
||||
var quicktimeEpochDate = time.Date(1904, time.January, 4, 0, 0, 0, 0, time.UTC)
|
||||
var quicktimeEpoch = scalar.DescriptionActualUTime(quicktimeEpochDate, time.RFC3339)
|
||||
var quicktimeEpoch = scalar.DescriptionTimeFn(scalar.S.TryActualU, quicktimeEpochDate, time.RFC3339)
|
||||
|
||||
func decodeFieldMatrix(d *decode.D, name string) {
|
||||
d.FieldStruct(name, func(d *decode.D) {
|
||||
|
@ -271,7 +271,7 @@ func mp4Tracks(d *decode.D, ctx *decodeContext) {
|
||||
}
|
||||
}
|
||||
|
||||
d.FieldValueStr("data_foramt", trackSDDataFormat, dataFormatNames)
|
||||
d.FieldValueStr("data_format", trackSDDataFormat, dataFormatNames)
|
||||
|
||||
switch trackSDDataFormat {
|
||||
case "lpcm",
|
||||
|
2
format/mp4/testdata/aac.fqtest
vendored
2
format/mp4/testdata/aac.fqtest
vendored
@ -429,4 +429,4 @@ $ fq -d mp4 dv aac.mp4
|
||||
0x290|b4 |. | [1]: raw bits byte_align 0x290.6-0x290.7 (0.2)
|
||||
0x290| 70 | p | [2]: raw bits data 0x291-0x291.7 (1)
|
||||
| | | id: 1 0x59d-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x59d-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x59d-NA (0)
|
||||
|
2
format/mp4/testdata/av1.fqtest
vendored
2
format/mp4/testdata/av1.fqtest
vendored
@ -341,4 +341,4 @@ $ fq -d mp4 dv av1.mp4
|
||||
0x0050|f6 0a 4f ae f3 fe ec e7 30 4f 3f 13 9c 75 c9 6a|..O.....0O?..u.j| data: raw bits 0x50-0x11bf.7 (4464)
|
||||
* |until 0x11bf.7 (4464) | |
|
||||
| | | id: 1 0x14b2-NA (0)
|
||||
| | | data_foramt: "av01" (AV1 video) 0x14b2-NA (0)
|
||||
| | | data_format: "av01" (AV1 video) 0x14b2-NA (0)
|
||||
|
2
format/mp4/testdata/avc.fqtest
vendored
2
format/mp4/testdata/avc.fqtest
vendored
@ -514,4 +514,4 @@ $ fq -d mp4 dv avc.mp4
|
||||
0x00d50|79 0a ff 01 f9 2d 04 d3 29 fe 4d 76 42 26 f6 cd|y....-..).MvB&..|
|
||||
* |until 0xd80.7 (51) | |
|
||||
| | | id: 1 0x10e0-NA (0)
|
||||
| | | data_foramt: "avc1" (Advanced Video Coding) 0x10e0-NA (0)
|
||||
| | | data_format: "avc1" (Advanced Video Coding) 0x10e0-NA (0)
|
||||
|
8
format/mp4/testdata/dash.fqtest
vendored
8
format/mp4/testdata/dash.fqtest
vendored
@ -348,7 +348,7 @@ $ fq -d mp4 dv dash_audio_init.mp4
|
||||
| | | tracks[0:1]: 0x330-NA (0)
|
||||
| | | [0]{}: track 0x330-NA (0)
|
||||
| | | id: 1 0x330-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x330-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x330-NA (0)
|
||||
| | | samples[0:0]: 0x330-NA (0)
|
||||
$ fq -d mp4 dv dash_audio_1.m4s
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: dash_audio_1.m4s (mp4) 0x0-0x4eb.7 (1260)
|
||||
@ -491,7 +491,7 @@ $ fq -d mp4 dv dash_audio_1.m4s
|
||||
* |until 0x4e6.7 (193) | |
|
||||
0x4e0| 01 18 81 b4 70| | ....p| | [5]: raw bits sample 0x4e7-0x4eb.7 (5)
|
||||
| | | id: 1 0x4ec-NA (0)
|
||||
| | | data_foramt: "unknown" 0x4ec-NA (0)
|
||||
| | | data_format: "unknown" 0x4ec-NA (0)
|
||||
$ fq -d mp4 dv dash_video_init.mp4
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: dash_video_init.mp4 (mp4) 0x0-0x332.7 (819)
|
||||
| | | boxes[0:2]: 0x0-0x332.7 (819)
|
||||
@ -886,7 +886,7 @@ $ fq -d mp4 dv dash_video_init.mp4
|
||||
| | | tracks[0:1]: 0x333-NA (0)
|
||||
| | | [0]{}: track 0x333-NA (0)
|
||||
| | | id: 1 0x333-NA (0)
|
||||
| | | data_foramt: "avc1" (Advanced Video Coding) 0x333-NA (0)
|
||||
| | | data_format: "avc1" (Advanced Video Coding) 0x333-NA (0)
|
||||
| | | samples[0:0]: 0x333-NA (0)
|
||||
$ fq -d mp4 dv dash_video_1.m4s
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: dash_video_1.m4s (mp4) 0x0-0x1fd0.7 (8145)
|
||||
@ -1001,4 +1001,4 @@ $ fq -d mp4 dv dash_video_1.m4s
|
||||
0x1720|53 23 af ff f2 50 06 7f 30 02 17 55 d4 5a 6f db|S#...P..0..U.Zo.|
|
||||
* |until 0x1fd0.7 (end) (2240) | |
|
||||
| | | id: 1 0x1fd1-NA (0)
|
||||
| | | data_foramt: "unknown" 0x1fd1-NA (0)
|
||||
| | | data_format: "unknown" 0x1fd1-NA (0)
|
||||
|
2
format/mp4/testdata/decode_samples.fqtest
vendored
2
format/mp4/testdata/decode_samples.fqtest
vendored
@ -14,4 +14,4 @@ $ fq -o decode_samples=false -d mp4 '.tracks | dv' aac.mp4
|
||||
0x280| 01 18 81| ...| [3]: raw bits sample 0x28d-0x291.7 (5)
|
||||
0x290|b4 70 |.p |
|
||||
| | | id: 1 0x59d-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x59d-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x59d-NA (0)
|
||||
|
2
format/mp4/testdata/flac.fqtest
vendored
2
format/mp4/testdata/flac.fqtest
vendored
@ -372,4 +372,4 @@ $ fq -d mp4 dv flac.mp4
|
||||
0x280| 00 | . | byte_align: 0 (valid) 0x287.2-0x287.7 (0.6)
|
||||
0x280| 82 cb | .. | footer_crc: "82cb" (raw bits) (valid) 0x288-0x289.7 (2)
|
||||
| | | id: 1 0x543-NA (0)
|
||||
| | | data_foramt: "fLaC" (Fres Lossless Audio Codec) 0x543-NA (0)
|
||||
| | | data_format: "fLaC" (Fres Lossless Audio Codec) 0x543-NA (0)
|
||||
|
4
format/mp4/testdata/fragmented.fqtest
vendored
4
format/mp4/testdata/fragmented.fqtest
vendored
@ -1167,7 +1167,7 @@ $ fq -d mp4 dv fragmented.mp4
|
||||
0x02240|86 f8 14 d8 53 23 af ff f2 50 06 7f 30 02 17 55|....S#...P..0..U|
|
||||
* |until 0x2af4.7 (2234) | |
|
||||
| | | id: 1 0x2bb4-NA (0)
|
||||
| | | data_foramt: "avc1" (Advanced Video Coding) 0x2bb4-NA (0)
|
||||
| | | data_format: "avc1" (Advanced Video Coding) 0x2bb4-NA (0)
|
||||
| | | [1]{}: track 0x13e0-0x2bb3.7 (6100)
|
||||
| | | samples[0:6]: 0x13e0-0x2af9.7 (5914)
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef| [0][0:4]: sample (aac_frame) 0x13e0-0x14ad.7 (206)
|
||||
@ -1270,4 +1270,4 @@ $ fq -d mp4 dv fragmented.mp4
|
||||
0x02af0| b4 | . | [1]: raw bits byte_align 0x2af8.6-0x2af8.7 (0.2)
|
||||
0x02af0| 70 | p | [2]: raw bits data 0x2af9-0x2af9.7 (1)
|
||||
| | | id: 2 0x2bb4-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x2bb4-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x2bb4-NA (0)
|
||||
|
2
format/mp4/testdata/hevc.fqtest
vendored
2
format/mp4/testdata/hevc.fqtest
vendored
@ -636,4 +636,4 @@ $ fq -d mp4 dv hevc.mp4
|
||||
0x0040|fd a9 78 83 ff fb 75 6c 0b 3f ff 94 ce 7f aa fe|..x...ul.?......|
|
||||
* |until 0x880.7 (2127) | |
|
||||
| | | id: 1 0x149b-NA (0)
|
||||
| | | data_foramt: "hev1" (High Efficiency Video Coding) 0x149b-NA (0)
|
||||
| | | data_format: "hev1" (High Efficiency Video Coding) 0x149b-NA (0)
|
||||
|
2
format/mp4/testdata/in24.fqtest
vendored
2
format/mp4/testdata/in24.fqtest
vendored
@ -280,4 +280,4 @@ $ fq dv in24.mp4
|
||||
| | | tracks[0:1]: 0x3ec-NA (0)
|
||||
| | | [0]{}: track 0x3ec-NA (0)
|
||||
| | | id: 1 0x3ec-NA (0)
|
||||
| | | data_foramt: "in24" 0x3ec-NA (0)
|
||||
| | | data_format: "in24" 0x3ec-NA (0)
|
||||
|
2
format/mp4/testdata/lpcm.fqtest
vendored
2
format/mp4/testdata/lpcm.fqtest
vendored
@ -271,4 +271,4 @@ $ fq dv lpcm.mp4
|
||||
| | | tracks[0:1]: 0x512-NA (0)
|
||||
| | | [0]{}: track 0x512-NA (0)
|
||||
| | | id: 1 0x512-NA (0)
|
||||
| | | data_foramt: "lpcm" 0x512-NA (0)
|
||||
| | | data_format: "lpcm" 0x512-NA (0)
|
||||
|
2
format/mp4/testdata/mp3.fqtest
vendored
2
format/mp4/testdata/mp3.fqtest
vendored
@ -506,4 +506,4 @@ $ fq -d mp4 dv mp3.mp4
|
||||
* |until 0x29d.7 (188) | |
|
||||
| | | crc_calculated: "c36b" (raw bits) 0x29e-NA (0)
|
||||
| | | id: 1 0x565-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x565-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x565-NA (0)
|
||||
|
2
format/mp4/testdata/mpeg2.fqtest
vendored
2
format/mp4/testdata/mpeg2.fqtest
vendored
@ -356,4 +356,4 @@ $ fq -d mp4 dv mpeg2.mp4
|
||||
0x0040|00 00 00 00 01 b8 00 08 00 40 00 00 01 00 00 0f|.........@......|
|
||||
* |until 0x1fa5.7 (8046) | |
|
||||
| | | id: 1 0x22a9-NA (0)
|
||||
| | | data_foramt: "mp4v" (MPEG-4 Visual) 0x22a9-NA (0)
|
||||
| | | data_format: "mp4v" (MPEG-4 Visual) 0x22a9-NA (0)
|
||||
|
2
format/mp4/testdata/opus.fqtest
vendored
2
format/mp4/testdata/opus.fqtest
vendored
@ -353,4 +353,4 @@ $ fq -d mp4 dv opus.mp4
|
||||
0x120|02 cc 49 57 27 d4 a3 83 e9 53 33 fe 45 62 33 33|..IW'....S3.Eb33|
|
||||
* |until 0x196.7 (120) | |
|
||||
| | | id: 1 0x439-NA (0)
|
||||
| | | data_foramt: "Opus" (Opus audio coding) 0x439-NA (0)
|
||||
| | | data_format: "Opus" (Opus audio coding) 0x439-NA (0)
|
||||
|
2
format/mp4/testdata/stz2.fqtest
vendored
2
format/mp4/testdata/stz2.fqtest
vendored
@ -463,4 +463,4 @@ $ fq -d mp4 'dv' stz2.mp4
|
||||
* |until 0x4f5.7 (188) | |
|
||||
| | | crc_calculated: "c36b" (raw bits) 0x4f6-NA (0)
|
||||
| | | id: 1 0x530-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x530-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x530-NA (0)
|
||||
|
2
format/mp4/testdata/vorbis.fqtest
vendored
2
format/mp4/testdata/vorbis.fqtest
vendored
@ -383,4 +383,4 @@ $ fq -d mp4 dv vorbis.mp4
|
||||
0x0130|f2 81 46 bb c2 48 52 08 27 b8 83 10 ca 08 b1 a7|..F..HR.'.......|
|
||||
* |until 0x1dc.7 (174) | |
|
||||
| | | id: 1 0x1189-NA (0)
|
||||
| | | data_foramt: "mp4a" (MPEG-4 Audio) 0x1189-NA (0)
|
||||
| | | data_format: "mp4a" (MPEG-4 Audio) 0x1189-NA (0)
|
||||
|
2
format/mp4/testdata/vp9.fqtest
vendored
2
format/mp4/testdata/vp9.fqtest
vendored
@ -336,4 +336,4 @@ $ fq -d mp4 dv vp9.mp4
|
||||
0x0040|f9 be 8f e7 71 ff 5f 97 ef c3 f9 7e 37 b0 7e ad|....q._....~7.~.|
|
||||
* |until 0x1563.7 (5424) | |
|
||||
| | | id: 1 0x184f-NA (0)
|
||||
| | | data_foramt: "vp09" (VP9 video) 0x184f-NA (0)
|
||||
| | | data_format: "vp09" (VP9 video) 0x184f-NA (0)
|
||||
|
2
format/prores/testdata/prores_frame.fqtest
vendored
2
format/prores/testdata/prores_frame.fqtest
vendored
@ -325,4 +325,4 @@ $ fq -d mp4 dv prores_frame.mov
|
||||
0x00c0|40 00 00 6b 7d 00 2d 30 05 a7 05 df 00 25 05 6c|@..k}.-0.....%.l| picture_data: raw bits 0xc0-0x6c3c.7 (27517)
|
||||
* |until 0x6c3c.7 (27517) | |
|
||||
| | | id: 1 0x6eff-NA (0)
|
||||
| | | data_foramt: "ap4h" (Apple ProRes 4444) 0x6eff-NA (0)
|
||||
| | | data_format: "ap4h" (Apple ProRes 4444) 0x6eff-NA (0)
|
||||
|
@ -5,6 +5,7 @@ package tar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/wader/fq/format"
|
||||
"github.com/wader/fq/pkg/decode"
|
||||
@ -53,7 +54,7 @@ func tarDecode(d *decode.D, _ any) any {
|
||||
d.Fatalf("could not decode size")
|
||||
}
|
||||
size := int64(sizeS.SymU()) * 8
|
||||
d.FieldUTF8NullFixedLen("mtime", 12, scalar.TrySymUParseUint(8), scalar.DescriptionSymUUnixTime)
|
||||
d.FieldUTF8NullFixedLen("mtime", 12, scalar.TrySymUParseUint(8), scalar.DescriptionUnixTimeFn(scalar.S.TrySymU, time.RFC3339))
|
||||
d.FieldUTF8NullFixedLen("chksum", 8, scalar.TrySymUParseUint(8))
|
||||
d.FieldUTF8("typeflag", 1, mapTrimSpaceNull)
|
||||
d.FieldUTF8("linkname", 100, mapTrimSpaceNull)
|
||||
|
@ -157,6 +157,34 @@ func decodeChunk(d *decode.D, expectedChunkID string, stringData bool) {
|
||||
"fact": func(d *decode.D) {
|
||||
d.FieldU32("sample_length")
|
||||
},
|
||||
"smpl": func(d *decode.D) {
|
||||
d.FieldU32("manufacturer")
|
||||
d.FieldU32("product")
|
||||
d.FieldU32("sample_period")
|
||||
d.FieldU32("midi_unity_note")
|
||||
d.FieldU32("midi_pitch_fraction")
|
||||
d.FieldU32("smpte_format")
|
||||
d.FieldU32("smpte_offset")
|
||||
numSampleLoops := int(d.FieldU32("number_of_sample_loops"))
|
||||
samplerDataBytes := int(d.FieldU32("sampler_data_bytes"))
|
||||
d.FieldArray("samples_loops", func(d *decode.D) {
|
||||
for i := 0; i < numSampleLoops; i++ {
|
||||
d.FieldStruct("sample_loop", func(d *decode.D) {
|
||||
d.FieldUTF8("id", 4)
|
||||
d.FieldU32("type", scalar.UToSymStr{
|
||||
0: "forward",
|
||||
1: "forward_backward",
|
||||
2: "backward",
|
||||
})
|
||||
d.FieldU32("start")
|
||||
d.FieldU32("end")
|
||||
d.FieldU32("fraction")
|
||||
d.FieldU32("number_of_times")
|
||||
})
|
||||
}
|
||||
})
|
||||
d.FieldRawLen("sampler_data", int64(samplerDataBytes)*8)
|
||||
},
|
||||
}
|
||||
|
||||
trimChunkID := d.FieldStrFn("id", func(d *decode.D) string {
|
||||
|
2
format/xml/testdata/help_xml.fqtest
vendored
2
format/xml/testdata/help_xml.fqtest
vendored
@ -78,7 +78,7 @@ Elements are arrays of the shape ["#text": "body text", "attr_name", {key: "attr
|
||||
|
||||
|
||||
# decode as array
|
||||
✗ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -d xml -o array=true
|
||||
$ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -d xml -o array=true
|
||||
[
|
||||
"a",
|
||||
null,
|
||||
|
@ -60,7 +60,7 @@ Elements are arrays of the shape `["#text": "body text", "attr_name", {key: "att
|
||||
|
||||
```sh
|
||||
# decode as array
|
||||
✗ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -d xml -o array=true
|
||||
$ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -d xml -o array=true
|
||||
[
|
||||
"a",
|
||||
null,
|
||||
|
2
fq.go
2
fq.go
@ -6,7 +6,7 @@ import (
|
||||
"github.com/wader/fq/pkg/interp"
|
||||
)
|
||||
|
||||
const version = "0.0.9"
|
||||
const version = "0.0.10"
|
||||
|
||||
func main() {
|
||||
cli.Main(interp.DefaultRegistry, version)
|
||||
|
16
go.mod
16
go.mod
@ -4,9 +4,9 @@ go 1.18
|
||||
|
||||
require (
|
||||
// fork of github.com/itchyny/gojq, see github.com/wader/gojq fq branch
|
||||
github.com/wader/gojq v0.12.1-0.20220822132002-64fe65a68424
|
||||
github.com/wader/gojq v0.12.1-0.20220929141349-8874f5c7907c
|
||||
// fork of github.com/chzyer/readline, see github.com/wader/readline fq branch
|
||||
github.com/wader/readline v0.0.0-20220704090837-31be50517a56
|
||||
github.com/wader/readline v0.0.0-20220928125628-732951d41240
|
||||
)
|
||||
|
||||
require (
|
||||
@ -27,7 +27,7 @@ require (
|
||||
|
||||
// has no tags
|
||||
// go get -d github.com/gomarkdown/markdown@master && go mod tidy
|
||||
github.com/gomarkdown/markdown v0.0.0-20220627144906-e9a81102ebeb
|
||||
github.com/gomarkdown/markdown v0.0.0-20220905174103-7b278df48cfb
|
||||
|
||||
// has no tags yet
|
||||
// bump-disabled: gomod-gopacket /github\.com\/gopacket\/gopacket v(.*)/ https://github.com/gopacket/gopacket.git|^1
|
||||
@ -52,15 +52,15 @@ require (
|
||||
|
||||
// has no tags
|
||||
// go get -d golang.org/x/crypto@master && go mod tidy
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
|
||||
|
||||
// has no tags
|
||||
// go get -d golang.org/x/exp@master && go mod tidy
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
||||
golang.org/x/exp v0.0.0-20220927162542-c76eaa363f9d
|
||||
|
||||
// has no tags
|
||||
// go get -d golang.org/x/net@master && go mod tidy
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc
|
||||
|
||||
// bump: gomod-golang/text /golang\.org\/x\/text v(.*)/ https://github.com/golang/text.git|^0
|
||||
// bump: gomod-golang/text command go get -d golang.org/x/text@v$LATEST && go mod tidy
|
||||
@ -74,9 +74,9 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/itchyny/timefmt-go v0.1.3 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.4 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
)
|
||||
|
32
go.sum
32
go.sum
@ -4,12 +4,12 @@ github.com/creasty/defaults v1.6.0 h1:ltuE9cfphUtlrBeomuu8PEyISTXnxqkBIoQfXgv7BS
|
||||
github.com/creasty/defaults v1.6.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20220627144906-e9a81102ebeb h1:5b/eFaSaKPFG9ygDBaPKkydKU5nFJYk08g9jPIVogMg=
|
||||
github.com/gomarkdown/markdown v0.0.0-20220627144906-e9a81102ebeb/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/gomarkdown/markdown v0.0.0-20220905174103-7b278df48cfb h1:7h+tPfwoUE+qLvWYmsvKSiRlXv6WGorb6PUKaZUclwc=
|
||||
github.com/gomarkdown/markdown v0.0.0-20220905174103-7b278df48cfb/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/gopacket/gopacket v0.0.0-20220819214934-ee81b8c880da h1:AAwDU9N39fQNYUtg270aiU6N7U2ZVsGZKiRwsCMsWEo=
|
||||
github.com/gopacket/gopacket v0.0.0-20220819214934-ee81b8c880da/go.mod h1:DlRRfaM/QjAu2ADqraIure1Eif0HpNL8hmyVQ+qci5Y=
|
||||
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
|
||||
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/itchyny/timefmt-go v0.1.4 h1:hFEfWVdwsEi+CY8xY2FtgWHGQaBaC3JeHd+cve0ynVM=
|
||||
github.com/itchyny/timefmt-go v0.1.4/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
@ -23,19 +23,19 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/wader/gojq v0.12.1-0.20220822132002-64fe65a68424 h1:4SZxNA1ti22+TemKXy5H2qyFPPqi5X55vtyjmOnLsqk=
|
||||
github.com/wader/gojq v0.12.1-0.20220822132002-64fe65a68424/go.mod h1:HM2cB+ANeJ4kBhxQp/4cNKewKjvYHACuCpBneQBgHkg=
|
||||
github.com/wader/readline v0.0.0-20220704090837-31be50517a56 h1:MEvdJFQfJD9D5nH2C5aXW+jWGcU1YmL8fJWIDsXvrJw=
|
||||
github.com/wader/readline v0.0.0-20220704090837-31be50517a56/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
github.com/wader/gojq v0.12.1-0.20220929141349-8874f5c7907c h1:aQz+gw5Xe2x++st4HBIaMHPPTIyPtESG2HMZJmOaRkc=
|
||||
github.com/wader/gojq v0.12.1-0.20220929141349-8874f5c7907c/go.mod h1:/+WKHSfP8sgyJDXeD9VjxlGjExGfjPUy0zm/Zy3Ind0=
|
||||
github.com/wader/readline v0.0.0-20220928125628-732951d41240 h1:fqNaldd6kVsMNGXLXH4TtB1kJtaPQOzGwqNYgswIM94=
|
||||
github.com/wader/readline v0.0.0-20220928125628-732951d41240/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20220927162542-c76eaa363f9d h1:3wgmvnqHUJ8SxiNWwea5NCzTwAVfhTtuV+0ClVFlClc=
|
||||
golang.org/x/exp v0.0.0-20220927162542-c76eaa363f9d/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -1,8 +1,8 @@
|
||||
// This is gojq:s cli/encoder.go extract to be reusable and non-global color config
|
||||
// Package colorjson is gojq:s cli/encoder.go extract to be reusable and have non-global color config
|
||||
// TODO: possible gojq can export it?
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
// Copyright (c) 2019-2021 itchyny
|
||||
// Copyright (c) 2019-2022 itchyny
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -21,23 +21,17 @@
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// skip errcheck to keep code similar to gojq version
|
||||
//
|
||||
//nolint:errcheck
|
||||
package colorjson
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type Colors struct {
|
||||
@ -53,13 +47,14 @@ type Colors struct {
|
||||
}
|
||||
|
||||
type Encoder struct {
|
||||
w *bufio.Writer
|
||||
wErr error
|
||||
out io.Writer
|
||||
w *bytes.Buffer
|
||||
tab bool
|
||||
indent int
|
||||
depth int
|
||||
buf [64]byte
|
||||
|
||||
color bool
|
||||
tab bool
|
||||
indent int
|
||||
depth int
|
||||
buf [64]byte
|
||||
valueFn func(v any) any
|
||||
colors Colors
|
||||
}
|
||||
@ -67,6 +62,7 @@ type Encoder struct {
|
||||
func NewEncoder(color bool, tab bool, indent int, valueFn func(v any) any, colors Colors) *Encoder {
|
||||
// reuse the buffer in multiple calls of marshal
|
||||
return &Encoder{
|
||||
w: new(bytes.Buffer),
|
||||
color: color,
|
||||
tab: tab,
|
||||
indent: indent,
|
||||
@ -75,16 +71,22 @@ func NewEncoder(color bool, tab bool, indent int, valueFn func(v any) any, color
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) Marshal(v any, w io.Writer) error {
|
||||
e.w = bufio.NewWriter(w)
|
||||
e.encode(v)
|
||||
if e.wErr != nil {
|
||||
return e.wErr
|
||||
}
|
||||
return e.w.Flush()
|
||||
func (e *Encoder) flush() error {
|
||||
_, err := e.out.Write(e.w.Bytes())
|
||||
e.w.Reset()
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(v any) {
|
||||
func (e *Encoder) Marshal(v interface{}, w io.Writer) error {
|
||||
e.out = w
|
||||
err := e.encode(v)
|
||||
if ferr := e.flush(); ferr != nil && err == nil {
|
||||
err = ferr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(v interface{}) error {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
e.write([]byte("null"), e.colors.Null)
|
||||
@ -102,18 +104,26 @@ func (e *Encoder) encode(v any) {
|
||||
e.write(v.Append(e.buf[:0], 10), e.colors.Number)
|
||||
case string:
|
||||
e.encodeString(v, e.colors.String)
|
||||
case []any:
|
||||
e.encodeArray(v)
|
||||
case map[string]any:
|
||||
e.encodeMap(v)
|
||||
case []interface{}:
|
||||
if err := e.encodeArray(v); err != nil {
|
||||
return err
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if err := e.encodeMap(v); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if e.valueFn != nil {
|
||||
v = e.valueFn(v)
|
||||
} else {
|
||||
panic(fmt.Sprintf("invalid value: %#+v", v))
|
||||
panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v))
|
||||
}
|
||||
e.encode(v)
|
||||
return e.encode(v)
|
||||
}
|
||||
if e.w.Len() > 8*1024 {
|
||||
return e.flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ref: floatEncoder in encoding/json
|
||||
@ -144,37 +154,38 @@ func (e *Encoder) encodeFloat64(f float64) {
|
||||
|
||||
// ref: encodeState#string in encoding/json
|
||||
func (e *Encoder) encodeString(s string, color []byte) {
|
||||
if e.color {
|
||||
e.w.Write(color)
|
||||
if color != nil {
|
||||
e.setColor(e.w, color)
|
||||
}
|
||||
e.w.WriteByte('"')
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if ']' <= b && b <= '~' || '#' <= b && b <= '[' || b == ' ' || b == '!' {
|
||||
if ' ' <= b && b <= '~' && b != '"' && b != '\\' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
e.w.WriteString(s[start:i])
|
||||
}
|
||||
e.w.WriteByte('\\')
|
||||
switch b {
|
||||
case '\\', '"':
|
||||
e.w.WriteByte(b)
|
||||
case '"':
|
||||
e.w.WriteString(`\"`)
|
||||
case '\\':
|
||||
e.w.WriteString(`\\`)
|
||||
case '\b':
|
||||
e.w.WriteByte('b')
|
||||
e.w.WriteString(`\b`)
|
||||
case '\f':
|
||||
e.w.WriteByte('f')
|
||||
e.w.WriteString(`\f`)
|
||||
case '\n':
|
||||
e.w.WriteByte('n')
|
||||
e.w.WriteString(`\n`)
|
||||
case '\r':
|
||||
e.w.WriteByte('r')
|
||||
e.w.WriteString(`\r`)
|
||||
case '\t':
|
||||
e.w.WriteByte('t')
|
||||
e.w.WriteString(`\t`)
|
||||
default:
|
||||
const hex = "0123456789abcdef"
|
||||
e.w.WriteString("u00")
|
||||
e.w.WriteString(`\u00`)
|
||||
e.w.WriteByte(hex[b>>4])
|
||||
e.w.WriteByte(hex[b&0xF])
|
||||
}
|
||||
@ -198,40 +209,39 @@ func (e *Encoder) encodeString(s string, color []byte) {
|
||||
e.w.WriteString(s[start:])
|
||||
}
|
||||
e.w.WriteByte('"')
|
||||
if e.color {
|
||||
e.w.Write(e.colors.Reset)
|
||||
if color != nil {
|
||||
e.setColor(e.w, e.colors.Reset)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeArray(vs []any) {
|
||||
func (e *Encoder) encodeArray(vs []interface{}) error {
|
||||
e.writeByte('[', e.colors.Array)
|
||||
e.depth += e.indent
|
||||
for i, v := range vs {
|
||||
if e.wErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
e.writeByte(',', e.colors.Array)
|
||||
}
|
||||
if e.indent != 0 {
|
||||
e.writeIndent()
|
||||
}
|
||||
e.encode(v)
|
||||
if err := e.encode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.depth -= e.indent
|
||||
if len(vs) > 0 && e.indent != 0 {
|
||||
e.writeIndent()
|
||||
}
|
||||
e.writeByte(']', e.colors.Array)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeMap(vs map[string]any) {
|
||||
func (e *Encoder) encodeMap(vs map[string]interface{}) error {
|
||||
e.writeByte('{', e.colors.Object)
|
||||
e.depth += e.indent
|
||||
type keyVal struct {
|
||||
key string
|
||||
val any
|
||||
val interface{}
|
||||
}
|
||||
kvs := make([]keyVal, len(vs))
|
||||
var i int
|
||||
@ -239,12 +249,10 @@ func (e *Encoder) encodeMap(vs map[string]any) {
|
||||
kvs[i] = keyVal{k, v}
|
||||
i++
|
||||
}
|
||||
slices.SortFunc(kvs, func(a, b keyVal) bool { return a.key < b.key })
|
||||
sort.Slice(kvs, func(i, j int) bool {
|
||||
return kvs[i].key < kvs[j].key
|
||||
})
|
||||
for i, kv := range kvs {
|
||||
if e.wErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
e.writeByte(',', e.colors.Object)
|
||||
}
|
||||
@ -256,82 +264,65 @@ func (e *Encoder) encodeMap(vs map[string]any) {
|
||||
if e.indent != 0 {
|
||||
e.w.WriteByte(' ')
|
||||
}
|
||||
e.encode(kv.val)
|
||||
if err := e.encode(kv.val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.depth -= e.indent
|
||||
if len(vs) > 0 && e.indent != 0 {
|
||||
e.writeIndent()
|
||||
}
|
||||
e.writeByte('}', e.colors.Object)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeIndent() {
|
||||
e.w.WriteByte('\n')
|
||||
if n := e.depth; n > 0 {
|
||||
if e.tab {
|
||||
const tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
|
||||
for n > len(tabs) {
|
||||
e.w.Write([]byte(tabs))
|
||||
n -= len(tabs)
|
||||
}
|
||||
e.w.Write([]byte(tabs)[:n])
|
||||
e.writeIndentInternal(n, "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")
|
||||
} else {
|
||||
const spaces = " "
|
||||
for n > len(spaces) {
|
||||
e.w.Write([]byte(spaces))
|
||||
n -= len(spaces)
|
||||
e.writeIndentInternal(n, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) writeIndentInternal(n int, spaces string) {
|
||||
if l := len(spaces); n <= l {
|
||||
e.w.WriteString(spaces[:n])
|
||||
} else {
|
||||
e.w.WriteString(spaces)
|
||||
for n -= l; n > 0; n, l = n-l, l*2 {
|
||||
if n < l {
|
||||
l = n
|
||||
}
|
||||
e.w.Write([]byte(spaces)[:n])
|
||||
e.w.Write(e.w.Bytes()[e.w.Len()-l:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) writeByte(b byte, color []byte) {
|
||||
if e.wErr != nil {
|
||||
return
|
||||
}
|
||||
if color == nil {
|
||||
if err := e.w.WriteByte(b); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
e.w.WriteByte(b)
|
||||
} else {
|
||||
if e.color {
|
||||
if _, err := e.w.Write(color); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
}
|
||||
if err := e.w.WriteByte(b); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
if e.color {
|
||||
if _, err := e.w.Write(e.colors.Reset); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
}
|
||||
e.setColor(e.w, color)
|
||||
e.w.WriteByte(b)
|
||||
e.setColor(e.w, e.colors.Reset)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) write(bs []byte, color []byte) {
|
||||
if e.wErr != nil {
|
||||
return
|
||||
}
|
||||
if color == nil {
|
||||
if _, err := e.w.Write(bs); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
e.w.Write(bs)
|
||||
} else {
|
||||
if e.color {
|
||||
if _, err := e.w.Write(color); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
}
|
||||
if _, err := e.w.Write(bs); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
if e.color {
|
||||
if _, err := e.w.Write(e.colors.Reset); err != nil {
|
||||
e.wErr = err
|
||||
}
|
||||
}
|
||||
e.setColor(e.w, color)
|
||||
e.w.Write(bs)
|
||||
e.setColor(e.w, e.colors.Reset)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) setColor(buf *bytes.Buffer, color []byte) {
|
||||
if e.color {
|
||||
buf.Write(color)
|
||||
}
|
||||
}
|
||||
|
@ -688,11 +688,15 @@ func (d *D) trySeekAbs(pos int64, fns ...func(d *D)) (int64, error) {
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// SeekRel seeks relative to current bit position
|
||||
// TrySeekRel seeks relative to current bit position. If one or more functions is
|
||||
// passed, the original seek position will be restored after decoding.
|
||||
func (d *D) TrySeekRel(delta int64, fns ...func(d *D)) (int64, error) {
|
||||
return d.trySeekAbs(d.Pos()+delta, fns...)
|
||||
}
|
||||
|
||||
// SeekRel seeks relative to current bit position. If one or more functions is
|
||||
// passed, the original seek position will be restored after decoding. Panics if
|
||||
// an error occurs while seeking.
|
||||
func (d *D) SeekRel(delta int64, fns ...func(d *D)) int64 {
|
||||
n, err := d.trySeekAbs(d.Pos()+delta, fns...)
|
||||
if err != nil {
|
||||
@ -701,7 +705,8 @@ func (d *D) SeekRel(delta int64, fns ...func(d *D)) int64 {
|
||||
return n
|
||||
}
|
||||
|
||||
// SeekAbs seeks to absolute position
|
||||
// TrySeekAbs seeks to absolute position. If one or more functions is passed,
|
||||
// the original seek position will be restored after decoding.
|
||||
func (d *D) TrySeekAbs(pos int64, fns ...func(d *D)) (int64, error) {
|
||||
return d.trySeekAbs(pos, fns...)
|
||||
}
|
||||
@ -798,6 +803,15 @@ func (d *D) FieldStructArrayLoop(name string, structName string, condFn func() b
|
||||
})
|
||||
}
|
||||
|
||||
// FieldStructNArray decodes an array of elements with a known count.
|
||||
func (d *D) FieldStructNArray(name string, structName string, count int64, fn func(d *D)) *D {
|
||||
return d.FieldArray(name, func(d *D) {
|
||||
for i := int64(0); i < count; i++ {
|
||||
d.FieldStruct(structName, fn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (d *D) FieldArrayLoop(name string, condFn func() bool, fn func(d *D)) *D {
|
||||
return d.FieldArray(name, func(d *D) {
|
||||
for condFn() {
|
||||
|
@ -289,7 +289,7 @@ func (i *Interp) _decode(c any, format string, opts decodeOpts) any {
|
||||
}
|
||||
}
|
||||
|
||||
return makeDecodeValueOut(dv, formatOutMap)
|
||||
return makeDecodeValueOut(dv, decodeValueValue, formatOutMap)
|
||||
}
|
||||
|
||||
func valueKey(name string, a, b func(name string) any) any {
|
||||
@ -326,11 +326,19 @@ func toValue(optsFn func() Options, v any) (any, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeDecodeValue(dv *decode.Value) any {
|
||||
return makeDecodeValueOut(dv, nil)
|
||||
type decodeValueKind int
|
||||
|
||||
const (
|
||||
decodeValueValue decodeValueKind = iota
|
||||
decodeValueActual
|
||||
decodeValueSym
|
||||
)
|
||||
|
||||
func makeDecodeValue(dv *decode.Value, kind decodeValueKind) any {
|
||||
return makeDecodeValueOut(dv, kind, nil)
|
||||
}
|
||||
|
||||
func makeDecodeValueOut(dv *decode.Value, out any) any {
|
||||
func makeDecodeValueOut(dv *decode.Value, kind decodeValueKind, out any) any {
|
||||
switch vv := dv.V.(type) {
|
||||
case *decode.Compound:
|
||||
if vv.IsArray {
|
||||
@ -338,7 +346,18 @@ func makeDecodeValueOut(dv *decode.Value, out any) any {
|
||||
}
|
||||
return NewStructDecodeValue(dv, out, vv)
|
||||
case *scalar.S:
|
||||
switch vv := vv.Value().(type) {
|
||||
// TODO: rethink value/actual/sym handling
|
||||
var vvv any
|
||||
switch kind {
|
||||
case decodeValueValue:
|
||||
vvv = vv.Value()
|
||||
case decodeValueActual:
|
||||
vvv = vv.Actual
|
||||
case decodeValueSym:
|
||||
vvv = vv.Sym
|
||||
}
|
||||
|
||||
switch vvv := vvv.(type) {
|
||||
case bitio.ReaderAtSeeker:
|
||||
// is lazy so that in situations where the decode value is only used to
|
||||
// create another binary we don't have to read and create a string, ex:
|
||||
@ -349,11 +368,11 @@ func makeDecodeValueOut(dv *decode.Value, out any) any {
|
||||
IsScalar: true,
|
||||
Fn: func() (gojq.JQValue, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
vvC, err := bitio.CloneReader(vv)
|
||||
vvvC, err := bitio.CloneReader(vvv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := bitioex.CopyBits(buf, vvC); err != nil {
|
||||
if _, err := bitioex.CopyBits(buf, vvvC); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gojqex.String([]rune(buf.String())), nil
|
||||
@ -364,42 +383,42 @@ func makeDecodeValueOut(dv *decode.Value, out any) any {
|
||||
}
|
||||
case bool:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Boolean(vv),
|
||||
JQValue: gojqex.Boolean(vvv),
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case int:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Number{V: vv},
|
||||
JQValue: gojqex.Number{V: vvv},
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case int64:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Number{V: big.NewInt(vv)},
|
||||
JQValue: gojqex.Number{V: big.NewInt(vvv)},
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case uint64:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Number{V: new(big.Int).SetUint64(vv)},
|
||||
JQValue: gojqex.Number{V: new(big.Int).SetUint64(vvv)},
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case float64:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Number{V: vv},
|
||||
JQValue: gojqex.Number{V: vvv},
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case string:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.String(vv),
|
||||
JQValue: gojqex.String(vvv),
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case []any:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Array(vv),
|
||||
JQValue: gojqex.Array(vvv),
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case map[string]any:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Object(vv),
|
||||
JQValue: gojqex.Object(vvv),
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
case nil:
|
||||
@ -409,11 +428,11 @@ func makeDecodeValueOut(dv *decode.Value, out any) any {
|
||||
}
|
||||
case *big.Int:
|
||||
return decodeValue{
|
||||
JQValue: gojqex.Number{V: vv},
|
||||
JQValue: gojqex.Number{V: vvv},
|
||||
decodeValueBase: decodeValueBase{dv: dv},
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unreachable vv %#+v", vv))
|
||||
panic(fmt.Sprintf("unreachable vv %#+v", vvv))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unreachable dv %#+v", dv))
|
||||
@ -482,37 +501,29 @@ func (dvb decodeValueBase) JQValueKey(name string) any {
|
||||
case "_name":
|
||||
return dv.Name
|
||||
case "_root":
|
||||
return makeDecodeValue(dv.Root())
|
||||
return makeDecodeValue(dv.Root(), decodeValueValue)
|
||||
case "_buffer_root":
|
||||
// TODO: rename?
|
||||
return makeDecodeValue(dv.BufferRoot())
|
||||
return makeDecodeValue(dv.BufferRoot(), decodeValueValue)
|
||||
case "_format_root":
|
||||
// TODO: rename?
|
||||
return makeDecodeValue(dv.FormatRoot())
|
||||
return makeDecodeValue(dv.FormatRoot(), decodeValueValue)
|
||||
case "_parent":
|
||||
if dv.Parent == nil {
|
||||
return nil
|
||||
}
|
||||
return makeDecodeValue(dv.Parent)
|
||||
return makeDecodeValue(dv.Parent, decodeValueValue)
|
||||
case "_actual":
|
||||
switch vv := dv.V.(type) {
|
||||
switch dv.V.(type) {
|
||||
case *scalar.S:
|
||||
jv, ok := gojqex.ToGoJQValue(vv.Actual)
|
||||
if !ok {
|
||||
return fmt.Errorf("can't convert actual value jq value %#+v", vv.Actual)
|
||||
}
|
||||
return jv
|
||||
return makeDecodeValue(dv, decodeValueActual)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case "_sym":
|
||||
switch vv := dv.V.(type) {
|
||||
switch dv.V.(type) {
|
||||
case *scalar.S:
|
||||
jv, ok := gojqex.ToGoJQValue(vv.Sym)
|
||||
if !ok {
|
||||
return fmt.Errorf("can't convert sym value jq value %#+v", vv.Actual)
|
||||
}
|
||||
return jv
|
||||
return makeDecodeValue(dv, decodeValueSym)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -642,19 +653,19 @@ func (v ArrayDecodeValue) JQValueIndex(index int) any {
|
||||
if index < 0 {
|
||||
return nil
|
||||
}
|
||||
return makeDecodeValue((v.Compound.Children)[index])
|
||||
return makeDecodeValue((v.Compound.Children)[index], decodeValueValue)
|
||||
}
|
||||
func (v ArrayDecodeValue) JQValueSlice(start int, end int) any {
|
||||
vs := make([]any, end-start)
|
||||
for i, e := range (v.Compound.Children)[start:end] {
|
||||
vs[i] = makeDecodeValue(e)
|
||||
vs[i] = makeDecodeValue(e, decodeValueValue)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
func (v ArrayDecodeValue) JQValueEach() any {
|
||||
props := make([]gojq.PathValue, len(v.Compound.Children))
|
||||
for i, f := range v.Compound.Children {
|
||||
props[i] = gojq.PathValue{Path: i, Value: makeDecodeValue(f)}
|
||||
props[i] = gojq.PathValue{Path: i, Value: makeDecodeValue(f, decodeValueValue)}
|
||||
}
|
||||
return props
|
||||
}
|
||||
@ -680,7 +691,7 @@ func (v ArrayDecodeValue) JQValueHas(key any) any {
|
||||
func (v ArrayDecodeValue) JQValueToGoJQ() any {
|
||||
vs := make([]any, len(v.Compound.Children))
|
||||
for i, f := range v.Compound.Children {
|
||||
vs[i] = makeDecodeValue(f)
|
||||
vs[i] = makeDecodeValue(f, decodeValueValue)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
@ -711,7 +722,7 @@ func (v StructDecodeValue) JQValueKey(name string) any {
|
||||
}
|
||||
if v.Compound.ByName != nil {
|
||||
if f, ok := v.Compound.ByName[name]; ok {
|
||||
return makeDecodeValue(f)
|
||||
return makeDecodeValue(f, decodeValueValue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,7 +731,7 @@ func (v StructDecodeValue) JQValueKey(name string) any {
|
||||
func (v StructDecodeValue) JQValueEach() any {
|
||||
props := make([]gojq.PathValue, len(v.Compound.Children))
|
||||
for i, f := range v.Compound.Children {
|
||||
props[i] = gojq.PathValue{Path: f.Name, Value: makeDecodeValue(f)}
|
||||
props[i] = gojq.PathValue{Path: f.Name, Value: makeDecodeValue(f, decodeValueValue)}
|
||||
}
|
||||
return props
|
||||
}
|
||||
@ -752,7 +763,7 @@ func (v StructDecodeValue) JQValueHas(key any) any {
|
||||
func (v StructDecodeValue) JQValueToGoJQ() any {
|
||||
vm := make(map[string]any, len(v.Compound.Children))
|
||||
for _, f := range v.Compound.Children {
|
||||
vm[f.Name] = makeDecodeValue(f)
|
||||
vm[f.Name] = makeDecodeValue(f, decodeValueValue)
|
||||
}
|
||||
return vm
|
||||
}
|
||||
|
@ -56,8 +56,10 @@ def decode: decode(options.decode_format; {});
|
||||
def topath: _decode_value(._path);
|
||||
def tovalue($opts): _tovalue(options($opts));
|
||||
def tovalue: _tovalue(options({}));
|
||||
def toactual: _decode_value(._actual);
|
||||
def tosym: _decode_value(._sym);
|
||||
def toactual($opts): _decode_value(._actual) | tovalue($opts);
|
||||
def toactual: toactual({});
|
||||
def tosym($opts): _decode_value(._sym) | tovalue($opts);
|
||||
def tosym: tosym({});
|
||||
def todescription: _decode_value(._description);
|
||||
|
||||
# TODO: rename?
|
||||
|
@ -135,6 +135,9 @@ def _cli_repl_error($_):
|
||||
_eval_error("compile"; "repl can only be used from interactive repl");
|
||||
def _cli_slurp_error(_):
|
||||
_eval_error("compile"; "slurp can only be used from interactive repl");
|
||||
# TODO: rewrite query to reuse _display_default_opts value? also _repl_display
|
||||
def _cli_display:
|
||||
display(_display_default_opts);
|
||||
# _cli_eval halts on compile errors
|
||||
def _cli_eval($expr; $opts):
|
||||
eval(
|
||||
@ -145,7 +148,7 @@ def _cli_eval($expr; $opts):
|
||||
repl: "_cli_repl_error",
|
||||
slurp: "_cli_slurp_error"
|
||||
},
|
||||
catch_query: _query_func("_cli_eval_on_expr_error")
|
||||
catch_query: _query_func("_cli_eval_on_expr_error"),
|
||||
};
|
||||
_cli_eval_on_error;
|
||||
_cli_eval_on_compile_error
|
||||
@ -235,7 +238,6 @@ def _main:
|
||||
)
|
||||
else
|
||||
( _cli_last_expr_error(null) as $_
|
||||
| _display_default_opts as $default_opts
|
||||
| _cli_eval(
|
||||
$opts.expr;
|
||||
( $eval_opts
|
||||
@ -248,9 +250,11 @@ def _main:
|
||||
else _query_func("inputs")
|
||||
end
|
||||
)
|
||||
# call display in sub eval so it can be interrupted
|
||||
# for repl case value will used as input to _repl instead
|
||||
| .output_query = _query_func("_cli_display")
|
||||
)
|
||||
)
|
||||
| display($default_opts)
|
||||
)
|
||||
end;
|
||||
# finally
|
||||
|
@ -227,7 +227,7 @@ def _repl_eval($expr; on_error; on_compile_error):
|
||||
},
|
||||
# input to repl is always array of values to iterate
|
||||
input_query: (_query_ident | _query_iter), # .[]
|
||||
# each input should be evaluted separatel like with cli, so catch and just print errors
|
||||
# each input should be evaluted separately like cli file args, so catch and just print errors
|
||||
catch_query: _query_func("_repl_on_expr_error"),
|
||||
# run display in sub eval so it can be interrupted
|
||||
output_query: _query_func("_repl_display")
|
||||
|
10
pkg/interp/testdata/value_array.fqtest
vendored
10
pkg/interp/testdata/value_array.fqtest
vendored
@ -1,5 +1,5 @@
|
||||
$ fq -i -d mp3 . test.mp3
|
||||
mp3> .headers | ., tovalue, type, length?
|
||||
mp3> .headers | ., tovalue, toactual, tosym, type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.headers[0:1]:
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x00|49 44 33 04 00 00 00 00 00 23 54 53 53 45 00 00|ID3......#TSSE..| [0]{}: header (id3v2)
|
||||
@ -40,6 +40,8 @@ mp3> .headers | ., tovalue, type, length?
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
null
|
||||
null
|
||||
"array"
|
||||
1
|
||||
mp3> .headers[0] | ., type, length?
|
||||
@ -149,6 +151,10 @@ mp3> .headers._name | ., type, length?
|
||||
"headers"
|
||||
"string"
|
||||
7
|
||||
mp3> .headers._actual | ., type, length?
|
||||
null
|
||||
"null"
|
||||
0
|
||||
mp3> .headers._sym | ., type, length?
|
||||
null
|
||||
"null"
|
||||
@ -169,14 +175,12 @@ mp3> .headers._bits | ., type, length?
|
||||
* |until 0x2c.7 (45) | |
|
||||
"string"
|
||||
360
|
||||
mp3>
|
||||
mp3> .headers._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x00|49 44 33 04 00 00 00 00 00 23 54 53 53 45 00 00|ID3......#TSSE..|.: raw bits 0x0-0x2c.7 (45)
|
||||
* |until 0x2c.7 (45) | |
|
||||
"string"
|
||||
45
|
||||
mp3>
|
||||
mp3> .headers._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
13
pkg/interp/testdata/value_boolean.fqtest
vendored
13
pkg/interp/testdata/value_boolean.fqtest
vendored
@ -1,8 +1,10 @@
|
||||
$ fq -i -d mp3 . test.mp3
|
||||
mp3> .headers[0].flags.unsynchronisation | ., tovalue, type, length?
|
||||
mp3> .headers[0].flags.unsynchronisation | ., tovalue, toactual, tosym, type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 00 | . |.headers[0].flags.unsynchronisation: false
|
||||
false
|
||||
false
|
||||
null
|
||||
"boolean"
|
||||
mp3> .headers[0].flags.unsynchronisation[0] | ., type, length?
|
||||
error: expected an array but got: boolean
|
||||
@ -52,8 +54,13 @@ mp3> .headers[0].flags.unsynchronisation._name | ., type, length?
|
||||
"unsynchronisation"
|
||||
"string"
|
||||
17
|
||||
mp3> .headers[0].flags.unsynchronisation._actual | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 00 | . |.headers[0].flags.unsynchronisation: false
|
||||
"boolean"
|
||||
mp3> .headers[0].flags.unsynchronisation._sym | ., type, length?
|
||||
null
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 00 | . |.headers[0].flags.unsynchronisation: false
|
||||
"null"
|
||||
0
|
||||
mp3> .headers[0].flags.unsynchronisation._description | ., type, length?
|
||||
@ -74,13 +81,11 @@ mp3> .headers[0].flags.unsynchronisation._bits | ., type, length?
|
||||
0x0| 00 | . |.: raw bits 0x5-0x5 (0.1)
|
||||
"string"
|
||||
1
|
||||
mp3>
|
||||
mp3> .headers[0].flags.unsynchronisation._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 00 | . |.: raw bits 0x5-0x5 (0.1)
|
||||
"string"
|
||||
0
|
||||
mp3>
|
||||
mp3> .headers[0].flags.unsynchronisation._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
10
pkg/interp/testdata/value_json_array.fqtest
vendored
10
pkg/interp/testdata/value_json_array.fqtest
vendored
@ -1,7 +1,9 @@
|
||||
$ fq -i -n '"[]" | json'
|
||||
json> (.) | ., tovalue, type, length?
|
||||
json> (.) | ., tovalue, toactual, tosym, type, length?
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
null
|
||||
"array"
|
||||
0
|
||||
json> (.)[0] | ., type, length?
|
||||
@ -65,6 +67,10 @@ json> (.)._name | ., type, length?
|
||||
""
|
||||
"string"
|
||||
0
|
||||
json> (.)._actual | ., type, length?
|
||||
[]
|
||||
"array"
|
||||
0
|
||||
json> (.)._sym | ., type, length?
|
||||
null
|
||||
"null"
|
||||
@ -82,13 +88,11 @@ json> (.)._bits | ., type, length?
|
||||
0x0|5b 5d| |[]| |.: raw bits 0x0-0x1.7 (2)
|
||||
"string"
|
||||
16
|
||||
json>
|
||||
json> (.)._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0|5b 5d| |[]| |.: raw bits 0x0-0x1.7 (2)
|
||||
"string"
|
||||
2
|
||||
json>
|
||||
json> (.)._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
10
pkg/interp/testdata/value_json_object.fqtest
vendored
10
pkg/interp/testdata/value_json_object.fqtest
vendored
@ -1,7 +1,9 @@
|
||||
$ fq -i -n '"{}" | json'
|
||||
json> (.) | ., tovalue, type, length?
|
||||
json> (.) | ., tovalue, toactual, tosym, type, length?
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
null
|
||||
"object"
|
||||
0
|
||||
json> (.)[0] | ., type, length?
|
||||
@ -55,6 +57,10 @@ json> (.)._name | ., type, length?
|
||||
""
|
||||
"string"
|
||||
0
|
||||
json> (.)._actual | ., type, length?
|
||||
{}
|
||||
"object"
|
||||
0
|
||||
json> (.)._sym | ., type, length?
|
||||
null
|
||||
"null"
|
||||
@ -72,13 +78,11 @@ json> (.)._bits | ., type, length?
|
||||
0x0|7b 7d| |{}| |.: raw bits 0x0-0x1.7 (2)
|
||||
"string"
|
||||
16
|
||||
json>
|
||||
json> (.)._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0|7b 7d| |{}| |.: raw bits 0x0-0x1.7 (2)
|
||||
"string"
|
||||
2
|
||||
json>
|
||||
json> (.)._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
14
pkg/interp/testdata/value_null.fqtest
vendored
14
pkg/interp/testdata/value_null.fqtest
vendored
@ -1,8 +1,10 @@
|
||||
$ fq -i -d mp3 . test.mp3
|
||||
mp3> .headers[0].padding | ., tovalue, type, length?
|
||||
mp3> .headers[0].padding | ., tovalue, toactual, tosym, type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x20| 00 00 00 00 00 00 00 00 00 00 | .......... |.headers[0].padding: raw bits (all zero)
|
||||
"<10>AAAAAAAAAAAAAA=="
|
||||
"<10>AAAAAAAAAAAAAA=="
|
||||
null
|
||||
"string"
|
||||
10
|
||||
mp3> .headers[0].padding[0] | ., type, length?
|
||||
@ -65,8 +67,14 @@ mp3> .headers[0].padding._name | ., type, length?
|
||||
"padding"
|
||||
"string"
|
||||
7
|
||||
mp3> .headers[0].padding._actual | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x20| 00 00 00 00 00 00 00 00 00 00 | .......... |.headers[0].padding: raw bits (all zero)
|
||||
"string"
|
||||
10
|
||||
mp3> .headers[0].padding._sym | ., type, length?
|
||||
null
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x20| 00 00 00 00 00 00 00 00 00 00 | .......... |.headers[0].padding: raw bits (all zero)
|
||||
"null"
|
||||
0
|
||||
mp3> .headers[0].padding._description | ., type, length?
|
||||
@ -86,13 +94,11 @@ mp3> .headers[0].padding._bits | ., type, length?
|
||||
0x20| 00 00 00 00 00 00 00 00 00 00 | .......... |.: raw bits 0x23-0x2c.7 (10)
|
||||
"string"
|
||||
80
|
||||
mp3>
|
||||
mp3> .headers[0].padding._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x20| 00 00 00 00 00 00 00 00 00 00 | .......... |.: raw bits 0x23-0x2c.7 (10)
|
||||
"string"
|
||||
10
|
||||
mp3>
|
||||
mp3> .headers[0].padding._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
14
pkg/interp/testdata/value_number.fqtest
vendored
14
pkg/interp/testdata/value_number.fqtest
vendored
@ -1,8 +1,10 @@
|
||||
$ fq -i -d mp3 . test.mp3
|
||||
mp3> .headers[0].version | ., tovalue, type, length?
|
||||
mp3> .headers[0].version | ., tovalue, toactual, tosym, type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 04 | . |.headers[0].version: 4
|
||||
4
|
||||
4
|
||||
null
|
||||
"number"
|
||||
4
|
||||
mp3> .headers[0].version[0] | ., type, length?
|
||||
@ -53,8 +55,14 @@ mp3> .headers[0].version._name | ., type, length?
|
||||
"version"
|
||||
"string"
|
||||
7
|
||||
mp3> .headers[0].version._actual | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 04 | . |.headers[0].version: 4
|
||||
"number"
|
||||
4
|
||||
mp3> .headers[0].version._sym | ., type, length?
|
||||
null
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 04 | . |.headers[0].version: 4
|
||||
"null"
|
||||
0
|
||||
mp3> .headers[0].version._description | ., type, length?
|
||||
@ -74,13 +82,11 @@ mp3> .headers[0].version._bits | ., type, length?
|
||||
0x0| 04 | . |.: raw bits 0x3-0x3.7 (1)
|
||||
"string"
|
||||
8
|
||||
mp3>
|
||||
mp3> .headers[0].version._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 04 | . |.: raw bits 0x3-0x3.7 (1)
|
||||
"string"
|
||||
1
|
||||
mp3>
|
||||
mp3> .headers[0].version._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
10
pkg/interp/testdata/value_object.fqtest
vendored
10
pkg/interp/testdata/value_object.fqtest
vendored
@ -1,5 +1,5 @@
|
||||
$ fq -i -d mp3 . test.mp3
|
||||
mp3> .headers[0].flags | ., tovalue, type, length?
|
||||
mp3> .headers[0].flags | ., tovalue, toactual, tosym, type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.headers[0].flags{}:
|
||||
0x0| 00 | . | unsynchronisation: false
|
||||
0x0| 00 | . | extended_header: false
|
||||
@ -11,6 +11,8 @@ mp3> .headers[0].flags | ., tovalue, type, length?
|
||||
"unsynchronisation": false,
|
||||
"unused": 0
|
||||
}
|
||||
null
|
||||
null
|
||||
"object"
|
||||
4
|
||||
mp3> .headers[0].flags[0] | ., type, length?
|
||||
@ -69,6 +71,10 @@ mp3> .headers[0].flags._name | ., type, length?
|
||||
"flags"
|
||||
"string"
|
||||
5
|
||||
mp3> .headers[0].flags._actual | ., type, length?
|
||||
null
|
||||
"null"
|
||||
0
|
||||
mp3> .headers[0].flags._sym | ., type, length?
|
||||
null
|
||||
"null"
|
||||
@ -90,13 +96,11 @@ mp3> .headers[0].flags._bits | ., type, length?
|
||||
0x0| 00 | . |.: raw bits 0x5-0x5.7 (1)
|
||||
"string"
|
||||
8
|
||||
mp3>
|
||||
mp3> .headers[0].flags._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0| 00 | . |.: raw bits 0x5-0x5.7 (1)
|
||||
"string"
|
||||
1
|
||||
mp3>
|
||||
mp3> .headers[0].flags._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
14
pkg/interp/testdata/value_string.fqtest
vendored
14
pkg/interp/testdata/value_string.fqtest
vendored
@ -1,8 +1,10 @@
|
||||
$ fq -i -d mp3 . test.mp3
|
||||
mp3> .headers[0].magic | ., tovalue, type, length?
|
||||
mp3> .headers[0].magic | ., tovalue, toactual, tosym, type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0|49 44 33 |ID3 |.headers[0].magic: "ID3" (valid)
|
||||
"ID3"
|
||||
"ID3"
|
||||
null
|
||||
"string"
|
||||
3
|
||||
mp3> .headers[0].magic[0] | ., type, length?
|
||||
@ -65,8 +67,14 @@ mp3> .headers[0].magic._name | ., type, length?
|
||||
"magic"
|
||||
"string"
|
||||
5
|
||||
mp3> .headers[0].magic._actual | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0|49 44 33 |ID3 |.headers[0].magic: "ID3" (valid)
|
||||
"string"
|
||||
3
|
||||
mp3> .headers[0].magic._sym | ., type, length?
|
||||
null
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0|49 44 33 |ID3 |.headers[0].magic: "ID3" (valid)
|
||||
"null"
|
||||
0
|
||||
mp3> .headers[0].magic._description | ., type, length?
|
||||
@ -86,13 +94,11 @@ mp3> .headers[0].magic._bits | ., type, length?
|
||||
0x0|49 44 33 |ID3 |.: raw bits 0x0-0x2.7 (3)
|
||||
"string"
|
||||
24
|
||||
mp3>
|
||||
mp3> .headers[0].magic._bytes | ., type, length?
|
||||
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|
|
||||
0x0|49 44 33 |ID3 |.: raw bits 0x0-0x2.7 (3)
|
||||
"string"
|
||||
3
|
||||
mp3>
|
||||
mp3> .headers[0].magic._error | ., type, length?
|
||||
null
|
||||
"null"
|
||||
|
6
pkg/interp/testdata/value_t.fqtest.tmpl
vendored
6
pkg/interp/testdata/value_t.fqtest.tmpl
vendored
@ -1,6 +1,5 @@
|
||||
/test.mp3:
|
||||
$ CMD
|
||||
PROMPT> EXPR | ., tovalue, type, length?
|
||||
PROMPT> EXPR | ., tovalue, toactual, tosym, type, length?
|
||||
PROMPT> EXPR[0] | ., type, length?
|
||||
PROMPT> EXPR[-1000] | ., type, length?
|
||||
PROMPT> EXPR[1000] | ., type, length?
|
||||
@ -21,7 +20,8 @@ PROMPT> EXPR._start | ., type, length?
|
||||
PROMPT> EXPR._stop | ., type, length?
|
||||
PROMPT> EXPR._len | ., type, length?
|
||||
PROMPT> EXPR._name | ., type, length?
|
||||
PROMPT> EXPR._symbol | ., type, length?
|
||||
PROMPT> EXPR._actual | ., type, length?
|
||||
PROMPT> EXPR._sym | ., type, length?
|
||||
PROMPT> EXPR._description | ., type, length?
|
||||
PROMPT> EXPR._path | ., type, length?
|
||||
PROMPT> EXPR._bits | ., type, length?
|
||||
|
12
pkg/interp/testdata/value_t.fqtest.tmpl.sh
vendored
12
pkg/interp/testdata/value_t.fqtest.tmpl.sh
vendored
@ -1,10 +1,10 @@
|
||||
#!/bin/sh
|
||||
sed 's/CMD/fq -i -d mp3 . \/test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers/g' | sed 's/PROMPT/mp3/g' >value_array.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . \/test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].flags.unsynchronisation/g' | sed 's/PROMPT/mp3/g' >value_boolean.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . \/test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].padding/g' | sed 's/PROMPT/mp3/g' >value_null.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . \/test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].version/g' | sed 's/PROMPT/mp3/g' >value_number.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . \/test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].flags/g' | sed 's/PROMPT/mp3/g' >value_object.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . \/test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].magic/g' | sed 's/PROMPT/mp3/g' >value_string.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers/g' | sed 's/PROMPT/mp3/g' >value_array.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].flags.unsynchronisation/g' | sed 's/PROMPT/mp3/g' >value_boolean.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].padding/g' | sed 's/PROMPT/mp3/g' >value_null.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].version/g' | sed 's/PROMPT/mp3/g' >value_number.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].flags/g' | sed 's/PROMPT/mp3/g' >value_object.fqtest
|
||||
sed 's/CMD/fq -i -d mp3 . test.mp3/g' <value_t.fqtest.tmpl | sed 's/EXPR/.headers[0].magic/g' | sed 's/PROMPT/mp3/g' >value_string.fqtest
|
||||
|
||||
sed "s/CMD/fq -i -n '\"[]\" | json'/g" <value_t.fqtest.tmpl | sed 's/EXPR/(.)/g' | sed 's/PROMPT/json/g' >value_json_array.fqtest
|
||||
sed "s/CMD/fq -i -n '\"{}\" | json'/g" <value_t.fqtest.tmpl | sed 's/EXPR/(.)/g' | sed 's/PROMPT/json/g' >value_json_object.fqtest
|
||||
|
@ -73,6 +73,14 @@ func Gaps(total Range, ranges []Range) []Range {
|
||||
for i := 0; i < len(ranges); {
|
||||
madded = false
|
||||
m = ranges[i]
|
||||
|
||||
// skip empty ranges
|
||||
if m.Len == 0 {
|
||||
i++
|
||||
madded = true
|
||||
continue
|
||||
}
|
||||
|
||||
j := i + 1
|
||||
for ; j < len(ranges); j++ {
|
||||
if m.Start <= ranges[j].Start && m.Stop()+1 >= ranges[j].Start {
|
||||
@ -91,10 +99,15 @@ func Gaps(total Range, ranges []Range) []Range {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !madded {
|
||||
merged = append(merged, m)
|
||||
}
|
||||
|
||||
if len(merged) == 0 {
|
||||
return []Range{total}
|
||||
}
|
||||
|
||||
gaps := make([]Range, 0, len(merged))
|
||||
if merged[0].Start != total.Start {
|
||||
gaps = append(gaps, Range{Start: 0, Len: merged[0].Start})
|
||||
|
@ -27,6 +27,13 @@ func TestRangeGaps(t *testing.T) {
|
||||
{"0:10", "1:1 2:5 8:1", "0:1 9:1"},
|
||||
{"0:10", "1:1 2:8 8:2", "0:1"},
|
||||
{"0:10", "0:4 2:8 8:2", ""},
|
||||
|
||||
// handle empty ranges
|
||||
{"0:12", "4:4 8:0", "0:4 8:4"},
|
||||
{"0:12", "0:0 4:4", "0:4 8:4"},
|
||||
{"0:12", "0:0 4:4 8:0", "0:4 8:4"},
|
||||
{"0:12", "0:0 0:0 4:4 8:0 8:0", "0:4 8:4"},
|
||||
{"0:12", "8:0", "0:12"},
|
||||
}
|
||||
for _, tC := range testCases {
|
||||
t.Run(fmt.Sprintf("%v_%v_%v", tC.total, tC.ranges, tC.expected), func(t *testing.T) {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/wader/fq/internal/bitioex"
|
||||
"github.com/wader/fq/pkg/bitio"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
//go:generate sh -c "cat scalar_gen.go.tmpl | go run ../../dev/tmpl.go ../decode/types.json | gofmt > scalar_gen.go"
|
||||
@ -246,40 +247,48 @@ func (m BytesToScalar) MapScalar(s S) (S, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// TODO: nicer api, use generics
|
||||
// TODO: nicer api, use generic, many generic Try function somehow?
|
||||
|
||||
func mapFn[T any](tryVFn func(S) (T, bool), vFn func(S) T, fn func(s S, v T) S) Mapper {
|
||||
return Fn(func(s S) (S, error) {
|
||||
var v T
|
||||
var ok bool
|
||||
if tryVFn != nil {
|
||||
v, ok = tryVFn(s)
|
||||
if !ok {
|
||||
return s, nil
|
||||
}
|
||||
} else {
|
||||
v = vFn(s)
|
||||
}
|
||||
return fn(s, v), nil
|
||||
})
|
||||
}
|
||||
|
||||
func TryMapFn[T any](epoch time.Time, format string, vFn func(S) (T, bool), fn func(s S, v T) S) Mapper {
|
||||
return mapFn(vFn, nil, fn)
|
||||
}
|
||||
|
||||
func MapFn[T any](epoch time.Time, format string, vFn func(S) T, fn func(s S, v T) S) Mapper {
|
||||
return mapFn(nil, vFn, fn)
|
||||
}
|
||||
|
||||
var unixTimeEpochDate = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
func DescriptionActualUTime(epoch time.Time, format string) Mapper {
|
||||
return Fn(func(s S) (S, error) {
|
||||
s.Description = epoch.Add(time.Second * time.Duration(s.ActualU())).Format(format)
|
||||
return s, nil
|
||||
func DescriptionFn[T any](vFn func(S) (T, bool), fn func(v T) string) Mapper {
|
||||
return mapFn(vFn, nil, func(s S, v T) S {
|
||||
s.Description = fn(v)
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
func DescriptionSymUTime(epoch time.Time, format string) Mapper {
|
||||
return Fn(func(s S) (S, error) {
|
||||
s.Description = epoch.Add(time.Second * time.Duration(s.SymU())).Format(format)
|
||||
return s, nil
|
||||
// TODO: scalar types constraints
|
||||
func DescriptionTimeFn[T constraints.Integer | constraints.Float](vFn func(S) (T, bool), epoch time.Time, format string) Mapper {
|
||||
return DescriptionFn(vFn, func(v T) string {
|
||||
return epoch.Add(time.Duration(v) * time.Second).Format(format)
|
||||
})
|
||||
}
|
||||
|
||||
var DescriptionActualUUnixTime = DescriptionActualUTime(unixTimeEpochDate, time.RFC3339)
|
||||
var DescriptionSymUUnixTime = DescriptionSymUTime(unixTimeEpochDate, time.RFC3339)
|
||||
|
||||
func DescriptionActualSTime(epoch time.Time, format string) Mapper {
|
||||
return Fn(func(s S) (S, error) {
|
||||
s.Description = epoch.Add(time.Second * time.Duration(s.ActualS())).Format(format)
|
||||
return s, nil
|
||||
})
|
||||
func DescriptionUnixTimeFn[T constraints.Integer | constraints.Float](vFn func(S) (T, bool), format string) Mapper {
|
||||
return DescriptionTimeFn(vFn, unixTimeEpochDate, format)
|
||||
}
|
||||
|
||||
func DescriptionSymSTime(epoch time.Time, format string) Mapper {
|
||||
return Fn(func(s S) (S, error) {
|
||||
s.Description = epoch.Add(time.Second * time.Duration(s.SymS())).Format(format)
|
||||
return s, nil
|
||||
})
|
||||
}
|
||||
|
||||
var DescriptionActualSUnixTime = DescriptionActualSTime(unixTimeEpochDate, time.RFC3339)
|
||||
var DescriptionSymSUnixTime = DescriptionSymSTime(unixTimeEpochDate, time.RFC3339)
|
||||
|
@ -10,11 +10,17 @@ import (
|
||||
|
||||
// Type BigInt
|
||||
|
||||
// TryActualBigInt try assert actual value is a BigInt and return result
|
||||
func (s S) TryActualBigInt() (*big.Int, bool) {
|
||||
v, ok := s.Actual.(*big.Int)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualBigInt asserts actual value is a BigInt and returns it
|
||||
func (s S) ActualBigInt() *big.Int {
|
||||
v, ok := s.Actual.(*big.Int)
|
||||
v, ok := s.TryActualBigInt()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as *big.Int", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as *big.Int", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -29,13 +35,19 @@ func (fn ActualBigIntFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymBigInt asserts symbolic value is a BigInt and returns it
|
||||
func (s S) SymBigInt() *big.Int {
|
||||
v, ok := s.Sym.(*big.Int)
|
||||
v, ok := s.TrySymBigInt()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as *big.Int", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as *big.Int", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymBigInt try assert symbolic value is a BigInt and return result
|
||||
func (s S) TrySymBigInt() (*big.Int, bool) {
|
||||
v, ok := s.Sym.(*big.Int)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymBigIntFn map sym BigInt using f
|
||||
type SymBigIntFn func(a *big.Int) *big.Int
|
||||
|
||||
@ -46,11 +58,17 @@ func (f SymBigIntFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// Type BitBuf
|
||||
|
||||
// TryActualBitBuf try assert actual value is a BitBuf and return result
|
||||
func (s S) TryActualBitBuf() (bitio.ReaderAtSeeker, bool) {
|
||||
v, ok := s.Actual.(bitio.ReaderAtSeeker)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualBitBuf asserts actual value is a BitBuf and returns it
|
||||
func (s S) ActualBitBuf() bitio.ReaderAtSeeker {
|
||||
v, ok := s.Actual.(bitio.ReaderAtSeeker)
|
||||
v, ok := s.TryActualBitBuf()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as bitio.ReaderAtSeeker", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as bitio.ReaderAtSeeker", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -65,13 +83,19 @@ func (fn ActualBitBufFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymBitBuf asserts symbolic value is a BitBuf and returns it
|
||||
func (s S) SymBitBuf() bitio.ReaderAtSeeker {
|
||||
v, ok := s.Sym.(bitio.ReaderAtSeeker)
|
||||
v, ok := s.TrySymBitBuf()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as bitio.ReaderAtSeeker", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as bitio.ReaderAtSeeker", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymBitBuf try assert symbolic value is a BitBuf and return result
|
||||
func (s S) TrySymBitBuf() (bitio.ReaderAtSeeker, bool) {
|
||||
v, ok := s.Sym.(bitio.ReaderAtSeeker)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymBitBufFn map sym BitBuf using f
|
||||
type SymBitBufFn func(a bitio.ReaderAtSeeker) bitio.ReaderAtSeeker
|
||||
|
||||
@ -82,11 +106,17 @@ func (f SymBitBufFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// Type Bool
|
||||
|
||||
// TryActualBool try assert actual value is a Bool and return result
|
||||
func (s S) TryActualBool() (bool, bool) {
|
||||
v, ok := s.Actual.(bool)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualBool asserts actual value is a Bool and returns it
|
||||
func (s S) ActualBool() bool {
|
||||
v, ok := s.Actual.(bool)
|
||||
v, ok := s.TryActualBool()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as bool", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as bool", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -101,13 +131,19 @@ func (fn ActualBoolFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymBool asserts symbolic value is a Bool and returns it
|
||||
func (s S) SymBool() bool {
|
||||
v, ok := s.Sym.(bool)
|
||||
v, ok := s.TrySymBool()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as bool", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as bool", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymBool try assert symbolic value is a Bool and return result
|
||||
func (s S) TrySymBool() (bool, bool) {
|
||||
v, ok := s.Sym.(bool)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymBoolFn map sym Bool using f
|
||||
type SymBoolFn func(a bool) bool
|
||||
|
||||
@ -118,11 +154,17 @@ func (f SymBoolFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// Type F
|
||||
|
||||
// TryActualF try assert actual value is a F and return result
|
||||
func (s S) TryActualF() (float64, bool) {
|
||||
v, ok := s.Actual.(float64)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualF asserts actual value is a F and returns it
|
||||
func (s S) ActualF() float64 {
|
||||
v, ok := s.Actual.(float64)
|
||||
v, ok := s.TryActualF()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as float64", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as float64", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -137,13 +179,19 @@ func (fn ActualFFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymF asserts symbolic value is a F and returns it
|
||||
func (s S) SymF() float64 {
|
||||
v, ok := s.Sym.(float64)
|
||||
v, ok := s.TrySymF()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as float64", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as float64", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymF try assert symbolic value is a F and return result
|
||||
func (s S) TrySymF() (float64, bool) {
|
||||
v, ok := s.Sym.(float64)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymFFn map sym F using f
|
||||
type SymFFn func(a float64) float64
|
||||
|
||||
@ -154,11 +202,17 @@ func (f SymFFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// Type S
|
||||
|
||||
// TryActualS try assert actual value is a S and return result
|
||||
func (s S) TryActualS() (int64, bool) {
|
||||
v, ok := s.Actual.(int64)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualS asserts actual value is a S and returns it
|
||||
func (s S) ActualS() int64 {
|
||||
v, ok := s.Actual.(int64)
|
||||
v, ok := s.TryActualS()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as int64", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as int64", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -173,13 +227,19 @@ func (fn ActualSFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymS asserts symbolic value is a S and returns it
|
||||
func (s S) SymS() int64 {
|
||||
v, ok := s.Sym.(int64)
|
||||
v, ok := s.TrySymS()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as int64", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as int64", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymS try assert symbolic value is a S and return result
|
||||
func (s S) TrySymS() (int64, bool) {
|
||||
v, ok := s.Sym.(int64)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymSFn map sym S using f
|
||||
type SymSFn func(a int64) int64
|
||||
|
||||
@ -190,11 +250,17 @@ func (f SymSFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// Type Str
|
||||
|
||||
// TryActualStr try assert actual value is a Str and return result
|
||||
func (s S) TryActualStr() (string, bool) {
|
||||
v, ok := s.Actual.(string)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualStr asserts actual value is a Str and returns it
|
||||
func (s S) ActualStr() string {
|
||||
v, ok := s.Actual.(string)
|
||||
v, ok := s.TryActualStr()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as string", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as string", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -209,13 +275,19 @@ func (fn ActualStrFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymStr asserts symbolic value is a Str and returns it
|
||||
func (s S) SymStr() string {
|
||||
v, ok := s.Sym.(string)
|
||||
v, ok := s.TrySymStr()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as string", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as string", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymStr try assert symbolic value is a Str and return result
|
||||
func (s S) TrySymStr() (string, bool) {
|
||||
v, ok := s.Sym.(string)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymStrFn map sym Str using f
|
||||
type SymStrFn func(a string) string
|
||||
|
||||
@ -226,11 +298,17 @@ func (f SymStrFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// Type U
|
||||
|
||||
// TryActualU try assert actual value is a U and return result
|
||||
func (s S) TryActualU() (uint64, bool) {
|
||||
v, ok := s.Actual.(uint64)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// ActualU asserts actual value is a U and returns it
|
||||
func (s S) ActualU() uint64 {
|
||||
v, ok := s.Actual.(uint64)
|
||||
v, ok := s.TryActualU()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as uint64", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as uint64", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -245,13 +323,19 @@ func (fn ActualUFn) MapScalar(s S) (S, error) {
|
||||
|
||||
// SymU asserts symbolic value is a U and returns it
|
||||
func (s S) SymU() uint64 {
|
||||
v, ok := s.Sym.(uint64)
|
||||
v, ok := s.TrySymU()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as uint64", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as uint64", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySymU try assert symbolic value is a U and return result
|
||||
func (s S) TrySymU() (uint64, bool) {
|
||||
v, ok := s.Sym.(uint64)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SymUFn map sym U using f
|
||||
type SymUFn func(a uint64) uint64
|
||||
|
||||
|
@ -11,11 +11,17 @@ import (
|
||||
{{- range $name, $t := $.types }}
|
||||
// Type {{$name}}
|
||||
|
||||
// TryActual{{$name}} try assert actual value is a {{$name}} and return result
|
||||
func (s S) TryActual{{$name}}() ({{$t.go_type}}, bool) {
|
||||
v, ok := s.Actual.({{$t.go_type}})
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Actual{{$name}} asserts actual value is a {{$name}} and returns it
|
||||
func (s S) Actual{{$name}}() {{$t.go_type}} {
|
||||
v, ok := s.Actual.({{$t.go_type}})
|
||||
v, ok := s.TryActual{{$name}}()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v as {{$t.go_type}}", s.Actual))
|
||||
panic(fmt.Sprintf("failed to type assert s.Actual %v (%T) as {{$t.go_type}}", s.Actual, s.Actual))
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -30,13 +36,19 @@ import (
|
||||
|
||||
// Sym{{$name}} asserts symbolic value is a {{$name}} and returns it
|
||||
func (s S) Sym{{$name}}() {{$t.go_type}} {
|
||||
v, ok := s.Sym.({{$t.go_type}})
|
||||
v, ok := s.TrySym{{$name}}()
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v as {{$t.go_type}}", s.Sym))
|
||||
panic(fmt.Sprintf("failed to type assert s.Sym %v (%T) as {{$t.go_type}}", s.Sym, s.Sym))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TrySym{{$name}} try assert symbolic value is a {{$name}} and return result
|
||||
func (s S) TrySym{{$name}}() ({{$t.go_type}}, bool) {
|
||||
v, ok := s.Sym.({{$t.go_type}})
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// Sym{{$name}}Fn map sym {{$name}} using f
|
||||
type Sym{{$name}}Fn func(a {{$t.go_type}}) {{$t.go_type}}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user