1
1
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:
Pavel Safonov 2022-10-05 19:39:35 +03:00
commit 6588e1dd88
66 changed files with 4365 additions and 1208 deletions

View File

@ -7,7 +7,7 @@ on:
pull_request:
env:
GOLANGCILINT_VERSION: 1.49.0
GOLANGCILINT_VERSION: 1.50.0
jobs:
lint:

View File

@ -154,14 +154,18 @@
"tfhd",
"tfra",
"tmpl",
"toactual",
"toarray",
"toboolean",
"tobytes",
"tobytesrange",
"todescription",
"toimage",
"tojson",
"tojvalue",
"topath",
"torepr",
"tosym",
"tovalue",
"toxml",
"toxmlentities",

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -27,6 +27,7 @@
|[`bitcoin_block`](#bitcoin_block) |Bitcoin&nbsp;block |<sub>`bitcoin_transaction`</sub>|
|`bitcoin_script` |Bitcoin&nbsp;script |<sub></sub>|
|`bitcoin_transaction` |Bitcoin&nbsp;transaction |<sub>`bitcoin_script`</sub>|
|[`bplist`](#bplist) |Apple&nbsp;Binary&nbsp;Property&nbsp;List |<sub></sub>|
|`bsd_loopback_frame` |BSD&nbsp;loopback&nbsp;frame |<sub>`inet_packet`</sub>|
|[`bson`](#bson) |Binary&nbsp;JSON |<sub></sub>|
|`bzip2` |bzip2&nbsp;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,

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 132 KiB

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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
View 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
View 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
View 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

Binary file not shown.

2424
format/bplist/testdata/bplist.fqtest vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -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"

View File

@ -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)

View File

@ -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) {

View File

@ -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",

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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,

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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)
}
}

View File

@ -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() {

View File

@ -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
}

View File

@ -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?

View File

@ -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

View File

@ -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")

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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?

View File

@ -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

View File

@ -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})

View File

@ -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) {

View File

@ -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)

View File

@ -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

View File

@ -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}}