mirror of https://github.com/apache/cassandra
Optimize UTF8Validator.validate for ASCII prefixed Strings
Use a plain loop to check if it is ASCII symbol before going into more complicated UTF8 parsing. Avoid ValueAccessor to get extra boost for the ASCII check, especially in non-monomorphic cases. Patch by Dmitry Konstantinov; reviewed by Jyothsna Konisa, Stefan Miklosovic for CASSANDRA-21075
This commit is contained in:
parent
d7bd75303f
commit
c295c33a26
|
|
@ -1,4 +1,5 @@
|
|||
5.1
|
||||
* Optimize UTF8Validator.validate for ASCII prefixed Strings (CASSANDRA-21075)
|
||||
* Switch LatencyMetrics to use ThreadLocalTimer/ThreadLocalCounter (CASSANDRA-21080)
|
||||
* Accord: write rejections would be returned to users as server errors rather than INVALID and TxnReferenceOperation didn't handle all collections prperly (CASSANDRA-21061)
|
||||
* Use byte[] directly in QueryOptions instead of ByteBuffer and convert them to ArrayCell instead of BufferCell to reduce allocations (CASSANDRA-20166)
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
*/
|
||||
package org.apache.cassandra.serializers;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.cassandra.db.marshal.ByteArrayAccessor;
|
||||
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
|
||||
import org.apache.cassandra.db.marshal.ValueAccessor;
|
||||
|
||||
public class UTF8Serializer extends AbstractTextSerializer
|
||||
|
|
@ -57,8 +60,53 @@ public class UTF8Serializer extends AbstractTextSerializer
|
|||
if (value == null)
|
||||
return false;
|
||||
|
||||
int b = 0;
|
||||
int offset = 0;
|
||||
// perf optimizations:
|
||||
// 1) avoid bimorphic/megamorphic calls via ValueAccessor
|
||||
// 2) use a simplified logic to handle ASCII prefixed String scenario faster
|
||||
if (accessor == ByteArrayAccessor.instance)
|
||||
{
|
||||
byte[] valueAsArray = accessor.toArray(value);
|
||||
return validateByteArray(valueAsArray, 0, valueAsArray.length, value, accessor);
|
||||
}
|
||||
|
||||
if (accessor == ByteBufferAccessor.instance)
|
||||
{
|
||||
ByteBuffer valueAsBuffer = accessor.toBuffer(value);
|
||||
if (valueAsBuffer.hasArray())
|
||||
{
|
||||
byte[] valueAsArray = valueAsBuffer.array();
|
||||
int start = valueAsBuffer.position();
|
||||
int end = start + valueAsBuffer.remaining();
|
||||
return validateByteArray(valueAsArray, start, end, value, accessor);
|
||||
}
|
||||
}
|
||||
|
||||
int end = accessor.size(value);
|
||||
for (int i = 0; i < end; i++)
|
||||
{
|
||||
if (accessor.getByte(value, i) < 0)
|
||||
{
|
||||
return validateSlowPath(value, accessor, i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static <V> boolean validateByteArray(byte[] valueAsArray, int start, int end,
|
||||
V value, ValueAccessor<V> accessor)
|
||||
{
|
||||
assert start >= 0 && end <= valueAsArray.length;
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
if (valueAsArray[i] < 0)
|
||||
return validateSlowPath(value, accessor, i - start);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static <V> boolean validateSlowPath(V value, ValueAccessor<V> accessor, int offset)
|
||||
{
|
||||
int b;
|
||||
State state = State.START;
|
||||
while (!accessor.isEmptyFromOffset(value, offset))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.test.microbench;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.cassandra.db.marshal.ByteArrayAccessor;
|
||||
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
|
||||
import org.apache.cassandra.serializers.UTF8Serializer;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||
@Fork(value = 3, jvmArgsAppend = "-Xmx512M")
|
||||
@Threads(1)
|
||||
@State(Scope.Benchmark)
|
||||
public class UTF8ValidatorBench
|
||||
{
|
||||
|
||||
@Param({ "short ASCII", "long ASCII", "short ASCII prefix non-ASCII", "short non-ASCII", "long non-ASCII"})
|
||||
private String stringType;
|
||||
|
||||
byte[] arrayValue;
|
||||
ByteBuffer heapByteBufferValue;
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() throws Throwable
|
||||
{
|
||||
switch (stringType)
|
||||
{
|
||||
case "short ASCII":
|
||||
arrayValue = "ASCII string".getBytes(StandardCharsets.UTF_8);
|
||||
break;
|
||||
case "long ASCII":
|
||||
arrayValue = ("ASCII is an acronym for American Standard Code for Information Interchange, " +
|
||||
"is a character encoding standard for representing a particular set of 95 " +
|
||||
"(English language focused) printable and 33 control characters – a total of 128 code points. " +
|
||||
"The set of available punctuation had significant impact on the syntax of computer languages " +
|
||||
"and text markup. ASCII hugely influenced the design of character sets used by modern computers; " +
|
||||
"for example, the first 128 code points of Unicode are the same as ASCII.").getBytes(StandardCharsets.UTF_8);
|
||||
break;
|
||||
case "short ASCII prefix non-ASCII":
|
||||
arrayValue = "a hierarchy of number systems: ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ".getBytes(StandardCharsets.UTF_8);
|
||||
break;
|
||||
case "short non-ASCII":
|
||||
arrayValue = "ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ".getBytes(StandardCharsets.UTF_8);
|
||||
break;
|
||||
case "long non-ASCII": // https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html
|
||||
arrayValue = ("⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞\n" +
|
||||
" ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎\n" +
|
||||
" ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂\n" +
|
||||
" ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙\n" +
|
||||
" ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ \n" +
|
||||
" ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲").getBytes(StandardCharsets.UTF_8);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
heapByteBufferValue = ByteBuffer.allocate(arrayValue.length);
|
||||
heapByteBufferValue.put(arrayValue).rewind();
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
public void testBimorphic()
|
||||
{
|
||||
UTF8Serializer.instance.validate(heapByteBufferValue, ByteBufferAccessor.instance);
|
||||
UTF8Serializer.instance.validate(arrayValue, ByteArrayAccessor.instance);
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
public void testMonomorphicArray()
|
||||
{
|
||||
UTF8Serializer.instance.validate(arrayValue, ByteArrayAccessor.instance);
|
||||
UTF8Serializer.instance.validate(arrayValue, ByteArrayAccessor.instance);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testMonomorphicHeapByteBuffer()
|
||||
{
|
||||
UTF8Serializer.instance.validate(heapByteBufferValue, ByteBufferAccessor.instance);
|
||||
UTF8Serializer.instance.validate(heapByteBufferValue, ByteBufferAccessor.instance);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.serializers;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.db.marshal.ByteArrayAccessor;
|
||||
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
// https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html
|
||||
public class UTF8ValidatorTest
|
||||
{
|
||||
@Test
|
||||
public void testValidStrings()
|
||||
{
|
||||
assertValidUtf8String("");
|
||||
assertValidUtf8String("ASCII text");
|
||||
assertValidUtf8String("\n\r");
|
||||
assertValidUtf8String("a hierarchy of number systems: ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ");
|
||||
assertValidUtf8String("ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ");
|
||||
assertValidUtf8String("⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞\n" +
|
||||
" ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎\n" +
|
||||
" ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂\n" +
|
||||
" ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙\n" +
|
||||
" ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ \n" +
|
||||
" ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲");
|
||||
assertValidUtf8String("Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese\n" +
|
||||
" classic 'San Gua'):\n" +
|
||||
"\n" +
|
||||
" [----------------------------|------------------------]\n" +
|
||||
" ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่\n" +
|
||||
" สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา\n" +
|
||||
" ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา\n" +
|
||||
" โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ\n" +
|
||||
" เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ\n" +
|
||||
" ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ\n" +
|
||||
" พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้\n" +
|
||||
" ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ");
|
||||
|
||||
assertValidUtf8String("ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ");
|
||||
assertValidUtf8String("Box drawing alignment tests: █\n" +
|
||||
" ▉\n" +
|
||||
" ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳\n" +
|
||||
" ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳\n" +
|
||||
" ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳\n" +
|
||||
" ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\n" +
|
||||
" ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎\n" +
|
||||
" ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏\n" +
|
||||
" ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n");
|
||||
|
||||
}
|
||||
|
||||
@Test // https://www.w3.org/2001/06/utf-8-wrong/UTF-8-test.html
|
||||
public void testInvalidStrings()
|
||||
{
|
||||
// continuation bytes only
|
||||
assertInvalidUtf8String(0x80);
|
||||
assertInvalidUtf8String(0xbf);
|
||||
assertInvalidUtf8String(0x80,0x80);
|
||||
// Bad trailing bytes
|
||||
assertInvalidUtf8String(0xF0, 0xA4, 0xAD, 0x7F);
|
||||
assertInvalidUtf8String(0xF0, 0xA4, 0xAD, 0x7F);
|
||||
// first bytes of 2-byte sequences (0xc0-0xdf), each followed by a space character
|
||||
assertInvalidUtf8String(0xc0, ' ', 0xdf, ' ');
|
||||
// first bytes of 3-byte sequences (0xe0-0xef), each followed by a space character
|
||||
assertInvalidUtf8String(0xe0, ' ', 0xe1, ' ');
|
||||
// first bytes of 4-byte sequences (0xf0-0xf7), each followed by a space character
|
||||
assertInvalidUtf8String(0xf0, ' ', 0xf7, ' ');
|
||||
// first bytes of 5-byte sequences (0xf8-0xfb), each followed by a space character
|
||||
assertInvalidUtf8String(0xf8, ' ', 0xfb, ' ');
|
||||
// first bytes of 6-byte sequences (0xfc-0xfd), each followed by a space character
|
||||
assertInvalidUtf8String(0xfc, ' ', 0xfd, ' ');
|
||||
// Impossible bytes
|
||||
assertInvalidUtf8String(0xfe);
|
||||
assertInvalidUtf8String(0xff);
|
||||
assertInvalidUtf8String(0xfe, 0xfe, 0xff, 0xff);
|
||||
// Sequences with last continuation byte missing
|
||||
assertInvalidUtf8String(0xc0);
|
||||
assertInvalidUtf8String(0xe0, 0x80);
|
||||
// Maximum overlong sequences
|
||||
assertInvalidUtf8String(0xc1, 0xbf);
|
||||
|
||||
// 'ASCII' + continuation byte at the end
|
||||
assertInvalidUtf8String(0x41, 0x53, 0x43, 0x49, 0x49, 0x80);
|
||||
}
|
||||
|
||||
public void assertValidUtf8String(String value)
|
||||
{
|
||||
byte[] byteArrayValue = value.getBytes(StandardCharsets.UTF_8);
|
||||
ByteBuffer bufferValue = ByteBuffer.wrap(byteArrayValue);
|
||||
ByteBuffer bufferValueInTheMiddle = ByteBuffer.allocate(byteArrayValue.length + 2 + 2);
|
||||
wrapValueWithImpossibleBytes(bufferValueInTheMiddle, byteArrayValue);
|
||||
|
||||
ByteBuffer directBufferValue = ByteBuffer.allocateDirect(byteArrayValue.length);
|
||||
directBufferValue.put(byteArrayValue);
|
||||
directBufferValue.rewind();
|
||||
ByteBuffer directBufferValueInTheMiddle = ByteBuffer.allocate(byteArrayValue.length + 2 + 2);
|
||||
wrapValueWithImpossibleBytes(directBufferValueInTheMiddle, byteArrayValue);
|
||||
|
||||
assertTrue(UTF8Serializer.UTF8Validator.validate(byteArrayValue, ByteArrayAccessor.instance));
|
||||
|
||||
assertTrue(UTF8Serializer.UTF8Validator.validate(bufferValue, ByteBufferAccessor.instance));
|
||||
assertTrue(UTF8Serializer.UTF8Validator.validate(bufferValueInTheMiddle, ByteBufferAccessor.instance));
|
||||
|
||||
assertTrue(UTF8Serializer.UTF8Validator.validate(directBufferValue, ByteBufferAccessor.instance));
|
||||
assertTrue(UTF8Serializer.UTF8Validator.validate(bufferValueInTheMiddle, ByteBufferAccessor.instance));
|
||||
}
|
||||
|
||||
private static void wrapValueWithImpossibleBytes(ByteBuffer bufferValueInTheMiddle, byte[] byteArrayValue)
|
||||
{
|
||||
// bufferValue wrapped by impossible bytes
|
||||
// to ensure that validate method does not read outside of buffer boundaries
|
||||
bufferValueInTheMiddle.put((byte)0xfe);
|
||||
bufferValueInTheMiddle.put((byte)0xfe);
|
||||
bufferValueInTheMiddle.put(byteArrayValue);
|
||||
bufferValueInTheMiddle.put((byte)0xfe);
|
||||
bufferValueInTheMiddle.put((byte)0xfe);
|
||||
bufferValueInTheMiddle.rewind();
|
||||
bufferValueInTheMiddle.position(2);
|
||||
bufferValueInTheMiddle.limit(bufferValueInTheMiddle.limit() - 2);
|
||||
}
|
||||
|
||||
public void assertInvalidUtf8String(int ... bytes)
|
||||
{
|
||||
byte[] byteArrayValue = toByteArray(bytes);
|
||||
ByteBuffer bufferValue = ByteBuffer.wrap(byteArrayValue);
|
||||
ByteBuffer directBufferValue = ByteBuffer.allocateDirect(byteArrayValue.length);
|
||||
directBufferValue.put(byteArrayValue);
|
||||
directBufferValue.rewind();
|
||||
|
||||
assertFalse(UTF8Serializer.UTF8Validator.validate(byteArrayValue, ByteArrayAccessor.instance));
|
||||
assertFalse(UTF8Serializer.UTF8Validator.validate(bufferValue, ByteBufferAccessor.instance));
|
||||
assertFalse(UTF8Serializer.UTF8Validator.validate(directBufferValue, ByteBufferAccessor.instance));
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(int... bytes)
|
||||
{
|
||||
byte[] value = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++)
|
||||
value[i] = (byte) bytes[i];
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue