summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java4
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java3
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java6
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java4
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java17
-rw-r--r--storage/src/tests/distributor/distributor_stripe_test.cpp9
-rw-r--r--storage/src/vespa/storage/distributor/distributor_stripe.cpp10
-rw-r--r--storage/src/vespa/storage/distributor/idealstatemanager.cpp6
-rw-r--r--storage/src/vespa/storage/distributor/idealstatemetricsset.cpp16
-rw-r--r--storage/src/vespa/storage/distributor/idealstatemetricsset.h7
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp4
-rw-r--r--storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h33
-rw-r--r--vespajlib/src/main/java/com/yahoo/slime/BufferedOutput.java5
-rw-r--r--vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java47
14 files changed, 111 insertions, 60 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
index 5b1faa7218a..c91c4e92486 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/VespaMetricSet.java
@@ -704,6 +704,10 @@ public class VespaMetricSet {
metrics.add(new Metric("vds.idealstate.buckets_toomanycopies.average"));
metrics.add(new Metric("vds.idealstate.buckets.average"));
metrics.add(new Metric("vds.idealstate.buckets_notrusted.average"));
+ metrics.add(new Metric("vds.idealstate.bucket_replicas_moving_out.average"));
+ metrics.add(new Metric("vds.idealstate.bucket_replicas_copying_out.average"));
+ metrics.add(new Metric("vds.idealstate.bucket_replicas_copying_in.average"));
+ metrics.add(new Metric("vds.idealstate.bucket_replicas_syncing.average"));
metrics.add(new Metric("vds.idealstate.delete_bucket.done_ok.rate"));
metrics.add(new Metric("vds.idealstate.delete_bucket.done_failed.rate"));
metrics.add(new Metric("vds.idealstate.delete_bucket.pending.average"));
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java
index d0c08d2747d..fd27dedf041 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java
@@ -10,7 +10,8 @@ import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.ProvisionLogger;
import com.yahoo.config.provision.Provisioner;
-import java.util.*;
+import java.util.List;
+
/**
* A wrapper for {@link Provisioner} to avoid having to expose multitenant
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 14b5da1edd3..87736d8e591 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -140,12 +140,6 @@ public class Flags {
"Only allow modifications of disks by disk task in limited situations.",
"Takes effect on next host-admin tick.");
- public static final UnboundBooleanFlag NEW_SPARE_DISKS = defineFeatureFlag(
- "new-spare-disks", true,
- List.of("hakonhall"), "2021-09-08", "2021-11-08",
- "Use a new algorithm to calculate the spare disks of a host.",
- "Takes effect on first run of DiskTask, typically after host-admin restart/upgrade.");
-
public static final UnboundBooleanFlag LOCAL_SUSPEND = defineFeatureFlag(
"local-suspend", false,
List.of("hakonhall"), "2021-09-21", "2021-10-21",
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
index 487c60e0dcb..000b6cd4149 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java
@@ -1,4 +1,4 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.hosted.api;
import com.yahoo.config.provision.ApplicationId;
@@ -340,6 +340,8 @@ public abstract class ControllerHttpClient {
Slime slime = new Slime();
Cursor rootObject = slime.setObject();
deployment.version().ifPresent(version -> rootObject.setString("vespaVersion", version));
+ if (deployment.isDryRun())
+ rootObject.setString("dryRun", "true");
return toJson(slime);
}
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java
index d012d27fbd8..77cc116b413 100644
--- a/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java
+++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Deployment.java
@@ -1,9 +1,8 @@
-// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.hosted.api;
import java.nio.file.Path;
import java.util.Optional;
-import java.util.OptionalLong;
/**
* A deployment intended for hosted Vespa, containing an application package and some meta data.
@@ -12,23 +11,31 @@ public class Deployment {
private final Optional<String> version;
private final Path applicationZip;
+ private final boolean dryRun;
- private Deployment(Optional<String> version, Path applicationZip) {
+ private Deployment(Optional<String> version, Path applicationZip, boolean dryRun) {
this.version = version;
this.applicationZip = applicationZip;
+ this.dryRun = dryRun;
}
/** Returns a deployment which will use the provided application package. */
public static Deployment ofPackage(Path applicationZipFile) {
- return new Deployment(Optional.empty(), applicationZipFile);
+ return new Deployment(Optional.empty(), applicationZipFile, false);
}
/** Returns a copy of this which will have the specified Vespa version on its nodes. */
public Deployment atVersion(String vespaVersion) {
- return new Deployment(Optional.of(vespaVersion), applicationZip);
+ return new Deployment(Optional.of(vespaVersion), applicationZip, false);
+ }
+
+ /** Returns a copy of this which will do a dry-run deployment. */
+ public Deployment withDryRun() {
+ return new Deployment(version, applicationZip, true);
}
public Optional<String> version() { return version; }
public Path applicationZip() { return applicationZip; }
+ public boolean isDryRun() { return dryRun; }
}
diff --git a/storage/src/tests/distributor/distributor_stripe_test.cpp b/storage/src/tests/distributor/distributor_stripe_test.cpp
index a38b40d3682..067b0efdd5c 100644
--- a/storage/src/tests/distributor/distributor_stripe_test.cpp
+++ b/storage/src/tests/distributor/distributor_stripe_test.cpp
@@ -475,7 +475,7 @@ TEST_F(DistributorStripeTest, merge_stats_are_accumulated_during_database_iterat
// added to existing.
tickDistributorNTimes(50);
- const auto& stats = stripe_maintenance_stats();
+ const auto stats = stripe_maintenance_stats();
{
NodeMaintenanceStats wanted;
wanted.syncing = 1;
@@ -501,6 +501,11 @@ TEST_F(DistributorStripeTest, merge_stats_are_accumulated_during_database_iterat
assertBucketSpaceStats(1, 3, 0, "default", bucketStats);
assertBucketSpaceStats(0, 1, 1, "default", bucketStats);
assertBucketSpaceStats(3, 1, 2, "default", bucketStats);
+
+ EXPECT_EQ(stats.perNodeStats.total_replica_stats().movingOut, 1);
+ EXPECT_EQ(stats.perNodeStats.total_replica_stats().copyingOut, 2);
+ EXPECT_EQ(stats.perNodeStats.total_replica_stats().copyingIn, 2);
+ EXPECT_EQ(stats.perNodeStats.total_replica_stats().syncing, 2);
}
void
@@ -534,7 +539,7 @@ TEST_F(DistributorStripeTest, stats_generated_for_preempted_operations)
// by activation, we'll see no merge stats at all.
addNodesToBucketDB(document::BucketId(16, 1), "0=1/1/1,1=2/2/2");
tickDistributorNTimes(50);
- const auto& stats = stripe_maintenance_stats();
+ const auto stats = stripe_maintenance_stats();
{
NodeMaintenanceStats wanted;
wanted.syncing = 1;
diff --git a/storage/src/vespa/storage/distributor/distributor_stripe.cpp b/storage/src/vespa/storage/distributor/distributor_stripe.cpp
index 21059649ef9..da32b7ad4c6 100644
--- a/storage/src/vespa/storage/distributor/distributor_stripe.cpp
+++ b/storage/src/vespa/storage/distributor/distributor_stripe.cpp
@@ -558,8 +558,14 @@ DistributorStripe::propagateInternalScanMetricsToExternal()
// All shared values are written when _metricLock is held, so no races.
if (_bucketDBMetricUpdater.hasCompletedRound()) {
- _bucketDbStats.propagateMetrics(_idealStateManager.getMetrics(), getMetrics());
- _idealStateManager.getMetrics().setPendingOperations(_maintenanceStats.global.pending);
+ auto& ideal_state_metrics = _idealStateManager.getMetrics();
+ _bucketDbStats.propagateMetrics(ideal_state_metrics, getMetrics());
+ ideal_state_metrics.setPendingOperations(_maintenanceStats.global.pending);
+ const auto& total_stats = _maintenanceStats.perNodeStats.total_replica_stats();
+ ideal_state_metrics.buckets_replicas_moving_out.set(total_stats.movingOut);
+ ideal_state_metrics.buckets_replicas_copying_out.set(total_stats.copyingOut);
+ ideal_state_metrics.buckets_replicas_copying_in.set(total_stats.copyingIn);
+ ideal_state_metrics.buckets_replicas_syncing.set(total_stats.syncing);
}
}
diff --git a/storage/src/vespa/storage/distributor/idealstatemanager.cpp b/storage/src/vespa/storage/distributor/idealstatemanager.cpp
index 124f75ec169..f1d2b163623 100644
--- a/storage/src/vespa/storage/distributor/idealstatemanager.cpp
+++ b/storage/src/vespa/storage/distributor/idealstatemanager.cpp
@@ -21,8 +21,7 @@ using document::BucketSpace;
using storage::lib::Node;
using storage::lib::NodeType;
-namespace storage {
-namespace distributor {
+namespace storage::distributor {
IdealStateManager::IdealStateManager(
const DistributorNodeContext& node_ctx,
@@ -298,5 +297,4 @@ void IdealStateManager::getBucketStatus(std::ostream& out) const {
}
}
-} // distributor
-} // storage
+} // storage::distributor
diff --git a/storage/src/vespa/storage/distributor/idealstatemetricsset.cpp b/storage/src/vespa/storage/distributor/idealstatemetricsset.cpp
index fd193ad6fd8..e786d81df91 100644
--- a/storage/src/vespa/storage/distributor/idealstatemetricsset.cpp
+++ b/storage/src/vespa/storage/distributor/idealstatemetricsset.cpp
@@ -86,7 +86,21 @@ IdealStateMetricSet::IdealStateMetricSet()
{{"logdefault"},{"yamasdefault"}},
"The number of buckets that we are rechecking for "
"ideal state operations", this),
- startOperationsLatency("start_operations_latency", {}, "Time used in startOperations()", this),
+ buckets_replicas_moving_out("bucket_replicas_moving_out",
+ {{"logdefault"},{"yamasdefault"}},
+ "Bucket replicas that should be moved out, e.g. retirement case or node "
+ "added to cluster that has higher ideal state priority.", this),
+ buckets_replicas_copying_in("bucket_replicas_copying_in",
+ {{"logdefault"},{"yamasdefault"}},
+ "Bucket replicas that should be copied in, e.g. node does not have a "
+ "replica for a bucket that it is in ideal state for", this),
+ buckets_replicas_copying_out("bucket_replicas_copying_out",
+ {{"logdefault"},{"yamasdefault"}},
+ "Bucket replicas that should be copied out, e.g. node is in ideal state "
+ "but might have to provide data other nodes in a merge", this),
+ buckets_replicas_syncing("bucket_replicas_syncing",
+ {{"logdefault"},{"yamasdefault"}},
+ "Bucket replicas that need syncing due to mismatching metadata", this),
nodesPerMerge("nodes_per_merge", {}, "The number of nodes involved in a single merge operation.", this)
{
createOperationMetrics();
diff --git a/storage/src/vespa/storage/distributor/idealstatemetricsset.h b/storage/src/vespa/storage/distributor/idealstatemetricsset.h
index c1fb39bb50a..e9ccef9f93e 100644
--- a/storage/src/vespa/storage/distributor/idealstatemetricsset.h
+++ b/storage/src/vespa/storage/distributor/idealstatemetricsset.h
@@ -38,8 +38,11 @@ public:
metrics::LongValueMetric buckets_toomanycopies;
metrics::LongValueMetric buckets;
metrics::LongValueMetric buckets_notrusted;
- metrics::LongValueMetric buckets_rechecking;
- metrics::LongAverageMetric startOperationsLatency;
+ metrics::LongValueMetric buckets_rechecking; // TODO remove, not used (but exposed by VespaMetricSet)
+ metrics::LongValueMetric buckets_replicas_moving_out;
+ metrics::LongValueMetric buckets_replicas_copying_in;
+ metrics::LongValueMetric buckets_replicas_copying_out;
+ metrics::LongValueMetric buckets_replicas_syncing;
metrics::DoubleAverageMetric nodesPerMerge;
void createOperationMetrics();
diff --git a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp
index 4e7f7d9d89d..db2eb6aadc9 100644
--- a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp
+++ b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.cpp
@@ -34,9 +34,9 @@ merge_bucket_spaces_stats(NodeMaintenanceStatsTracker::BucketSpacesStats& dest,
void
NodeMaintenanceStatsTracker::merge(const NodeMaintenanceStatsTracker& rhs)
{
- for (const auto& entry : rhs._stats) {
+ for (const auto& entry : rhs._node_stats) {
auto node_index = entry.first;
- merge_bucket_spaces_stats(_stats[node_index], entry.second);
+ merge_bucket_spaces_stats(_node_stats[node_index], entry.second);
}
}
diff --git a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h
index 6399e53089b..3c45bcdd5e5 100644
--- a/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h
+++ b/storage/src/vespa/storage/distributor/maintenance/node_maintenance_stats_tracker.h
@@ -50,7 +50,8 @@ public:
using PerNodeStats = std::unordered_map<uint16_t, BucketSpacesStats>;
private:
- PerNodeStats _stats;
+ PerNodeStats _node_stats;
+ NodeMaintenanceStats _total_stats;
static const NodeMaintenanceStats _emptyNodeMaintenanceStats;
public:
@@ -58,23 +59,28 @@ public:
~NodeMaintenanceStatsTracker();
void incMovingOut(uint16_t node, document::BucketSpace bucketSpace) {
- ++_stats[node][bucketSpace].movingOut;
+ ++_node_stats[node][bucketSpace].movingOut;
+ ++_total_stats.movingOut;
}
void incSyncing(uint16_t node, document::BucketSpace bucketSpace) {
- ++_stats[node][bucketSpace].syncing;
+ ++_node_stats[node][bucketSpace].syncing;
+ ++_total_stats.syncing;
}
void incCopyingIn(uint16_t node, document::BucketSpace bucketSpace) {
- ++_stats[node][bucketSpace].copyingIn;
+ ++_node_stats[node][bucketSpace].copyingIn;
+ ++_total_stats.copyingIn;
}
void incCopyingOut(uint16_t node, document::BucketSpace bucketSpace) {
- ++_stats[node][bucketSpace].copyingOut;
+ ++_node_stats[node][bucketSpace].copyingOut;
+ ++_total_stats.copyingOut;
}
void incTotal(uint16_t node, document::BucketSpace bucketSpace) {
- ++_stats[node][bucketSpace].total;
+ ++_node_stats[node][bucketSpace].total;
+ ++_total_stats.total;
}
/**
@@ -82,8 +88,8 @@ public:
* if none have been recorded yet
*/
const NodeMaintenanceStats& forNode(uint16_t node, document::BucketSpace bucketSpace) const {
- auto nodeItr = _stats.find(node);
- if (nodeItr != _stats.end()) {
+ auto nodeItr = _node_stats.find(node);
+ if (nodeItr != _node_stats.end()) {
auto bucketSpaceItr = nodeItr->second.find(bucketSpace);
if (bucketSpaceItr != nodeItr->second.end()) {
return bucketSpaceItr->second;
@@ -93,11 +99,18 @@ public:
}
const PerNodeStats& perNodeStats() const {
- return _stats;
+ return _node_stats;
+ }
+
+ // Note: the total statistics are across all replicas across all buckets across all bucket spaces.
+ // That means it's possible for a single bucket to count more than once, up to once per replica.
+ // So this should not be treated as a bucket-level statistic.
+ const NodeMaintenanceStats& total_replica_stats() const noexcept {
+ return _total_stats;
}
bool operator==(const NodeMaintenanceStatsTracker& rhs) const {
- return _stats == rhs._stats;
+ return _node_stats == rhs._node_stats;
}
void merge(const NodeMaintenanceStatsTracker& rhs);
};
diff --git a/vespajlib/src/main/java/com/yahoo/slime/BufferedOutput.java b/vespajlib/src/main/java/com/yahoo/slime/BufferedOutput.java
index 0fabea77df0..4c76abd4707 100644
--- a/vespajlib/src/main/java/com/yahoo/slime/BufferedOutput.java
+++ b/vespajlib/src/main/java/com/yahoo/slime/BufferedOutput.java
@@ -3,6 +3,8 @@ package com.yahoo.slime;
import com.yahoo.compress.Compressor;
+import java.nio.charset.Charset;
+
final class BufferedOutput {
private byte[] buf;
@@ -56,6 +58,9 @@ final class BufferedOutput {
System.arraycopy(buf, 0, ret, 0, pos);
return ret;
}
+ public String toString(Charset charset) {
+ return new String(buf, 0, pos, charset);
+ }
Compressor.Compression compress(Compressor compressor) {
return compressor.compress(buf, pos);
}
diff --git a/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java b/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java
index b7f11f53cd5..682eccdab42 100644
--- a/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java
+++ b/vespajlib/src/main/java/com/yahoo/slime/JsonDecoder.java
@@ -2,10 +2,9 @@
package com.yahoo.slime;
import com.yahoo.text.Text;
-import com.yahoo.text.Utf8;
-import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
/**
* A port of the C++ json decoder intended to be fast.
@@ -20,7 +19,7 @@ public class JsonDecoder {
private final SlimeInserter slimeInserter = new SlimeInserter(null);
private final ArrayInserter arrayInserter = new ArrayInserter(null);
private final ObjectInserter objectInserter = new ObjectInserter(null, null);
- private final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ private final BufferedOutput buf = new BufferedOutput();
private static final byte[] TRUE = {'t', 'r', 'u', 'e'};
private static final byte[] FALSE = {'f', 'a', 'l', 's', 'e'};
@@ -86,15 +85,15 @@ public class JsonDecoder {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '+': case '-':
- buf.write(c);
+ buf.put(c);
next();
break;
default:
if (likelyFloatingPoint) {
- double num = Double.parseDouble(Utf8.toString(buf.toByteArray()));
+ double num = Double.parseDouble(buf.toString(StandardCharsets.UTF_8));
inserter.insertDOUBLE(num);
} else {
- long num = Long.parseLong(Utf8.toString(buf.toByteArray()));
+ long num = Long.parseLong(buf.toString(StandardCharsets.UTF_8));
inserter.insertLONG(num);
}
return;
@@ -103,8 +102,8 @@ public class JsonDecoder {
}
private void expect(byte[] expected) {
- for (int i = 0; i < expected.length; i++) {
- if ( ! skip(expected[i])) {
+ for (byte b : expected) {
+ if ( ! skip(b)) {
in.fail("Unexpected " + characterToReadableString(c));
return;
}
@@ -150,9 +149,9 @@ public class JsonDecoder {
default:
for (;;) {
switch (c) {
- case ':': case ' ': case '\t': case '\n': case '\r': case '\0': return Utf8.toString(buf.toByteArray());
+ case ':': case ' ': case '\t': case '\n': case '\r': case '\0': return buf.toString(StandardCharsets.UTF_8);
default:
- buf.write(c);
+ buf.put(c);
next();
break;
}
@@ -176,13 +175,13 @@ public class JsonDecoder {
next();
switch (c) {
case '"': case '\\': case '/': case '\'':
- buf.write(c);
+ buf.put(c);
break;
- case 'b': buf.write((byte) '\b'); break;
- case 'f': buf.write((byte) '\f'); break;
- case 'n': buf.write((byte) '\n'); break;
- case 'r': buf.write((byte) '\r'); break;
- case 't': buf.write((byte) '\t'); break;
+ case 'b': buf.put((byte) '\b'); break;
+ case 'f': buf.put((byte) '\f'); break;
+ case 'n': buf.put((byte) '\n'); break;
+ case 'r': buf.put((byte) '\r'); break;
+ case 't': buf.put((byte) '\t'); break;
case 'u': writeUtf8(dequoteUtf16(), buf, 0xffffff80); continue;
default:
in.fail("Invalid quoted char(" + c + ")");
@@ -193,34 +192,34 @@ public class JsonDecoder {
case '"': case '\'':
if (c == quote) {
next();
- return Utf8.toString(buf.toByteArray());
+ return buf.toString(StandardCharsets.UTF_8);
} else {
- buf.write(c);
+ buf.put(c);
next();
}
break;
case '\0':
in.fail("Unterminated string");
- return Utf8.toString(buf.toByteArray());
+ return buf.toString(StandardCharsets.UTF_8);
default:
- buf.write(c);
+ buf.put(c);
next();
break;
}
}
}
- private static void writeUtf8(long codepoint, ByteArrayOutputStream buf, long mask) {
+ private static void writeUtf8(long codepoint, BufferedOutput buf, long mask) {
if ((codepoint & mask) == 0) {
- buf.write((byte) ((mask << 1) | codepoint));
+ buf.put((byte) ((mask << 1) | codepoint));
} else {
writeUtf8(codepoint >> 6, buf, mask >> (2 - ((mask >> 6) & 0x1)));
- buf.write((byte) (0x80 | (codepoint & 0x3f)));
+ buf.put((byte) (0x80 | (codepoint & 0x3f)));
}
}
- private static byte[] unicodeStart = {'\\', 'u'};
+ private final static byte[] unicodeStart = {'\\', 'u'};
private long dequoteUtf16() {
next();
long codepoint = readHexValue(4);