($INBOX_DIR/description missing)
help / color / mirror / Atom feed[PATCH 8/8] batch build
33+ messages / 5 participants
[nested] [flat]
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* [PATCH 8/8] batch build
@ 2021-01-22 00:11 Tomas Vondra <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Tomas Vondra @ 2021-01-22 00:11 UTC (permalink / raw)
---
src/backend/access/brin/brin_minmax_multi.c | 204 ++++++++++++++++++--
src/backend/access/brin/brin_tuple.c | 1 +
src/include/access/brin_tuple.h | 2 +-
3 files changed, 186 insertions(+), 21 deletions(-)
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 094ecd2be9..72cca6f26b 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -57,6 +57,7 @@
#include "access/brin.h"
#include "access/brin_internal.h"
#include "access/brin_tuple.h"
+#include "access/hash.h" /* XXX strange that it fails because of BRIN_AM_OID without this */
#include "access/reloptions.h"
#include "access/stratnum.h"
#include "access/htup_details.h"
@@ -150,12 +151,23 @@ typedef struct MinMaxOptions
typedef struct Ranges
{
Oid typid;
+ Oid colloid;
+ AttrNumber attno;
/* (2*nranges + nvalues) <= maxvalues */
int nranges; /* number of ranges in the array (stored) */
int nvalues; /* number of values in the data array (all) */
int maxvalues; /* maximum number of values (reloption) */
+ /*
+ * Batch mode means we're simply appending values into new range,
+ * without any expensive steps (sorting, deduplication, ...). The
+ * buffer is sized to be much larger, but we keep the actual target
+ * size so that when serializing values we can do the right thing.
+ */
+ bool batch_mode;
+ int target_maxvalues;
+
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
@@ -375,6 +387,7 @@ range_deserialize(SerializedRanges *serialized)
range->nvalues = serialized->nvalues;
range->maxvalues = serialized->maxvalues;
range->typid = serialized->typid;
+ range->batch_mode = false;
typbyval = get_typbyval(serialized->typid);
typlen = get_typlen(serialized->typid);
@@ -844,11 +857,14 @@ fill_combine_ranges(CombineRange *cranges, int ncranges, Ranges *ranges)
/*
* Sort combine ranges using qsort (with BTLessStrategyNumber function).
*/
-static void
+static int
sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
- CombineRange *cranges, int ncranges)
+ CombineRange *cranges, int ncranges,
+ bool deduplicate)
{
- compare_context cxt;
+ int n;
+ int i;
+ compare_context cxt;
/* sort the values */
cxt.colloid = colloid;
@@ -856,6 +872,24 @@ sort_combine_ranges(FmgrInfo *cmp, Oid colloid,
qsort_arg(cranges, ncranges, sizeof(CombineRange),
compare_combine_ranges, (void *) &cxt);
+
+ if (!deduplicate)
+ return ncranges;
+
+ /* optionally deduplicate the ranges */
+ n = 1;
+ for (i = 1; i < ncranges; i++)
+ {
+ if (compare_combine_ranges(&cranges[i-1], &cranges[i], (void *) &cxt))
+ {
+ if (i != n)
+ memcpy(&cranges[n], &cranges[i], sizeof(CombineRange));
+
+ n++;
+ }
+ }
+
+ return n;
}
/*
@@ -1026,26 +1060,40 @@ build_distances(FmgrInfo *distanceFn, Oid colloid,
*/
static CombineRange *
build_combine_ranges(FmgrInfo *cmp, Oid colloid, Ranges *ranges,
- Datum newvalue, int *nranges)
+ bool addvalue, Datum newvalue, int *nranges,
+ bool deduplicate)
{
int ncranges;
CombineRange *cranges;
/* now do the actual merge sort */
- ncranges = ranges->nranges + ranges->nvalues + 1;
+ ncranges = ranges->nranges + ranges->nvalues;
+
+ /* should we add an extra value? */
+ if (addvalue)
+ ncranges += 1;
+
cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
- *nranges = ncranges;
/* put the new value at the beginning */
- cranges[0].minval = newvalue;
- cranges[0].maxval = newvalue;
- cranges[0].collapsed = true;
+ if (addvalue)
+ {
+ cranges[0].minval = newvalue;
+ cranges[0].maxval = newvalue;
+ cranges[0].collapsed = true;
- /* then the regular and collapsed ranges */
- fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ /* then the regular and collapsed ranges */
+ fill_combine_ranges(&cranges[1], ncranges-1, ranges);
+ }
+ else
+ fill_combine_ranges(cranges, ncranges, ranges);
/* and sort the ranges */
- sort_combine_ranges(cmp, colloid, cranges, ncranges);
+ ncranges = sort_combine_ranges(cmp, colloid, cranges, ncranges,
+ deduplicate);
+
+ /* remember how many cranges we built */
+ *nranges = ncranges;
return cranges;
}
@@ -1074,6 +1122,7 @@ count_values(CombineRange *cranges, int ncranges)
}
#endif
+
/*
* reduce_combine_ranges
* reduce the ranges until the number of values is low enough
@@ -1250,6 +1299,25 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
Assert(2*ranges->nranges + ranges->nvalues <= ranges->maxvalues);
+ Assert((ranges->nranges >= 0) && (ranges->nvalues >= 0) && (ranges->maxvalues >= 0));
+
+ /*
+ * When batch-building, there should be no ranges. So either the
+ * number of ranges is 0 or we're not in batching mode.
+ */
+ Assert(!ranges->batch_mode || (ranges->nranges == 0));
+
+ /* When batch-building, just add it and we're done. */
+ if (ranges->batch_mode)
+ {
+ /* there has to be free space, if we've sized the struct */
+ Assert(ranges->nvalues < ranges->maxvalues);
+
+ ranges->values[ranges->nvalues++] = newval;
+
+ return true;
+ }
+
/*
* Bail out if the value already is covered by the range.
*
@@ -1322,7 +1390,9 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
ranges->nvalues);
/* OK build the combine ranges */
- cranges = build_combine_ranges(cmpFn, colloid, ranges, newval, &ncranges);
+ cranges = build_combine_ranges(cmpFn, colloid, ranges,
+ true, newval, &ncranges,
+ false);
/* Reduce the ranges if needed */
if (ncranges > ranges->maxvalues)
@@ -1382,6 +1452,81 @@ range_add_value(BrinDesc *bdesc, Oid colloid,
return true;
}
+/*
+ * Generate range representation of data collected during "batch mode".
+ * This is similar to reduce_combine_ranges, except that we can't assume
+ * the values are sorted and there may be duplicate values.
+ */
+static void
+compactify_ranges(BrinDesc *bdesc, Ranges *ranges, int max_values)
+{
+ FmgrInfo *cmpFn,
+ *distanceFn;
+
+ /* combine ranges */
+ CombineRange *cranges;
+ int ncranges;
+ DistanceValue *distances;
+
+ MemoryContext ctx;
+ MemoryContext oldctx;
+
+ /*
+ * This should only be used in batch mode, and there should be no
+ * ranges, just individual values.
+ */
+ Assert((ranges->batch_mode) && (ranges->nranges == 0));
+
+ /* we'll certainly need the comparator, so just look it up now */
+ cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
+ BTLessStrategyNumber);
+
+ /* and we'll also need the 'distance' procedure */
+ distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
+
+ /*
+ * The distanceFn calls (which may internally call e.g. numeric_le) may
+ * allocate quite a bit of memory, and we must not leak it. Otherwise
+ * we'd have problems e.g. when building indexes. So we create a local
+ * memory context and make sure we free the memory before leaving this
+ * function (not after every call).
+ */
+ ctx = AllocSetContextCreate(CurrentMemoryContext,
+ "minmax-multi context",
+ ALLOCSET_DEFAULT_SIZES);
+
+ oldctx = MemoryContextSwitchTo(ctx);
+
+ /* OK build the combine ranges */
+ cranges = build_combine_ranges(cmpFn, ranges->colloid, ranges,
+ false, (Datum) 0, &ncranges,
+ true); /* deduplicate */
+
+ /* build array of gap distances and sort them in ascending order */
+ distances = build_distances(distanceFn, ranges->colloid, cranges, ncranges);
+
+ /*
+ * Combine ranges until we get below max_values. We don't use any scale
+ * factor, because this is used at the very end of "batch mode" and we
+ * don't expect more tuples to be inserted soon.
+ */
+ ncranges = reduce_combine_ranges(cranges, ncranges, distances,
+ max_values, cmpFn, ranges->colloid);
+
+ Assert(count_values(cranges, ncranges) <= max_values);
+
+ /* decompose the combine ranges into regular ranges and single values */
+ store_combine_ranges(ranges, cranges, ncranges);
+
+ MemoryContextSwitchTo(oldctx);
+ MemoryContextDelete(ctx);
+
+ /* Check the ordering invariants are not violated (for both parts). */
+ AssertArrayOrder(cmpFn, ranges->colloid, ranges->values, ranges->nranges*2);
+ AssertArrayOrder(cmpFn, ranges->colloid, &ranges->values[ranges->nranges*2],
+ ranges->nvalues);
+}
+
Datum
brin_minmax_multi_opcinfo(PG_FUNCTION_ARGS)
{
@@ -1799,10 +1944,16 @@ brin_minmax_multi_distance_inet(PG_FUNCTION_ARGS)
}
static void
-brin_minmax_multi_serialize(Datum src, Datum *dst)
+brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
- SerializedRanges *s = range_serialize(ranges);
+ SerializedRanges *s;
+
+ /* In batch mode, we need to compress the accumulated values. */
+ if (ranges->batch_mode)
+ compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
+
+ s = range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
@@ -1845,15 +1996,28 @@ brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise just extract the existing range list from BrinValues.
+ *
+ * When starting with an empty range, we assume this is a batch mode,
+ * i.e. we size the buffer for the maximum possible number of items in
+ * the range (based on range size and max number of items on a page).
+ *
+ * XXX This may require quite a bit of memory, so maybe we should use
+ * some value in between. OTOH most tables will have much wider rows,
+ * so the number of rows per page is much lower.
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
- oldctx = MemoryContextSwitchTo(column->bv_context);
+ BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
- ranges = minmax_multi_init(brin_minmax_multi_get_values(bdesc, opts));
+ oldctx = MemoryContextSwitchTo(column->bv_context);
+ ranges = minmax_multi_init(MaxHeapTuplesPerPage * pagesPerRange);
+ ranges->attno = attno;
+ ranges->colloid = colloid;
ranges->typid = attr->atttypid;
+ ranges->batch_mode = true;
+ ranges->target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
MemoryContextSwitchTo(oldctx);
@@ -2125,7 +2289,7 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
- cranges = (CombineRange *)palloc0(ncranges * sizeof(CombineRange));
+ cranges = (CombineRange *) palloc0(ncranges * sizeof(CombineRange));
/* fill the combine ranges with entries for the first range */
fill_combine_ranges(cranges, ranges_a->nranges + ranges_a->nvalues,
@@ -2139,8 +2303,8 @@ brin_minmax_multi_union(PG_FUNCTION_ARGS)
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
- /* sort the combine ranges */
- sort_combine_ranges(cmpFn, colloid, cranges, ncranges);
+ /* sort the combine ranges (don't deduplicate) */
+ sort_combine_ranges(cmpFn, colloid, cranges, ncranges, false);
/*
* We've merged two different lists of ranges, so some of them may be
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index df820db029..bf8635d788 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -163,6 +163,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
if (tuple->bt_columns[keyno].bv_serialize)
{
tuple->bt_columns[keyno].bv_serialize(
+ brdesc,
tuple->bt_columns[keyno].bv_mem_value,
tuple->bt_columns[keyno].bv_values);
}
diff --git a/src/include/access/brin_tuple.h b/src/include/access/brin_tuple.h
index 66a6c3c792..e08998b71a 100644
--- a/src/include/access/brin_tuple.h
+++ b/src/include/access/brin_tuple.h
@@ -14,7 +14,7 @@
#include "access/brin_internal.h"
#include "access/tupdesc.h"
-typedef void (*brin_serialize_callback_type) (Datum src, Datum * dst);
+typedef void (*brin_serialize_callback_type) (BrinDesc *bdesc, Datum src, Datum * dst);
/*
* A BRIN index stores one index tuple per page range. Each index tuple
--
2.26.2
--------------FA974B75A0C3E9CA18CE7207
Content-Type: application/x-shellscript;
name="brin.sh"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="brin.sh"
IyEvdXNyL2Jpbi9iYXNoCgpOUk9XUz0kMQoKZHJvcGRiIC0taWYtZXhpc3RzIHRlc3QKY3Jl
YXRlZGIgdGVzdAoKIyBtb25vdG9uaWMsIGFzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1j
ICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgaSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygxLCROUk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1
bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50
IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxh
ciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVh
dGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy1hc2MgaW50NCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBz
cWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0
IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy1hc2MgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQgYnJpbi1tdWx0aS1taW5t
YXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8
RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9i
bG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1w
IHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCBhc2NlbmRpbmcsIGludDQsIHBh
ZGRlZAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIg
dGV4dCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNl
bGVjdCBpLCBtZDUoaTo6dGV4dCkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoMSwkTlJPV1MpIHMo
aSkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+
IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwg
Mj4mMQoKZm9yIHIgaW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8
IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGlu
dDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAg
fCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWFzYyBp
bnQ0LXBhZGRlZCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5k
ZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlk
eCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtYXNjIGludDQtcGFkZGVkIGJyaW4tbXVs
dGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmlu
LnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChh
IGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBi
cmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9u
aWMtYXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAt
YyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIGRlc2Nl
bmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEg
aW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2Vs
ZWN0IGkgZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAy
PiYxCnBzcWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIg
aW4gYHNlcSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJv
cCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8
IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJ0cmVlICRy
ICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5k
ZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sg
J3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLWRlc2MgaW50NCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtZGVzYyBpbnQ0IGJyaW4tbXVsdGktbWlubWF4ICRyICR0CgoJIyBicmluIGJsb29t
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoK
IyBtb25vdG9uaWMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0IC1jICJj
cmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBpLCBtZDUoaTo6dGV4dCkg
ZnJvbSBnZW5lcmF0ZV9zZXJpZXMoJE5ST1dTLDEsLTEpIHMoaSkiID4gL2Rldi9udWxsIDI+
JjEKcHNxbCB0ZXN0IC1jICJ2YWN1dW0gYW5hbHl6ZSB0IiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAiY2hlY2twb2ludCIgPiAvZGV2L251bGwgMj4mMQoKZm9yIHIgaW4gYHNl
cSAxIDEwYDsgZG8KCgkjIHJlZ3VsYXIgYnRyZWUgaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBhZGRlZCBidHJlZSAk
ciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9G
Clx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4t
bWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4g
YnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJp
biAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1g
Z3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgll
Y2hvIG1vbm90b25pYy1kZXNjIGludDQtcGFkZGVkIGJyaW4tbXVsdGktbWlubWF4ICRyICR0
CgoJIyBicmluIGJsb29tIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGlt
aW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfYmxvb21fb3Bz
KTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQg
LW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIG1vbm90b25pYywg
MTAwIGRpc3RpbmN0IHZhbHVlcywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCRO
Uk9XUykgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFs
eXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9k
ZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJl
ZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5k
ZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMt
MTAwLWFzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBp
bmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXgg
aWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGlu
ZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdr
ICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtYXNjIGludDQgYnJpbi1tdWx0
aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4u
dG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEg
aW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJy
aW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25p
Yy0xMDAtYXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJk
cm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIHZhbHVl
cywgYXNjZW5kaW5nLCBpbnQ0LCBwYWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSwgbWQ1KGk6OnRleHQpIGZy
b20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBz
cWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRl
c3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAx
MGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8
PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4
OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmlu
dCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIg
JHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRl
eCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAn
e3ByaW50ICQyfSdgCgoJZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmlu
LW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9
YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJ
ZWNobyBtb25vdG9uaWMtMTAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1hc2Mg
aW50NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9w
IHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgojIG1vbm90b25pYywgMTAwIGRpc3RpbmN0
IHZhbHVlcywgZGVzY2VuZGluZywgaW50NAoKcHNxbCB0ZXN0IC1jICJjcmVhdGUgdW5sb2dn
ZWQgdGFibGUgdCAoYSBpbnQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5z
ZXJ0IGludG8gdCBzZWxlY3QgbW9kKGksMTAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygkTlJP
V1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBh
bmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+
IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBi
dHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1l
IGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90
b25pYy0xMDAtZGVzYyBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3Rv
bmljLTEwMC1kZXNjIGludDQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlu
bWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCB1c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRy
b3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEg
fCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQgYnJp
bi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+
IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJy
aW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBU
aW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1v
bm90b25pYy0xMDAtZGVzYyBpbnQ0IGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwgdGVz
dCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoKIyBtb25vdG9uaWMsIDEw
MCBkaXN0aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCBtb2QoaSwgMTAw
KSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKCROUk9XUywxLC0xKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMC1kZXNj
IGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEw
MC1kZXNjIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAtZGVzYyBpbnQ0LXBh
ZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0
PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoK
CWVjaG8gbW9ub3RvbmljLTEwMC1kZXNjIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQK
CmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgoj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0aW5jdCB2YWx1ZXMs
IGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIg
PiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImluc2VydCBpbnRvIHQgc2VsZWN0IG1v
ZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3NlcmllcygxLCROUk9XUykgcyhpKSIgPiAvZGV2
L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZhY3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxs
IDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3BvaW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3Ig
ciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVndWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
dHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NCBi
cmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVz
dCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5n
IGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtYXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIg
JHQKCgkjIGJyaW4gYmxvb20gaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0
aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9v
cHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2Mg
aW50NCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCB2YWx1ZXMsIGFzY2Vu
ZGluZywgaW50NCwgcGFkZGVkCgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dlZCB0YWJs
ZSB0IChhIGludCwgYiB0ZXh0KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2Vu
ZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVz
dCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
ImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRv
CgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpc
dGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YK
Cgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0n
YAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBidHJlZSAkciAkdAoK
CSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1p
bmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEpOwpkcm9wIGluZGV4IGlk
eDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJp
bnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1hc2MgaW50NC1wYWRkZWQgYnJpbi1t
aW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBi
cmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmlu
IChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBn
cmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVj
aG8gbW9ub3RvbmljLTEwMDAwLWFzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWFz
YyBpbnQ0LXBhZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRy
b3AgdGFibGUgdCIgPiAvZGV2L251bGwgMj4mMQoKCiMgbW9ub3RvbmljLCAxMDAwMCBkaXN0
aW5jdCB2YWx1ZXMsIGRlc2NlbmRpbmcsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVu
bG9nZ2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMg
Imluc2VydCBpbnRvIHQgc2VsZWN0IG1vZChpLDEwMDAwKSBmcm9tIGdlbmVyYXRlX3Nlcmll
cygkTlJPV1MsMSwtMSkgcyhpKSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgInZh
Y3V1bSBhbmFseXplIHQiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJjaGVja3Bv
aW50IiA+IC9kZXYvbnVsbCAyPiYxCgpmb3IgciBpbiBgc2VxIDEgMTBgOyBkbwoKCSMgcmVn
dWxhciBidHJlZSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpj
cmVhdGUgaW5kZXggaWR4IG9uIHQgKGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3Jl
cCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hv
IG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4
IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRl
eCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdy
ZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNo
byBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmlu
IG11bHRpIG1pbm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0
aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwg
aGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1k
ZXNjIGludDQgYnJpbi1tdWx0aS1taW5tYXggJHIgJHQKCgkjIGJyaW4gYmxvb20gaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9ibG9vbV9vcHMpOwpkcm9wIGluZGV4IGlkeDsKRU9G
CgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9
J2AKCgllY2hvIG1vbm90b25pYy0xMDAwMC1kZXNjIGludDQgYnJpbi1ibG9vbSAkciAkdAoK
ZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCgoj
IG1vbm90b25pYywgMTAwMDAgZGlzdGluY3QgdmFsdWVzLCBkZXNjZW5kaW5nLCBpbnQ0LCBw
YWRkZWQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBi
IHRleHQpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBz
ZWxlY3QgbW9kKGksIDEwMDAwKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KCROUk9XUywxLC0xKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFj
dXVtIGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9p
bnQiID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1
bGFyIGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNy
ZWF0ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
bW9ub3RvbmljLTEwMDAwLWRlc2MgaW50NC1wYWRkZWQgYnRyZWUgJHIgJHQKCgkjIGJyaW4g
bWlubWF4IGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCB1c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoK
CXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdg
CgoJZWNobyBtb25vdG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJyaW4udG1w
IDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4gKGEgaW50
NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGlt
ZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyBtb25v
dG9uaWMtMTAwMDAtZGVzYyBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gbW9ub3RvbmljLTEwMDAwLWRlc2MgaW50
NC1wYWRkZWQgYnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRh
YmxlIHQiID4gL2Rldi9udWxsIDI+JjEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyByYW5kb20s
IDEwMCBkaXN0aW5jdCB2YWx1ZXMsIGludDQKCnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9n
Z2VkIHRhYmxlIHQgKGEgaW50KSIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImlu
c2VydCBpbnRvIHQgc2VsZWN0IDEwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMCBpbnQ0IGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJcHNxbCB0
ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNp
bmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50
bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBp
bnQ0IGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1pbm1heCBpbmRleAoJcHNx
bCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQg
dXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpkcm9wIGluZGV4IGlkeDsK
RU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAxIHwgYXdrICd7cHJpbnQg
JDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAkciAkdAoK
CSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWlu
ZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7
CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1u
IDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0IGJyaW4tYmxv
b20gJHIgJHQKCmRvbmUKCnBzcWwgdGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVs
bCAyPiYxCgoKIyByYW5kb20sIDEwMCB2YWx1ZXMsIGludDQsIHBhZGRlZAoKcHNxbCB0ZXN0
IC1jICJjcmVhdGUgdW5sb2dnZWQgdGFibGUgdCAoYSBpbnQsIGIgdGV4dCkiID4gL2Rldi9u
dWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNlcnQgaW50byB0IHNlbGVjdCAxMDAgKiByYW5k
b20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEsJE5ST1dTKSBzKGkp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFuYWx5emUgdCIgPiAv
ZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4gL2Rldi9udWxsIDI+
JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0cmVlIGluZGV4Cglw
c3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24g
dCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBidHJlZSAkciAkdAoKCSMgYnJpbiBtaW5tYXggaW5kZXgKCXBzcWwgdGVzdCA+IGJy
aW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBvbiB0IHVzaW5nIGJyaW4g
KGEpOwpkcm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVh
ZCAtbiAxIHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAgaW50NC1wYWRk
ZWQgYnJpbi1taW5tYXggJHIgJHQKCgkjIGJyaW4gbXVsdGkgbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhIGludDRfbWlubWF4X211bHRpX29wcyk7CmRyb3AgaW5kZXggaWR4OwpF
T0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAk
Mn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBhZGRlZCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMCBpbnQ0LXBh
ZGRlZCBicmluLWJsb29tICRyICR0Cgpkb25lCgpwc3FsIHRlc3QgLWMgImRyb3AgdGFibGUg
dCIgPiAvZGV2L251bGwgMj4mMQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIHJhbmRvbSwgMTAw
MDAgZGlzdGluY3QgdmFsdWVzLCBpbnQ0Cgpwc3FsIHRlc3QgLWMgImNyZWF0ZSB1bmxvZ2dl
ZCB0YWJsZSB0IChhIGludCkiID4gL2Rldi9udWxsIDI+JjEKcHNxbCB0ZXN0IC1jICJpbnNl
cnQgaW50byB0IHNlbGVjdCAxMDAwMCAqIHJhbmRvbSgpIGZyb20gZ2VuZXJhdGVfc2VyaWVz
KDEsJE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVt
IGFuYWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQi
ID4gL2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFy
IGJ0cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0
ZSBpbmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRp
bWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFu
ZG9tLTEwMDAwIGludDQgYnRyZWUgJHIgJHQKCgkjIGJyaW4gbWlubWF4IGluZGV4Cglwc3Fs
IHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBpbmRleCBpZHggb24gdCB1
c2luZyBicmluIChhKTsKZHJvcCBpbmRleCBpZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmlu
LnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3ByaW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAw
MDAgaW50NCBicmluLW1pbm1heCAkciAkdAoKCSMgYnJpbiBtdWx0aSBtaW5tYXggaW5kZXgK
CXBzcWwgdGVzdCA+IGJyaW4udG1wIDw8RU9GClx0aW1pbmcKY3JlYXRlIGluZGV4IGlkeCBv
biB0IHVzaW5nIGJyaW4gKGEgaW50NF9taW5tYXhfbXVsdGlfb3BzKTsKZHJvcCBpbmRleCBp
ZHg7CkVPRgoKCXQ9YGdyZXAgVGltZSBicmluLnRtcCB8IGhlYWQgLW4gMSB8IGF3ayAne3By
aW50ICQyfSdgCgoJZWNobyByYW5kb20tMTAwMDAgaW50NCBicmluLW11bHRpLW1pbm1heCAk
ciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YK
XHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X2Jsb29t
X29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUgYnJpbi50bXAgfCBo
ZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9tLTEwMDAwIGludDQg
YnJpbi1ibG9vbSAkciAkdAoKZG9uZQoKcHNxbCB0ZXN0IC1jICJkcm9wIHRhYmxlIHQiID4g
L2Rldi9udWxsIDI+JjEKCgojIHJhbmRvbSwgMTAwMDAgdmFsdWVzLCBpbnQ0LCBwYWRkZWQK
CnBzcWwgdGVzdCAtYyAiY3JlYXRlIHVubG9nZ2VkIHRhYmxlIHQgKGEgaW50LCBiIHRleHQp
IiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAiaW5zZXJ0IGludG8gdCBzZWxlY3Qg
MTAwMDAgKiByYW5kb20oKSwgbWQ1KGk6OnRleHQpIGZyb20gZ2VuZXJhdGVfc2VyaWVzKDEs
JE5ST1dTKSBzKGkpIiA+IC9kZXYvbnVsbCAyPiYxCnBzcWwgdGVzdCAtYyAidmFjdXVtIGFu
YWx5emUgdCIgPiAvZGV2L251bGwgMj4mMQpwc3FsIHRlc3QgLWMgImNoZWNrcG9pbnQiID4g
L2Rldi9udWxsIDI+JjEKCmZvciByIGluIGBzZXEgMSAxMGA7IGRvCgoJIyByZWd1bGFyIGJ0
cmVlIGluZGV4Cglwc3FsIHRlc3QgPiBicmluLnRtcCA8PEVPRgpcdGltaW5nCmNyZWF0ZSBp
bmRleCBpZHggb24gdCAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJ0cmVlICRyICR0CgoJIyBicmluIG1pbm1heCBpbmRleAoJ
cHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9u
IHQgdXNpbmcgYnJpbiAoYSk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVwIFRpbWUg
YnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8gcmFuZG9t
LTEwMDAwIGludDQtcGFkZGVkIGJyaW4tbWlubWF4ICRyICR0CgoJIyBicmluIG11bHRpIG1p
bm1heCBpbmRleAoJcHNxbCB0ZXN0ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUg
aW5kZXggaWR4IG9uIHQgdXNpbmcgYnJpbiAoYSBpbnQ0X21pbm1heF9tdWx0aV9vcHMpOwpk
cm9wIGluZGV4IGlkeDsKRU9GCgoJdD1gZ3JlcCBUaW1lIGJyaW4udG1wIHwgaGVhZCAtbiAx
IHwgYXdrICd7cHJpbnQgJDJ9J2AKCgllY2hvIHJhbmRvbS0xMDAwMCBpbnQ0LXBhZGRlZCBi
cmluLW11bHRpLW1pbm1heCAkciAkdAoKCSMgYnJpbiBibG9vbSBpbmRleAoJcHNxbCB0ZXN0
ID4gYnJpbi50bXAgPDxFT0YKXHRpbWluZwpjcmVhdGUgaW5kZXggaWR4IG9uIHQgdXNpbmcg
YnJpbiAoYSBpbnQ0X2Jsb29tX29wcyk7CmRyb3AgaW5kZXggaWR4OwpFT0YKCgl0PWBncmVw
IFRpbWUgYnJpbi50bXAgfCBoZWFkIC1uIDEgfCBhd2sgJ3twcmludCAkMn0nYAoKCWVjaG8g
cmFuZG9tLTEwMDAwIGludDQtcGFkZGVkIGJyaW4tYmxvb20gJHIgJHQKCmRvbmUKCnBzcWwg
dGVzdCAtYyAiZHJvcCB0YWJsZSB0IiA+IC9kZXYvbnVsbCAyPiYxCgo=
--------------FA974B75A0C3E9CA18CE7207--
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
@ 2023-03-16 03:55 Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
0 siblings, 1 reply; 33+ messages in thread
From: Andres Freund @ 2023-03-16 03:55 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-15 08:14:09 +0100, Peter Eisentraut wrote:
> I have identified several open issues with the documentation build under
> Meson (approximately in priority order):
>
> 1. Image files are not handled at all, so they don't show up in the final
> product.
Hm. Somehow I thought I'd tackled that at some point. Ah. I got there for the
PDF output, but didn't realize it's also an issue for the html output.
For FO it sufficed to set the img.src.path param. For HTML that's not enough,
because that just adjusts the link to the file - but we don't want to link to
the source file. We actually solved this for the single-page html version - we
just embed the svg. I wonder if we should just do that as well.
Another way would be to emit the files into the desired place as part of the
stylesheet. While it requires touching xslt, it does seems somewhat more
elegant than just copying files around. I did implement that, curious what you
think.
> 2. Defaults to website stylesheet, no way to configure. This should be
> adjusted to match the make build.
Should we add a meson option?
> 3. The various build targets and their combinations are mismatching and
> incomplete. For example:
>
> Top-level GNUmakefile has these targets:
>
> - docs (builds html and man)
> - html
> - man
>
> (Those are the formats that are part of a distribution build.)
>
> doc/src/sgml/Makefile has these documented targets:
>
> - default target is html
> - all (builds html and man, maps to top-level "docs")
> - html
> - man
> - postgres-A4.pdf
> - postgres-US.pdf
> - check
>
> as well as (undocumented):
>
> - htmlhelp
> - postgres.html
> - postgres.txt
> - epub
> - postgres.epub
> - postgres.info
>
> meson has the following documented targets:
>
> - docs (builds only html)
> - alldocs (builds all formats, including obscure ones)
>
> as well as the following undocumented targets:
>
> - html
> - man
> - html_help [sic]
renamed in the attached patch.
> - postgres-A4.pdf
> - postgres-US.pdf
> - postgres.epub
Note that these are actually named doc/src/sgml/{html,man,...}, not top-level
targets.
> - [info is not implemented at all]
Would be easy to implement, but not sure it's worth doing.
> - [didn't find an equivalent of check]
That's probably worth doing - should it be run as an actual test, or be a
target?
> 4. There doesn't appear to be a way to install the documentation.
> (There are also some open questions in the top-level meson.build about
> the installation directories, but I suppose if we can't install them
> then exactly where to install them hasn't been thought about too
> much.)
WIP patch for that attached. There's now
install-doc-man
install-doc-html
run targets and a
install-docs
alias target.
I did end up getting stuck when hacking on this, and ended up adding css
support for nochunk and support for the website style for htmlhelp and
nochunk, as well as obsoleting the need for copying the css files... But
perhaps that's a bit too much.
Greetings,
Andres Freund
Attachments:
[text/x-diff] v1-0001-meson-rename-html_help-target-to-htmlhelp.patch (1.2K, ../../[email protected]/2-v1-0001-meson-rename-html_help-target-to-htmlhelp.patch)
download | inline diff:
From e604a5c5acdaa840ede0c83205c9799a9edb2630 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 15:53:14 -0700
Subject: [PATCH v1 1/5] meson: rename html_help target to htmlhelp
Reported-by: Peter Eisentraut <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
doc/src/sgml/meson.build | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index 38f1b8e7b17..e6fe124c7bc 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -123,7 +123,7 @@ if xsltproc_bin.found()
# build multi-page html docs as part of docs target
docs += html
- html_help = custom_target('html_help',
+ htmlhelp = custom_target('htmlhelp',
input: ['stylesheet-hh.xsl', postgres_full_xml],
output: 'htmlhelp',
depfile: 'htmlhelp.d',
@@ -131,7 +131,7 @@ if xsltproc_bin.found()
command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
build_by_default: false,
)
- alldocs += html_help
+ alldocs += htmlhelp
# single-page HTML
--
2.38.0
[text/x-diff] v1-0002-meson-make-install_test_files-more-generic-rename.patch (5.1K, ../../[email protected]/3-v1-0002-meson-make-install_test_files-more-generic-rename.patch)
download | inline diff:
From a739950d06678f28f08deb48d080939041dc5584 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:29:27 -0700
Subject: [PATCH v1 2/5] meson: make install_test_files more generic, rename to
install_files
Now it supports installing directories and directory contents as well. This
will be used in a subsequent patch to install doc contents.
---
meson.build | 5 ++-
src/tools/install_files | 71 ++++++++++++++++++++++++++++++++++++
src/tools/install_test_files | 33 -----------------
3 files changed, 74 insertions(+), 35 deletions(-)
create mode 100644 src/tools/install_files
delete mode 100644 src/tools/install_test_files
diff --git a/meson.build b/meson.build
index 2ebdf914c1b..b4b87f1a9fc 100644
--- a/meson.build
+++ b/meson.build
@@ -369,6 +369,8 @@ flex_cmd = [python, flex_wrapper,
wget = find_program('wget', required: false, native: true)
wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
+install_files = files('src/tools/install_files')
+
###############################################################
@@ -2859,9 +2861,8 @@ testprep_targets += test_install_libs
# command to install files used for tests, which aren't installed by default
-install_test_files = files('src/tools/install_test_files')
install_test_files_args = [
- install_test_files,
+ install_files,
'--prefix', dir_prefix,
'--install', contrib_data_dir, test_install_data,
'--install', dir_lib_pkg, test_install_libs,
diff --git a/src/tools/install_files b/src/tools/install_files
new file mode 100644
index 00000000000..23ffd0aa2f7
--- /dev/null
+++ b/src/tools/install_files
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+
+# Helper to install additional files into the temporary installation
+# for tests, beyond those that are installed by meson/ninja install.
+
+import argparse
+import shutil
+import os
+from pathlib import PurePath
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('--destdir', type=str,
+ default=os.environ.get('DESTDIR', None))
+parser.add_argument('--prefix', type=str)
+parser.add_argument('--install', type=str, nargs='+',
+ action='append', default=[])
+parser.add_argument('--install-dirs', type=str, nargs='+',
+ action='append', default=[])
+parser.add_argument('--install-dir-contents', type=str, nargs='+',
+ action='append', default=[])
+
+args = parser.parse_args()
+
+
+def error_exit(msg: str):
+ if os.path.isdir(src):
+ error_exit(f"{src} is a directory")
+
+
+def create_target_dir(prefix: str, destdir: str, targetdir: str):
+ if not os.path.isabs(targetdir):
+ targetdir = os.path.join(prefix, targetdir)
+
+ if destdir is not None:
+ # copy of meson's logic for joining destdir and install paths
+ targetdir = str(PurePath(destdir, *PurePath(targetdir).parts[1:]))
+
+ os.makedirs(targetdir, exist_ok=True)
+
+ return targetdir
+
+
+def copy_files(targetdir: str, src_list: list):
+ for src in src_list:
+ shutil.copy2(src, targetdir)
+
+
+def copy_dirs(targetdir: str, src_list: list, contents: bool):
+ for src in src_list:
+ if not os.path.isdir(src):
+ error_exit('{0} is not a directory'.format(src))
+
+ if contents:
+ target = targetdir
+ else:
+ target = os.path.join(targetdir, os.path.split(src)[1])
+ shutil.copytree(src, target, dirs_exist_ok=True)
+
+
+for installs in args.install:
+ targetdir = create_target_dir(args.prefix, args.destdir, installs[0])
+ copy_files(targetdir, installs[1:])
+
+for installs in args.install_dirs:
+ targetdir = create_target_dir(args.prefix, args.destdir, installs[0])
+ copy_dirs(targetdir, installs[1:], contents=False)
+
+for installs in args.install_dir_contents:
+ targetdir = create_target_dir(args.prefix, args.destdir, installs[0])
+ copy_dirs(targetdir, installs[1:], contents=True)
diff --git a/src/tools/install_test_files b/src/tools/install_test_files
deleted file mode 100644
index 8e0b36a74d1..00000000000
--- a/src/tools/install_test_files
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python3
-
-# Helper to install additional files into the temporary installation
-# for tests, beyond those that are installed by meson/ninja install.
-
-import argparse
-import shutil
-import os
-from pathlib import PurePath
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument('--destdir', type=str, default=os.environ.get('DESTDIR', None))
-parser.add_argument('--prefix', type=str)
-parser.add_argument('--install', type=str, nargs='+', action='append')
-
-args = parser.parse_args()
-
-def copy_files(prefix: str, destdir: str, targetdir: str, src_list: list):
- if not os.path.isabs(targetdir):
- targetdir = os.path.join(prefix, targetdir)
-
- if destdir is not None:
- # copy of meson's logic for joining destdir and install paths
- targetdir = str(PurePath(destdir, *PurePath(targetdir).parts[1:]))
-
- os.makedirs(targetdir, exist_ok=True)
-
- for src in src_list:
- shutil.copy2(src, targetdir)
-
-for installs in args.install:
- copy_files(args.prefix, args.destdir, installs[0], installs[1:])
--
2.38.0
[text/x-diff] v1-0003-wip-meson-add-install-docs-doc-html-doc-man-targe.patch (2.3K, ../../[email protected]/4-v1-0003-wip-meson-add-install-docs-doc-html-doc-man-targe.patch)
download | inline diff:
From 1ca73641bd0def58bbaedd2c3a5b1ae95918cd32 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:43:40 -0700
Subject: [PATCH v1 3/5] wip: meson: add install-{docs,doc-html,doc-man}
targets
---
meson.build | 2 +-
doc/src/sgml/meson.build | 27 ++++++++++++++++++++++++++-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/meson.build b/meson.build
index b4b87f1a9fc..0ffeffa9edc 100644
--- a/meson.build
+++ b/meson.build
@@ -504,7 +504,7 @@ dir_man = get_option('mandir')
# FIXME: These used to be separately configurable - worth adding?
dir_doc = get_option('datadir') / 'doc' / 'postgresql'
-dir_doc_html = dir_doc
+dir_doc_html = dir_doc / 'html'
dir_locale = get_option('localedir')
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index e6fe124c7bc..2da737c8af4 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -1,6 +1,7 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
docs = []
+installdocs = []
alldocs = []
doc_generated = []
@@ -120,8 +121,16 @@ if xsltproc_bin.found()
)
alldocs += html
- # build multi-page html docs as part of docs target
+ install_doc_html = run_target('install-doc-html',
+ command: [
+ python, install_files, '--prefix', dir_prefix,
+ '--install-dir-contents', dir_doc_html, html.full_path()],
+ depends: html)
+
+ # build and install multi-page html docs as part of docs target
docs += html
+ installdocs += install_doc_html
+
htmlhelp = custom_target('htmlhelp',
input: ['stylesheet-hh.xsl', postgres_full_xml],
@@ -208,6 +217,21 @@ if xsltproc_bin.found()
build_by_default: false,
)
alldocs += man
+
+ mans = []
+ foreach man_n : man.to_list()
+ mans += man_n.full_path()
+ endforeach
+
+ install_doc_man = run_target('install-doc-man',
+ command: [
+ python, install_files, '--prefix', dir_prefix,
+ '--install-dirs', dir_man, mans],
+ depends: man)
+
+ # even though we don't want to build man pages as part of 'docs', we do want
+ # to install them as part of install-docs
+ installdocs += install_doc_man
endif
@@ -266,6 +290,7 @@ if docs.length() == 0
run_target('docs', command: [missing, 'xsltproc'])
else
alias_target('docs', docs)
+ alias_target('install-docs', installdocs)
endif
if alldocs.length() == 0
--
2.38.0
[text/x-diff] v1-0004-docs-html-copy-images-to-output-as-part-of-xslt-b.patch (3.3K, ../../[email protected]/5-v1-0004-docs-html-copy-images-to-output-as-part-of-xslt-b.patch)
download | inline diff:
From 679973341b9fb131034e9d7a7acafe761ecf9ff8 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:45:14 -0700
Subject: [PATCH v1 4/5] docs: html: copy images to output as part of xslt
build
---
doc/src/sgml/Makefile | 2 --
doc/src/sgml/stylesheet-common.xsl | 22 ++++++++++++++++++++++
doc/src/sgml/stylesheet-hh.xsl | 6 ++++++
doc/src/sgml/stylesheet.xsl | 7 ++++++-
4 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index b96c7cbf223..1b098f983ec 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -144,7 +144,6 @@ html: html-stamp
html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
- cp $(ALL_IMAGES) html/
cp $(srcdir)/stylesheet.css html/
touch $@
@@ -152,7 +151,6 @@ htmlhelp: htmlhelp-stamp
htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
- cp $(ALL_IMAGES) htmlhelp/
cp $(srcdir)/stylesheet.css htmlhelp/
touch $@
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index 761484c7fef..d2928f86eb7 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -101,4 +101,26 @@
<xsl:apply-templates select="." mode="xref"/>
</xsl:template>
+
+<!--
+ Support for copying images to the output directory, so the output is self
+ contained.
+-->
+<xsl:template name="write-image">
+ <xsl:variable name="input_filename">
+ <xsl:value-of select="imagedata/@fileref"/>
+ </xsl:variable>
+
+ <!-- references images directly, without images/ -->
+ <xsl:variable name="output_filename">
+ <xsl:value-of select="concat($chunk.base.dir, substring-after($input_filename, '/'))"/>
+ </xsl:variable>
+
+ <xsl:call-template name="write.chunk">
+ <xsl:with-param name="filename" select="$output_filename"/>
+ <xsl:with-param name="content" select="document($input_filename)"/>
+ </xsl:call-template>
+</xsl:template>
+
+
</xsl:stylesheet>
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 6f4b706dac6..568ccf36d2a 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -39,6 +39,12 @@
</xsl:template>
+<xsl:template match="imageobject">
+ <xsl:call-template name="write-image"/>
+ <!-- copy images to the output directory, so the output is self contained -->
+ <xsl:apply-templates select="imagedata"/>
+</xsl:template>
+
<!-- strip directory name from image filerefs -->
<xsl:template match="imagedata/@fileref">
<xsl:value-of select="substring-after(., '/')"/>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index b6141303abd..f9163e7d946 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -29,7 +29,12 @@
</xsl:param>
-<!-- strip directory name from image filerefs -->
+<xsl:template match="imageobject">
+ <xsl:call-template name="write-image"/>
+ <!-- copy images to the output directory, so the output is self contained -->
+ <xsl:apply-templates select="imagedata"/>
+</xsl:template>
+
<xsl:template match="imagedata/@fileref">
<xsl:value-of select="substring-after(., '/')"/>
</xsl:template>
--
2.38.0
[text/x-diff] v1-0005-wip-docs-copy-or-inline-css.patch (7.2K, ../../[email protected]/6-v1-0005-wip-docs-copy-or-inline-css.patch)
download | inline diff:
From f5e081b911797fa6897a883b0f1f3e35daf170ad Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 20:49:47 -0700
Subject: [PATCH v1 5/5] wip: docs: copy or inline css
---
doc/src/sgml/Makefile | 4 +---
doc/src/sgml/meson.build | 2 +-
doc/src/sgml/stylesheet-common.xsl | 21 +++++++++++++++++++++
doc/src/sgml/stylesheet-hh.xsl | 10 +++++++++-
doc/src/sgml/stylesheet-html-nochunk.xsl | 20 ++++++++++++++++++++
doc/src/sgml/stylesheet.xsl | 15 ++++++---------
6 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index 1b098f983ec..3f971f6eb33 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -53,7 +53,7 @@ else
XSLTPROC = $(missing) xsltproc
endif
-override XSLTPROCFLAGS += --stringparam pg.version '$(VERSION)'
+override XSLTPROCFLAGS += --xincludestyle --stringparam pg.version '$(VERSION)'
GENERATED_SGML = version.sgml \
@@ -144,14 +144,12 @@ html: html-stamp
html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
- cp $(srcdir)/stylesheet.css html/
touch $@
htmlhelp: htmlhelp-stamp
htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
- cp $(srcdir)/stylesheet.css htmlhelp/
touch $@
# single-page HTML
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index 2da737c8af4..c581c7246f0 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -104,7 +104,7 @@ if xsltproc_bin.found()
xsltproc_flags = [
'--nonet',
'--stringparam', 'pg.version', pg_version,
- '--param', 'website.stylesheet', '1'
+ '--xincludestyle',
]
xsltproc = xmltools_wrapper + [
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index d2928f86eb7..23dea8f29bf 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -41,6 +41,8 @@
<xsl:param name="variablelist.term.break.after">1</xsl:param>
<xsl:param name="variablelist.term.separator"></xsl:param>
<xsl:param name="xref.with.number.and.title" select="0"></xsl:param>
+<!-- currently htmlhelp and other html have no common stylesheet -->
+<xsl:param name="website.stylesheet" select="0"/>
<!-- Change display of some elements -->
@@ -122,5 +124,24 @@
</xsl:call-template>
</xsl:template>
+<xsl:template name="stylesheet-reference">
+ <xsl:choose>
+ <xsl:when test="$website.stylesheet = 0">stylesheet.css
+ <xsl:variable name="styleout" select="concat($base.dir, 'stylesheet.css')"/>
+ <xsl:call-template name="write.chunk">
+ <xsl:with-param name="filename" select="$styleout"/>
+ <xsl:with-param name="quiet" select="0"/> <!-- XXX: remove -->
+ <xsl:with-param name="omit-xml-declaration" select="'yes'"/>
+ <xsl:with-param name="content">
+ <!-- xinclude is only processed in toplevel stylesheet -->
+ <xsl:call-template name="stylesheet-contents"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ https://www.postgresql.org/media/css/docs-complete.css
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
</xsl:stylesheet>
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 568ccf36d2a..6f6c6c64626 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -1,5 +1,6 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/htmlhelp/htmlhelp.xsl"/>
@@ -9,7 +10,6 @@
<xsl:param name="htmlhelp.use.hhk" select="'1'"/>
<xsl:param name="base.dir" select="'htmlhelp/'"></xsl:param>
-<xsl:param name="html.stylesheet" select="'stylesheet.css'"></xsl:param>
<xsl:param name="use.id.as.filename" select="'1'"></xsl:param>
<xsl:param name="manifest.in.base.dir" select="1"/>
<xsl:param name="make.valid.html" select="1"></xsl:param>
@@ -19,6 +19,14 @@
<xsl:param name="chunker.output.indent" select="'yes'"/>
<xsl:param name="chunk.quietly" select="1"></xsl:param>
+<xsl:param name="html.stylesheet">
+ <xsl:call-template name="stylesheet-reference"/>
+</xsl:param>
+
+<xsl:template name="stylesheet-contents">
+ <xi:include href="stylesheet.css" parse="text"/>
+</xsl:template>
+
<!-- Change display of some elements -->
diff --git a/doc/src/sgml/stylesheet-html-nochunk.xsl b/doc/src/sgml/stylesheet-html-nochunk.xsl
index 8167127b93a..560f6a0b0a7 100644
--- a/doc/src/sgml/stylesheet-html-nochunk.xsl
+++ b/doc/src/sgml/stylesheet-html-nochunk.xsl
@@ -1,5 +1,7 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns="http://www.w3.org/1999/xhtml"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"/>
@@ -7,6 +9,24 @@
<xsl:include href="stylesheet-html-common.xsl" />
<xsl:include href="stylesheet-speedup-xhtml.xsl" />
+
+<xsl:param name="generate.css.header" select="1"/>
+<xsl:template name="generate.css.headers">
+ <xsl:choose>
+ <!-- inline css style sheet -->
+ <xsl:when test="$website.stylesheet = 0">
+ <style type="text/css">
+ <xi:include href="stylesheet.css" parse="text"/>
+ </style>
+ </xsl:when>
+ <!-- link to website -->
+ <xsl:otherwise>
+ <link rel="stylesheet" type="text/css" href="https://www.postgresql.org/media/css/docs-complete.css"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
<!-- embed SVG images into output file -->
<xsl:template match="imagedata[@format='SVG']">
<xsl:variable name="filename">
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index f9163e7d946..c551b9cd809 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -1,7 +1,8 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version='1.0'
- xmlns="http://www.w3.org/1999/xhtml">
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl"/>
<xsl:include href="stylesheet-common.xsl" />
@@ -17,17 +18,13 @@
<xsl:param name="chunk.quietly" select="1"></xsl:param>
<xsl:param name="admon.style"></xsl:param> <!-- handled by CSS stylesheet -->
-<xsl:param name="website.stylesheet" select="0"/>
-
<xsl:param name="html.stylesheet">
- <xsl:choose>
- <xsl:when test="$website.stylesheet = 0">stylesheet.css</xsl:when>
- <xsl:otherwise>
- https://www.postgresql.org/media/css/docs-complete.css
- </xsl:otherwise>
- </xsl:choose>
+ <xsl:call-template name="stylesheet-reference"/>
</xsl:param>
+<xsl:template name="stylesheet-contents">
+ <xi:include href="stylesheet.css" parse="text"/>
+</xsl:template>
<xsl:template match="imageobject">
<xsl:call-template name="write-image"/>
--
2.38.0
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
@ 2023-03-20 02:33 ` Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 19:16 ` Re: meson documentation build open issues Andres Freund <[email protected]>
0 siblings, 2 replies; 33+ messages in thread
From: Andres Freund @ 2023-03-20 02:33 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-15 20:55:33 -0700, Andres Freund wrote:
> WIP patch for that attached. There's now
> install-doc-man
> install-doc-html
> run targets and a
> install-docs
> alias target.
>
>
> I did end up getting stuck when hacking on this, and ended up adding css
> support for nochunk and support for the website style for htmlhelp and
> nochunk, as well as obsoleting the need for copying the css files... But
> perhaps that's a bit too much.
Updated set of patches attached. This one works in older meson versions too
and adds install-world and install-quiet targets.
I also ended up getting so frustrated at the docs build speed that I started
to hack a bit on that. I attached a patch shaving a few seconds off the
buildtime.
I think we can make the docs build in parallel and incrementally, by building
the different parts of the docs in parallel, using --stringparam rootid,
e.g. building each 'part' separately.
A very very rough draft attached:
parallel with parts:
real 0m10.831s
user 0m58.295s
sys 0m1.402s
normal:
real 0m32.215s
user 0m31.876s
sys 0m0.328s
1/3 of the build time at 2x the cost is nothing to sneeze at.
Greetings,
Andres Freund
Attachments:
[text/x-diff] v2-0001-meson-rename-html_help-target-to-htmlhelp.patch (1.2K, ../../[email protected]/2-v2-0001-meson-rename-html_help-target-to-htmlhelp.patch)
download | inline diff:
From 9313c2938265109b3a82f96697bae26ed3f74412 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 15:53:14 -0700
Subject: [PATCH v2 1/8] meson: rename html_help target to htmlhelp
Reported-by: Peter Eisentraut <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
doc/src/sgml/meson.build | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index 38f1b8e7b17..e6fe124c7bc 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -123,7 +123,7 @@ if xsltproc_bin.found()
# build multi-page html docs as part of docs target
docs += html
- html_help = custom_target('html_help',
+ htmlhelp = custom_target('htmlhelp',
input: ['stylesheet-hh.xsl', postgres_full_xml],
output: 'htmlhelp',
depfile: 'htmlhelp.d',
@@ -131,7 +131,7 @@ if xsltproc_bin.found()
command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
build_by_default: false,
)
- alldocs += html_help
+ alldocs += htmlhelp
# single-page HTML
--
2.38.0
[text/x-diff] v2-0002-meson-make-install_test_files-more-generic-rename.patch (5.1K, ../../[email protected]/3-v2-0002-meson-make-install_test_files-more-generic-rename.patch)
download | inline diff:
From 10fd8d20ae15889054e69e0b62a445e52b9afe2f Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:29:27 -0700
Subject: [PATCH v2 2/8] meson: make install_test_files more generic, rename to
install_files
Now it supports installing directories and directory contents as well. This
will be used in a subsequent patch to install doc contents.
---
meson.build | 5 ++-
src/tools/install_files | 72 ++++++++++++++++++++++++++++++++++++
src/tools/install_test_files | 33 -----------------
3 files changed, 75 insertions(+), 35 deletions(-)
create mode 100644 src/tools/install_files
delete mode 100644 src/tools/install_test_files
diff --git a/meson.build b/meson.build
index 7f76a101ecf..84fe2c3d4c3 100644
--- a/meson.build
+++ b/meson.build
@@ -369,6 +369,8 @@ flex_cmd = [python, flex_wrapper,
wget = find_program('wget', required: false, native: true)
wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
+install_files = files('src/tools/install_files')
+
###############################################################
@@ -2845,9 +2847,8 @@ testprep_targets += test_install_libs
# command to install files used for tests, which aren't installed by default
-install_test_files = files('src/tools/install_test_files')
install_test_files_args = [
- install_test_files,
+ install_files,
'--prefix', dir_prefix,
'--install', contrib_data_dir, test_install_data,
'--install', dir_lib_pkg, test_install_libs,
diff --git a/src/tools/install_files b/src/tools/install_files
new file mode 100644
index 00000000000..0428316b67e
--- /dev/null
+++ b/src/tools/install_files
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+# Helper to install additional files into the temporary installation
+# for tests, beyond those that are installed by meson/ninja install.
+
+import argparse
+import os
+import shutil
+import sys
+from pathlib import PurePath
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('--destdir', type=str,
+ default=os.environ.get('DESTDIR', None))
+parser.add_argument('--prefix', type=str)
+parser.add_argument('--install', type=str, nargs='+',
+ action='append', default=[])
+parser.add_argument('--install-dirs', type=str, nargs='+',
+ action='append', default=[])
+parser.add_argument('--install-dir-contents', type=str, nargs='+',
+ action='append', default=[])
+
+args = parser.parse_args()
+
+
+def error_exit(msg: str):
+ print(msg, file=sys.stderr)
+ exit(1)
+
+
+def create_target_dir(prefix: str, destdir: str, targetdir: str):
+ if not os.path.isabs(targetdir):
+ targetdir = os.path.join(prefix, targetdir)
+
+ if destdir is not None:
+ # copy of meson's logic for joining destdir and install paths
+ targetdir = str(PurePath(destdir, *PurePath(targetdir).parts[1:]))
+
+ os.makedirs(targetdir, exist_ok=True)
+
+ return targetdir
+
+
+def copy_files(targetdir: str, src_list: list):
+ for src in src_list:
+ shutil.copy2(src, targetdir)
+
+
+def copy_dirs(targetdir: str, src_list: list, contents: bool):
+ for src in src_list:
+ if not os.path.isdir(src):
+ error_exit('{0} is not a directory'.format(src))
+
+ if contents:
+ target = targetdir
+ else:
+ target = os.path.join(targetdir, os.path.split(src)[1])
+ shutil.copytree(src, target, dirs_exist_ok=True)
+
+
+for installs in args.install:
+ targetdir = create_target_dir(args.prefix, args.destdir, installs[0])
+ copy_files(targetdir, installs[1:])
+
+for installs in args.install_dirs:
+ targetdir = create_target_dir(args.prefix, args.destdir, installs[0])
+ copy_dirs(targetdir, installs[1:], contents=False)
+
+for installs in args.install_dir_contents:
+ targetdir = create_target_dir(args.prefix, args.destdir, installs[0])
+ copy_dirs(targetdir, installs[1:], contents=True)
diff --git a/src/tools/install_test_files b/src/tools/install_test_files
deleted file mode 100644
index 8e0b36a74d1..00000000000
--- a/src/tools/install_test_files
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python3
-
-# Helper to install additional files into the temporary installation
-# for tests, beyond those that are installed by meson/ninja install.
-
-import argparse
-import shutil
-import os
-from pathlib import PurePath
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument('--destdir', type=str, default=os.environ.get('DESTDIR', None))
-parser.add_argument('--prefix', type=str)
-parser.add_argument('--install', type=str, nargs='+', action='append')
-
-args = parser.parse_args()
-
-def copy_files(prefix: str, destdir: str, targetdir: str, src_list: list):
- if not os.path.isabs(targetdir):
- targetdir = os.path.join(prefix, targetdir)
-
- if destdir is not None:
- # copy of meson's logic for joining destdir and install paths
- targetdir = str(PurePath(destdir, *PurePath(targetdir).parts[1:]))
-
- os.makedirs(targetdir, exist_ok=True)
-
- for src in src_list:
- shutil.copy2(src, targetdir)
-
-for installs in args.install:
- copy_files(args.prefix, args.destdir, installs[0], installs[1:])
--
2.38.0
[text/x-diff] v2-0003-wip-meson-add-install-docs-doc-html-doc-man-targe.patch (3.8K, ../../[email protected]/4-v2-0003-wip-meson-add-install-docs-doc-html-doc-man-targe.patch)
download | inline diff:
From bb0756f88e3dba00bfc5f3efe90d9ab785f62831 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:43:40 -0700
Subject: [PATCH v2 3/8] wip: meson: add install-{docs,doc-html,doc-man}
targets
---
meson.build | 2 +-
doc/src/sgml/meson.build | 67 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/meson.build b/meson.build
index 84fe2c3d4c3..33dd5b43ed5 100644
--- a/meson.build
+++ b/meson.build
@@ -504,7 +504,7 @@ dir_man = get_option('mandir')
# FIXME: These used to be separately configurable - worth adding?
dir_doc = get_option('datadir') / 'doc' / 'postgresql'
-dir_doc_html = dir_doc
+dir_doc_html = dir_doc / 'html'
dir_locale = get_option('localedir')
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index e6fe124c7bc..f24acebb0fe 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -1,6 +1,7 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
docs = []
+installdocs = []
alldocs = []
doc_generated = []
@@ -120,8 +121,21 @@ if xsltproc_bin.found()
)
alldocs += html
- # build multi-page html docs as part of docs target
+ install_doc_html = custom_target('install-html',
+ output: 'install-html', input: html,
+ command: [
+ python, install_files, '--prefix', dir_prefix,
+ '--install-dir-contents', dir_doc_html, '@INPUT@'],
+ depends: html,
+ build_always_stale: true,
+ build_by_default: false,
+ )
+ alias_target('install-doc-html', install_doc_html)
+
+ # build and install multi-page html docs as part of docs target
docs += html
+ installdocs += install_doc_html
+
htmlhelp = custom_target('htmlhelp',
input: ['stylesheet-hh.xsl', postgres_full_xml],
@@ -208,6 +222,20 @@ if xsltproc_bin.found()
build_by_default: false,
)
alldocs += man
+
+ install_doc_man = custom_target('install-man',
+ output: 'install-man', input: man,
+ command: [
+ python, install_files, '--prefix', dir_prefix,
+ '--install-dirs', dir_man, '@INPUT@'],
+ build_always_stale: true,
+ build_by_default: false,
+ )
+ alias_target('install-doc-man', install_doc_man)
+
+ # even though we don't want to build man pages as part of 'docs', we do want
+ # to install them as part of install-docs
+ installdocs += install_doc_man
endif
@@ -262,10 +290,47 @@ if pandoc.found() and xsltproc_bin.found()
alldocs += postgres_epub
endif
+
+##
+## Experimental Texinfo targets
+##
+
+db2x_xsltproc = find_program('db2x_xsltproc', native: true, required: false)
+db2x_texixml = find_program('db2x_texixml', native: true, required: false)
+makeinfo = find_program('makeinfo', native: true, required: false)
+
+if db2x_texixml.found() and db2x_xsltproc.found() and makeinfo.found()
+ postgres_texixml = custom_target('postgres.texixml',
+ input: postgres_full_xml,
+ output: 'postgres.texixml',
+ command: [db2x_xsltproc, '-s', 'texi',
+ '-g', 'output-file=postgres',
+ '@INPUT@', '-o', '@OUTPUT@'],
+ build_by_default: false,
+ )
+ postgres_texi = custom_target('postgres.texi',
+ input: postgres_texixml,
+ output: 'postgres.texi',
+ command: [db2x_texixml, '--encoding=utf-8', '@INPUT@',
+ '--output-dir=@OUTDIR@'],
+ build_by_default: false,
+ )
+ postgres_info = custom_target('postgres.info',
+ input: postgres_texi,
+ output: 'postgres.info',
+ command: [makeinfo, '--enable-encoding', '--no-split', '--no-validate',
+ '@INPUT@', '-o', '@OUTPUT0@'],
+ build_by_default: false,
+ )
+ alldocs += postgres_info
+endif
+
+
if docs.length() == 0
run_target('docs', command: [missing, 'xsltproc'])
else
alias_target('docs', docs)
+ alias_target('install-docs', installdocs)
endif
if alldocs.length() == 0
--
2.38.0
[text/x-diff] v2-0004-meson-add-install-quiet-world-targets.patch (14.9K, ../../[email protected]/5-v2-0004-meson-add-install-quiet-world-targets.patch)
download | inline diff:
From efc9cc54de401cb7e20e02d4edbe85384ecf01a2 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Sun, 19 Mar 2023 11:17:54 -0700
Subject: [PATCH v2 4/8] meson: add install-{quiet, world} targets
---
meson.build | 36 ++++++++++++++++++++++
src/backend/po/meson.build | 2 +-
src/bin/initdb/po/meson.build | 2 +-
src/bin/pg_amcheck/po/meson.build | 2 +-
src/bin/pg_archivecleanup/po/meson.build | 2 +-
src/bin/pg_basebackup/po/meson.build | 2 +-
src/bin/pg_checksums/po/meson.build | 2 +-
src/bin/pg_config/po/meson.build | 2 +-
src/bin/pg_controldata/po/meson.build | 2 +-
src/bin/pg_ctl/po/meson.build | 2 +-
src/bin/pg_dump/po/meson.build | 2 +-
src/bin/pg_resetwal/po/meson.build | 2 +-
src/bin/pg_rewind/po/meson.build | 2 +-
src/bin/pg_test_fsync/po/meson.build | 2 +-
src/bin/pg_test_timing/po/meson.build | 2 +-
src/bin/pg_upgrade/po/meson.build | 2 +-
src/bin/pg_verifybackup/po/meson.build | 2 +-
src/bin/pg_waldump/po/meson.build | 2 +-
src/bin/psql/po/meson.build | 2 +-
src/bin/scripts/po/meson.build | 2 +-
src/interfaces/libpq/po/meson.build | 2 +-
src/interfaces/libpq/test/meson.build | 4 +--
src/pl/plperl/po/meson.build | 2 +-
src/pl/plpgsql/src/po/meson.build | 2 +-
src/pl/plpython/po/meson.build | 2 +-
src/pl/tcl/po/meson.build | 2 +-
src/interfaces/ecpg/ecpglib/po/meson.build | 2 +-
src/interfaces/ecpg/preproc/po/meson.build | 2 +-
28 files changed, 64 insertions(+), 28 deletions(-)
diff --git a/meson.build b/meson.build
index 33dd5b43ed5..bd1c1bf9f1d 100644
--- a/meson.build
+++ b/meson.build
@@ -2543,6 +2543,7 @@ bin_targets = []
pl_targets = []
contrib_targets = []
testprep_targets = []
+nls_targets = []
# Define the tests to distribute them to the correct test styles later
@@ -3185,6 +3186,7 @@ if meson.version().version_compare('>=0.57')
endif
+
###############################################################
# Pseudo targets
###############################################################
@@ -3195,6 +3197,40 @@ alias_target('pl', pl_targets)
alias_target('contrib', contrib_targets)
alias_target('testprep', testprep_targets)
+# i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
+# generation happens during install, so that's not a real issue.
+nls_mo_targets = []
+if libintl.found() and meson.version().version_compare('>=0.60')
+ # use range() to avoid the flattening of the list that forech() would do
+ foreach off : range(0, nls_targets.length())
+ # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
+ # -pot target 3) maintainer -pot target
+ nls_mo_targets += nls_targets[off][0]
+ endforeach
+ alias_target('nls', nls_mo_targets)
+endif
+
+all_built = [
+ backend_targets,
+ bin_targets,
+ libpq_st,
+ pl_targets,
+ contrib_targets,
+ nls_mo_targets,
+ testprep_targets,
+ ecpg_targets,
+]
+
+install_quiet = custom_target('install-quiet',
+ output: 'install-quiet',
+ build_always_stale: true,
+ build_by_default: false,
+ command: meson_args + ['install', '--quiet', '--no-rebuild'],
+ depends: all_built,
+)
+
+alias_target('install-world', install_quiet, installdocs)
+
###############################################################
diff --git a/src/backend/po/meson.build b/src/backend/po/meson.build
index 772399b0bd8..b3cb083eb2c 100644
--- a/src/backend/po/meson.build
+++ b/src/backend/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('postgres-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('postgres-' + pg_version_major.to_string())]
diff --git a/src/bin/initdb/po/meson.build b/src/bin/initdb/po/meson.build
index 6506c5264ee..ad193984175 100644
--- a/src/bin/initdb/po/meson.build
+++ b/src/bin/initdb/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('initdb-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('initdb-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_amcheck/po/meson.build b/src/bin/pg_amcheck/po/meson.build
index 9cb62eaaae2..618de4f4458 100644
--- a/src/bin/pg_amcheck/po/meson.build
+++ b/src/bin/pg_amcheck/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_amcheck-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_amcheck-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_archivecleanup/po/meson.build b/src/bin/pg_archivecleanup/po/meson.build
index 79957fa2b98..c6f33edcad1 100644
--- a/src/bin/pg_archivecleanup/po/meson.build
+++ b/src/bin/pg_archivecleanup/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_archivecleanup-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_archivecleanup-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_basebackup/po/meson.build b/src/bin/pg_basebackup/po/meson.build
index 437f7e0606f..2a39f5ffeba 100644
--- a/src/bin/pg_basebackup/po/meson.build
+++ b/src/bin/pg_basebackup/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_basebackup-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_basebackup-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_checksums/po/meson.build b/src/bin/pg_checksums/po/meson.build
index 273f55d9012..97b20f263cf 100644
--- a/src/bin/pg_checksums/po/meson.build
+++ b/src/bin/pg_checksums/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_checksums-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_checksums-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_config/po/meson.build b/src/bin/pg_config/po/meson.build
index 7e4629ffc4e..f515af816ea 100644
--- a/src/bin/pg_config/po/meson.build
+++ b/src/bin/pg_config/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_config-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_config-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_controldata/po/meson.build b/src/bin/pg_controldata/po/meson.build
index 73a77568b6f..685f1c167a7 100644
--- a/src/bin/pg_controldata/po/meson.build
+++ b/src/bin/pg_controldata/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_controldata-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_controldata-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_ctl/po/meson.build b/src/bin/pg_ctl/po/meson.build
index da7710090e0..3d35f7ad211 100644
--- a/src/bin/pg_ctl/po/meson.build
+++ b/src/bin/pg_ctl/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_ctl-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_ctl-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_dump/po/meson.build b/src/bin/pg_dump/po/meson.build
index 3b627711d73..cc9d3680119 100644
--- a/src/bin/pg_dump/po/meson.build
+++ b/src/bin/pg_dump/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_dump-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_dump-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_resetwal/po/meson.build b/src/bin/pg_resetwal/po/meson.build
index c82c6a4263f..fa75d287526 100644
--- a/src/bin/pg_resetwal/po/meson.build
+++ b/src/bin/pg_resetwal/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_resetwal-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_resetwal-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_rewind/po/meson.build b/src/bin/pg_rewind/po/meson.build
index b0ccd16bbb9..bffe7debea9 100644
--- a/src/bin/pg_rewind/po/meson.build
+++ b/src/bin/pg_rewind/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_rewind-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_rewind-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_test_fsync/po/meson.build b/src/bin/pg_test_fsync/po/meson.build
index b25a6d0f826..46d0ac587e2 100644
--- a/src/bin/pg_test_fsync/po/meson.build
+++ b/src/bin/pg_test_fsync/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_test_fsync-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_test_fsync-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_test_timing/po/meson.build b/src/bin/pg_test_timing/po/meson.build
index 918225a1cf5..7bc84d5c7ff 100644
--- a/src/bin/pg_test_timing/po/meson.build
+++ b/src/bin/pg_test_timing/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_test_timing-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_test_timing-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_upgrade/po/meson.build b/src/bin/pg_upgrade/po/meson.build
index b4ba67b1c5e..8531c3b31ef 100644
--- a/src/bin/pg_upgrade/po/meson.build
+++ b/src/bin/pg_upgrade/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_upgrade-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_upgrade-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_verifybackup/po/meson.build b/src/bin/pg_verifybackup/po/meson.build
index 092b8a95f31..181cf640e71 100644
--- a/src/bin/pg_verifybackup/po/meson.build
+++ b/src/bin/pg_verifybackup/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_verifybackup-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_verifybackup-' + pg_version_major.to_string())]
diff --git a/src/bin/pg_waldump/po/meson.build b/src/bin/pg_waldump/po/meson.build
index 076dbcafb42..c4188032d71 100644
--- a/src/bin/pg_waldump/po/meson.build
+++ b/src/bin/pg_waldump/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pg_waldump-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pg_waldump-' + pg_version_major.to_string())]
diff --git a/src/bin/psql/po/meson.build b/src/bin/psql/po/meson.build
index da257154d4c..103ed93a789 100644
--- a/src/bin/psql/po/meson.build
+++ b/src/bin/psql/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('psql-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('psql-' + pg_version_major.to_string())]
diff --git a/src/bin/scripts/po/meson.build b/src/bin/scripts/po/meson.build
index a28ec9ed049..00a9f208acf 100644
--- a/src/bin/scripts/po/meson.build
+++ b/src/bin/scripts/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pgscripts-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pgscripts-' + pg_version_major.to_string())]
diff --git a/src/interfaces/libpq/po/meson.build b/src/interfaces/libpq/po/meson.build
index 5489b23b795..a7a1df87ae3 100644
--- a/src/interfaces/libpq/po/meson.build
+++ b/src/interfaces/libpq/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('libpq' + '5' + '-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('libpq' + '5' + '-' + pg_version_major.to_string())]
diff --git a/src/interfaces/libpq/test/meson.build b/src/interfaces/libpq/test/meson.build
index b2a4b06fd23..d56b63e1186 100644
--- a/src/interfaces/libpq/test/meson.build
+++ b/src/interfaces/libpq/test/meson.build
@@ -10,7 +10,7 @@ if host_system == 'windows'
'--FILEDESC', 'libpq test program',])
endif
-executable('libpq_uri_regress',
+testprep_targets += executable('libpq_uri_regress',
libpq_uri_regress_sources,
dependencies: [frontend_code, libpq],
kwargs: default_bin_args + {
@@ -29,7 +29,7 @@ if host_system == 'windows'
'--FILEDESC', 'libpq test program',])
endif
-executable('libpq_testclient',
+testprep_targets += executable('libpq_testclient',
libpq_testclient_sources,
dependencies: [frontend_code, libpq],
kwargs: default_bin_args + {
diff --git a/src/pl/plperl/po/meson.build b/src/pl/plperl/po/meson.build
index 2c3cd190cd9..f1c8e7d00a1 100644
--- a/src/pl/plperl/po/meson.build
+++ b/src/pl/plperl/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('plperl-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('plperl-' + pg_version_major.to_string())]
diff --git a/src/pl/plpgsql/src/po/meson.build b/src/pl/plpgsql/src/po/meson.build
index 1fa97d6ab50..aad875948b6 100644
--- a/src/pl/plpgsql/src/po/meson.build
+++ b/src/pl/plpgsql/src/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('plpgsql-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('plpgsql-' + pg_version_major.to_string())]
diff --git a/src/pl/plpython/po/meson.build b/src/pl/plpython/po/meson.build
index e5457069b47..ddaa1913b84 100644
--- a/src/pl/plpython/po/meson.build
+++ b/src/pl/plpython/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('plpython-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('plpython-' + pg_version_major.to_string())]
diff --git a/src/pl/tcl/po/meson.build b/src/pl/tcl/po/meson.build
index 3e7d3287f84..9e5c8e95f15 100644
--- a/src/pl/tcl/po/meson.build
+++ b/src/pl/tcl/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('pltcl-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('pltcl-' + pg_version_major.to_string())]
diff --git a/src/interfaces/ecpg/ecpglib/po/meson.build b/src/interfaces/ecpg/ecpglib/po/meson.build
index 95e748d9fa5..39fcc93bfcc 100644
--- a/src/interfaces/ecpg/ecpglib/po/meson.build
+++ b/src/interfaces/ecpg/ecpglib/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('ecpglib' + '6' + '-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('ecpglib' + '6' + '-' + pg_version_major.to_string())]
diff --git a/src/interfaces/ecpg/preproc/po/meson.build b/src/interfaces/ecpg/preproc/po/meson.build
index 1b2ff116547..43f825b88bb 100644
--- a/src/interfaces/ecpg/preproc/po/meson.build
+++ b/src/interfaces/ecpg/preproc/po/meson.build
@@ -1,3 +1,3 @@
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
-i18n.gettext('ecpg-' + pg_version_major.to_string())
+nls_targets += [i18n.gettext('ecpg-' + pg_version_major.to_string())]
--
2.38.0
[text/x-diff] v2-0005-docs-html-copy-images-to-output-as-part-of-xslt-b.patch (3.3K, ../../[email protected]/6-v2-0005-docs-html-copy-images-to-output-as-part-of-xslt-b.patch)
download | inline diff:
From f7f88de6726a5663d90da5a454c9b3a1ba037fd1 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:45:14 -0700
Subject: [PATCH v2 5/8] docs: html: copy images to output as part of xslt
build
---
doc/src/sgml/Makefile | 2 --
doc/src/sgml/stylesheet-common.xsl | 22 ++++++++++++++++++++++
doc/src/sgml/stylesheet-hh.xsl | 6 ++++++
doc/src/sgml/stylesheet.xsl | 7 ++++++-
4 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index b96c7cbf223..1b098f983ec 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -144,7 +144,6 @@ html: html-stamp
html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
- cp $(ALL_IMAGES) html/
cp $(srcdir)/stylesheet.css html/
touch $@
@@ -152,7 +151,6 @@ htmlhelp: htmlhelp-stamp
htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
- cp $(ALL_IMAGES) htmlhelp/
cp $(srcdir)/stylesheet.css htmlhelp/
touch $@
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index 761484c7fef..d2928f86eb7 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -101,4 +101,26 @@
<xsl:apply-templates select="." mode="xref"/>
</xsl:template>
+
+<!--
+ Support for copying images to the output directory, so the output is self
+ contained.
+-->
+<xsl:template name="write-image">
+ <xsl:variable name="input_filename">
+ <xsl:value-of select="imagedata/@fileref"/>
+ </xsl:variable>
+
+ <!-- references images directly, without images/ -->
+ <xsl:variable name="output_filename">
+ <xsl:value-of select="concat($chunk.base.dir, substring-after($input_filename, '/'))"/>
+ </xsl:variable>
+
+ <xsl:call-template name="write.chunk">
+ <xsl:with-param name="filename" select="$output_filename"/>
+ <xsl:with-param name="content" select="document($input_filename)"/>
+ </xsl:call-template>
+</xsl:template>
+
+
</xsl:stylesheet>
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 6f4b706dac6..568ccf36d2a 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -39,6 +39,12 @@
</xsl:template>
+<xsl:template match="imageobject">
+ <xsl:call-template name="write-image"/>
+ <!-- copy images to the output directory, so the output is self contained -->
+ <xsl:apply-templates select="imagedata"/>
+</xsl:template>
+
<!-- strip directory name from image filerefs -->
<xsl:template match="imagedata/@fileref">
<xsl:value-of select="substring-after(., '/')"/>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index b6141303abd..f9163e7d946 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -29,7 +29,12 @@
</xsl:param>
-<!-- strip directory name from image filerefs -->
+<xsl:template match="imageobject">
+ <xsl:call-template name="write-image"/>
+ <!-- copy images to the output directory, so the output is self contained -->
+ <xsl:apply-templates select="imagedata"/>
+</xsl:template>
+
<xsl:template match="imagedata/@fileref">
<xsl:value-of select="substring-after(., '/')"/>
</xsl:template>
--
2.38.0
[text/x-diff] v2-0006-wip-docs-copy-or-inline-css.patch (7.1K, ../../[email protected]/7-v2-0006-wip-docs-copy-or-inline-css.patch)
download | inline diff:
From 481cf9704196db7f6c05054943d184bfea303466 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 20:49:47 -0700
Subject: [PATCH v2 6/8] wip: docs: copy or inline css
---
doc/src/sgml/Makefile | 4 +---
doc/src/sgml/meson.build | 2 +-
doc/src/sgml/stylesheet-common.xsl | 20 ++++++++++++++++++++
doc/src/sgml/stylesheet-hh.xsl | 10 +++++++++-
doc/src/sgml/stylesheet-html-nochunk.xsl | 20 ++++++++++++++++++++
doc/src/sgml/stylesheet.xsl | 15 ++++++---------
6 files changed, 57 insertions(+), 14 deletions(-)
diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index 1b098f983ec..3f971f6eb33 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -53,7 +53,7 @@ else
XSLTPROC = $(missing) xsltproc
endif
-override XSLTPROCFLAGS += --stringparam pg.version '$(VERSION)'
+override XSLTPROCFLAGS += --xincludestyle --stringparam pg.version '$(VERSION)'
GENERATED_SGML = version.sgml \
@@ -144,14 +144,12 @@ html: html-stamp
html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
- cp $(srcdir)/stylesheet.css html/
touch $@
htmlhelp: htmlhelp-stamp
htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
- cp $(srcdir)/stylesheet.css htmlhelp/
touch $@
# single-page HTML
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index f24acebb0fe..bbe285b29bb 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -104,7 +104,7 @@ if xsltproc_bin.found()
xsltproc_flags = [
'--nonet',
'--stringparam', 'pg.version', pg_version,
- '--param', 'website.stylesheet', '1'
+ '--xincludestyle',
]
xsltproc = xmltools_wrapper + [
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index d2928f86eb7..25d05b9e5f4 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -41,6 +41,8 @@
<xsl:param name="variablelist.term.break.after">1</xsl:param>
<xsl:param name="variablelist.term.separator"></xsl:param>
<xsl:param name="xref.with.number.and.title" select="0"></xsl:param>
+<!-- currently htmlhelp and other html have no common stylesheet -->
+<xsl:param name="website.stylesheet" select="0"/>
<!-- Change display of some elements -->
@@ -122,5 +124,23 @@
</xsl:call-template>
</xsl:template>
+<xsl:template name="stylesheet-reference">
+ <xsl:choose>
+ <xsl:when test="$website.stylesheet = 0">stylesheet.css
+ <xsl:variable name="styleout" select="concat($base.dir, 'stylesheet.css')"/>
+ <xsl:call-template name="write.chunk">
+ <xsl:with-param name="filename" select="$styleout"/>
+ <xsl:with-param name="omit-xml-declaration" select="'yes'"/>
+ <xsl:with-param name="content">
+ <!-- xinclude is only processed in toplevel stylesheet -->
+ <xsl:call-template name="stylesheet-contents"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ https://www.postgresql.org/media/css/docs-complete.css
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
</xsl:stylesheet>
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 568ccf36d2a..6f6c6c64626 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -1,5 +1,6 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/htmlhelp/htmlhelp.xsl"/>
@@ -9,7 +10,6 @@
<xsl:param name="htmlhelp.use.hhk" select="'1'"/>
<xsl:param name="base.dir" select="'htmlhelp/'"></xsl:param>
-<xsl:param name="html.stylesheet" select="'stylesheet.css'"></xsl:param>
<xsl:param name="use.id.as.filename" select="'1'"></xsl:param>
<xsl:param name="manifest.in.base.dir" select="1"/>
<xsl:param name="make.valid.html" select="1"></xsl:param>
@@ -19,6 +19,14 @@
<xsl:param name="chunker.output.indent" select="'yes'"/>
<xsl:param name="chunk.quietly" select="1"></xsl:param>
+<xsl:param name="html.stylesheet">
+ <xsl:call-template name="stylesheet-reference"/>
+</xsl:param>
+
+<xsl:template name="stylesheet-contents">
+ <xi:include href="stylesheet.css" parse="text"/>
+</xsl:template>
+
<!-- Change display of some elements -->
diff --git a/doc/src/sgml/stylesheet-html-nochunk.xsl b/doc/src/sgml/stylesheet-html-nochunk.xsl
index 8167127b93a..560f6a0b0a7 100644
--- a/doc/src/sgml/stylesheet-html-nochunk.xsl
+++ b/doc/src/sgml/stylesheet-html-nochunk.xsl
@@ -1,5 +1,7 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns="http://www.w3.org/1999/xhtml"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"/>
@@ -7,6 +9,24 @@
<xsl:include href="stylesheet-html-common.xsl" />
<xsl:include href="stylesheet-speedup-xhtml.xsl" />
+
+<xsl:param name="generate.css.header" select="1"/>
+<xsl:template name="generate.css.headers">
+ <xsl:choose>
+ <!-- inline css style sheet -->
+ <xsl:when test="$website.stylesheet = 0">
+ <style type="text/css">
+ <xi:include href="stylesheet.css" parse="text"/>
+ </style>
+ </xsl:when>
+ <!-- link to website -->
+ <xsl:otherwise>
+ <link rel="stylesheet" type="text/css" href="https://www.postgresql.org/media/css/docs-complete.css"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
<!-- embed SVG images into output file -->
<xsl:template match="imagedata[@format='SVG']">
<xsl:variable name="filename">
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index f9163e7d946..c551b9cd809 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -1,7 +1,8 @@
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version='1.0'
- xmlns="http://www.w3.org/1999/xhtml">
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl"/>
<xsl:include href="stylesheet-common.xsl" />
@@ -17,17 +18,13 @@
<xsl:param name="chunk.quietly" select="1"></xsl:param>
<xsl:param name="admon.style"></xsl:param> <!-- handled by CSS stylesheet -->
-<xsl:param name="website.stylesheet" select="0"/>
-
<xsl:param name="html.stylesheet">
- <xsl:choose>
- <xsl:when test="$website.stylesheet = 0">stylesheet.css</xsl:when>
- <xsl:otherwise>
- https://www.postgresql.org/media/css/docs-complete.css
- </xsl:otherwise>
- </xsl:choose>
+ <xsl:call-template name="stylesheet-reference"/>
</xsl:param>
+<xsl:template name="stylesheet-contents">
+ <xi:include href="stylesheet.css" parse="text"/>
+</xsl:template>
<xsl:template match="imageobject">
<xsl:call-template name="write-image"/>
--
2.38.0
[text/x-diff] v2-0007-docs-speed-up-docs-build-by-special-casing-the-ge.patch (3.6K, ../../[email protected]/8-v2-0007-docs-speed-up-docs-build-by-special-casing-the-ge.patch)
download | inline diff:
From 8d4cc69ea39304decc78d687fd3b97a9b1907bf9 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Sun, 19 Mar 2023 11:11:37 -0700
Subject: [PATCH v2 7/8] docs: speed up docs build by special-casing the
gentext.template
---
doc/src/sgml/stylesheet-speedup-common.xsl | 79 ++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/doc/src/sgml/stylesheet-speedup-common.xsl b/doc/src/sgml/stylesheet-speedup-common.xsl
index e3fb582a1cc..5266ff587b9 100644
--- a/doc/src/sgml/stylesheet-speedup-common.xsl
+++ b/doc/src/sgml/stylesheet-speedup-common.xsl
@@ -96,5 +96,84 @@
<!-- Just hardcode the language for the whole document, to make it faster. -->
<xsl:template name="l10n.language">en</xsl:template>
+<xsl:param name='pg.l10n.xml' select="document('file:///usr/share/xml/docbook/stylesheet/docbook-xsl/common/en.xml')[1]"/>
+
+<xsl:template name="gentext.template.recurse">
+ <xsl:param name="context"/>
+ <xsl:param name="name"/>
+ <xsl:param name="origname"/>
+ <xsl:param name="verbose"/>
+
+
+ <xsl:choose>
+ <xsl:when test="contains($name, '/')">
+ <xsl:call-template name="gentext.template.recurse">
+ <xsl:with-param name="context" select="$context"/>
+ <xsl:with-param name="name" select="substring-after($name, '/')"/>
+ <xsl:with-param name="origname" select="$origname"/>
+ <xsl:with-param name="verbose" select="$verbose"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+
+ <!-- FIXME: should find a way to avoid the concat and [1] here -->
+ <xsl:variable name="template.node"
+ select="key('l10n-template', concat($context, '#', $name))[1]"/>
+
+ <xsl:choose>
+ <xsl:when test="$template.node/@text">
+ <xsl:value-of select="$template.node/@text"/>
+ </xsl:when>
+ <xsl:when test="$verbose = 0">
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message>
+ <xsl:text>No template for "</xsl:text>
+ <xsl:value-of select="$origname"/>
+ <xsl:text>" (or any of its leaves) exists in the context named "</xsl:text>
+ <xsl:value-of select="$context"/>
+ <xsl:text>" in the "</xsl:text>
+ <xsl:text>" en localization.</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="gentext.template">
+ <xsl:param name="context" select="'default'"/>
+ <xsl:param name="name" select="'default'"/>
+ <xsl:param name="origname" select="$name"/>
+ <xsl:param name="lang" select="'en'"/>
+ <xsl:param name="verbose" select="1"/>
+
+ <!-- FIXME: unnecessary recursion for leading -->
+ <xsl:for-each select="$pg.l10n.xml">
+ <xsl:variable name="context.node"
+ select="key('l10n-context', $context)[1]"/>
+
+ <xsl:if test="count($context.node) = 0
+ and $verbose != 0">
+ <xsl:message>
+ <xsl:text>No context named "</xsl:text>
+ <xsl:value-of select="$context"/>
+ <xsl:text>" exists in the "</xsl:text>
+ <xsl:value-of select="$lang"/>
+ <xsl:text>" localization.</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:for-each select="$context.node">
+ <xsl:call-template name="gentext.template.recurse">
+ <xsl:with-param name="context" select="$context"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="origname" select="$origname"/>
+ <xsl:with-param name="verbose" select="$verbose"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:for-each>
+</xsl:template>
</xsl:stylesheet>
--
2.38.0
[text/x-diff] v2-0008-VERY-WIP-parallel-doc-generation.patch (5.3K, ../../[email protected]/9-v2-0008-VERY-WIP-parallel-doc-generation.patch)
download | inline diff:
From df0548082b821e90b783131f37053326e28cfd06 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Sun, 19 Mar 2023 19:29:41 -0700
Subject: [PATCH v2 8/8] VERY WIP: parallel doc generation
---
doc/src/sgml/meson.build | 17 +++++++++++
doc/src/sgml/postgres.sgml | 2 +-
doc/src/sgml/stylesheet.xsl | 51 ++++++++++++++++++++++++++++++-
doc/src/sgml/xmltools_dep_wrapper | 5 +++
4 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index bbe285b29bb..1adae3f7ee5 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -121,6 +121,23 @@ if xsltproc_bin.found()
)
alldocs += html
+ parts = ['bookinfo', 'preface', 'tutorial', 'sql', 'admin', 'client-interfaces', 'server-programming', 'reference', 'internals', 'appendixes', 'biblio', 'bookindex']
+ html_parts = []
+ foreach part : parts
+ html_parts += custom_target('html-@0@'.format(part),
+ input: ['stylesheet.xsl', postgres_full_xml],
+ output: 'html-stamp-@0@'.format(part),
+ depfile: 'html.d',
+ depends: doc_generated,
+ command: [
+ xmltools_wrapper, '--tool', xsltproc_bin, '--stamp', '@OUTPUT0@', '--',
+ '-o', '@OUTDIR@/', xsltproc_flags, '--stringparam', 'rootid', part, '@INPUT@',
+ ],
+ build_by_default: false,
+ )
+ endforeach
+ alias_target('html-parts', html_parts)
+
install_doc_html = custom_target('install-html',
output: 'install-html', input: html,
command: [
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 2e271862fc1..a169fd6f30a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -23,7 +23,7 @@ break is not needed in a wider output rendering.
<book id="postgres">
<title>PostgreSQL &version; Documentation</title>
- <bookinfo>
+ <bookinfo id="bookinfo">
<corpauthor>The PostgreSQL Global Development Group</corpauthor>
<productname>PostgreSQL</productname>
<productnumber>&version;</productnumber>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index c551b9cd809..32bceba2d54 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -15,7 +15,7 @@
<xsl:param name="use.id.as.filename" select="'1'"></xsl:param>
<xsl:param name="generate.legalnotice.link" select="1"></xsl:param>
<xsl:param name="chunk.first.sections" select="1"/>
-<xsl:param name="chunk.quietly" select="1"></xsl:param>
+<xsl:param name="chunk.quietly" select="0"></xsl:param>
<xsl:param name="admon.style"></xsl:param> <!-- handled by CSS stylesheet -->
<xsl:param name="html.stylesheet">
@@ -36,6 +36,55 @@
<xsl:value-of select="substring-after(., '/')"/>
</xsl:template>
+<!-- Emit index.html and legalnotice.html -->
+<xsl:template name="pg.write.bookinfo">
+ <xsl:for-each select="/book">
+ <xsl:call-template name="process-chunk">
+ <xsl:with-param name="prev" select="/dontexist"/>
+ <xsl:with-param name="next" select="preface"/>
+ <xsl:with-param name="content">
+
+ <div>
+ <xsl:apply-templates select="." mode="common.html.attributes"/>
+ <xsl:call-template name="id.attribute">
+ <xsl:with-param name="conditional" select="0"/>
+ </xsl:call-template>
+
+ <xsl:call-template name="book.titlepage"/>
+
+ <xsl:apply-templates select="dedication" mode="dedication"/>
+ <xsl:apply-templates select="acknowledgements" mode="acknowledgements"/>
+
+ <xsl:variable name="toc.params">
+ <xsl:call-template name="find.path.params">
+ <xsl:with-param name="table" select="normalize-space($generate.toc)"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:call-template name="make.lots">
+ <xsl:with-param name="toc.params" select="$toc.params"/>
+ <xsl:with-param name="toc">
+ <xsl:call-template name="division.toc">
+ <xsl:with-param name="toc.title.p" select="contains($toc.params, 'title')"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </div>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="/book/bookinfo" mode="process.root" priority="2">
+ <xsl:choose>
+ <xsl:when test="$rootid = 'bookinfo'">
+ <xsl:call-template name="pg.write.bookinfo"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-imports/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
<!--
Customization of header
diff --git a/doc/src/sgml/xmltools_dep_wrapper b/doc/src/sgml/xmltools_dep_wrapper
index dd96f784268..a5f5981dcee 100644
--- a/doc/src/sgml/xmltools_dep_wrapper
+++ b/doc/src/sgml/xmltools_dep_wrapper
@@ -14,6 +14,7 @@ parser = argparse.ArgumentParser(
parser.add_argument('--targetname', type=str, required=False, nargs='+')
parser.add_argument('--depfile', type=str, required=False)
parser.add_argument('--tool', type=str, required=True)
+parser.add_argument('--stamp', type=str, required=False, default=None)
parser.add_argument('flags', nargs='*')
args = parser.parse_args()
@@ -51,4 +52,8 @@ else:
command = [args.tool] + args.flags
res = subprocess.run(command)
+if args.stamp is not None:
+ with open(args.stamp, "w") as f:
+ pass
+
exit(res.returncode)
--
2.38.0
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
@ 2023-03-20 10:58 ` Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
1 sibling, 1 reply; 33+ messages in thread
From: Peter Eisentraut @ 2023-03-20 10:58 UTC (permalink / raw)
To: Andres Freund <[email protected]>; +Cc: pgsql-hackers
On 20.03.23 03:33, Andres Freund wrote:
>> I did end up getting stuck when hacking on this, and ended up adding css
>> support for nochunk and support for the website style for htmlhelp and
>> nochunk, as well as obsoleting the need for copying the css files... But
>> perhaps that's a bit too much.
> Updated set of patches attached. This one works in older meson versions too
> and adds install-world and install-quiet targets.
Oh, this patch set grew quite quickly. ;-)
[PATCH v2 1/8] meson: rename html_help target to htmlhelp
This is obvious.
[PATCH v2 5/8] docs: html: copy images to output as part of xslt build
Making the XSLT stylesheets do the copying has some appeal. I think it
would only work for SVG (or other XML) files, which I guess is okay, but
maybe the templates should have a filter on format="SVG" or something.
Also, this copying actually modifies the files in some XML-equivalent
way. Also okay, I think, but worth noting.
Note sure why you removed this comment
-<!-- strip directory name from image filerefs -->
since the code still exists.
[PATCH v2 6/8] wip: docs: copy or inline css
This seems pretty complicated compared to just copying a file?
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
@ 2023-03-20 17:32 ` Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
0 siblings, 1 reply; 33+ messages in thread
From: Andres Freund @ 2023-03-20 17:32 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-20 11:58:08 +0100, Peter Eisentraut wrote:
> Oh, this patch set grew quite quickly. ;-)
Yep :)
> [PATCH v2 5/8] docs: html: copy images to output as part of xslt build
>
> Making the XSLT stylesheets do the copying has some appeal. I think it
> would only work for SVG (or other XML) files, which I guess is okay, but
> maybe the templates should have a filter on format="SVG" or something. Also,
> this copying actually modifies the files in some XML-equivalent way. Also
> okay, I think, but worth noting.
I think it can be made work for non-xml files with xinclude too. But the
restriction around only working in top-level stylesheets (vs everywhere for
documents) is quite annoying.
> [PATCH v2 6/8] wip: docs: copy or inline css
>
> This seems pretty complicated compared to just copying a file?
Mainly that it works correctly for the standalone file.
Greetings,
Andres Freund
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
@ 2023-03-22 18:59 ` Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
0 siblings, 1 reply; 33+ messages in thread
From: Andres Freund @ 2023-03-22 18:59 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-20 10:32:49 -0700, Andres Freund wrote:
> On 2023-03-20 11:58:08 +0100, Peter Eisentraut wrote:
> > Oh, this patch set grew quite quickly. ;-)
>
> Yep :)
Unless somebody sees a reason to wait, I am planning to commit:
meson: add install-{quiet, world} targets
meson: add install-{docs,doc-html,doc-man} targets
meson: make install_test_files more generic, rename to install_files
While I don't think we have necessarily the path forward around .css and .svg,
the above are independent of that.
For the .svg: I wonder if we should just inline the images in the chunked
html, just like we do in the single page one. It's not like we reuse one image
across a lot of pages, so there's no bandwidth saved from having the images
separate...
For the .css: docbook-xsl actually has support for writing the .css: [1] - but
it requires the .css file be valid xml. I wonder if the cleanest approch would
be to have a build step to create .css.xml - then the non-chunked build's
generate.css.header would do the right thing.
I'll start a new thread for
docs: speed up docs build by special-casing the gentext.template
VERY WIP: parallel doc generation
after the feature freeze.
After looking into it a tiny bit more, it seems we should use neither pandoc
nor dbtoepub for epub generation.
All the dbtoepub does is to invoke the docbook-xsl support for epubs and zip
the result - except it doesn't use our stylesheets, so it looks randomly
different and doesn't use our speedups. At the very least we should use our
customizations, if we want epub support. Or we should just remove it.
Pandoc unfortunately doesn't do docbook well enough to be usable for now to
directly parse our docbook.
Regards,
Andres
[1] https://docbook.sourceforge.net/release/xsl/current/doc/html/custom.css.source.html
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
@ 2023-03-24 07:26 ` Andres Freund <[email protected]>
2023-03-24 10:59 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 33+ messages in thread
From: Andres Freund @ 2023-03-24 07:26 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-22 11:59:17 -0700, Andres Freund wrote:
> Unless somebody sees a reason to wait, I am planning to commit:
> meson: add install-{quiet, world} targets
> meson: add install-{docs,doc-html,doc-man} targets
> meson: make install_test_files more generic, rename to install_files
I've done that now.
> For the .css: docbook-xsl actually has support for writing the .css: [1] - but
> it requires the .css file be valid xml. I wonder if the cleanest approch would
> be to have a build step to create .css.xml - then the non-chunked build's
> generate.css.header would do the right thing.
We don't even need to do that! The attached patch just creates a wrapper
css.xml that loads the .css via an entity reference.
I think this looks reasonably complicated, given that it gives us a working
stylesheet for the non-chunked output?
I don't know if my hack of putting the paramters in stylesheet-common.xsl is
reasonable. Perhaps we should just include stylesheet-html-common.xsl in
stylesheet-hh.xsl, then this uglyness wouldn't be required.
Greetings,
Andres Freund
Attachments:
[text/x-diff] v3-0001-docs-html-copy-images-to-output-as-part-of-xslt-b.patch (3.3K, ../../[email protected]/2-v3-0001-docs-html-copy-images-to-output-as-part-of-xslt-b.patch)
download | inline diff:
From e7145805c25c0f1af983268882107f1557b67d44 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Wed, 15 Mar 2023 16:45:14 -0700
Subject: [PATCH v3 1/4] docs: html: copy images to output as part of xslt
build
---
doc/src/sgml/Makefile | 2 --
doc/src/sgml/stylesheet-common.xsl | 22 ++++++++++++++++++++++
doc/src/sgml/stylesheet-hh.xsl | 6 ++++++
doc/src/sgml/stylesheet.xsl | 7 ++++++-
4 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index b96c7cbf223..1b098f983ec 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -144,7 +144,6 @@ html: html-stamp
html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
- cp $(ALL_IMAGES) html/
cp $(srcdir)/stylesheet.css html/
touch $@
@@ -152,7 +151,6 @@ htmlhelp: htmlhelp-stamp
htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
- cp $(ALL_IMAGES) htmlhelp/
cp $(srcdir)/stylesheet.css htmlhelp/
touch $@
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index 761484c7fef..d2928f86eb7 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -101,4 +101,26 @@
<xsl:apply-templates select="." mode="xref"/>
</xsl:template>
+
+<!--
+ Support for copying images to the output directory, so the output is self
+ contained.
+-->
+<xsl:template name="write-image">
+ <xsl:variable name="input_filename">
+ <xsl:value-of select="imagedata/@fileref"/>
+ </xsl:variable>
+
+ <!-- references images directly, without images/ -->
+ <xsl:variable name="output_filename">
+ <xsl:value-of select="concat($chunk.base.dir, substring-after($input_filename, '/'))"/>
+ </xsl:variable>
+
+ <xsl:call-template name="write.chunk">
+ <xsl:with-param name="filename" select="$output_filename"/>
+ <xsl:with-param name="content" select="document($input_filename)"/>
+ </xsl:call-template>
+</xsl:template>
+
+
</xsl:stylesheet>
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 6f4b706dac6..568ccf36d2a 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -39,6 +39,12 @@
</xsl:template>
+<xsl:template match="imageobject">
+ <xsl:call-template name="write-image"/>
+ <!-- copy images to the output directory, so the output is self contained -->
+ <xsl:apply-templates select="imagedata"/>
+</xsl:template>
+
<!-- strip directory name from image filerefs -->
<xsl:template match="imagedata/@fileref">
<xsl:value-of select="substring-after(., '/')"/>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index b6141303abd..f9163e7d946 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -29,7 +29,12 @@
</xsl:param>
-<!-- strip directory name from image filerefs -->
+<xsl:template match="imageobject">
+ <xsl:call-template name="write-image"/>
+ <!-- copy images to the output directory, so the output is self contained -->
+ <xsl:apply-templates select="imagedata"/>
+</xsl:template>
+
<xsl:template match="imagedata/@fileref">
<xsl:value-of select="substring-after(., '/')"/>
</xsl:template>
--
2.38.0
[text/x-diff] v3-0002-docs-load-stylesheet-via-custom.css.source.patch (6.8K, ../../[email protected]/3-v3-0002-docs-load-stylesheet-via-custom.css.source.patch)
download | inline diff:
From fdd771319069322383e144eba68f07f2c305abc1 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Fri, 24 Mar 2023 00:13:53 -0700
Subject: [PATCH v3 2/4] docs: load stylesheet via custom.css.source
---
doc/src/sgml/Makefile | 4 +---
doc/src/sgml/meson.build | 12 ++++++------
doc/src/sgml/stylesheet-common.xsl | 14 ++++++++++++++
doc/src/sgml/stylesheet-hh.xsl | 1 -
doc/src/sgml/stylesheet-html-nochunk.xsl | 3 +++
doc/src/sgml/stylesheet.css.xml | 8 ++++++++
doc/src/sgml/stylesheet.xsl | 11 -----------
7 files changed, 32 insertions(+), 21 deletions(-)
create mode 100644 doc/src/sgml/stylesheet.css.xml
diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index 1b098f983ec..9be480613b4 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -39,7 +39,7 @@ ifndef FOP
FOP = $(missing) fop
endif
-XMLINCLUDE = --path .
+XMLINCLUDE = --path . --path $(srcdir)
ifdef XMLLINT
XMLLINT := $(XMLLINT) --nonet
@@ -144,14 +144,12 @@ html: html-stamp
html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
- cp $(srcdir)/stylesheet.css html/
touch $@
htmlhelp: htmlhelp-stamp
htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
- cp $(srcdir)/stylesheet.css htmlhelp/
touch $@
# single-page HTML
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index 157b8589e55..d9c46224dc9 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -104,7 +104,7 @@ if xsltproc_bin.found()
xsltproc_flags = [
'--nonet',
'--stringparam', 'pg.version', pg_version,
- '--param', 'website.stylesheet', '1'
+ '--path', '@CURRENT_SOURCE_DIR@', '--path', '@OUTDIR@',
]
xsltproc = xmltools_wrapper + [
@@ -140,7 +140,7 @@ if xsltproc_bin.found()
output: 'htmlhelp',
depfile: 'htmlhelp.d',
depends: doc_generated,
- command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
+ command: [xsltproc, '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
build_by_default: false,
)
alldocs += htmlhelp
@@ -152,7 +152,7 @@ if xsltproc_bin.found()
output: 'postgres.html',
depfile: 'postgres.html.d',
depends: doc_generated,
- command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
+ command: [xsltproc, '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
build_by_default: false,
)
alldocs += postgres_html
@@ -180,14 +180,14 @@ if xsltproc_bin.found()
output: 'INSTALL.xml',
depfile: 'INSTALL.xml.d',
depends: doc_generated + [postgres_full_xml],
- command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTPUT@', xsltproc_flags, '--xinclude', '@INPUT@'],
+ command: [xsltproc, '-o', '@OUTPUT@', xsltproc_flags, '--xinclude', '@INPUT@'],
build_by_default: false,
)
install_html = custom_target('INSTALL.html',
input: ['stylesheet-text.xsl', install_xml],
output: 'INSTALL.html',
depfile: 'INSTALL.html.d',
- command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
+ command: [xsltproc, '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
build_by_default: false,
)
alldocs += install_html
@@ -216,7 +216,7 @@ if xsltproc_bin.found()
output: ['man1', 'man3', 'man7'],
depfile: 'man.d',
depends: doc_generated,
- command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
+ command: [xsltproc, '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
build_by_default: false,
)
alldocs += man
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index d2928f86eb7..c1a7d46bcc6 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -42,6 +42,20 @@
<xsl:param name="variablelist.term.separator"></xsl:param>
<xsl:param name="xref.with.number.and.title" select="0"></xsl:param>
+<!--
+ currently htmlhelp and "normal" html have no common stylesheet, so this is
+ implemented here
+-->
+<xsl:param name="website.stylesheet" select="0"/>
+
+<xsl:param name="custom.css.source">
+ <xsl:if test="$website.stylesheet = 0">stylesheet.css.xml</xsl:if>
+</xsl:param>
+
+<xsl:param name="html.stylesheet">
+ <xsl:if test="not($website.stylesheet = 0)">https://www.postgresql.org/media/css/docs-complete.css.xml</xsl:if>
+</xsl:param>
+
<!-- Change display of some elements -->
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 568ccf36d2a..fb659d2ec38 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -9,7 +9,6 @@
<xsl:param name="htmlhelp.use.hhk" select="'1'"/>
<xsl:param name="base.dir" select="'htmlhelp/'"></xsl:param>
-<xsl:param name="html.stylesheet" select="'stylesheet.css'"></xsl:param>
<xsl:param name="use.id.as.filename" select="'1'"></xsl:param>
<xsl:param name="manifest.in.base.dir" select="1"/>
<xsl:param name="make.valid.html" select="1"></xsl:param>
diff --git a/doc/src/sgml/stylesheet-html-nochunk.xsl b/doc/src/sgml/stylesheet-html-nochunk.xsl
index 8167127b93a..fae8d5fbd96 100644
--- a/doc/src/sgml/stylesheet-html-nochunk.xsl
+++ b/doc/src/sgml/stylesheet-html-nochunk.xsl
@@ -7,6 +7,9 @@
<xsl:include href="stylesheet-html-common.xsl" />
<xsl:include href="stylesheet-speedup-xhtml.xsl" />
+<!-- embed stylesheet.css if using that -->
+<xsl:param name="generate.css.header" select="$website.stylesheet = 0"/>
+
<!-- embed SVG images into output file -->
<xsl:template match="imagedata[@format='SVG']">
<xsl:variable name="filename">
diff --git a/doc/src/sgml/stylesheet.css.xml b/doc/src/sgml/stylesheet.css.xml
new file mode 100644
index 00000000000..a21fcca576f
--- /dev/null
+++ b/doc/src/sgml/stylesheet.css.xml
@@ -0,0 +1,8 @@
+<!--
+ wrapper around stylesheet.css.xml to allow it to be loaded via docbook-xsl's
+ generate.css.header parameter.
+-->
+<!DOCTYPE style [
+<!ENTITY css SYSTEM "stylesheet.css">
+]>
+<style>&css;</style>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index f9163e7d946..65606ca3dbe 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -17,17 +17,6 @@
<xsl:param name="chunk.quietly" select="1"></xsl:param>
<xsl:param name="admon.style"></xsl:param> <!-- handled by CSS stylesheet -->
-<xsl:param name="website.stylesheet" select="0"/>
-
-<xsl:param name="html.stylesheet">
- <xsl:choose>
- <xsl:when test="$website.stylesheet = 0">stylesheet.css</xsl:when>
- <xsl:otherwise>
- https://www.postgresql.org/media/css/docs-complete.css
- </xsl:otherwise>
- </xsl:choose>
-</xsl:param>
-
<xsl:template match="imageobject">
<xsl:call-template name="write-image"/>
--
2.38.0
[text/x-diff] v3-0003-docs-speed-up-docs-build-by-special-casing-the-ge.patch (3.6K, ../../[email protected]/4-v3-0003-docs-speed-up-docs-build-by-special-casing-the-ge.patch)
download | inline diff:
From 4a220cc1bf79e19e9cb5e94d8a650123a0347bb4 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Sun, 19 Mar 2023 11:11:37 -0700
Subject: [PATCH v3 3/4] docs: speed up docs build by special-casing the
gentext.template
---
doc/src/sgml/stylesheet-speedup-common.xsl | 79 ++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/doc/src/sgml/stylesheet-speedup-common.xsl b/doc/src/sgml/stylesheet-speedup-common.xsl
index e3fb582a1cc..5266ff587b9 100644
--- a/doc/src/sgml/stylesheet-speedup-common.xsl
+++ b/doc/src/sgml/stylesheet-speedup-common.xsl
@@ -96,5 +96,84 @@
<!-- Just hardcode the language for the whole document, to make it faster. -->
<xsl:template name="l10n.language">en</xsl:template>
+<xsl:param name='pg.l10n.xml' select="document('file:///usr/share/xml/docbook/stylesheet/docbook-xsl/common/en.xml')[1]"/>
+
+<xsl:template name="gentext.template.recurse">
+ <xsl:param name="context"/>
+ <xsl:param name="name"/>
+ <xsl:param name="origname"/>
+ <xsl:param name="verbose"/>
+
+
+ <xsl:choose>
+ <xsl:when test="contains($name, '/')">
+ <xsl:call-template name="gentext.template.recurse">
+ <xsl:with-param name="context" select="$context"/>
+ <xsl:with-param name="name" select="substring-after($name, '/')"/>
+ <xsl:with-param name="origname" select="$origname"/>
+ <xsl:with-param name="verbose" select="$verbose"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+
+ <!-- FIXME: should find a way to avoid the concat and [1] here -->
+ <xsl:variable name="template.node"
+ select="key('l10n-template', concat($context, '#', $name))[1]"/>
+
+ <xsl:choose>
+ <xsl:when test="$template.node/@text">
+ <xsl:value-of select="$template.node/@text"/>
+ </xsl:when>
+ <xsl:when test="$verbose = 0">
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message>
+ <xsl:text>No template for "</xsl:text>
+ <xsl:value-of select="$origname"/>
+ <xsl:text>" (or any of its leaves) exists in the context named "</xsl:text>
+ <xsl:value-of select="$context"/>
+ <xsl:text>" in the "</xsl:text>
+ <xsl:text>" en localization.</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="gentext.template">
+ <xsl:param name="context" select="'default'"/>
+ <xsl:param name="name" select="'default'"/>
+ <xsl:param name="origname" select="$name"/>
+ <xsl:param name="lang" select="'en'"/>
+ <xsl:param name="verbose" select="1"/>
+
+ <!-- FIXME: unnecessary recursion for leading -->
+ <xsl:for-each select="$pg.l10n.xml">
+ <xsl:variable name="context.node"
+ select="key('l10n-context', $context)[1]"/>
+
+ <xsl:if test="count($context.node) = 0
+ and $verbose != 0">
+ <xsl:message>
+ <xsl:text>No context named "</xsl:text>
+ <xsl:value-of select="$context"/>
+ <xsl:text>" exists in the "</xsl:text>
+ <xsl:value-of select="$lang"/>
+ <xsl:text>" localization.</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:for-each select="$context.node">
+ <xsl:call-template name="gentext.template.recurse">
+ <xsl:with-param name="context" select="$context"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="origname" select="$origname"/>
+ <xsl:with-param name="verbose" select="$verbose"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:for-each>
+</xsl:template>
</xsl:stylesheet>
--
2.38.0
[text/x-diff] v3-0004-VERY-WIP-parallel-doc-generation.patch (4.9K, ../../[email protected]/5-v3-0004-VERY-WIP-parallel-doc-generation.patch)
download | inline diff:
From 37c880b04e13efa36b67832c647255ee80e726bd Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Sun, 19 Mar 2023 19:29:41 -0700
Subject: [PATCH v3 4/4] VERY WIP: parallel doc generation
---
doc/src/sgml/meson.build | 17 +++++++++++
doc/src/sgml/postgres.sgml | 2 +-
doc/src/sgml/stylesheet.xsl | 49 +++++++++++++++++++++++++++++++
doc/src/sgml/xmltools_dep_wrapper | 5 ++++
4 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index d9c46224dc9..c6195965a71 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -121,6 +121,23 @@ if xsltproc_bin.found()
)
alldocs += html
+ parts = ['bookinfo', 'preface', 'tutorial', 'sql', 'admin', 'client-interfaces', 'server-programming', 'reference', 'internals', 'appendixes', 'biblio', 'bookindex']
+ html_parts = []
+ foreach part : parts
+ html_parts += custom_target('html-@0@'.format(part),
+ input: ['stylesheet.xsl', postgres_full_xml],
+ output: 'html-stamp-@0@'.format(part),
+ depfile: 'html.d',
+ depends: doc_generated,
+ command: [
+ xmltools_wrapper, '--tool', xsltproc_bin, '--stamp', '@OUTPUT0@', '--',
+ '-o', '@OUTDIR@/', xsltproc_flags, '--stringparam', 'rootid', part, '@INPUT@',
+ ],
+ build_by_default: false,
+ )
+ endforeach
+ alias_target('html-parts', html_parts)
+
install_doc_html = custom_target('install-html',
output: 'install-html',
command: [
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 2e271862fc1..a169fd6f30a 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -23,7 +23,7 @@ break is not needed in a wider output rendering.
<book id="postgres">
<title>PostgreSQL &version; Documentation</title>
- <bookinfo>
+ <bookinfo id="bookinfo">
<corpauthor>The PostgreSQL Global Development Group</corpauthor>
<productname>PostgreSQL</productname>
<productnumber>&version;</productnumber>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index 65606ca3dbe..f9986d17f4a 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -28,6 +28,55 @@
<xsl:value-of select="substring-after(., '/')"/>
</xsl:template>
+<!-- Emit index.html and legalnotice.html -->
+<xsl:template name="pg.write.bookinfo">
+ <xsl:for-each select="/book">
+ <xsl:call-template name="process-chunk">
+ <xsl:with-param name="prev" select="/dontexist"/>
+ <xsl:with-param name="next" select="preface"/>
+ <xsl:with-param name="content">
+
+ <div>
+ <xsl:apply-templates select="." mode="common.html.attributes"/>
+ <xsl:call-template name="id.attribute">
+ <xsl:with-param name="conditional" select="0"/>
+ </xsl:call-template>
+
+ <xsl:call-template name="book.titlepage"/>
+
+ <xsl:apply-templates select="dedication" mode="dedication"/>
+ <xsl:apply-templates select="acknowledgements" mode="acknowledgements"/>
+
+ <xsl:variable name="toc.params">
+ <xsl:call-template name="find.path.params">
+ <xsl:with-param name="table" select="normalize-space($generate.toc)"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:call-template name="make.lots">
+ <xsl:with-param name="toc.params" select="$toc.params"/>
+ <xsl:with-param name="toc">
+ <xsl:call-template name="division.toc">
+ <xsl:with-param name="toc.title.p" select="contains($toc.params, 'title')"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </div>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="/book/bookinfo" mode="process.root" priority="2">
+ <xsl:choose>
+ <xsl:when test="$rootid = 'bookinfo'">
+ <xsl:call-template name="pg.write.bookinfo"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-imports/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
<!--
Customization of header
diff --git a/doc/src/sgml/xmltools_dep_wrapper b/doc/src/sgml/xmltools_dep_wrapper
index dd96f784268..a5f5981dcee 100644
--- a/doc/src/sgml/xmltools_dep_wrapper
+++ b/doc/src/sgml/xmltools_dep_wrapper
@@ -14,6 +14,7 @@ parser = argparse.ArgumentParser(
parser.add_argument('--targetname', type=str, required=False, nargs='+')
parser.add_argument('--depfile', type=str, required=False)
parser.add_argument('--tool', type=str, required=True)
+parser.add_argument('--stamp', type=str, required=False, default=None)
parser.add_argument('flags', nargs='*')
args = parser.parse_args()
@@ -51,4 +52,8 @@ else:
command = [args.tool] + args.flags
res = subprocess.run(command)
+if args.stamp is not None:
+ with open(args.stamp, "w") as f:
+ pass
+
exit(res.returncode)
--
2.38.0
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
@ 2023-03-24 10:59 ` Peter Eisentraut <[email protected]>
2023-03-24 16:58 ` Remove 'htmlhelp' documentat format (was meson documentation build open issues) Andres Freund <[email protected]>
0 siblings, 1 reply; 33+ messages in thread
From: Peter Eisentraut @ 2023-03-24 10:59 UTC (permalink / raw)
To: Andres Freund <[email protected]>; +Cc: pgsql-hackers
On 24.03.23 08:26, Andres Freund wrote:
>> For the .css: docbook-xsl actually has support for writing the .css: [1] - but
>> it requires the .css file be valid xml. I wonder if the cleanest approch would
>> be to have a build step to create .css.xml - then the non-chunked build's
>> generate.css.header would do the right thing.
>
> We don't even need to do that! The attached patch just creates a wrapper
> css.xml that loads the .css via an entity reference.
That looks like a better solution.
> I don't know if my hack of putting the paramters in stylesheet-common.xsl is
> reasonable. Perhaps we should just include stylesheet-html-common.xsl in
> stylesheet-hh.xsl, then this uglyness wouldn't be required.
Maybe, but it's not clear whether all the customizations in there are
applicable to htmlhelp.
Another option here is to remove support for htmlhelp.
^ permalink raw reply [nested|flat] 33+ messages in thread
* Remove 'htmlhelp' documentat format (was meson documentation build open issues)
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 10:59 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
@ 2023-03-24 16:58 ` Andres Freund <[email protected]>
2023-03-24 21:00 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Daniel Gustafsson <[email protected]>
2023-03-28 09:46 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Peter Eisentraut <[email protected]>
0 siblings, 2 replies; 33+ messages in thread
From: Andres Freund @ 2023-03-24 16:58 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-24 11:59:23 +0100, Peter Eisentraut wrote:
> Another option here is to remove support for htmlhelp.
That might actually be the best path - it certainly doesn't look like anybody
has been actively using it. Or otherwise somebody would have complained about
there not being any instructions on how to actually compile a .chm file. And
perhaps complained that it takes next to forever to build.
I also have the impression that people don't use the .chm stuff much anymore,
but that might just be me not using windows.
Greetings,
Andres
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues)
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 10:59 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-24 16:58 ` Remove 'htmlhelp' documentat format (was meson documentation build open issues) Andres Freund <[email protected]>
@ 2023-03-24 21:00 ` Daniel Gustafsson <[email protected]>
1 sibling, 0 replies; 33+ messages in thread
From: Daniel Gustafsson @ 2023-03-24 21:00 UTC (permalink / raw)
To: Andres Freund <[email protected]>; +Cc: Peter Eisentraut <[email protected]>; pgsql-hackers
> On 24 Mar 2023, at 17:58, Andres Freund <[email protected]> wrote:
> On 2023-03-24 11:59:23 +0100, Peter Eisentraut wrote:
>> Another option here is to remove support for htmlhelp.
>
> That might actually be the best path - it certainly doesn't look like anybody
> has been actively using it.
I had no idea we had support for building a .chm until reading this, but I've
also never come across anyone asking for such a docset. FWIW, no objections to
it going.
--
Daniel Gustafsson
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues)
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 10:59 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-24 16:58 ` Remove 'htmlhelp' documentat format (was meson documentation build open issues) Andres Freund <[email protected]>
@ 2023-03-28 09:46 ` Peter Eisentraut <[email protected]>
2023-03-28 09:50 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Dave Page <[email protected]>
1 sibling, 1 reply; 33+ messages in thread
From: Peter Eisentraut @ 2023-03-28 09:46 UTC (permalink / raw)
To: Andres Freund <[email protected]>; +Cc: pgsql-hackers
On 24.03.23 17:58, Andres Freund wrote:
> On 2023-03-24 11:59:23 +0100, Peter Eisentraut wrote:
>> Another option here is to remove support for htmlhelp.
>
> That might actually be the best path - it certainly doesn't look like anybody
> has been actively using it. Or otherwise somebody would have complained about
> there not being any instructions on how to actually compile a .chm file. And
> perhaps complained that it takes next to forever to build.
>
> I also have the impression that people don't use the .chm stuff much anymore,
> but that might just be me not using windows.
I think in ancient times, pgadmin used it for its internal help.
But I have heard less about htmlhelp over the years than about the info
format.
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues)
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 10:59 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-24 16:58 ` Remove 'htmlhelp' documentat format (was meson documentation build open issues) Andres Freund <[email protected]>
2023-03-28 09:46 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Peter Eisentraut <[email protected]>
@ 2023-03-28 09:50 ` Dave Page <[email protected]>
0 siblings, 0 replies; 33+ messages in thread
From: Dave Page @ 2023-03-28 09:50 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Andres Freund <[email protected]>; pgsql-hackers
On Tue, 28 Mar 2023 at 10:46, Peter Eisentraut <
[email protected]> wrote:
> On 24.03.23 17:58, Andres Freund wrote:
> > On 2023-03-24 11:59:23 +0100, Peter Eisentraut wrote:
> >> Another option here is to remove support for htmlhelp.
> >
> > That might actually be the best path - it certainly doesn't look like
> anybody
> > has been actively using it. Or otherwise somebody would have complained
> about
> > there not being any instructions on how to actually compile a .chm file.
> And
> > perhaps complained that it takes next to forever to build.
> >
> > I also have the impression that people don't use the .chm stuff much
> anymore,
> > but that might just be me not using windows.
>
> I think in ancient times, pgadmin used it for its internal help.
>
Yes, very ancient :-). We use Sphinx now.
>
> But I have heard less about htmlhelp over the years than about the info
> format.
>
>
>
--
Dave Page
Blog: https://pgsnake.blogspot.com
Twitter: @pgsnake
EDB: https://www.enterprisedb.com
^ permalink raw reply [nested|flat] 33+ messages in thread
* Re: meson documentation build open issues
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
@ 2023-03-20 19:16 ` Andres Freund <[email protected]>
1 sibling, 0 replies; 33+ messages in thread
From: Andres Freund @ 2023-03-20 19:16 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers
Hi,
On 2023-03-19 19:33:38 -0700, Andres Freund wrote:
> I think we can make the docs build in parallel and incrementally, by building
> the different parts of the docs in parallel, using --stringparam rootid,
> e.g. building each 'part' separately.
>
> A very very rough draft attached:
>
> parallel with parts:
> real 0m10.831s
> user 0m58.295s
> sys 0m1.402s
>
> normal:
> real 0m32.215s
> user 0m31.876s
> sys 0m0.328s
>
> 1/3 of the build time at 2x the cost is nothing to sneeze at.
I could not make myself stop trying to figure out where the big constant time
factor comes from. Every invocation costs about 2s, even if not much is
rendered. Turns out, that's solely spent building all the <xsl:key>s. The
first time *any* key() is invoked for a document, all the keys are computed in
a single pass over the document.
A single reasonable key doesn't take that much time, even for the size of our
docs. But there are several redundant keys being built. Some of them somewhat
expensive. E.g. each
<xsl:key name="genid" match="*" use="generate-id()"/>
takes about 300ms. There's one in chunk-common and one in
docbook-no-doctype.xsl.
I'm going to cry now.
Greetings,
Andres Freund
^ permalink raw reply [nested|flat] 33+ messages in thread
end of thread, other threads:[~2023-03-28 09:50 UTC | newest]
Thread overview: 33+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2021-01-22 00:11 [PATCH 8/8] batch build Tomas Vondra <[email protected]>
2023-03-16 03:55 Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 02:33 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-20 10:58 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-20 17:32 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-22 18:59 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 07:26 ` Re: meson documentation build open issues Andres Freund <[email protected]>
2023-03-24 10:59 ` Re: meson documentation build open issues Peter Eisentraut <[email protected]>
2023-03-24 16:58 ` Remove 'htmlhelp' documentat format (was meson documentation build open issues) Andres Freund <[email protected]>
2023-03-24 21:00 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Daniel Gustafsson <[email protected]>
2023-03-28 09:46 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Peter Eisentraut <[email protected]>
2023-03-28 09:50 ` Re: Remove 'htmlhelp' documentat format (was meson documentation build open issues) Dave Page <[email protected]>
2023-03-20 19:16 ` Re: meson documentation build open issues Andres Freund <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox