diff options
author | Jon Bratseth <bratseth@gmail.com> | 2023-03-20 11:26:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-20 11:26:26 +0100 |
commit | 47e7d6a7509c5ba3a339afbaa0f17b16d9b382af (patch) | |
tree | ad511fda70c2bdaffa21311ce3fb069cf95730b3 | |
parent | 89862f601ce193c1b8aa96d9979ad5ef57c1a505 (diff) | |
parent | 24b297f12cdb8707d15ad554554eaaff76bcbdd3 (diff) |
Merge branch 'master' into arnej/handle-dynamic-tensor
17 files changed, 480 insertions, 102 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/RankProfile.java b/config-model/src/main/java/com/yahoo/schema/RankProfile.java index f9b3bc77040..639930041c3 100644 --- a/config-model/src/main/java/com/yahoo/schema/RankProfile.java +++ b/config-model/src/main/java/com/yahoo/schema/RankProfile.java @@ -683,6 +683,25 @@ public class RankProfile implements Cloneable { addRankProperty(new RankProperty(name, parameter)); } + /* + * set a rank-property that should be a single-value parameter; + * if the same name is used multiple times, that parameter must be identical each time. + */ + public void setRankProperty(String name, String parameter) { + var old = rankProperties.get(name); + if (old != null) { + if (old.size() != 1) { + throw new IllegalStateException("setRankProperty used for multi-valued property " + name); + } + var oldVal = old.get(0).getValue(); + if (! oldVal.equals(parameter)) { + throw new IllegalArgumentException("setRankProperty would change property " + name + " from " + oldVal + " to " + parameter); + } + } else { + addRankProperty(new RankProperty(name, parameter)); + } + } + private void addRankProperty(RankProperty rankProperty) { // Just the usual multimap semantics here rankProperties.computeIfAbsent(rankProperty.getName(), (String key) -> new ArrayList<>(1)).add(rankProperty); @@ -1091,7 +1110,7 @@ public class RankProfile implements Cloneable { inlineFunctions); RankingExpression expression = expressionTransforms.transform(function.function().getBody(), context); for (Map.Entry<String, String> rankProperty : context.rankProperties().entrySet()) { - addRankProperty(rankProperty.getKey(), rankProperty.getValue()); + setRankProperty(rankProperty.getKey(), rankProperty.getValue()); } return function.withExpression(expression); } diff --git a/config-model/src/main/java/com/yahoo/schema/document/Attribute.java b/config-model/src/main/java/com/yahoo/schema/document/Attribute.java index 34e86cbf4a8..70fcf64dff3 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/Attribute.java +++ b/config-model/src/main/java/com/yahoo/schema/document/Attribute.java @@ -100,7 +100,8 @@ public final class Attribute implements Cloneable, Serializable { BOOL("bool", "BOOL"), PREDICATE("predicate", "PREDICATE"), TENSOR("tensor", "TENSOR"), - REFERENCE("reference", "REFERENCE"); + REFERENCE("reference", "REFERENCE"), + RAW("raw", "RAW"); private final String myName; // different from what name() returns. private final String exportAttributeTypeName; @@ -290,7 +291,7 @@ public final class Attribute implements Cloneable, Serializable { } else if (fval instanceof ByteFieldValue) { return Type.BYTE; } else if (fval instanceof Raw) { - return Type.BYTE; + return Type.RAW; } else if (fval instanceof PredicateFieldValue) { return Type.PREDICATE; } else if (fval instanceof TensorFieldValue) { @@ -344,6 +345,7 @@ public final class Attribute implements Cloneable, Serializable { case PREDICATE -> DataType.PREDICATE; case TENSOR -> DataType.getTensor(tensorType.orElseThrow(IllegalStateException::new)); case REFERENCE-> createReferenceDataType(); + case RAW -> DataType.RAW; default -> throw new IllegalArgumentException("Unknown attribute type " + attributeType); }; } diff --git a/config-model/src/main/java/com/yahoo/schema/expressiontransforms/RankProfileTransformContext.java b/config-model/src/main/java/com/yahoo/schema/expressiontransforms/RankProfileTransformContext.java index cfc859345ad..890fa5e7a10 100644 --- a/config-model/src/main/java/com/yahoo/schema/expressiontransforms/RankProfileTransformContext.java +++ b/config-model/src/main/java/com/yahoo/schema/expressiontransforms/RankProfileTransformContext.java @@ -12,7 +12,7 @@ import com.yahoo.searchlib.rankingexpression.transform.TransformContext; import com.yahoo.tensor.Tensor; import com.yahoo.tensor.TensorType; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; @@ -27,7 +27,7 @@ public class RankProfileTransformContext extends TransformContext { private final QueryProfileRegistry queryProfiles; private final ImportedMlModels importedModels; private final Map<String, RankProfile.RankingExpressionFunction> inlineFunctions; - private final Map<String, String> rankProperties = new HashMap<>(); + private final Map<String, String> rankProperties = new LinkedHashMap<>(); public RankProfileTransformContext(RankProfile rankProfile, QueryProfileRegistry queryProfiles, diff --git a/config-model/src/main/java/com/yahoo/schema/processing/Processing.java b/config-model/src/main/java/com/yahoo/schema/processing/Processing.java index 8f7e8daeed0..df4b0d0d941 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/Processing.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/Processing.java @@ -89,7 +89,7 @@ public class Processing { OnnxModelConfigGenerator::new, OnnxModelTypeResolver::new, RankingExpressionTypeResolver::new, - BoolAttributeValidator::new, + SingleValueOnlyAttributeValidator::new, PagedAttributeValidator::new, // These should be last: IndexingValidation::new, diff --git a/config-model/src/main/java/com/yahoo/schema/processing/BoolAttributeValidator.java b/config-model/src/main/java/com/yahoo/schema/processing/SingleValueOnlyAttributeValidator.java index bdb1eed4b10..b2786e6c785 100644 --- a/config-model/src/main/java/com/yahoo/schema/processing/BoolAttributeValidator.java +++ b/config-model/src/main/java/com/yahoo/schema/processing/SingleValueOnlyAttributeValidator.java @@ -14,9 +14,9 @@ import com.yahoo.vespa.model.container.search.QueryProfiles; * * @author geirst */ -public class BoolAttributeValidator extends Processor { +public class SingleValueOnlyAttributeValidator extends Processor { - public BoolAttributeValidator(Schema schema, DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry, QueryProfiles queryProfiles) { + public SingleValueOnlyAttributeValidator(Schema schema, DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry, QueryProfiles queryProfiles) { super(schema, deployLogger, rankProfileRegistry, queryProfiles); } @@ -27,9 +27,10 @@ public class BoolAttributeValidator extends Processor { if (attribute == null) { continue; } - if (attribute.getType().equals(Attribute.Type.BOOL) && + if ((attribute.getType().equals(Attribute.Type.BOOL) || + attribute.getType().equals(Attribute.Type.RAW)) && !attribute.getCollectionType().equals(Attribute.CollectionType.SINGLE)) { - fail(schema, field, "Only single value bool attribute fields are supported"); + fail(schema, field, "Only single value " + attribute.getType().getName() + " attribute fields are supported"); } } } diff --git a/config-model/src/test/derived/rankproperties/rank-profiles.cfg b/config-model/src/test/derived/rankproperties/rank-profiles.cfg index 3ca44288282..b6f51a88197 100644 --- a/config-model/src/test/derived/rankproperties/rank-profiles.cfg +++ b/config-model/src/test/derived/rankproperties/rank-profiles.cfg @@ -50,3 +50,49 @@ rankprofile[].fef.property[].name "vespa.fieldweight.description" rankprofile[].fef.property[].value "35" rankprofile[].fef.property[].name "vespa.fieldweight.tag" rankprofile[].fef.property[].value "88" +rankprofile[].name "withconstants1" +rankprofile[].fef.property[].name "constant(a).value" +rankprofile[].fef.property[].value "tensor(x[2]):[1.0, 2.0]" +rankprofile[].fef.property[].name "constant(a).type" +rankprofile[].fef.property[].value "tensor(x[2])" +rankprofile[].fef.property[].name "constant(b).value" +rankprofile[].fef.property[].value "tensor(y[3]):[3.0, 4.0, 5.0]" +rankprofile[].fef.property[].name "constant(b).type" +rankprofile[].fef.property[].value "tensor(y[3])" +rankprofile[].fef.property[].name "rankingExpression(usea).rankingScript" +rankprofile[].fef.property[].value "constant(a) * 1.0" +rankprofile[].fef.property[].name "rankingExpression(usea).type" +rankprofile[].fef.property[].value "tensor(x[2])" +rankprofile[].fef.property[].name "rankingExpression(useb).rankingScript" +rankprofile[].fef.property[].value "constant(a) * 1.0 + constant(b)" +rankprofile[].fef.property[].name "rankingExpression(useb).type" +rankprofile[].fef.property[].value "tensor(x[2],y[3])" +rankprofile[].fef.property[].name "vespa.rank.firstphase" +rankprofile[].fef.property[].value "rankingExpression(firstphase)" +rankprofile[].fef.property[].name "rankingExpression(firstphase).rankingScript" +rankprofile[].fef.property[].value "reduce(nativeFieldMatch + constant(a) * 1.0 + constant(b), sum)" +rankprofile[].name "withconstants2" +rankprofile[].fef.property[].name "constant(a).value" +rankprofile[].fef.property[].value "tensor(x[2]):[1.0, 2.0]" +rankprofile[].fef.property[].name "constant(a).type" +rankprofile[].fef.property[].value "tensor(x[2])" +rankprofile[].fef.property[].name "constant(b).value" +rankprofile[].fef.property[].value "tensor(y[3]):[3.0, 4.0, 5.0]" +rankprofile[].fef.property[].name "constant(b).type" +rankprofile[].fef.property[].value "tensor(y[3])" +rankprofile[].fef.property[].name "rankingExpression(usea).rankingScript" +rankprofile[].fef.property[].value "constant(a) * 1.0" +rankprofile[].fef.property[].name "rankingExpression(usea).type" +rankprofile[].fef.property[].value "tensor(x[2])" +rankprofile[].fef.property[].name "rankingExpression(useb).rankingScript" +rankprofile[].fef.property[].value "constant(a) * 1.0 + constant(b)" +rankprofile[].fef.property[].name "rankingExpression(useb).type" +rankprofile[].fef.property[].value "tensor(x[2],y[3])" +rankprofile[].fef.property[].name "rankingExpression(usec).rankingScript" +rankprofile[].fef.property[].value "constant(a) + constant(b)" +rankprofile[].fef.property[].name "rankingExpression(usec).type" +rankprofile[].fef.property[].value "tensor(x[2],y[3])" +rankprofile[].fef.property[].name "vespa.rank.firstphase" +rankprofile[].fef.property[].value "rankingExpression(firstphase)" +rankprofile[].fef.property[].name "rankingExpression(firstphase).rankingScript" +rankprofile[].fef.property[].value "reduce(nativeFieldMatch + constant(a) + constant(b), sum)" diff --git a/config-model/src/test/derived/rankproperties/rankproperties.sd b/config-model/src/test/derived/rankproperties/rankproperties.sd index a2eb987d4ff..a13b3081ca7 100644 --- a/config-model/src/test/derived/rankproperties/rankproperties.sd +++ b/config-model/src/test/derived/rankproperties/rankproperties.sd @@ -59,4 +59,28 @@ schema rankproperties { } } + rank-profile withconstants1 { + constants { + constant(a) tensor(x[2]) : [1,2] + constant(b) tensor(y[3]) : [3,4,5] + } + function inline usea() { + expression: constant(a)*1.0 + } + function inline useb() { + expression: usea + constant(b) + } + first-phase { + expression: sum(nativeFieldMatch + useb) + } + } + + rank-profile withconstants2 inherits withconstants1 { + function inline usec() { + expression: constant(a)+constant(b) + } + first-phase { + expression: sum(nativeFieldMatch + usec) + } + } } diff --git a/config-model/src/test/java/com/yahoo/schema/processing/BoolAttributeValidatorTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/BoolAttributeValidatorTestCase.java deleted file mode 100644 index f19b1f43115..00000000000 --- a/config-model/src/test/java/com/yahoo/schema/processing/BoolAttributeValidatorTestCase.java +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.schema.processing; - -import com.yahoo.schema.parser.ParseException; -import org.junit.jupiter.api.Test; - -import static com.yahoo.schema.ApplicationBuilder.createFromString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static com.yahoo.config.model.test.TestUtil.joinLines; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * @author geirst - */ -public class BoolAttributeValidatorTestCase { - - @Test - void array_of_bool_attribute_is_not_supported() throws ParseException { - try { - createFromString(getSd("field b type array<bool> { indexing: attribute }")); - fail("Expected exception"); - } - catch (IllegalArgumentException e) { - assertEquals("For schema 'test', field 'b': Only single value bool attribute fields are supported", - e.getMessage()); - } - } - - @Test - void weigtedset_of_bool_attribute_is_not_supported() throws ParseException { - try { - createFromString(getSd("field b type weightedset<bool> { indexing: attribute }")); - fail("Expected exception"); - } - catch (IllegalArgumentException e) { - assertEquals("For schema 'test', field 'b': Only single value bool attribute fields are supported", - e.getMessage()); - } - } - - private String getSd(String field) { - return joinLines( - "schema test {", - " document test {", - " " + field, - " }", - "}"); - } - -} diff --git a/config-model/src/test/java/com/yahoo/schema/processing/SingleValueOnlyAttributeValidatorTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/SingleValueOnlyAttributeValidatorTestCase.java new file mode 100644 index 00000000000..a7f4125a537 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/schema/processing/SingleValueOnlyAttributeValidatorTestCase.java @@ -0,0 +1,73 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.schema.processing; + +import com.yahoo.schema.parser.ParseException; +import org.junit.jupiter.api.Test; + +import static com.yahoo.schema.ApplicationBuilder.createFromString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static com.yahoo.config.model.test.TestUtil.joinLines; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author geirst + */ +public class SingleValueOnlyAttributeValidatorTestCase { + + private static void array_attribute_is_not_supported(String type) throws ParseException { + try { + createFromString(getSd("field b type array<" + type + "> { indexing: attribute }")); + fail("Expected exception"); + } + catch (IllegalArgumentException e) { + assertEquals("For schema 'test', field 'b': Only single value " + type + " attribute fields are supported", + e.getMessage()); + } + } + + private static void weightedset_attribute_is_not_supported(String type) throws ParseException { + try { + createFromString(getSd("field b type weightedset<" + type + "> { indexing: attribute }")); + fail("Expected exception"); + } + catch (IllegalArgumentException e) { + if (type.equals("raw")) { + assertEquals("weightedset of complex type '[type BUILTIN] {raw}' is not supported", + e.getMessage()); + } else { + assertEquals("For schema 'test', field 'b': Only single value " + type + " attribute fields are supported", + e.getMessage()); + } + } + } + + @Test + void array_of_bool_attribute_is_not_supported() throws ParseException { + array_attribute_is_not_supported("bool"); + } + + @Test + void weightedset_of_bool_attribute_is_not_supported() throws ParseException { + weightedset_attribute_is_not_supported("bool"); + } + + @Test + void array_of_raw_attribute_is_not_supported() throws ParseException { + array_attribute_is_not_supported("raw"); + } + + @Test + void weightedset_of_raw_attribute_is_not_supported() throws ParseException { + weightedset_attribute_is_not_supported("raw"); + } + + private static String getSd(String field) { + return joinLines( + "schema test {", + " document test {", + " " + field, + " }", + "}"); + } + +} diff --git a/config-model/src/test/java/com/yahoo/schema/processing/VespaMlModelTestCase.java b/config-model/src/test/java/com/yahoo/schema/processing/VespaMlModelTestCase.java index 01e80e0f47a..664cfaf4ad7 100644 --- a/config-model/src/test/java/com/yahoo/schema/processing/VespaMlModelTestCase.java +++ b/config-model/src/test/java/com/yahoo/schema/processing/VespaMlModelTestCase.java @@ -25,8 +25,8 @@ public class VespaMlModelTestCase { private final Path applicationDir = Path.fromString("src/test/integration/vespa/"); private final String expectedRankConfig = - "constant(constant1).type : tensor(x[3])\n" + "constant(constant1).value : tensor(x[3]):[0.5, 1.5, 2.5]\n" + + "constant(constant1).type : tensor(x[3])\n" + "rankingExpression(foo1).rankingScript : reduce(reduce(input1 * input2, sum, name) * constant(constant1), max, x) * 3.0\n" + "rankingExpression(foo1).input1.type : tensor(name{},x[3])\n" + "rankingExpression(foo1).input2.type : tensor(x[3])\n" + diff --git a/dist/vespa.spec b/dist/vespa.spec index fe493dc883f..091a17e822b 100644 --- a/dist/vespa.spec +++ b/dist/vespa.spec @@ -116,7 +116,7 @@ BuildRequires: gmock-devel %if 0%{?fedora} BuildRequires: cmake >= 3.9.1 BuildRequires: maven -%if 0%{?amzn2022} +%if 0%{?amzn2023} BuildRequires: maven-amazon-corretto17 %define _java_home /usr/lib/jvm/java-17-amazon-corretto %else @@ -134,7 +134,7 @@ BuildRequires: boost-devel BuildRequires: gtest-devel BuildRequires: gmock-devel %endif -%if 0%{?amzn2022} +%if 0%{?amzn2023} BuildRequires: vespa-xxhash-devel >= 0.8.1 %define _use_vespa_xxhash 1 %else @@ -146,7 +146,7 @@ BuildRequires: vespa-openblas-devel = 0.3.21 %else BuildRequires: openblas-devel %endif -%if 0%{?amzn2022} +%if 0%{?amzn2023} BuildRequires: vespa-re2-devel = 20210801 %define _use_vespa_re2 1 %else @@ -154,7 +154,7 @@ BuildRequires: re2-devel %endif BuildRequires: zlib-devel BuildRequires: libicu-devel -%if 0%{?amzn2022} +%if 0%{?amzn2023} BuildRequires: java-17-amazon-corretto-devel BuildRequires: java-17-amazon-corretto %else @@ -178,7 +178,7 @@ Requires: numactl BuildRequires: perl BuildRequires: valgrind BuildRequires: perf -%if 0%{?amzn2022} +%if 0%{?amzn2023} Requires: vespa-xxhash >= 0.8.1 %else Requires: xxhash-libs >= 0.8.1 @@ -223,7 +223,7 @@ Vespa - The open big data serving engine Summary: Vespa - The open big data serving engine - base -%if 0%{?amzn2022} +%if 0%{?amzn2023} Requires: java-17-amazon-corretto-devel Requires: java-17-amazon-corretto %else @@ -244,7 +244,7 @@ Summary: Vespa - The open big data serving engine - base C++ libraries %if 0%{?centos} || 0%{?rocky} || 0%{?oraclelinux} Requires: epel-release %endif -%if 0%{?amzn2022} +%if 0%{?amzn2023} Requires: vespa-xxhash >= 0.8.1 %else Requires: xxhash-libs >= 0.8.1 @@ -261,7 +261,7 @@ Requires: vespa-openblas = 0.3.21 %else Requires: openblas-serial %endif -%if 0%{?amzn2022} +%if 0%{?amzn2023} Requires: vespa-re2 = 20210801 %else Requires: re2 diff --git a/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp b/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp index 8464e0abfec..1080d44f2fb 100644 --- a/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp +++ b/searchlib/src/tests/docstore/logdatastore/logdatastore_test.cpp @@ -21,6 +21,7 @@ #include <vespa/vespalib/util/memory.h> #include <filesystem> #include <iomanip> +#include <random> using document::BucketId; using document::StringFieldValue; @@ -92,8 +93,7 @@ calcLastFlushedSerialNum(const std::vector<DataStoreFileChunkStats> &chunkStats) { SerialNum lastFlushedSerialNum = 0u; for (const auto &chunk : chunkStats) { - lastFlushedSerialNum = std::max(lastFlushedSerialNum, - chunk.lastFlushedSerialNum()); + lastFlushedSerialNum = std::max(lastFlushedSerialNum, chunk.lastFlushedSerialNum()); } return lastFlushedSerialNum; } @@ -130,10 +130,8 @@ checkStats(IDataStore &store, EXPECT_EQUAL(expLastSerial, storageStats.lastSerialNum()); EXPECT_EQUAL(expLastFlushedSerial, storageStats.lastFlushedSerialNum()); EXPECT_EQUAL(storageStats.lastSerialNum(), calcLastSerialNum(chunkStats)); - EXPECT_EQUAL(storageStats.lastFlushedSerialNum(), - calcLastFlushedSerialNum(chunkStats)); - EXPECT_EQUAL(storageStats.diskUsage(), - calcDiskUsage(chunkStats)); + EXPECT_EQUAL(storageStats.lastFlushedSerialNum(), calcLastFlushedSerialNum(chunkStats)); + EXPECT_EQUAL(storageStats.diskUsage(), calcDiskUsage(chunkStats)); EXPECT_EQUAL(storageStats.diskBloat(), calcDiskBloat(chunkStats)); } @@ -218,14 +216,16 @@ void verifyGrowing(const LogDataStore::Config & config, uint32_t minFiles, uint3 { LogDataStore datastore(executor, "growing", config, GrowStrategy(), TuneFileSummary(), fileHeaderContext, tlSyncer, nullptr); - srand(7); + unsigned int seed = 383451; char buffer[12000]; SerialNum lastSyncToken(0); + std::minstd_rand rand_gen(seed); for (size_t i(0); i < sizeof(buffer); i++) { - buffer[i] = rand() & 0xff; + buffer[i] = rand_gen() & 0xff; } + for (size_t i(1); i < 10000; i++) { - long r = rand()%10000; + long r = rand_gen()%10000; assert(i > lastSyncToken); lastSyncToken = i; datastore.write(i, i, &buffer[r], uint8_t(buffer[r])*4); @@ -260,7 +260,7 @@ TEST("testGrowingChunkedBySize") { LogDataStore::Config config; config.setMaxFileSize(100000).setMaxBucketSpread(3.0).setMinFileSizeFactor(0.2) .compactCompression({CompressionConfig::LZ4}) - .setFileConfig({{CompressionConfig::LZ4, 9, 60}, 1000}); + .setFileConfig({{CompressionConfig::ZSTD, 9, 60}, 1000}); verifyGrowing(config, 40, 120); } @@ -268,7 +268,7 @@ TEST("testGrowingChunkedByNumLids") { LogDataStore::Config config; config.setMaxNumLids(1000).setMaxBucketSpread(3.0).setMinFileSizeFactor(0.2) .compactCompression({CompressionConfig::LZ4}) - .setFileConfig({{CompressionConfig::LZ4, 9, 60}, 1000}); + .setFileConfig({{CompressionConfig::ZSTD, 9, 60}, 1000}); verifyGrowing(config,10, 10); } @@ -488,7 +488,7 @@ private: class VerifyVisitor : public IDocumentVisitor { public: VerifyVisitor(VisitCacheStore & vcs, std::vector<uint32_t> lids, bool allowCaching); - ~VerifyVisitor(); + ~VerifyVisitor() override; void visit(uint32_t lid, Document::UP doc) override { EXPECT_TRUE(_expected.find(lid) != _expected.end()); EXPECT_TRUE(_actual.find(lid) == _actual.end()); @@ -514,7 +514,7 @@ private: }; VisitCacheStore::VerifyVisitor::VerifyVisitor(VisitCacheStore & vcs, std::vector<uint32_t> lids, bool allowCaching) - : _vcs(vcs), _expected(), _actual(), _allowVisitCaching(allowCaching) + : _vcs(vcs), _expected(), _actual(), _allowVisitCaching(allowCaching) { for (uint32_t lid : lids) { _expected.insert(lid); diff --git a/searchlib/src/vespa/searchlib/docstore/logdocumentstore.h b/searchlib/src/vespa/searchlib/docstore/logdocumentstore.h index 2b7d7365c1e..f2b8130fa95 100644 --- a/searchlib/src/vespa/searchlib/docstore/logdocumentstore.h +++ b/searchlib/src/vespa/searchlib/docstore/logdocumentstore.h @@ -26,7 +26,6 @@ public: _logConfig(log) { } const LogDataStore::Config & getLogConfig() const { return _logConfig; } - LogDataStore::Config & getLogConfig() { return _logConfig; } bool operator == (const Config & rhs) const; bool operator != (const Config & rhs) const { return ! (*this == rhs); } private: diff --git a/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java b/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java index dbd9771afe9..9f455a5b7d4 100644 --- a/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java +++ b/vespajlib/src/main/java/com/yahoo/slime/ArrayValue.java @@ -6,9 +6,94 @@ package com.yahoo.slime; */ final class ArrayValue extends Value { - private int capacity = 16; + static final int initial_capacity = 16; + static final Impl initial_impl = new EmptyImpl(); + + private interface Impl { + public void prepareFor(ArrayValue self, Type type); + public Value add(Value value, int used); + public Value get(int index); + } + + private static final class EmptyImpl implements Impl { + public void prepareFor(ArrayValue self, Type type) { + if (type == Type.LONG) { + self.impl = new LongImpl(); + } else if (type == Type.DOUBLE) { + self.impl = new DoubleImpl(); + } else { + self.impl = new GenericImpl(this, 0); + } + } + public Value add(Value value, int used) { return NixValue.invalid(); } + public Value get(int index) { return NixValue.invalid(); } + } + + private static final class LongImpl implements Impl { + private long[] values = new long[initial_capacity]; + public void prepareFor(ArrayValue self, Type type) { + if (type != Type.LONG) { + self.impl = new GenericImpl(this, self.used); + } + } + public Value add(Value value, int used) { + if (used == values.length) { + long[] v = values; + values = new long[v.length << 1]; + System.arraycopy(v, 0, values, 0, used); + } + values[used] = value.asLong(); + return get(used); + } + public Value get(int index) { return new LongValue(values[index]); } + } + + private static final class DoubleImpl implements Impl { + private double[] values = new double[initial_capacity]; + public void prepareFor(ArrayValue self, Type type) { + if (type != Type.DOUBLE) { + self.impl = new GenericImpl(this, self.used); + } + } + public Value add(Value value, int used) { + if (used == values.length) { + double[] v = values; + values = new double[v.length << 1]; + System.arraycopy(v, 0, values, 0, used); + } + values[used] = value.asDouble(); + return get(used); + } + public Value get(int index) { return new DoubleValue(values[index]); } + } + + private static final class GenericImpl implements Impl { + private Value[] values; + GenericImpl(Impl src, int len) { + int capacity = initial_capacity; + while (capacity < (len + 1)) { + capacity = capacity << 1; + } + values = new Value[capacity]; + for (int i = 0; i < len; i++) { + values[i] = src.get(i); + } + } + public void prepareFor(ArrayValue self, Type type) {} + public Value add(Value value, int used) { + if (used == values.length) { + Value[] v = values; + values = new Value[v.length << 1]; + System.arraycopy(v, 0, values, 0, used); + } + values[used] = value; + return get(used); + } + public Value get(int index) { return values[index]; } + } + + private Impl impl = initial_impl; private int used = 0; - private Value[] values = new Value[capacity]; private final SymbolTable names; public ArrayValue(SymbolTable names) { this.names = names; } @@ -16,33 +101,22 @@ final class ArrayValue extends Value { public int children() { return used; } public int entries() { return used; } public Value entry(int index) { - return (index < used) ? values[index] : NixValue.invalid(); + return (index >= 0 && index < used) ? impl.get(index) : NixValue.invalid(); } public void accept(Visitor v) { v.visitArray(this); } public void traverse(ArrayTraverser at) { for (int i = 0; i < used; i++) { - at.entry(i, values[i]); + at.entry(i, impl.get(i)); } } - private void grow() { - Value[] v = values; - capacity = (capacity << 1); - values = new Value[capacity]; - System.arraycopy(v, 0, values, 0, used); - } - protected Value addLeaf(Value value) { - if (used == capacity) { - grow(); - } - values[used++] = value; - return value; + impl.prepareFor(this, value.type()); + return impl.add(value, used++); } public Value addArray() { return addLeaf(new ArrayValue(names)); } public Value addObject() { return addLeaf(new ObjectValue(names)); } - } diff --git a/vespajlib/src/test/java/com/yahoo/slime/ArrayValueTestCase.java b/vespajlib/src/test/java/com/yahoo/slime/ArrayValueTestCase.java new file mode 100644 index 00000000000..c9ff86e7c2e --- /dev/null +++ b/vespajlib/src/test/java/com/yahoo/slime/ArrayValueTestCase.java @@ -0,0 +1,188 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.slime; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.sameInstance; + +import java.util.List; +import java.util.ArrayList; + +public class ArrayValueTestCase { + + static ArrayValue makeArray() { + return new ArrayValue(new SymbolTable()); + } + + @Test + public void testSymbolTableForwarding() { + SymbolTable names = new SymbolTable(); + assertThat(names.symbols(), is(0)); + new ArrayValue(names).addArray().addObject().setLong("foo", 3); + assertThat(names.symbols(), is(1)); + } + + @Test + public void testOutOfBoundsAccess() { + var array = makeArray(); + array.addBool(true); + assertThat(array.entry(-1).valid(), is(false)); + assertThat(array.entry(1).valid(), is(false)); + } + + @Test + public void testGenericArray() { + var array = makeArray(); + var added = new ArrayList<Cursor>(); + for (int i = 0; i < 128; ++i) { + added.add(array.addString("foo" + i)); + } + for (int i = 0; i < 128; i++) { + var e1 = array.entry(i); + var e2 = array.entry(i); + var e3 = added.get(i); + assertThat(e1, sameInstance(e2)); + assertThat(e1, sameInstance(e3)); + } + } + + @Test + public void testNativeLongArray() { + var array = makeArray(); + var added = new ArrayList<Cursor>(); + for (int i = 0; i < 128; ++i) { + added.add(array.addLong(i)); + } + for (int i = 0; i < 128; ++i) { + long expect = i; + var e1 = array.entry(i); + var e2 = array.entry(i); + var e3 = added.get(i); + assertThat(e1, not(sameInstance(e2))); + assertThat(e1, not(sameInstance(e3))); + assertThat(e1.equalTo(e2), is(true)); + assertThat(e1.equalTo(e3), is(true)); + assertThat(e1.type(), is(Type.LONG)); + assertThat(e1.asLong(), is(expect)); + } + } + + @Test + public void testNativeDoubleArray() { + var array = makeArray(); + var added = new ArrayList<Cursor>(); + for (int i = 0; i < 128; ++i) { + added.add(array.addDouble((double)i)); + } + for (int i = 0; i < 128; ++i) { + double expect = i; + var e1 = array.entry(i); + var e2 = array.entry(i); + var e3 = added.get(i); + assertThat(e1, not(sameInstance(e2))); + assertThat(e1, not(sameInstance(e3))); + assertThat(e1.equalTo(e2), is(true)); + assertThat(e1.equalTo(e3), is(true)); + assertThat(e1.type(), is(Type.DOUBLE)); + assertThat(e1.asDouble(), is(expect)); + } + } + + @Test + public void testLongToGenericConversion() { + for (Type type: Type.values()) { + if (type != Type.LONG) { + var array = makeArray(); + var added = new ArrayList<Cursor>(); + for (int i = 0; i < 64; ++i) { + added.add(array.addLong(i)); + } + switch (type) { + case NIX: added.add(array.addNix()); break; + case BOOL: added.add(array.addBool(true)); break; + case DOUBLE: added.add(array.addDouble(42.0)); break; + case STRING: added.add(array.addString("foo")); break; + case DATA: added.add(array.addData(new byte[1])); break; + case ARRAY: added.add(array.addArray()); break; + case OBJECT: added.add(array.addObject()); break; + } + assertThat(array.entries(), is(65)); + assertThat(array.entry(64).type(), is(type)); + assertThat(added.get(64), sameInstance(array.entry(64))); + for (int i = 0; i < 64; ++i) { + var e1 = array.entry(i); + var e2 = array.entry(i); + var e3 = added.get(i); + long expect = i; + assertThat(e1, sameInstance(e2)); + assertThat(e1, not(sameInstance(e3))); + assertThat(e1.equalTo(e2), is(true)); + assertThat(e1.equalTo(e3), is(true)); + assertThat(e1.type(), is(Type.LONG)); + assertThat(e1.asLong(), is(expect)); + } + } + } + } + + @Test + public void testDoubleToGenericConversion() { + for (Type type: Type.values()) { + if (type != Type.DOUBLE) { + var array = makeArray(); + var added = new ArrayList<Cursor>(); + for (int i = 0; i < 64; ++i) { + added.add(array.addDouble(i)); + } + switch (type) { + case NIX: added.add(array.addNix()); break; + case BOOL: added.add(array.addBool(true)); break; + case LONG: added.add(array.addLong(42)); break; + case STRING: added.add(array.addString("foo")); break; + case DATA: added.add(array.addData(new byte[1])); break; + case ARRAY: added.add(array.addArray()); break; + case OBJECT: added.add(array.addObject()); break; + } + assertThat(array.entries(), is(65)); + assertThat(array.entry(64).type(), is(type)); + assertThat(added.get(64), sameInstance(array.entry(64))); + for (int i = 0; i < 64; ++i) { + var e1 = array.entry(i); + var e2 = array.entry(i); + var e3 = added.get(i); + double expect = i; + assertThat(e1, sameInstance(e2)); + assertThat(e1, not(sameInstance(e3))); + assertThat(e1.equalTo(e2), is(true)); + assertThat(e1.equalTo(e3), is(true)); + assertThat(e1.type(), is(Type.DOUBLE)); + assertThat(e1.asDouble(), is(expect)); + } + } + } + } + + @Test + public void testGenericArrayStart() { + for (Type type: Type.values()) { + if (type != Type.LONG && type != Type.DOUBLE) { + var array = makeArray(); + Cursor added = null; + switch (type) { + case NIX: added = array.addNix(); break; + case BOOL: added = array.addBool(true); break; + case STRING: added = array.addString("foo"); break; + case DATA: added = array.addData(new byte[1]); break; + case ARRAY: added = array.addArray(); break; + case OBJECT: added = array.addObject(); break; + } + assertThat(array.entries(), is(1)); + assertThat(array.entry(0).type(), is(type)); + assertThat(added, sameInstance(array.entry(0))); + } + } + } +} diff --git a/vespalib/src/vespa/vespalib/util/time.cpp b/vespalib/src/vespa/vespalib/util/time.cpp index 42155647870..cba26f24059 100644 --- a/vespalib/src/vespa/vespalib/util/time.cpp +++ b/vespalib/src/vespa/vespalib/util/time.cpp @@ -93,9 +93,10 @@ Timer::waitAtLeast(duration dur, bool busyWait) { } -#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000 +#if (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000) || (!defined(_LIBCPP_VERSION) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12) // Temporary workaround until libc++ supports stream operators for duration +// Temporary workaround while using libstdc++ 11 #include <ostream> diff --git a/vespalib/src/vespa/vespalib/util/time.h b/vespalib/src/vespa/vespalib/util/time.h index 2cb53df8ae2..b893661832f 100644 --- a/vespalib/src/vespa/vespalib/util/time.h +++ b/vespalib/src/vespa/vespalib/util/time.h @@ -101,9 +101,10 @@ duration adjustTimeoutByHz(duration timeout, long hz); } -#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000 +#if (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000) || (!defined(_LIBCPP_VERSION) && defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12) // Temporary workaround until libc++ supports stream operators for duration +// Temporary workaround while using libstdc++ 11 #include <iosfwd> |