diff options
author | Nicolas Williams <nico@cryptonector.com> | 2017-12-11 11:20:16 -0600 |
---|---|---|
committer | Nicolas Williams <nico@cryptonector.com> | 2017-12-11 11:20:16 -0600 |
commit | 9a4576e7567dd38b91f28592b47eb6dafe0c4332 (patch) | |
tree | 3f825020433fd48aafd9d424f1095abe099a9abf | |
parent | b5560d8420d330c4f90b3282c028bba476b01c1c (diff) |
Revert "reduce: handle empty updates (fix #1313)"
This reverts commit e24af3c78e78a3aab05a2800d825d56f1d842b1b.
While the semantics are desirable, there is no way to implement them
efficiently. The reason is that in order to handle backtracking (empty)
from the state update expression, we have to retain a reference to the
reduction state value in order to restore it upon backtracking.
Retaining a reference to the reduction state kills performance by
causing lots of additional memory allocations and garbage because the
input to the update expression will always have at least two references,
thus no changes to it can be done in-place, and all changes end up being
CoW changes.
Avoiding this is the very reason for the LOADVN instruction (leaving
`null` in the variable loaded from).
-rw-r--r-- | docs/content/3.manual/manual.yml | 8 | ||||
-rw-r--r-- | jq.1.prebuilt | 21 | ||||
-rw-r--r-- | src/compile.c | 22 | ||||
-rw-r--r-- | tests/jq.test | 5 |
4 files changed, 4 insertions, 52 deletions
diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index f03efcda..91209a1e 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -2596,18 +2596,10 @@ sections: (2 as $item | . + $item) | (1 as $item | . + $item) - If the reduction update expression outputs `empty` (that is, - no values), then the reduction state is left as-is. For - example, `reduce range(4) as $n ({}; if .==2 then empty else - .[$n|tostring] |= $n)` will produce `{"0":0,"1":1,"3":3}`. - examples: - program: 'reduce .[] as $item (0; . + $item)' input: '[10,2,5,3]' output: ['20'] - - program: 'reduce .[] as $n ([]; if $n%2==0 then empty else . + [$n] end)' - input: '[0,1,2,3,4,5]' - output: ['[1,3,5]'] - title: "`isempty(exp)`" body: | diff --git a/jq.1.prebuilt b/jq.1.prebuilt index 03672cc2..b0939ca0 100644 --- a/jq.1.prebuilt +++ b/jq.1.prebuilt @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "JQ" "1" "May 2017" "" "" +.TH "JQ" "1" "December 2017" "" "" . .SH "NAME" \fBjq\fR \- Command\-line JSON processor @@ -2100,7 +2100,7 @@ The \fBstrftime(fmt)\fR builtin formats a time (GMT) with the given format\. The The format strings for \fBstrptime\fR and \fBstrftime\fR are described in typical C library documentation\. The format string for ISO 8601 datetime is \fB"%Y\-%m\-%dT%H:%M:%SZ"\fR\. . .P -jq may not support some or all of this date functionality on some systems\. +jq may not support some or all of this date functionality on some systems\. In particular, the \fB%u\fR and \fB%j\fR specifiers for \fBstrptime(fmt)\fR are not supported on macOS\. . .IP "" 4 . @@ -2378,7 +2378,7 @@ break $out .P because no label \fB$out\fR is visible\. . -.SS "Error Suppresion / Optional Operator: ?" +.SS "Error Suppression / Optional Operator: ?" The \fB?\fR operator, used as \fBEXP?\fR, is shorthand for \fBtry EXP\fR\. . .IP "" 4 @@ -2861,25 +2861,10 @@ For each result that \fB\.[]\fR produces, \fB\. + $item\fR is run to accumulate 0 | (3 as $item | \. + $item) | (2 as $item | \. + $item) | (1 as $item | \. + $item) -. -.fi -. -.IP "" 0 -. -.P -If the reduction update expression outputs \fBempty\fR (that is, no values), then the reduction state is left as\-is\. For example, \fBreduce range(4) as $n ({}; if \.==2 then empty else \.[$n|tostring] |= $n)\fR will produce \fB{"0":0,"1":1,"3":3}\fR\. -. -.IP "" 4 -. -.nf jq \'reduce \.[] as $item (0; \. + $item)\' [10,2,5,3] => 20 - -jq \'reduce \.[] as $n ([]; if $n%2==0 then empty else \. + [$n] end)\' - [0,1,2,3,4,5] -=> [1,3,5] . .fi . diff --git a/src/compile.c b/src/compile.c index afd42aed..33a8e72c 100644 --- a/src/compile.c +++ b/src/compile.c @@ -823,32 +823,12 @@ static block bind_alternation_matchers(block matchers, block body) { block gen_reduce(block source, block matcher, block init, block body) { block res_var = gen_op_var_fresh(STOREV, "reduce"); - block update_var = gen_op_bound(STOREV, res_var); - block jmp = gen_op_targetlater(JUMP); - if (body.last == NULL) { - inst_set_target(jmp, jmp); - } else { - inst_set_target(jmp, body); - } block loop = BLOCK(gen_op_simple(DUPN), source, bind_alternation_matchers(matcher, BLOCK(gen_op_bound(LOADVN, res_var), - /* - * We fork to the body, jump to - * the STOREV. This means that - * if body produces no results - * (i.e., it just does empty) - * then we keep the current - * reduction state as-is. - * - * To break out of a - * reduction... use break. - */ - gen_op_target(FORK, jmp), - jmp, body, - update_var)), + gen_op_bound(STOREV, res_var))), gen_op_simple(BACKTRACK)); return BLOCK(gen_op_simple(DUP), init, diff --git a/tests/jq.test b/tests/jq.test index 12d2b4db..8771ba65 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -705,11 +705,6 @@ reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j) null 14 -# Test fix for #1313 (reduce should handle empty updates) -reduce range(5) as $n ([]; select($n%2 == 1) | . + [$n]) -null -[1,3] - # This, while useless, should still compile. reduce . as $n (.; .) null |